From 830407e88f9d40d954356c3754f2647f91d5c06a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:26:00 +0200 Subject: Adding upstream version 5.6.0. Signed-off-by: Daniel Baumann --- modules/README.rst | 251 + modules/bogus_log/.packaging/test.config | 4 + modules/bogus_log/README.rst | 45 + modules/bogus_log/bogus_log.c | 135 + modules/bogus_log/meson.build | 21 + modules/bogus_log/test.integr/deckard.yaml | 13 + modules/bogus_log/test.integr/kresd_config.j2 | 90 + .../test.integr/val_minimal_expiredsignature.rpl | 125 + modules/cookies/README.rst | 56 + modules/cookies/cookiectl.c | 689 +++ modules/cookies/cookiectl.h | 35 + modules/cookies/cookiemonster.c | 464 ++ modules/cookies/cookiemonster.h | 15 + modules/cookies/cookies.c | 78 + modules/daf/.packaging/test.config | 4 + modules/daf/README.rst | 146 + modules/daf/daf.js | 295 + modules/daf/daf.lua | 392 ++ modules/daf/daf.test.lua | 80 + modules/daf/daf_http.test.lua | 216 + modules/daf/meson.build | 21 + modules/daf/test.integr/deckard.yaml | 12 + modules/daf/test.integr/kresd_config.j2 | 65 + modules/daf/test.integr/module_daf.rpl | 30 + modules/detect_time_jump/.packaging/test.config | 4 + modules/detect_time_jump/README.rst | 22 + modules/detect_time_jump/detect_time_jump.lua | 45 + modules/detect_time_skew/.packaging/test.config | 4 + modules/detect_time_skew/README.rst | 23 + modules/detect_time_skew/detect_time_skew.lua | 83 + modules/dns64/.packaging/test.config | 4 + modules/dns64/README.rst | 62 + modules/dns64/dns64.lua | 220 + modules/dns64/dns64.test.lua | 53 + modules/dnstap/.packaging/centos/7/builddeps | 3 + modules/dnstap/.packaging/centos/7/rundeps | 2 + modules/dnstap/.packaging/centos/8/builddeps | 3 + modules/dnstap/.packaging/centos/8/rundeps | 2 + modules/dnstap/.packaging/debian/10/builddeps | 3 + modules/dnstap/.packaging/debian/10/rundeps | 2 + modules/dnstap/.packaging/debian/9/builddeps | 3 + modules/dnstap/.packaging/debian/9/rundeps | 2 + modules/dnstap/.packaging/fedora/31/builddeps | 3 + modules/dnstap/.packaging/fedora/31/rundeps | 2 + modules/dnstap/.packaging/fedora/32/builddeps | 3 + modules/dnstap/.packaging/fedora/32/rundeps | 2 + modules/dnstap/.packaging/leap/15.2/builddeps | 3 + modules/dnstap/.packaging/leap/15.2/rundeps | 2 + modules/dnstap/.packaging/test.config | 4 + modules/dnstap/.packaging/ubuntu/16.04/builddeps | 3 + modules/dnstap/.packaging/ubuntu/16.04/rundeps | 2 + modules/dnstap/.packaging/ubuntu/18.04/builddeps | 3 + modules/dnstap/.packaging/ubuntu/18.04/rundeps | 2 + modules/dnstap/.packaging/ubuntu/20.04/builddeps | 3 + modules/dnstap/.packaging/ubuntu/20.04/rundeps | 2 + modules/dnstap/README.rst | 42 + modules/dnstap/dnstap.c | 524 ++ modules/dnstap/dnstap.proto | 273 + modules/dnstap/meson.build | 57 + modules/edns_keepalive/.packaging/test.config | 10 + modules/edns_keepalive/README.rst | 22 + modules/edns_keepalive/edns_keepalive.c | 61 + modules/edns_keepalive/meson.build | 17 + modules/etcd/.packaging/centos/7/pre-test.sh | 1 + modules/etcd/.packaging/centos/7/rundeps | 6 + modules/etcd/.packaging/centos/8/NOTSUPPORTED | 0 modules/etcd/.packaging/debian/10/pre-test.sh | 1 + modules/etcd/.packaging/debian/10/rundeps | 4 + modules/etcd/.packaging/debian/9/pre-test.sh | 1 + modules/etcd/.packaging/debian/9/rundeps | 4 + modules/etcd/.packaging/fedora/31/NOTSUPPORTED | 16 + modules/etcd/.packaging/fedora/32/NOTSUPPORTED | 16 + modules/etcd/.packaging/leap/15.2/pre-test.sh | 1 + modules/etcd/.packaging/leap/15.2/rundeps | 6 + modules/etcd/.packaging/test.config | 4 + modules/etcd/.packaging/ubuntu/16.04/pre-test.sh | 1 + modules/etcd/.packaging/ubuntu/16.04/rundeps | 3 + modules/etcd/.packaging/ubuntu/18.04/pre-test.sh | 1 + modules/etcd/.packaging/ubuntu/18.04/rundeps | 3 + modules/etcd/.packaging/ubuntu/20.04/pre-test.sh | 1 + modules/etcd/.packaging/ubuntu/20.04/rundeps | 4 + modules/etcd/README.rst | 46 + modules/etcd/etcd.lua | 56 + .../.packaging/centos/7/rundeps | 1 + .../.packaging/centos/8/rundeps | 1 + .../.packaging/debian/10/rundeps | 1 + .../.packaging/debian/9/rundeps | 1 + .../.packaging/fedora/31/rundeps | 1 + .../.packaging/fedora/32/rundeps | 1 + .../.packaging/leap/15.2/NOTSUPPORTED | 6 + .../.packaging/leap/15.2/pre-test.sh | 1 + .../.packaging/leap/15.2/rundeps | 4 + .../experimental_dot_auth/.packaging/test.config | 4 + .../.packaging/ubuntu/16.04/NOTSUPPORTED | 0 .../.packaging/ubuntu/18.04/rundeps | 1 + .../.packaging/ubuntu/20.04/rundeps | 1 + modules/experimental_dot_auth/README.rst | 91 + .../experimental_dot_auth.lua | 122 + modules/experimental_dot_auth/meson.build | 13 + modules/extended_error/extended_error.c | 47 + modules/extended_error/meson.build | 20 + modules/graphite/.packaging/centos/7/rundeps | 1 + modules/graphite/.packaging/centos/8/rundeps | 1 + modules/graphite/.packaging/debian/10/rundeps | 1 + modules/graphite/.packaging/debian/9/rundeps | 1 + modules/graphite/.packaging/fedora/31/rundeps | 1 + modules/graphite/.packaging/fedora/32/rundeps | 1 + modules/graphite/.packaging/leap/15.2/NOTSUPPORTED | 6 + modules/graphite/.packaging/leap/15.2/pre-test.sh | 1 + modules/graphite/.packaging/leap/15.2/rundeps | 6 + modules/graphite/.packaging/test.config | 4 + modules/graphite/.packaging/ubuntu/16.04/rundeps | 1 + modules/graphite/.packaging/ubuntu/18.04/rundeps | 1 + modules/graphite/.packaging/ubuntu/20.04/rundeps | 1 + modules/graphite/README.rst | 49 + modules/graphite/graphite.lua | 146 + modules/hints/.packaging/test.config | 4 + modules/hints/README.rst | 145 + modules/hints/hints.c | 677 +++ modules/hints/meson.build | 24 + modules/hints/tests/hints.test.hosts | 1 + modules/hints/tests/hints.test.lua | 64 + modules/hints/tests/hints_test.zone | 2 + modules/http/.packaging/centos/7/rundeps | 1 + modules/http/.packaging/centos/8/rundeps | 1 + modules/http/.packaging/debian/10/rundeps | 1 + modules/http/.packaging/debian/9/rundeps | 1 + modules/http/.packaging/fedora/31/rundeps | 1 + modules/http/.packaging/fedora/32/rundeps | 1 + modules/http/.packaging/leap/15.2/NOTSUPPORTED | 5 + modules/http/.packaging/leap/15.2/pre-test.sh | 1 + modules/http/.packaging/leap/15.2/rundeps | 7 + modules/http/.packaging/test.config | 4 + modules/http/.packaging/ubuntu/16.04/NOTSUPPORTED | 0 modules/http/.packaging/ubuntu/18.04/rundeps | 1 + modules/http/.packaging/ubuntu/20.04/rundeps | 1 + modules/http/README.rst | 188 + modules/http/custom_services.rst | 145 + modules/http/debug_opensslkeylog.c | 369 ++ modules/http/http.lua.in | 418 ++ modules/http/http.test.lua | 128 + modules/http/http_doh.lua | 116 + modules/http/http_doh.test.lua | 419 ++ modules/http/http_tls_cert.lua | 186 + modules/http/http_trace.lua | 77 + modules/http/meson.build | 61 + modules/http/prometheus.lua | 178 + modules/http/prometheus.rst | 45 + modules/http/static/bootstrap-theme.min.css | 6 + modules/http/static/bootstrap-theme.min.css.spdx | 11 + modules/http/static/bootstrap.min.css | 11 + modules/http/static/bootstrap.min.css.spdx | 11 + modules/http/static/bootstrap.min.js | 7 + modules/http/static/bootstrap.min.js.spdx | 11 + modules/http/static/d3.js | 6 + modules/http/static/d3.spdx | 12 + modules/http/static/datamaps.world.min.js | 3 + modules/http/static/datamaps.world.min.spdx | 11 + modules/http/static/dygraph.min.js | 7 + modules/http/static/dygraph.min.js.spdx | 12 + modules/http/static/epoch.css | 2 + modules/http/static/epoch.js | 4 + modules/http/static/epoch.spdx | 11 + modules/http/static/favicon.ico | Bin 0 -> 1545 bytes .../http/static/glyphicons-halflings-regular.spdx | 11 + .../http/static/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes modules/http/static/jquery.js | 5 + modules/http/static/jquery.spdx | 12 + modules/http/static/kresd.css | 44 + modules/http/static/kresd.js | 367 ++ modules/http/static/main.tpl | 87 + modules/http/static/selectize.bootstrap3.css | 418 ++ modules/http/static/selectize.min.js | 4 + modules/http/static/selectize.spdx | 11 + modules/http/static/topojson.js | 2 + modules/http/static/topojson.spdx | 12 + modules/http/test_tls/broken.crt | 3 + modules/http/test_tls/broken.key | Bin 0 -> 512 bytes modules/http/test_tls/ca.crt | 20 + modules/http/test_tls/chain.crt | 41 + modules/http/test_tls/test.crt | 20 + modules/http/test_tls/test.key | 27 + modules/http/test_tls/tls.test.lua | 193 + modules/http/trace.rst | 43 + modules/meson.build | 59 + modules/nsid/.packaging/test.config | 4 + modules/nsid/README.rst | 35 + modules/nsid/meson.build | 24 + modules/nsid/nsid.c | 114 + modules/nsid/nsid.test.lua | 22 + modules/policy/.packaging/test.config | 4 + modules/policy/README.rst | 774 +++ modules/policy/lua-aho-corasick/LICENSE | 28 + modules/policy/lua-aho-corasick/Makefile | 134 + modules/policy/lua-aho-corasick/README.md | 40 + modules/policy/lua-aho-corasick/ac.cxx | 101 + modules/policy/lua-aho-corasick/ac.h | 49 + modules/policy/lua-aho-corasick/ac_fast.cxx | 468 ++ modules/policy/lua-aho-corasick/ac_fast.hpp | 124 + modules/policy/lua-aho-corasick/ac_lua.cxx | 173 + modules/policy/lua-aho-corasick/ac_slow.cxx | 318 ++ modules/policy/lua-aho-corasick/ac_slow.hpp | 158 + modules/policy/lua-aho-corasick/ac_util.hpp | 69 + modules/policy/lua-aho-corasick/load_ac.lua | 90 + modules/policy/lua-aho-corasick/mytest.cxx | 200 + modules/policy/lua-aho-corasick/tests/Makefile | 65 + modules/policy/lua-aho-corasick/tests/ac_bench.cxx | 519 ++ .../policy/lua-aho-corasick/tests/ac_test_aggr.cxx | 135 + .../lua-aho-corasick/tests/ac_test_simple.cxx | 275 + .../policy/lua-aho-corasick/tests/dict/README.txt | 1 + .../policy/lua-aho-corasick/tests/dict/dict1.txt | 11 + .../policy/lua-aho-corasick/tests/load_ac_test.lua | 82 + modules/policy/lua-aho-corasick/tests/lua_test.lua | 67 + .../policy/lua-aho-corasick/tests/test_base.hpp | 60 + .../policy/lua-aho-corasick/tests/test_bigfile.cxx | 167 + .../policy/lua-aho-corasick/tests/test_main.cxx | 33 + modules/policy/meson.build | 50 + modules/policy/noipv6.test.integr/broken-ipv6.rpl | 47 + modules/policy/noipv6.test.integr/deckard.yaml | 12 + modules/policy/noipv6.test.integr/kresd_config.j2 | 59 + modules/policy/noipvx.test.integr/broken-ipvx.rpl | 35 + modules/policy/noipvx.test.integr/deckard.yaml | 12 + modules/policy/noipvx.test.integr/kresd_config.j2 | 60 + modules/policy/policy.lua | 1109 ++++ modules/policy/policy.rpz.test.lua | 65 + modules/policy/policy.slice.test.lua | 109 + modules/policy/policy.test.lua | 145 + modules/policy/policy.test.rpz | 18 + modules/policy/policy.test.rpz.soa | 5 + modules/policy/test.integr/deckard.yaml | 12 + modules/policy/test.integr/kresd_config.j2 | 59 + modules/policy/test.integr/refuse.rpl | 44 + modules/predict/.packaging/test.config | 4 + modules/predict/README.rst | 67 + modules/predict/predict.lua | 189 + modules/predict/predict.test.lua | 61 + modules/prefill/.packaging/test.config | 4 + modules/prefill/README.rst | 43 + modules/prefill/prefill.lua | 199 + modules/prefill/prefill.test/empty.zone | 0 modules/prefill/prefill.test/example.com.zone | 12 + modules/prefill/prefill.test/prefill.test.lua | 123 + modules/prefill/prefill.test/random.zone | 2 + modules/prefill/prefill.test/testroot.zone | 59 + .../prefill/prefill.test/testroot.zone.unsigned | 4 + modules/prefill/prefill.test/testroot_no_soa.zone | 52 + modules/priming/.packaging/test.config | 4 + modules/priming/README.rst | 18 + modules/priming/priming.lua | 130 + modules/rebinding/.packaging/test.config | 4 + modules/rebinding/README.rst | 29 + modules/rebinding/rebinding.lua | 115 + modules/rebinding/test.integr/deckard.yaml | 12 + modules/rebinding/test.integr/kresd_config.j2 | 59 + modules/rebinding/test.integr/module_rebinding.rpl | 834 +++ modules/refuse_nord/.packaging/test.config | 3 + modules/refuse_nord/README.rst | 16 + modules/refuse_nord/meson.build | 21 + modules/refuse_nord/refuse_nord.c | 38 + modules/refuse_nord/test.integr/deckard.yaml | 12 + modules/refuse_nord/test.integr/kresd_config.j2 | 56 + modules/refuse_nord/test.integr/refuse_nord.rpl | 24 + modules/renumber/.packaging/test.config | 4 + modules/renumber/README.rst | 36 + modules/renumber/renumber.lua | 181 + modules/renumber/renumber.test.lua | 103 + modules/rfc7706.rst | 12 + modules/serve_stale/.packaging/test.config | 4 + modules/serve_stale/README.rst | 23 + modules/serve_stale/serve_stale.lua | 42 + modules/serve_stale/test.integr/deckard.yaml | 12 + modules/serve_stale/test.integr/kresd_config.j2 | 70 + .../serve_stale/test.integr/module_serve_stale.rpl | 280 + modules/stats/.packaging/test.config | 4 + modules/stats/README.rst | 211 + modules/stats/meson.build | 25 + modules/stats/stats.c | 534 ++ modules/stats/test.integr/deckard.yaml | 12 + modules/stats/test.integr/kresd_config.j2 | 114 + modules/stats/test.integr/stats.rpl | 194 + modules/ta_sentinel/.packaging/test.config | 4 + modules/ta_sentinel/README.rst | 18 + modules/ta_sentinel/ta_sentinel.lua | 80 + modules/ta_signal_query/.packaging/test.config | 4 + modules/ta_signal_query/README.rst | 31 + modules/ta_signal_query/ta_signal_query.lua | 64 + modules/ta_update/.packaging/test.config | 4 + modules/ta_update/meson.build | 21 + modules/ta_update/root.keys | 1 + modules/ta_update/ta_update.lua | 349 ++ .../ta_update/ta_update.test.integr/deckard.yaml | 12 + .../ta_update.test.integr/kresd_config.j2 | 56 + .../rfc5011-monotonictime.rpl | 5755 ++++++++++++++++++++ .../ta_update/ta_update.test.integr/rfc5011/README | 13 + .../ta_update.test.integr/rfc5011/dns2rpl.py | 222 + .../ta_update.test.integr/rfc5011/empty.rpl | 20 + .../ta_update.test.integr/rfc5011/genkeyszones.sh | 174 + .../ta_update.test.integr/rfc5011/knot.root.conf | 26 + .../ta_update.test.integr/rfc5011/pydnstest | 1 + .../rfc5011/unsigned_check.db | 8 + .../ta_update.test.integr/rfc5011/unsigned_ok.db | 8 + .../rfc5011_unsupported_key_rollover.rpl | 91 + modules/ta_update/ta_update.test.lua | 84 + .../deckard.yaml | 12 + .../kresd_config.j2 | 72 + .../ta_update.unmanagedkey.test.integr/rfc5011 | 1 + .../unmanagedkey-missing-monotonictime.rpl | 758 +++ .../unmanagedkey-present-monotonictime.rpl | 757 +++ .../unmanagedkey-revoke-monotonictime.rpl | 762 +++ modules/view/.packaging/test.config | 4 + modules/view/README.rst | 92 + modules/view/addr.test.integr/deckard.yaml | 12 + modules/view/addr.test.integr/kresd_config.j2 | 62 + modules/view/addr.test.integr/module_view_addr.rpl | 79 + modules/view/meson.build | 11 + modules/view/tsig.test.integr/deckard.yaml | 12 + modules/view/tsig.test.integr/kresd_config.j2 | 64 + modules/view/tsig.test.integr/module_view_tsig.rpl | 114 + modules/view/view.lua | 121 + modules/watchdog/.packaging/test.config | 4 + modules/watchdog/README.rst | 43 + modules/watchdog/watchdog.lua | 129 + modules/workarounds/.packaging/test.config | 4 + modules/workarounds/README.rst | 11 + modules/workarounds/workarounds.lua | 23 + 325 files changed, 30783 insertions(+) create mode 100644 modules/README.rst create mode 100644 modules/bogus_log/.packaging/test.config create mode 100644 modules/bogus_log/README.rst create mode 100644 modules/bogus_log/bogus_log.c create mode 100644 modules/bogus_log/meson.build create mode 100644 modules/bogus_log/test.integr/deckard.yaml create mode 100644 modules/bogus_log/test.integr/kresd_config.j2 create mode 100644 modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl create mode 100644 modules/cookies/README.rst create mode 100644 modules/cookies/cookiectl.c create mode 100644 modules/cookies/cookiectl.h create mode 100644 modules/cookies/cookiemonster.c create mode 100644 modules/cookies/cookiemonster.h create mode 100644 modules/cookies/cookies.c create mode 100644 modules/daf/.packaging/test.config create mode 100644 modules/daf/README.rst create mode 100644 modules/daf/daf.js create mode 100644 modules/daf/daf.lua create mode 100644 modules/daf/daf.test.lua create mode 100644 modules/daf/daf_http.test.lua create mode 100644 modules/daf/meson.build create mode 100644 modules/daf/test.integr/deckard.yaml create mode 100644 modules/daf/test.integr/kresd_config.j2 create mode 100644 modules/daf/test.integr/module_daf.rpl create mode 100644 modules/detect_time_jump/.packaging/test.config create mode 100644 modules/detect_time_jump/README.rst create mode 100644 modules/detect_time_jump/detect_time_jump.lua create mode 100644 modules/detect_time_skew/.packaging/test.config create mode 100644 modules/detect_time_skew/README.rst create mode 100644 modules/detect_time_skew/detect_time_skew.lua create mode 100644 modules/dns64/.packaging/test.config create mode 100644 modules/dns64/README.rst create mode 100644 modules/dns64/dns64.lua create mode 100644 modules/dns64/dns64.test.lua create mode 100644 modules/dnstap/.packaging/centos/7/builddeps create mode 100644 modules/dnstap/.packaging/centos/7/rundeps create mode 100644 modules/dnstap/.packaging/centos/8/builddeps create mode 100644 modules/dnstap/.packaging/centos/8/rundeps create mode 100644 modules/dnstap/.packaging/debian/10/builddeps create mode 100644 modules/dnstap/.packaging/debian/10/rundeps create mode 100644 modules/dnstap/.packaging/debian/9/builddeps create mode 100644 modules/dnstap/.packaging/debian/9/rundeps create mode 100644 modules/dnstap/.packaging/fedora/31/builddeps create mode 100644 modules/dnstap/.packaging/fedora/31/rundeps create mode 100644 modules/dnstap/.packaging/fedora/32/builddeps create mode 100644 modules/dnstap/.packaging/fedora/32/rundeps create mode 100644 modules/dnstap/.packaging/leap/15.2/builddeps create mode 100644 modules/dnstap/.packaging/leap/15.2/rundeps create mode 100644 modules/dnstap/.packaging/test.config create mode 100644 modules/dnstap/.packaging/ubuntu/16.04/builddeps create mode 100644 modules/dnstap/.packaging/ubuntu/16.04/rundeps create mode 100644 modules/dnstap/.packaging/ubuntu/18.04/builddeps create mode 100644 modules/dnstap/.packaging/ubuntu/18.04/rundeps create mode 100644 modules/dnstap/.packaging/ubuntu/20.04/builddeps create mode 100644 modules/dnstap/.packaging/ubuntu/20.04/rundeps create mode 100644 modules/dnstap/README.rst create mode 100644 modules/dnstap/dnstap.c create mode 100644 modules/dnstap/dnstap.proto create mode 100644 modules/dnstap/meson.build create mode 100644 modules/edns_keepalive/.packaging/test.config create mode 100644 modules/edns_keepalive/README.rst create mode 100644 modules/edns_keepalive/edns_keepalive.c create mode 100644 modules/edns_keepalive/meson.build create mode 100755 modules/etcd/.packaging/centos/7/pre-test.sh create mode 100644 modules/etcd/.packaging/centos/7/rundeps create mode 100644 modules/etcd/.packaging/centos/8/NOTSUPPORTED create mode 100755 modules/etcd/.packaging/debian/10/pre-test.sh create mode 100644 modules/etcd/.packaging/debian/10/rundeps create mode 100755 modules/etcd/.packaging/debian/9/pre-test.sh create mode 100644 modules/etcd/.packaging/debian/9/rundeps create mode 100644 modules/etcd/.packaging/fedora/31/NOTSUPPORTED create mode 100644 modules/etcd/.packaging/fedora/32/NOTSUPPORTED create mode 100755 modules/etcd/.packaging/leap/15.2/pre-test.sh create mode 100644 modules/etcd/.packaging/leap/15.2/rundeps create mode 100644 modules/etcd/.packaging/test.config create mode 100755 modules/etcd/.packaging/ubuntu/16.04/pre-test.sh create mode 100644 modules/etcd/.packaging/ubuntu/16.04/rundeps create mode 100755 modules/etcd/.packaging/ubuntu/18.04/pre-test.sh create mode 100644 modules/etcd/.packaging/ubuntu/18.04/rundeps create mode 100755 modules/etcd/.packaging/ubuntu/20.04/pre-test.sh create mode 100644 modules/etcd/.packaging/ubuntu/20.04/rundeps create mode 100644 modules/etcd/README.rst create mode 100644 modules/etcd/etcd.lua create mode 100644 modules/experimental_dot_auth/.packaging/centos/7/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/centos/8/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/debian/10/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/debian/9/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/fedora/31/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/fedora/32/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/leap/15.2/NOTSUPPORTED create mode 100755 modules/experimental_dot_auth/.packaging/leap/15.2/pre-test.sh create mode 100644 modules/experimental_dot_auth/.packaging/leap/15.2/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/test.config create mode 100644 modules/experimental_dot_auth/.packaging/ubuntu/16.04/NOTSUPPORTED create mode 100644 modules/experimental_dot_auth/.packaging/ubuntu/18.04/rundeps create mode 100644 modules/experimental_dot_auth/.packaging/ubuntu/20.04/rundeps create mode 100644 modules/experimental_dot_auth/README.rst create mode 100644 modules/experimental_dot_auth/experimental_dot_auth.lua create mode 100644 modules/experimental_dot_auth/meson.build create mode 100644 modules/extended_error/extended_error.c create mode 100644 modules/extended_error/meson.build create mode 100644 modules/graphite/.packaging/centos/7/rundeps create mode 100644 modules/graphite/.packaging/centos/8/rundeps create mode 100644 modules/graphite/.packaging/debian/10/rundeps create mode 100644 modules/graphite/.packaging/debian/9/rundeps create mode 100644 modules/graphite/.packaging/fedora/31/rundeps create mode 100644 modules/graphite/.packaging/fedora/32/rundeps create mode 100644 modules/graphite/.packaging/leap/15.2/NOTSUPPORTED create mode 100755 modules/graphite/.packaging/leap/15.2/pre-test.sh create mode 100644 modules/graphite/.packaging/leap/15.2/rundeps create mode 100644 modules/graphite/.packaging/test.config create mode 100644 modules/graphite/.packaging/ubuntu/16.04/rundeps create mode 100644 modules/graphite/.packaging/ubuntu/18.04/rundeps create mode 100644 modules/graphite/.packaging/ubuntu/20.04/rundeps create mode 100644 modules/graphite/README.rst create mode 100644 modules/graphite/graphite.lua create mode 100644 modules/hints/.packaging/test.config create mode 100644 modules/hints/README.rst create mode 100644 modules/hints/hints.c create mode 100644 modules/hints/meson.build create mode 100644 modules/hints/tests/hints.test.hosts create mode 100644 modules/hints/tests/hints.test.lua create mode 100644 modules/hints/tests/hints_test.zone create mode 100644 modules/http/.packaging/centos/7/rundeps create mode 100644 modules/http/.packaging/centos/8/rundeps create mode 100644 modules/http/.packaging/debian/10/rundeps create mode 100644 modules/http/.packaging/debian/9/rundeps create mode 100644 modules/http/.packaging/fedora/31/rundeps create mode 100644 modules/http/.packaging/fedora/32/rundeps create mode 100644 modules/http/.packaging/leap/15.2/NOTSUPPORTED create mode 100755 modules/http/.packaging/leap/15.2/pre-test.sh create mode 100644 modules/http/.packaging/leap/15.2/rundeps create mode 100644 modules/http/.packaging/test.config create mode 100644 modules/http/.packaging/ubuntu/16.04/NOTSUPPORTED create mode 100644 modules/http/.packaging/ubuntu/18.04/rundeps create mode 100644 modules/http/.packaging/ubuntu/20.04/rundeps create mode 100644 modules/http/README.rst create mode 100644 modules/http/custom_services.rst create mode 100644 modules/http/debug_opensslkeylog.c create mode 100644 modules/http/http.lua.in create mode 100644 modules/http/http.test.lua create mode 100644 modules/http/http_doh.lua create mode 100644 modules/http/http_doh.test.lua create mode 100644 modules/http/http_tls_cert.lua create mode 100644 modules/http/http_trace.lua create mode 100644 modules/http/meson.build create mode 100644 modules/http/prometheus.lua create mode 100644 modules/http/prometheus.rst create mode 100644 modules/http/static/bootstrap-theme.min.css create mode 100644 modules/http/static/bootstrap-theme.min.css.spdx create mode 100644 modules/http/static/bootstrap.min.css create mode 100644 modules/http/static/bootstrap.min.css.spdx create mode 100644 modules/http/static/bootstrap.min.js create mode 100644 modules/http/static/bootstrap.min.js.spdx create mode 100644 modules/http/static/d3.js create mode 100644 modules/http/static/d3.spdx create mode 100644 modules/http/static/datamaps.world.min.js create mode 100644 modules/http/static/datamaps.world.min.spdx create mode 100644 modules/http/static/dygraph.min.js create mode 100644 modules/http/static/dygraph.min.js.spdx create mode 100644 modules/http/static/epoch.css create mode 100644 modules/http/static/epoch.js create mode 100644 modules/http/static/epoch.spdx create mode 100644 modules/http/static/favicon.ico create mode 100644 modules/http/static/glyphicons-halflings-regular.spdx create mode 100644 modules/http/static/glyphicons-halflings-regular.woff2 create mode 100644 modules/http/static/jquery.js create mode 100644 modules/http/static/jquery.spdx create mode 100644 modules/http/static/kresd.css create mode 100644 modules/http/static/kresd.js create mode 100644 modules/http/static/main.tpl create mode 100644 modules/http/static/selectize.bootstrap3.css create mode 100644 modules/http/static/selectize.min.js create mode 100644 modules/http/static/selectize.spdx create mode 100644 modules/http/static/topojson.js create mode 100644 modules/http/static/topojson.spdx create mode 100644 modules/http/test_tls/broken.crt create mode 100644 modules/http/test_tls/broken.key create mode 100644 modules/http/test_tls/ca.crt create mode 100644 modules/http/test_tls/chain.crt create mode 100644 modules/http/test_tls/test.crt create mode 100644 modules/http/test_tls/test.key create mode 100644 modules/http/test_tls/tls.test.lua create mode 100644 modules/http/trace.rst create mode 100644 modules/meson.build create mode 100644 modules/nsid/.packaging/test.config create mode 100644 modules/nsid/README.rst create mode 100644 modules/nsid/meson.build create mode 100644 modules/nsid/nsid.c create mode 100644 modules/nsid/nsid.test.lua create mode 100644 modules/policy/.packaging/test.config create mode 100644 modules/policy/README.rst create mode 100644 modules/policy/lua-aho-corasick/LICENSE create mode 100644 modules/policy/lua-aho-corasick/Makefile create mode 100644 modules/policy/lua-aho-corasick/README.md create mode 100644 modules/policy/lua-aho-corasick/ac.cxx create mode 100644 modules/policy/lua-aho-corasick/ac.h create mode 100644 modules/policy/lua-aho-corasick/ac_fast.cxx create mode 100644 modules/policy/lua-aho-corasick/ac_fast.hpp create mode 100644 modules/policy/lua-aho-corasick/ac_lua.cxx create mode 100644 modules/policy/lua-aho-corasick/ac_slow.cxx create mode 100644 modules/policy/lua-aho-corasick/ac_slow.hpp create mode 100644 modules/policy/lua-aho-corasick/ac_util.hpp create mode 100644 modules/policy/lua-aho-corasick/load_ac.lua create mode 100644 modules/policy/lua-aho-corasick/mytest.cxx create mode 100644 modules/policy/lua-aho-corasick/tests/Makefile create mode 100644 modules/policy/lua-aho-corasick/tests/ac_bench.cxx create mode 100644 modules/policy/lua-aho-corasick/tests/ac_test_aggr.cxx create mode 100644 modules/policy/lua-aho-corasick/tests/ac_test_simple.cxx create mode 100644 modules/policy/lua-aho-corasick/tests/dict/README.txt create mode 100644 modules/policy/lua-aho-corasick/tests/dict/dict1.txt create mode 100644 modules/policy/lua-aho-corasick/tests/load_ac_test.lua create mode 100644 modules/policy/lua-aho-corasick/tests/lua_test.lua create mode 100644 modules/policy/lua-aho-corasick/tests/test_base.hpp create mode 100644 modules/policy/lua-aho-corasick/tests/test_bigfile.cxx create mode 100644 modules/policy/lua-aho-corasick/tests/test_main.cxx create mode 100644 modules/policy/meson.build create mode 100644 modules/policy/noipv6.test.integr/broken-ipv6.rpl create mode 100644 modules/policy/noipv6.test.integr/deckard.yaml create mode 100644 modules/policy/noipv6.test.integr/kresd_config.j2 create mode 100644 modules/policy/noipvx.test.integr/broken-ipvx.rpl create mode 100644 modules/policy/noipvx.test.integr/deckard.yaml create mode 100644 modules/policy/noipvx.test.integr/kresd_config.j2 create mode 100644 modules/policy/policy.lua create mode 100644 modules/policy/policy.rpz.test.lua create mode 100644 modules/policy/policy.slice.test.lua create mode 100644 modules/policy/policy.test.lua create mode 100644 modules/policy/policy.test.rpz create mode 100644 modules/policy/policy.test.rpz.soa create mode 100644 modules/policy/test.integr/deckard.yaml create mode 100644 modules/policy/test.integr/kresd_config.j2 create mode 100644 modules/policy/test.integr/refuse.rpl create mode 100644 modules/predict/.packaging/test.config create mode 100644 modules/predict/README.rst create mode 100644 modules/predict/predict.lua create mode 100644 modules/predict/predict.test.lua create mode 100644 modules/prefill/.packaging/test.config create mode 100644 modules/prefill/README.rst create mode 100644 modules/prefill/prefill.lua create mode 100644 modules/prefill/prefill.test/empty.zone create mode 100644 modules/prefill/prefill.test/example.com.zone create mode 100644 modules/prefill/prefill.test/prefill.test.lua create mode 100644 modules/prefill/prefill.test/random.zone create mode 100644 modules/prefill/prefill.test/testroot.zone create mode 100644 modules/prefill/prefill.test/testroot.zone.unsigned create mode 100644 modules/prefill/prefill.test/testroot_no_soa.zone create mode 100644 modules/priming/.packaging/test.config create mode 100644 modules/priming/README.rst create mode 100644 modules/priming/priming.lua create mode 100644 modules/rebinding/.packaging/test.config create mode 100644 modules/rebinding/README.rst create mode 100644 modules/rebinding/rebinding.lua create mode 100644 modules/rebinding/test.integr/deckard.yaml create mode 100644 modules/rebinding/test.integr/kresd_config.j2 create mode 100644 modules/rebinding/test.integr/module_rebinding.rpl create mode 100644 modules/refuse_nord/.packaging/test.config create mode 100644 modules/refuse_nord/README.rst create mode 100644 modules/refuse_nord/meson.build create mode 100644 modules/refuse_nord/refuse_nord.c create mode 100644 modules/refuse_nord/test.integr/deckard.yaml create mode 100644 modules/refuse_nord/test.integr/kresd_config.j2 create mode 100644 modules/refuse_nord/test.integr/refuse_nord.rpl create mode 100644 modules/renumber/.packaging/test.config create mode 100644 modules/renumber/README.rst create mode 100644 modules/renumber/renumber.lua create mode 100644 modules/renumber/renumber.test.lua create mode 100644 modules/rfc7706.rst create mode 100644 modules/serve_stale/.packaging/test.config create mode 100644 modules/serve_stale/README.rst create mode 100644 modules/serve_stale/serve_stale.lua create mode 100644 modules/serve_stale/test.integr/deckard.yaml create mode 100644 modules/serve_stale/test.integr/kresd_config.j2 create mode 100644 modules/serve_stale/test.integr/module_serve_stale.rpl create mode 100644 modules/stats/.packaging/test.config create mode 100644 modules/stats/README.rst create mode 100644 modules/stats/meson.build create mode 100644 modules/stats/stats.c create mode 100644 modules/stats/test.integr/deckard.yaml create mode 100644 modules/stats/test.integr/kresd_config.j2 create mode 100644 modules/stats/test.integr/stats.rpl create mode 100644 modules/ta_sentinel/.packaging/test.config create mode 100644 modules/ta_sentinel/README.rst create mode 100644 modules/ta_sentinel/ta_sentinel.lua create mode 100644 modules/ta_signal_query/.packaging/test.config create mode 100644 modules/ta_signal_query/README.rst create mode 100644 modules/ta_signal_query/ta_signal_query.lua create mode 100644 modules/ta_update/.packaging/test.config create mode 100644 modules/ta_update/meson.build create mode 100644 modules/ta_update/root.keys create mode 100644 modules/ta_update/ta_update.lua create mode 100644 modules/ta_update/ta_update.test.integr/deckard.yaml create mode 100644 modules/ta_update/ta_update.test.integr/kresd_config.j2 create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011-monotonictime.rpl create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011/README create mode 100755 modules/ta_update/ta_update.test.integr/rfc5011/dns2rpl.py create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011/empty.rpl create mode 100755 modules/ta_update/ta_update.test.integr/rfc5011/genkeyszones.sh create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011/knot.root.conf create mode 120000 modules/ta_update/ta_update.test.integr/rfc5011/pydnstest create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011/unsigned_check.db create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011/unsigned_ok.db create mode 100644 modules/ta_update/ta_update.test.integr/rfc5011_unsupported_key_rollover.rpl create mode 100644 modules/ta_update/ta_update.test.lua create mode 100644 modules/ta_update/ta_update.unmanagedkey.test.integr/deckard.yaml create mode 100644 modules/ta_update/ta_update.unmanagedkey.test.integr/kresd_config.j2 create mode 120000 modules/ta_update/ta_update.unmanagedkey.test.integr/rfc5011 create mode 100644 modules/ta_update/ta_update.unmanagedkey.test.integr/unmanagedkey-missing-monotonictime.rpl create mode 100644 modules/ta_update/ta_update.unmanagedkey.test.integr/unmanagedkey-present-monotonictime.rpl create mode 100644 modules/ta_update/ta_update.unmanagedkey.test.integr/unmanagedkey-revoke-monotonictime.rpl create mode 100644 modules/view/.packaging/test.config create mode 100644 modules/view/README.rst create mode 100644 modules/view/addr.test.integr/deckard.yaml create mode 100644 modules/view/addr.test.integr/kresd_config.j2 create mode 100644 modules/view/addr.test.integr/module_view_addr.rpl create mode 100644 modules/view/meson.build create mode 100644 modules/view/tsig.test.integr/deckard.yaml create mode 100644 modules/view/tsig.test.integr/kresd_config.j2 create mode 100644 modules/view/tsig.test.integr/module_view_tsig.rpl create mode 100644 modules/view/view.lua create mode 100644 modules/watchdog/.packaging/test.config create mode 100644 modules/watchdog/README.rst create mode 100644 modules/watchdog/watchdog.lua create mode 100644 modules/workarounds/.packaging/test.config create mode 100644 modules/workarounds/README.rst create mode 100644 modules/workarounds/workarounds.lua (limited to 'modules') diff --git a/modules/README.rst b/modules/README.rst new file mode 100644 index 0000000..1096c37 --- /dev/null +++ b/modules/README.rst @@ -0,0 +1,251 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _modules-api: + +********************* +Modules API reference +********************* + +.. contents:: + :depth: 1 + :local: + +Supported languages +=================== + +Currently modules written in C and Lua(JIT) are supported. + +The anatomy of an extension +=========================== + +A module is a shared object or script defining specific functions/fields; here's an overview. + +.. csv-table:: + :header: "C", "Lua", "Params", "Comment" + + "``X_api()`` [#]_", "", "", "API version" + "``X_init()``", "``X.init()``", "``module``", "Constructor" + "``X_deinit()``", "``X.deinit()``", "``module``", "Destructor" + "``X_config()``", "``X.config()``", "``module, str``", "Configuration" + "``X_layer``", "``X.layer``", "", ":ref:`Module layer `" + "``X_props``", "", "", "List of properties" + +.. [#] Mandatory symbol; defined by using :c:func:`KR_MODULE_EXPORT`. + +The ``X`` corresponds to the module name; if the module name is ``hints``, the prefix for constructor would be ``hints_init()``. +More details are in docs for the :c:type:`kr_module` and :c:type:`kr_layer_api` structures. + +.. note:: + The modules get ordered -- by default in the same as the order in which they were loaded. The loading command can specify where in the order the module should be positioned. + + +Writing a module in Lua +======================= + +The probably most convenient way of writing modules is Lua since you can use already installed modules +from system and have first-class access to the scripting engine. You can also tap to all the events, that +the C API has access to, but keep in mind that transitioning from the C to Lua function is slower than +the other way round, especially when JIT-compilation is taken into account. + +.. note:: The Lua functions retrieve an additional first parameter compared to the C counterparts - a "state". + Most useful C functions and structures have lua FFI wrappers, sometimes with extra sugar. + +The modules follow the `Lua way `_, where the module interface is returned in a named table. + +.. code-block:: lua + + --- @module Count incoming queries + local counter = {} + + function counter.init(module) + counter.total = 0 + counter.last = 0 + counter.failed = 0 + end + + function counter.deinit(module) + print('counted', counter.total, 'queries') + end + + -- @function Run the q/s counter with given interval. + function counter.config(conf) + -- We can use the scripting facilities here + if counter.ev then event.cancel(counter.ev) + event.recurrent(conf.interval, function () + print(counter.total - counter.last, 'q/s') + counter.last = counter.total + end) + end + + return counter + +.. vv Hmm, we do not use these coroutine returns anywhere, so it's unclear whether they still work OK. Splitting work over time is now typically done via the ``event`` timers. + +.. The API functions may return an integer value just like in other languages, but they may also return a coroutine that will be continued asynchronously. A good use case for this approach is is a deferred initialization, e.g. loading a chunks of data or waiting for I/O. + +.. .. code-block:: lua + + function counter.init(module) + counter.total = 0 + counter.last = 0 + counter.failed = 0 + return coroutine.create(function () + for line in io.lines('/etc/hosts') do + load(module, line) + coroutine.yield() + end + end) + end + +The created module can be then loaded just like any other module, except it isn't very useful since it +doesn't provide any layer to capture events. The Lua module can however provide a processing layer, just +:ref:`like its C counterpart `. + +.. code-block:: lua + + -- Notice it isn't a function, but a table of functions + counter.layer = { + begin = function (state, data) + counter.total = counter.total + 1 + return state + end, + finish = function (state, req, answer) + if state == kres.FAIL then + counter.failed = counter.failed + 1 + end + return state + end + } + +There is currently an additional "feature" in comparison to C layer functions: +some functions do not get called at all if ``state == kres.FAIL``; +see docs for details: :c:type:`kr_layer_api`. + +Since the modules are like any other Lua modules, you can interact with them through the CLI and and any interface. + +.. tip:: Module discovery: ``kres_modules.`` is prepended to the module name and lua search path is used on that. + + +Writing a module in C +===================== + +As almost all the functions are optional, the minimal module looks like this: + +.. code-block:: c + + #include "lib/module.h" + /* Convenience macro to declare module ABI. */ + KR_MODULE_EXPORT(mymodule) + +.. TODO it's probably not a good idea to start C module tutorial by pthread_create() + +Let's define an observer thread for the module as well. It's going to be stub for the sake of brevity, +but you can for example create a condition, and notify the thread from query processing by declaring +module layer (see the :ref:`Writing layers `). + +.. code-block:: c + + static void* observe(void *arg) + { + /* ... do some observing ... */ + } + + int mymodule_init(struct kr_module *module) + { + /* Create a thread and start it in the background. */ + pthread_t thr_id; + int ret = pthread_create(&thr_id, NULL, &observe, NULL); + if (ret != 0) { + return kr_error(errno); + } + + /* Keep it in the thread */ + module->data = thr_id; + return kr_ok(); + } + + int mymodule_deinit(struct kr_module *module) + { + /* ... signalize cancellation ... */ + void *res = NULL; + pthread_t thr_id = (pthread_t) module->data; + int ret = pthread_join(thr_id, res); + if (ret != 0) { + return kr_error(errno); + } + + return kr_ok(); + } + +This example shows how a module can run in the background, this enables you to, for example, observe +and publish data about query resolution. + +Configuring modules +=================== + +There is a callback ``X_config()`` that you can implement, see hints module. + +.. _mod-properties: + +Exposing C module properties +============================ + +A module can offer NULL-terminated list of *properties*, each property is essentially a callable with free-form JSON input/output. +JSON was chosen as an interchangeable format that doesn't require any schema beforehand, so you can do two things - query the module properties +from external applications or between modules (e.g. `statistics` module can query `cache` module for memory usage). +JSON was chosen not because it's the most efficient protocol, but because it's easy to read and write and interface to outside world. + +.. note:: The ``void *env`` is a generic module interface. Since we're implementing daemon modules, the pointer can be cast to ``struct engine*``. + This is guaranteed by the implemented API version (see `Writing a module in C`_). + +Here's an example how a module can expose its property: + +.. code-block:: c + + char* get_size(void *env, struct kr_module *m, + const char *args) + { + /* Get cache from engine. */ + struct engine *engine = env; + struct kr_cache *cache = &engine->resolver.cache; + /* Read item count */ + int count = (cache->api)->count(cache->db); + char *result = NULL; + asprintf(&result, "{ \"result\": %d }", count); + + return result; + } + + struct kr_prop *cache_props(void) + { + static struct kr_prop prop_list[] = { + /* Callback, Name, Description */ + {&get_size, "get_size", "Return number of records."}, + {NULL, NULL, NULL} + }; + return prop_list; + } + + KR_MODULE_EXPORT(cache) + +Once you load the module, you can call the module property from the interactive console. +*Note:* the JSON output will be transparently converted to Lua tables. + +.. code-block:: bash + + $ kresd + ... + [system] started in interactive mode, type 'help()' + > modules.load('cached') + > cached.get_size() + [size] => 53 + +.. No idea what this talks about, but kept for now: +.. *Note:* this relies on function pointers, so the same ``static inline`` trick as for the ``Layer()`` is required for C. + +Special properties +------------------ + +If the module declares properties ``get`` or ``set``, they can be used in the Lua interpreter as +regular tables. + diff --git a/modules/bogus_log/.packaging/test.config b/modules/bogus_log/.packaging/test.config new file mode 100644 index 0000000..bf1c821 --- /dev/null +++ b/modules/bogus_log/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('bogus_log') +assert(bogus_log) +quit() diff --git a/modules/bogus_log/README.rst b/modules/bogus_log/README.rst new file mode 100644 index 0000000..d60c278 --- /dev/null +++ b/modules/bogus_log/README.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-bogus_log: + +DNSSEC validation failure logging +================================= + +This module logs a message for each DNSSEC validation failure (on ``notice`` :func:`level `). +It is meant to provide hint to operators which queries should be +investigated using diagnostic tools like DNSViz_. + +Add following line to your configuration file to enable it: + +.. code-block:: lua + + modules.load('bogus_log') + +Example of error message logged by this module: + +.. code-block:: none + + [dnssec] validation failure: dnssec-failed.org. DNSKEY + +.. _DNSViz: http://dnsviz.net/ + +List of most frequent queries which fail as DNSSEC bogus can be obtained at run-time: + +.. code-block:: lua + + > bogus_log.frequent() + { + { + ['count'] = 1, + ['name'] = 'dnssec-failed.org.', + ['type'] = 'DNSKEY', + }, + { + ['count'] = 13, + ['name'] = 'rhybar.cz.', + ['type'] = 'DNSKEY', + }, + } + +Please note that in future this module might be replaced +with some other way to log this information. diff --git a/modules/bogus_log/bogus_log.c b/modules/bogus_log/bogus_log.c new file mode 100644 index 0000000..7b36187 --- /dev/null +++ b/modules/bogus_log/bogus_log.c @@ -0,0 +1,135 @@ +/* Copyright (C) Knot Resolver contributors + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This module logs (query name, type) pairs which failed DNSSEC validation. */ + +#include +#include +#include +#include + +#include "daemon/engine.h" +#include "lib/layer.h" +#include "lib/generic/lru.h" + +#ifdef LRU_REP_SIZE + #define FREQUENT_COUNT LRU_REP_SIZE /* Size of frequent tables */ +#else + #define FREQUENT_COUNT 5000 /* Size of frequent tables */ +#endif + +/** @internal LRU hash of most frequent names. */ +typedef lru_t(unsigned) namehash_t; + +/** @internal Stats data structure. */ +struct stat_data { + namehash_t *frequent; +}; + +static int consume(kr_layer_t *ctx, knot_pkt_t *pkt) +{ + if (!(ctx->state & KR_STATE_FAIL) + || !ctx->req + || !ctx->req->current_query + || !ctx->req->current_query->flags.DNSSEC_BOGUS + || knot_wire_get_qdcount(pkt->wire) != 1) + return ctx->state; + + auto_free char *qname_text = kr_dname_text(knot_pkt_qname(pkt)); + auto_free char *qtype_text = kr_rrtype_text(knot_pkt_qtype(pkt)); + + kr_log_notice(DNSSEC, "validation failure: %s %s\n", qname_text, qtype_text); + + /* log of most frequent bogus queries */ + uint16_t type = knot_pkt_qtype(pkt); + char key[sizeof(type) + KNOT_DNAME_MAXLEN]; + memcpy(key, &type, sizeof(type)); + int key_len = knot_dname_to_wire((uint8_t *)key + sizeof(type), knot_pkt_qname(pkt), KNOT_DNAME_MAXLEN); + if (key_len >= 0) { + struct kr_module *module = ctx->api->data; + struct stat_data *data = module->data; + unsigned *count = lru_get_new(data->frequent, key, key_len+sizeof(type), NULL); + if (count) + *count += 1; + } + + return ctx->state; +} + +/** @internal Helper for dump_list: add a single namehash_t item to JSON. */ +static enum lru_apply_do dump_value(const char *key, uint len, unsigned *val, void *baton) +{ + uint16_t key_type = 0; + /* Extract query name, type and counter */ + memcpy(&key_type, key, sizeof(key_type)); + KR_DNAME_GET_STR(key_name, (uint8_t *)key + sizeof(key_type)); + KR_RRTYPE_GET_STR(type_str, key_type); + + /* Convert to JSON object */ + JsonNode *json_val = json_mkobject(); + json_append_member(json_val, "count", json_mknumber(*val)); + json_append_member(json_val, "name", json_mkstring(key_name)); + json_append_member(json_val, "type", json_mkstring(type_str)); + json_append_element((JsonNode *)baton, json_val); + return LRU_APPLY_DO_NOTHING; // keep the item +} + +/** + * List frequent names. + * + * Output: [{ count: , name: , type: }, ... ] + */ +static char* dump_list(void *env, struct kr_module *module, const char *args, namehash_t *table) +{ + if (!table) { + return NULL; + } + JsonNode *root = json_mkarray(); + lru_apply(table, dump_value, root); + char *ret = json_encode(root); + json_delete(root); + return ret; +} + +static char* dump_frequent(void *env, struct kr_module *module, const char *args) +{ + struct stat_data *data = module->data; + return dump_list(env, module, args, data->frequent); +} + +KR_EXPORT +int bogus_log_init(struct kr_module *module) +{ + static kr_layer_api_t layer = { + .consume = &consume, + }; + layer.data = module; + module->layer = &layer; + + static const struct kr_prop props[] = { + { &dump_frequent, "frequent", "List most frequent queries.", }, + { NULL, NULL, NULL } + }; + module->props = props; + + struct stat_data *data = calloc(1, sizeof(*data)); + if (!data) { + return kr_error(ENOMEM); + } + module->data = data; + lru_create(&data->frequent, FREQUENT_COUNT, NULL, NULL); + return kr_ok(); +} + +KR_EXPORT +int bogus_log_deinit(struct kr_module *module) +{ + struct stat_data *data = module->data; + if (data) { + lru_free(data->frequent); + free(data); + } + return kr_ok(); +} + +KR_MODULE_EXPORT(bogus_log) diff --git a/modules/bogus_log/meson.build b/modules/bogus_log/meson.build new file mode 100644 index 0000000..2dcf87f --- /dev/null +++ b/modules/bogus_log/meson.build @@ -0,0 +1,21 @@ +# C module: bogus_log +# SPDX-License-Identifier: GPL-3.0-or-later + +bogus_log_src = files([ + 'bogus_log.c', +]) +c_src_lint += bogus_log_src + +bogus_log_mod = shared_module( + 'bogus_log', + bogus_log_src, + dependencies: libknot, + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, +) + +integr_tests += [ + ['bogus_log', meson.current_source_dir() / 'test.integr'], +] diff --git a/modules/bogus_log/test.integr/deckard.yaml b/modules/bogus_log/test.integr/deckard.yaml new file mode 100644 index 0000000..ecd6cc5 --- /dev/null +++ b/modules/bogus_log/test.integr/deckard.yaml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +programs: +- name: kresd + binary: kresd + additional: + - --noninteractive + templates: + - modules/bogus_log/test.integr/kresd_config.j2 + - tests/integration/hints_zone.j2 + configs: + - config + - hints +noclean: True diff --git a/modules/bogus_log/test.integr/kresd_config.j2 b/modules/bogus_log/test.integr/kresd_config.j2 new file mode 100644 index 0000000..0471a27 --- /dev/null +++ b/modules/bogus_log/test.integr/kresd_config.j2 @@ -0,0 +1,90 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local ffi = require('ffi') + +{% for TAF in TRUST_ANCHOR_FILES %} +trust_anchors.add_file('{{TAF}}') +{% endfor %} + +{% raw %} +modules.load('bogus_log') + +function check_stats(got) + log_info(ffi.C.LOG_GRP_TESTS, 'checking if bogus_log.frequent values match expected values:') + local expected = { + [1] = { + ['type'] = 'DNSKEY', + ['count'] = 2, + ['name'] = '.', + } + } + print(table_print(expected)) + + if table_print(expected) == table_print(got) then + log_info(ffi.C.LOG_GRP_TESTS, 'no problem found') + return policy.DENY_MSG('Ok') + else + log_info(ffi.C.LOG_GRP_TESTS, 'mismatch!') + return policy.DENY_MSG('bogus_log.frequent mismatch, see logs') + end +end + +function reply_result(state, req) + local got = bogus_log.frequent() + print('current bogus_log.frequent() values:') + print(table_print(got)) + local result = check_stats(got) + return result(state, req) +end +policy.add(policy.pattern(reply_result, 'bogus_log.test.')) + +-- Disable RFC5011 TA update +if ta_update then + modules.unload('ta_update') +end + +-- Disable RFC8145 signaling, scenario doesn't provide expected answers +if ta_signal_query then + modules.unload('ta_signal_query') +end + +-- Disable RFC8109 priming, scenario doesn't provide expected answers +if priming then + modules.unload('priming') +end + +-- Disable this module because it make one priming query +if detect_time_skew then + modules.unload('detect_time_skew') +end + +_hint_root_file('hints') +cache.size = 2*MB +log_level('debug') +{% endraw %} + +net = { '{{SELF_ADDR}}' } + + +{% if QMIN == "false" %} +option('NO_MINIMIZE', true) +{% else %} +option('NO_MINIMIZE', false) +{% endif %} + + +-- Self-checks on globals +assert(help() ~= nil) +assert(worker.id ~= nil) +-- Self-checks on facilities +assert(cache.count() == 0) +assert(cache.stats() ~= nil) +assert(cache.backends() ~= nil) +assert(worker.stats() ~= nil) +assert(net.interfaces() ~= nil) +-- Self-checks on loaded stuff +assert(net.list()[1].transport.ip == '{{SELF_ADDR}}') +assert(#modules.list() > 0) +-- Self-check timers +ev = event.recurrent(1 * sec, function (ev) return 1 end) +event.cancel(ev) +ev = event.after(0, function (ev) return 1 end) diff --git a/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl b/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl new file mode 100644 index 0000000..46c228c --- /dev/null +++ b/modules/bogus_log/test.integr/val_minimal_expiredsignature.rpl @@ -0,0 +1,125 @@ +; SPDX-License-Identifier: GPL-3.0-or-later + trust-anchor: ". IN DS 49060 8 2 E7B1EB56D7D5791B3D45630FEAA9C823DB84B385ACEEAC5F44DD08885C36700F" + val-override-date: "20170410000000" + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. + query-minimization: off +CONFIG_END + +SCENARIO_BEGIN Date after expiration of signatures. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 + ADDRESS 2001:7fd::1 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. 518400 IN NS k.root-servers.net. +. 518400 IN RRSIG NS 8 0 518400 20170409093827 20170310093827 20661 . uBuJpbRh1NYVciSKK0r3SA6NFnqE4s/+CqLfTXu26/HrY5c1aOhQHXZM cCDDjfPGFa7Eh4mqF0i9I+i+bFbYQitI1Heexye599VE19REbVsK4qaU xkArvt9k6HVqd/7BXXUyzLN1N0CScdyuT5tiEI9154SDNVpnC+z8i2u0 9hW8JEk4qqVWX/I1MYQB/UOcFSeDhD1Qku/26opqDuLl/1eaShxhMQ/c rjzOb5ZYzD0x+TUJZMYSOMwAraaFuYTT84oe6QYY+EGctAk1b50nA/5E C3Tm/xGuo9ioVtYhTwoo1XDUVeHmghdILjQZvR4pOSZoRGGP9ovb08Qg OmPXuQ== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +. IN DNSKEY +SECTION ANSWER +. IN DNSKEY 256 3 8 AwEAAe1oA46eOLNris1CtS0qM5TdMESK6i4hpalqa6JDv57eOUkaOeje ZW1tIFUokmaK7kuKEFEosddA89CYM8rt2RbC+sfKalbHAWOus0tXZyAL efb2sW95QRzyG6LNul0jQFn9eYWBUHrVe5Wqd0zrFCbTQLUhELSfrlkI UBpO/xKaGinRHX2JjyOnle4aPZY3bEVa/+KyY2ZU6UC4SBo3aHXanP26 ok91rOTmpTWp64ybsMdCXOU8deyuQFQf6q8DhIDmJrkymhX1MXWQQlE0 fAYIYf8/t9OCwucg8oEg4FPU8Gb4Zm/l6PgO4HFkFjBT6iGFCQt3qXe2 Qe3alUWoATc= +. IN DNSKEY 257 3 8 AwEAAb8sZgVVa02muJ+/+SVhJAvz2EWKGEGquhPbQXuF6ALBYoF4KWTO bZVF8sIVTGoaX5+UWkwwHthg7RwS1DALT/AJymYeHhUwA04gLsfCZ/cv BjmRy5RozeSJ1uxAhoCYHCT2hQBZ0cH0n8roXFXI2Y+6708pO1IBkTPT 9MpAGfezTtGYOortbSn+vqT/Zu8jOpNwkleXON4rlZRBZPd4JUMGL9Y5 N/j6+ClYeM+eFQTKXrLi1oC+0yK1sG5OlqrBDhAhBnz+IhfZz4TOkqJ9 Li2BVMatHBeB9GQHtu0FZuC3J0EQgiZxvq1RgkefFJAiB+5uVRN8U7up 5mLDxSgmT0M= +. 2592000 IN RRSIG DNSKEY 8 0 3600000 20170409093827 20170310093827 49060 . G7s3QiWNgOsl+LoG6OKjdBHPcFyhmCS17GFnaKjfJNdPQaFL5nM/vrXo eUIIdJXAvjj62TY7wTyFlnx3yjK93RVGKEEySpGC/1gkn5AdjVoQszog IxYjKzubizULSaX7SQ3/Ar+uHLxakdS1qgNdFu6hHCl857LJPtmC8SJt iFUmm5HFyARokMrfA88VrFRKEqojcCWajeZMfRtgBipFJZoYgPUCaFlz 8OupNdNUWCbGhnDWrXCWMzeKVXTQVlJf75PXXgtkuBUmr5RSWu7AYr+c wTJ4E4610goRqYxnZ33efKE/MuhKeY66xelPh0sirPrBMR5JAlyjV3k1 qDzhcQ== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION ANSWER +k.root-servers.net. 3600000 IN AAAA 2001:7fd::1 +k.root-servers.net. 2592000 IN RRSIG AAAA 8 3 3600000 20170409093827 20170310093827 20661 . qjVXwuxzmoRhdrXyQvKfrrzFxGiYuTTJHxwZPasJ1nVmN48dPyU6wA55 JeqoJv1Jm+XvIL1q0WtX6Zh6KLt6vVjHuMkhmFuIZYkFi/dmsEwFY8C0 ebyXyztQT5+6FOSVTAKacYc40LfBo8FqEn8RYlCu1mkAd8ANvvLrdLWW W03LVOY7JlCzyrKlAlmPmuV8z+e9PxNkUh6KfTEvAReoAAX7wYZkdefg 2d64c7rNWXvYm6LxBX6qeQ39d5WyKc8v+G01DJuDzs2Tx368QoK86vm/ qo9ERdT7koRt+gBZNYv8V4fh2SjaFsy2TJq/tiYcSia9snGDTFj6LWVM 6sBCYQ== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. 3600000 IN A 193.0.14.129 +k.root-servers.net. 2592000 IN RRSIG A 8 3 3600000 20170409093827 20170310093827 20661 . feIXXjskcsyH+ALZu67GVDaPWXjUGTWsTlDwzgJcLBzSuRVY/GVD5Z1Q B4/oUW99rLKB5bNS1MuasZ+nZFV67sBwJk1+SqNB2bAe7G5Tv1sR2Qgi qDAoB37YDVk5JGHfuxByLYbAVG9PrPXT60BN17OYrD/TFPzprye65gk3 7l9kPpAlblcsqdvh5piKrWc7VBcyMhlp56qdASNAl+Lrb+i0DZYyJXh+ b8LV5g5zp9FaVGKe0Gi4+yDXVjcM6VEtuNRAu2+flLoc3ho6qQF1Po4Y wueL72I+yFoUxkIOJvK47eWb+YUBIBK/L8/ORjYoLBRsrbc79wb0I3Zj Xy6O4Q== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype +ADJUST copy_id copy_query +REPLY QR AA REFUSED +SECTION QUESTION +. IN RRSIG +SECTION ANSWER +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +. IN NS +ENTRY_END + + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +. IN NS +SECTION ANSWER +SECTION AUTHORITY +SECTION ADDITIONAL +ENTRY_END + +STEP 11 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +. IN NS +ENTRY_END + +STEP 12 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +. IN NS +SECTION ANSWER +SECTION AUTHORITY +SECTION ADDITIONAL +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +bogus_log.test. IN TXT +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA DO NXDOMAIN +SECTION QUESTION +bogus_log.test. IN TXT +SECTION AUTHORITY +bogus_log.test. 10800 IN SOA bogus_log.test. nobody.invalid. 1 3600 1200 604800 10800 +SECTION ADDITIONAL +explanation.invalid. 10800 IN TXT "Ok" +ENTRY_END + +SCENARIO_END diff --git a/modules/cookies/README.rst b/modules/cookies/README.rst new file mode 100644 index 0000000..b8aba8a --- /dev/null +++ b/modules/cookies/README.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-cookies: + +DNS Cookies +=========== + +The module performs most of the :rfc:`7873` DNS cookies functionality. Its main purpose is to check the cookies of inbound queries and responses. It is also used to alter the behaviour of the cookie functionality. + +Example Configuration +--------------------- + +.. code-block:: lua + + -- Load the module before the 'iterate' layer. + modules = { + 'cookies < iterate' + } + + -- Configure the client part of the resolver. Set 8 bytes of the client + -- secret and choose the hashing algorithm to be used. + -- Use a string composed of hexadecimal digits to set the secret. + cookies.config { client_secret = '0123456789ABCDEF', + client_cookie_alg = 'FNV-64' } + + -- Configure the server part of the resolver. + cookies.config { server_secret = 'FEDCBA9876543210', + server_cookie_alg = 'FNV-64' } + + -- Enable client cookie functionality. (Add cookies into outbound + -- queries.) + cookies.config { client_enabled = true } + + -- Enable server cookie functionality. (Handle cookies in inbound + -- requests.) + cookies.config { server_enabled = true } + +.. tip:: If you want to change several parameters regarding the client or server configuration then do it within a single ``cookies.config()`` invocation. + +.. warning:: The module must be loaded before any other module that has direct influence on query processing and response generation. The module must be able to intercept an incoming query before the processing of the actual query starts. It must also be able to check the cookies of inbound responses and eventually discard them before they are handled by other functional units. + +Properties +---------- + +.. function:: cookies.config(configuration) + + :param table configuration: part of cookie configuration to be changed, may be called without parameter + :return: JSON dictionary containing current configuration + + The function may be called without any parameter. In such case it only returns current configuration. The returned JSON also contains available algorithm choices. + +Dependencies +------------ + +* `Nettle `_ required for HMAC-SHA256 + diff --git a/modules/cookies/cookiectl.c b/modules/cookies/cookiectl.c new file mode 100644 index 0000000..f1ab80a --- /dev/null +++ b/modules/cookies/cookiectl.c @@ -0,0 +1,689 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#include "lib/cookies/alg_containers.h" +#include "modules/cookies/cookiectl.h" + +#define NAME_CLIENT_ENABLED "client_enabled" +#define NAME_CLIENT_SECRET "client_secret" +#define NAME_CLIENT_COOKIE_ALG "client_cookie_alg" +#define NAME_AVAILABLE_CLIENT_COOKIE_ALGS "available_client_cookie_algs" + +#define NAME_SERVER_ENABLED "server_enabled" +#define NAME_SERVER_SECRET "server_secret" +#define NAME_SERVER_COOKIE_ALG "server_cookie_alg" +#define NAME_AVAILABLE_SERVER_COOKIE_ALGS "available_server_cookie_algs" + +/** + * @brief Initialises cookie control context. + * @param ctx cookie control context + */ +static void kr_cookie_ctx_init(struct kr_cookie_ctx *ctx) +{ + if (!ctx) { + return; + } + + memset(ctx, 0, sizeof(*ctx)); + + ctx->clnt.current.alg_id = ctx->clnt.recent.alg_id = -1; + ctx->srvr.current.alg_id = ctx->srvr.recent.alg_id = -1; +} + +/** + * @brief Check whether node holds proper 'enabled' value. + * @param node JSON node holding the value + * @return true if value OK + */ +static bool enabled_ok(const JsonNode *node) +{ + if (kr_fails_assert(node)) + return false; + + return node->tag == JSON_BOOL; +} + +/** + * @brief Check whether node holds proper 'secret' value. + * @param node JSON node holding the value + * @return true if value OK + */ +static bool secret_ok(const JsonNode *node) +{ + if (kr_fails_assert(node)) + return false; + + if (node->tag != JSON_STRING) { + return false; + } + + const char *hexstr = node->string_; + + size_t len = strlen(hexstr); + if ((len % 2) != 0) { + return false; + } + /* A check for minimal required length could also be performed. */ + + for (size_t i = 0; i < len; ++i) { + if (!isxdigit(tolower(hexstr[i]))) { + return false; + } + } + + return true; +} + +/** + * @brief Find hash function with given name. + * @param node JSON node holding the value + * @param table lookup table with algorithm names + * @return pointer to table entry or NULL on error if does not exist + */ +static const knot_lookup_t *hash_func_lookup(const JsonNode *node, + const knot_lookup_t table[]) +{ + if (!node || node->tag != JSON_STRING) { + return NULL; + } + + return knot_lookup_by_name(table, node->string_); +} + +/** + * @brief Creates a cookie secret structure. + * @param size size of the actual secret + * @param zero set to true if value should be cleared + * @return pointer to new structure, NULL on failure or if @size is zero + */ +static struct kr_cookie_secret *new_cookie_secret(size_t size, bool zero) +{ + if (size == 0) { + return NULL; + } + + struct kr_cookie_secret *sq = malloc(sizeof(*sq) + size); + if (!sq) { + return NULL; + } + + sq->size = size; + if (zero) { + memset(sq->data, 0, size); + } + return sq; +} + +/** + * @brief Clone a cookie secret. + * @param sec secret to be cloned + * @return pointer to new structure, NULL on failure or if @size is zero + */ +static struct kr_cookie_secret *clone_cookie_secret(const struct kr_cookie_secret *sec) +{ + if (!sec || sec->size == 0) { + return NULL; + } + + struct kr_cookie_secret *sq = malloc(sizeof(*sq) + sec->size); + if (!sq) { + return NULL; + } + + sq->size = sec->size; + memcpy(sq->data, sec->data, sq->size); + return sq; +} + +static int hexchar2val(int d) +{ + if (('0' <= d) && (d <= '9')) { + return d - '0'; + } else if (('a' <= d) && (d <= 'f')) { + return d - 'a' + 0x0a; + } else { + return -1; + } +} + +static int hexval2char(int i) +{ + if ((0 <= i) && (i <= 9)) { + return i + '0'; + } else if ((0x0a <= i) && (i <= 0x0f)) { + return i - 0x0a + 'A'; + } else { + return -1; + } +} + +/** + * @brief Converts string containing two-digit hexadecimal number into int. + * @param hexstr hexadecimal string + * @return -1 on error, value from 0 to 255 else. + */ +static int hexbyte2int(const char *hexstr) +{ + if (!hexstr) { + return -1; + } + + int dhi = tolower(hexstr[0]); + if (!isxdigit(dhi)) { + /* Exit also on empty string. */ + return -1; + } + int dlo = tolower(hexstr[1]); + if (!isxdigit(dlo)) { + return -1; + } + + dhi = hexchar2val(dhi); + if (kr_fails_assert(dhi != -1)) + return -1; + dlo = hexchar2val(dlo); + if (kr_fails_assert(dlo != -1)) + return -1; + + return (dhi << 4) | dlo; +} + +/** + * @brief Writes two hexadecimal digits (two byes) into given memory location. + * @param tgt target location + * @param i number from 0 to 255 + * @return 0 on success, -1 on failure + */ +static int int2hexbyte(char *tgt, int i) +{ + if (!tgt || i < 0x00 || i > 0xff) { + return -1; + } + + int ilo = hexval2char(i & 0x0f); + if (kr_fails_assert(ilo != -1)) + return -1; + int ihi = hexval2char((i >> 4) & 0x0f); + if (kr_fails_assert(ihi != -1)) + return -1; + + tgt[0] = ihi; + tgt[1] = ilo; + + return 0; +} + +/** + * @brief Reads a string containing hexadecimal values. + * @note String must consist of hexadecimal digits only and must have even + * non-zero length. + */ +static struct kr_cookie_secret *new_sq_from_hexstr(const char *hexstr) +{ + if (!hexstr) { + return NULL; + } + + size_t len = strlen(hexstr); + if ((len % 2) != 0) { + return NULL; + } + + struct kr_cookie_secret *sq = new_cookie_secret(len / 2, false); + if (!sq) { + return NULL; + } + + uint8_t *data = sq->data; + for (size_t i = 0; i < len; i += 2) { + int num = hexbyte2int(hexstr + i); + if (num == -1) { + free(sq); + return NULL; + } + if (kr_fails_assert(0x00 <= num && num <= 0xff)) { + free(sq); + return NULL; + } + *data = num; + ++data; + } + + return sq; +} + +/** + * @brief Creates new secret. + * @param node JSON node holding the secret value + * @return pointer to newly allocated secret, NULL on error + */ +static struct kr_cookie_secret *create_secret(const JsonNode *node) +{ + if (!node) { + return NULL; + } + + if (node->tag != JSON_STRING) { + return NULL; + } + + return new_sq_from_hexstr(node->string_); +} + +/** + * @brief Check whether configuration node contains valid values. + */ +static bool configuration_node_ok(const JsonNode *node) +{ + if (kr_fails_assert(node)) + return false; + + if (!node->key) { + /* All top most nodes must have names. */ + return false; + } + + if (strcmp(node->key, NAME_CLIENT_ENABLED) == 0) { + return enabled_ok(node); + } else if (strcmp(node->key, NAME_CLIENT_SECRET) == 0) { + return secret_ok(node); + } else if (strcmp(node->key, NAME_CLIENT_COOKIE_ALG) == 0) { + return hash_func_lookup(node, kr_cc_alg_names) != NULL; + } else if (strcmp(node->key, NAME_SERVER_ENABLED) == 0) { + return enabled_ok(node); + } else if (strcmp(node->key, NAME_SERVER_SECRET) == 0) { + return secret_ok(node); + } else if (strcmp(node->key, NAME_SERVER_COOKIE_ALG) == 0) { + return hash_func_lookup(node, kr_sc_alg_names) != NULL; + } + + return false; +} + +/** + * @brief Creates a new string from secret quantity. + * @param sq secret quantity + * @return newly allocated string or NULL on error + */ +static char *new_hexstr_from_sq(const struct kr_cookie_secret *sq) +{ + if (!sq) { + return NULL; + } + + char *new_str = malloc((sq->size * 2) + 1); + if (!new_str) { + return NULL; + } + + char *tgt = new_str; + for (size_t i = 0; i < sq->size; ++i) { + if (0 != int2hexbyte(tgt, sq->data[i])) { + free(new_str); + return NULL; + } + tgt += 2; + } + + *tgt = '\0'; + return new_str; +} + +static bool read_secret(JsonNode *root, const char *node_name, + const struct kr_cookie_secret *secret) +{ + if (kr_fails_assert(root && node_name && secret)) + return false; + + char *secret_str = new_hexstr_from_sq(secret); + if (!secret_str) { + return false; + } + + JsonNode *str_node = json_mkstring(secret_str); + if (!str_node) { + free(secret_str); + return false; + } + + json_append_member(root, node_name, str_node); + + free(secret_str); + return true; +} + +static bool read_available_hashes(JsonNode *root, const char *root_name, + const knot_lookup_t table[]) +{ + if (kr_fails_assert(root && root_name && table)) + return false; + + JsonNode *array = json_mkarray(); + if (!array) { + return false; + } + + const knot_lookup_t *aux_ptr = table; + while (aux_ptr && (aux_ptr->id >= 0) && aux_ptr->name) { + JsonNode *element = json_mkstring(aux_ptr->name); + if (!element) { + goto fail; + } + json_append_element(array, element); + ++aux_ptr; + } + + json_append_member(root, root_name, array); + + return true; + +fail: + if (array) { + json_delete(array); + } + return false; +} + +/** + * @brief Check whether new settings are different from the old ones. + */ +static bool is_modified(const struct kr_cookie_comp *running, + struct kr_cookie_secret *secr, + const knot_lookup_t *alg_lookup) +{ + if (kr_fails_assert(running)) + return false; + + if (alg_lookup && alg_lookup->id >= 0) { + if (running->alg_id != alg_lookup->id) { + return true; + } + } + + if (secr) { + if (kr_fails_assert(secr->size > 0)) + return false; + if (running->secr->size != secr->size || + 0 != memcmp(running->secr->data, secr->data, + running->secr->size)) { + return true; + } + } + + return false; +} + +/** + * @brief Returns newly allocated secret via pointer argument. + */ +static bool obtain_secret(JsonNode *root_node, struct kr_cookie_secret **secret, + const char *name) +{ + if (kr_fails_assert(secret && name)) + return false; + + const JsonNode *node; + if ((node = json_find_member(root_node, name)) != NULL) { + *secret = create_secret(node); + if (!*secret) { + return false; + } + } + + return true; +} + +/** + * @brief Updates the current configuration and moves current to recent. + */ +static void update_running(struct kr_cookie_settings *running, + struct kr_cookie_secret **secret, + const knot_lookup_t *alg_lookup) +{ + if (kr_fails_assert(running && secret) || kr_fails_assert(*secret || alg_lookup)) + return; + + running->recent.alg_id = -1; + free(running->recent.secr); + running->recent.secr = NULL; + + running->recent.alg_id = running->current.alg_id; + if (alg_lookup) { + if (kr_fails_assert(alg_lookup->id >= 0)) + return; + running->current.alg_id = alg_lookup->id; + } + + if (*secret) { + running->recent.secr = running->current.secr; + running->current.secr = *secret; + *secret = NULL; + } else { + running->recent.secr = clone_cookie_secret(running->current.secr); + } +} + +/** + * @brief Applies modification onto client/server running configuration. + * @note The @a secret is going to be consumed. + * @param secret pointer to new secret + * @param alg_lookup new algorithm + * @param enabled JSON node holding boolean value + */ +static void apply_changes(struct kr_cookie_settings *running, + struct kr_cookie_secret **secret, + const knot_lookup_t *alg_lookup, + const JsonNode *enabled) +{ + if (kr_fails_assert(running && secret)) + return; + + if (is_modified(&running->current, *secret, alg_lookup)) { + update_running(running, secret, alg_lookup); + } + + if (enabled) { + kr_assert(enabled->tag == JSON_BOOL); + running->enabled = enabled->bool_; + } +} + +/** + * @brief Applies configuration. + * + * @note The function must be called after the input values have been checked + * for validity. Only first found values are applied. + * + * @param ctx cookie configuration context + * @param root_node JSON root node + * @return true if changes were applied + */ +static bool config_apply_json(struct kr_cookie_ctx *ctx, JsonNode *root_node) +{ + if (kr_fails_assert(ctx && root_node)) + return; + + /* + * These must be allocated before actual change. Allocation failure + * should not leave configuration in inconsistent state. + */ + struct kr_cookie_secret *new_clnt_secret = NULL; + struct kr_cookie_secret *new_srvr_secret = NULL; + if (!obtain_secret(root_node, &new_clnt_secret, NAME_CLIENT_SECRET)) { + return false; + } + if (!obtain_secret(root_node, &new_srvr_secret, NAME_SERVER_SECRET)) { + free(new_clnt_secret); + return false; + } + + /* Algorithm pointers. */ + const knot_lookup_t *clnt_lookup = hash_func_lookup(json_find_member(root_node, NAME_CLIENT_COOKIE_ALG), kr_cc_alg_names); + const knot_lookup_t *srvr_lookup = hash_func_lookup(json_find_member(root_node, NAME_SERVER_COOKIE_ALG), kr_sc_alg_names); + + const JsonNode *clnt_enabled_node = json_find_member(root_node, NAME_CLIENT_ENABLED); + const JsonNode *srvr_enabled_node = json_find_member(root_node, NAME_SERVER_ENABLED); + + apply_changes(&ctx->clnt, &new_clnt_secret, clnt_lookup, clnt_enabled_node); + apply_changes(&ctx->srvr, &new_srvr_secret, srvr_lookup, srvr_enabled_node); + + /* + * Allocated secrets should be already consumed. There is no need to + * free them. + */ + + return true; +} + +bool config_apply(struct kr_cookie_ctx *ctx, const char *args) +{ + if (!ctx) { + return false; + } + + if (!args || !strlen(args)) { + return true; + } + + if (!args || !strlen(args)) { + return true; + } + + bool success = false; + + /* Check whether all supplied data are valid. */ + JsonNode *root_node = json_decode(args); + if (!root_node) { + return false; + } + JsonNode *node; + json_foreach (node, root_node) { + success = configuration_node_ok(node); + if (!success) { + break; + } + } + + /* Apply configuration if values seem to be OK. */ + if (success) { + success = config_apply_json(ctx, root_node); + } + + json_delete(root_node); + + return success; +} + +char *config_read(struct kr_cookie_ctx *ctx) +{ + if (!ctx) { + return NULL; + } + + const knot_lookup_t *lookup; + char *result; + JsonNode *root_node = json_mkobject(); + if (!root_node) { + return NULL; + } + + json_append_member(root_node, NAME_CLIENT_ENABLED, + json_mkbool(ctx->clnt.enabled)); + + read_secret(root_node, NAME_CLIENT_SECRET, ctx->clnt.current.secr); + + lookup = knot_lookup_by_id(kr_cc_alg_names, ctx->clnt.current.alg_id); + if (lookup) { + json_append_member(root_node, NAME_CLIENT_COOKIE_ALG, + json_mkstring(lookup->name)); + } + + read_available_hashes(root_node, NAME_AVAILABLE_CLIENT_COOKIE_ALGS, + kr_cc_alg_names); + + json_append_member(root_node, NAME_SERVER_ENABLED, + json_mkbool(ctx->srvr.enabled)); + + read_secret(root_node, NAME_SERVER_SECRET, ctx->srvr.current.secr); + + lookup = knot_lookup_by_id(kr_sc_alg_names, ctx->srvr.current.alg_id); + if (lookup) { + json_append_member(root_node, NAME_SERVER_COOKIE_ALG, + json_mkstring(lookup->name)); + } + + read_available_hashes(root_node, NAME_AVAILABLE_SERVER_COOKIE_ALGS, + kr_sc_alg_names); + + result = json_encode(root_node); + json_delete(root_node); + return result; +} + +int config_init(struct kr_cookie_ctx *ctx) +{ + if (!ctx) { + return kr_error(EINVAL); + } + + kr_cookie_ctx_init(ctx); + + struct kr_cookie_secret *cs = new_cookie_secret(KNOT_OPT_COOKIE_CLNT, + true); + struct kr_cookie_secret *ss = new_cookie_secret(KNOT_OPT_COOKIE_CLNT, + true); + if (!cs || !ss) { + free(cs); + free(ss); + return kr_error(ENOMEM); + } + + const knot_lookup_t *clookup = knot_lookup_by_name(kr_cc_alg_names, + "FNV-64"); + const knot_lookup_t *slookup = knot_lookup_by_name(kr_sc_alg_names, + "FNV-64"); + if (!clookup || !slookup) { + free(cs); + free(ss); + return kr_error(ENOENT); + } + + ctx->clnt.current.secr = cs; + ctx->clnt.current.alg_id = clookup->id; + + ctx->srvr.current.secr = ss; + ctx->srvr.current.alg_id = slookup->id; + + return kr_ok(); +} + +void config_deinit(struct kr_cookie_ctx *ctx) +{ + if (!ctx) { + return; + } + + ctx->clnt.enabled = false; + + free(ctx->clnt.recent.secr); + ctx->clnt.recent.secr = NULL; + + free(ctx->clnt.current.secr); + ctx->clnt.current.secr = NULL; + + ctx->srvr.enabled = false; + + free(ctx->srvr.recent.secr); + ctx->srvr.recent.secr = NULL; + + free(ctx->srvr.current.secr); + ctx->srvr.current.secr = NULL; +} diff --git a/modules/cookies/cookiectl.h b/modules/cookies/cookiectl.h new file mode 100644 index 0000000..6740e16 --- /dev/null +++ b/modules/cookies/cookiectl.h @@ -0,0 +1,35 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "lib/cookies/control.h" + +/** + * @brief Sets cookie control context structure. + * @param ctx cookie control context + * @param args JSON string describing configuration changes + * @return true if changes successfully applied + */ +bool config_apply(struct kr_cookie_ctx *ctx, const char *args); + +/** + * @brief Reads cookie control context structure. + * @param ctx cookie control context + * @return JSON string or NULL on error + */ +char *config_read(struct kr_cookie_ctx *ctx); + +/** + * @brief Initialises cookie control context to default values. + * @param ctx cookie control context + * @return kr_ok() or error code + */ +int config_init(struct kr_cookie_ctx *ctx); + +/** + * @brief Clears the cookie control context. + * @param ctx cookie control context + */ +void config_deinit(struct kr_cookie_ctx *ctx); diff --git a/modules/cookies/cookiemonster.c b/modules/cookies/cookiemonster.c new file mode 100644 index 0000000..595317b --- /dev/null +++ b/modules/cookies/cookiemonster.c @@ -0,0 +1,464 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/cookies/alg_containers.h" +#include "lib/cookies/control.h" +#include "lib/cookies/helper.h" +#include "lib/cookies/lru_cache.h" +#include "lib/cookies/nonce.h" +#include "lib/resolve.h" +#include "lib/rplan.h" +#include "modules/cookies/cookiemonster.h" + +#define VERBOSE_MSG(qry, ...) kr_log_q(qry, COOKIES, __VA_ARGS__) + +/** + * Obtain address from query/response context if if can be obtained. + * @param req resolution context + * @return pointer to where the server socket address, NULL if not provided within context + */ +static const struct sockaddr *passed_server_sockaddr(const struct kr_request *req) +{ + if (!req || !req->upstream.addr) { + return NULL; + } + + if (req->upstream.addr->sa_family == AF_INET || + req->upstream.addr->sa_family == AF_INET6) { + return req->upstream.addr; + } + + return NULL; +} + +/** + * Obtain pointer to server socket address that matches obtained cookie. + * @param srvr_sa server socket address + * @param cc client cookie from the response + * @param cc_len client cookie size + * @param clnt_sett client cookie settings structure + * @retval 1 if cookie matches current settings + * @retval 0 if cookie matches recent settings + * @return -1 if cookie does not match + * @return -2 on any error + */ +static int srvr_sockaddr_cc_check(const struct sockaddr *srvr_sa, + const uint8_t *cc, uint16_t cc_len, + const struct kr_cookie_settings *clnt_sett) +{ + if (kr_fails_assert(cc && cc_len > 0 && clnt_sett)) + return -2; + + if (!srvr_sa) { + return -2; + } + + if (kr_fails_assert(clnt_sett->current.secr)) + return -2; + + /* The address must correspond with the client cookie. */ + struct knot_cc_input input = { + .clnt_sockaddr = NULL, /* Not supported yet. */ + .srvr_sockaddr = srvr_sa, + .secret_data = clnt_sett->current.secr->data, + .secret_len = clnt_sett->current.secr->size + }; + + const struct knot_cc_alg *cc_alg = kr_cc_alg_get(clnt_sett->current.alg_id); + if (!cc_alg) { + return -2; + } + int comp_ret = -1; /* Cookie does not match. */ + int ret = knot_cc_check(cc, cc_len, &input, cc_alg); + if (ret == KNOT_EOK) { + comp_ret = 1; + } else { + cc_alg = kr_cc_alg_get(clnt_sett->recent.alg_id); + if (clnt_sett->recent.secr && cc_alg) { + input.secret_data = clnt_sett->recent.secr->data; + input.secret_len = clnt_sett->recent.secr->size; + ret = knot_cc_check(cc, cc_len, &input, cc_alg); + if (ret == KNOT_EOK) { + comp_ret = 0; + } + } + } + + return comp_ret; +} + +/** + * Obtain cookie from cache. + * @note Cookies with invalid length are ignored. + * @param cache cache context + * @param sa key value + * @param cookie_opt entire EDNS cookie option (including header) + * @return true if a cookie exists in cache + */ +static const uint8_t *get_cookie_opt(kr_cookie_lru_t *cache, + const struct sockaddr *sa) +{ + if (kr_fails_assert(cache && sa)) + return NULL; + + const uint8_t *cached_cookie_opt = kr_cookie_lru_get(cache, sa); + if (!cached_cookie_opt) { + return NULL; + } + + size_t cookie_opt_size = KNOT_EDNS_OPTION_HDRLEN + + knot_edns_opt_get_length(cached_cookie_opt); + if (cookie_opt_size > KR_COOKIE_OPT_MAX_LEN) { + return NULL; + } + + return cached_cookie_opt; +} + +/** + * Check whether the supplied cookie is cached under the given key. + * @param cache cache context + * @param sa key value + * @param cookie_opt cookie option to search for + */ +static bool is_cookie_cached(kr_cookie_lru_t *cache, const struct sockaddr *sa, + const uint8_t *cookie_opt) +{ + if (kr_fails_assert(cache && sa && cookie_opt)) + return false; + + const uint8_t *cached_opt = get_cookie_opt(cache, sa); + if (!cached_opt) { + return false; + } + + uint16_t cookie_opt_size = KNOT_EDNS_OPTION_HDRLEN + + knot_edns_opt_get_length(cookie_opt); + uint16_t cached_opt_size = KNOT_EDNS_OPTION_HDRLEN + + knot_edns_opt_get_length(cached_opt); + + if (cookie_opt_size != cached_opt_size) { + return false; + } + + return memcmp(cookie_opt, cached_opt, cookie_opt_size) == 0; +} + +/** + * Check cookie content and store it to cache. + */ +static bool check_cookie_content_and_cache(const struct kr_cookie_settings *clnt_sett, + struct kr_request *req, + uint8_t *pkt_cookie_opt, + kr_cookie_lru_t *cache) +{ + if (kr_fails_assert(clnt_sett && req && pkt_cookie_opt && cache)) + return false; + + const uint8_t *pkt_cookie_data = knot_edns_opt_get_data(pkt_cookie_opt); + uint16_t pkt_cookie_len = knot_edns_opt_get_length(pkt_cookie_opt); + /* knot_edns_opt_cookie_parse() returns error on invalid data. */ + + const uint8_t *pkt_cc = NULL, *pkt_sc = NULL; + uint16_t pkt_cc_len = 0, pkt_sc_len = 0; + + int ret = knot_edns_opt_cookie_parse(pkt_cookie_data, pkt_cookie_len, + &pkt_cc, &pkt_cc_len, + &pkt_sc, &pkt_sc_len); + if (ret != KNOT_EOK || !pkt_sc) { + VERBOSE_MSG(NULL, "%s\n", + "got malformed DNS cookie or server cookie missing"); + return false; + } + if (kr_fails_assert(pkt_cc_len == KNOT_OPT_COOKIE_CLNT)) + return false; + + /* Check server address against received client cookie. */ + const struct sockaddr *srvr_sockaddr = passed_server_sockaddr(req); + ret = srvr_sockaddr_cc_check(srvr_sockaddr, pkt_cc, pkt_cc_len, + clnt_sett); + if (ret < 0) { + VERBOSE_MSG(NULL, "%s\n", "could not match received cookie"); + return false; + } + if (kr_fails_assert(srvr_sockaddr)) + return false; + + /* Don't cache received cookies that don't match the current secret. */ + if ((ret == 1) && + !is_cookie_cached(cache, srvr_sockaddr, pkt_cookie_opt)) { + ret = kr_cookie_lru_set(cache, srvr_sockaddr, pkt_cookie_opt); + if (ret != kr_ok()) { + VERBOSE_MSG(NULL, "%s\n", "failed caching cookie"); + } else { + VERBOSE_MSG(NULL, "%s\n", "cookie cached"); + } + } + + return true; +} + +/** Process incoming response. */ +int check_response(kr_layer_t *ctx, knot_pkt_t *pkt) +{ + struct kr_request *req = ctx->req; + struct kr_query *qry = req->current_query; + struct kr_cookie_ctx *cookie_ctx = &req->ctx->cookie_ctx; + + if (ctx->state & (KR_STATE_DONE | KR_STATE_FAIL)) { + return ctx->state; + } + + if (!cookie_ctx->clnt.enabled || (qry->flags.TCP)) { + return ctx->state; + } + + /* Obtain cookie if present in response. Don't check actual content. */ + uint8_t *pkt_cookie_opt = NULL; + if (knot_pkt_has_edns(pkt)) { + pkt_cookie_opt = knot_edns_get_option(pkt->opt_rr, + KNOT_EDNS_OPTION_COOKIE); + } + + kr_cookie_lru_t *cookie_cache = req->ctx->cache_cookie; + + const struct sockaddr *srvr_sockaddr = passed_server_sockaddr(req); + + if (!pkt_cookie_opt && srvr_sockaddr && + get_cookie_opt(cookie_cache, srvr_sockaddr)) { + /* We haven't received any cookies although we should. */ + VERBOSE_MSG(NULL, "%s\n", + "expected to receive a cookie but none received"); + return KR_STATE_FAIL; + } + + if (!pkt_cookie_opt) { + /* Don't do anything if no cookies expected and received. */ + return ctx->state; + } + + if (!check_cookie_content_and_cache(&cookie_ctx->clnt, req, + pkt_cookie_opt, cookie_cache)) { + return KR_STATE_FAIL; + } + + uint16_t rcode = knot_pkt_ext_rcode(pkt); + if (rcode == KNOT_RCODE_BADCOOKIE) { + struct kr_query *next = NULL; + if (!(qry->flags.BADCOOKIE_AGAIN)) { + /* Received first BADCOOKIE, regenerate query. */ + next = kr_rplan_push(&req->rplan, qry->parent, + qry->sname, qry->sclass, + qry->stype); + } + + if (next) { + VERBOSE_MSG(NULL, "%s\n", "BADCOOKIE querying again"); + qry->flags.BADCOOKIE_AGAIN = true; + } else { + /* + * Either the planning of the second request failed or + * BADCOOKIE received for the second time. + * + * RFC7873 5.3 says that TCP should be used. Currently + * we always expect that the server doesn't support TCP. + */ + qry->flags.BADCOOKIE_AGAIN = false; + return KR_STATE_FAIL; + } + + return KR_STATE_CONSUME; + } + + return ctx->state; +} + +static inline uint8_t *req_cookie_option(struct kr_request *req) +{ + if (!req || !req->qsource.opt) { + return NULL; + } + + return knot_edns_get_option(req->qsource.opt, KNOT_EDNS_OPTION_COOKIE); +} + +/** + * @brief Returns resolver state and sets answer RCODE on missing or invalid + * server cookie. + * + * @note Caller should exit when only KR_STATE_FAIL is returned. + * + * @param state original resolver state + * @param sc_present true if server cookie is present + * @param ignore_badcookie true if bad cookies should be treated as good ones + * @param req request context + * @return new resolver state + */ +static int invalid_sc_status(int state, bool sc_present, bool ignore_badcookie, + const struct kr_request *req, knot_pkt_t *answer) +{ + if (kr_fails_assert(req && answer)) + return KR_STATE_FAIL; + + const knot_pkt_t *pkt = req->qsource.packet; + + if (!pkt) { + return KR_STATE_FAIL; + } + + if (knot_wire_get_qdcount(pkt->wire) == 0) { + /* RFC7873 5.4 */ + state = KR_STATE_DONE; + if (sc_present) { + kr_pkt_set_ext_rcode(answer, KNOT_RCODE_BADCOOKIE); + state |= KR_STATE_FAIL; + } + } else if (!ignore_badcookie) { + /* Generate BADCOOKIE response. */ + VERBOSE_MSG(NULL, "%s\n", + !sc_present ? "request is missing server cookie" : + "request has invalid server cookie"); + if (!knot_pkt_has_edns(answer)) { + VERBOSE_MSG(NULL, "%s\n", + "missing EDNS section in prepared answer"); + /* Caller should exit on this (and only this) state. */ + return KR_STATE_FAIL; + } + kr_pkt_set_ext_rcode(answer, KNOT_RCODE_BADCOOKIE); + state = KR_STATE_FAIL | KR_STATE_DONE; + } + + return state; +} + +int check_request(kr_layer_t *ctx) +{ + struct kr_request *req = ctx->req; + struct kr_cookie_settings *srvr_sett = &req->ctx->cookie_ctx.srvr; + + if (!srvr_sett->enabled) { + return ctx->state; + } + + knot_pkt_t *answer = req->answer; // FIXME: see kr_request_ensure_answer() + + if (ctx->state & (KR_STATE_DONE | KR_STATE_FAIL)) { + return ctx->state; + } + + if (!srvr_sett->enabled) { + if (knot_pkt_has_edns(answer)) { + /* Delete any cookies. */ + knot_edns_remove_options(answer->opt_rr, + KNOT_EDNS_OPTION_COOKIE); + } + return ctx->state; + } + + uint8_t *req_cookie_opt = req_cookie_option(req); + if (!req_cookie_opt) { + return ctx->state; /* Don't do anything without cookies. */ + } + + struct knot_dns_cookies cookies; + memset(&cookies, 0, sizeof(cookies)); + int ret = kr_parse_cookie_opt(req_cookie_opt, &cookies); + if (ret != kr_ok()) { + /* FORMERR -- malformed cookies. */ + VERBOSE_MSG(NULL, "%s\n", "request with malformed cookie"); + knot_wire_set_rcode(answer->wire, KNOT_RCODE_FORMERR); + return KR_STATE_FAIL | KR_STATE_DONE; + } + + /* + * RFC7873 5.2.3 and 5.2.4 suggest that queries with invalid or + * missing server cookies can be treated like normal. + * Right now bad cookies are always ignored (i.e. treated as valid). + */ + bool ignore_badcookie = true; + + const struct knot_sc_alg *current_sc_alg = kr_sc_alg_get(srvr_sett->current.alg_id); + + if (!req->qsource.addr || !srvr_sett->current.secr || !current_sc_alg) { + VERBOSE_MSG(NULL, "%s\n", "missing valid server cookie context"); + return KR_STATE_FAIL; + } + + int return_state = ctx->state; + + struct knot_sc_private srvr_data = { + .clnt_sockaddr = req->qsource.addr, + .secret_data = srvr_sett->current.secr->data, + .secret_len = srvr_sett->current.secr->size + }; + + struct knot_sc_input sc_input = { + .cc = cookies.cc, + .cc_len = cookies.cc_len, + /* Don't set nonce here. */ + .srvr_data = &srvr_data + }; + + struct kr_nonce_input nonce = { + .rand = kr_rand_bytes(sizeof(nonce.rand)), + .time = req->current_query->timestamp.tv_sec + }; + + if (!cookies.sc) { + /* Request has no server cookie. */ + return_state = invalid_sc_status(return_state, false, + ignore_badcookie, req, answer); + if (return_state & KR_STATE_FAIL) { + return return_state; + } + goto answer_add_cookies; + } + + /* Check server cookie obtained in request. */ + + ret = knot_sc_check(KR_NONCE_LEN, &cookies, &srvr_data, current_sc_alg); + if (ret == KNOT_EINVAL && srvr_sett->recent.secr) { + const struct knot_sc_alg *recent_sc_alg = kr_sc_alg_get(srvr_sett->recent.alg_id); + if (recent_sc_alg) { + /* Try recent algorithm. */ + struct knot_sc_private recent_srvr_data = { + .clnt_sockaddr = req->qsource.addr, + .secret_data = srvr_sett->recent.secr->data, + .secret_len = srvr_sett->recent.secr->size + }; + ret = knot_sc_check(KR_NONCE_LEN, &cookies, + &recent_srvr_data, recent_sc_alg); + } + } + if (ret != KNOT_EOK) { + /* Invalid server cookie. */ + return_state = invalid_sc_status(return_state, true, + ignore_badcookie, req, answer); + if (return_state & KR_STATE_FAIL) { + return return_state; + } + goto answer_add_cookies; + } + + /* Server cookie is OK. */ + +answer_add_cookies: + /* Add server cookie into response. */ + ret = kr_answer_write_cookie(&sc_input, &nonce, current_sc_alg, answer); + if (ret != kr_ok()) { + return_state = KR_STATE_FAIL; + } + return return_state; +} + +#undef VERBOSE_MSG diff --git a/modules/cookies/cookiemonster.h b/modules/cookies/cookiemonster.h new file mode 100644 index 0000000..ab1fdeb --- /dev/null +++ b/modules/cookies/cookiemonster.h @@ -0,0 +1,15 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "lib/layer.h" + +/** Checks cookies of inbound requests. It's for kr_layer_api_t::begin. */ +int check_request(kr_layer_t *ctx); + +/** Checks cookies of received responses. It's for kr_layer_api_t::consume. */ +int check_response(kr_layer_t *ctx, knot_pkt_t *pkt); diff --git a/modules/cookies/cookies.c b/modules/cookies/cookies.c new file mode 100644 index 0000000..5b688d3 --- /dev/null +++ b/modules/cookies/cookies.c @@ -0,0 +1,78 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "daemon/engine.h" +#include "lib/layer.h" +#include "modules/cookies/cookiectl.h" +#include "modules/cookies/cookiemonster.h" + +/** + * Get/set DNS cookie related stuff. + * + * Input: { name: value, ... } + * Output: current configuration + */ +static char *cookies_config(void *env, struct kr_module *module, + const char *args) +{ + struct kr_cookie_ctx *cookie_ctx = module->data; + if (kr_fails_assert(cookie_ctx)) + return NULL; + + /* Apply configuration, if any. */ + config_apply(cookie_ctx, args); + + /* Return current configuration. */ + return config_read(cookie_ctx); +} + +/* + * Module implementation. + */ + +KR_EXPORT +int cookies_init(struct kr_module *module) +{ + /* The function answer_finalize() in resolver is called before any + * .finish callback. Therefore this layer does not use it. */ + static kr_layer_api_t layer = { + .begin = &check_request, + .consume = &check_response + }; + /* Store module reference */ + layer.data = module; + module->layer = &layer; + + static const struct kr_prop props[] = { + { &cookies_config, "config", "Empty value to return current configuration.", }, + { NULL, NULL, NULL } + }; + module->props = props; + + struct engine *engine = module->data; + + struct kr_cookie_ctx *cookie_ctx = &engine->resolver.cookie_ctx; + + int ret = config_init(cookie_ctx); + if (ret != kr_ok()) { + return ret; + } + + /* Replace engine pointer. */ + module->data = cookie_ctx; + + return kr_ok(); +} + +KR_EXPORT +int cookies_deinit(struct kr_module *module) +{ + struct kr_cookie_ctx *cookie_ctx = module->data; + + config_deinit(cookie_ctx); + + return kr_ok(); +} + +KR_MODULE_EXPORT(cookies) diff --git a/modules/daf/.packaging/test.config b/modules/daf/.packaging/test.config new file mode 100644 index 0000000..2fa1d8c --- /dev/null +++ b/modules/daf/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('daf') +assert(daf) +quit() diff --git a/modules/daf/README.rst b/modules/daf/README.rst new file mode 100644 index 0000000..a5e025e --- /dev/null +++ b/modules/daf/README.rst @@ -0,0 +1,146 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-daf: + +DNS Application Firewall +======================== + +This module is a high-level interface for other powerful filtering modules and DNS views. It provides an easy interface to apply and monitor DNS filtering rules and a persistent memory for them. It also provides a restful service interface and an HTTP interface. + +Example configuration +--------------------- + +Firewall rules are declarative and consist of filters and actions. Filters have ``field operator operand`` notation (e.g. ``qname = example.com``), and may be chained using AND/OR keywords. Actions may or may not have parameters after the action name. + +.. code-block:: lua + + -- Let's write some daft rules! + modules = { 'daf' } + + -- Block all queries with QNAME = example.com + daf.add('qname = example.com deny') + + -- Filters can be combined using AND/OR... + -- Block all queries with QNAME match regex and coming from given subnet + daf.add('qname ~ %w+.example.com AND src = 192.0.2.0/24 deny') + + -- We also can reroute addresses in response to alternate target + -- This reroutes 192.0.2.1 to localhost + daf.add('src = 127.0.0.0/8 reroute 192.0.2.1-127.0.0.1') + + -- Subnets work too, this reroutes a whole subnet + -- e.g. 192.0.2.55 to 127.0.0.55 + daf.add('src = 127.0.0.0/8 reroute 192.0.2.0/24-127.0.0.0') + + -- This rewrites all A answers for 'example.com' from + -- whatever the original address was to 127.0.0.2 + daf.add('src = 127.0.0.0/8 rewrite example.com A 127.0.0.2') + + -- Mirror queries matching given name to DNS logger + daf.add('qname ~ %w+.example.com mirror 127.0.0.2') + daf.add('qname ~ example-%d.com mirror 127.0.0.3@5353') + + -- Forward queries from subnet + daf.add('src = 127.0.0.1/8 forward 127.0.0.1@5353') + -- Forward to multiple targets + daf.add('src = 127.0.0.1/8 forward 127.0.0.1@5353,127.0.0.2@5353') + + -- Truncate queries based on destination IPs + daf.add('dst = 192.0.2.51 truncate') + + -- Disable a rule + daf.disable(2) + -- Enable a rule + daf.enable(2) + -- Delete a rule + daf.del(2) + + -- Delete all rules and start from scratch + daf.clear() + +.. warning:: Only the first matching rule's action is executed. Defining + additional actions for the same matching rule, e.g. ``src = 127.0.0.1/8``, + will have no effect. + +If you're not sure what firewall rules are in effect, see ``daf.rules``: + +.. code-block:: text + + -- Show active rules + > daf.rules + [1] => { + [rule] => { + [count] => 42 + [id] => 1 + [cb] => function: 0x1a3eda38 + } + [info] => qname = example.com AND src = 127.0.0.1/8 deny + [policy] => function: 0x1a3eda38 + } + [2] => { + [rule] => { + [suspended] => true + [count] => 123522 + [id] => 2 + [cb] => function: 0x1a3ede88 + } + [info] => qname ~ %w+.facebook.com AND src = 127.0.0.1/8 deny... + [policy] => function: 0x1a3ede88 + } + +Web interface +------------- + +If you have :ref:`HTTP/2 ` loaded, the firewall automatically loads as a snippet. +You can create, track, suspend and remove firewall rules from the web interface. +If you load both modules, you have to load `daf` after `http`. + +RESTful interface +----------------- + +The module also exports a RESTful API for operations over rule chains. + + +.. csv-table:: + :header: "URL", "HTTP Verb", "Action" + + "/daf", "GET", "Return JSON list of active rules." + "/daf", "POST", "Insert new rule, rule string is expected in body. Returns rule information in JSON." + "/daf/", "GET", "Retrieve a rule matching given ID." + "/daf/", "DELETE", "Delete a rule matching given ID." + "/daf///", "PATCH", "Modify given rule, for example /daf/3/active/false suspends rule 3." + +This interface is used by the web interface for all operations, but you can also use it directly +for testing. + +.. code-block:: bash + + # Get current rule set + $ curl -s -X GET http://localhost:8453/daf | jq . + {} + + # Create new rule + $ curl -s -X POST -d "src = 127.0.0.1 pass" http://localhost:8453/daf | jq . + { + "count": 0, + "active": true, + "info": "src = 127.0.0.1 pass", + "id": 1 + } + + # Disable rule + $ curl -s -X PATCH http://localhost:8453/daf/1/active/false | jq . + true + + # Retrieve a rule information + $ curl -s -X GET http://localhost:8453/daf/1 | jq . + { + "count": 4, + "active": true, + "info": "src = 127.0.0.1 pass", + "id": 1 + } + + # Delete a rule + $ curl -s -X DELETE http://localhost:8453/daf/1 | jq . + true diff --git a/modules/daf/daf.js b/modules/daf/daf.js new file mode 100644 index 0000000..05b171b --- /dev/null +++ b/modules/daf/daf.js @@ -0,0 +1,295 @@ +/* Filter grammar + * SPDX-License-Identifier: GPL-3.0-or-later */ +const dafg = { + key: {'qname': true, 'src': true, 'dst': true}, + op: {'=': true, '~': true}, + conj: {'and': true, 'or': true}, + action: {'pass': true, 'deny': true, 'drop': true, 'truncate': true, 'forward': true, 'reroute': true, 'rewrite': true, 'mirror': true}, + suggest: [ + 'QNAME = example.com', + 'QNAME ~ %d+.example.com', + 'SRC = 127.0.0.1', + 'SRC = 127.0.0.1/8', + 'DST = 127.0.0.1', + 'DST = 127.0.0.1/8', + /* Action examples */ + 'PASS', 'DENY', 'DROP', 'TRUNCATE', + 'FORWARD 127.0.0.1', + 'MIRROR 127.0.0.1', + 'REROUTE 127.0.0.1-192.168.1.1', + 'REROUTE 127.0.0.1/24-192.168.1.0', + 'REWRITE example.com A 127.0.0.1', + 'REWRITE example.com AAAA ::1', + ] +}; + +function setValidateHint(cls) { + var builderForm = $('#daf-builder-form'); + builderForm.removeClass('has-error has-warning has-success'); + if (cls) { + builderForm.addClass(cls); + } +} + +function validateToken(tok, tbl) { + if (tok.length > 0 && tok[0].length > 0) { + if (tbl[tok[0].toLowerCase()]) { + setValidateHint('has-success'); + return true; + } else { setValidateHint('has-error'); } + } else { setValidateHint('has-warning'); } + return false; +} + +function parseOption(tok) { + var key = tok.shift().toLowerCase(); + var op = null; + if (dafg.key[key]) { + op = tok.shift(); + if (op) { + op = op.toLowerCase(); + } + } + const item = { + text: key.toUpperCase() + ' ' + (op ? op.toUpperCase() : '') + ' ' + tok.join(' '), + }; + if (dafg.key[key]) { + item.class = 'tag-default'; + } else if (dafg.action[key]) { + item.class = 'tag-warning'; + } else if (dafg.conj[key]) { + item.class = 'tag-success'; + } + return item; +} + +function createOption(input) { + const item = parseOption(input.split(' ')); + item.value = input; + return item; +} + +function dafComplete(form) { + const items = form.items; + for (var i in items) { + const tok = items[i].split(' ')[0].toLowerCase(); + if (dafg.action[tok]) { + return true; + } + } + return false; +} + +function formatRule(input) { + const tok = input.split(' '); + var res = []; + while (tok.length > 0) { + const key = tok.shift().toLowerCase(); + if (dafg.key[key]) { + var item = parseOption([key, tok.shift(), tok.shift()]); + res.push(''+item.text+''); + } else if (dafg.action[key]) { + var item = parseOption([key].concat(tok)); + res.push(''+item.text+''); + tok.splice(0, tok.length); + } else if (dafg.conj[key]) { + var item = parseOption([key]); + res.push(''+item.text+''); + } + } + return res.join(''); +} + +function toggleRule(row, span, enabled) { + if (!enabled) { + span.removeClass('glyphicon-pause'); + span.addClass('glyphicon-play'); + row.addClass('warning'); + } else { + span.removeClass('glyphicon-play'); + span.addClass('glyphicon-pause'); + row.removeClass('warning'); + } +} + +function ruleControl(cell, type, url, action) { + const row = cell.parent(); + $.ajax({ + url: 'daf/' + row.data('rule-id') + url, + type: type, + success: action, + error: function (data) { + row.show(); + const reason = data.responseText.length > 0 ? data.responseText : 'internal error'; + cell.find('.alert').remove(); + cell.append( + '' + ); + }, + }); +} + +function bindRuleControl(cell) { + const row = cell.parent(); + cell.find('.daf-remove').click(function() { + row.hide(); + ruleControl(cell, 'DELETE', '', function (data) { + cell.parent().remove(); + }); + }); + cell.find('.daf-suspend').click(function() { + const span = $(this).find('span'); + ruleControl(cell, 'PATCH', span.hasClass('glyphicon-pause') ? '/active/false' : '/active/true'); + toggleRule(row, span, span.hasClass('glyphicon-play')); + }); +} + +function loadRule(rule, tbl) { + const row = $(''); + row.append('' + formatRule(rule.info) + ''); + row.append('' + rule.count + ''); + row.append(''); + row.append('' + + '
' + + '' + + '' + + '
'); + tbl.append(row); + /* Bind rule controls */ + bindRuleControl(row.find('.daf-ctl')); + toggleRule(row, row.find('.daf-suspend span'), rule.active); +} + +/* Load the filter table from JSON */ +function loadTable(resp) { + const tbl = $('#daf-rules') + tbl.children().remove(); + tbl.append('RuleMatchesRate') + for (var i in resp) { + loadRule(resp[i], tbl); + } +} + +document.addEventListener("DOMContentLoaded", () => { + /* Load the filter table. */ + $.ajax({ + url: 'daf', + type: 'get', + dataType: 'json', + success: loadTable + }); + /* Listen for counter updates */ + const wsStats = ('https:' == document.location.protocol ? 'wss://' : 'ws://') + location.host + '/daf'; + const ws = new Socket(wsStats); + var lastRateUpdate = Date.now(); + ws.onmessage = function(evt) { + var data = JSON.parse(evt.data); + /* Update heartbeat clock */ + var now = Date.now(); + var dt = now - lastRateUpdate; + lastRateUpdate = now; + /* Update match counts and rates */ + $('#daf-rules .daf-rate span').text(''); + for (var key in data) { + const row = $('tr[data-rule-id="'+key+'"]'); + if (row) { + const cell = row.find('.daf-count'); + const diff = data[key] - parseInt(cell.text()); + cell.text(data[key]); + const badge = row.find('.daf-rate span'); + if (diff > 0) { + /* Normalize difference to heartbeat (in msecs) */ + const rate = Math.ceil((1000 * diff) / dt); + badge.text(rate + ' pps'); + } + } + } + }; + /* Rule builder UI */ + $('#daf-builder').selectize({ + delimiter: ',', + persist: true, + highlight: true, + closeAfterSelect: true, + onItemAdd: function (input, item) { + setValidateHint(); + /* Prevent new rules when action is specified */ + const tok = input.split(' '); + if (dafg.action[tok[0].toLowerCase()]) { + $('#daf-add').focus(); + } else if(dafComplete(this)) { + /* No more rules after query is complete. */ + item.remove(); + } + }, + createFilter: function (input) { + const tok = input.split(' '); + var key, op, expr; + /* If there are already filters, allow conjunctions. */ + if (tok.length > 0 && this.items.length > 0 && dafg.conj[tok[0]]) { + setValidateHint(); + return true; + } + /* First token is expected to be filter key, + * or any postrule with a parameter */ + if (validateToken(tok, dafg.key)) { + key = tok.shift(); + } else if (tok.length > 1 && validateToken(tok, dafg.action)) { + setValidateHint(); + return true; + } else { + return false; + } + /* Input is a filter - second token must be operator */ + if (validateToken(tok, dafg.op)) { + op = tok.shift(); + } else { + return false; + } + /* Input is a filter - the rest of the tokens are RHS arguments. */ + if (tok.length > 0 && tok[0].length > 0) { + expr = tok.join(' '); + } else { + setValidateHint('has-warning'); + return false; + } + setValidateHint('has-success'); + return true; + }, + create: createOption, + render: { + item: function(item, escape) { + return '
' + escape(item.text) + ''; + }, + }, + }); + /* Add default suggestions. */ + const dafBuilder = $('#daf-builder')[0].selectize; + for (var i in dafg.suggest) { + dafBuilder.addOption(createOption(dafg.suggest[i])); + } + /* Rule builder submit */ + $('#daf-add').click(function () { + const form = $('#daf-builder-form').parent(); + if (dafBuilder.items.length == 0 || form.hasClass('has-error')) { + return; + } + /* Clear previous errors and resubmit. */ + form.parent().find('.alert').remove(); + $.post('daf', dafBuilder.items.join(' ')) + .done(function (data) { + dafBuilder.clear(); + loadRule(data, $('#daf-rules')); + }) + .fail(function (data) { + const reason = data.responseText.length > 0 ? data.responseText : 'internal error'; + form.after( + '' + ); + }); + }); +}); diff --git a/modules/daf/daf.lua b/modules/daf/daf.lua new file mode 100644 index 0000000..c3b089b --- /dev/null +++ b/modules/daf/daf.lua @@ -0,0 +1,392 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later + +local ffi = require('ffi') + +-- Load dependent modules +if not view then modules.load('view') end +if not policy then modules.load('policy') end + +-- Actions +local actions = { + pass = function() return policy.PASS end, + deny = function () return policy.DENY end, + drop = function() return policy.DROP end, + tc = function() return policy.TC end, + truncate = function() return policy.TC end, + forward = function (g) + local addrs = {} + local tok = g() + for addr in string.gmatch(tok, '[^,]+') do + table.insert(addrs, addr) + end + return policy.FORWARD(addrs) + end, + mirror = function (g) + return policy.MIRROR(g()) + end, + reroute = function (g) + local rules = {} + local tok = g() + while tok do + local from, to = tok:match '([^-]+)-(%S+)' + rules[from] = to + tok = g() + end + return policy.REROUTE(rules) + end, + rewrite = function (g) + local rules = {} + local tok = g() + while tok do + -- This is currently limited to A/AAAA rewriting + -- in fixed format ' ' + local _, to = g(), g() + rules[tok] = to + tok = g() + end + return policy.REROUTE(rules, true) + end, +} + +-- Filter rules per column +local filters = { + -- Filter on QNAME (either pattern or suffix match) + qname = function (g) + local op, val = g(), todname(g()) + if op == '~' then return policy.pattern(true, val:sub(2)) -- Skip leading label length + elseif op == '=' then return policy.suffix(true, {val}) + else error(string.format('invalid operator "%s" on qname', op)) end + end, + -- Filter on source address + src = function (g) + local op = g() + if op ~= '=' then error('address supports only "=" operator') end + return view.rule_src(true, g()) + end, + -- Filter on destination address + dst = function (g) + local op = g() + if op ~= '=' then error('address supports only "=" operator') end + return view.rule_dst(true, g()) + end, +} + +local function parse_filter(tok, g, prev) + if not tok then error(string.format('expected filter after "%s"', prev)) end + local filter = filters[tok:lower()] + if not filter then error(string.format('invalid filter "%s"', tok)) end + return filter(g) +end + +local function parse_rule(g) + -- Allow action without filter + local tok = g() + if tok == nil then + error('empty rule is not allowed') + end + if not filters[tok:lower()] then + return tok, nil + end + local f = parse_filter(tok, g) + -- Compose filter functions on conjunctions + -- or terminate filter chain and return + tok = g() + while tok do + if tok:lower() == 'and' then + local fa, fb = f, parse_filter(g(), g, tok) + f = function (req, qry) return fa(req, qry) and fb(req, qry) end + elseif tok:lower() == 'or' then + local fa, fb = f, parse_filter(g(), g, tok) + f = function (req, qry) return fa(req, qry) or fb(req, qry) end + else + break + end + tok = g() + end + return tok, f +end + +local function parse_query(g) + local ok, actid, filter = pcall(parse_rule, g) + if not ok then return nil, actid end + actid = actid:lower() + if not actions[actid] then return nil, string.format('invalid action "%s"', actid) end + -- Parse and interpret action + local action = actions[actid] + if type(action) == 'function' then + action = action(g) + end + return actid, action, filter +end + +-- Compile a rule described by query language +-- The query language is modelled by iptables/nftables +-- conj = AND | OR +-- op = IS | NOT | LIKE | IN +-- filter = +-- rule = | +-- action = PASS | DENY | DROP | TC | FORWARD +-- query = +local function compile(query) + local g = string.gmatch(query, '%S+') + return parse_query(g) +end + +-- @function Describe given rule for presentation +local function rule_info(r) + return {info=r.info, id=r.rule.id, active=(r.rule.suspended ~= true), count=r.rule.count} +end + +-- Module declaration +local M = { + rules = {} +} + +-- @function Remove a rule + +-- @function Cleanup module +function M.deinit() + if http then + local endpoints = http.configs._builtin.webmgmt.endpoints + endpoints['/daf'] = nil + endpoints['/daf.js'] = nil + http.snippets['/daf'] = nil + end +end + +-- @function Add rule +function M.add(rule) + -- Ignore duplicates + for _, r in ipairs(M.rules) do + if r.info == rule then return r end + end + local id, action, filter = compile(rule) + if not id then error(action) end + -- Combine filter and action into policy + local p + if filter then + p = function (req, qry) + return filter(req, qry) and action + end + else + p = function () + return action + end + end + local desc = {info=rule, policy=p} + -- Enforce in policy module, special actions are postrules + if id == 'reroute' or id == 'rewrite' then + desc.rule = policy.add(p, true) + else + desc.rule = policy.add(p) + end + table.insert(M.rules, desc) + return desc +end + +-- @function Remove a rule +function M.del(id) + for key, r in ipairs(M.rules) do + if r.rule.id == id then + policy.del(id) + table.remove(M.rules, key) + return true + end + end + return nil +end + +-- @function Remove all rules +function M.clear() + for _, r in ipairs(M.rules) do + policy.del(r.rule.id) + end + M.rules = {} + return true +end + +-- @function Find a rule +function M.get(id) + for _, r in ipairs(M.rules) do + if r.rule.id == id then + return r + end + end + return nil +end + +-- @function Enable/disable a rule +function M.toggle(id, val) + for _, r in ipairs(M.rules) do + if r.rule.id == id then + r.rule.suspended = not val + return true + end + end + return nil +end + +-- @function Enable/disable a rule +function M.disable(id) + return M.toggle(id, false) +end +function M.enable(id) + return M.toggle(id, true) +end + +local function consensus(op, ...) + local results = map(string.format(op, ...)) + local ret = results.n > 0 -- init to true for non-empty results + for idx=1, results.n do + ret = ret and results[idx] + end + return ret +end + +-- @function Public-facing API +local function api(h, stream) + local m = h:get(':method') + -- GET method + if m == 'GET' then + local path = h:get(':path') + local id = tonumber(path:match '/([^/]*)$') + if id then + local r = M.get(id) + if r then + return rule_info(r) + end + return 404, '"No such rule"' -- Not found + else + local ret = {} + for _, r in ipairs(M.rules) do + table.insert(ret, rule_info(r)) + end + return ret + end + -- DELETE method + elseif m == 'DELETE' then + local path = h:get(':path') + local id = tonumber(path:match '/([^/]*)$') + if id then + if consensus('daf.del(%s)', id) then + return tojson(true) + end + return 404, '"No such rule"' -- Not found + end + return 400 -- Request doesn't have numeric id + -- POST method + elseif m == 'POST' then + local query = stream:get_body_as_string() + if query then + local ok, r = pcall(M.add, query) + if not ok then return 500, string.format('"%s"', r:match('/([^/]+)$')) end + -- Dispatch to all other workers: + -- we ignore return values except error() because they are not serializable + consensus('daf.add "%s" and true', query) + return rule_info(r) + end + return 400 + -- PATCH method + elseif m == 'PATCH' then + local path = h:get(':path') + local id, action, val = path:match '(%d+)/([^/]*)/([^/]*)$' + id = tonumber(id) + if not id or not action or not val then + return 400 -- Request not well formatted + end + -- We do not support more actions + if action == 'active' then + if consensus('daf.toggle(%d, %s)', id, val == 'true' or 'false') then + return tojson(true) + else + return 404, '"No such rule"' + end + else + return 501, '"Action not implemented"' + end + end +end + +local function getmatches() + local update = {} + -- Must have string keys for JSON object and not an array + local inst_counters = map('ret = {} ' + .. 'for _, rule in ipairs(daf.rules) do ' + .. 'ret[tostring(rule.rule.id)] = rule.rule.count ' + .. 'end ' + .. 'return ret') + for inst_idx=1, inst_counters.n do + for r_id, r_cnt in pairs(inst_counters[inst_idx]) do + update[r_id] = (update[r_id] or 0) + r_cnt + end + end + return update +end + +-- @function Publish DAF statistics +local function publish(_, ws) + local ok, last = true, nil + while ok do + -- Check if we have new rule matches + local diff = {} + local has_update, update = pcall(getmatches) + if has_update then + if last then + for id, count in pairs(update) do + if not last[id] or last[id] < count then + diff[id] = count + end + end + end + last = update + end + -- Update counters when there is a new data + if next(diff) ~= nil then + ok = ws:send(tojson(diff)) + else + ok = ws:send_ping() + end + worker.sleep(1) + end +end + +function M.init() + -- avoid ordering problem between HTTP and daf module + event.after(0, M.config) +end + +-- @function Configure module +function M.config() + if not http then + log_warn(ffi.C.LOG_GRP_DAF, + 'HTTP API unavailable because HTTP module is not loaded, use modules.load("http")') + return + end + local endpoints = http.configs._builtin.webmgmt.endpoints + -- Export API and data publisher + endpoints['/daf.js'] = http.page('daf.js', 'daf') + endpoints['/daf'] = {'application/json', api, publish} + -- Export snippet + http.snippets['/daf'] = {'Application Firewall', [[ + +
+
+
+ +
+
+ +
+
+
+
+
+ + +
No rules here yet.
+
+
+ ]]} +end + +return M diff --git a/modules/daf/daf.test.lua b/modules/daf/daf.test.lua new file mode 100644 index 0000000..2a46393 --- /dev/null +++ b/modules/daf/daf.test.lua @@ -0,0 +1,80 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later + +-- do not attempt to contact outside world, operate only on cache +net.ipv4 = false +net.ipv6 = false +-- do not listen, test is driven by config code +env.KRESD_NO_LISTEN = true + +local path = worker.cwd..'/control/'..worker.pid +same(true, net.listen(path, nil, {kind = 'control'}), + 'new control sockets were created so map() can work') + +modules.load('hints > iterate') +modules.load('daf') + +hints['pass.'] = '127.0.0.1' +hints['deny.'] = '127.0.0.1' +hints['deny.'] = '127.0.0.1' +hints['drop.'] = '127.0.0.1' +hints['del.'] = '127.0.0.1' +hints['del2.'] = '127.0.0.1' +hints['toggle.'] = '127.0.0.1' + +local check_answer = require('test_utils').check_answer + +local function test_sanity() + check_answer('daf sanity (no rules)', 'pass.', kres.type.A, kres.rcode.NOERROR) + check_answer('daf sanity (no rules)', 'deny.', kres.type.A, kres.rcode.NOERROR) + check_answer('daf sanity (no rules)', 'drop.', kres.type.A, kres.rcode.NOERROR) + check_answer('daf sanity (no rules)', 'del.', kres.type.A, kres.rcode.NOERROR) + check_answer('daf sanity (no rules)', 'del2.', kres.type.A, kres.rcode.NOERROR) + check_answer('daf sanity (no rules)', 'toggle.', kres.type.A, kres.rcode.NOERROR) +end + +local function test_basic_actions() + daf.add('qname = pass. pass') + daf.add('qname = deny. deny') + daf.add('qname = drop. drop') + + check_answer('daf pass action', 'pass.', kres.type.A, kres.rcode.NOERROR) + check_answer('daf deny action', 'deny.', kres.type.A, kres.rcode.NXDOMAIN) + check_answer('daf drop action', 'drop.', kres.type.A, kres.rcode.SERVFAIL) +end + +local function test_del() + -- first matching rule is used + local first = daf.add('qname = del. deny') + local second = daf.add('qname = del2. deny') + + check_answer('daf del - first rule active', + 'del.', kres.type.A, kres.rcode.NXDOMAIN) + check_answer('daf del - second rule active', + 'del2.', kres.type.A, kres.rcode.NXDOMAIN) + daf.del(first.rule.id) + check_answer('daf del - first rule deleted', + 'del.', kres.type.A, kres.rcode.NOERROR) + daf.del(second.rule.id) + check_answer('daf del - second rule deleted', + 'del2.', kres.type.A, kres.rcode.NOERROR) +end + +local function test_toggle() + local toggle = daf.add('qname = toggle. deny') + + check_answer('daf - toggle active', + 'toggle.', kres.type.A, kres.rcode.NXDOMAIN) + daf.disable(toggle.rule.id) + check_answer('daf - toggle disabled', + 'toggle.', kres.type.A, kres.rcode.NOERROR) + daf.enable(toggle.rule.id) + check_answer('daf - toggle enabled', + 'toggle.', kres.type.A, kres.rcode.NXDOMAIN) +end + +return { + test_sanity, -- must be first, expects no daf rules + test_basic_actions, + test_del, + test_toggle, +} diff --git a/modules/daf/daf_http.test.lua b/modules/daf/daf_http.test.lua new file mode 100644 index 0000000..20d5f90 --- /dev/null +++ b/modules/daf/daf_http.test.lua @@ -0,0 +1,216 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- check prerequisites +local has_http = pcall(require, 'kres_modules.http') and pcall(require, 'http.request') +if not has_http then + -- skipping daf module test because http its not installed + os.exit(77) +else + local path = worker.cwd..'/control/'..worker.pid + same(true, net.listen(path, nil, {kind = 'control'}), + 'new control sockets were created so map() can work') + + local request = require('http.request') + + modules.load('http') + modules.load('daf') + + local bound + for _ = 1,1000 do + bound, _err = pcall(net.listen, '127.0.0.1', math.random(40000, 49999), { kind = 'webmgmt'}) + if bound then + break + end + end + assert(bound, 'unable to bind a port for HTTP module (1000 attempts)') + + -- globals for this module + local _, host, port, baseuri + local function start_server() + local server_fd = next(http.servers) + assert(server_fd) + local server = http.servers[server_fd].server + ok(server ~= nil, 'creates server instance') + _, host, port = server:localname() + ok(host and port, 'binds to an interface') + baseuri = string.format('http://%s:%d/daf', host, port) + end + + -- helper for returning useful values to test on +-- local function http_get(uri) +-- local headers, stream = assert(request.new_from_uri(uri):go(16)) +-- local body = assert(stream:get_body_as_string()) +-- return tonumber(headers:get(':status')), body, headers:get('content-type') +-- end + + local function http_req(uri, method, reqbody) + local req = assert(request.new_from_uri(baseuri .. uri)) + req.headers:upsert(':method', method) + req:set_body(reqbody) + local headers, stream = assert(req:go(16)) + local ansbody = assert(stream:get_body_as_string()) + return tonumber(headers:get(':status')), ansbody, headers:get('content-type') + end + + local function http_get(uri) + return http_req(uri, 'GET') + end + + -- compare two tables, expected value is specified as JSON + -- comparison relies on table_print which sorts table keys + local function compare_tables(expectedjson, gotjson, desc) + same( + table_print(fromjson(expectedjson)), + table_print(fromjson(gotjson)), + desc) + end + + -- test whether http interface responds and binds + local function test_daf_api() + local code, body, mime + -- rule listing /daf + code, body, mime = http_get('/') + same(code, 200, 'rule listing return 200 OK') + same(body, '{}', 'daf rule list is empty after start') + same(mime, 'application/json', 'daf rule list has application/json content type') + -- get non-existing rule + code, body = http_req('/0', 'GET') + same(code, 404, 'non-existing rule retrieval returns 404') + same(body, '"No such rule"', 'explanatory message is present') + + -- delete non-existing rule + code, body = http_req('/0', 'DELETE') + same(code, 404, 'non-existing rule deletion returns 404') + same(body, '"No such rule"', 'explanatory message is present') + + -- bad PATCH + code = http_req('/0', 'PATCH') + same(code, 400, 'PATCH detects missing parameters') + + -- bad POST + code = http_req('/', 'POST') + same(code, 500, 'POST without parameters is detected') + + -- POST first new rule + code, body, mime = http_req('/', 'POST', 'src = 192.0.2.0 pass') + same(code, 200, 'first POST succeeds') + compare_tables(body, + '{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}', + 'POST returns new rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- GET first rule + code, body, mime = http_req('/0', 'GET') + same(code, 200, 'GET for first rule succeeds') + compare_tables(body, + '{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}', + 'POST returns new rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- POST second new rule + code, body, mime = http_req('/', 'POST', 'src = 192.0.2.1 pass') + same(code, 200, 'second POST succeeds') + compare_tables(body, + '{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}', + 'POST returns new rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- GET second rule + code, body, mime = http_req('/1', 'GET') + same(code, 200, 'GET for second rule succeeds') + compare_tables(body, + '{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}', + 'POST returns new rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- PATCH first rule + code, body, mime = http_req('/0/active/false', 'PATCH') + same(code, 200, 'PATCH for first rule succeeds') + same(body, 'true', 'PATCH returns success in body') + same(mime, 'application/json', 'PATCH return value has application/json content type') + + -- GET modified first rule + code, body, mime = http_req('/0', 'GET') + same(code, 200, 'GET for first rule succeeds') + compare_tables(body, + '{"count":0,"active":false,"id":0,"info":"src = 192.0.2.0 pass"}', + 'GET returns modified rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- GET both rules + code, body, mime = http_req('/', 'GET') + same(code, 200, 'GET for both rule succeeds') + compare_tables(body, [[ + [ + {"count":0,"active":false,"info":"src = 192.0.2.0 pass","id":0}, + {"count":0,"active":true,"info":"src = 192.0.2.1 pass","id":1}] + ]], + 'GET returns both rules in JSON including modifications') + same(mime, 'application/json', 'rule list has application/json content type') + + -- PATCH first rule back to original state + code, body, mime = http_req('/0/active/true', 'PATCH') + same(code, 200, 'PATCH for first rule succeeds') + same(body, 'true', 'PATCH returns success in body') + same(mime, 'application/json', 'PATCH return value has application/json content type') + + -- GET modified (reversed) first rule + code, body, mime = http_req('/0', 'GET') + same(code, 200, 'GET for first rule succeeds') + compare_tables(body, + '{"count":0,"active":true,"id":0,"info":"src = 192.0.2.0 pass"}', + 'GET returns modified rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- DELETE first rule + code, body, mime = http_req('/0', 'DELETE') + same(code, 200, 'DELETE for first rule succeeds') + same(body, 'true', 'DELETE returns success in body') + same(mime, 'application/json', 'DELETE return value has application/json content type') + + -- GET deleted (first) rule + code, body = http_req('/0', 'GET') + same(code, 404, 'GET for deleted fails with 404') + same(body, '"No such rule"', 'failed GET contains explanatory message') + + -- GET second rule + code, body, mime = http_req('/1', 'GET') + same(code, 200, 'GET for second rule still succeeds') + compare_tables(body, + '{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}', + 'POST returns new rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- GET list of all rules + code, body, mime = http_req('/', 'GET') + same(code, 200, 'GET returns list with the remaining rule') + compare_tables(body, + '[{"count":0,"active":true,"id":1,"info":"src = 192.0.2.1 pass"}]', + 'rule list contains only the remaining rule in JSON') + same(mime, 'application/json', 'rule has application/json content type') + + -- try to DELETE first rule again + code, body = http_req('/0', 'DELETE') + same(code, 404, 'DELETE for already deleted rule fails with 404') + same(body, '"No such rule"', 'DELETE explains failure') + + -- DELETE second rule + code, body, mime = http_req('/1', 'DELETE') + same(code, 200, 'DELETE for second rule succeeds') + same(body, 'true', 'DELETE returns success in body') + same(mime, 'application/json', 'DELETE return value has application/json content type') + + -- GET (supposedly empty) list of all rules + code, body, mime = http_req('/', 'GET') + same(code, 200, 'GET returns list with the remaining rule') + compare_tables(body, '[]', 'rule list is now empty JSON list') + same(mime, 'application/json', 'rule has application/json content type') + end + + -- plan tests + local tests = { + start_server, + test_daf_api, + } + + return tests +end diff --git a/modules/daf/meson.build b/modules/daf/meson.build new file mode 100644 index 0000000..c46b749 --- /dev/null +++ b/modules/daf/meson.build @@ -0,0 +1,21 @@ +# LUA module: daf +# SPDX-License-Identifier: GPL-3.0-or-later + +config_tests += [ + ['daf', files('daf.test.lua')], + ['daf_http', files('daf_http.test.lua')], +] + +integr_tests += [ + ['daf', meson.current_source_dir() / 'test.integr'], +] + +lua_mod_src += [ + files('daf.lua'), +] + +# install daf.js +install_data( + 'daf.js', + install_dir: modules_dir / 'daf', +) diff --git a/modules/daf/test.integr/deckard.yaml b/modules/daf/test.integr/deckard.yaml new file mode 100644 index 0000000..455086f --- /dev/null +++ b/modules/daf/test.integr/deckard.yaml @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +programs: +- name: kresd + binary: kresd + additional: + - --noninteractive + templates: + - modules/daf/test.integr/kresd_config.j2 + - tests/integration/hints_zone.j2 + configs: + - config + - hints diff --git a/modules/daf/test.integr/kresd_config.j2 b/modules/daf/test.integr/kresd_config.j2 new file mode 100644 index 0000000..0381f77 --- /dev/null +++ b/modules/daf/test.integr/kresd_config.j2 @@ -0,0 +1,65 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +{% raw %} +-- make sure DNSSEC is turned off for tests +trust_anchors.remove('.') + +-- Disable RFC5011 TA update +if ta_update then + modules.unload('ta_update') +end + +-- Disable RFC8145 signaling, scenario doesn't provide expected answers +if ta_signal_query then + modules.unload('ta_signal_query') +end + +-- Disable RFC8109 priming, scenario doesn't provide expected answers +if priming then + modules.unload('priming') +end + +-- Disable this module because it make one priming query +if detect_time_skew then + modules.unload('detect_time_skew') +end + +modules.load('hints > iterate') +modules.load('daf') + +hints['hints.net.'] = '192.0.2.1' + +daf.add('src = 127.0.0.0/8 reroute 192.0.2.1-192.0.2.101') + +policy.add(policy.suffix(policy.PASS, {todname('test.')})) + +_hint_root_file('hints') +cache.size = 2*MB +log_level('debug') +{% endraw %} + +net = { '{{SELF_ADDR}}' } + + +{% if QMIN == "false" %} +option('NO_MINIMIZE', true) +{% else %} +option('NO_MINIMIZE', false) +{% endif %} + + +-- Self-checks on globals +assert(help() ~= nil) +assert(worker.id ~= nil) +-- Self-checks on facilities +assert(cache.count() == 0) +assert(cache.stats() ~= nil) +assert(cache.backends() ~= nil) +assert(worker.stats() ~= nil) +assert(net.interfaces() ~= nil) +-- Self-checks on loaded stuff +assert(net.list()[1].transport.ip == '{{SELF_ADDR}}') +assert(#modules.list() > 0) +-- Self-check timers +ev = event.recurrent(1 * sec, function (ev) return 1 end) +event.cancel(ev) +ev = event.after(0, function (ev) return 1 end) diff --git a/modules/daf/test.integr/module_daf.rpl b/modules/daf/test.integr/module_daf.rpl new file mode 100644 index 0000000..686f04c --- /dev/null +++ b/modules/daf/test.integr/module_daf.rpl @@ -0,0 +1,30 @@ +; SPDX-License-Identifier: GPL-3.0-or-later +; config options +; target-fetch-policy: "0 0 0 0 0" +; module-config: "iterator" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test DNS Application Firewall + +STEP 11 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +hints.net. IN A +ENTRY_END + +; test rewrite rule applies to hints +STEP 12 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +hints.net. IN A +SECTION ANSWER +hints.net. IN A 192.0.2.101 +ENTRY_END + + +SCENARIO_END diff --git a/modules/detect_time_jump/.packaging/test.config b/modules/detect_time_jump/.packaging/test.config new file mode 100644 index 0000000..7ed0e60 --- /dev/null +++ b/modules/detect_time_jump/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('detect_time_jump') +assert(detect_time_jump) +quit() diff --git a/modules/detect_time_jump/README.rst b/modules/detect_time_jump/README.rst new file mode 100644 index 0000000..066f5a3 --- /dev/null +++ b/modules/detect_time_jump/README.rst @@ -0,0 +1,22 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-detect_time_jump: + +Detect discontinuous jumps in the system time +============================================= + +This module detect discontinuous jumps in the system time when resolver +is running. It clears cache when a significant backward time jumps occurs. + +Time jumps are usually created by NTP time change or by admin intervention. +These change can affect cache records as they store timestamp and TTL in real +time. + +If you want to preserve cache during time travel you should disable +this module by ``modules.unload('detect_time_jump')``. + +Due to the way monotonic system time works on typical systems, +suspend-resume cycles will be perceived as forward time jumps, +but this direction of shift does not have the risk of using records +beyond their intended TTL, so forward jumps do not cause erasing the cache. + diff --git a/modules/detect_time_jump/detect_time_jump.lua b/modules/detect_time_jump/detect_time_jump.lua new file mode 100644 index 0000000..6c5b63f --- /dev/null +++ b/modules/detect_time_jump/detect_time_jump.lua @@ -0,0 +1,45 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module interface +local ffi = require('ffi') + +local mod = {} +mod.threshold = 10 * min +local event_id = nil + +-- Get time of last cache clear. Compute difference between realtime +-- and monotonic time. Compute difference of actual realtime and monotonic +-- time. In ideal case these differences should be almost same. +-- If they differ more than mod.threshold value then clear cache. +local function check_time() + local checkpoint = cache.checkpoint() + local cache_timeshift = checkpoint.walltime.sec * 1000 - checkpoint.monotime + local actual_timeshift = os.time() * 1000 - tonumber(ffi.C.kr_now()) + local jump_backward = cache_timeshift - actual_timeshift + if jump_backward > mod.threshold then + log_info(ffi.C.LOG_GRP_DETECTTIMEJUMP, "Detected backwards time jump, clearing cache.\n" .. + "But what does that mean? It means your future hasn't been written yet." + ) + cache.clear() + elseif -jump_backward > mod.threshold then + -- On Linux 4.17+ this shouldn't happen anymore: https://lwn.net/Articles/751482/ + log_info(ffi.C.LOG_GRP_DETECTTIMEJUMP, "Detected forward time jump. (Suspend-resume, possibly.)") + cache.checkpoint(true) + end +end + +function mod.init() + if event_id then + error("Module is already loaded.") + else + event_id = event.recurrent(1 * min , check_time) + end +end + +function mod.deinit() + if event_id then + event.cancel(event_id) + event_id = nil + end +end + +return mod diff --git a/modules/detect_time_skew/.packaging/test.config b/modules/detect_time_skew/.packaging/test.config new file mode 100644 index 0000000..3a37907 --- /dev/null +++ b/modules/detect_time_skew/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('detect_time_skew') +assert(detect_time_skew) +quit() diff --git a/modules/detect_time_skew/README.rst b/modules/detect_time_skew/README.rst new file mode 100644 index 0000000..be66bd0 --- /dev/null +++ b/modules/detect_time_skew/README.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-detect_time_skew: + +System time skew detector +========================= + +This module compares local system time with inception and expiration time +bounds in DNSSEC signatures for ``. NS`` records. If the local system time is +outside of these bounds, it is likely a misconfiguration which will cause +all DNSSEC validation (and resolution) to fail. + +In case of mismatch, a warning message will be logged to help with +further diagnostics. + +.. warning:: Information printed by this module can be forged by a network attacker! + System administrator MUST verify values printed by this module and + fix local system time using a trusted source. + +This module is useful for debugging purposes. It runs only once during resolver +start does not anything after that. It is enabled by default. +You may disable the module by appending +``modules.unload('detect_time_skew')`` to your configuration. diff --git a/modules/detect_time_skew/detect_time_skew.lua b/modules/detect_time_skew/detect_time_skew.lua new file mode 100644 index 0000000..6669ce8 --- /dev/null +++ b/modules/detect_time_skew/detect_time_skew.lua @@ -0,0 +1,83 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module interface +local ffi = require('ffi') + +local mod = {} +local event_id = nil + +-- Resolve callback +-- Check time validity of RRSIGs in priming query +-- luacheck: no unused args +local function check_time_callback(pkt, req) + if pkt == nil or pkt:rcode() ~= kres.rcode.NOERROR then + log_warn(ffi.C.LOG_GRP_DETECTTIMESKEW, "cannot resolve '.' NS") + return nil + end + local seen_rrsigs = 0 + local valid_rrsigs = 0 + local section = pkt:rrsets(kres.section.ANSWER) + local now = os.time() + local time_diff = 0 + local inception = 0 + local expiration = 0 + for i = 1, #section do + local rr = section[i] + assert(rr.type) + if rr.type == kres.type.RRSIG then + for k = 0, rr.rrs.count - 1 do + seen_rrsigs = seen_rrsigs + 1 + local rdata = rr:rdata_pt(k) + inception = ffi.C.kr_rrsig_sig_inception(rdata) + expiration = ffi.C.kr_rrsig_sig_expiration(rdata) + if now > expiration then + -- positive value = in the future + time_diff = now - expiration + elseif now < inception then + -- negative value = in the past + time_diff = now - inception + else + valid_rrsigs = valid_rrsigs + 1 + end + end + end + end + if seen_rrsigs == 0 then + log_info(ffi.C.LOG_GRP_DETECTTIMESKEW, "No RRSIGs received! ".. + "You really should configure DNSSEC trust anchor for the root.") + elseif valid_rrsigs == 0 then + log_warn(ffi.C.LOG_GRP_DETECTTIMESKEW, "Local system time %q seems to be at ".. + "least %u seconds in the %s. DNSSEC signatures for '.' NS ".. + "are not valid %s. Please check your system clock!", + os.date("%c", now), + math.abs(time_diff), + time_diff > 0 and "future" or "past", + time_diff > 0 and "anymore" or "yet") + else + log_info(ffi.C.LOG_GRP_DETECTTIMESKEW, "Local system time %q is within ".. + "RRSIG validity interval <%q,%q>.", os.date("%c", now), + os.date("%c", inception), os.date("%c", expiration)) + end +end + +-- Do uncached priming query and check time validity of RRSIGs. +local function check_time() + resolve(".", kres.type.NS, kres.class.IN, {"DNSSEC_WANT", "DNSSEC_CD", "NO_CACHE"}, + check_time_callback) +end + +function mod.init() + if event_id then + error("Module is already loaded.") + else + event_id = event.after(0 , check_time) + end +end + +function mod.deinit() + if event_id then + event.cancel(event_id) + event_id = nil + end +end + +return mod diff --git a/modules/dns64/.packaging/test.config b/modules/dns64/.packaging/test.config new file mode 100644 index 0000000..5abf524 --- /dev/null +++ b/modules/dns64/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('dns64') +assert(dns64) +quit() diff --git a/modules/dns64/README.rst b/modules/dns64/README.rst new file mode 100644 index 0000000..04d2427 --- /dev/null +++ b/modules/dns64/README.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-dns64: + +DNS64 +===== + +The module for :rfc:`6147` DNS64 AAAA-from-A record synthesis, it is used to enable client-server communication between an IPv6-only client and an IPv4-only server. See the well written `introduction`_ in the PowerDNS documentation. +If no address is passed (i.e. ``nil``), the well-known prefix ``64:ff9b::`` is used. + +.. _introduction: https://doc.powerdns.com/md/recursor/dns64 + +Simple example +-------------- + +.. code-block:: lua + + -- Load the module with default settings + modules = { 'dns64' } + -- Reconfigure later + dns64.config({ prefix = '2001:db8::aabb:0:0' }) + +.. warning:: The module currently won't work well with :func:`policy.STUB`. + Also, the IPv6 ``prefix`` passed in configuration is assumed to be ``/96``. + +.. tip:: The A record sub-requests will be DNSSEC secured, but the synthetic AAAA records can't be. Make sure the last mile between stub and resolver is secure to avoid spoofing. + + +Advanced options +---------------- + +TTL in CNAME generated in the reverse ``ip6.arpa.`` subtree is configurable: + +.. code-block:: lua + + dns64.config({ prefix = '2001:db8:77ff::', rev_ttl = 300 }) + +You can specify a set of IPv6 subnets that are disallowed in answer. +If they appear, they will be replaced by AAAAs generated from As. + +.. code-block:: lua + + dns64.config({ + prefix = '2001:db8:3::', + exclude_subnets = { '2001:db8:888::/48', '::ffff/96' }, + }) + -- You could even pass '::/0' to always force using generated AAAAs. + +In case you don't want dns64 for all clients, +you can set ``DNS64_DISABLE`` flag via the :ref:`view module `. + +.. code-block:: lua + + modules = { 'dns64', 'view' } + -- disable dns64 for all IPv4 source addresses + view:addr('0.0.0.0/0', policy.all(policy.FLAGS('DNS64_DISABLE'))) + -- disable dns64 for all IPv6 source addresses + view:addr('::/0', policy.all(policy.FLAGS('DNS64_DISABLE'))) + -- re-enable dns64 for two IPv6 subnets + view:addr('2001:db8:11::/48', policy.all(policy.FLAGS(nil, 'DNS64_DISABLE'))) + view:addr('2001:db8:93::/48', policy.all(policy.FLAGS(nil, 'DNS64_DISABLE'))) + diff --git a/modules/dns64/dns64.lua b/modules/dns64/dns64.lua new file mode 100644 index 0000000..b4fb1ec --- /dev/null +++ b/modules/dns64/dns64.lua @@ -0,0 +1,220 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module interface +local kres = require('kres') +local ffi = require('ffi') +local C = ffi.C +local M = { layer = { } } +local addr_buf = ffi.new('char[16]') + +--[[ +Missing parts of the RFC: + > The implementation SHOULD support mapping of separate IPv4 address + > ranges to separate IPv6 prefixes for AAAA record synthesis. This + > allows handling of special use IPv4 addresses [RFC5735]. + + TODO: support different prefix lengths, defaulting to /96 if not specified + https://tools.ietf.org/html/rfc6052#section-2.2 +]] + +-- Config +function M.config(conf) + if type(conf) ~= 'table' then + conf = { prefix = conf } + end + M.proxy = kres.str2ip(tostring(conf.prefix or '64:ff9b::')) + if M.proxy == nil or #M.proxy ~= 16 then + error(string.format('[dns64] %q is not a valid IPv6 address', conf.prefix), 2) + end + + M.rev_ttl = conf.rev_ttl or 60 + M.rev_suffix = kres.str2dname(M.proxy + :sub(1, 96/8) + -- hexdump, reverse, intersperse by dots + :gsub('.', function (ch) return string.format('%02x', string.byte(ch)) end) + :reverse() + :gsub('.', '%1.') + .. 'ip6.arpa.' + ) + + -- RFC 6147.5.1.4 + M.exclude_subnets = {} + if conf.exclude_subnets ~= nil and type(conf.exclude_subnets) ~= 'table' then + error('[dns64] .exclude_subnets is not a table') + end + for _, subnet_cfg in ipairs(conf.exclude_subnets or { '::ffff/96' }) do + local subnet = {} + subnet.prefix = ffi.new('char[16]') + subnet.bitlen = C.kr_straddr_subnet(subnet.prefix, tostring(subnet_cfg)) + if subnet.bitlen < 0 or not string.find(subnet_cfg, ':', 1, true) then + error(string.format('[dns64] failed to parse IPv6 subnet: %q', subnet_cfg)) + end + table.insert(M.exclude_subnets, subnet) + end +end + +-- Filter the AAAA records from the last ANSWER, return iff it's NODATA afterwards. +-- Currently the implementation is lazy and kills it all if any AAAA is excluded. +local function do_exclude_prefixes(qry) + local rrsel = qry.request.answ_selected + for i = 0, tonumber(rrsel.len) - 1 do + local rr_e = rrsel.at[i] -- struct ranked_rr_array_entry + if rr_e.qry_uid ~= qry.uid or rr_e.rr.type ~= kres.type.AAAA or not rr_e.to_wire + then goto next_rrset end + -- Found answer AAAA RRset + for _, subnet in ipairs(M.exclude_subnets) do + for j = 0, rr_e.rr:rdcount() - 1 do + local rd = rr_e.rr:rdata_pt(j) + if rd.len == 16 and C.kr_bitcmp(subnet.prefix, rd.data, subnet.bitlen) == 0 then + -- We can't use this RR. TODO: and we're lazy, + -- so we kill the whole RRset instead of filtering. + rr_e.to_wire = false + return true + end + end + end + -- We can use the answer -> return false + -- We use a nonsensical if to fool the parser; is return adjacent to a label forbidden? + if true then return false end + + ::next_rrset:: + end + -- No RRset found, it was probably NODATA. + return true +end + +function M.layer.consume(state, req, pkt) + if state == kres.FAIL then return state end + local qry = req:current() + -- Observe only final answers in IN class where request has no CD flag. + if M.proxy == nil or not qry.flags.RESOLVED or qry.flags.DNS64_DISABLE + or pkt:qclass() ~= kres.class.IN or req.qsource.packet:cd() then + return state + end + + -- Observe final AAAA NODATA responses to the current SNAME. + if pkt:qtype() == kres.type.AAAA and pkt:qname() == qry:name() + and qry.flags.RESOLVED and not qry.flags.CNAME and qry.parent == nil + and pkt:rcode() == kres.rcode.NOERROR and do_exclude_prefixes(qry) then + -- Start a *marked* corresponding A sub-query. + local extraFlags = kres.mk_qflags({}) + extraFlags.DNSSEC_WANT = qry.flags.DNSSEC_WANT + extraFlags.AWAIT_CUT = true + extraFlags.DNS64_MARK = true + req:push(pkt:qname(), kres.type.A, kres.class.IN, extraFlags, qry) + return state + end + + + -- Observe answer to the marked sub-query, and convert all A records in ANSWER + -- to corresponding AAAA records to be put into the request's answer. + if not qry.flags.DNS64_MARK then return state end + -- Find rank for the NODATA answer. + -- That will result into corresponding AD flag. See RFC 6147 5.5.2. + local neg_rank + if qry.parent.flags.DNSSEC_WANT and not qry.parent.flags.DNSSEC_INSECURE + then neg_rank = ffi.C.KR_RANK_SECURE + else neg_rank = ffi.C.KR_RANK_INSECURE + end + -- Find TTL bound from SOA, according to RFC 6147 5.1.7.4. + local max_ttl = 600 + for i = 1, tonumber(req.auth_selected.len) do + local entry = req.auth_selected.at[i - 1] + if entry.qry_uid == qry.parent.uid and entry.rr + and entry.rr.type == kres.type.SOA + and entry.rr.rclass == kres.class.IN then + max_ttl = entry.rr:ttl() + end + end + -- Find the As and do the conversion itself. + for i = 1, tonumber(req.answ_selected.len) do + local orig = req.answ_selected.at[i - 1] + if orig.qry_uid == qry.uid and orig.rr.type == kres.type.A then + local rank = neg_rank + if orig.rank < rank then rank = orig.rank end + -- Disable GC, as this object doesn't own owner or RDATA, it's just a reference + local ttl = orig.rr:ttl() + if ttl > max_ttl then ttl = max_ttl end + local rrs = ffi.gc(kres.rrset(nil, kres.type.AAAA, orig.rr.rclass, ttl), nil) + rrs._owner = orig.rr._owner + for k = 1, orig.rr.rrs.count do + local rdata = orig.rr:rdata( k - 1 ) + ffi.copy(addr_buf, M.proxy, 12) + ffi.copy(addr_buf + 12, rdata, 4) + ffi.C.knot_rrset_add_rdata(rrs, ffi.string(addr_buf, 16), 16, req.pool) + end + ffi.C.kr_ranked_rrarray_add( + req.answ_selected, + rrs, + rank, + true, + qry.uid, + req.pool) + end + end + ffi.C.kr_ranked_rrarray_finalize(req.answ_selected, qry.uid, req.pool) + req:set_extended_error(kres.extended_error.FORGED, "BHD4: DNS64 synthesis") +end + +local function hexchar2int(char) + if char >= string.byte('0') and char <= string.byte('9') then + return char - string.byte('0') + elseif char >= string.byte('a') and char <= string.byte('f') then + return 10 + char - string.byte('a') + else + return nil + end +end + +-- Map the reverse subtree by generating CNAMEs; similarly to the hints module. +-- +-- RFC 6147.5.3.1.2 says we SHOULD only generate CNAME if it points to data, +-- but I can't see what's wrong with a CNAME to an NXDOMAIN/NODATA +-- Reimplementation idea: as-if we had a DNAME in policy/cache? +function M.layer.produce(_, req, pkt) + local qry = req.current_query + local sname = qry.sname + if ffi.C.knot_dname_in_bailiwick(sname, M.rev_suffix) < 0 or qry.flags.DNS64_DISABLE + then return end + -- Update packet question if it was minimized. + qry.flags.NO_MINIMIZE = true + if not ffi.C.knot_dname_is_equal(pkt.wire + 12, sname) or not pkt:has_wire() then + if not pkt:recycle() or not pkt:question(sname, qry.sclass, qry.stype) + then return end + end + + -- Generate a CNAME iff the full address is queried; otherwise leave NODATA. + local labels_missing = 16*2 + 2 - ffi.C.knot_dname_labels(sname, nil) + if labels_missing == 0 then + -- Transforming v6 labels (hex) to v4 ones (decimal) isn't trivial: + local labels = sname + local v4name = '' + for _ = 1, 4 do -- append one IPv4 label at a time into v4name + local v4octet = 0 + for i = 0, 1 do + if labels[0] ~= 1 then return end + local ch = hexchar2int(labels[1]) + if not ch then return end + v4octet = v4octet + ch * 16^i + labels = labels + 2 + end + v4octet = tostring(v4octet) + v4name = v4name .. string.char(#v4octet) .. v4octet + end + v4name = v4name .. '\7in-addr\4arpa\0' + if not pkt:put(sname, M.rev_ttl, kres.class.IN, kres.type.CNAME, v4name) + then return end + end + + -- Simple finishing touches. + if labels_missing < 0 then -- and use NXDOMAIN for too long queries + pkt:rcode(kres.rcode.NXDOMAIN) + else + pkt:rcode(kres.rcode.NOERROR) + end + pkt.parsed = pkt.size + pkt:aa(true) + pkt:qr(true) + qry.flags.CACHED = true +end + +return M diff --git a/modules/dns64/dns64.test.lua b/modules/dns64/dns64.test.lua new file mode 100644 index 0000000..45956a4 --- /dev/null +++ b/modules/dns64/dns64.test.lua @@ -0,0 +1,53 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local condition = require('cqueues.condition') + +-- setup resolver +modules = { 'hints', 'dns64' } +hints['dns64.example'] = '192.168.1.1' +hints.use_nodata(true) -- Respond NODATA to AAAA query +hints.ttl(60) +dns64.config('fe80::21b:77ff:0:0') + +-- helper to wait for query resolution +local function wait_resolve(qname, qtype) + local waiting, done, cond = false, false, condition.new() + local rcode, answers = kres.rcode.SERVFAIL, {} + resolve { + name = qname, + type = qtype, + finish = function (answer, _) + rcode = answer:rcode() + answers = answer:section(kres.section.ANSWER) + -- Signal as completed + if waiting then + cond:signal() + end + done = true + end, + } + -- Wait if it didn't finish immediately + if not done then + waiting = true + cond:wait() + end + return rcode, answers +end + +-- test builtin rules +local function test_builtin_rules() + local rcode, answers = wait_resolve('dns64.example', kres.type.AAAA) + same(rcode, kres.rcode.NOERROR, 'dns64.example returns NOERROR') + same(#answers, 1, 'dns64.example synthesised answer') + local expect = {'dns64.example.', '60', 'AAAA', 'fe80::21b:77ff:c0a8:101'} + if #answers > 0 then + local rr = {kres.rr2str(answers[1]):match('(%S+)%s+(%S+)%s+(%S+)%s+(%S+)')} + same(rr, expect, 'dns64.example synthesised correct AAAA record') + end +end + +-- plan tests +local tests = { + test_builtin_rules, +} + +return tests diff --git a/modules/dnstap/.packaging/centos/7/builddeps b/modules/dnstap/.packaging/centos/7/builddeps new file mode 100644 index 0000000..d3ab354 --- /dev/null +++ b/modules/dnstap/.packaging/centos/7/builddeps @@ -0,0 +1,3 @@ +fstrm-devel +protobuf-c-devel +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/centos/7/rundeps b/modules/dnstap/.packaging/centos/7/rundeps new file mode 100644 index 0000000..06c2792 --- /dev/null +++ b/modules/dnstap/.packaging/centos/7/rundeps @@ -0,0 +1,2 @@ +fstrm +protobuf-c diff --git a/modules/dnstap/.packaging/centos/8/builddeps b/modules/dnstap/.packaging/centos/8/builddeps new file mode 100644 index 0000000..d3ab354 --- /dev/null +++ b/modules/dnstap/.packaging/centos/8/builddeps @@ -0,0 +1,3 @@ +fstrm-devel +protobuf-c-devel +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/centos/8/rundeps b/modules/dnstap/.packaging/centos/8/rundeps new file mode 100644 index 0000000..06c2792 --- /dev/null +++ b/modules/dnstap/.packaging/centos/8/rundeps @@ -0,0 +1,2 @@ +fstrm +protobuf-c diff --git a/modules/dnstap/.packaging/debian/10/builddeps b/modules/dnstap/.packaging/debian/10/builddeps new file mode 100644 index 0000000..417dc04 --- /dev/null +++ b/modules/dnstap/.packaging/debian/10/builddeps @@ -0,0 +1,3 @@ +libfstrm-dev +libprotobuf-c-dev +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/debian/10/rundeps b/modules/dnstap/.packaging/debian/10/rundeps new file mode 100644 index 0000000..a726e12 --- /dev/null +++ b/modules/dnstap/.packaging/debian/10/rundeps @@ -0,0 +1,2 @@ +libfstrm0 +libprotobuf-c1 diff --git a/modules/dnstap/.packaging/debian/9/builddeps b/modules/dnstap/.packaging/debian/9/builddeps new file mode 100644 index 0000000..417dc04 --- /dev/null +++ b/modules/dnstap/.packaging/debian/9/builddeps @@ -0,0 +1,3 @@ +libfstrm-dev +libprotobuf-c-dev +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/debian/9/rundeps b/modules/dnstap/.packaging/debian/9/rundeps new file mode 100644 index 0000000..a726e12 --- /dev/null +++ b/modules/dnstap/.packaging/debian/9/rundeps @@ -0,0 +1,2 @@ +libfstrm0 +libprotobuf-c1 diff --git a/modules/dnstap/.packaging/fedora/31/builddeps b/modules/dnstap/.packaging/fedora/31/builddeps new file mode 100644 index 0000000..d3ab354 --- /dev/null +++ b/modules/dnstap/.packaging/fedora/31/builddeps @@ -0,0 +1,3 @@ +fstrm-devel +protobuf-c-devel +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/fedora/31/rundeps b/modules/dnstap/.packaging/fedora/31/rundeps new file mode 100644 index 0000000..06c2792 --- /dev/null +++ b/modules/dnstap/.packaging/fedora/31/rundeps @@ -0,0 +1,2 @@ +fstrm +protobuf-c diff --git a/modules/dnstap/.packaging/fedora/32/builddeps b/modules/dnstap/.packaging/fedora/32/builddeps new file mode 100644 index 0000000..d3ab354 --- /dev/null +++ b/modules/dnstap/.packaging/fedora/32/builddeps @@ -0,0 +1,3 @@ +fstrm-devel +protobuf-c-devel +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/fedora/32/rundeps b/modules/dnstap/.packaging/fedora/32/rundeps new file mode 100644 index 0000000..06c2792 --- /dev/null +++ b/modules/dnstap/.packaging/fedora/32/rundeps @@ -0,0 +1,2 @@ +fstrm +protobuf-c diff --git a/modules/dnstap/.packaging/leap/15.2/builddeps b/modules/dnstap/.packaging/leap/15.2/builddeps new file mode 100644 index 0000000..30f8d9e --- /dev/null +++ b/modules/dnstap/.packaging/leap/15.2/builddeps @@ -0,0 +1,3 @@ +fstrm-devel +libprotobuf-c-devel +protobuf-c diff --git a/modules/dnstap/.packaging/leap/15.2/rundeps b/modules/dnstap/.packaging/leap/15.2/rundeps new file mode 100644 index 0000000..06c2792 --- /dev/null +++ b/modules/dnstap/.packaging/leap/15.2/rundeps @@ -0,0 +1,2 @@ +fstrm +protobuf-c diff --git a/modules/dnstap/.packaging/test.config b/modules/dnstap/.packaging/test.config new file mode 100644 index 0000000..5966860 --- /dev/null +++ b/modules/dnstap/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('dnstap') +assert(dnstap) +quit() diff --git a/modules/dnstap/.packaging/ubuntu/16.04/builddeps b/modules/dnstap/.packaging/ubuntu/16.04/builddeps new file mode 100644 index 0000000..417dc04 --- /dev/null +++ b/modules/dnstap/.packaging/ubuntu/16.04/builddeps @@ -0,0 +1,3 @@ +libfstrm-dev +libprotobuf-c-dev +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/ubuntu/16.04/rundeps b/modules/dnstap/.packaging/ubuntu/16.04/rundeps new file mode 100644 index 0000000..a726e12 --- /dev/null +++ b/modules/dnstap/.packaging/ubuntu/16.04/rundeps @@ -0,0 +1,2 @@ +libfstrm0 +libprotobuf-c1 diff --git a/modules/dnstap/.packaging/ubuntu/18.04/builddeps b/modules/dnstap/.packaging/ubuntu/18.04/builddeps new file mode 100644 index 0000000..417dc04 --- /dev/null +++ b/modules/dnstap/.packaging/ubuntu/18.04/builddeps @@ -0,0 +1,3 @@ +libfstrm-dev +libprotobuf-c-dev +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/ubuntu/18.04/rundeps b/modules/dnstap/.packaging/ubuntu/18.04/rundeps new file mode 100644 index 0000000..a726e12 --- /dev/null +++ b/modules/dnstap/.packaging/ubuntu/18.04/rundeps @@ -0,0 +1,2 @@ +libfstrm0 +libprotobuf-c1 diff --git a/modules/dnstap/.packaging/ubuntu/20.04/builddeps b/modules/dnstap/.packaging/ubuntu/20.04/builddeps new file mode 100644 index 0000000..417dc04 --- /dev/null +++ b/modules/dnstap/.packaging/ubuntu/20.04/builddeps @@ -0,0 +1,3 @@ +libfstrm-dev +libprotobuf-c-dev +protobuf-c-compiler diff --git a/modules/dnstap/.packaging/ubuntu/20.04/rundeps b/modules/dnstap/.packaging/ubuntu/20.04/rundeps new file mode 100644 index 0000000..a726e12 --- /dev/null +++ b/modules/dnstap/.packaging/ubuntu/20.04/rundeps @@ -0,0 +1,2 @@ +libfstrm0 +libprotobuf-c1 diff --git a/modules/dnstap/README.rst b/modules/dnstap/README.rst new file mode 100644 index 0000000..456d218 --- /dev/null +++ b/modules/dnstap/README.rst @@ -0,0 +1,42 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-dnstap: + +Dnstap (traffic collection) +=========================== + +The ``dnstap`` module supports logging DNS requests and responses to a unix +socket in `dnstap format `_ using fstrm framing library. +This logging is useful if you need effectively log all DNS traffic. + +The unix socket and the socket reader must be present before starting resolver instances. +Also it needs appropriate filesystem permissions; +the typical user and group of the daemon are called ``knot-resolver``. + +Tunables: + +* ``socket_path``: the unix socket file where dnstap messages will be sent +* ``identity``: identity string as typically returned by an "NSID" (RFC 5001) query, empty by default +* ``version``: version string of the resolver, defaulting to "Knot Resolver major.minor.patch" +* ``client.log_queries``: if ``true`` queries from downstream in wire format will be logged +* ``client.log_responses``: if ``true`` responses to downstream in wire format will be logged + +.. Very non-standard and it seems unlikely that others want to collect the RTT. +.. * ``client.log_tcp_rtt``: if ``true`` and on Linux, + add "extra" field with "rtt=12345\n", + signifying kernel's current estimate of RTT micro-seconds for the non-UDP connection + (alongside every arrived DNS message). + +.. code-block:: lua + + modules = { + dnstap = { + socket_path = "/tmp/dnstap.sock", + identity = nsid.name() or "", + version = "My Custom Knot Resolver " .. package_version(), + client = { + log_queries = true, + log_responses = true, + }, + } + } diff --git a/modules/dnstap/dnstap.c b/modules/dnstap/dnstap.c new file mode 100644 index 0000000..7572667 --- /dev/null +++ b/modules/dnstap/dnstap.c @@ -0,0 +1,524 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * + * @file dnstap.c + * @brief dnstap based query logging support + * + */ + +#include "lib/module.h" +#include "modules/dnstap/dnstap.pb-c.h" + +#include "contrib/cleanup.h" +#include "daemon/session.h" +#include "daemon/worker.h" +#include "lib/layer.h" +#include "lib/resolve.h" + +#include +#include +#include +#include +#include + +#define DEBUG_MSG(fmt, ...) kr_log_debug(DNSTAP, fmt, ##__VA_ARGS__); +#define ERROR_MSG(fmt, ...) kr_log_error(DNSTAP, fmt, ##__VA_ARGS__); +#define CFG_SOCK_PATH "socket_path" +#define CFG_IDENTITY_STRING "identity" +#define CFG_VERSION_STRING "version" +#define CFG_LOG_CLIENT_PKT "client" +#define CFG_LOG_QR_PKT "log_queries" +#define CFG_LOG_RESP_PKT "log_responses" +#define CFG_LOG_TCP_RTT "log_tcp_rtt" +#define DEFAULT_SOCK_PATH "/tmp/dnstap.sock" +#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap" +#define DNSTAP_INITIAL_BUF_SIZE 256 + +#define auto_destroy_uopts __attribute__((cleanup(fstrm_unix_writer_options_destroy))) +#define auto_destroy_wopts __attribute__((cleanup(fstrm_writer_options_destroy))) + +/* + * Internal processing phase + * Distinguishes whether query or response should be processed + */ +enum dnstap_log_phase { + CLIENT_QUERY_PHASE = 0, + CLIENT_RESPONSE_PHASE, +}; + +/* Internal data structure */ +struct dnstap_data { + char *identity; + size_t identity_len; + char *version; + size_t version_len; + bool log_qr_pkt; + bool log_resp_pkt; + bool log_tcp_rtt; + struct fstrm_iothr *iothread; + struct fstrm_iothr_queue *ioq; +}; + +/* + * dt_pack packs the dnstap message for transport + * https://gitlab.nic.cz/knot/knot-dns/blob/master/src/contrib/dnstap/dnstap.c#L24 + * */ +uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz) +{ + ProtobufCBufferSimple sbuf = { { NULL } }; + + sbuf.base.append = protobuf_c_buffer_simple_append; + sbuf.len = 0; + sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE; + sbuf.data = malloc(sbuf.alloced); + if (sbuf.data == NULL) { + return NULL; + } + sbuf.must_free_data = true; + + *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf); + *buf = sbuf.data; + return *buf; +} + +/* set_address fills in address detail in dnstap_message + * https://gitlab.nic.cz/knot/knot-dns/blob/master/src/contrib/dnstap/message.c#L28 + */ +static void set_address(const struct sockaddr *sockaddr, + ProtobufCBinaryData *addr, + protobuf_c_boolean *has_addr, + uint32_t *port, + protobuf_c_boolean *has_port) { + const char *saddr = kr_inaddr(sockaddr); + if (saddr == NULL) { + *has_addr = false; + *has_port = false; + return; + } + + addr->data = (uint8_t *)(saddr); + addr->len = kr_inaddr_len(sockaddr); + *has_addr = true; + *port = kr_inaddr_port(sockaddr); + *has_port = true; +} + +#ifndef HAS_TCP_INFO + /* TCP RTT: not portable; not sure where else it might work. */ + #define HAS_TCP_INFO __linux__ +#endif +#if HAS_TCP_INFO +/** Fill a tcp_info or return kr_error(). */ +static int get_tcp_info(const struct kr_request *req, struct tcp_info *info) +{ + if(kr_fails_assert(req && info)) + return kr_error(EINVAL); + if (!req->qsource.dst_addr || !req->qsource.flags.tcp) /* not TCP-based */ + return -abs(ENOENT); + /* First obtain the file-descriptor. */ + uv_handle_t *h = session_get_handle(worker_request_get_source_session(req)); + uv_os_fd_t fd; + int ret = uv_fileno(h, &fd); + if (ret) + return kr_error(ret); + + socklen_t tcp_info_length = sizeof(*info); + if (getsockopt(fd, SOL_TCP, TCP_INFO, info, &tcp_info_length)) + return kr_error(errno); + return kr_ok(); +} +#endif + +/* dnstap_log prepares dnstap message and sends it to fstrm + * + * Return codes are kr_error(E*) and unused for now. + */ +static int dnstap_log(kr_layer_t *ctx, enum dnstap_log_phase phase) { + const struct kr_request *req = ctx->req; + const struct kr_module *module = ctx->api->data; + const struct kr_rplan *rplan = &req->rplan; + const struct dnstap_data *dnstap_dt = module->data; + + if (!req->qsource.addr) { + return kr_ok(); + } + + /* check if we have a valid iothread */ + if (!dnstap_dt->iothread || !dnstap_dt->ioq) { + DEBUG_MSG("dnstap_dt->iothread or dnstap_dt->ioq is NULL\n"); + return kr_error(EFAULT); + } + + /* Create dnstap message */ + Dnstap__Message m; + Dnstap__Dnstap dnstap = DNSTAP__DNSTAP__INIT; + dnstap.type = DNSTAP__DNSTAP__TYPE__MESSAGE; + dnstap.message = &m; + + memset(&m, 0, sizeof(m)); + + m.base.descriptor = &dnstap__message__descriptor; + + if (req->qsource.addr) { + set_address(req->qsource.addr, + &m.query_address, + &m.has_query_address, + &m.query_port, + &m.has_query_port); + } + + if (req->qsource.dst_addr) { + if (req->qsource.flags.http) { + m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__DOH; + } else if (req->qsource.flags.tls) { + m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__DOT; + } else if (req->qsource.flags.tcp) { + m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP; + } else { + m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP; + } + m.has_socket_protocol = true; + + set_address(req->qsource.dst_addr, + &m.response_address, + &m.has_response_address, + &m.response_port, + &m.has_response_port); + switch (req->qsource.dst_addr->sa_family) { + case AF_INET: + m.socket_family = DNSTAP__SOCKET_FAMILY__INET; + m.has_socket_family = true; + break; + case AF_INET6: + m.socket_family = DNSTAP__SOCKET_FAMILY__INET6; + m.has_socket_family = true; + break; + } + } + + char dnstap_extra_buf[24]; + if (phase == CLIENT_QUERY_PHASE) { + m.type = DNSTAP__MESSAGE__TYPE__CLIENT_QUERY; + + if (dnstap_dt->log_qr_pkt) { + const knot_pkt_t *qpkt = req->qsource.packet; + m.has_query_message = qpkt != NULL; + if (qpkt != NULL) { + m.query_message.len = qpkt->size; + m.query_message.data = qpkt->wire; + } + } + + /* set query time to the timestamp of the first kr_query */ + if (rplan->initial) { + struct kr_query *first = rplan->initial; + + m.query_time_sec = first->timestamp.tv_sec; + m.has_query_time_sec = true; + m.query_time_nsec = first->timestamp.tv_usec * 1000; + m.has_query_time_nsec = true; + } +#if HAS_TCP_INFO + struct tcp_info ti = { 0 }; + if (dnstap_dt->log_tcp_rtt && get_tcp_info(req, &ti) == kr_ok()) { + int len = snprintf(dnstap_extra_buf, sizeof(dnstap_extra_buf), + "rtt=%u\n", (unsigned)ti.tcpi_rtt); + if (len < sizeof(dnstap_extra_buf)) { + dnstap.extra.data = (uint8_t *)dnstap_extra_buf; + dnstap.extra.len = len; + dnstap.has_extra = true; + } + } +#else + (void)dnstap_extra_buf; +#endif + } else if (phase == CLIENT_RESPONSE_PHASE) { + m.type = DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE; + + /* current time */ + struct timeval now; + gettimeofday(&now, NULL); + + if (dnstap_dt->log_resp_pkt) { + const knot_pkt_t *rpkt = req->answer; + m.has_response_message = rpkt != NULL; + if (rpkt != NULL) { + m.response_message.len = rpkt->size; + m.response_message.data = rpkt->wire; + } + } + + /* Set response time to now */ + m.response_time_sec = now.tv_sec; + m.has_response_time_sec = true; + m.response_time_nsec = now.tv_usec * 1000; + m.has_response_time_nsec = true; + } + + if (dnstap_dt->identity) { + dnstap.identity.data = (uint8_t*)dnstap_dt->identity; + dnstap.identity.len = dnstap_dt->identity_len; + dnstap.has_identity = true; + } + + if (dnstap_dt->version) { + dnstap.version.data = (uint8_t*)dnstap_dt->version; + dnstap.version.len = dnstap_dt->version_len; + dnstap.has_version = true; + } + + /* Pack the message */ + uint8_t *frame = NULL; + size_t size = 0; + dt_pack(&dnstap, &frame, &size); + if (!frame) { + return kr_error(ENOMEM); + } + + /* Submit a request to send message to fstrm_iothr*/ + fstrm_res res = fstrm_iothr_submit(dnstap_dt->iothread, dnstap_dt->ioq, frame, size, + fstrm_free_wrapper, NULL); + if (res != fstrm_res_success) { + DEBUG_MSG("Error submitting dnstap message to iothr\n"); + free(frame); + return kr_error(EBUSY); + } + + return kr_ok(); +} + +/* dnstap_log_query prepares dnstap CLIENT_QUERY message and sends it to fstrm */ +static int dnstap_log_query(kr_layer_t *ctx) { + dnstap_log(ctx, CLIENT_QUERY_PHASE); + return ctx->state; +} + +/* dnstap_log_response prepares dnstap CLIENT_RESPONSE message and sends it to fstrm */ +static int dnstap_log_response(kr_layer_t *ctx) { + dnstap_log(ctx, CLIENT_RESPONSE_PHASE); + return ctx->state; +} + +KR_EXPORT +int dnstap_init(struct kr_module *module) { + static kr_layer_api_t layer = { + .begin = &dnstap_log_query, + .finish = &dnstap_log_response, + }; + /* Store module reference */ + layer.data = module; + module->layer = &layer; + + /* allocated memory for internal data */ + struct dnstap_data *data = calloc(1, sizeof(*data)); + if (!data) { + return kr_error(ENOMEM); + } + + /* save pointer to internal struct in module for future reference */ + module->data = data; + return kr_ok(); +} + +/** Clear, i.e. get to state as after the first dnstap_init(). */ +static void dnstap_clear(struct kr_module *module) { + struct dnstap_data *data = module->data; + if (data) { + free(data->identity); + free(data->version); + + fstrm_iothr_destroy(&data->iothread); + DEBUG_MSG("fstrm iothread destroyed\n"); + } +} + +KR_EXPORT +int dnstap_deinit(struct kr_module *module) { + dnstap_clear(module); + free(module->data); + return kr_ok(); +} + +/* dnstap_unix_writer returns a unix fstream writer + * https://gitlab.nic.cz/knot/knot-dns/blob/master/src/knot/modules/dnstap.c#L159 + */ +static struct fstrm_writer* dnstap_unix_writer(const char *path) { + + auto_destroy_uopts struct fstrm_unix_writer_options *opt = fstrm_unix_writer_options_init(); + if (!opt) { + return NULL; + } + fstrm_unix_writer_options_set_socket_path(opt, path); + + auto_destroy_wopts struct fstrm_writer_options *wopt = fstrm_writer_options_init(); + if (!wopt) { + fstrm_unix_writer_options_destroy(&opt); + return NULL; + } + fstrm_writer_options_add_content_type(wopt, DNSTAP_CONTENT_TYPE, + strlen(DNSTAP_CONTENT_TYPE)); + + struct fstrm_writer *writer = fstrm_unix_writer_init(opt, wopt); + fstrm_unix_writer_options_destroy(&opt); + fstrm_writer_options_destroy(&wopt); + if (!writer) { + return NULL; + } + + fstrm_res res = fstrm_writer_open(writer); + if (res != fstrm_res_success) { + DEBUG_MSG("fstrm_writer_open returned %d\n", res); + fstrm_writer_destroy(&writer); + return NULL; + } + + return writer; +} + +/* find_string + * create a new string from json + * *var is set to pointer of new string + * node must of type JSON_STRING + * new string can be at most len bytes + */ +static int find_string(const JsonNode *node, char **val, size_t len) { + if (!node || !node->key) + return kr_error(EINVAL); + if (kr_fails_assert(node->tag == JSON_STRING)) + return kr_error(EINVAL); + *val = strndup(node->string_, len); + if (kr_fails_assert(*val != NULL)) + return kr_error(errno); + return kr_ok(); +} + +/* find_bool returns bool from json */ +static bool find_bool(const JsonNode *node) { + if (!node || !node->key) + return false; + if (kr_fails_assert(node->tag == JSON_BOOL)) + return false; + return node->bool_; +} + +/* parse config */ +KR_EXPORT +int dnstap_config(struct kr_module *module, const char *conf) { + dnstap_clear(module); + if (!conf) return kr_ok(); /* loaded module without configuring */ + struct dnstap_data *data = module->data; + auto_free char *sock_path = NULL; + + /* Empty conf passed, set default */ + if (strlen(conf) < 1) { + sock_path = strdup(DEFAULT_SOCK_PATH); + } else { + + JsonNode *root_node = json_decode(conf); + if (!root_node) { + ERROR_MSG("error parsing json\n"); + return kr_error(EINVAL); + } + + JsonNode *node; + /* dnstapPath key */ + node = json_find_member(root_node, CFG_SOCK_PATH); + if (!node || find_string(node, &sock_path, PATH_MAX) != kr_ok()) { + sock_path = strdup(DEFAULT_SOCK_PATH); + } + + /* identity string key */ + node = json_find_member(root_node, CFG_IDENTITY_STRING); + if (!node || find_string(node, &data->identity, KR_EDNS_PAYLOAD) != kr_ok()) { + data->identity = NULL; + data->identity_len = 0; + } else { + data->identity_len = strlen(data->identity); + } + + /* version string key */ + node = json_find_member(root_node, CFG_VERSION_STRING); + if (!node || find_string(node, &data->version, KR_EDNS_PAYLOAD) != kr_ok()) { + data->version = strdup("Knot Resolver " PACKAGE_VERSION); + if (data->version) { + data->version_len = strlen(data->version); + } + } else { + data->version_len = strlen(data->version); + } + + node = json_find_member(root_node, CFG_LOG_CLIENT_PKT); + if (node) { + JsonNode *subnode; + /* logRespPkt key */ + subnode = json_find_member(node, CFG_LOG_RESP_PKT); + if (subnode) { + data->log_resp_pkt = find_bool(subnode); + } else { + data->log_resp_pkt = false; + } + + /* logQrPkt key */ + subnode = json_find_member(node, CFG_LOG_QR_PKT); + if (subnode) { + data->log_qr_pkt = find_bool(subnode); + } else { + data->log_qr_pkt = false; + } + + subnode = json_find_member(node, CFG_LOG_TCP_RTT); + if (subnode) { + data->log_tcp_rtt = find_bool(subnode); + } else { + data->log_tcp_rtt = false; + } + } else { + data->log_qr_pkt = false; + data->log_resp_pkt = false; + data->log_tcp_rtt = false; + } + + /* clean up json, we don't need it no more */ + json_delete(root_node); + } + + DEBUG_MSG("opening sock file %s\n",sock_path); + struct fstrm_writer *writer = dnstap_unix_writer(sock_path); + if (!writer) { + ERROR_MSG("failed to open socket %s\n" + "Please ensure that it exists beforehand and has appropriate access permissions.\n", + sock_path); + return kr_error(EINVAL); + } + + struct fstrm_iothr_options *opt = fstrm_iothr_options_init(); + if (!opt) { + ERROR_MSG("can't init fstrm options\n"); + fstrm_writer_destroy(&writer); + return kr_error(EINVAL); + } + + /* Create the I/O thread. */ + data->iothread = fstrm_iothr_init(opt, &writer); + fstrm_iothr_options_destroy(&opt); + if (!data->iothread) { + ERROR_MSG("can't init fstrm_iothr\n"); + fstrm_writer_destroy(&writer); + return kr_error(ENOMEM); + } + + /* Get fstrm thread handle + * We only have one input queue, hence idx=0 + */ + data->ioq = fstrm_iothr_get_input_queue_idx(data->iothread, 0); + if (!data->ioq) { + fstrm_iothr_destroy(&data->iothread); + ERROR_MSG("can't get fstrm queue\n"); + return kr_error(EBUSY); + } + + return kr_ok(); +} + +KR_MODULE_EXPORT(dnstap) + diff --git a/modules/dnstap/dnstap.proto b/modules/dnstap/dnstap.proto new file mode 100644 index 0000000..f2b7273 --- /dev/null +++ b/modules/dnstap/dnstap.proto @@ -0,0 +1,273 @@ +// dnstap: flexible, structured event replication format for DNS software +// +// This file contains the protobuf schemas for the "dnstap" structured event +// replication format for DNS software. + +// Written in 2013-2014 by Farsight Security, Inc. +// +// SPDX-License-Identifier: CC0-1.0 + +syntax = "proto2"; +package dnstap; + +// "Dnstap": this is the top-level dnstap type, which is a "union" type that +// contains other kinds of dnstap payloads, although currently only one type +// of dnstap payload is defined. +// See: https://developers.google.com/protocol-buffers/docs/techniques#union +message Dnstap { + // DNS server identity. + // If enabled, this is the identity string of the DNS server which generated + // this message. Typically this would be the same string as returned by an + // "NSID" (RFC 5001) query. + optional bytes identity = 1; + + // DNS server version. + // If enabled, this is the version string of the DNS server which generated + // this message. Typically this would be the same string as returned by a + // "version.bind" query. + optional bytes version = 2; + + // Extra data for this payload. + // This field can be used for adding an arbitrary byte-string annotation to + // the payload. No encoding or interpretation is applied or enforced. + optional bytes extra = 3; + + // Identifies which field below is filled in. + enum Type { + MESSAGE = 1; + } + required Type type = 15; + + // One of the following will be filled in. + optional Message message = 14; +} + +// SocketFamily: the network protocol family of a socket. This specifies how +// to interpret "network address" fields. +enum SocketFamily { + INET = 1; // IPv4 (RFC 791) + INET6 = 2; // IPv6 (RFC 2460) +} + +// SocketProtocol: the protocol used to transport a DNS message. +enum SocketProtocol { + UDP = 1; // DNS over UDP transport (RFC 1035 section 4.2.1) + TCP = 2; // DNS over TCP transport (RFC 1035 section 4.2.2) + DOT = 3; // DNS over TLS (RFC 7858) + DOH = 4; // DNS over HTTPS (RFC 8484) +} + +// Message: a wire-format (RFC 1035 section 4) DNS message and associated +// metadata. Applications generating "Message" payloads should follow +// certain requirements based on the MessageType, see below. +message Message { + + // There are eight types of "Message" defined that correspond to the + // four arrows in the following diagram, slightly modified from RFC 1035 + // section 2: + + // +---------+ +----------+ +--------+ + // | | query | | query | | + // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. | + // | Resolver| | Server | | Name | + // | |<-SR--------CR-| |<-RR----AR-| Server | + // +---------+ response | | response | | + // +----------+ +--------+ + + // Each arrow has two Type values each, one for each "end" of each arrow, + // because these are considered to be distinct events. Each end of each + // arrow on the diagram above has been marked with a two-letter Type + // mnemonic. Clockwise from upper left, these mnemonic values are: + // + // SQ: STUB_QUERY + // CQ: CLIENT_QUERY + // RQ: RESOLVER_QUERY + // AQ: AUTH_QUERY + // AR: AUTH_RESPONSE + // RR: RESOLVER_RESPONSE + // CR: CLIENT_RESPONSE + // SR: STUB_RESPONSE + + // Two additional types of "Message" have been defined for the + // "forwarding" case where an upstream DNS server is responsible for + // further recursion. These are not shown on the diagram above, but have + // the following mnemonic values: + + // FQ: FORWARDER_QUERY + // FR: FORWARDER_RESPONSE + + // The "Message" Type values are defined below. + + enum Type { + // AUTH_QUERY is a DNS query message received from a resolver by an + // authoritative name server, from the perspective of the authoritative + // name server. + AUTH_QUERY = 1; + + // AUTH_RESPONSE is a DNS response message sent from an authoritative + // name server to a resolver, from the perspective of the authoritative + // name server. + AUTH_RESPONSE = 2; + + // RESOLVER_QUERY is a DNS query message sent from a resolver to an + // authoritative name server, from the perspective of the resolver. + // Resolvers typically clear the RD (recursion desired) bit when + // sending queries. + RESOLVER_QUERY = 3; + + // RESOLVER_RESPONSE is a DNS response message received from an + // authoritative name server by a resolver, from the perspective of + // the resolver. + RESOLVER_RESPONSE = 4; + + // CLIENT_QUERY is a DNS query message sent from a client to a DNS + // server which is expected to perform further recursion, from the + // perspective of the DNS server. The client may be a stub resolver or + // forwarder or some other type of software which typically sets the RD + // (recursion desired) bit when querying the DNS server. The DNS server + // may be a simple forwarding proxy or it may be a full recursive + // resolver. + CLIENT_QUERY = 5; + + // CLIENT_RESPONSE is a DNS response message sent from a DNS server to + // a client, from the perspective of the DNS server. The DNS server + // typically sets the RA (recursion available) bit when responding. + CLIENT_RESPONSE = 6; + + // FORWARDER_QUERY is a DNS query message sent from a downstream DNS + // server to an upstream DNS server which is expected to perform + // further recursion, from the perspective of the downstream DNS + // server. + FORWARDER_QUERY = 7; + + // FORWARDER_RESPONSE is a DNS response message sent from an upstream + // DNS server performing recursion to a downstream DNS server, from the + // perspective of the downstream DNS server. + FORWARDER_RESPONSE = 8; + + // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS + // server, from the perspective of the stub resolver. + STUB_QUERY = 9; + + // STUB_RESPONSE is a DNS response message sent from a DNS server to a + // stub resolver, from the perspective of the stub resolver. + STUB_RESPONSE = 10; + + // TOOL_QUERY is a DNS query message sent from a DNS software tool to a + // DNS server, from the perspective of the tool. + TOOL_QUERY = 11; + + // TOOL_RESPONSE is a DNS response message received by a DNS software + // tool from a DNS server, from the perspective of the tool. + TOOL_RESPONSE = 12; + + // UPDATE_QUERY is a DNS update query message received from a resolver + // by an authoritative name server, from the perspective of the + // authoritative name server. + UPDATE_QUERY = 13; + + // UPDATE_RESPONSE is a DNS update response message sent from an + // authoritative name server to a resolver, from the perspective of the + // authoritative name server. + UPDATE_RESPONSE = 14; + } + + // One of the Type values described above. + required Type type = 1; + + // One of the SocketFamily values described above. + optional SocketFamily socket_family = 2; + + // One of the SocketProtocol values described above. + optional SocketProtocol socket_protocol = 3; + + // The network address of the message initiator. + // For SocketFamily INET, this field is 4 octets (IPv4 address). + // For SocketFamily INET6, this field is 16 octets (IPv6 address). + optional bytes query_address = 4; + + // The network address of the message responder. + // For SocketFamily INET, this field is 4 octets (IPv4 address). + // For SocketFamily INET6, this field is 16 octets (IPv6 address). + optional bytes response_address = 5; + + // The transport port of the message initiator. + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + optional uint32 query_port = 6; + + // The transport port of the message responder. + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + optional uint32 response_port = 7; + + // The time at which the DNS query message was sent or received, depending + // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY. + // This is the number of seconds since the UNIX epoch. + optional uint64 query_time_sec = 8; + + // The time at which the DNS query message was sent or received. + // This is the seconds fraction, expressed as a count of nanoseconds. + optional fixed32 query_time_nsec = 9; + + // The initiator's original wire-format DNS query message, verbatim. + optional bytes query_message = 10; + + // The "zone" or "bailiwick" pertaining to the DNS query message. + // This is a wire-format DNS domain name. + optional bytes query_zone = 11; + + // The time at which the DNS response message was sent or received, + // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or + // CLIENT_RESPONSE. + // This is the number of seconds since the UNIX epoch. + optional uint64 response_time_sec = 12; + + // The time at which the DNS response message was sent or received. + // This is the seconds fraction, expressed as a count of nanoseconds. + optional fixed32 response_time_nsec = 13; + + // The responder's original wire-format DNS response message, verbatim. + optional bytes response_message = 14; +} + +// All fields except for 'type' in the Message schema are optional. +// It is recommended that at least the following fields be filled in for +// particular types of Messages. + +// AUTH_QUERY: +// socket_family, socket_protocol +// query_address, query_port +// query_message +// query_time_sec, query_time_nsec + +// AUTH_RESPONSE: +// socket_family, socket_protocol +// query_address, query_port +// query_time_sec, query_time_nsec +// response_message +// response_time_sec, response_time_nsec + +// RESOLVER_QUERY: +// socket_family, socket_protocol +// query_message +// query_time_sec, query_time_nsec +// query_zone +// response_address, response_port + +// RESOLVER_RESPONSE: +// socket_family, socket_protocol +// query_time_sec, query_time_nsec +// query_zone +// response_address, response_port +// response_message +// response_time_sec, response_time_nsec + +// CLIENT_QUERY: +// socket_family, socket_protocol +// query_message +// query_time_sec, query_time_nsec + +// CLIENT_RESPONSE: +// socket_family, socket_protocol +// query_time_sec, query_time_nsec +// response_message +// response_time_sec, response_time_nsec diff --git a/modules/dnstap/meson.build b/modules/dnstap/meson.build new file mode 100644 index 0000000..e8a94bf --- /dev/null +++ b/modules/dnstap/meson.build @@ -0,0 +1,57 @@ +# C module: dnstap +# SPDX-License-Identifier: GPL-3.0-or-later + +dnstap_src = files([ + 'dnstap.c', +]) + +## dnstap dependencies +build_dnstap = false +if get_option('dnstap') != 'disabled' + dnstap_required = get_option('dnstap') == 'enabled' + message('--- dnstap module dependencies ---') + libprotobuf_c = dependency('libprotobuf-c', version: '>=1', required: dnstap_required) + libfstrm = dependency('libfstrm', version: '>=0.2', required: dnstap_required) + protoc_c = find_program('protoc-c', required: dnstap_required) + message('----------------------------------') + if libprotobuf_c.found() and libfstrm.found() and protoc_c.found() + build_dnstap = true + endif +endif + + +if build_dnstap + c_src_lint += dnstap_src + + # generate protobuf-c sources using protoc-c + dnstap_pb = custom_target( + 'dnstap_pb', + command: [ + protoc_c, + '--c_out=' + meson.current_build_dir(), + '--proto_path', meson.current_source_dir(), + meson.current_source_dir() / 'dnstap.proto', + ], + input: [ 'dnstap.proto' ], + output: [ + 'dnstap.pb-c.h', + 'dnstap.pb-c.c', + ], + ) + + # build dnstap module + dnstap_mod = shared_module( + 'dnstap', + dnstap_src, + dependencies: [ + declare_dependency(sources: dnstap_pb), + libfstrm, + libprotobuf_c, + libknot, + ], + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, + ) +endif diff --git a/modules/edns_keepalive/.packaging/test.config b/modules/edns_keepalive/.packaging/test.config new file mode 100644 index 0000000..5c71c79 --- /dev/null +++ b/modules/edns_keepalive/.packaging/test.config @@ -0,0 +1,10 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('edns_keepalive') + +for _,item in ipairs(modules.list()) do + if item == "edns_keepalive" then + os.exit(0) + end +end + +os.exit(1) diff --git a/modules/edns_keepalive/README.rst b/modules/edns_keepalive/README.rst new file mode 100644 index 0000000..d8034d2 --- /dev/null +++ b/modules/edns_keepalive/README.rst @@ -0,0 +1,22 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-edns_keepalive: + +EDNS keepalive +============== + +The ``edns_keepalive`` module implements :rfc:`7828` for *clients* +connecting to Knot Resolver via TCP and TLS. +The module just allows clients to discover the connection timeout, +client connections are always timed-out the same way *regardless* +of clients sending the EDNS option. + +When connecting to servers, Knot Resolver does not send this EDNS option. +It still attempts to reuse established connections intelligently. + +This module is loaded by default. For debugging purposes it can be +unloaded using standard means: + +.. code-block:: lua + + modules.unload('edns_keepalive') diff --git a/modules/edns_keepalive/edns_keepalive.c b/modules/edns_keepalive/edns_keepalive.c new file mode 100644 index 0000000..30d5df3 --- /dev/null +++ b/modules/edns_keepalive/edns_keepalive.c @@ -0,0 +1,61 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/** + * @file edns_keepalive.c + * @brief Minimalistic EDNS keepalive implementation on server side. + * If keepalive option is present in query, + * always reply with constant timeout value. + * + */ +#include +#include "daemon/worker.h" +#include "lib/module.h" +#include "lib/layer.h" + +static int edns_keepalive_finalize(kr_layer_t *ctx) +{ + struct kr_request *req = ctx->req; + knot_pkt_t *answer = req->answer; + const knot_rrset_t *src_opt = req->qsource.packet->opt_rr; + knot_rrset_t *answ_opt = answer->opt_rr; + + const bool ka_want = + req->qsource.flags.tcp && + src_opt != NULL && + knot_edns_get_option(src_opt, KNOT_EDNS_OPTION_TCP_KEEPALIVE, NULL) && + answ_opt != NULL; + if (!ka_want) { + return ctx->state; + } + const struct network *net = &the_worker->engine->net; + uint64_t timeout = net->tcp.in_idle_timeout / 100; + if (timeout > UINT16_MAX) { + timeout = UINT16_MAX; + } + knot_mm_t *pool = &answer->mm; + uint16_t ka_size = knot_edns_keepalive_size(timeout); + uint8_t ka_buf[ka_size]; + int ret = knot_edns_keepalive_write(ka_buf, ka_size, timeout); + if (ret == KNOT_EOK) { + ret = knot_edns_add_option(answ_opt, KNOT_EDNS_OPTION_TCP_KEEPALIVE, + ka_size, ka_buf, pool); + } + if (ret != KNOT_EOK) { + ctx->state = KR_STATE_FAIL; + } + return ctx->state; +} + +KR_EXPORT int edns_keepalive_init(struct kr_module *self) +{ + static const kr_layer_api_t layer = { + .answer_finalize = &edns_keepalive_finalize, + }; + self->layer = &layer; + return kr_ok(); +} + +KR_MODULE_EXPORT(edns_keepalive) + diff --git a/modules/edns_keepalive/meson.build b/modules/edns_keepalive/meson.build new file mode 100644 index 0000000..979452f --- /dev/null +++ b/modules/edns_keepalive/meson.build @@ -0,0 +1,17 @@ +# C module: edns_keepalive +# SPDX-License-Identifier: GPL-3.0-or-later + +edns_keepalive_src = files([ + 'edns_keepalive.c', +]) +c_src_lint += edns_keepalive_src + +edns_keepalive_mod = shared_module( + 'edns_keepalive', + edns_keepalive_src, + dependencies: libknot, + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, +) diff --git a/modules/etcd/.packaging/centos/7/pre-test.sh b/modules/etcd/.packaging/centos/7/pre-test.sh new file mode 100755 index 0000000..4df79d9 --- /dev/null +++ b/modules/etcd/.packaging/centos/7/pre-test.sh @@ -0,0 +1 @@ +luarocks install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/centos/7/rundeps b/modules/etcd/.packaging/centos/7/rundeps new file mode 100644 index 0000000..795a3c4 --- /dev/null +++ b/modules/etcd/.packaging/centos/7/rundeps @@ -0,0 +1,6 @@ +openssl-devel +lua-devel +luarocks +git +gcc +make diff --git a/modules/etcd/.packaging/centos/8/NOTSUPPORTED b/modules/etcd/.packaging/centos/8/NOTSUPPORTED new file mode 100644 index 0000000..e69de29 diff --git a/modules/etcd/.packaging/debian/10/pre-test.sh b/modules/etcd/.packaging/debian/10/pre-test.sh new file mode 100755 index 0000000..20073dc --- /dev/null +++ b/modules/etcd/.packaging/debian/10/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/debian/10/rundeps b/modules/etcd/.packaging/debian/10/rundeps new file mode 100644 index 0000000..02d3fcf --- /dev/null +++ b/modules/etcd/.packaging/debian/10/rundeps @@ -0,0 +1,4 @@ +libssl-dev +luarocks +git +make diff --git a/modules/etcd/.packaging/debian/9/pre-test.sh b/modules/etcd/.packaging/debian/9/pre-test.sh new file mode 100755 index 0000000..4df79d9 --- /dev/null +++ b/modules/etcd/.packaging/debian/9/pre-test.sh @@ -0,0 +1 @@ +luarocks install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/debian/9/rundeps b/modules/etcd/.packaging/debian/9/rundeps new file mode 100644 index 0000000..02d3fcf --- /dev/null +++ b/modules/etcd/.packaging/debian/9/rundeps @@ -0,0 +1,4 @@ +libssl-dev +luarocks +git +make diff --git a/modules/etcd/.packaging/fedora/31/NOTSUPPORTED b/modules/etcd/.packaging/fedora/31/NOTSUPPORTED new file mode 100644 index 0000000..b912289 --- /dev/null +++ b/modules/etcd/.packaging/fedora/31/NOTSUPPORTED @@ -0,0 +1,16 @@ +Error installing etcd using luarocks: + + + +Missing dependencies for process 1.9.0-1: + luarocks-fetch-gitrec >= 0.2 (not installed) + +process 1.9.0-1 depends on luarocks-fetch-gitrec >= 0.2 (not installed) +Installing https://luarocks.org/luarocks-fetch-gitrec-0.2-1.src.rock + +No existing manifest. Attempting to rebuild... +luarocks-fetch-gitrec 0.2-1 is now installed in /root/.luarocks (license: MIT) + + +Error: Unknown protocol gitrec + diff --git a/modules/etcd/.packaging/fedora/32/NOTSUPPORTED b/modules/etcd/.packaging/fedora/32/NOTSUPPORTED new file mode 100644 index 0000000..b912289 --- /dev/null +++ b/modules/etcd/.packaging/fedora/32/NOTSUPPORTED @@ -0,0 +1,16 @@ +Error installing etcd using luarocks: + + + +Missing dependencies for process 1.9.0-1: + luarocks-fetch-gitrec >= 0.2 (not installed) + +process 1.9.0-1 depends on luarocks-fetch-gitrec >= 0.2 (not installed) +Installing https://luarocks.org/luarocks-fetch-gitrec-0.2-1.src.rock + +No existing manifest. Attempting to rebuild... +luarocks-fetch-gitrec 0.2-1 is now installed in /root/.luarocks (license: MIT) + + +Error: Unknown protocol gitrec + diff --git a/modules/etcd/.packaging/leap/15.2/pre-test.sh b/modules/etcd/.packaging/leap/15.2/pre-test.sh new file mode 100755 index 0000000..20073dc --- /dev/null +++ b/modules/etcd/.packaging/leap/15.2/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/leap/15.2/rundeps b/modules/etcd/.packaging/leap/15.2/rundeps new file mode 100644 index 0000000..e8df59b --- /dev/null +++ b/modules/etcd/.packaging/leap/15.2/rundeps @@ -0,0 +1,6 @@ +libopenssl-devel +lua51-devel +lua51-luarocks +git +gcc +make diff --git a/modules/etcd/.packaging/test.config b/modules/etcd/.packaging/test.config new file mode 100644 index 0000000..1cc7e5a --- /dev/null +++ b/modules/etcd/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('etcd') +assert(etcd) +quit() diff --git a/modules/etcd/.packaging/ubuntu/16.04/pre-test.sh b/modules/etcd/.packaging/ubuntu/16.04/pre-test.sh new file mode 100755 index 0000000..4df79d9 --- /dev/null +++ b/modules/etcd/.packaging/ubuntu/16.04/pre-test.sh @@ -0,0 +1 @@ +luarocks install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/ubuntu/16.04/rundeps b/modules/etcd/.packaging/ubuntu/16.04/rundeps new file mode 100644 index 0000000..a355a9f --- /dev/null +++ b/modules/etcd/.packaging/ubuntu/16.04/rundeps @@ -0,0 +1,3 @@ +libssl-dev +luarocks +git diff --git a/modules/etcd/.packaging/ubuntu/18.04/pre-test.sh b/modules/etcd/.packaging/ubuntu/18.04/pre-test.sh new file mode 100755 index 0000000..4df79d9 --- /dev/null +++ b/modules/etcd/.packaging/ubuntu/18.04/pre-test.sh @@ -0,0 +1 @@ +luarocks install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/ubuntu/18.04/rundeps b/modules/etcd/.packaging/ubuntu/18.04/rundeps new file mode 100644 index 0000000..a355a9f --- /dev/null +++ b/modules/etcd/.packaging/ubuntu/18.04/rundeps @@ -0,0 +1,3 @@ +libssl-dev +luarocks +git diff --git a/modules/etcd/.packaging/ubuntu/20.04/pre-test.sh b/modules/etcd/.packaging/ubuntu/20.04/pre-test.sh new file mode 100755 index 0000000..20073dc --- /dev/null +++ b/modules/etcd/.packaging/ubuntu/20.04/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install etcd --from=https://mah0x211.github.io/rocks/ diff --git a/modules/etcd/.packaging/ubuntu/20.04/rundeps b/modules/etcd/.packaging/ubuntu/20.04/rundeps new file mode 100644 index 0000000..02d3fcf --- /dev/null +++ b/modules/etcd/.packaging/ubuntu/20.04/rundeps @@ -0,0 +1,4 @@ +libssl-dev +luarocks +git +make diff --git a/modules/etcd/README.rst b/modules/etcd/README.rst new file mode 100644 index 0000000..0ffa781 --- /dev/null +++ b/modules/etcd/README.rst @@ -0,0 +1,46 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-etcd: + +Etcd support +------------ + +The `etcd` module connects to `etcd `_ peers and watches +for configuration changes. By default, the module watches the subtree under +``/knot-resolver`` directory, but you can change this in the +`etcd library configuration `_. + +The subtree structure corresponds to the configuration variables in the declarative style. + +.. code-block:: bash + + $ etcdctl set /knot-resolver/net/127.0.0.1 53 + $ etcdctl set /knot-resolver/cache/size 10000000 + +Configures all listening nodes to following configuration: + +.. code-block:: lua + + net = { '127.0.0.1' } + cache.size = 10000000 + +Example configuration +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: lua + + modules.load('etcd') + etcd.config({ + prefix = '/knot-resolver', + peer = 'http://127.0.0.1:7001' + }) + +.. warning:: Work in progress! + +Dependencies +^^^^^^^^^^^^ + +* `lua-etcd `_ library available in LuaRocks + + ``$ luarocks --lua-version 5.1 install etcd --from=https://mah0x211.github.io/rocks/`` + diff --git a/modules/etcd/etcd.lua b/modules/etcd/etcd.lua new file mode 100644 index 0000000..ab58024 --- /dev/null +++ b/modules/etcd/etcd.lua @@ -0,0 +1,56 @@ +--- @module etcd +-- SPDX-License-Identifier: GPL-3.0-or-later +local etcd = {} + +-- @function update subtree configuration +local function update_subtree(tree) + if not tree then return end + for _, k in pairs(tree) do + if k.dir then + update_subtree(k.nodes) + else + local key,opt = k.key:gmatch('([^/]+)/([^/]+)$')() + if _G[key][opt] ~= k.value then + _G[key][opt] = k.value + end + end + end +end + +-- @function reload whole configuration +function etcd.reload() + local res, err = etcd.cli:readdir('/', true) + if err then + error(err) + end + update_subtree(res.body.node.nodes) +end + +function etcd.init() + etcd.Etcd = require('etcd.luasocket') + etcd.defaults = { prefix = '/knot-resolver' } +end + +function etcd.deinit() + if etcd.ev then event.cancel(etcd.ev) end +end + +function etcd.config(conf) + local options = etcd.defaults + if type(conf) == 'table' then + for k,v in pairs(conf) do options[k] = v end + end + -- create connection + local cli, err = etcd.Etcd.new(options) + if err then + error(err) + end + etcd.cli = cli + -- schedule recurrent polling + -- @todo: the etcd has watch() API, but this requires + -- coroutines on socket operations + if etcd.ev then event.cancel(etcd.ev) end + etcd.ev = event.recurrent(5 * sec, etcd.reload) +end + +return etcd diff --git a/modules/experimental_dot_auth/.packaging/centos/7/rundeps b/modules/experimental_dot_auth/.packaging/centos/7/rundeps new file mode 100644 index 0000000..36b83e1 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/centos/7/rundeps @@ -0,0 +1 @@ +lua-basexx diff --git a/modules/experimental_dot_auth/.packaging/centos/8/rundeps b/modules/experimental_dot_auth/.packaging/centos/8/rundeps new file mode 100644 index 0000000..984c7ce --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/centos/8/rundeps @@ -0,0 +1 @@ +lua5.1-basexx diff --git a/modules/experimental_dot_auth/.packaging/debian/10/rundeps b/modules/experimental_dot_auth/.packaging/debian/10/rundeps new file mode 100644 index 0000000..36b83e1 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/debian/10/rundeps @@ -0,0 +1 @@ +lua-basexx diff --git a/modules/experimental_dot_auth/.packaging/debian/9/rundeps b/modules/experimental_dot_auth/.packaging/debian/9/rundeps new file mode 100644 index 0000000..36b83e1 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/debian/9/rundeps @@ -0,0 +1 @@ +lua-basexx diff --git a/modules/experimental_dot_auth/.packaging/fedora/31/rundeps b/modules/experimental_dot_auth/.packaging/fedora/31/rundeps new file mode 100644 index 0000000..984c7ce --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/fedora/31/rundeps @@ -0,0 +1 @@ +lua5.1-basexx diff --git a/modules/experimental_dot_auth/.packaging/fedora/32/rundeps b/modules/experimental_dot_auth/.packaging/fedora/32/rundeps new file mode 100644 index 0000000..984c7ce --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/fedora/32/rundeps @@ -0,0 +1 @@ +lua5.1-basexx diff --git a/modules/experimental_dot_auth/.packaging/leap/15.2/NOTSUPPORTED b/modules/experimental_dot_auth/.packaging/leap/15.2/NOTSUPPORTED new file mode 100644 index 0000000..682eff0 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/leap/15.2/NOTSUPPORTED @@ -0,0 +1,6 @@ + +ERROR:test_packaging:Installing https://luarocks.org/basexx-0.4.1-1.rockspec +Error: Failed extracting v0.4.1.tar.gz + +Doesn't works on GitLab CI/CD, but works on localhost. +gzip and tar packages are installed, all packages has same version as packages on localhost's docker container. diff --git a/modules/experimental_dot_auth/.packaging/leap/15.2/pre-test.sh b/modules/experimental_dot_auth/.packaging/leap/15.2/pre-test.sh new file mode 100755 index 0000000..df5d784 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/leap/15.2/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install basexx --from=https://mah0x211.github.io/rocks/ diff --git a/modules/experimental_dot_auth/.packaging/leap/15.2/rundeps b/modules/experimental_dot_auth/.packaging/leap/15.2/rundeps new file mode 100644 index 0000000..9e636d8 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/leap/15.2/rundeps @@ -0,0 +1,4 @@ +lua51-luarocks +git +tar +gzip diff --git a/modules/experimental_dot_auth/.packaging/test.config b/modules/experimental_dot_auth/.packaging/test.config new file mode 100644 index 0000000..39e9aed --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('experimental_dot_auth') +assert(experimental_dot_auth) +quit() diff --git a/modules/experimental_dot_auth/.packaging/ubuntu/16.04/NOTSUPPORTED b/modules/experimental_dot_auth/.packaging/ubuntu/16.04/NOTSUPPORTED new file mode 100644 index 0000000..e69de29 diff --git a/modules/experimental_dot_auth/.packaging/ubuntu/18.04/rundeps b/modules/experimental_dot_auth/.packaging/ubuntu/18.04/rundeps new file mode 100644 index 0000000..36b83e1 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/ubuntu/18.04/rundeps @@ -0,0 +1 @@ +lua-basexx diff --git a/modules/experimental_dot_auth/.packaging/ubuntu/20.04/rundeps b/modules/experimental_dot_auth/.packaging/ubuntu/20.04/rundeps new file mode 100644 index 0000000..36b83e1 --- /dev/null +++ b/modules/experimental_dot_auth/.packaging/ubuntu/20.04/rundeps @@ -0,0 +1 @@ +lua-basexx diff --git a/modules/experimental_dot_auth/README.rst b/modules/experimental_dot_auth/README.rst new file mode 100644 index 0000000..a93f516 --- /dev/null +++ b/modules/experimental_dot_auth/README.rst @@ -0,0 +1,91 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-experimental_dot_auth: + +Experimental DNS-over-TLS Auto-discovery +======================================== + +This experimental module provides automatic discovery of authoritative servers' supporting DNS-over-TLS. +The module uses magic NS names to detect SPKI_ fingerprint which is very similar to `dnscurve`_ mechanism. + +.. warning:: This protocol and module is experimental and can be changed or removed at any time. Use at own risk, security properties were not analyzed! + +How it works +------------ + +The module will look for NS target names formatted as: +``dot-{base32(sha256(SPKI))}....`` + +For instance, Knot Resolver will detect NS names formatted like this + +.. code-block:: none + + example.com NS dot-tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a.example.com + +and automatically discover that example.com NS supports DoT with the base64-encoded SPKI digest of ``m+12GgMFIiheEhKvUcOynjbn3WYQUp5tVGDh7Snwj/Q=`` +and will associate it with the IPs of ``dot-tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a.example.com``. + +In that example, the base32 encoded (no padding) version of the sha256 PIN is ``tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a``, which when +converted to base64 translates to ``m+12GgMFIiheEhKvUcOynjbn3WYQUp5tVGDh7Snwj/Q=``. + +Generating NS target names +-------------------------- + +To generate the NS target name, use the following command to generate the base32 encoded string of the SPKI fingerprint: + +.. code-block:: bash + + openssl x509 -in /path/to/cert.pem -pubkey -noout | \ + openssl pkey -pubin -outform der | \ + openssl dgst -sha256 -binary | \ + base32 | tr -d '=' | tr '[:upper:]' '[:lower:]' + tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a + +Then add a target to your NS with: ``dot-${b32}.a.example.com`` + +Finally, map ``dot-${b32}.a.example.com`` to the right set of IPs. + +.. code-block:: bash + + ... + ... + ;; QUESTION SECTION: + ;example.com. IN NS + + ;; AUTHORITY SECTION: + example.com. 3600 IN NS dot-tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a.a.example.com. + example.com. 3600 IN NS dot-tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a.b.example.com. + + ;; ADDITIONAL SECTION: + dot-tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a.a.example.com. 3600 IN A 192.0.2.1 + dot-tpwxmgqdaurcqxqsckxvdq5sty3opxlgcbjj43kumdq62kpqr72a.b.example.com. 3600 IN AAAA 2001:DB8::1 + ... + ... + +Example configuration +--------------------- + +To enable the module, add this snippet to your config: + +.. code-block:: lua + + -- Start an experiment, use with caution + modules.load('experimental_dot_auth') + +This module requires standard ``basexx`` Lua library which is typically provided by ``lua-basexx`` package. + +Caveats +------- + +The module relies on seeing the reply of the NS query and as such will not work +if Knot Resolver uses data from its cache. You may need to delete the cache before starting ``kresd`` to work around this. + +The module also assumes that the NS query answer will return both the NS targets in the Authority section as well as the glue records in the Additional section. + +Dependencies +------------ + +* `lua-basexx `_ available in LuaRocks + +.. _dnscurve: https://dnscurve.org/ +.. _SPKI: https://en.wikipedia.org/wiki/Simple_public-key_infrastructure diff --git a/modules/experimental_dot_auth/experimental_dot_auth.lua b/modules/experimental_dot_auth/experimental_dot_auth.lua new file mode 100644 index 0000000..08c5080 --- /dev/null +++ b/modules/experimental_dot_auth/experimental_dot_auth.lua @@ -0,0 +1,122 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module interface + +local ffi = require('ffi') +local basexx = require('basexx') +local C = ffi.C + +-- Export module interface +local M = {} +M.layer = {} +local base32 = {} +local str = {} +local AF_INET = 2 +local AF_INET6 = 10 +local INET_ADDRSTRLEN = 16 +local INET6_ADDRSTRLEN = 46 + +ffi.cdef[[ +/* + * Data structures + */ +typedef int socklen_t; + struct sockaddr_storage{ + unsigned short int ss_family; + unsigned long int __ss_align; + char __ss_padding[128 - (2 *sizeof(unsigned long int))]; + }; + struct in_addr{ + unsigned char s_addr[4]; + }; + struct in6_addr{ + unsigned char s6_addr[16]; + }; + struct sockaddr_in{ + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; + } __attribute__ ((__packed__)); + struct sockaddr_in6{ + unsigned short sin6_family; + unsigned short sin6_port; + unsigned int sin6_flowinfo; + struct in6_addr sin6_addr; + unsigned int sin6_scope_id; + }; + typedef unsigned short sa_family_t; + struct sockaddr_un { + sa_family_t sun_family; + char sun_path[108]; + }; + const char *inet_ntop( + int af, + const void *cp, + char *buf, + socklen_t len); +]] + +function base32.pad(b32) + local m = #b32 % 8 + if m ~= 0 then + b32 = b32 .. string.rep("=", 8 - m) + end + return b32 +end + +function str.starts(String,Start) + return string.sub(String,1,string.len(Start))==Start +end + +-- Handle DoT signalling NS domains. +function M.layer.consume(state, _, pkt) + -- Only successful answers + if state == kres.FAIL then return state end + -- log_debug(ffi.C.LOG_GRP_DOTAUTH, "%s", pkt:tostring()) + local authority = pkt:section(kres.section.AUTHORITY) + local additional = pkt:section(kres.section.ADDITIONAL) + for _, rr in ipairs(authority) do + --log_debug(ffi.C.LOG_GRP_DOTAUTH, "%d %s", rr.type, kres.dname2str(rr.rdata)) + if rr.type == kres.type.NS then + local name = kres.dname2str(rr.rdata):upper() + -- log_debug(ffi.C.LOG_GRP_DOTAUTH, "NS %d", name:len()) + if name:len() > 56 and str.starts(name, "DOT-") then + local k = basexx.to_base64( + basexx.from_base32( + base32.pad(string.sub(name, 5, string.find(name, '[.]') - 1)) + ) + ) + for _, rr_add in ipairs(additional) do + if rr_add.type == kres.type.A or rr_add.type == kres.type.AAAA then + local name_add = kres.dname2str(rr_add.owner):upper() + if name == name_add then + local addrbuf + if rr_add.type == kres.type.A then + local ns_addr = ffi.new("struct sockaddr_in") + ns_addr.sin_family = AF_INET + + ns_addr.sin_addr.s_addr = rr_add.rdata + addrbuf = ffi.new("char[?]", INET_ADDRSTRLEN) + C.inet_ntop(AF_INET, ns_addr.sin_addr, addrbuf, INET_ADDRSTRLEN) + else + local ns_addr = ffi.new("struct sockaddr_in6") + ns_addr.sin6_family = AF_INET6 + + ns_addr.sin6_addr.s6_addr = rr_add.rdata + addrbuf = ffi.new("char[?]", INET6_ADDRSTRLEN) + C.inet_ntop(AF_INET6, ns_addr.sin6_addr, addrbuf, INET6_ADDRSTRLEN) + end + net.tls_client(ffi.string(addrbuf).."@853", {k}) + log_info(ffi.C.LOG_GRP_DOTAUTH, "Adding %s IP %s %s", name_add, ffi.string(addrbuf).."@853", k) + end + end + end + end + end + end + + return state + +end + +return M diff --git a/modules/experimental_dot_auth/meson.build b/modules/experimental_dot_auth/meson.build new file mode 100644 index 0000000..e2e1edf --- /dev/null +++ b/modules/experimental_dot_auth/meson.build @@ -0,0 +1,13 @@ +# LUA module: experimental_dot_auth +# SPDX-License-Identifier: GPL-3.0-or-later + +lua_mod_src += [ + files('experimental_dot_auth.lua'), +] + +# install static files +install_subdir( + 'static', + strip_directory: true, + install_dir: modules_dir / 'http', +) diff --git a/modules/extended_error/extended_error.c b/modules/extended_error/extended_error.c new file mode 100644 index 0000000..db0ed57 --- /dev/null +++ b/modules/extended_error/extended_error.c @@ -0,0 +1,47 @@ +#include + +#include "lib/module.h" +#include "daemon/engine.h" + +static int extended_error_finalize(kr_layer_t *ctx) { + struct kr_request *req = ctx->req; + const knot_rrset_t *src_opt = req->qsource.packet->opt_rr; + const struct kr_extended_error *ede = &req->extended_error; + + if (ede->info_code == KNOT_EDNS_EDE_NONE /* no extended error */ + || src_opt == NULL /* no EDNS in query */ + || kr_fails_assert(ede->info_code >= 0 && ede->info_code < UINT16_MAX) /* info code out of range */ + || kr_fails_assert(req->answer->opt_rr) /* sanity check - answer should have EDNS */ + ) { + return ctx->state; + } + + const uint16_t info_code = (uint16_t)ede->info_code; + const size_t extra_len = ede->extra_text ? strlen(ede->extra_text) : 0; + uint8_t buf[sizeof(info_code) + extra_len]; + knot_wire_write_u16(buf, info_code); + if (extra_len) + memcpy(buf + sizeof(info_code), ede->extra_text, extra_len); + + if (knot_edns_add_option(req->answer->opt_rr, KNOT_EDNS_OPTION_EDE, + sizeof(buf), buf, &req->pool) != KNOT_EOK) { + /* something went wrong and there is no way to salvage content of OPT RRset */ + kr_log_req(req, 0, 0, EDE, "unable to add Extended Error option\n"); + knot_rrset_clear(req->answer->opt_rr, &req->pool); + } + + return ctx->state; +} + +KR_EXPORT +int extended_error_init(struct kr_module *module) { + static kr_layer_api_t layer = { + .answer_finalize = &extended_error_finalize, + }; + layer.data = module; + module->layer = &layer; + + return kr_ok(); +} + +KR_MODULE_EXPORT(extended_error) diff --git a/modules/extended_error/meson.build b/modules/extended_error/meson.build new file mode 100644 index 0000000..26e87b0 --- /dev/null +++ b/modules/extended_error/meson.build @@ -0,0 +1,20 @@ +# C module: extended_error +# SPDX-License-Identifier: GPL-3.0-or-later + +extended_error_src = files([ + 'extended_error.c', +]) +c_src_lint += extended_error_src + +extended_error_mod = shared_module( + 'extended_error', + extended_error_src, + dependencies: [ + libknot, + luajit_inc, + ], + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, +) diff --git a/modules/graphite/.packaging/centos/7/rundeps b/modules/graphite/.packaging/centos/7/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/centos/7/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/centos/8/rundeps b/modules/graphite/.packaging/centos/8/rundeps new file mode 100644 index 0000000..182251d --- /dev/null +++ b/modules/graphite/.packaging/centos/8/rundeps @@ -0,0 +1 @@ +lua5.1-cqueues diff --git a/modules/graphite/.packaging/debian/10/rundeps b/modules/graphite/.packaging/debian/10/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/debian/10/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/debian/9/rundeps b/modules/graphite/.packaging/debian/9/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/debian/9/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/fedora/31/rundeps b/modules/graphite/.packaging/fedora/31/rundeps new file mode 100644 index 0000000..182251d --- /dev/null +++ b/modules/graphite/.packaging/fedora/31/rundeps @@ -0,0 +1 @@ +lua5.1-cqueues diff --git a/modules/graphite/.packaging/fedora/32/rundeps b/modules/graphite/.packaging/fedora/32/rundeps new file mode 100644 index 0000000..182251d --- /dev/null +++ b/modules/graphite/.packaging/fedora/32/rundeps @@ -0,0 +1 @@ +lua5.1-cqueues diff --git a/modules/graphite/.packaging/leap/15.2/NOTSUPPORTED b/modules/graphite/.packaging/leap/15.2/NOTSUPPORTED new file mode 100644 index 0000000..b1ae77d --- /dev/null +++ b/modules/graphite/.packaging/leap/15.2/NOTSUPPORTED @@ -0,0 +1,6 @@ + +ERROR:test_packaging:Installing https://luarocks.org/cqueues-20190813.51-0.src.rock +164 Error: Failed extracting rel-20190813.tar.gz + +Doesn't works on GitLab CI/CD, but works on localhost. +gzip and tar packages are installed, all packages has same version as packages on localhost's docker container. diff --git a/modules/graphite/.packaging/leap/15.2/pre-test.sh b/modules/graphite/.packaging/leap/15.2/pre-test.sh new file mode 100755 index 0000000..9614066 --- /dev/null +++ b/modules/graphite/.packaging/leap/15.2/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install cqueues --from=https://mah0x211.github.io/rocks/ diff --git a/modules/graphite/.packaging/leap/15.2/rundeps b/modules/graphite/.packaging/leap/15.2/rundeps new file mode 100644 index 0000000..8323887 --- /dev/null +++ b/modules/graphite/.packaging/leap/15.2/rundeps @@ -0,0 +1,6 @@ +libopenssl-devel +lua51-luarocks +git +tar +gzip +m4 diff --git a/modules/graphite/.packaging/test.config b/modules/graphite/.packaging/test.config new file mode 100644 index 0000000..c23033b --- /dev/null +++ b/modules/graphite/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('graphite') +assert(graphite) +quit() diff --git a/modules/graphite/.packaging/ubuntu/16.04/rundeps b/modules/graphite/.packaging/ubuntu/16.04/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/ubuntu/16.04/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/ubuntu/18.04/rundeps b/modules/graphite/.packaging/ubuntu/18.04/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/ubuntu/18.04/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/ubuntu/20.04/rundeps b/modules/graphite/.packaging/ubuntu/20.04/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/ubuntu/20.04/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/README.rst b/modules/graphite/README.rst new file mode 100644 index 0000000..2f86a6f --- /dev/null +++ b/modules/graphite/README.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-graphite: + +Graphite/InfluxDB/Metronome +--------------------------- + +The ``graphite`` sends statistics over the Graphite_ protocol to either Graphite_, Metronome_, InfluxDB_ or any compatible storage. This allows powerful visualization over metrics collected by Knot Resolver. + +.. tip:: The Graphite server is challenging to get up and running, InfluxDB_ combined with Grafana_ are much easier, and provide richer set of options and available front-ends. Metronome_ by PowerDNS alternatively provides a mini-graphite server for much simpler setups. + +Example configuration: + +Only the ``host`` parameter is mandatory. + +By default the module uses UDP so it doesn't guarantee the delivery, set ``tcp = true`` to enable Graphite over TCP. If the TCP consumer goes down or the connection with Graphite is lost, resolver will periodically attempt to reconnect with it. + +.. code-block:: lua + + modules = { + graphite = { + prefix = hostname() .. worker.id, -- optional metric prefix + host = '127.0.0.1', -- graphite server address + port = 2003, -- graphite server port + interval = 5 * sec, -- publish interval + tcp = false -- set to true if you want TCP mode + } + } + +The module supports sending data to multiple servers at once. + +.. code-block:: lua + + modules = { + graphite = { + host = { '127.0.0.1', '1.2.3.4', '::1' }, + } + } + +Dependencies +^^^^^^^^^^^^ + +* `lua cqueues `_ package. + + +.. _Graphite: https://graphite.readthedocs.io/en/latest/feeding-carbon.html +.. _InfluxDB: https://influxdb.com/ +.. _Metronome: https://github.com/ahuPowerDNS/metronome +.. _Grafana: http://grafana.org/ diff --git a/modules/graphite/graphite.lua b/modules/graphite/graphite.lua new file mode 100644 index 0000000..be2d7b2 --- /dev/null +++ b/modules/graphite/graphite.lua @@ -0,0 +1,146 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Load dependent modules +if not stats then modules.load('stats') end + +-- This is leader-only module +local M = {} +local ffi = require("ffi") +local socket = require("cqueues.socket") +local proto_txt = { + [socket.SOCK_DGRAM] = 'udp', + [socket.SOCK_STREAM] = 'tcp' +} + +local function make_socket(host, port, stype) + local s, err, status + -- timeout before next interval begins (roughly) + local timeout_sec = (M.interval - 10) / sec + + s = socket.connect({ host = host, port = port, type = stype }) + s:setmode('bn', 'bn') + s:settimeout(timeout_sec) + status, err = pcall(s.connect, s, timeout_sec) + if status == true and err == nil then + err = 'connect timeout' + s:close() + status = false + end + + if not status then + log_info(ffi.C.LOG_GRP_GRAPHITE, 'connecting: %s@%d %s reason: %s', + host, port, proto_txt[stype], err) + return status, err + end + return s +end + +-- Create connected UDP socket +local function make_udp(host, port) + return make_socket(host, port, socket.SOCK_DGRAM) +end + +-- Create connected TCP socket +local function make_tcp(host, port) + return make_socket(host, port, socket.SOCK_STREAM) +end + +-- Send the metrics in a table to multiple Graphite consumers +local function publish_table(metrics, prefix, now) + local s + for i in ipairs(M.cli) do + local host = M.info[i] + + if M.cli[i] == -1 then + if host.tcp then + s = make_tcp(host.addr, host.port) + else + s = make_udp(host.addr, host.port) + end + if s then + M.cli[i] = s + end + end + + if M.cli[i] ~= -1 then + for key,val in pairs(metrics) do + local msg = key..' '..val..' '..now..'\n' + if prefix then + msg = prefix..'.'..msg + end + + local ok, err = pcall(M.cli[i].write, M.cli[i], msg) + if not ok then + local tcp = M.cli[i]['connect'] ~= nil + if tcp and host.seen + 2 * M.interval / 1000 <= now then + local sock_type = (host.tcp and socket.SOCK_STREAM) + or socket.SOCK_DGRAM + log_info(ffi.C.LOG_GRP_GRAPHITE, 'reconnecting: %s@%d %s reason: %s', + host.addr, host.port, proto_txt[sock_type], err) + s = make_tcp(host.addr, host.port) + if s then + M.cli[i] = s + host.seen = now + else + M.cli[i] = -1 + break + end + end + end + end -- loop metrics + end + end -- loop M.cli +end + +function M.init() + M.ev = nil + M.cli = {} + M.info = {} + M.interval = 5 * sec + M.prefix = string.format('kresd.%s.%s', hostname(), worker.id) + return 0 +end + +function M.deinit() + if M.ev then event.cancel(M.ev) end + return 0 +end + +-- @function Publish results to the Graphite server(s) +function M.publish() + local now = os.time() + -- Publish built-in statistics + if not M.cli then error("no graphite server configured") end + publish_table(cache.stats(), M.prefix..'.cache', now) + publish_table(worker.stats(), M.prefix..'.worker', now) + -- Publish extended statistics if available + publish_table(stats.list(), M.prefix, now) + return 0 +end + +-- @function Make connection to Graphite server. +function M.add_server(_, host, port, tcp) + table.insert(M.cli, -1) + table.insert(M.info, {addr = host, port = port, tcp = tcp, seen = 0}) + return 0 +end + +function M.config(conf) + -- config defaults + if not conf then return 0 end + if not conf.port then conf.port = 2003 end + if conf.interval then M.interval = conf.interval end + if conf.prefix then M.prefix = conf.prefix end + if type(conf.host) == 'table' then + for _, val in pairs(conf.host) do + M:add_server(val, conf.port, conf.tcp) + end + else + M:add_server(conf.host, conf.port, conf.tcp) + end + -- start publishing stats + if M.ev then event.cancel(M.ev) end + M.ev = event.recurrent(M.interval, function() worker.coroutine(M.publish) end) + return 0 +end + +return M diff --git a/modules/hints/.packaging/test.config b/modules/hints/.packaging/test.config new file mode 100644 index 0000000..d89c7f0 --- /dev/null +++ b/modules/hints/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('hints') +assert(hints) +quit() diff --git a/modules/hints/README.rst b/modules/hints/README.rst new file mode 100644 index 0000000..97d24dd --- /dev/null +++ b/modules/hints/README.rst @@ -0,0 +1,145 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-hints: + +Static hints +============ + +This is a module providing static hints for forward records (A/AAAA) and reverse records (PTR). +The records can be loaded from ``/etc/hosts``-like files and/or added directly. + +You can also use the module to change the root hints; they are used as a safety belt or if the root NS +drops out of cache. + +.. tip:: + + For blocking large lists of domains please use :func:`policy.rpz` + instead of creating huge list of domains with IP address *0.0.0.0*. + +Examples +-------- + +.. code-block:: lua + + -- Load hints after iterator (so hints take precedence before caches) + modules = { 'hints > iterate' } + -- Add a custom hosts file + hints.add_hosts('hosts.custom') + -- Override the root hints + hints.root({ + ['j.root-servers.net.'] = { '2001:503:c27::2:30', '192.58.128.30' } + }) + -- Add a custom hint + hints['foo.bar'] = '127.0.0.1' + +.. note:: + The :ref:`policy ` module applies before hints, + so your hints might get surprisingly shadowed by even default policies. + + That most often happens for :rfc:`6761#section-6` names, e.g. + ``localhost`` and ``test`` or with ``PTR`` records in private address ranges. + To unblock the required names, you may use an explicit :any:`policy.PASS` action. + + .. code-block:: lua + + policy.add(policy.suffix(policy.PASS, {todname('1.168.192.in-addr.arpa')})) + + This ``.PASS`` workaround isn't ideal. To improve some cases, + we recommend to move these ``.PASS`` lines to the end of your rule list. + The point is that applying any :ref:`non-chain action ` + (e.g. :ref:`forwarding actions ` or ``.PASS`` itself) + stops processing *any* later policy rules for that request (including the default block-rules). + You probably don't want this ``.PASS`` to shadow any other rules you might have; + and on the other hand, if any other non-chain rule triggers, + additional ``.PASS`` would not change anything even if it were somehow force-executed. + +Properties +---------- + +.. function:: hints.config([path]) + + :param string path: path to hosts-like file, default: no file + :return: ``{ result: bool }`` + + Clear any configured hints, and optionally load a hosts-like file as in ``hints.add_hosts(path)``. + (Root hints are not touched.) + +.. function:: hints.add_hosts([path]) + + :param string path: path to hosts-like file, default: ``/etc/hosts`` + + Add hints from a host-like file. + +.. function:: hints.get(hostname) + + :param string hostname: i.e. ``"localhost"`` + :return: ``{ result: [address1, address2, ...] }`` + + Return list of address record matching given name. + If no hostname is specified, all hints are returned in the table format used by ``hints.root()``. + +.. function:: hints.set(pair) + + :param string pair: ``hostname address`` i.e. ``"localhost 127.0.0.1"`` + :return: ``{ result: bool }`` + + Add a hostname--address pair hint. + + .. note:: + + If multiple addresses have been added for a name (in separate ``hints.set()`` commands), + all are returned in a forward query. + If multiple names have been added to an address, the last one defined is returned + in a corresponding PTR query. + +.. function:: hints.del(pair) + + :param string pair: ``hostname address`` i.e. ``"localhost 127.0.0.1"``, or just ``hostname`` + :return: ``{ result: bool }`` + + Remove a hostname - address pair hint. If address is omitted, all addresses for the given name are deleted. + +.. function:: hints.root_file(path) + + Replace current root hints from a zonefile. If the path is omitted, the compiled-in path is used, i.e. the root hints are reset to the default. + +.. function:: hints.root(root_hints) + + :param table root_hints: new set of root hints i.e. ``{['name'] = 'addr', ...}`` + :return: ``{ ['a.root-servers.net.'] = { '1.2.3.4', '5.6.7.8', ...}, ... }`` + + Replace current root hints and return the current table of root hints. + + .. tip:: If no parameters are passed, it only returns current root hints set without changing anything. + + Example: + + .. code-block:: lua + + > hints.root({ + ['l.root-servers.net.'] = '199.7.83.42', + ['m.root-servers.net.'] = '202.12.27.33' + }) + [l.root-servers.net.] => { + [1] => 199.7.83.42 + } + [m.root-servers.net.] => { + [1] => 202.12.27.33 + } + + .. tip:: A good rule of thumb is to select only a few fastest root hints. The server learns RTT and NS quality over time, and thus tries all servers available. You can help it by preselecting the candidates. + +.. function:: hints.use_nodata(toggle) + + :param bool toggle: true if enabling NODATA synthesis, false if disabling + :return: ``{ result: bool }`` + + If set to true (the default), NODATA will be synthesised for matching hint name, but mismatching type (e.g. AAAA query when only A hint exists). + +.. function:: hints.ttl([new_ttl]) + + :param int new_ttl: new TTL to set (optional) + :return: the TTL setting + + This function allows to read and write the TTL value used for records generated by the hints module. + diff --git a/modules/hints/hints.c b/modules/hints/hints.c new file mode 100644 index 0000000..34c08b9 --- /dev/null +++ b/modules/hints/hints.c @@ -0,0 +1,677 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/** + * @file hints.c + * @brief Constructed zone cut from the hosts-like file, see @zonecut.h + * + * The module provides an override for queried address records. + */ + +#include +#include +#include +#include +#include +#include + +#include "daemon/engine.h" +#include "lib/zonecut.h" +#include "lib/module.h" +#include "lib/layer.h" + +#include +#include + +/* Defaults */ +#define VERBOSE_MSG(qry, ...) kr_log_q(qry, HINT, __VA_ARGS__) +#define ERR_MSG(...) kr_log_error(HINT, "[ ]" __VA_ARGS__) + +struct hints_data { + struct kr_zonecut hints; + struct kr_zonecut reverse_hints; + bool use_nodata; /**< See hint_use_nodata() description, exposed via lua. */ + uint32_t ttl; /**< TTL used for the hints, exposed via lua. */ +}; +static const uint32_t HINTS_TTL_DEFAULT = 5; + +/** Useful for returning from module properties. */ +static char * bool2jsonstr(bool val) +{ + char *result = NULL; + if (-1 == asprintf(&result, "{ \"result\": %s }", val ? "true" : "false")) + result = NULL; + return result; +} + +static int put_answer(knot_pkt_t *pkt, struct kr_query *qry, knot_rrset_t *rr, bool use_nodata) +{ + int ret = 0; + if (!knot_rrset_empty(rr) || use_nodata) { + /* Update packet question */ + if (!knot_dname_is_equal(knot_pkt_qname(pkt), rr->owner)) { + kr_pkt_recycle(pkt); + knot_pkt_put_question(pkt, qry->sname, qry->sclass, qry->stype); + } + if (!knot_rrset_empty(rr)) { + /* Append to packet */ + ret = knot_pkt_put_rotate(pkt, KNOT_COMPR_HINT_QNAME, rr, + qry->reorder, KNOT_PF_FREE); + } else { + /* Return empty answer if name exists, but type doesn't match */ + knot_wire_set_aa(pkt->wire); + } + } else { + ret = kr_error(ENOENT); + } + /* Clear RR if failed */ + if (ret != 0) { + knot_rrset_clear(rr, &pkt->mm); + } + return ret; +} + +static int satisfy_reverse(/*const*/ struct hints_data *data, + knot_pkt_t *pkt, struct kr_query *qry) +{ + /* Find a matching name */ + pack_t *addr_set = kr_zonecut_find(&data->reverse_hints, qry->sname); + if (!addr_set || addr_set->len == 0) { + return kr_error(ENOENT); + } + knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm); + knot_rrset_t rr; + knot_rrset_init(&rr, qname, KNOT_RRTYPE_PTR, KNOT_CLASS_IN, data->ttl); + + /* Append address records from hints */ + uint8_t *addr = pack_last(*addr_set); + if (addr != NULL) { + size_t len = pack_obj_len(addr); + void *addr_val = pack_obj_val(addr); + knot_rrset_add_rdata(&rr, addr_val, len, &pkt->mm); + } + + return put_answer(pkt, qry, &rr, data->use_nodata); +} + +static int satisfy_forward(/*const*/ struct hints_data *data, + knot_pkt_t *pkt, struct kr_query *qry) +{ + /* Find a matching name */ + pack_t *addr_set = kr_zonecut_find(&data->hints, qry->sname); + if (!addr_set || addr_set->len == 0) { + return kr_error(ENOENT); + } + knot_dname_t *qname = knot_dname_copy(qry->sname, &pkt->mm); + knot_rrset_t rr; + knot_rrset_init(&rr, qname, qry->stype, qry->sclass, data->ttl); + + size_t family_len; + switch (rr.type) { + case KNOT_RRTYPE_A: + family_len = sizeof(struct in_addr); + break; + case KNOT_RRTYPE_AAAA: + family_len = sizeof(struct in6_addr); + break; + default: + goto finish; + }; + + /* Append address records from hints */ + uint8_t *addr = pack_head(*addr_set); + while (addr != pack_tail(*addr_set)) { + size_t len = pack_obj_len(addr); + void *addr_val = pack_obj_val(addr); + if (len == family_len) { + knot_rrset_add_rdata(&rr, addr_val, len, &pkt->mm); + } + addr = pack_obj_next(addr); + } +finish: + return put_answer(pkt, qry, &rr, data->use_nodata); +} + +static int query(kr_layer_t *ctx, knot_pkt_t *pkt) +{ + struct kr_query *qry = ctx->req->current_query; + if (!qry || (ctx->state & KR_STATE_FAIL)) { + return ctx->state; + } + + struct kr_module *module = ctx->api->data; + struct hints_data *data = module->data; + if (!data) { /* No valid file. */ + return ctx->state; + } + /* We can optimize for early return like this: */ + if (!data->use_nodata && qry->stype != KNOT_RRTYPE_A + && qry->stype != KNOT_RRTYPE_AAAA && qry->stype != KNOT_RRTYPE_PTR) { + return ctx->state; + } + /* FIXME: putting directly into packet breaks ordering in case the hint + * is applied after a CNAME jump. */ + if (knot_dname_in_bailiwick(qry->sname, (const uint8_t *)"\4arpa\0") >= 0) { + if (satisfy_reverse(data, pkt, qry) != 0) + return ctx->state; + } else { + if (satisfy_forward(data, pkt, qry) != 0) + return ctx->state; + } + + VERBOSE_MSG(qry, "<= answered from hints\n"); + qry->flags.DNSSEC_WANT = false; /* Never authenticated */ + qry->flags.CACHED = true; + qry->flags.NO_MINIMIZE = true; + pkt->parsed = pkt->size; + knot_wire_set_qr(pkt->wire); + return KR_STATE_DONE; +} + +static int parse_addr_str(union kr_sockaddr *sa, const char *addr) +{ + int family = strchr(addr, ':') ? AF_INET6 : AF_INET; + memset(sa, 0, sizeof(*sa)); + sa->ip.sa_family = family; + char *addr_bytes = (/*const*/char *)kr_inaddr(&sa->ip); + if (inet_pton(family, addr, addr_bytes) != 1) { + return kr_error(EILSEQ); + } + return 0; +} + +/** @warning _NOT_ thread-safe; returns a pointer to static data! */ +static const knot_dname_t * raw_addr2reverse(const uint8_t *raw_addr, int family) +{ + #define REV_MAXLEN (4*16 + 16 /* the suffix, terminator, etc. */) + char reverse_addr[REV_MAXLEN]; + static knot_dname_t dname[REV_MAXLEN]; + #undef REV_MAXLEN + + if (family == AF_INET) { + snprintf(reverse_addr, sizeof(reverse_addr), + "%d.%d.%d.%d.in-addr.arpa.", + raw_addr[3], raw_addr[2], raw_addr[1], raw_addr[0]); + } else if (family == AF_INET6) { + char *ra_it = reverse_addr; + for (int i = 15; i >= 0; --i) { + ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it; + int written = snprintf(ra_it, free_space, "%x.%x.", + raw_addr[i] & 0x0f, raw_addr[i] >> 4); + if (kr_fails_assert(written < free_space)) + return NULL; + ra_it += written; + } + ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it; + if (snprintf(ra_it, free_space, "ip6.arpa.") >= free_space) { + return NULL; + } + } else { + return NULL; + } + + if (!knot_dname_from_str(dname, reverse_addr, sizeof(dname))) { + return NULL; + } + return dname; +} + +static const knot_dname_t * addr2reverse(const char *addr) +{ + /* Parse address string */ + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) { + return NULL; + } + return raw_addr2reverse((const /*sign*/uint8_t *)kr_inaddr(&ia.ip), + kr_inaddr_family(&ia.ip)); +} + +static int add_pair(struct kr_zonecut *hints, const char *name, const char *addr) +{ + /* Build key */ + knot_dname_t key[KNOT_DNAME_MAXLEN]; + if (!knot_dname_from_str(key, name, sizeof(key))) { + return kr_error(EINVAL); + } + knot_dname_to_lower(key); + + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) { + return kr_error(EINVAL); + } + + return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip)); +} + +static int add_reverse_pair(struct kr_zonecut *hints, const char *name, const char *addr) +{ + const knot_dname_t *key = addr2reverse(addr); + + if (key == NULL) { + return kr_error(EINVAL); + } + + knot_dname_t ptr_name[KNOT_DNAME_MAXLEN]; + if (!knot_dname_from_str(ptr_name, name, sizeof(ptr_name))) { + return kr_error(EINVAL); + } + + return kr_zonecut_add(hints, key, ptr_name, knot_dname_size(ptr_name)); +} + +/** For a given name, remove either one address or all of them (if == NULL). + * + * Also remove the corresponding reverse records. + */ +static int del_pair(struct hints_data *data, const char *name, const char *addr) +{ + /* Build key */ + knot_dname_t key[KNOT_DNAME_MAXLEN]; + if (!knot_dname_from_str(key, name, sizeof(key))) { + return kr_error(EINVAL); + } + int key_len = knot_dname_size(key); + + if (addr) { + /* Remove the pair. */ + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) { + return kr_error(EINVAL); + } + + const knot_dname_t *reverse_key = addr2reverse(addr); + kr_zonecut_del(&data->reverse_hints, reverse_key, key, key_len); + return kr_zonecut_del(&data->hints, key, + kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip)); + } + /* We're removing everything for the name; + * first find the name's pack */ + pack_t *addr_set = kr_zonecut_find(&data->hints, key); + if (!addr_set || addr_set->len == 0) { + return kr_error(ENOENT); + } + + /* Remove address records in hints from reverse_hints. */ + + for (uint8_t *a = pack_head(*addr_set); a != pack_tail(*addr_set); + a = pack_obj_next(a)) { + void *addr_val = pack_obj_val(a); + int family = pack_obj_len(a) == kr_family_len(AF_INET) + ? AF_INET : AF_INET6; + const knot_dname_t *reverse_key = raw_addr2reverse(addr_val, family); + if (reverse_key != NULL) { + kr_zonecut_del(&data->reverse_hints, reverse_key, key, key_len); + } + } + + /* Remove the whole name. */ + return kr_zonecut_del_all(&data->hints, key); +} + +static int load_file(struct kr_module *module, const char *path) +{ + auto_fclose FILE *fp = fopen(path, "r"); + if (fp == NULL) { + ERR_MSG("reading '%s' failed: %s\n", path, strerror(errno)); + return kr_error(errno); + } else { + VERBOSE_MSG(NULL, "reading '%s'\n", path); + } + + /* Load file to map */ + struct hints_data *data = module->data; + size_t line_len_unused = 0; + size_t count = 0; + size_t line_count = 0; + auto_free char *line = NULL; + int ret = kr_ok(); + + while (getline(&line, &line_len_unused, fp) > 0) { + ++line_count; + /* Ingore #comments as described in man hosts.5 */ + char *comm = strchr(line, '#'); + if (comm) { + *comm = '\0'; + } + + char *saveptr = NULL; + const char *addr = strtok_r(line, " \t\n", &saveptr); + if (addr == NULL || strlen(addr) == 0) { + continue; + } + const char *canonical_name = strtok_r(NULL, " \t\n", &saveptr); + if (canonical_name == NULL) { + ret = -1; + goto error; + } + /* Since the last added PTR records takes preference, + * we add canonical name as the last one. */ + const char *name_tok; + while ((name_tok = strtok_r(NULL, " \t\n", &saveptr)) != NULL) { + ret = add_pair(&data->hints, name_tok, addr); + if (!ret) { + ret = add_reverse_pair(&data->reverse_hints, name_tok, addr); + } + if (ret) { + ret = -1; + goto error; + } + count += 1; + } + ret = add_pair(&data->hints, canonical_name, addr); + if (!ret) { + ret = add_reverse_pair(&data->reverse_hints, canonical_name, addr); + } + if (ret) { + ret = -1; + goto error; + } + count += 1; + } +error: + if (ret) { + ret = kr_error(ret); + ERR_MSG("%s:%zu: invalid syntax\n", path, line_count); + } + VERBOSE_MSG(NULL, "loaded %zu hints\n", count); + return ret; +} + +static char* hint_add_hosts(void *env, struct kr_module *module, const char *args) +{ + if (!args) + args = "/etc/hosts"; + int err = load_file(module, args); + return bool2jsonstr(err == kr_ok()); +} + +/** + * Set name => address hint. + * + * Input: { name, address } + * Output: { result: bool } + * + */ +static char* hint_set(void *env, struct kr_module *module, const char *args) +{ + struct hints_data *data = module->data; + if (!args) + return NULL; + auto_free char *args_copy = strdup(args); + if (!args_copy) + return NULL; + + int ret = -1; + char *addr = strchr(args_copy, ' '); + if (addr) { + *addr = '\0'; + ++addr; + ret = add_reverse_pair(&data->reverse_hints, args_copy, addr); + if (ret) { + del_pair(data, args_copy, addr); + } else { + ret = add_pair(&data->hints, args_copy, addr); + } + } + + return bool2jsonstr(ret == 0); +} + +static char* hint_del(void *env, struct kr_module *module, const char *args) +{ + struct hints_data *data = module->data; + if (!args) + return NULL; + auto_free char *args_copy = strdup(args); + if (!args_copy) + return NULL; + + int ret = -1; + char *addr = strchr(args_copy, ' '); + if (addr) { + *addr = '\0'; + ++addr; + } + ret = del_pair(data, args_copy, addr); + + return bool2jsonstr(ret == 0); +} + +/** @internal Pack address list into JSON array. */ +static JsonNode *pack_addrs(pack_t *pack) +{ + char buf[INET6_ADDRSTRLEN]; + JsonNode *root = json_mkarray(); + uint8_t *addr = pack_head(*pack); + while (addr != pack_tail(*pack)) { + size_t len = pack_obj_len(addr); + int family = len == sizeof(struct in_addr) ? AF_INET : AF_INET6; + if (!inet_ntop(family, pack_obj_val(addr), buf, sizeof(buf))) { + break; + } + json_append_element(root, json_mkstring(buf)); + addr = pack_obj_next(addr); + } + return root; +} + +static char* pack_hints(struct kr_zonecut *hints); +/** + * Retrieve address hints, either for given name or for all names. + * + * Input: name + * Output: NULL or "{ address1, address2, ... }" + */ +static char* hint_get(void *env, struct kr_module *module, const char *args) +{ + struct kr_zonecut *hints = &((struct hints_data *) module->data)->hints; + if (kr_fails_assert(hints)) + return NULL; + + if (!args) { + return pack_hints(hints); + } + + knot_dname_t key[KNOT_DNAME_MAXLEN]; + pack_t *pack = NULL; + if (knot_dname_from_str(key, args, sizeof(key))) { + pack = kr_zonecut_find(hints, key); + } + if (!pack || pack->len == 0) { + return NULL; + } + + char *result = NULL; + JsonNode *root = pack_addrs(pack); + if (root) { + result = json_encode(root); + json_delete(root); + } + return result; +} + +/** @internal Pack all hints into serialized JSON. */ +static char* pack_hints(struct kr_zonecut *hints) { + char *result = NULL; + JsonNode *root_node = json_mkobject(); + trie_it_t *it; + for (it = trie_it_begin(hints->nsset); !trie_it_finished(it); trie_it_next(it)) { + KR_DNAME_GET_STR(nsname_str, (const knot_dname_t *)trie_it_key(it, NULL)); + JsonNode *addr_list = pack_addrs((pack_t *)*trie_it_val(it)); + if (!addr_list) goto error; + json_append_member(root_node, nsname_str, addr_list); + } + result = json_encode(root_node); +error: + trie_it_free(it); + json_delete(root_node); + return result; +} + +static void unpack_hint(struct kr_zonecut *root_hints, JsonNode *table, const char *name) +{ + JsonNode *node = NULL; + json_foreach(node, table) { + switch(node->tag) { + case JSON_STRING: add_pair(root_hints, name ? name : node->key, node->string_); break; + case JSON_ARRAY: unpack_hint(root_hints, node, name ? name : node->key); break; + default: continue; + } + } +} + +/** + * Get/set root hints set. + * + * Input: { name: [addr_list], ... } + * Output: current list + * + */ +static char* hint_root(void *env, struct kr_module *module, const char *args) +{ + struct engine *engine = env; + struct kr_context *ctx = &engine->resolver; + struct kr_zonecut *root_hints = &ctx->root_hints; + /* Replace root hints if parameter is set */ + if (args && args[0] != '\0') { + JsonNode *root_node = json_decode(args); + kr_zonecut_set(root_hints, (const uint8_t *)""); + unpack_hint(root_hints, root_node, NULL); + json_delete(root_node); + } + /* Return current root hints */ + return pack_hints(root_hints); +} + +static char* hint_root_file(void *env, struct kr_module *module, const char *args) +{ + struct engine *engine = env; + struct kr_context *ctx = &engine->resolver; + const char *err_msg = engine_hint_root_file(ctx, args); + if (err_msg) { + luaL_error(engine->L, "error when opening '%s': %s", args, err_msg); + } + return strdup(err_msg ? err_msg : ""); +} + +static char* hint_use_nodata(void *env, struct kr_module *module, const char *args) +{ + struct hints_data *data = module->data; + if (!args) { + return NULL; + } + + JsonNode *root_node = json_decode(args); + if (!root_node || root_node->tag != JSON_BOOL) { + json_delete(root_node); + return bool2jsonstr(false); + } + + data->use_nodata = root_node->bool_; + json_delete(root_node); + return bool2jsonstr(true); +} + +static char* hint_ttl(void *env, struct kr_module *module, const char *args) +{ + struct hints_data *data = module->data; + + /* Do no change on nonsense TTL values (incl. suspicious floats). */ + JsonNode *root_node = args ? json_decode(args) : NULL; + if (root_node && root_node->tag == JSON_NUMBER) { + double ttl_d = root_node->number_; + uint32_t ttl = (uint32_t)round(ttl_d); + if (ttl_d >= 0 && fabs(ttl_d - ttl) * 64 < 1) { + data->ttl = ttl; + } + } + json_delete(root_node); + + /* Always return the current TTL setting. Plain number is valid JSON. */ + char *result = NULL; + if (-1 == asprintf(&result, "%"PRIu32, data->ttl)) { + result = NULL; + } + return result; +} + +/** Basic initialization: get a memory pool, etc. */ +KR_EXPORT +int hints_init(struct kr_module *module) +{ + static kr_layer_api_t layer = { + .produce = &query, + }; + /* Store module reference */ + layer.data = module; + module->layer = &layer; + + static const struct kr_prop props[] = { + { &hint_set, "set", "Set {name, address} hint.", }, + { &hint_del, "del", "Delete one {name, address} hint or all addresses for the name.", }, + { &hint_get, "get", "Retrieve hint for given name.", }, + { &hint_ttl, "ttl", "Set/get TTL used for the hints.", }, + { &hint_add_hosts, "add_hosts", "Load a file with hosts-like formatting and add contents into hints.", }, + { &hint_root, "root", "Replace root hints set (empty value to return current list).", }, + { &hint_root_file, "root_file", "Replace root hints set from a zonefile.", }, + { &hint_use_nodata, "use_nodata", "Synthesise NODATA if name matches, but type doesn't. True by default.", }, + { NULL, NULL, NULL } + }; + module->props = props; + + knot_mm_t *pool = mm_ctx_mempool2(MM_DEFAULT_BLKSIZE); + if (!pool) { + return kr_error(ENOMEM); + } + struct hints_data *data = mm_alloc(pool, sizeof(struct hints_data)); + if (!data) { + mp_delete(pool->ctx); + return kr_error(ENOMEM); + } + kr_zonecut_init(&data->hints, (const uint8_t *)(""), pool); + kr_zonecut_init(&data->reverse_hints, (const uint8_t *)(""), pool); + data->use_nodata = true; + data->ttl = HINTS_TTL_DEFAULT; + module->data = data; + + return kr_ok(); +} + +/** Release all resources. */ +KR_EXPORT +int hints_deinit(struct kr_module *module) +{ + struct hints_data *data = module->data; + if (data) { + kr_zonecut_deinit(&data->hints); + kr_zonecut_deinit(&data->reverse_hints); + mp_delete(data->hints.pool->ctx); + module->data = NULL; + } + return kr_ok(); +} + +/** Drop all hints, and load a hosts file if any was specified. + * + * It seems slightly strange to drop all, but keep doing that for now. + */ +KR_EXPORT +int hints_config(struct kr_module *module, const char *conf) +{ + hints_deinit(module); + int err = hints_init(module); + if (err != kr_ok()) { + return err; + } + + if (conf && conf[0]) { + return load_file(module, conf); + } + return kr_ok(); +} + +KR_MODULE_EXPORT(hints) + +#undef VERBOSE_MSG diff --git a/modules/hints/meson.build b/modules/hints/meson.build new file mode 100644 index 0000000..0a0945c --- /dev/null +++ b/modules/hints/meson.build @@ -0,0 +1,24 @@ +# C module: hints +# SPDX-License-Identifier: GPL-3.0-or-later + +hints_src = files([ + 'hints.c', +]) +c_src_lint += hints_src + +hints_mod = shared_module( + 'hints', + hints_src, + dependencies: [ + libknot, + luajit_inc, + ], + include_directories: mod_inc_dir, + name_prefix: '', + install: true, + install_dir: modules_dir, +) + +config_tests += [ + ['hints', files('tests/hints.test.lua'), ['skip_asan']], +] diff --git a/modules/hints/tests/hints.test.hosts b/modules/hints/tests/hints.test.hosts new file mode 100644 index 0000000..0111507 --- /dev/null +++ b/modules/hints/tests/hints.test.hosts @@ -0,0 +1 @@ +192.0.2.1 myname.lan # badname.lan and the rest of the comment diff --git a/modules/hints/tests/hints.test.lua b/modules/hints/tests/hints.test.lua new file mode 100644 index 0000000..b62e502 --- /dev/null +++ b/modules/hints/tests/hints.test.lua @@ -0,0 +1,64 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local utils = require('test_utils') + +-- setup resolver +modules = { 'hints > iterate' } + +-- test for default configuration +local function test_default() + -- get loaded root hints and change names to lowercase + local hints_data = utils.table_keys_to_lower(hints.root()) + + -- root hints loaded from default location + -- check correct ip address of a.root-server.net + utils.contains(hints_data['a.root-servers.net.'], '198.41.0.4', 'has IP address for a.root-servers.net.') +end + +-- test loading from config file +local function test_custom() + -- load custom root hints file with fake ip address for a.root-server.net + local err_msg = hints.root_file('hints_test.zone') + same(err_msg, '', 'load root hints from file') + + -- get loaded root hints and change names to lowercase + local hints_data = utils.table_keys_to_lower(hints.root()) + isnt(hints_data['a.root-servers.net.'], nil, 'can retrieve root hints') + + -- check loaded ip address of a.root-server.net + utils.not_contains(hints_data['a.root-servers.net.'], '198.41.0.4', + 'real IP address for a.root-servers.net. is replaced') + utils.contains(hints_data['a.root-servers.net.'], '10.0.0.1', + 'real IP address for a.root-servers.net. is correct') +end + +-- test that setting an address hint works (TODO: and NXDOMAIN) +local function test_nxdomain() + hints.config() -- clean start + hints.use_nodata(false) + hints.add_hosts('hints.test.hosts') + -- TODO: prefilling or some other way of getting NXDOMAIN (instead of SERVFAIL) + utils.check_answer('bad name gives NXDOMAIN', + 'badname.lan', kres.type.A, kres.rcode.SERVFAIL) + utils.check_answer('another type gives NXDOMAIN', + 'myname.lan', kres.type.AAAA, kres.rcode.SERVFAIL) + utils.check_answer('record itself is OK', + 'myname.lan', kres.type.A, kres.rcode.NOERROR) +end + +-- test that NODATA is correctly generated +local function test_nodata() + hints.config() -- clean start + hints.use_nodata(true) -- default ATM but let's not depend on that + hints['myname.lan'] = '2001:db8::1' + utils.check_answer('another type gives NODATA', + 'myname.lan', kres.type.MX, utils.NODATA) + utils.check_answer('record itself is OK', + 'myname.lan', kres.type.AAAA, kres.rcode.NOERROR) +end + +return { + test_default, + test_custom, + test_nxdomain, + test_nodata, +} diff --git a/modules/hints/tests/hints_test.zone b/modules/hints/tests/hints_test.zone new file mode 100644 index 0000000..c3252f8 --- /dev/null +++ b/modules/hints/tests/hints_test.zone @@ -0,0 +1,2 @@ +; SPDX-License-Identifier: GPL-3.0-or-later +A.ROOT-SERVERS.NET. 3600000 A 10.0.0.1 diff --git a/modules/http/.packaging/centos/7/rundeps b/modules/http/.packaging/centos/7/rundeps new file mode 100644 index 0000000..c557cb2 --- /dev/null +++ b/modules/http/.packaging/centos/7/rundeps @@ -0,0 +1 @@ +lua-http diff --git a/modules/http/.packaging/centos/8/rundeps b/modules/http/.packaging/centos/8/rundeps new file mode 100644 index 0000000..ed5aee1 --- /dev/null +++ b/modules/http/.packaging/centos/8/rundeps @@ -0,0 +1 @@ +lua5.1-http diff --git a/modules/http/.packaging/debian/10/rundeps b/modules/http/.packaging/debian/10/rundeps new file mode 100644 index 0000000..c557cb2 --- /dev/null +++ b/modules/http/.packaging/debian/10/rundeps @@ -0,0 +1 @@ +lua-http diff --git a/modules/http/.packaging/debian/9/rundeps b/modules/http/.packaging/debian/9/rundeps new file mode 100644 index 0000000..c557cb2 --- /dev/null +++ b/modules/http/.packaging/debian/9/rundeps @@ -0,0 +1 @@ +lua-http diff --git a/modules/http/.packaging/fedora/31/rundeps b/modules/http/.packaging/fedora/31/rundeps new file mode 100644 index 0000000..ed5aee1 --- /dev/null +++ b/modules/http/.packaging/fedora/31/rundeps @@ -0,0 +1 @@ +lua5.1-http diff --git a/modules/http/.packaging/fedora/32/rundeps b/modules/http/.packaging/fedora/32/rundeps new file mode 100644 index 0000000..ed5aee1 --- /dev/null +++ b/modules/http/.packaging/fedora/32/rundeps @@ -0,0 +1 @@ +lua5.1-http diff --git a/modules/http/.packaging/leap/15.2/NOTSUPPORTED b/modules/http/.packaging/leap/15.2/NOTSUPPORTED new file mode 100644 index 0000000..bb50260 --- /dev/null +++ b/modules/http/.packaging/leap/15.2/NOTSUPPORTED @@ -0,0 +1,5 @@ + +https://github.com/wahern/luaossl/issues/175 + + +Doesn't work with libopenssl-devel 1.1.0i-lp151.1.1 diff --git a/modules/http/.packaging/leap/15.2/pre-test.sh b/modules/http/.packaging/leap/15.2/pre-test.sh new file mode 100755 index 0000000..bb1e131 --- /dev/null +++ b/modules/http/.packaging/leap/15.2/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install http --from=https://mah0x211.github.io/rocks/ diff --git a/modules/http/.packaging/leap/15.2/rundeps b/modules/http/.packaging/leap/15.2/rundeps new file mode 100644 index 0000000..ab05188 --- /dev/null +++ b/modules/http/.packaging/leap/15.2/rundeps @@ -0,0 +1,7 @@ +libopenssl-devel +lua51-devel +lua51-luarocks +git +tar +gzip +m4 diff --git a/modules/http/.packaging/test.config b/modules/http/.packaging/test.config new file mode 100644 index 0000000..cb5e5dd --- /dev/null +++ b/modules/http/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('http') +assert(http) +quit() diff --git a/modules/http/.packaging/ubuntu/16.04/NOTSUPPORTED b/modules/http/.packaging/ubuntu/16.04/NOTSUPPORTED new file mode 100644 index 0000000..e69de29 diff --git a/modules/http/.packaging/ubuntu/18.04/rundeps b/modules/http/.packaging/ubuntu/18.04/rundeps new file mode 100644 index 0000000..c557cb2 --- /dev/null +++ b/modules/http/.packaging/ubuntu/18.04/rundeps @@ -0,0 +1 @@ +lua-http diff --git a/modules/http/.packaging/ubuntu/20.04/rundeps b/modules/http/.packaging/ubuntu/20.04/rundeps new file mode 100644 index 0000000..c557cb2 --- /dev/null +++ b/modules/http/.packaging/ubuntu/20.04/rundeps @@ -0,0 +1 @@ +lua-http diff --git a/modules/http/README.rst b/modules/http/README.rst new file mode 100644 index 0000000..6d8a075 --- /dev/null +++ b/modules/http/README.rst @@ -0,0 +1,188 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-http: + +Other HTTP services +=================== + +.. tip:: In most distributions, the ``http`` module is available from a + separate package ``knot-resolver-module-http``. The module isn't packaged + for openSUSE. + +This module does the heavy lifting to provide an HTTP and HTTP/2 enabled +server which provides few built-in services and also allows other +modules to export restful APIs and websocket streams. + +One example is statistics module that can stream live metrics on the website, +or publish metrics on request for Prometheus scraper. + +By default this module provides two kinds of endpoints, +and unlimited number of "used-defined kinds" can be added in configuration. + ++--------------+---------------------------------------------------------------------------------+ +| **Kind** | **Explanation** | ++--------------+---------------------------------------------------------------------------------+ +| webmgmt | :ref:`built-in web management ` APIs (includes DoH) | ++--------------+---------------------------------------------------------------------------------+ +| doh_legacy | :ref:`mod-http-doh` | ++--------------+---------------------------------------------------------------------------------+ + +Each network address and port combination can be configured to expose +one kind of endpoint. This is done using the same mechanisms as +network configuration for plain DNS and DNS-over-TLS, +see chapter :ref:`network-configuration` for more details. + +.. warning:: Management endpoint (``webmgmt``) must not be directly exposed + to untrusted parties. Use `reverse-proxy`_ like Apache_ + or Nginx_ if you need to authenticate API clients + for the management API. + +By default all endpoints share the same configuration for TLS certificates etc. +This can be changed using ``http.config()`` configuration call explained below. + +.. _mod-http-example: + +Example configuration +--------------------- + +This section shows how to configure HTTP module itself. For information how +to configure HTTP server's IP addresses and ports please see chapter +:ref:`network-configuration`. + +.. code-block:: lua + + -- load HTTP module with defaults (self-signed TLS cert) + modules.load('http') + -- optionally load geoIP database for server map + http.config({ + geoip = 'GeoLite2-City.mmdb', + -- e.g. https://dev.maxmind.com/geoip/geoip2/geolite2/ + -- and install mmdblua library + }) + +Now you can reach the web services and APIs, done! + +.. code-block:: bash + + $ curl -k https://localhost:8453 + $ curl -k https://localhost:8453/stats + +.. _mod-http-tls: + +HTTPS (TLS for HTTP) +-------------------- + +By default, the web interface starts HTTPS/2 on specified port using an ephemeral +TLS certificate that is valid for 90 days and is automatically renewed. It is of +course self-signed. Why not use something like +`Let's Encrypt `_? + +.. warning:: + + If you use package ``luaossl < 20181207``, intermediate certificate is not sent to clients, + which may cause problems with validating the connection in some cases. + +You can disable unencrypted HTTP and enforce HTTPS by passing +``tls = true`` option for all HTTP endpoints: + +.. code-block:: lua + + http.config({ + tls = true, + }) + +It is also possible to provide different configuration for each +kind of endpoint, e.g. to enforce TLS and use custom certificate only for DoH: + +.. code-block:: lua + + http.config({ + tls = true, + cert = '/etc/knot-resolver/mycert.crt', + key = '/etc/knot-resolver/mykey.key', + }, 'doh_legacy') + +The format of both certificate and key is expected to be PEM, e.g. equivalent to +the outputs of following: + +.. code-block:: bash + + openssl ecparam -genkey -name prime256v1 -out mykey.key + openssl req -new -key mykey.key -out csr.pem + openssl req -x509 -days 90 -key mykey.key -in csr.pem -out mycert.crt + +It is also possible to disable HTTPS altogether by passing ``tls = false`` option. +Plain HTTP gets handy if you want to use `reverse-proxy`_ like Apache_ or Nginx_ +for authentication to API etc. +(Unencrypted HTTP could be fine for localhost tests as, for example, +Safari doesn't allow WebSockets over HTTPS with a self-signed certificate. +Major drawback is that current browsers won't do HTTP/2 over insecure connection.) + +.. warning:: + + If you use multiple Knot Resolver instances with these automatically maintained ephemeral certificates, + they currently won't be shared. + It's assumed that you don't want a self-signed certificate for serious deployments anyway. + +.. _mod-http-doh: + +Legacy DNS-over-HTTPS (DoH) +--------------------------- + +.. warning:: The legacy DoH implementation using ``http`` module (``kind='doh_legacy'``) + is deprecated. It has known performance and stability issues that won't be fixed. + Use new :ref:`dns-over-https` implementation instead. + +This was an experimental implementation of :rfc:`8484`. It can be configured using +``doh_legacy`` kind in :func:`net.listen`. Its configuration (such as certificates) +takes place in ``http.config()``. + +Queries were served on ``/doh`` and ``/dns-query`` endpoints. + +.. _mod-http-built-in-services: + +Built-in services +----------------- + +The HTTP module has several built-in services to use. + +.. csv-table:: + :header: "Endpoint", "Service", "Description" + + "``/stats``", "Statistics/metrics", "Exported :ref:`metrics ` from :ref:`mod-stats` in JSON format." + "``/metrics``", "Prometheus metrics", "Exported metrics for Prometheus_." + "``/trace/:name/:type``", "Tracking", ":ref:`Trace resolution ` of a DNS query and return its debug-level logs." + "``/doh``", "Legacy DNS-over-HTTPS", ":rfc:`8484` endpoint, see :ref:`mod-http-doh`." + "``/dns-query``", "Legacy DNS-over-HTTPS", ":rfc:`8484` endpoint, see :ref:`mod-http-doh`." + +Dependencies +------------ + +* `lua-http `_ (>= 0.3) available in LuaRocks + + If you're installing via Homebrew on OS X, you need OpenSSL too. + + .. code-block:: bash + + $ brew update + $ brew install openssl + $ brew link openssl --force # Override system OpenSSL + + Some other systems can install from LuaRocks directly: + + .. code-block:: bash + + $ luarocks --lua-version 5.1 install http + +* (*optional*) `mmdblua `_ available in LuaRocks + + .. code-block:: bash + + $ luarocks --lua-version 5.1 install --server=https://luarocks.org/dev mmdblua + $ curl -O https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz + $ gzip -d GeoLite2-City.mmdb.gz + +.. _Prometheus: https://prometheus.io +.. _reverse-proxy: https://en.wikipedia.org/wiki/Reverse_proxy +.. _Apache: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html +.. _Nginx: https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ diff --git a/modules/http/custom_services.rst b/modules/http/custom_services.rst new file mode 100644 index 0000000..09ba5ab --- /dev/null +++ b/modules/http/custom_services.rst @@ -0,0 +1,145 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-http-custom-endpoint: + +Custom HTTP services +==================== + +This chapter describes how to create custom HTTP services inside Knot Resolver. +Please read HTTP module basics in chapter :ref:`mod-http` before continuing. + +Each network address+protocol+port combination configured using :func:`net.listen` +is associated with *kind* of endpoint, e.g. ``doh_legacy`` or ``webmgmt``. + +Each of these *kind* names is associated with table of HTTP endpoints, +and the default table can be replaced using ``http.config()`` configuration call +which allows your to provide your own HTTP endpoints. + +Items in the table of HTTP endpoints are small tables describing a triplet +- ``{mime, on_serve, on_websocket}``. +In order to register a new service in ``webmgmt`` *kind* of HTTP endpoint +add the new endpoint description to respective table: + +.. code-block:: lua + + -- custom function to handle HTTP /health requests + local on_health = {'application/json', + function (h, stream) + -- API call, return a JSON table + return {state = 'up', uptime = 0} + end, + function (h, ws) + -- Stream current status every second + local ok = true + while ok do + local push = tojson('up') + ok = ws:send(tojson({'up'})) + require('cqueues').sleep(1) + end + -- Finalize the WebSocket + ws:close() + end} + + modules.load('http') + -- copy all existing webmgmt endpoints + my_mgmt_endpoints = http.configs._builtin.webmgmt.endpoints + -- add custom endpoint to the copy + my_mgmt_endpoints['/health'] = on_health + -- use custom HTTP configuration for webmgmt + http.config({ + endpoints = my_mgmt_endpoints + }, 'webmgmt') + +Then you can query the API endpoint, or tail the WebSocket using curl. + +.. code-block:: bash + + $ curl -k https://localhost:8453/health + {"state":"up","uptime":0} + $ curl -k -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: localhost:8453/health" -H "Sec-Websocket-Key: nope" -H "Sec-Websocket-Version: 13" https://localhost:8453/health + HTTP/1.1 101 Switching Protocols + upgrade: websocket + sec-websocket-accept: eg18mwU7CDRGUF1Q+EJwPM335eM= + connection: upgrade + + ?["up"]?["up"]?["up"] + +Since the stream handlers are effectively coroutines, you are free to keep state +and yield using `cqueues library `_. + +This is especially useful for WebSockets, as you can stream content in a simple loop instead of +chains of callbacks. + +Last thing you can publish from modules are *"snippets"*. Snippets are plain pieces of HTML code +that are rendered at the end of the built-in webpage. The snippets can be extended with JS code to talk to already +exported restful APIs and subscribe to WebSockets. + +.. code-block:: lua + + http.snippets['/health'] = {'Health service', '

UP!

'} + +Custom RESTful services +----------------------- + +A RESTful service is likely to respond differently to different type of methods and requests, +there are three things that you can do in a service handler to send back results. +First is to just send whatever you want to send back, it has to respect MIME type that the service +declared in the endpoint definition. The response code would then be ``200 OK``, any non-string +responses will be packed to JSON. Alternatively, you can respond with a number corresponding to +the HTTP response code or send headers and body yourself. + +.. code-block:: lua + + -- Our upvalue + local value = 42 + + -- Expose the service + local service = {'application/json', + function (h, stream) + -- Get request method and deal with it properly + local m = h:get(':method') + local path = h:get(':path') + log('method %s path %s', m, path) + -- Return table, response code will be '200 OK' + if m == 'GET' then + return {key = path, value = value} + -- Save body, perform check and either respond with 505 or 200 OK + elseif m == 'POST' then + local data = stream:get_body_as_string() + if not tonumber(data) then + return 500, 'Not a good request' + end + value = tonumber(data) + -- Unsupported method, return 405 Method not allowed + else + return 405, 'Cannot do that' + end + end} + modules.load('http') + http.config({ + endpoints = { ['/service'] = service } + }, 'myservice') + -- do not forget to create socket of new kind using + -- net.listen(..., { kind = 'myservice' }) + -- or configure systemd socket kresd-myservice.socket + +In some cases you might need to send back your own headers instead of default provided by HTTP handler, +you can do this, but then you have to return ``false`` to notify handler that it shouldn't try to generate +a response. + +.. code-block:: lua + + local headers = require('http.headers') + function (h, stream) + -- Send back headers + local hsend = headers.new() + hsend:append(':status', '200') + hsend:append('content-type', 'binary/octet-stream') + assert(stream:write_headers(hsend, false)) + -- Send back data + local data = 'binary-data' + assert(stream:write_chunk(data, true)) + -- Disable default handler action + return false + end + diff --git a/modules/http/debug_opensslkeylog.c b/modules/http/debug_opensslkeylog.c new file mode 100644 index 0000000..6709eb7 --- /dev/null +++ b/modules/http/debug_opensslkeylog.c @@ -0,0 +1,369 @@ +/* + * Dumps master keys for OpenSSL clients to file. The format is documented at + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format + * Supports TLS 1.3 when used with OpenSSL 1.1.1. + * + * Copyright (C) 2014 Peter Wu + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Usage: + * cc sslkeylog.c -shared -o libsslkeylog.so -fPIC -ldl + * SSLKEYLOGFILE=premaster.txt LD_PRELOAD=./libsslkeylog.so openssl ... + */ + +/* + * A single libsslkeylog.so supports multiple OpenSSL runtime versions. If you + * would like to build this library without OpenSSL development headers and do + * not require support for older OpenSSL versions, then disable it by defining + * the NO_OPENSSL_102_SUPPORT or NO_OPENSSL_110_SUPPORT macros. + */ +/* Define to drop OpenSSL <= 1.0.2 support and require OpenSSL >= 1.1.0. */ +//#define NO_OPENSSL_102_SUPPORT +/* Define to drop OpenSSL <= 1.1.0 support and require OpenSSL >= 1.1.1. */ +//#define NO_OPENSSL_110_SUPPORT + +/* No OpenSSL 1.1.0 support implies no OpenSSL 1.0.2 support. */ +#ifdef NO_OPENSSL_110_SUPPORT +# define NO_OPENSSL_102_SUPPORT +#endif + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE /* for RTLD_NEXT */ +#endif + +#include +#ifndef NO_OPENSSL_102_SUPPORT +# include +#endif /* ! NO_OPENSSL_102_SUPPORT */ +#include +#include +#include +#include +#include + +#ifndef OPENSSL_SONAME +/* fallback library if OpenSSL is not already loaded. Other values to try: + * libssl.so.0.9.8 libssl.so.1.0.0 libssl.so.1.1 */ +# define OPENSSL_SONAME "libssl.so" +#endif + +/* When building for OpenSSL 1.1.0 or newer, no headers are required. */ +#ifdef NO_OPENSSL_102_SUPPORT +typedef struct ssl_st SSL; +typedef struct ssl_ctx_st SSL_CTX; +/* Extra definitions for OpenSSL 1.1.0 support when headers are unavailable. */ +# ifndef NO_OPENSSL_110_SUPPORT +typedef struct ssl_session_st SSL_SESSION; +# define SSL3_RANDOM_SIZE 32 +# define SSL_MAX_MASTER_KEY_LENGTH 48 +# define OPENSSL_VERSION_NUMBER 0x10100000L +# endif /* ! NO_OPENSSL_110_SUPPORT */ +#endif /* ! NO_OPENSSL_102_SUPPORT */ + +static int keylog_file_fd = -1; + +/* Legacy routines for dumping TLS <= 1.2 secrets on older OpenSSL versions. */ +#ifndef NO_OPENSSL_110_SUPPORT +#define PREFIX "CLIENT_RANDOM " +#define PREFIX_LEN (sizeof(PREFIX) - 1) + +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wunused-result" + +static inline void put_hex(char *buffer, int pos, char c) +{ + unsigned char c1 = ((unsigned char) c) >> 4; + unsigned char c2 = c & 0xF; + buffer[pos] = c1 < 10 ? '0' + c1 : 'A' + c1 - 10; + buffer[pos+1] = c2 < 10 ? '0' + c2 : 'A' + c2 - 10; +} + +static void dump_to_fd(int fd, unsigned char *client_random, + unsigned char *master_key, int master_key_length) +{ + int pos, i; + char line[PREFIX_LEN + 2 * SSL3_RANDOM_SIZE + 1 + + 2 * SSL_MAX_MASTER_KEY_LENGTH + 1]; + + memcpy(line, PREFIX, PREFIX_LEN); + pos = PREFIX_LEN; + /* Client Random for SSLv3/TLS */ + for (i = 0; i < SSL3_RANDOM_SIZE; i++) { + put_hex(line, pos, client_random[i]); + pos += 2; + } + line[pos++] = ' '; + /* Master Secret (size is at most SSL_MAX_MASTER_KEY_LENGTH) */ + for (i = 0; i < master_key_length; i++) { + put_hex(line, pos, master_key[i]); + pos += 2; + } + line[pos++] = '\n'; + /* Write at once rather than using buffered I/O. Perhaps there is concurrent + * write access so do not write hex values one by one. */ + write(fd, line, pos); +} +#endif /* ! NO_OPENSSL_110_SUPPORT */ + +static void init_keylog_file(void) +{ + if (keylog_file_fd >= 0) + return; + + const char *filename = getenv("OPENSSLKEYLOGFILE"); + if (filename) { + /* ctime output is max 26 bytes, POSIX 1003.1-2017 */ + keylog_file_fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0600); + if (keylog_file_fd >= 0) { + time_t timenow = time(NULL); + char txtnow[30] = { '#', ' ', 0 }; + ctime_r(&timenow, txtnow + 2); + /* file is opened successfully and there is no data (pos == 0) */ + write(keylog_file_fd, txtnow, strlen(txtnow)); + } + } +} + +static inline void *try_lookup_symbol(const char *sym, int optional) +{ + void *func = dlsym(RTLD_NEXT, sym); + if (!func && optional && dlsym(RTLD_NEXT, "SSL_new")) { + /* Symbol not found, but an old OpenSSL version was actually loaded. */ + return NULL; + } + /* Symbol not found, OpenSSL is not loaded (linked) so try to load it + * manually. This is error-prone as it depends on a fixed library name. + * Perhaps it should be an env name? */ + if (!func) { + void *handle = dlopen(OPENSSL_SONAME, RTLD_LAZY); + if (!handle) { + fprintf(stderr, "Lookup error for %s: %s\n", sym, dlerror()); + abort(); + } + func = dlsym(handle, sym); + if (!func && !optional) { + fprintf(stderr, "Cannot lookup %s\n", sym); + abort(); + } + dlclose(handle); + } + return func; +} + +static inline void *lookup_symbol(const char *sym) +{ + return try_lookup_symbol(sym, 0); +} + +#ifndef NO_OPENSSL_110_SUPPORT +typedef struct ssl_tap_state { + int master_key_length; + unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; + +} ssl_tap_state_t; + +static inline SSL_SESSION *ssl_get_session(const SSL *ssl) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + static SSL_SESSION *(*func)(); + if (!func) { + func = lookup_symbol("SSL_get_session"); + } + return func(ssl); +#else + return ssl->session; +#endif +} + +static void copy_master_secret(const SSL_SESSION *session, + unsigned char *master_key_out, int *keylen_out) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + static size_t (*func)(); + if (!func) { + func = lookup_symbol("SSL_SESSION_get_master_key"); + } + *keylen_out = func(session, master_key_out, SSL_MAX_MASTER_KEY_LENGTH); +#else + if (session->master_key_length > 0) { + *keylen_out = session->master_key_length; + memcpy(master_key_out, session->master_key, + session->master_key_length); + } +#endif +} + +static void copy_client_random(const SSL *ssl, unsigned char *client_random) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + static size_t (*func)(); + if (!func) { + func = lookup_symbol("SSL_get_client_random"); + } + /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that + * we have a valid SSL context if we have a non-NULL session. */ + func(ssl, client_random, SSL3_RANDOM_SIZE); +#else + if (ssl->s3) { + memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE); + } +#endif +} + +/* non-NULL if the new OpenSSL 1.1.1 keylog API is supported. */ +static int supports_keylog_api(void) +{ + static int supported = -1; + if (supported == -1) { + supported = try_lookup_symbol("SSL_CTX_set_keylog_callback", 1) != NULL; + } + return supported; +} + +/* Copies SSL state for later comparison in tap_ssl_key. */ +static void ssl_tap_state_init(ssl_tap_state_t *state, const SSL *ssl) +{ + if (supports_keylog_api()) { + /* Favor using the callbacks API to extract secrets. */ + return; + } + + const SSL_SESSION *session = ssl_get_session(ssl); + + memset(state, 0, sizeof(ssl_tap_state_t)); + if (session) { + copy_master_secret(session, state->master_key, &state->master_key_length); + } +} + +#define SSL_TAP_STATE(state, ssl) \ + ssl_tap_state_t state; \ + ssl_tap_state_init(&state, ssl) + +static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) +{ + if (supports_keylog_api()) { + /* Favor using the callbacks API to extract secrets. */ + return; + } + + const SSL_SESSION *session = ssl_get_session(ssl); + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; + int master_key_length = 0; + + if (session) { + copy_master_secret(session, master_key, &master_key_length); + /* Assume we have a client random if the master key is set. */ + if (master_key_length > 0) { + copy_client_random(ssl, client_random); + } + } + + /* Write the logfile when the master key is available for SSLv3/TLSv1. */ + if (master_key_length > 0) { + /* Skip writing keys if it did not change. */ + if (state->master_key_length == master_key_length && + memcmp(state->master_key, master_key, master_key_length) == 0) { + return; + } + + init_keylog_file(); + if (keylog_file_fd >= 0) { + dump_to_fd(keylog_file_fd, client_random, master_key, + master_key_length); + } + } +} + +int SSL_connect(SSL *ssl) +{ + static int (*func)(); + if (!func) { + func = lookup_symbol(__func__); + } + SSL_TAP_STATE(state, ssl); + int ret = func(ssl); + tap_ssl_key(ssl, &state); + return ret; +} + +int SSL_do_handshake(SSL *ssl) +{ + static int (*func)(); + if (!func) { + func = lookup_symbol(__func__); + } + SSL_TAP_STATE(state, ssl); + int ret = func(ssl); + tap_ssl_key(ssl, &state); + return ret; +} + +int SSL_accept(SSL *ssl) +{ + static int (*func)(); + if (!func) { + func = lookup_symbol(__func__); + } + SSL_TAP_STATE(state, ssl); + int ret = func(ssl); + tap_ssl_key(ssl, &state); + return ret; +} + +int SSL_read(SSL *ssl, void *buf, int num) +{ + static int (*func)(); + if (!func) { + func = lookup_symbol(__func__); + } + SSL_TAP_STATE(state, ssl); + int ret = func(ssl, buf, num); + tap_ssl_key(ssl, &state); + return ret; +} + +int SSL_write(SSL *ssl, const void *buf, int num) +{ + static int (*func)(); + if (!func) { + func = lookup_symbol(__func__); + } + SSL_TAP_STATE(state, ssl); + int ret = func(ssl, buf, num); + tap_ssl_key(ssl, &state); + return ret; +} +#endif /* ! NO_OPENSSL_110_SUPPORT */ + +/* Key extraction via the new OpenSSL 1.1.1 API. */ +static void keylog_callback(const SSL *ssl, const char *line) +{ + init_keylog_file(); + if (keylog_file_fd >= 0) { + write(keylog_file_fd, line, strlen(line)); + write(keylog_file_fd, "\n", 1); + } +} + +SSL *SSL_new(SSL_CTX *ctx) +{ + static SSL *(*func)(); + static void (*set_keylog_cb)(); + if (!func) { + func = lookup_symbol(__func__); +#ifdef NO_OPENSSL_110_SUPPORT + /* The new API MUST be available since OpenSSL 1.1.1. */ + set_keylog_cb = lookup_symbol("SSL_CTX_set_keylog_callback"); +#else /* ! NO_OPENSSL_110_SUPPORT */ + /* May be NULL if used with an older OpenSSL runtime library. */ + set_keylog_cb = try_lookup_symbol("SSL_CTX_set_keylog_callback", 1); +#endif /* ! NO_OPENSSL_110_SUPPORT */ + } + if (set_keylog_cb) { + /* Override any previous key log callback. */ + set_keylog_cb(ctx, keylog_callback); + } + return func(ctx); +} diff --git a/modules/http/http.lua.in b/modules/http/http.lua.in new file mode 100644 index 0000000..b6cf16a --- /dev/null +++ b/modules/http/http.lua.in @@ -0,0 +1,418 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later + +-- Load dependent modules +if not stats then modules.load('stats') end +if not bogus_log then modules.load('bogus_log') end + +local ffi = require('ffi') +local cqueues = require('cqueues') +cqueues.socket = require('cqueues.socket') +assert(cqueues.VERSION >= 20150112) -- fdopen changed semantics + +-- This is a module that does the heavy lifting to provide an HTTP/2 enabled +-- server that supports TLS by default and provides endpoint for other modules +-- in order to enable them to export restful APIs and websocket streams. +-- One example is statistics module that can stream live metrics on the website, +-- or publish metrics on request for Prometheus scraper. +local http_server = require('http.server') +local http_headers = require('http.headers') +local http_websocket = require('http.websocket') +local http_util = require "http.util" +local has_mmdb, mmdb = pcall(require, 'mmdb') + +-- A sub-module for certificate management. +local tls_cert = require('kres_modules.http_tls_cert') + +-- Module declaration +local M = { + servers = {}, + configs = { _builtin = {} } -- configuration templates +} + +-- inherited by all configurations +M.configs._builtin._all = { + cq = worker.bg_worker.cq, + cert = 'self.crt', + key = 'self.key', + ephemeral = true, + client_timeout = 5 +} +-- log errors but do not throw +M.configs._builtin._all.onerror = function(myserver, context, op, err, errno) -- luacheck: ignore 212 + local msg = op .. ' on ' .. tostring(context) .. ' failed' + if err then + msg = msg .. ': ' .. tostring(err) + end + log_info(ffi.C.LOG_GRP_HTTP, msg) +end + +-- M.config() without explicit "kind" modifies this +M.configs._all = {} + +-- DoH +M.configs._builtin.doh_legacy = {} + +-- management endpoint +M.configs._builtin.webmgmt = {} + +-- Map extensions to MIME type +local mime_types = { + js = 'application/javascript', + css = 'text/css', + tpl = 'text/html', + ico = 'image/x-icon' +} + +-- Preload static contents, nothing on runtime will touch the disk +local function pgload(relpath, modname) + if not modname then modname = 'http' end + local fp, err = io.open(string.format( + '@modules_dir@/%s/%s', modname, relpath), 'r') + if not fp then + fp, err = io.open(string.format( + '@modules_dir@/%s/static/%s', modname, relpath), 'r') + end + if not fp then error(err) end + local data = fp:read('*all') + fp:close() + -- Guess content type + local ext = relpath:match('[^\\.]+$') + return {mime_types[ext] or 'text', data, nil, 86400} +end +M.page = pgload + +-- Preloaded static assets +local pages = { + 'favicon.ico', + 'kresd.js', + 'kresd.css', + 'jquery.js', + 'd3.js', + 'topojson.js', + 'datamaps.world.min.js', + 'dygraph.min.js', + 'selectize.min.js', + 'selectize.bootstrap3.css', + 'bootstrap.min.js', + 'bootstrap.min.css', + 'bootstrap-theme.min.css', + 'glyphicons-halflings-regular.woff2', +} + +-- Serve preloaded root page +local function serve_root() + local data = pgload('main.tpl')[2] + data = data + :gsub('{{ title }}', M.title or ('kresd @ ' .. hostname())) + :gsub('{{ host }}', hostname()) + return function (_, _) + -- Render snippets + local rsnippets = {} + for _,v in pairs(M.snippets) do + local sid = string.lower(string.gsub(v[1], ' ', '-')) + table.insert(rsnippets, string.format('

%s

\n%s
', sid, v[1], v[2])) + end + -- Return index page + return data + :gsub('{{ snippets }}', table.concat(rsnippets, '\n')) + end +end + +-- Export HTTP service endpoints +M.configs._builtin.doh_legacy.endpoints = {} +M.configs._builtin.webmgmt.endpoints = {} +local mgmt_endpoints = M.configs._builtin.webmgmt.endpoints + +mgmt_endpoints['/'] = {'text/html', serve_root()} + +-- Export static pages +for _, pg in ipairs(pages) do + mgmt_endpoints['/'..pg] = pgload(pg) +end + +-- Export built-in prometheus interface +local prometheus = require('kres_modules.prometheus') +for k, v in pairs(prometheus.endpoints) do + mgmt_endpoints[k] = v +end +M.prometheus = prometheus + +-- Export built-in trace interface +local http_trace = require('kres_modules.http_trace') +for k, v in pairs(http_trace.endpoints) do + mgmt_endpoints[k] = v +end +M.trace = http_trace + +M.configs._builtin.doh_legacy.endpoints = {} +local http_doh = require('kres_modules.http_doh') +for k, v in pairs(http_doh.endpoints) do + mgmt_endpoints[k] = v + M.configs._builtin.doh_legacy.endpoints[k] = v +end +M.doh = http_doh + +-- Export HTTP service page snippets +M.snippets = {} + +-- Serve known requests, for methods other than GET +-- the endpoint must be a closure and not a preloaded string +local function serve(endpoints, h, stream) + local hsend = http_headers.new() + local path = h:get(':path') + local entry = endpoints[path] + if not entry then -- Accept top-level path match + entry = endpoints[path:match '^/[^/?]*'] + end + -- Unpack MIME and data + local data, mime, ttl, any_origin, err + if entry then + mime = entry[1] + data = entry[2] + ttl = entry[4] + any_origin = entry[5] + end + -- Get string data out of service endpoint + if type(data) == 'function' then + local set_mime, set_ttl + data, err, set_mime, set_ttl = data(h, stream) + -- Override default endpoint mime/TTL + if set_mime then mime = set_mime end + if set_ttl then ttl = set_ttl end + -- Handler doesn't provide any data + if data == false then return end + if type(data) == 'number' then return tostring(data), err end + -- Methods other than GET require handler to be closure + elseif h:get(':method') ~= 'GET' then + return '501', '' + end + if type(data) == 'table' then data = tojson(data) end + if not mime or type(data) ~= 'string' then + return '404', '' + else + -- Serve content type appropriately + hsend:append(':status', '200') + hsend:append('content-type', mime) + hsend:append('content-length', tostring(#data)) + if ttl then + hsend:append('cache-control', string.format('max-age=%d', ttl)) + end + if any_origin then + hsend:append('access-control-allow-origin', '*') + end + assert(stream:write_headers(hsend, false)) + assert(stream:write_chunk(data, true)) + end +end + +-- Web server service closure +local function route(endpoints) + assert(type(endpoints) == 'table', 'endpoints are not a table, is it a botched template?') + return function (_, stream) + -- HTTP/2: We're only permitted to send in open/half-closed (remote) + local connection = stream.connection + if connection.version >= 2 then + if stream.state ~= 'open' and stream.state ~= 'half closed (remote)' then + return + end + end + -- Start reading headers + local h = assert(stream:get_headers()) + local m = h:get(':method') + local path = h:get(':path') + + -- Upgrade connection to WebSocket + local ws = http_websocket.new_from_stream(stream, h) + if ws then + log_info(ffi.C.LOG_GRP_HTTP, '%s %s HTTP/%s web socket open', + m, path, tostring(connection.version)) + assert(ws:accept { protocols = {'json'} }) + -- Continue streaming results to client + local ep = endpoints[path] + local cb = ep[3] + if cb then + cb(h, ws) + end + ws:close() + log_info(ffi.C.LOG_GRP_HTTP, '%s %s HTTP/%s web socket closed', + m, path, tostring(connection.version)) + return + else + local ok, err, reason = http_util.yieldable_pcall(serve, endpoints, h, stream) + if not ok or err then + err = err or '500' + log_info(ffi.C.LOG_GRP_HTTP, '%s %s HTTP/%s %s %s', + m, path, tostring(connection.version), err, reason or '') + -- Method is not supported + local hsend = http_headers.new() + hsend:append(':status', err) + if reason then + assert(stream:write_headers(hsend, false)) + assert(stream:write_chunk(reason, true)) + else + assert(stream:write_headers(hsend, true)) + end + else + log_info(ffi.C.LOG_GRP_HTTP, '%s %s HTTP/%s 200', + m, path, tostring(connection.version)) + end + + end + end +end + +-- @function Merge dictionaries, nil is like empty dict. +-- Values from right-hand side dictionaries take precedence. +local function mergeconf(...) + local merged = {} + local ntables = select('#', ...) + local tables = {...} + for i = 1, ntables do + local intable = tables[i] + if intable ~= nil then + assert(type(intable) == 'table', 'cannot merge non-tables') + for key, val in pairs(intable) do + merged[key] = val + end + end + end + return merged +end + +-- @function Listen on given socket +-- using configuration for specific "kind" of HTTP server +local function add_socket(fd, kind, addr_str) + assert(M.servers[fd] == nil, 'socket is already served by an HTTP instance') + local conf = mergeconf(M.configs._builtin._all, M.configs._builtin[kind], + M.configs._all, M.configs[kind]) + conf.socket = cqueues.socket.fdopen({ fd = fd, reuseport = true, reuseaddr = true }) + if conf.tls ~= false then -- Create a TLS context, either from files or new. + if conf.ephemeral then + if not M.ephem_state then + M.ephem_state = { servers = M.servers } + tls_cert.ephemeral_state_maintain(M.ephem_state, conf.cert, conf.key) + end + conf.ctx = M.ephem_state.ctx + else + local certs, key = tls_cert.load(conf.cert, conf.key) + conf.ctx = tls_cert.new_tls_context(certs, key) + end + assert(conf.ctx) + end + -- Compose server handler + local routes = route(conf.endpoints) + conf.onstream = routes + -- Create TLS context and start listening + local s, err = http_server.new(conf) + -- Manually call :listen() so that we are bound before calling :localname() + if s then + err = select(2, s:listen()) + end + if err then + panic('failed to listen on %s: %s', addr_str, err) + end + M.servers[fd] = {kind = kind, server = s, config = conf} +end + +-- @function Stop listening on given socket +local function remove_socket(fd) + local instance = M.servers[fd] + assert(instance, 'HTTP module is not listening on given socket') + + instance.server:close() + M.servers[fd] = nil +end + +-- @function Listen for config changes from net.listen()/net.close() +local function cb_socket(...) + local added, endpoint, addr_str = unpack({...}) + endpoint = ffi.cast('struct endpoint **', endpoint)[0] + local kind = ffi.string(endpoint.flags.kind) + local socket = endpoint.fd + if added then + return add_socket(socket, kind, addr_str) + else + return remove_socket(socket) + end +end + +-- @function Init module +function M.init() + net.register_endpoint_kind('doh_legacy', cb_socket) + net.register_endpoint_kind('webmgmt', cb_socket) +end + +-- @function Cleanup module +function M.deinit() + for fd, _ in pairs(M.servers) do + remove_socket(fd) + end + tls_cert.ephemeral_state_destroy(M.ephem_state) + net.register_endpoint_kind('doh_legacy') + net.register_endpoint_kind('webmgmt') +end + +-- @function Configure module, i.e. store new configuration template +-- kind = socket type (doh_legacy/webmgmt) +function M.config(conf, kind) + if conf == nil and kind == nil then + -- default module config, nothing to do + return + end + + kind = kind or '_all' + assert(type(kind) == 'string') + + local operation + -- builtins cannot be removed or added + if M.configs._builtin[kind] then + operation = 'modify' + conf = conf or {} + elseif M.configs[kind] then -- config on an existing user template + if conf then operation = 'modify' + else operation = 'delete' end + else -- config for not-yet-existing template + if conf then operation = 'add' + else panic('[http] endpoint kind "%s" does not exist, ' + .. 'nothing to delete', kind) end + end + + if operation == 'modify' or operation == 'add' then + assert(type(conf) == 'table', 'config { cert = "...", key = "..." }') + + if conf.cert then + conf.ephemeral = false + if not conf.key then + panic('[http] certificate provided, but missing key') + end + -- test if it can be loaded or not + tls_cert.load(conf.cert, conf.key) + end + if conf.geoip then + if has_mmdb then + M.geoip = mmdb.open(conf.geoip) + else + error('[http] mmdblua library not found, please remove GeoIP configuration') + end + end + end + + for _, instance in pairs(M.servers) do + -- modification cannot be implemented as + -- remove_socket + add_socket because remove closes the socket + if instance.kind == kind or kind == '_all' then + panic('unable to modify configuration for ' + .. 'endpoint kind "%s" because it is in ' + .. 'use, use net.close() first', kind) + end + end + + if operation == 'add' then + net.register_endpoint_kind(kind, cb_socket) + elseif operation == 'delete' then + net.register_endpoint_kind(kind) + end + M.configs[kind] = conf +end + +return M diff --git a/modules/http/http.test.lua b/modules/http/http.test.lua new file mode 100644 index 0000000..b882f10 --- /dev/null +++ b/modules/http/http.test.lua @@ -0,0 +1,128 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- check prerequisites +local has_http = pcall(require, 'kres_modules.http') and pcall(require, 'http.request') +if not has_http then + -- skipping http module test because its not installed + os.exit(77) +else + local path = worker.cwd..'/control/'..worker.pid + same(true, net.listen(path, nil, {kind = 'control'}), + 'new control sockets were created so map() can work') + + local request = require('http.request') + + modules.load('http') + local endpoints = http.configs._builtin.webmgmt.endpoints + + -- custom endpoints + endpoints['/test'] = {'text/custom', function () return 'hello' end} + + -- setup HTTP module with an additional endpoint + http.config({ + tls = false, + endpoints = endpoints, + }, 'webtest') + + local bound + for _ = 1,1000 do + bound, _err = pcall(net.listen, '127.0.0.1', math.random(20000, 29999), { kind = 'webtest'}) + if bound then + break + end + end + assert(bound, 'unable to bind a port for HTTP module (1000 attempts)') + + -- globals for this module + local _, host, port + local function start_server() + local server_fd = next(http.servers) + assert(server_fd) + local server = http.servers[server_fd].server + ok(server ~= nil, 'creates server instance') + _, host, port = server:localname() + ok(host and port, 'binds to an interface') + end + + -- helper for returning useful values to test on + local function http_get(uri) + local headers, stream = assert(request.new_from_uri(uri .. '/'):go(16)) + local body = assert(stream:get_body_as_string()) + return tonumber(headers:get(':status')), body, headers:get('content-type') + end + + -- test whether http interface responds and binds + local function test_builtin_pages() + local code, body, mime + local uri = string.format('http://%s:%d', host, port) + -- simple static page + code, body, mime = http_get(uri .. '/') + same(code, 200, 'static page return 200 OK') + ok(#body > 0, 'static page has non-empty body') + same(mime, 'text/html', 'static page has text/html content type') + -- custom endpoint + code, body, mime = http_get(uri .. '/test') + same(code, 200, 'custom page return 200 OK') + same(body, 'hello', 'custom page has non-empty body') + same(mime, 'text/custom', 'custom page has custom content type') + -- non-existent page + code = http_get(uri .. '/badpage') + same(code, 404, 'non-existent page returns 404') + -- /stats endpoint serves metrics + code, body, mime = http_get(uri .. '/stats') + same(code, 200, '/stats page return 200 OK') + ok(#body > 0, '/stats page has non-empty body') + same(mime, 'application/json', '/stats page has correct content type') + -- /metrics serves metrics + code, body, mime = http_get(uri .. '/metrics') + same(code, 200, '/metrics page return 200 OK') + ok(#body > 0, '/metrics page has non-empty body') + same(mime, 'text/plain; version=0.0.4', '/metrics page has correct content type') + -- /metrics serves frequent + code, body, mime = http_get(uri .. '/frequent') + same(code, 200, '/frequent page return 200 OK') + ok(#body > 0, '/frequent page has non-empty body') + same(mime, 'application/json', '/frequent page has correct content type') + -- /metrics serves bogus + code, body, mime = http_get(uri .. '/bogus') + same(code, 200, '/bogus page return 200 OK') + ok(#body > 0, '/bogus page has non-empty body') + same(mime, 'application/json', '/bogus page has correct content type') + -- /trace serves trace log for requests + code, body, mime = http_get(uri .. '/trace/localhost/A') + same(code, 200, '/trace page return 200 OK') + ok(#body > 0, '/trace page has non-empty body') + same(mime, 'text/plain', '/trace page has correct content type') + -- /trace checks variables + code = http_get(uri .. '/trace/localhost/BADTYPE') + same(code, 400, '/trace checks type') + code = http_get(uri .. '/trace/') + same(code, 400, '/trace requires name') + end + + -- AF_UNIX tests (very basic ATM) + local function test_unix_socket() + local s_path = os.tmpname() + os.remove(s_path) -- on POSIX .tmpname() (usually) creates a file :-/ + ok(net.listen(s_path, nil, { kind = 'webmgmt' }), 'AF_UNIX net.listen() on ' .. s_path) + -- Unfortunately we can't use standard functions for fetching http:// + local socket = require("cqueues.socket") + local sock = socket.connect({ path = s_path }) + local connection = require('http.h2_connection') + local conn = connection.new(sock, 'client') + local _, err = conn:connect() + os.remove(s_path) -- don't leave garbage around, hopefully not even on errors + same(err, nil, 'AF_UNIX connect(): ' .. (err or 'OK')) + same(conn:ping(), true, 'AF_UNIX http ping') + -- here we might do `conn:new_stream()` and some real queries + same(conn:close(), true, 'AF_UNIX close') + end + + -- plan tests + local tests = { + start_server, + test_builtin_pages, + test_unix_socket, + } + + return tests +end diff --git a/modules/http/http_doh.lua b/modules/http/http_doh.lua new file mode 100644 index 0000000..33815f7 --- /dev/null +++ b/modules/http/http_doh.lua @@ -0,0 +1,116 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local basexx = require('basexx') +local ffi = require('ffi') +local condition = require('cqueues.condition') + +-- Trace execution of DNS queries +local function serve_doh(h, stream) + local input + local method = h:get(':method') + if method == 'POST' then + input = stream:get_body_chars(1025, 2) -- read timeout = KR_CONN_RTT_MAX + elseif method == 'GET' then + local input_b64 = string.match(h:get(':path'), '^/[^?]*%?dns=([a-zA-Z0-9_-]+)$') or + string.match(h:get(':path'), '^/[^?]*%?dns=([a-zA-Z0-9_-]+)&') or + string.match(h:get(':path'), '^/[^?]*%?.*&dns=([a-zA-Z0-9_-]+)$') or + string.match(h:get(':path'), '^/[^?]*%?.*&dns=([a-zA-Z0-9_-]+)&') + if not input_b64 then + return 400, 'base64url query not found' + end + if #input_b64 > 1368 then -- base64url encode 1024 + return 414, 'query parameter in URI too long' + end + input = basexx.from_url64(input_b64) + if not input then + return 400, 'invalid base64url' + end + else + return 405, 'only HTTP POST and GET are supported' + end + + if not input or #input < 12 then + return 400, 'input too short' + elseif #input > 1024 then + return 413, 'input too long' + end + + local content_type = h:get('content-type') or 'application/dns-message' + if content_type ~= 'application/dns-message' then + return 415, 'only Content-Type: application/dns-message is supported' + end +-- RFC 8484 section-4.1 allows us to ignore Accept header +-- local accept = h:get('accept') or 'application/dns-message' +-- if accept ~= 'application/dns-message' then +-- return 406, 'only Accept: application/dns-message is supported' +-- end + + -- We get these values beforehand, because it's easier to handle errors now. + local _, peer_addr, peer_port = stream:peername() + local _, dst_addr, dst_port = stream:localname() + if not (peer_addr and peer_port and dst_addr and dst_port) then + -- The connection probably died in the meantime or something. + return 504, 'failed to determine your address' + end + + -- Output buffer + local output + local output_ttl + + -- Wait for the result of the query + -- Note: We can't do non-blocking write to stream directly from resolve callbacks + -- because they don't run inside cqueue. + local cond = condition.new() + local waiting, done = false, false + local finish_cb = function (answer, _) + output_ttl = ffi.C.packet_ttl(answer) + -- binary output + output = ffi.string(answer.wire, answer.size) + if waiting then + cond:signal() + end + done = true + end + + -- convert query to knot_pkt_t + local wire = ffi.cast("void *", input) + local pkt = ffi.gc(ffi.C.knot_pkt_new(wire, #input, nil), ffi.C.knot_pkt_free) + if not pkt then + return 500, 'internal server error' + end + + local result = ffi.C.knot_pkt_parse(pkt, 0) + if result ~= 0 then + return 400, 'unparseable DNS message' + end + + -- set source address so filters can work + local function init_cb(req) + req.qsource.addr = ffi.C.kr_straddr_socket(peer_addr, peer_port, req.pool) + req.qsource.dst_addr = ffi.C.kr_straddr_socket(dst_addr, dst_port, req.pool) + assert(req.qsource.addr ~= nil and req.qsource.dst_addr ~= nil) + req.qsource.flags.tcp = true + req.qsource.flags.tls = (stream.connection:checktls() ~= nil) + req.qsource.flags.http = true + end + + -- resolve query + worker.resolve_pkt(pkt, {}, finish_cb, init_cb) + if not done then + waiting = true + cond:wait() + end + + -- Return buffered data + if not done then + return 504, 'huh?' -- FIXME + end + return output, nil, 'application/dns-message', output_ttl +end + +-- Export endpoints +return { + endpoints = { + ['/doh'] = {'text/plain', serve_doh, nil, nil, true}, + ['/dns-query'] = {'text/plain', serve_doh, nil, nil, true}, + } +} diff --git a/modules/http/http_doh.test.lua b/modules/http/http_doh.test.lua new file mode 100644 index 0000000..f0685cb --- /dev/null +++ b/modules/http/http_doh.test.lua @@ -0,0 +1,419 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local basexx = require('basexx') +local ffi = require('ffi') + +local function gen_huge_answer(_, req) + local answer = req:ensure_answer() + ffi.C.kr_pkt_make_auth_header(answer) + + answer:rcode(kres.rcode.NOERROR) + + -- 64k answer + answer:begin(kres.section.ANSWER) + answer:put('\4test\0', 300, answer:qclass(), kres.type.URI, + '\0\0\0\0' .. string.rep('0', 65000)) + answer:put('\4test\0', 300, answer:qclass(), kres.type.URI, + '\0\0\0\0' .. 'done') + return kres.DONE +end + +local function gen_varying_ttls(_, req) + local qry = req:current() + local answer = req:ensure_answer() + ffi.C.kr_pkt_make_auth_header(answer) + + answer:rcode(kres.rcode.NOERROR) + + -- varying TTLs in ANSWER section + answer:begin(kres.section.ANSWER) + answer:put(qry.sname, 1800, answer:qclass(), kres.type.AAAA, + '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1') + answer:put(qry.sname, 900, answer:qclass(), kres.type.A, '\127\0\0\1') + answer:put(qry.sname, 20000, answer:qclass(), kres.type.NS, '\2ns\4test\0') + + -- shorter TTL than all other RRs + answer:begin(kres.section.AUTHORITY) + answer:put('\4test\0', 300, answer:qclass(), kres.type.SOA, + '\2ns\4test\0\6nobody\7invalid\0\0\0\0\1\0\0\14\16\0\0\4\176\0\9\58\128\0\0\42\48') + return kres.DONE +end + +function parse_pkt(input, desc) + local wire = ffi.cast("void *", input) + local pkt = ffi.C.knot_pkt_new(wire, #input, nil); + assert(pkt, desc .. ': failed to create new packet') + + local result = ffi.C.knot_pkt_parse(pkt, 0) + ok(result == 0, desc .. ': knot_pkt_parse works on received answer') + return pkt +end + +local function check_ok(req, desc) + local headers, stream, errno = req:go(16) + if errno then + local errmsg = stream + nok(errmsg, desc .. ': ' .. errmsg) + return + end + same(tonumber(headers:get(':status')), 200, desc .. ': status 200') + same(headers:get('content-type'), 'application/dns-message', desc .. ': content-type') + local body = assert(stream:get_body_as_string()) + local pkt = parse_pkt(body, desc) + return headers, pkt +end + +local function check_err(req, exp_status, desc) + local headers, errmsg, errno = req:go(16) + if errno then + nok(errmsg, desc .. ': ' .. errmsg) + return + end + local got_status = headers:get(':status') + same(got_status, exp_status, desc) +end + +-- check prerequisites +local has_http = pcall(require, 'kres_modules.http') and pcall(require, 'http.request') +if not has_http then + -- skipping http module test because its not installed + os.exit(77) +else + policy.add(policy.suffix(policy.DROP, policy.todnames({'servfail.test.'}))) + policy.add(policy.suffix(policy.DENY, policy.todnames({'nxdomain.test.'}))) + policy.add(policy.suffix(gen_varying_ttls, policy.todnames({'noerror.test.'}))) + + modules.load('http') + http.config({ + tls = false, + }, 'doh_legacy') + + local bound + for _ = 1,1000 do + bound, _err = pcall(net.listen, '127.0.0.1', math.random(30000, 39999), { kind = 'doh_legacy' }) + if bound then + break + end + end + assert(bound, 'unable to bind a port for HTTP module (1000 attempts)') + + local _, host, port, req_templ, uri_templ + local function start_server() + local request = require('http.request') + local server_fd = next(http.servers) + assert(server_fd) + local server = http.servers[server_fd].server + ok(server ~= nil, 'creates server instance') + _, host, port = server:localname() + ok(host and port, 'binds to an interface') + uri_templ = string.format('http://%s:%d/doh', host, port) + req_templ = assert(request.new_from_uri(uri_templ)) + req_templ.headers:upsert('content-type', 'application/dns-message') + end + + + -- test a valid DNS query using POST + local function test_post_servfail() + local desc = 'valid POST query which ends with SERVFAIL' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- servfail.test. A + 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ==')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- uncacheable + same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') + end + + local function test_post_noerror() + local desc = 'valid POST query which ends with NOERROR' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- noerror.test. A + 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_post_nxdomain() + local desc = 'valid POST query which ends with NXDOMAIN' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- nxdomain.test. A + 'viABAAABAAAAAAAACG54ZG9tYWluBHRlc3QAAAEAAQ==')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + same(headers:get('cache-control'), 'max-age=10800', desc .. ': TTL 10800') + same(pkt:rcode(), kres.rcode.NXDOMAIN, desc .. ': rcode matches') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + end + + -- RFC 8484 section 6 explicitly allows huge answers over HTTP + local function test_huge_answer() + policy.add(policy.suffix(gen_huge_answer, policy.todnames({'huge.test'}))) + local desc = 'POST query for a huge answer' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- huge.test. URI, no EDNS + 'HHwBAAABAAAAAAAABGh1Z2UEdGVzdAABAAAB')) + local _, pkt = check_ok(req, desc) + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode NOERROR') + same(pkt:tc(), false, desc .. ': no TC bit') + same(pkt:ancount(), 2, desc .. ': ANSWER contains both RRs') + end + + -- test an invalid DNS query using POST + local function test_post_short_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req:set_body(string.rep('0', 11)) -- 11 bytes < DNS msg header + check_err(req, '400', 'too short POST finishes with 400') + end + + local function test_post_long_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req:set_body(string.rep('s', 1025)) -- > DNS msg over UDP + check_err(req, '413', 'too long POST finishes with 413') + end + + local function test_post_unparseable_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req:set_body(string.rep('\0', 1024)) -- garbage + check_err(req, '400', 'unparseable DNS message finishes with 400') + end + + local function test_post_unsupp_type() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'POST') + req.headers:upsert('content-type', 'application/dns+json') + req:set_body(string.rep('\0', 12)) -- valid message + check_err(req, '415', 'unsupported request content type finishes with 415') + end + + -- test a valid DNS query using GET + local function test_get_servfail() + local desc = 'valid GET query which ends with SERVFAIL' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- servfail.test. A + .. 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- uncacheable + same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') + end + + local function test_get_noerror() + local desc = 'valid GET query which ends with NOERROR' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- noerror.test. A + .. 'vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- HTTP TTL is minimum from all RRs in the answer + same(headers:get('cache-control'), 'max-age=300', desc .. ': TTL 900') + same(pkt:rcode(), kres.rcode.NOERROR, desc .. ': rcode matches') + same(pkt:ancount(), 3, desc .. ': ANSWER is present') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + same(pkt:arcount(), 0, desc .. ': ADDITIONAL is empty') + end + + local function test_get_nxdomain() + local desc = 'valid GET query which ends with NXDOMAIN' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' -- nxdomain.test. A + .. 'viABAAABAAAAAAAACG54ZG9tYWluBHRlc3QAAAEAAQ') + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + same(headers:get('cache-control'), 'max-age=10800', desc .. ': TTL 10800') + same(pkt:rcode(), kres.rcode.NXDOMAIN, desc .. ': rcode matches') + same(pkt:nscount(), 1, desc .. ': AUTHORITY is present') + end + + local function test_get_other_params_before_dns() + local desc = 'GET query with other parameters before dns is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&another=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB') + check_ok(req, desc) + end + + local function test_get_other_params_after_dns() + local desc = 'GET query with other parameters after dns is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&other=something&another=something') + check_ok(req, desc) + end + + local function test_get_other_params() + local desc = 'GET query with other parameters than dns on both sides is valid' + local req = req_templ:clone() + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', + '/doh?other=something&dns=vMEBAAABAAAAAAAAB25vZXJyb3IEdGVzdAAAAQAB&another=something') + check_ok(req, desc) + end + + -- test an invalid DNS query using GET + local function test_get_long_input() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 1030))) + check_err(req, '414', 'too long GET finishes with 414') + end + + local function test_get_no_dns_param() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?notdns=' .. basexx.to_url64(string.rep('\0', 1024))) + check_err(req, '400', 'GET without dns parameter finishes with 400') + end + + local function test_get_unparseable() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh??dns=' .. basexx.to_url64(string.rep('\0', 1024))) + check_err(req, '400', 'unparseable GET finishes with 400') + end + + local function test_get_invalid_b64() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=thisisnotb64') + check_err(req, '400', 'GET with invalid base64 finishes with 400') + end + + local function test_get_invalid_chars() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'GET') + req.headers:upsert(':path', '/doh?dns=' .. basexx.to_url64(string.rep('\0', 200)) .. '@#$%?!') + check_err(req, '400', 'GET with invalid characters in b64 finishes with 400') + end + + local function test_unsupp_method() + local req = assert(req_templ:clone()) + req.headers:upsert(':method', 'PUT') + check_err(req, '405', 'unsupported method finishes with 405') + end + + local function test_dstaddr() + local triggered = false + local exp_dstaddr = ffi.gc(ffi.C.kr_straddr_socket(host, port, nil), ffi.C.free) + local function check_dstaddr(state, req) + triggered = true + same(ffi.C.kr_sockaddr_cmp(req.qsource.dst_addr, exp_dstaddr), 0, + 'request has correct server address') + return state + end + policy.add(policy.suffix(check_dstaddr, policy.todnames({'dstaddr.test'}))) + local desc = 'valid POST query has server address available in request' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- dstaddr.test. A + 'FnkBAAABAAAAAAAAB2RzdGFkZHIEdGVzdAAAAQAB')) + check_ok(req, desc) + ok(triggered, 'dstaddr policy was triggered') + end + + local function test_srcaddr() + modules.load('view') + assert(view) + local policy_refuse = policy.suffix(policy.REFUSE, policy.todnames({'srcaddr.test.knot-resolver.cz'})) + -- these netmasks would not work if the request did not contain IP addresses + view:addr('0.0.0.0/0', policy_refuse) + view:addr('::/0', policy_refuse) + + local desc = 'valid POST query has source address available in request' + local req = req_templ:clone() + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- srcaddr.test.knot-resolver.cz TXT + 'QNQBAAABAAAAAAAAB3NyY2FkZHIEdGVzdA1rbm90LXJlc29sdmVyAmN6AAAQAAE')) + local _, pkt = check_ok(req, desc) + same(pkt:rcode(), kres.rcode.REFUSED, desc .. ': view module caught it') + + modules.unload('view') + end + + local function test_dns_query_endpoint() + local desc = 'valid POST query which ends with SERVFAIL on /dns-query' + local request = require('http.request') + uri_templ = string.format('http://%s:%d/dns-query', host, port) + req = assert(request.new_from_uri(uri_templ)) + req.headers:upsert('content-type', 'application/dns-message') + req.headers:upsert(':method', 'POST') + req:set_body(basexx.from_base64( -- servfail.test. A + 'FZUBAAABAAAAAAAACHNlcnZmYWlsBHRlc3QAAAEAAQ==')) + local headers, pkt = check_ok(req, desc) + if not (headers and pkt) then + return + end + -- uncacheable + same(headers:get('cache-control'), 'max-age=0', desc .. ': TTL 0') + same(pkt:rcode(), kres.rcode.SERVFAIL, desc .. ': rcode matches') + end + +-- not implemented +-- local function test_post_unsupp_accept() +-- local req = assert(req_templ:clone()) +-- req.headers:upsert(':method', 'POST') +-- req.headers:upsert('accept', 'application/dns+json') +-- req:set_body(string.rep('\0', 12)) -- valid message +-- check_err(req, '406', 'unsupported Accept type finishes with 406') +-- end + + -- plan tests + local tests = { + start_server, + test_post_servfail, + test_post_noerror, + test_post_nxdomain, + test_huge_answer, + test_post_short_input, + test_post_long_input, + test_post_unparseable_input, + test_post_unsupp_type, + test_get_servfail, + test_get_noerror, + test_get_nxdomain, + test_get_other_params_before_dns, + test_get_other_params_after_dns, + test_get_other_params, + test_get_long_input, + test_get_no_dns_param, + test_get_unparseable, + test_get_invalid_b64, + test_get_invalid_chars, + test_unsupp_method, + test_dstaddr, + test_srcaddr, + test_dns_query_endpoint, + } + + return tests +end diff --git a/modules/http/http_tls_cert.lua b/modules/http/http_tls_cert.lua new file mode 100644 index 0000000..7e557c4 --- /dev/null +++ b/modules/http/http_tls_cert.lua @@ -0,0 +1,186 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +--[[ + Conventions: + - key = private+public key-pair in openssl.pkey format + - certs = lua list of certificates (at least one), each in openssl.x509 format, + ordered from leaf to almost-root + - panic('...') is used on bad problems instead of returning nils or such +--]] +local tls_cert = {} + +local ffi = require('ffi') +local x509, pkey = require('openssl.x509'), require('openssl.pkey') + +-- @function Create self-signed certificate; return certs, key +local function new_ephemeral(host) + -- Import luaossl directly + local name = require('openssl.x509.name') + local altname = require('openssl.x509.altname') + local openssl_bignum = require('openssl.bignum') + local openssl_rand = require('openssl.rand') + -- Create self-signed certificate + host = host or hostname() + local crt = x509.new() + local now = os.time() + crt:setVersion(3) + -- serial needs to be unique or browsers will show uninformative error messages + crt:setSerial(openssl_bignum.fromBinary(openssl_rand.bytes(16))) + -- use the host we're listening on as canonical name + local dn = name.new() + dn:add("CN", host) + crt:setSubject(dn) + crt:setIssuer(dn) -- should match subject for a self-signed + local alt = altname.new() + alt:add("DNS", host) + crt:setSubjectAlt(alt) + -- Valid for 90 days + crt:setLifetime(now, now + 90*60*60*24) + -- Can't be used as a CA + crt:setBasicConstraints{CA=false} + crt:setBasicConstraintsCritical(true) + -- Create and set key (default: EC/P-256 as a most "interoperable") + local key = pkey.new {type = 'EC', curve = 'prime256v1'} + crt:setPublicKey(key) + crt:sign(key) + return { crt }, key +end + +-- @function Write certs and key to files +local function write_cert_files(certs, key, certfile, keyfile) + -- Write certs + local f = assert(io.open(certfile, 'w'), string.format('cannot open "%s" for writing', certfile)) + for _, cert in ipairs(certs) do + f:write(tostring(cert)) + end + f:close() + -- Write key as a pair + f = assert(io.open(keyfile, 'w'), string.format('cannot open "%s" for writing', keyfile)) + local pub, priv = key:toPEM('public', 'private') + assert(f:write(pub .. priv)) + f:close() +end + +-- @function Start maintenance of a self-signed TLS context (at ephem_state.ctx). +-- Keep updating the ephem_state.servers table. Stop updating by calling _destroy(). +-- TODO: each process maintains its own ephemeral cert ATM, and the files aren't ever read from. +function tls_cert.ephemeral_state_maintain(ephem_state, certfile, keyfile) + local certs, key = new_ephemeral() + write_cert_files(certs, key, certfile, keyfile) + ephem_state.ctx = tls_cert.new_tls_context(certs, key) + -- Each server needs to have its ctx updated. + for _, s in pairs(ephem_state.servers) do + s.server.ctx = ephem_state.ctx + s.config.ctx = ephem_state.ctx -- not required, but let's keep it synchronized + end + log_info(ffi.C.LOG_GRP_HTTP, 'created new ephemeral TLS certificate') + local _, expiry_stamp = certs[1]:getLifetime() + local wait_msec = 1000 * math.max(1, expiry_stamp - os.time() - 3 * 24 * 3600) + if not ephem_state.timer_id then + ephem_state.timer_id = event.after(wait_msec, function () + tls_cert.ephemeral_state_maintain(ephem_state, certfile, keyfile) + end) + else + event.reschedule(ephem_state.timer_id, wait_msec) + end +end +function tls_cert.ephemeral_state_destroy(ephem_state) + if ephem_state and ephem_state.timer_id then + event.cancel(ephem_state.timer_id) + end +end + +-- @function Read a certificate chain and a key from files; return certs, key +function tls_cert.load(certfile, keyfile) + -- get key + local f, err = io.open(keyfile, 'r') + if not f then + panic('[http] unable to open TLS key file: %s', err) + end + local key = pkey.new(f:read('*all')) + f:close() + if not key then + panic('[http] unable to parse TLS key file %s', keyfile) + end + + -- get certs list + local certs = {} + local f, err = io.open(certfile, 'r') + if not f then + panic('[http] unable to read TLS certificate file: %s', err) + end + while true do + -- Get the next "block" = single certificate as PEM string. + local block = nil + local line + repeat + line = f:read() + if not line then break end + if block then + block = block .. '\n' .. line + else + block = line + end + -- separator: "posteb" in https://tools.ietf.org/html/rfc7468#section-3 + until string.sub(line, 1, 9) == '-----END ' + -- Empty block means clean EOF. + if not block then break end + if not line then + panic('[http] unable to parse TLS certificate file %s, certificate number %d', certfile, 1 + #certs) + end + + -- Parse the cert and append to the list. + local cert = x509.new(block, 'PEM') + if not cert then + panic('[http] unable to parse TLS certificate file %s, certificate number %d', certfile, 1 + #certs) + end + table.insert(certs, cert) + end + f:close() + + return certs, key +end + + +-- @function Prefer HTTP/2 or HTTP/1.1 +local function alpnselect(_, protos) + for _, proto in ipairs(protos) do + if proto == 'h2' or proto == 'http/1.1' then + return proto + end + end + return nil +end + +local warned_old_luaossl = false + +-- @function Return a new TLS context for a server. +function tls_cert.new_tls_context(certs, key) + local ctx = require('http.tls').new_server_context() + if ctx.setAlpnSelect then + ctx:setAlpnSelect(alpnselect) + end + assert(ctx:setPrivateKey(key)) + assert(ctx:setCertificate(certs[1])) + + -- Set up certificate chain to be sent, if required and possible. + if #certs == 1 then return ctx end + if ctx.setCertificateChain then + local chain = require('openssl.x509.chain').new() + assert(chain) + for i = 2, #certs do + chain:add(certs[i]) + assert(chain) + end + assert(ctx:setCertificateChain(chain)) + elseif not warned_old_luaossl then + -- old luaossl version -> only final cert sent to clients + log_warn(ffi.C.LOG_GRP_HTTP, + 'need luaossl >= 20181207 to support sending intermediary certificate to clients') + warned_old_luaossl = true + end + return ctx +end + + +return tls_cert + diff --git a/modules/http/http_trace.lua b/modules/http/http_trace.lua new file mode 100644 index 0000000..123cd52 --- /dev/null +++ b/modules/http/http_trace.lua @@ -0,0 +1,77 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +local ffi = require('ffi') +local condition = require('cqueues.condition') + +-- Trace execution of DNS queries +local function serve_trace(h, _) + local path = h:get(':path') + local qname, qtype_str = path:match('/trace/([^/]+)/?([^/]*)') + if not qname then + return 400, 'expected /trace//' + end + + -- Parse query type (or default to A) + if not qtype_str or #qtype_str == 0 then + qtype_str = 'A' + end + + local qtype = kres.type[qtype_str] + if not qtype then + return 400, string.format('unexpected query type: %s', qtype_str) + end + + -- Create logging handler callback + local buffer = {} + local buffer_log_cb = ffi.cast('trace_log_f', function (_, msg) + jit.off(true, true) -- JIT for (C -> lua)^2 nesting isn't allowed + table.insert(buffer, ffi.string(msg)) + end) + + -- Wait for the result of the query + -- Note: We can't do non-blocking write to stream directly from resolve callbacks + -- because they don't run inside cqueue. + local cond = condition.new() + local waiting, done = false, false + local finish_cb = ffi.cast('trace_callback_f', function (req) + jit.off(true, true) -- JIT for (C -> lua)^2 nesting isn't allowed + table.insert(buffer, req:selected_tostring()) + if waiting then + cond:signal() + end + done = true + end) + + -- Resolve query and buffer logs into table + resolve { + name = qname, + type = qtype, + options = {'TRACE'}, + init = function (req) + req:trace_chain_callbacks(buffer_log_cb, finish_cb) + end + } + + -- Wait for asynchronous query and free callbacks + if not done then + waiting = true + cond:wait() + end + + buffer_log_cb:free() + finish_cb:free() + + -- Build the result + local result = table.concat(buffer, '') + -- Return buffered data + if not done then + return 504, result + end + return result +end + +-- Export endpoints +return { + endpoints = { + ['/trace'] = {'text/plain', serve_trace}, + } +} diff --git a/modules/http/meson.build b/modules/http/meson.build new file mode 100644 index 0000000..6705143 --- /dev/null +++ b/modules/http/meson.build @@ -0,0 +1,61 @@ +# LUA module: http +# SPDX-License-Identifier: GPL-3.0-or-later + +lua_http_config = configuration_data() +lua_http_config.set('modules_dir', modules_dir) + +lua_http = configure_file( + input: 'http.lua.in', + output: 'http.lua', + configuration: lua_http_config, +) + +lua_mod_src += [ + lua_http, + files('http_doh.lua'), + files('http_trace.lua'), + files('http_tls_cert.lua'), + files('prometheus.lua'), +] + +config_tests += [ + ['http', files('http.test.lua')], + ['http.doh', files('http_doh.test.lua')], + ['http.tls', files('test_tls/tls.test.lua')], +] + +# install static files +install_subdir( + 'static', + strip_directory: true, + exclude_files: [ + 'bootstrap.min.css.spdx', + 'bootstrap.min.js.spdx', + 'bootstrap-theme.min.css.spdx', + 'datamaps.world.min.spdx', + 'dygraph.min.js.spdx', + 'd3.spdx', + 'epoch.spdx', + 'glyphicons-halflings-regular.spdx', + 'jquery.spdx', + 'selectize.spdx', + 'topojson.spdx', + ], + install_dir: modules_dir / 'http', +) + +# auxiliary debug library for HTTP module +if openssl.found() + debug_opensslkeylog_mod = shared_module( + 'debug_opensslkeylog', + ['debug_opensslkeylog.c'], + # visibility=default == public is required for LD_PRELOAD trick + c_args: '-fvisibility=default', + name_prefix: '', + install: true, + install_dir: lib_dir, + dependencies: [ + openssl, + ], + ) +endif diff --git a/modules/http/prometheus.lua b/modules/http/prometheus.lua new file mode 100644 index 0000000..3218552 --- /dev/null +++ b/modules/http/prometheus.lua @@ -0,0 +1,178 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Module implementation +local M = { + namespace = '', + finalize = function (_ --[[metrics]]) end, +} + +-- Gauge metrics +local gauges = { + ['worker.concurrent'] = true, + ['worker.rss'] = true, +} + +local function merge(t, results, prefix) + for _, result in pairs(results) do + if type(result) == 'table' then + for k, v in pairs(result) do + local val = t[prefix..k] + t[prefix..k] = (val or 0) + v + end + end + end +end + +local function getstats() + local t = {} + merge(t, map 'stats.list()', '') + merge(t, map 'cache.stats()', 'cache.') + merge(t, map 'worker.stats()', 'worker.') + return t +end + +-- @returns current stats + difference against previous data set passed in @param prev +local function snapshot_start(prev) + assert(type(prev) == 'table', 'table with previous values expected') + local is_empty = true + -- Get current snapshot + local cur, stats_dt = getstats(), {} + for k,v in pairs(cur) do + if gauges[k] then + stats_dt[k] = v + else + stats_dt[k] = v - (prev[k] or 0) + end + is_empty = is_empty and stats_dt[k] == 0 + end + -- Calculate upstreams and geotag them if possible + local upstreams + if http.geoip then + upstreams = stats.upstreams() + for k,v in pairs(upstreams) do + local gi + if string.find(k, '.', 1, true) then + gi = http.geoip:search_ipv4(k) + else + gi = http.geoip:search_ipv6(k) + end + if gi then + upstreams[k] = {data=v, location=gi.location, country=gi.country and gi.country.iso_code} + end + end + end + -- Aggregate per-worker metrics + local wdata = {} + for _, info in pairs(map 'worker.info()') do + if type(info) == 'table' then + wdata[tostring(info.pid)] = { + rss = info.rss, + usertime = info.usertime, + systime = info.systime, + pagefaults = info.pagefaults, + queries = info.queries + } + end + end + -- Publish stats updates periodically + if not is_empty then + local update = {time=os.time(), stats=stats_dt, upstreams=upstreams, workers=wdata} + return cur, update + end + return cur, nil +end + +-- Function to sort frequency list +local function stream_stats(_, ws) + local ok = true + -- Publish stats updates periodically + local prev = getstats() + while ok do + worker.sleep(1) + local update + prev, update = snapshot_start(prev) + local push = tojson(update) + ok = ws:send(push) + end +end + +-- Transform metrics from Graphite to Prometheus format +-- See: https://gitlab.nic.cz/knot/knot-resolver/-/issues/650 +-- E.g.: +-- worker.ipv4 -> worker_ipv4 +-- answer.blocked;stype=A -> answer_blocked{stype="A"} +local function get_metric(key) + local key_index, key_len, key_tag = 0, #key, 0 + return select(1, key:gsub('.', function (c) + key_index = key_index + 1 + if key_tag == 0 then + if c == '.' then return '_' end + if c == ';' then key_tag = 1; return '{' end + elseif key_tag == 1 then + if key_index == key_len then + if c == '=' then return '=""}' + else return c .. '"}' end + end + if c == '=' then key_tag = 2; return '="' end + elseif key_tag == 2 then + if key_index == key_len then + if c == ';' then return '"}' + else return c .. '"}' end + end + if c == ';' then key_tag = 1; return '",' end + end + return nil + end)) +end + +-- Render stats in Prometheus text format +local function serve_prometheus() + -- First aggregate metrics list and print counters + local slist, render = getstats(), {} + local latency = {} + local counter = '# TYPE %s counter\n%s %f' + for k,v in pairs(slist) do + k = get_metric(k) + -- Aggregate histograms + local band = k:match('answer_([%d]+)ms') + if band then + table.insert(latency, {band, v}) + elseif k == 'answer_slow' then + table.insert(latency, {'+Inf', v}) + -- Counter as a fallback + else + local key = M.namespace .. k + local name, label = key:match('^([^{]+)(.*)$') + table.insert(render, string.format(counter, name, name .. label, v)) + end + end + -- Fill in latency histogram + local function kweight(x) return tonumber(x) or math.huge end + table.sort(latency, function (a,b) return kweight(a[1]) < kweight(b[1]) end) + table.insert(render, string.format('# TYPE %slatency histogram', M.namespace)) + local count, sum = 0.0, 0.0 + for _,e in ipairs(latency) do + -- The information about the %Inf bin is lost, so we treat it + -- as a timeout (3000ms) for metrics purposes + count = count + e[2] + sum = sum + e[2] * (math.min(tonumber(e[1]), 3000.0)) + table.insert(render, string.format('%slatency_bucket{le="%s"} %f', M.namespace, e[1], count)) + end + table.insert(render, string.format('%slatency_count %f', M.namespace, count)) + table.insert(render, string.format('%slatency_sum %f', M.namespace, sum)) + -- Finalize metrics table before rendering + if type(M.finalize) == 'function' then + M.finalize(render) + end + return table.concat(render, '\n') .. '\n' +end + +-- Export module interface +M.endpoints = { + ['/stats'] = {'application/json', getstats, stream_stats}, + ['/frequent'] = {'application/json', function () return stats.frequent() end}, + ['/upstreams'] = {'application/json', function () return stats.upstreams() end}, + ['/bogus'] = {'application/json', function () return bogus_log.frequent() end}, + ['/metrics'] = {'text/plain; version=0.0.4', serve_prometheus}, +} + +return M diff --git a/modules/http/prometheus.rst b/modules/http/prometheus.rst new file mode 100644 index 0000000..acd8a82 --- /dev/null +++ b/modules/http/prometheus.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-http-prometheus: + +Prometheus metrics endpoint +--------------------------- + +The :ref:`HTTP module ` exposes ``/metrics`` endpoint that serves metrics +from :ref:`mod-stats` in Prometheus_ text format. +You can use it as soon as HTTP module is configured: + +.. code-block:: bash + + $ curl -k https://localhost:8453/metrics | tail + # TYPE latency histogram + latency_bucket{le=10} 2.000000 + latency_bucket{le=50} 2.000000 + latency_bucket{le=100} 2.000000 + latency_bucket{le=250} 2.000000 + latency_bucket{le=500} 2.000000 + latency_bucket{le=1000} 2.000000 + latency_bucket{le=1500} 2.000000 + latency_bucket{le=+Inf} 2.000000 + latency_count 2.000000 + latency_sum 11.000000 + +You can namespace the metrics in configuration, using `http.prometheus.namespace` attribute: + +.. code-block:: lua + + modules.load('http') + -- Set Prometheus namespace + http.prometheus.namespace = 'resolver_' + +You can also add custom metrics or rewrite existing metrics before they are returned to Prometheus client. + +.. code-block:: lua + + modules.load('http') + -- Add an arbitrary metric to Prometheus + http.prometheus.finalize = function (metrics) + table.insert(metrics, 'build_info{version="1.2.3"} 1') + end + +.. _Prometheus: https://prometheus.io diff --git a/modules/http/static/bootstrap-theme.min.css b/modules/http/static/bootstrap-theme.min.css new file mode 100644 index 0000000..449915d --- /dev/null +++ b/modules/http/static/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * SPDX-License-Identifier: MIT + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{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-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning: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-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .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:focus,.btn-default:hover{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.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{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:focus,.btn-primary:hover{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.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{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:focus,.btn-success:hover{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.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{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:focus,.btn-info:hover{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.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{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:focus,.btn-warning:hover{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.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{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:focus,.btn-danger:hover{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.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.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:focus,.dropdown-menu>li>a:hover{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:focus,.dropdown-menu>.active>a:hover{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>.active>a,.navbar-default .navbar-nav>.open>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>.active>a,.navbar-inverse .navbar-nav>.open>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-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{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:focus,.list-group-item.active:hover{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:focus .badge,.list-group-item.active:hover .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.min.css.map */ diff --git a/modules/http/static/bootstrap-theme.min.css.spdx b/modules/http/static/bootstrap-theme.min.css.spdx new file mode 100644 index 0000000..a56f287 --- /dev/null +++ b/modules/http/static/bootstrap-theme.min.css.spdx @@ -0,0 +1,11 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: bootstrap-theme +DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-2794db89-37c2-415b-b1bd-d66b445c5202 + +PackageName: bootstrap-theme +PackageVersion: 3.3.6 +PackageDownloadLocation: git+https://github.com/twbs/bootstrap.git@81df608a40bf0629a1dc08e584849bb1e43e0b7a#dist/css/bootstrap-theme.min.css +PackageOriginator: Organization: Twitter +PackageLicenseDeclared: MIT diff --git a/modules/http/static/bootstrap.min.css b/modules/http/static/bootstrap.min.css new file mode 100644 index 0000000..3bda7c6 --- /dev/null +++ b/modules/http/static/bootstrap.min.css @@ -0,0 +1,11 @@ +/*! + * bootswatch v3.3.6+2 yeti + * Homepage: http://bootswatch.com + * Copyright 2012-2016 Thomas Park + * SPDX-License-Identifier: MIT + * Based on Bootstrap +*//*! + * 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;-ms-text-size-adjust:100%;-webkit-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{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}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{border:0;padding: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-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-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('../glyphicons-halflings-regular.eot');src:url('../glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../glyphicons-halflings-regular.woff2') format('woff2'),url('../glyphicons-halflings-regular.woff') format('woff'),url('../glyphicons-halflings-regular.ttf') format('truetype'),url('../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:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#222222;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#008cba;text-decoration:none}a:hover,a:focus{color:#008cba;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:0}.img-thumbnail{padding:4px;line-height:1.4;background-color:#ffffff;border:1px solid #dddddd;border-radius:0;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #dddddd}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;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:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;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:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}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:10.5px;margin-bottom:10.5px}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:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:80%}mark,.mark{background-color:#fcf8e3;padding:.2em}.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:#999999}.text-primary{color:#008cba}a.text-primary:hover,a.text-primary:focus{color:#006687}.text-success{color:#43ac6a}a.text-success:hover,a.text-success:focus{color:#358753}.text-info{color:#5bc0de}a.text-info:hover,a.text-info:focus{color:#31b0d5}.text-warning{color:#e99002}a.text-warning:hover,a.text-warning:focus{color:#b67102}.text-danger{color:#f04124}a.text-danger:hover,a.text-danger:focus{color:#d32a0e}.bg-primary{color:#fff;background-color:#008cba}a.bg-primary:hover,a.bg-primary:focus{background-color:#006687}.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:9.5px;margin:42px 0 21px;border-bottom:1px solid #dddddd}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.4}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #dddddd}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.4;color:#6f6f6f}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #dddddd;border-left:0;text-align:right}.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:21px;font-style:normal;line-height:1.4}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:0}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:0;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25);box-shadow:inset 0 -1px 0 rgba(0,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:10px;margin:0 0 10.5px;font-size:14px;line-height:1.4;word-break:break-all;word-wrap:break-word;color:#333333;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:0}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{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-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-left:15px;padding-right: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:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.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.4;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.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 #dddddd}.table .table{background-color:#ffffff}.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 #dddddd}.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 #dddddd}.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;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.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{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.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{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#333333;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:9px;font-size:15px;line-height:1.4;color:#6f6f6f}.form-control{display:block;width:100%;height:39px;padding:8px 12px;font-size:15px;line-height:1.4;color:#6f6f6f;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:0;-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);-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,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;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:39px}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:36px}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:60px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:21px;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-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;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{padding-top:9px;padding-bottom:9px;margin-bottom:0;min-height:36px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:36px;padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}select.input-sm{height:36px;line-height:36px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:36px;padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}.form-group-sm select.form-control{height:36px;line-height:36px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:36px;min-height:33px;padding:9px 12px;font-size:12px;line-height:1.5}.input-lg{height:60px;padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-lg{height:60px;line-height:60px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:60px;padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}.form-group-lg select.form-control{height:60px;line-height:60px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:60px;min-height:40px;padding:17px 20px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:48.75px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:39px;height:39px;line-height:39px;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:60px;height:60px;line-height:60px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:36px;height:36px;line-height:36px}.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:#43ac6a}.has-success .form-control{border-color:#43ac6a;-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-success .form-control:focus{border-color:#358753;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #85d0a1;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #85d0a1}.has-success .input-group-addon{color:#43ac6a;border-color:#43ac6a;background-color:#dff0d8}.has-success .form-control-feedback{color:#43ac6a}.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:#e99002}.has-warning .form-control{border-color:#e99002;-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-warning .form-control:focus{border-color:#b67102;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #febc53;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #febc53}.has-warning .input-group-addon{color:#e99002;border-color:#e99002;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#e99002}.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:#f04124}.has-error .form-control{border-color:#f04124;-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 .form-control:focus{border-color:#d32a0e;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f79483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f79483}.has-error .input-group-addon{color:#f04124;border-color:#f04124;background-color:#f2dede}.has-error .form-control-feedback{color:#f04124}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#626262}@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{margin-top:0;margin-bottom:0;padding-top:9px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:30px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:9px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:17px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:9px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:8px 12px;font-size:15px;line-height:1.4;border-radius:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.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:#333333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333333;background-color:#e7e7e7;border-color:#cccccc}.btn-default:focus,.btn-default.focus{color:#333333;background-color:#cecece;border-color:#8c8c8c}.btn-default:hover{color:#333333;background-color:#cecece;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333333;background-color:#cecece;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:#333333;background-color:#bcbcbc;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:#e7e7e7;border-color:#cccccc}.btn-default .badge{color:#e7e7e7;background-color:#333333}.btn-primary{color:#ffffff;background-color:#008cba;border-color:#0079a1}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#006687;border-color:#001921}.btn-primary:hover{color:#ffffff;background-color:#006687;border-color:#004b63}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#006687;border-color:#004b63}.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:#ffffff;background-color:#004b63;border-color:#001921}.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:#008cba;border-color:#0079a1}.btn-primary .badge{color:#008cba;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#43ac6a;border-color:#3c9a5f}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#358753;border-color:#183e26}.btn-success:hover{color:#ffffff;background-color:#358753;border-color:#2b6e44}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#358753;border-color:#2b6e44}.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:#ffffff;background-color:#2b6e44;border-color:#183e26}.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:#43ac6a;border-color:#3c9a5f}.btn-success .badge{color:#43ac6a;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#ffffff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;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:#ffffff;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:#ffffff}.btn-warning{color:#ffffff;background-color:#e99002;border-color:#d08002}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#b67102;border-color:#513201}.btn-warning:hover{color:#ffffff;background-color:#b67102;border-color:#935b01}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#b67102;border-color:#935b01}.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:#ffffff;background-color:#935b01;border-color:#513201}.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:#e99002;border-color:#d08002}.btn-warning .badge{color:#e99002;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#f04124;border-color:#ea2f10}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#d32a0e;border-color:#731708}.btn-danger:hover{color:#ffffff;background-color:#d32a0e;border-color:#b1240c}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#d32a0e;border-color:#b1240c}.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:#ffffff;background-color:#b1240c;border-color:#731708}.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:#f04124;border-color:#ea2f10}.btn-danger .badge{color:#f04124;background-color:#ffffff}.btn-link{color:#008cba;font-weight:normal;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:#008cba;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:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}.btn-sm,.btn-group-sm>.btn{padding:8px 12px;font-size:12px;border-radius:0}.btn-xs,.btn-group-xs>.btn{padding:4px 6px;font-size:12px;line-height:1.5;border-radius:0}.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 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.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-property:height, visibility;-o-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:0.35s;-o-transition-duration:0.35s;transition-duration:0.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.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;list-style:none;font-size:15px;text-align:left;background-color:#ffffff;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:0;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);-webkit-background-clip:padding-box;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:rgba(0,0,0,0.2)}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.4;color:#555555;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#eeeeee}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;outline:0;background-color:#008cba}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.4;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.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-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-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-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,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-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.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-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;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-left:0;padding-right: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:60px;padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:60px;line-height:60px}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:36px;padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:36px;line-height:36px}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:8px 12px;font-size:15px;font-weight:normal;line-height:1;color:#6f6f6f;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:0}.input-group-addon.input-sm{padding:8px 12px;font-size:12px;border-radius:0}.input-group-addon.input-lg{padding:16px 20px;font-size:19px;border-radius:0}.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-bottom-right-radius:0;border-top-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-bottom-left-radius:0;border-top-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{margin-bottom:0;padding-left: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:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#008cba}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.4;border:1px solid transparent;border-radius:0 0 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#6f6f6f;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.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:0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:0 0 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:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:0}.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:#ffffff;background-color:#008cba}.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{text-align:center;margin-bottom:5px}.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:0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:0 0 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:45px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:0}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.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-left:0;padding-right: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;padding:12px 15px;font-size:19px;line-height:21px;height:45px}.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;margin-right:15px;padding:9px 10px;margin-top:5.5px;margin-bottom:5.5px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:0}.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:6px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@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:21px}.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:12px;padding-bottom:12px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:3px;margin-bottom:3px}@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;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:3px;margin-bottom:3px}.navbar-btn.btn-sm{margin-top:4.5px;margin-bottom:4.5px}.navbar-btn.btn-xs{margin-top:11.5px;margin-bottom:11.5px}.navbar-text{margin-top:12px;margin-bottom:12px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right: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:#333333;border-color:#222222}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#272727}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#272727}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:transparent}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:transparent}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#222222}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#272727;color:#ffffff}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#272727}.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:#ffffff;background-color:#272727}.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:#cccccc;background-color:transparent}}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.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:#cccccc}.navbar-inverse{background-color:#008cba;border-color:#006687}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#006687}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#006687}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:transparent}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:transparent}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#007196}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#006687;color:#ffffff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#006687}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#006687}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#006687}.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:#ffffff;background-color:#006687}.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:#444444;background-color:transparent}}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.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:#444444}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#f5f5f5;border-radius:0}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#999999}.breadcrumb>.active{color:#333333}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:0}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;line-height:1.4;text-decoration:none;color:#008cba;background-color:transparent;border:1px solid transparent;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:0;border-top-right-radius:0}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#008cba;background-color:#eeeeee;border-color:transparent}.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:#ffffff;background-color:#008cba;border-color:transparent;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;background-color:#ffffff;border-color:transparent;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:16px 20px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:0;border-top-left-radius:0}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:0;border-top-right-radius:0}.pagination-sm>li>a,.pagination-sm>li>span{padding:8px 12px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:0;border-top-left-radius:0}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:0;border-top-right-radius:0}.pager{padding-left:0;margin:21px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:transparent;border:1px solid transparent;border-radius:3px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.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:#999999;background-color:transparent;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#008cba}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#006687}.label-success{background-color:#43ac6a}.label-success[href]:hover,.label-success[href]:focus{background-color:#358753}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#e99002}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#b67102}.label-danger{background-color:#f04124}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#d32a0e}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#ffffff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#008cba;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:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#008cba;background-color:#ffffff}.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:#fafafa}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#e1e1e1}.container .jumbotron,.container-fluid .jumbotron{border-radius:0;padding-left:15px;padding-right:15px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.4;background-color:#ffffff;border:1px solid #dddddd;border-radius:0;-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-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#008cba}.thumbnail .caption{padding:9px;color:#222222}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:0}.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{background-color:#43ac6a;border-color:#3c9a5f;color:#ffffff}.alert-success hr{border-top-color:#358753}.alert-success .alert-link{color:#e6e6e6}.alert-info{background-color:#5bc0de;border-color:#3db5d8;color:#ffffff}.alert-info hr{border-top-color:#2aabd2}.alert-info .alert-link{color:#e6e6e6}.alert-warning{background-color:#e99002;border-color:#d08002;color:#ffffff}.alert-warning hr{border-top-color:#b67102}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{background-color:#f04124;border-color:#ea2f10;color:#ffffff}.alert-danger hr{border-top-color:#d32a0e}.alert-danger .alert-link{color:#e6e6e6}@-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{overflow:hidden;height:21px;margin-bottom:21px;background-color:#f5f5f5;border-radius:0;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:21px;color:#ffffff;text-align:center;background-color:#008cba;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.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:#43ac6a}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.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,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#e99002}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#f04124}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.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{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#555555;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{background-color:#eeeeee;color:#999999;cursor:not-allowed}.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:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#008cba;border-color:#008cba}.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:#87e1ff}.list-group-item-success{color:#43ac6a;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#43ac6a}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:#43ac6a;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:#43ac6a;border-color:#43ac6a}.list-group-item-info{color:#5bc0de;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#5bc0de}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:#5bc0de;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:#5bc0de;border-color:#5bc0de}.list-group-item-warning{color:#e99002;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#e99002}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:#e99002;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:#e99002;border-color:#e99002}.list-group-item-danger{color:#f04124;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#f04124}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:#f04124;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:#f04124;border-color:#f04124}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:0;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:-1;border-top-left-radius:-1}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;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 #dddddd;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.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-right-radius:-1;border-top-left-radius:-1}.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:-1;border-bottom-left-radius:-1}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-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-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:-1;border-top-left-radius:-1}.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:-1;border-top-right-radius:-1}.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:-1}.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:-1}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.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-left-radius:-1;border-bottom-right-radius:-1}.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:-1}.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:-1}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.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{border:0;margin-bottom:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:0}.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 #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#008cba}.panel-primary>.panel-heading{color:#ffffff;background-color:#008cba;border-color:#008cba}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#008cba}.panel-primary>.panel-heading .badge{color:#008cba;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#008cba}.panel-success{border-color:#3c9a5f}.panel-success>.panel-heading{color:#ffffff;background-color:#43ac6a;border-color:#3c9a5f}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3c9a5f}.panel-success>.panel-heading .badge{color:#43ac6a;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3c9a5f}.panel-info{border-color:#3db5d8}.panel-info>.panel-heading{color:#ffffff;background-color:#5bc0de;border-color:#3db5d8}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3db5d8}.panel-info>.panel-heading .badge{color:#5bc0de;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3db5d8}.panel-warning{border-color:#d08002}.panel-warning>.panel-heading{color:#ffffff;background-color:#e99002;border-color:#d08002}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d08002}.panel-warning>.panel-heading .badge{color:#e99002;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d08002}.panel-danger{border-color:#ea2f10}.panel-danger>.panel-heading{color:#ffffff;background-color:#f04124;border-color:#ea2f10}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ea2f10}.panel-danger>.panel-heading .badge{color:#f04124;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ea2f10}.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;left:0;bottom:0;height:100%;width: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:#fafafa;border:1px solid #e8e8e8;border-radius:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:0}.well-sm{padding:9px;border-radius:0}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.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:#ffffff;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);-webkit-background-clip:padding-box;background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:0.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.4}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.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,0.5);box-shadow:0 5px 15px rgba(0,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:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.4;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#333333;border-radius:0}.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:#333333}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#333333}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#333333}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#333333}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#333333}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#333333}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#333333}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#333333}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.4;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:15px;background-color:#333333;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #333333;border:1px solid transparent;border-radius:0;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:15px;background-color:#333333;border-bottom:1px solid #262626;border-radius:-1 -1 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{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#000000;border-top-color:rgba(0,0,0,0.05);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#333333}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#000000;border-right-color:rgba(0,0,0,0.05)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#333333}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#000000;border-bottom-color:rgba(0,0,0,0.05);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#333333}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#000000;border-left-color:rgba(0,0,0,0.05)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#333333;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-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{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left: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;left:0;bottom:0;width:15%;opacity:0.5;filter:alpha(opacity=50);font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-webkit-gradient(linear, left top, right top, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0.0001)));background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-webkit-gradient(linear, left top, right top, from(rgba(0,0,0,0.0001)), to(rgba(0,0,0,0.5)));background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.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;line-height:1;font-family:serif}.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%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #ffffff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#ffffff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,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{left:20%;right: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{content:" ";display:table}.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-left:auto;margin-right: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}}.navbar{border:none;font-size:13px;font-weight:300}.navbar .navbar-toggle:hover .icon-bar{background-color:#b3b3b3}.navbar-collapse{border-top-color:rgba(0,0,0,0.2);-webkit-box-shadow:none;box-shadow:none}.navbar .btn{padding-top:6px;padding-bottom:6px}.navbar-form{margin-top:7px;margin-bottom:5px}.navbar-form .form-control{height:auto;padding:4px 6px}.navbar-text{margin:12px 15px;line-height:21px}.navbar .dropdown-menu{border:none}.navbar .dropdown-menu>li>a,.navbar .dropdown-menu>li>a:focus{background-color:transparent;font-size:13px;font-weight:300}.navbar .dropdown-header{color:rgba(255,255,255,0.5)}.navbar-default .dropdown-menu{background-color:#333333}.navbar-default .dropdown-menu>li>a,.navbar-default .dropdown-menu>li>a:focus{color:#ffffff}.navbar-default .dropdown-menu>li>a:hover,.navbar-default .dropdown-menu>.active>a,.navbar-default .dropdown-menu>.active>a:hover{background-color:#272727}.navbar-inverse .dropdown-menu{background-color:#008cba}.navbar-inverse .dropdown-menu>li>a,.navbar-inverse .dropdown-menu>li>a:focus{color:#ffffff}.navbar-inverse .dropdown-menu>li>a:hover,.navbar-inverse .dropdown-menu>.active>a,.navbar-inverse .dropdown-menu>.active>a:hover{background-color:#006687}.btn{padding:8px 12px}.btn-lg{padding:16px 20px}.btn-sm{padding:8px 12px}.btn-xs{padding:4px 6px}.btn-group .btn~.dropdown-toggle{padding-left:16px;padding-right:16px}.btn-group .dropdown-menu{border-top-width:0}.btn-group.dropup .dropdown-menu{border-top-width:1px;border-bottom-width:0;margin-bottom:0}.btn-group .dropdown-toggle.btn-default~.dropdown-menu{background-color:#e7e7e7;border-color:#cccccc}.btn-group .dropdown-toggle.btn-default~.dropdown-menu>li>a{color:#333333}.btn-group .dropdown-toggle.btn-default~.dropdown-menu>li>a:hover{background-color:#d3d3d3}.btn-group .dropdown-toggle.btn-primary~.dropdown-menu{background-color:#008cba;border-color:#0079a1}.btn-group .dropdown-toggle.btn-primary~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-primary~.dropdown-menu>li>a:hover{background-color:#006d91}.btn-group .dropdown-toggle.btn-success~.dropdown-menu{background-color:#43ac6a;border-color:#3c9a5f}.btn-group .dropdown-toggle.btn-success~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-success~.dropdown-menu>li>a:hover{background-color:#388f58}.btn-group .dropdown-toggle.btn-info~.dropdown-menu{background-color:#5bc0de;border-color:#46b8da}.btn-group .dropdown-toggle.btn-info~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-info~.dropdown-menu>li>a:hover{background-color:#39b3d7}.btn-group .dropdown-toggle.btn-warning~.dropdown-menu{background-color:#e99002;border-color:#d08002}.btn-group .dropdown-toggle.btn-warning~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-warning~.dropdown-menu>li>a:hover{background-color:#c17702}.btn-group .dropdown-toggle.btn-danger~.dropdown-menu{background-color:#f04124;border-color:#ea2f10}.btn-group .dropdown-toggle.btn-danger~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-danger~.dropdown-menu>li>a:hover{background-color:#dc2c0f}.lead{color:#6f6f6f}cite{font-style:italic}blockquote{border-left-width:1px;color:#6f6f6f}blockquote.pull-right{border-right-width:1px}blockquote small{font-size:12px;font-weight:300}table{font-size:12px}label,.control-label,.help-block,.checkbox,.radio{font-size:12px;font-weight:normal}input[type="radio"],input[type="checkbox"]{margin-top:1px}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.nav-tabs>li>a{background-color:#e7e7e7;color:#222222}.nav-tabs .caret{border-top-color:#222222;border-bottom-color:#222222}.nav-pills{font-weight:300}.breadcrumb{border:1px solid #dddddd;border-radius:3px;font-size:10px;font-weight:300;text-transform:uppercase}.pagination{font-size:12px;font-weight:300;color:#999999}.pagination>li>a,.pagination>li>span{margin-left:4px;color:#999999}.pagination>.active>a,.pagination>.active>span{color:#fff}.pagination>li>a,.pagination>li:first-child>a,.pagination>li:last-child>a,.pagination>li>span,.pagination>li:first-child>span,.pagination>li:last-child>span{border-radius:3px}.pagination-lg>li>a,.pagination-lg>li>span{padding-left:22px;padding-right:22px}.pagination-sm>li>a,.pagination-sm>li>span{padding:0 5px}.pager{font-size:12px;font-weight:300;color:#999999}.list-group{font-size:12px;font-weight:300}.close{opacity:0.4;text-decoration:none;text-shadow:none}.close:hover,.close:focus{opacity:1}.alert{font-size:12px;font-weight:300}.alert .alert-link{font-weight:normal;color:#fff;text-decoration:underline}.label{padding-left:1em;padding-right:1em;border-radius:0;font-weight:300}.label-default{background-color:#e7e7e7;color:#333333}.badge{font-weight:300}.progress{height:22px;padding:2px;background-color:#f6f6f6;border:1px solid #ccc;-webkit-box-shadow:none;box-shadow:none}.dropdown-menu{padding:0;margin-top:0;font-size:12px}.dropdown-menu>li>a{padding:12px 15px}.dropdown-header{padding-left:15px;padding-right:15px;font-size:9px;text-transform:uppercase}.popover{color:#fff;font-size:12px;font-weight:300}.panel-heading,.panel-footer{border-top-right-radius:0;border-top-left-radius:0}.panel-default .close{color:#222222}.modal .close{color:#222222} diff --git a/modules/http/static/bootstrap.min.css.spdx b/modules/http/static/bootstrap.min.css.spdx new file mode 100644 index 0000000..53aef61 --- /dev/null +++ b/modules/http/static/bootstrap.min.css.spdx @@ -0,0 +1,11 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: bootswatch-yeti-bootstrap.min.css +DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-da039738-1b9b-430a-984b-a97d1415ad0f + +PackageName: bootswatch-yeti-bootstrap.min.css +PackageVersion: 3.3.6+2 +PackageDownloadLocation: git+https://github.com/twbs/bootstrap.git@a78dc3aed640a35914361b837ce24573a0515e19#yeti/bootstrap.min.css +PackageOriginator: Person: Thomas Park (thomas@thomaspark.co) +PackageLicenseDeclared: MIT diff --git a/modules/http/static/bootstrap.min.js b/modules/http/static/bootstrap.min.js new file mode 100644 index 0000000..07eaed1 --- /dev/null +++ b/modules/http/static/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * SPDX-License-Identifier: MIT + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.6",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},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!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.6",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); diff --git a/modules/http/static/bootstrap.min.js.spdx b/modules/http/static/bootstrap.min.js.spdx new file mode 100644 index 0000000..d0df6eb --- /dev/null +++ b/modules/http/static/bootstrap.min.js.spdx @@ -0,0 +1,11 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: bootstrap.js +DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-6797c679-d14a-4524-abe4-a668e07f213f + +PackageName: bootstrap.js +PackageVersion: 3.3.6 +PackageDownloadLocation: git+https://github.com/twbs/bootstrap.git@81df608a40bf0629a1dc08e584849bb1e43e0b7a#dist/js/bootstrap.min.js +PackageOriginator: Organization: Twitter +PackageLicenseDeclared: MIT diff --git a/modules/http/static/d3.js b/modules/http/static/d3.js new file mode 100644 index 0000000..c3a27fd --- /dev/null +++ b/modules/http/static/d3.js @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function m(){this._=Object.create(null)}function y(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};N(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c +},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++oe?[0/0,0/0]:[e>0?a[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return eo(n,t)},e}function ro(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.6"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g=i.length)return n;var r=[],u=o[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ta.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return o[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ta.set=function(n){var t=new m;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},c(m,{has:h,add:function(n){return this._[s(n+="")]=!0,n},remove:g,values:p,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,f(t))}}),ta.behavior={},ta.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ta.event=null,ta.requote=function(n){return n.replace(ma,"\\$&")};var ma=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ya={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySelector(n)},xa=function(n,t){return t.querySelectorAll(n)},ba=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(ba=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ma=function(n,t){return Sizzle(n,t)[0]||null},xa=Sizzle,ba=Sizzle.matchesSelector),ta.selection=function(){return ta.select(ua.documentElement)};var _a=ta.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=N(n);for(var o=-1,a=this.length;++o=0&&(e=n.slice(0,t),n=n.slice(t+1)),wa.hasOwnProperty(e)?{space:wa[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ta.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,u=-1;if(t=e.classList){for(;++uu){if("string"!=typeof n){2>u&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>u){var i=this.node();return t(i).getComputedStyle(i,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},_a.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},_a.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},_a.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},_a.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=j(n),t=N(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(F)},_a.data=function(n,t){function e(n,e){var r,u,i,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),v=new Array(o);if(t){var d,m=new l,y=new Array(o);for(r=-1;++rr;++r)p[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,a.push(p),c.push(g),s.push(v)}var r,u,i=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return A(u)},_a.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return Y(this,function(){++n}),n};var Sa=[];ta.selection.enter=Z,ta.selection.enter.prototype=Sa,Sa.append=_a.append,Sa.empty=_a.empty,Sa.node=_a.node,Sa.call=_a.call,Sa.size=_a.size,Sa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var ka=ta.map({mouseenter:"mouseover",mouseleave:"mouseout"});ua&&ka.forEach(function(n){"on"+n in ua&&ka.remove(n)});var Ea,Aa=0;ta.mouse=function(n){return J(n,k())};var Na=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ta.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return J(n,r)},ta.behavior.drag=function(){function n(){this.on("mousedown.drag",i).on("touchstart.drag",o)}function e(n,t,e,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],p|=n|e,M=r,g({type:"drag",x:r[0]+l[0],y:r[1]+l[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&ta.event.target===f),g({type:"dragend"}))}var l,s=this,f=ta.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=ta.select(e(f)).on(i+d,a).on(o+d,c),y=W(f),M=t(h,v);u?(l=u.apply(s,arguments),l=[l.x-M[0],l.y-M[1]]):l=[0,0],g({type:"dragstart"})}}var r=E(n,"drag","dragstart","dragend"),u=null,i=e(b,ta.mouse,t,"mousemove","mouseup"),o=e(G,ta.touch,y,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ta.rebind(n,r,"on")},ta.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?ra(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Ca=1e-6,za=Ca*Ca,qa=Math.PI,La=2*qa,Ta=La-Ca,Ra=qa/2,Da=qa/180,Pa=180/qa,Ua=Math.SQRT2,ja=2,Fa=4;ta.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=rt(v),o=i/(ja*h)*(e*ut(Ua*t+v)-et(v));return[r+o*l,u+o*s,i*e/rt(Ua*t+v)]}return[r+n*l,u+n*s,i*Math.exp(Ua*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-r,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+Fa*f)/(2*i*ja*h),p=(c*c-i*i-Fa*f)/(2*c*ja*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ua;return e.duration=1e3*y,e},ta.behavior.zoom=function(){function n(n){n.on(q,f).on(Oa+".zoom",g).on("dblclick.zoom",p).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function u(n){k.k=Math.max(N[0],Math.min(N[1],n))}function i(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},u(Math.pow(2,o)),i(d=e,r),t=ta.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function c(n){z++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function s(n){--z||(n({type:"zoomend"}),d=null)}function f(){function n(){f=1,i(ta.mouse(u),g),l(a)}function r(){h.on(L,null).on(T,null),p(f&&ta.event.target===o),s(a)}var u=this,o=ta.event.target,a=D.of(u,arguments),f=0,h=ta.select(t(u)).on(L,n).on(T,r),g=e(ta.mouse(u)),p=W(u);Dl.call(u),c(a)}function h(){function n(){var n=ta.touches(p);return g=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ta.event.target;ta.select(t).on(x,r).on(b,a),_.push(t);for(var e=ta.event.changedTouches,u=0,i=e.length;i>u;++u)d[e[u].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-M){var s=c[0];o(p,s,d[s.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=l}else if(c.length>1){var s=c[0],f=c[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function r(){var n,t,e,r,o=ta.touches(p);Dl.call(p);for(var a=0,c=o.length;c>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var s=(s=e[0]-n[0])*s+(s=e[1]-n[1])*s,f=m&&Math.sqrt(s/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],u(f*g)}M=null,i(n,t),l(v)}function a(){if(ta.event.touches.length){for(var t=ta.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}ta.selectAll(_).on(y,null),w.on(q,f).on(R,h),E(),s(v)}var g,p=this,v=D.of(p,arguments),d={},m=0,y=".zoom-"+ta.event.changedTouches[0].identifier,x="touchmove"+y,b="touchend"+y,_=[],w=ta.select(p),E=W(p);t(),c(v),w.on(q,null).on(R,t)}function g(){var n=D.of(this,arguments);y?clearTimeout(y):(Dl.call(this),v=e(d=m||ta.mouse(this)),c(n)),y=setTimeout(function(){y=null,s(n)},50),S(),u(Math.pow(2,.002*Ha())*k.k),i(d,v),l(n)}function p(){var n=ta.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ta.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,m,y,M,x,b,_,w,k={x:0,y:0,k:1},A=[960,500],N=Ia,C=250,z=0,q="mousedown.zoom",L="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=E(n,"zoomstart","zoom","zoomend");return Oa||(Oa="onwheel"in ua?(Ha=function(){return-ta.event.deltaY*(ta.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ua?(Ha=function(){return ta.event.wheelDelta},"mousewheel"):(Ha=function(){return-ta.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Tl?ta.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var e=A[0],r=A[1],u=d?d[0]:e/2,i=d?d[1]:r/2,o=ta.interpolateZoom([(u-k.x)/k.k,(i-k.y)/k.k,e/k.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:u-r[0]*a,y:i-r[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){s(n)}).each("end.zoom",function(){s(n)}):(this.__chart__=k,c(n),l(n),s(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:+t},a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(N=null==t?Ia:[+t[0],+t[1]],n):N},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(A=t&&[+t[0],+t[1]],n):A},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ta.rebind(n,D,"on")};var Ha,Oa,Ia=[0,1/0];ta.color=ot,ot.prototype.toString=function(){return this.rgb()+""},ta.hsl=at;var Ya=at.prototype=new ot;Ya.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,this.l/n)},Ya.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,n*this.l)},Ya.rgb=function(){return ct(this.h,this.s,this.l)},ta.hcl=lt;var Za=lt.prototype=new ot;Za.brighter=function(n){return new lt(this.h,this.c,Math.min(100,this.l+Va*(arguments.length?n:1)))},Za.darker=function(n){return new lt(this.h,this.c,Math.max(0,this.l-Va*(arguments.length?n:1)))},Za.rgb=function(){return st(this.h,this.c,this.l).rgb()},ta.lab=ft;var Va=18,Xa=.95047,$a=1,Ba=1.08883,Wa=ft.prototype=new ot;Wa.brighter=function(n){return new ft(Math.min(100,this.l+Va*(arguments.length?n:1)),this.a,this.b)},Wa.darker=function(n){return new ft(Math.max(0,this.l-Va*(arguments.length?n:1)),this.a,this.b)},Wa.rgb=function(){return ht(this.l,this.a,this.b)},ta.rgb=mt;var Ja=mt.prototype=new ot;Ja.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),new mt(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mt(u,u,u)},Ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mt(n*this.r,n*this.g,n*this.b)},Ja.hsl=function(){return _t(this.r,this.g,this.b)},Ja.toString=function(){return"#"+xt(this.r)+xt(this.g)+xt(this.b)};var Ga=ta.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});Ga.forEach(function(n,t){Ga.set(n,yt(t))}),ta.functor=Et,ta.xhr=At(y),ta.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Nt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(r!==c)continue;return n.slice(t,s-a)}return n.slice(t)}for(var r,u,i={},o={},a=[],l=n.length,s=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,f++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new m,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},ta.csv=ta.dsv(",","text/csv"),ta.tsv=ta.dsv(" ","text/tab-separated-values");var Ka,Qa,nc,tc,ec,rc=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ta.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Qa?Qa.n=i:Ka=i,Qa=i,nc||(tc=clearTimeout(tc),nc=1,rc(qt))},ta.timer.flush=function(){Lt(),Tt()},ta.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);ta.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ta.round(n,Rt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),uc[8+e/3]};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=ta.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ta.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ac=ta.time={},cc=Date;jt.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(){lc.setUTCDate.apply(this._,arguments)},setDay:function(){lc.setUTCDay.apply(this._,arguments)},setFullYear:function(){lc.setUTCFullYear.apply(this._,arguments)},setHours:function(){lc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){lc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){lc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){lc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){lc.setUTCSeconds.apply(this._,arguments)},setTime:function(){lc.setTime.apply(this._,arguments)}};var lc=Date.prototype;ac.year=Ft(function(n){return n=ac.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ac.years=ac.year.range,ac.years.utc=ac.year.utc.range,ac.day=Ft(function(n){var t=new cc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ac.days=ac.day.range,ac.days.utc=ac.day.utc.range,ac.dayOfYear=function(n){var t=ac.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ac[n]=Ft(function(n){return(n=ac.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ac[n+"s"]=e.range,ac[n+"s"].utc=e.utc.range,ac[n+"OfYear"]=function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)}}),ac.week=ac.sunday,ac.weeks=ac.sunday.range,ac.weeks.utc=ac.sunday.utc.range,ac.weekOfYear=ac.sundayOfYear;var sc={"-":"",_:" ",0:"0"},fc=/^\s*\d+/,hc=/^%/;ta.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var gc=ta.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"]});ta.format=gc.numberFormat,ta.geo={},ce.prototype={s:0,t:0,add:function(n){le(n,this.t,pc),le(pc.s,this.s,this),this.s?this.t+=pc.t:this.s=pc.t +},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var pc=new ce;ta.geo.stream=function(n,t){n&&vc.hasOwnProperty(n.type)?vc[n.type](n,t):se(n,t)};var vc={Feature:function(n,t){se(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*qa+n:n,Mc.lineStart=Mc.lineEnd=Mc.point=b}};ta.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*Da,e*Da]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);Me(o),o=xe(o);var c=t-p,l=c>0?1:-1,v=o[0]*Pa*l,d=ga(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Pa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Pa;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,e);m=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ga(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Mc.point(n,e),t(n,e)}function i(){Mc.lineStart()}function o(){u(v,d),Mc.lineEnd(),ga(y)>Ca&&(s=-(h=180)),x[0]=s,x[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nyc?(s=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ta.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,u=M[0],i=[u];t>r;++r)e=M[r],l(e[0],u)||l(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,s=e[0],h=u[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ta.geo.centroid=function(n){xc=bc=_c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,qc);var t=Nc,e=Cc,r=zc,u=t*t+e*e+r*r;return za>u&&(t=kc,e=Ec,r=Ac,Ca>bc&&(t=_c,e=wc,r=Sc),u=t*t+e*e+r*r,za>u)?[0/0,0/0]:[Math.atan2(e,t)*Pa,tt(r/Math.sqrt(u))*Pa]};var xc,bc,_c,wc,Sc,kc,Ec,Ac,Nc,Cc,zc,qc={sphere:b,point:_e,lineStart:Se,lineEnd:ke,polygonStart:function(){qc.lineStart=Ee},polygonEnd:function(){qc.lineStart=Se}},Lc=Le(Ne,Pe,je,[-qa,-qa/2]),Tc=1e9;ta.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ta.geo.conicEqualArea=function(){return Ye(Ze)}).raw=Ze,ta.geo.albers=function(){return ta.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ta.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=ta.geo.albers(),o=ta.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ta.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Ca,f+.12*l+Ca],[s-.214*l-Ca,f+.234*l-Ca]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Ca,f+.166*l+Ca],[s-.115*l-Ca,f+.234*l-Ca]]).stream(c).point,n},n.scale(1070)};var Rc,Dc,Pc,Uc,jc,Fc,Hc={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Dc=0,Hc.lineStart=Ve},polygonEnd:function(){Hc.lineStart=Hc.lineEnd=Hc.point=b,Rc+=ga(Dc/2)}},Oc={point:Xe,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Ic={point:We,lineStart:Je,lineEnd:Ge,polygonStart:function(){Ic.lineStart=Ke},polygonEnd:function(){Ic.point=We,Ic.lineStart=Je,Ic.lineEnd=Ge}};ta.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),ta.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Rc=0,ta.geo.stream(n,u(Hc)),Rc},n.centroid=function(n){return _c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,u(Ic)),zc?[Nc/zc,Cc/zc]:Ac?[kc/Ac,Ec/Ac]:Sc?[_c/Sc,wc/Sc]:[0/0,0/0]},n.bounds=function(n){return jc=Fc=-(Pc=Uc=1/0),ta.geo.stream(n,u(Oc)),[[Pc,Uc],[jc,Fc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):y,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new $e:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(ta.geo.albersUsa()).context(null)},ta.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.prototype={point:function(n,t){this.stream.point(n,t)},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()}},ta.geo.projection=ur,ta.geo.projectionMutator=ir,(ta.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,ta.geo.rotation=function(n){function t(t){return t=n(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t}return n=lr(n[0]%360*Da,n[1]*Da,n.length>2?n[2]*Da:0),t.invert=function(t){return t=n.invert(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t},t},cr.invert=ar,ta.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=lr(-n[0]*Da,-n[1]*Da,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Pa,n[1]*=Pa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*Da,u*Da),n):t},n.precision=function(r){return arguments.length?(e=gr(t*Da,(u=+r)*Da),n):u},n.angle(90)},ta.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Da,u=n[1]*Da,i=t[1]*Da,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=l*s-c*f*a)*e),c*s+l*f*a)},ta.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ta.range(Math.ceil(i/d)*d,u,d).map(h).concat(ta.range(Math.ceil(l/m)*m,c,m).map(g)).concat(ta.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ga(n%d)>Ca}).map(s)).concat(ta.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ga(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=vr(a,o,90),f=dr(r,e,y),h=vr(l,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},ta.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return ta.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},ta.geo.interpolate=function(n,t){return Mr(n[0]*Da,n[1]*Da,t[0]*Da,t[1]*Da)},ta.geo.length=function(n){return Yc=0,ta.geo.stream(n,Zc),Yc};var Yc,Zc={sphere:b,point:b,lineStart:xr,lineEnd:b,polygonStart:b,polygonEnd:b},Vc=br(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ta.geo.azimuthalEqualArea=function(){return ur(Vc)}).raw=Vc;var Xc=br(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},y);(ta.geo.azimuthalEquidistant=function(){return ur(Xc)}).raw=Xc,(ta.geo.conicConformal=function(){return Ye(_r)}).raw=_r,(ta.geo.conicEquidistant=function(){return Ye(wr)}).raw=wr;var $c=br(function(n){return 1/n},Math.atan);(ta.geo.gnomonic=function(){return ur($c)}).raw=$c,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ra]},(ta.geo.mercator=function(){return kr(Sr)}).raw=Sr;var Bc=br(function(){return 1},Math.asin);(ta.geo.orthographic=function(){return ur(Bc)}).raw=Bc;var Wc=br(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ta.geo.stereographic=function(){return ur(Wc)}).raw=Wc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ra]},(ta.geo.transverseMercator=function(){var n=kr(Er),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Er,ta.geom={},ta.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(zr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=Cr(a),s=Cr(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=r&&l.x<=i&&l.y>=u&&l.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];s.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ar,u=Nr,i=r,o=u,a=ul;return n?t(n):(t.links=function(n){return iu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Yr),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=r>=s,g=h<<1|f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=su()),f?u=l:a=l,h?o=s:c=s,i(n,t,e,r,u,o,a,c)}var s,f,h,g,p,v,d,m,y,M=Et(a),x=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);v>b&&(v=b),d>_&&(d=_),b>m&&(m=b),_>y&&(y=_),f.push(b),h.push(_)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=su();if(k.add=function(n){i(k,n,+M(n,++g),+x(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},k.find=function(n){return hu(k,n[0],n[1],v,d,m,y)},g=-1,null==t){for(;++g=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=cl.get(e)||al,r=ll.get(r)||y,Mu(r(e.apply(null,ea.call(arguments,1))))},ta.interpolateHcl=Lu,ta.interpolateHsl=Tu,ta.interpolateLab=Ru,ta.interpolateRound=Du,ta.transform=function(n){var t=ua.createElementNS(ta.ns.prefix.svg,"g");return(ta.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Pu(e?e.matrix:sl)})(n)},Pu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var sl={a:1,b:0,c:0,d:1,e:0,f:0};ta.interpolateTransform=Hu,ta.layout={},ta.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ta.event.x,n.py=ta.event.y,a.resume()}var e,r,u,i,o,a={},c=ta.dispatch("start","tick","end"),l=[1,1],s=.9,f=fl,h=hl,g=-30,p=gl,v=.1,d=.64,m=[],M=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,y,x,b=m.length,_=M.length;for(e=0;_>e;++e)a=M[e],f=a.source,h=a.target,y=h.x-f.x,x=h.y-f.y,(p=y*y+x*x)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,y*=p,x*=p,h.x-=y*(d=f.weight/(h.weight+f.weight)),h.y-=x*d,f.x+=y*(d=1-d),f.y+=x*d);if((d=r*v)&&(y=l[0]/2,x=l[1]/2,e=-1,d))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ta.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,l=o.length;++at;++t)(r=m[t]).index=t,r.weight=0;for(t=0;s>t;++t)r=M[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;s>t;++t)u[t]=+f.call(this,M[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,M[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=ta.behavior.drag().origin(y).on("dragstart.force",Xu).on("drag.force",t).on("dragend.force",$u)),arguments.length?void this.on("mouseover.force",Bu).on("mouseout.force",Wu).call(e):e},ta.rebind(a,c,"on")};var fl=20,hl=1,gl=1/0;ta.layout.hierarchy=function(){function n(u){var i,o=[u],a=[];for(u.depth=0;null!=(i=o.pop());)if(a.push(i),(l=e.call(n,i,i.depth))&&(c=l.length)){for(var c,l,s;--c>=0;)o.push(s=l[c]),s.parent=i,s.depth=i.depth+1;r&&(i.value=0),i.children=l}else r&&(i.value=+r.call(n,i,i.depth)||0),delete i.children;return Qu(u,function(n){var e,u;t&&(e=n.children)&&e.sort(t),r&&(u=n.parent)&&(u.value+=n.value)}),a}var t=ei,e=ni,r=ti;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(Ku(t,function(n){n.children&&(n.value=0)}),Qu(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ta.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,l=-1;for(r=t.value?r/t.value:0;++lf?-1:1),p=(f-c*g)/ta.sum(l),v=ta.range(c),d=[];return null!=e&&v.sort(e===pl?function(n,t){return l[t]-l[n]}:function(n,t){return e(o[n],o[t])}),v.forEach(function(n){d[n]={data:o[n],value:a=l[n],startAngle:s,endAngle:s+=a*p+g,padAngle:h}}),d}var t=Number,e=pl,r=0,u=La,i=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n.padAngle=function(t){return arguments.length?(i=t,n):i},n};var pl={};ta.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(e,r){return t.call(n,e,r)}),s=l.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,s,c);l=ta.permute(l,f),s=ta.permute(s,f);var h,g,p,v,d=r.call(n,s,c),m=l[0].length;for(p=0;m>p;++p)for(u.call(n,l[0][p],v=d[p],s[0][p][1]),g=1;h>g;++g)u.call(n,l[g][p],v+=s[g-1][p][1],s[g][p][1]);return a}var t=y,e=ai,r=ci,u=oi,i=ui,o=ii;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:vl.get(t)||ai,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:dl.get(t)||ci,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var vl=ta.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(li),i=n.map(si),o=ta.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],l.push(e)):(c+=i[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ta.range(n.length).reverse()},"default":ai}),dl=ta.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,u=0;s>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];s>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ci});ta.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(e,this),s=r.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[ta.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=pi,u=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return gi(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ta.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Qu(a,function(n){n.r=+s(n.value)}),Qu(a,Mi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Qu(a,function(n){n.r+=f}),Qu(a,Mi),Qu(a,function(n){n.r-=f})}return _i(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,e=ta.layout.hierarchy().sort(vi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Gu(n,e)},ta.layout.tree=function(){function n(n,u){var s=o.call(this,n,u),f=s[0],h=t(f);if(Qu(h,e),h.parent.m=-h.z,Ku(h,r),l)Ku(f,i);else{var g=f,p=f,v=f;Ku(f,function(n){n.xp.x&&(p=n),n.depth>v.depth&&(v=n)});var d=a(g,p)/2-g.x,m=c[0]/(p.x+a(p,g)/2+d),y=c[1]/(v.depth||1);Ku(f,function(n){n.x=(n.x+d)*m,n.y=n.depth*y})}return s}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var u,i=t.children,o=0,a=i.length;a>o;++o)r.push((i[o]=u={_:i[o],parent:t,children:(u=i[o].children)&&u.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=u);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Ni(n);var i=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-i):n.z=i}else r&&(n.z=r.z+a(n._,r._));n.parent.A=u(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function u(n,t,e){if(t){for(var r,u=n,i=n,o=t,c=u.parent.children[0],l=u.m,s=i.m,f=o.m,h=c.m;o=Ei(o),u=ki(u),o&&u;)c=ki(c),i=Ei(i),i.a=n,r=o.z+f-u.z-l+a(o._,u._),r>0&&(Ai(Ci(o,n,e),n,r),l+=r,s+=r),f+=o.m,l+=u.m,h+=c.m,s+=i.m;o&&!Ei(i)&&(i.t=o,i.m+=f-s),u&&!ki(c)&&(c.t=u,c.m+=l-h,e=n)}return e}function i(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ta.layout.hierarchy().sort(null).value(null),a=Si,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?i:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:i,n):l?c:null},Gu(n,o)},ta.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Qu(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=e(n,o):0,n.y=0,o=n)});var s=Li(c),f=Ti(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Qu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=ta.layout.hierarchy().sort(null).value(null),e=Si,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Gu(n,t)},ta.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,l=f(e),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=r(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ie.dx)&&(s=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=ta.random.normal.apply(ta,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ta.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ta.scale={};var ml={floor:y,ceil:y};ta.scale.linear=function(){return Ii([0,1],[0,1],mu,!1)};var yl={s:1,g:1,p:1,r:1,e:1};ta.scale.log=function(){return Ji(ta.scale.linear().domain([0,1]),10,!0,[1,10])};var Ml=ta.format(".0e"),xl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ta.scale.pow=function(){return Gi(ta.scale.linear(),1,[0,1])},ta.scale.sqrt=function(){return ta.scale.pow().exponent(.5)},ta.scale.ordinal=function(){return Qi([],{t:"range",a:[[]]})},ta.scale.category10=function(){return ta.scale.ordinal().range(bl)},ta.scale.category20=function(){return ta.scale.ordinal().range(_l)},ta.scale.category20b=function(){return ta.scale.ordinal().range(wl)},ta.scale.category20c=function(){return ta.scale.ordinal().range(Sl)};var bl=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(Mt),_l=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(Mt),wl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(Mt),Sl=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(Mt);ta.scale.quantile=function(){return no([],[])},ta.scale.quantize=function(){return to(0,1,[0,1])},ta.scale.threshold=function(){return eo([.5],[0,1])},ta.scale.identity=function(){return ro([0,1])},ta.svg={},ta.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),l=Math.max(0,+r.apply(this,arguments)),s=o.apply(this,arguments)-Ra,f=a.apply(this,arguments)-Ra,h=Math.abs(f-s),g=s>f?0:1;if(n>l&&(p=l,l=n,n=p),h>=Ta)return t(l,g)+(n?t(n,1-g):"")+"Z";var p,v,d,m,y,M,x,b,_,w,S,k,E=0,A=0,N=[];if((m=(+c.apply(this,arguments)||0)/2)&&(d=i===kl?Math.sqrt(n*n+l*l):+i.apply(this,arguments),g||(A*=-1),l&&(A=tt(d/l*Math.sin(m))),n&&(E=tt(d/n*Math.sin(m)))),l){y=l*Math.cos(s+A),M=l*Math.sin(s+A),x=l*Math.cos(f-A),b=l*Math.sin(f-A);var C=Math.abs(f-s-2*A)<=qa?0:1;if(A&&so(y,M,x,b)===g^C){var z=(s+f)/2;y=l*Math.cos(z),M=l*Math.sin(z),x=b=null}}else y=M=0;if(n){_=n*Math.cos(f-E),w=n*Math.sin(f-E),S=n*Math.cos(s+E),k=n*Math.sin(s+E);var q=Math.abs(s-f+2*E)<=qa?0:1;if(E&&so(_,w,S,k)===1-g^q){var L=(s+f)/2;_=n*Math.cos(L),w=n*Math.sin(L),S=k=null}}else _=w=0;if((p=Math.min(Math.abs(l-n)/2,+u.apply(this,arguments)))>.001){v=l>n^g?0:1;var T=null==S?[_,w]:null==x?[y,M]:Lr([y,M],[S,k],[x,b],[_,w]),R=y-T[0],D=M-T[1],P=x-T[0],U=b-T[1],j=1/Math.sin(Math.acos((R*P+D*U)/(Math.sqrt(R*R+D*D)*Math.sqrt(P*P+U*U)))/2),F=Math.sqrt(T[0]*T[0]+T[1]*T[1]);if(null!=x){var H=Math.min(p,(l-F)/(j+1)),O=fo(null==S?[_,w]:[S,k],[y,M],l,H,g),I=fo([x,b],[_,w],l,H,g);p===H?N.push("M",O[0],"A",H,",",H," 0 0,",v," ",O[1],"A",l,",",l," 0 ",1-g^so(O[1][0],O[1][1],I[1][0],I[1][1]),",",g," ",I[1],"A",H,",",H," 0 0,",v," ",I[0]):N.push("M",O[0],"A",H,",",H," 0 1,",v," ",I[0])}else N.push("M",y,",",M);if(null!=S){var Y=Math.min(p,(n-F)/(j-1)),Z=fo([y,M],[S,k],n,-Y,g),V=fo([_,w],null==x?[y,M]:[x,b],n,-Y,g);p===Y?N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",V[1],"A",n,",",n," 0 ",g^so(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-g," ",Z[1],"A",Y,",",Y," 0 0,",v," ",Z[0]):N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",Z[0])}else N.push("L",_,",",w)}else N.push("M",y,",",M),null!=x&&N.push("A",l,",",l," 0 ",C,",",g," ",x,",",b),N.push("L",_,",",w),null!=S&&N.push("A",n,",",n," 0 ",q,",",1-g," ",S,",",k);return N.push("Z"),N.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=io,r=oo,u=uo,i=kl,o=ao,a=co,c=lo;return n.innerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.cornerRadius=function(t){return arguments.length?(u=Et(t),n):u},n.padRadius=function(t){return arguments.length?(i=t==kl?kl:Et(t),n):i},n.startAngle=function(t){return arguments.length?(o=Et(t),n):o},n.endAngle=function(t){return arguments.length?(a=Et(t),n):a},n.padAngle=function(t){return arguments.length?(c=Et(t),n):c},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Ra;return[Math.cos(t)*n,Math.sin(t)*n]},n};var kl="auto";ta.svg.line=function(){return ho(y)};var El=ta.map({linear:go,"linear-closed":po,step:vo,"step-before":mo,"step-after":yo,basis:So,"basis-open":ko,"basis-closed":Eo,bundle:Ao,cardinal:bo,"cardinal-open":Mo,"cardinal-closed":xo,monotone:To});El.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Al=[0,2/3,1/3,0],Nl=[0,1/3,2/3,0],Cl=[0,1/6,2/3,1/6];ta.svg.line.radial=function(){var n=ho(Ro);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},mo.reverse=yo,yo.reverse=mo,ta.svg.area=function(){return Do(y)},ta.svg.area.radial=function(){var n=Do(Ro);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ta.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)-Ra,s=l.call(n,u,r)-Ra;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>qa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Po,c=ao,l=co;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},ta.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Uo;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ta.svg.diagonal.radial=function(){var n=ta.svg.diagonal(),t=Uo,e=n.projection;return n.projection=function(n){return arguments.length?e(jo(t=n)):t},n},ta.svg.symbol=function(){function n(n,r){return(zl.get(t.call(this,n,r))||Oo)(e.call(this,n,r))}var t=Ho,e=Fo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var zl=ta.map({circle:Oo,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Ll)),e=t*Ll;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ta.svg.symbolTypes=zl.keys();var ql=Math.sqrt(3),Ll=Math.tan(30*Da);_a.transition=function(n){for(var t,e,r=Tl||++Ul,u=Xo(n),i=[],o=Rl||{time:Date.now(),ease:Su,delay:0,duration:250},a=-1,c=this.length;++ai;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Yo(u,this.namespace,this.id)},Pl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(u){u[r][e].tween.set(n,t)})},Pl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Hu:mu,a=ta.ns.qualify(n);return Zo(this,"attr."+n,t,a.local?i:u)},Pl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=ta.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Pl.style=function(n,e,r){function u(){this.style.removeProperty(n)}function i(e){return null==e?u:(e+="",function(){var u,i=t(this).getComputedStyle(this,null).getPropertyValue(n);return i!==e&&(u=mu(i,e),function(t){this.style.setProperty(n,u(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Zo(this,"style."+n,e,i)},Pl.styleTween=function(n,e,r){function u(u,i){var o=e.call(this,u,i,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,u)},Pl.text=function(n){return Zo(this,"text",n,Vo)},Pl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Pl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ta.ease.apply(ta,arguments)),Y(this,function(r){r[e][t].ease=n}))},Pl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,u,i){r[e][t].delay=+n.call(r,r.__data__,u,i)}:(n=+n,function(r){r[e][t].delay=n}))},Pl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,u,i){r[e][t].duration=Math.max(1,n.call(r,r.__data__,u,i))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Pl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var u=Rl,i=Tl;try{Tl=e,Y(this,function(t,u,i){Rl=t[r][e],n.call(t,t.__data__,u,i)})}finally{Rl=u,Tl=i}}else Y(this,function(u){var i=u[r][e];(i.event||(i.event=ta.dispatch("start","end","interrupt"))).on(n,t)});return this},Pl.transition=function(){for(var n,t,e,r,u=this.id,i=++Ul,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],s=0,f=t.length;f>s;s++)(e=t[s])&&(r=e[o][u],$o(e,s,o,i,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Yo(a,o,i)},ta.svg.axis=function(){function n(n){n.each(function(){var n,l=ta.select(this),s=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):y:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=ta.transition(p.exit()).style("opacity",Ca).remove(),m=ta.transition(p.order()).style("opacity",1),M=Math.max(u,0)+o,x=Ui(f),b=l.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ta.transition(b));v.append("line"),v.append("text");var w,S,k,E,A=v.select("line"),N=m.select("line"),C=p.select("text").text(g),z=v.select("text"),q=m.select("text"),L="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=Bo,w="x",k="y",S="x2",E="y2",C.attr("dy",0>L?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+L*i+"V0H"+x[1]+"V"+L*i)):(n=Wo,w="y",k="x",S="y2",E="x2",C.attr("dy",".32em").style("text-anchor",0>L?"end":"start"),_.attr("d","M"+L*i+","+x[0]+"H0V"+x[1]+"H"+L*i)),A.attr(E,L*u),z.attr(k,L*M),N.attr(S,0).attr(E,L*u),q.attr(w,0).attr(k,L*M),f.rangeBand){var T=f,R=T.rangeBand()/2;s=f=function(n){return T(n)+R}}else s.rangeBand?s=f:d.call(n,f,s);v.call(n,s,f),m.call(n,f,f)})}var t,e=ta.scale.linear(),r=jl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Fl?t+"":jl,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var jl="bottom",Fl={top:1,right:1,bottom:1,left:1};ta.svg.brush=function(){function n(t){t.each(function(){var t=ta.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",i).on("touchstart.brush",i),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,y);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Hl[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var c,f=ta.transition(t),h=ta.transition(o);l&&(c=Ui(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),r(f)),s&&(c=Ui(s),h.attr("y",c[0]).attr("height",c[1]-c[0]),u(f)),e(f)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",f[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1]-f[0])}function u(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function i(){function i(){32==ta.event.keyCode&&(C||(M=null,q[0]-=f[1],q[1]-=h[1],C=2),S())}function v(){32==ta.event.keyCode&&2==C&&(q[0]+=f[1],q[1]+=h[1],C=0,S())}function d(){var n=ta.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ta.event.altKey?(M||(M=[(f[0]+f[1])/2,(h[0]+h[1])/2]),q[0]=f[+(n[0]s?(u=r,r=s):u=s),v[0]!=r||v[1]!=u?(e?a=null:o=null,v[0]=r,v[1]=u,!0):void 0}function y(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ta.select("body").style("cursor",null),L.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ta.select(ta.event.target),w=c.of(b,arguments),k=ta.select(b),E=_.datum(),A=!/^(n|s)$/.test(E)&&l,N=!/^(e|w)$/.test(E)&&s,C=_.classed("extent"),z=W(b),q=ta.mouse(b),L=ta.select(t(b)).on("keydown.brush",i).on("keyup.brush",v);if(ta.event.changedTouches?L.on("touchmove.brush",d).on("touchend.brush",y):L.on("mousemove.brush",d).on("mouseup.brush",y),k.interrupt().selectAll("*").interrupt(),C)q[0]=f[0]-q[0],q[1]=h[0]-q[1];else if(E){var T=+/w$/.test(E),R=+/^n/.test(E);x=[f[1-T]-q[0],h[1-R]-q[1]],q[0]=f[T],q[1]=h[R]}else ta.event.altKey&&(M=q.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ta.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=E(n,"brushstart","brush","brushend"),l=null,s=null,f=[0,0],h=[0,0],g=!0,p=!0,v=Ol[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:f,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Tl?ta.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,f=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=yu(f,t.x),r=yu(h,t.y);return o=a=null,function(u){f=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(l=t,v=Ol[!l<<1|!s],n):l},n.y=function(t){return arguments.length?(s=t,v=Ol[!l<<1|!s],n):s},n.clamp=function(t){return arguments.length?(l&&s?(g=!!t[0],p=!!t[1]):l?g=!!t:s&&(p=!!t),n):l&&s?[g,p]:l?g:s?p:null},n.extent=function(t){var e,r,u,i,c;return arguments.length?(l&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),o=[e,r],l.invert&&(e=l(e),r=l(r)),e>r&&(c=e,e=r,r=c),(e!=f[0]||r!=f[1])&&(f=[e,r])),s&&(u=t[0],i=t[1],l&&(u=u[1],i=i[1]),a=[u,i],s.invert&&(u=s(u),i=s(i)),u>i&&(c=u,u=i,i=c),(u!=h[0]||i!=h[1])&&(h=[u,i])),n):(l&&(o?(e=o[0],r=o[1]):(e=f[0],r=f[1],l.invert&&(e=l.invert(e),r=l.invert(r)),e>r&&(c=e,e=r,r=c))),s&&(a?(u=a[0],i=a[1]):(u=h[0],i=h[1],s.invert&&(u=s.invert(u),i=s.invert(i)),u>i&&(c=u,u=i,i=c))),l&&s?[[e,u],[r,i]]:l?[e,r]:s&&[u,i])},n.clear=function(){return n.empty()||(f=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&f[0]==f[1]||!!s&&h[0]==h[1]},ta.rebind(n,c,"on")};var Hl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Ol=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Il=ac.format=gc.timeFormat,Yl=Il.utc,Zl=Yl("%Y-%m-%dT%H:%M:%S.%LZ");Il.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Jo:Zl,Jo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Jo.toString=Zl.toString,ac.second=Ft(function(n){return new cc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ac.seconds=ac.second.range,ac.seconds.utc=ac.second.utc.range,ac.minute=Ft(function(n){return new cc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ac.minutes=ac.minute.range,ac.minutes.utc=ac.minute.utc.range,ac.hour=Ft(function(n){var t=n.getTimezoneOffset()/60;return new cc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ac.hours=ac.hour.range,ac.hours.utc=ac.hour.utc.range,ac.month=Ft(function(n){return n=ac.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ac.months=ac.month.range,ac.months.utc=ac.month.utc.range;var Vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Xl=[[ac.second,1],[ac.second,5],[ac.second,15],[ac.second,30],[ac.minute,1],[ac.minute,5],[ac.minute,15],[ac.minute,30],[ac.hour,1],[ac.hour,3],[ac.hour,6],[ac.hour,12],[ac.day,1],[ac.day,2],[ac.week,1],[ac.month,1],[ac.month,3],[ac.year,1]],$l=Il.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Ne]]),Bl={range:function(n,t,e){return ta.range(Math.ceil(n/e)*e,+t,e).map(Ko)},floor:y,ceil:y};Xl.year=ac.year,ac.scale=function(){return Go(ta.scale.linear(),Xl,$l)};var Wl=Xl.map(function(n){return[n[0].utc,n[1]]}),Jl=Yl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Ne]]);Wl.year=ac.year.utc,ac.scale.utc=function(){return Go(ta.scale.linear(),Wl,Jl)},ta.text=At(function(n){return n.responseText}),ta.json=function(n,t){return Nt(n,"application/json",Qo,t)},ta.html=function(n,t){return Nt(n,"text/html",na,t)},ta.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(ta):"object"==typeof module&&module.exports&&(module.exports=ta),this.d3=ta}(); diff --git a/modules/http/static/d3.spdx b/modules/http/static/d3.spdx new file mode 100644 index 0000000..95477b1 --- /dev/null +++ b/modules/http/static/d3.spdx @@ -0,0 +1,12 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: d3js +DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-d849c611-6d18-4c73-9318-f01eab74a036 + +PackageName: d3js +PackageVersion: 3.5.6 +PackageDownloadLocation: https://github.com/d3/d3/releases/download/v3.5.6/d3.zip#d3.min.js +PackageChecksum: SHA256: 3865a5ee7b9f91126f2ef1121a7635e57bd820c9dbc384c2c48626b93a13d3f6 +PackageOriginator: Person: Michael Bostock (mike@ocks.org) +PackageLicenseDeclared: BSD-3-Clause diff --git a/modules/http/static/datamaps.world.min.js b/modules/http/static/datamaps.world.min.js new file mode 100644 index 0000000..39e0e0a --- /dev/null +++ b/modules/http/static/datamaps.world.min.js @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: MIT */ +!function(){function a(a,b,c){return this.svg=m.select(a).append("svg").attr("width",c||a.offsetWidth).attr("data-width",c||a.offsetWidth).attr("class","datamap").attr("height",b||a.offsetHeight).style("overflow","hidden"),this.options.responsive&&(m.select(this.options.element).style({position:"relative","padding-bottom":"60%"}),m.select(this.options.element).select("svg").style({position:"absolute",width:"100%",height:"100%"}),m.select(this.options.element).select("svg").select("g").selectAll("path").style("vector-effect","non-scaling-stroke")),this.svg}function b(a,b){var c,d,e=b.width||a.offsetWidth,f=b.height||a.offsetHeight,g=this.svg;return b&&"undefined"==typeof b.scope&&(b.scope="world"),"usa"===b.scope?c=m.geo.albersUsa().scale(e).translate([e/2,f/2]):"world"===b.scope&&(c=m.geo[b.projection]().scale((e+1)/2/Math.PI).translate([e/2,f/("mercator"===b.projection?1.45:1.8)])),"orthographic"===b.projection&&(g.append("defs").append("path").datum({type:"Sphere"}).attr("id","sphere").attr("d",d),g.append("use").attr("class","stroke").attr("xlink:href","#sphere"),g.append("use").attr("class","fill").attr("xlink:href","#sphere"),c.scale(250).clipAngle(90).rotate(b.projectionConfig.rotation)),d=m.geo.path().projection(c),{path:d,projection:c}}function c(){m.select(".datamaps-style-block").empty()&&m.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 d(a){var b=this.options.fills,c=this.options.data||{},d=this.options.geographyConfig,e=this.svg.select("g.datamaps-subunits");e.empty()&&(e=this.addLayer("datamaps-subunits",null,!0));var f=n.feature(a,a.objects[this.options.scope]).features;d.hideAntarctica&&(f=f.filter(function(a){return"ATA"!==a.id}));var g=e.selectAll("path.datamaps-subunit").data(f);g.enter().append("path").attr("d",this.path).attr("class",function(a){return"datamaps-subunit "+a.id}).attr("data-info",function(a){return JSON.stringify(c[a.id])}).style("fill",function(a){var d;return c[a.id]&&(d=b[c[a.id].fillKey]),d||b.defaultFill}).style("stroke-width",d.borderWidth).style("stroke",d.borderColor)}function e(){function a(){this.parentNode.appendChild(this)}var b=this.svg,c=this,d=this.options.geographyConfig;(d.highlightOnHover||d.popupOnHover)&&b.selectAll(".datamaps-subunit").on("mouseover",function(e){var f=m.select(this);if(d.highlightOnHover){var g={fill:f.style("fill"),stroke:f.style("stroke"),"stroke-width":f.style("stroke-width"),"fill-opacity":f.style("fill-opacity")};f.style("fill",d.highlightFillColor).style("stroke",d.highlightBorderColor).style("stroke-width",d.highlightBorderWidth).style("fill-opacity",d.highlightFillOpacity).attr("data-previousAttributes",JSON.stringify(g)),/((MSIE)|(Trident))/.test||a.call(this)}d.popupOnHover&&c.updatePopup(f,e,d,b)}).on("mouseout",function(){var a=m.select(this);if(d.highlightOnHover){var b=JSON.parse(a.attr("data-previousAttributes"));for(var c in b)a.style(c,b[c])}a.on("mousemove",null),m.selectAll(".datamaps-hoverover").style("display","none")})}function f(a,b){if(b=b||{},this.options.fills){var c="
",d="";b.legendTitle&&(c="

"+b.legendTitle+"

"+c);for(var e in this.options.fills){if("defaultFill"===e){if(!b.defaultFillName)continue;d=b.defaultFillName}else d=b.labels&&b.labels[e]?b.labels[e]:e+": ";c+="
"+d+"
",c+='
 
'}c+="
",m.select(this.options.element).append("div").attr("class","datamaps-legend").html(c)}}function g(){var a=m.geo.graticule();this.svg.insert("path",".datamaps-subunits").datum(a).attr("class","datamaps-graticule").attr("d",this.path)}function h(a,b,c){var d=this;if(this.svg,!b||b&&!b.slice)throw"Datamaps Error - arcs must be an array";"undefined"==typeof c&&(c=o.arcConfig);var e=a.selectAll("path.datamaps-arc").data(b,JSON.stringify),f=m.geo.path().projection(d.projection),g=m.geo.greatArc().source(function(a){return[a.origin.longitude,a.origin.latitude]}).target(function(a){return[a.destination.longitude,a.destination.latitude]});e.enter().append("svg:path").attr("class","datamaps-arc").style("stroke-linecap","round").style("stroke",function(a){return a.options&&a.options.strokeColor?a.options.strokeColor:c.strokeColor}).style("fill","none").style("stroke-width",function(a){return a.options&&a.options.strokeWidth?a.options.strokeWidth:c.strokeWidth}).attr("d",function(a){var b=d.latLngToXY(a.origin.latitude,a.origin.longitude),e=d.latLngToXY(a.destination.latitude,a.destination.longitude),h=[(b[0]+e[0])/2,(b[1]+e[1])/2];return c.greatArc?f(g(a)):"M"+b[0]+","+b[1]+"S"+(h[0]+50*c.arcSharpness)+","+(h[1]-75*c.arcSharpness)+","+e[0]+","+e[1]}).transition().delay(100).style("fill",function(){var a=this.getTotalLength();return this.style.transition=this.style.WebkitTransition="none",this.style.strokeDasharray=a+" "+a,this.style.strokeDashoffset=a,this.getBoundingClientRect(),this.style.transition=this.style.WebkitTransition="stroke-dashoffset "+c.animationSpeed+"ms ease-out",this.style.strokeDashoffset="0","none"}),e.exit().transition().style("opacity",0).remove()}function i(a,b){var c=this;b=b||{};var d=this.projection([-67.707617,42.722131]);this.svg.selectAll(".datamaps-subunit").attr("data-foo",function(e){var f=c.path.centroid(e),g=7.5,h=5;["FL","KY","MI"].indexOf(e.id)>-1&&(g=-2.5),"NY"===e.id&&(g=-1),"MI"===e.id&&(h=18),"LA"===e.id&&(g=13);var i,j;i=f[0]-g,j=f[1]+h;var k=["VT","NH","MA","RI","CT","NJ","DE","MD","DC"].indexOf(e.id);if(k>-1){var l=d[1];i=d[0],j=l+k*(2+(b.fontSize||12)),a.append("line").attr("x1",i-3).attr("y1",j-5).attr("x2",f[0]).attr("y2",f[1]).style("stroke",b.labelColor||"#000").style("stroke-width",b.lineWidth||1)}return a.append("text").attr("x",i).attr("y",j).style("font-size",(b.fontSize||10)+"px").style("font-family",b.fontFamily||"Verdana").style("fill",b.labelColor||"#000").text(e.id),"bar"})}function j(a,b,c){function d(a){return"undefined"!=typeof a&&"undefined"!=typeof a.latitude&&"undefined"!=typeof a.longitude}var e=this,f=this.options.fills,g=this.svg;if(!b||b&&!b.slice)throw"Datamaps Error - bubbles must be an array";var h=a.selectAll("circle.datamaps-bubble").data(b,JSON.stringify);h.enter().append("svg:circle").attr("class","datamaps-bubble").attr("cx",function(a){var b;return d(a)?b=e.latLngToXY(a.latitude,a.longitude):a.centered&&(b=e.path.centroid(g.select("path."+a.centered).data()[0])),b?b[0]:void 0}).attr("cy",function(a){var b;return d(a)?b=e.latLngToXY(a.latitude,a.longitude):a.centered&&(b=e.path.centroid(g.select("path."+a.centered).data()[0])),b?b[1]:void 0}).attr("r",0).attr("data-info",function(a){return JSON.stringify(a)}).style("stroke",function(a){return"undefined"!=typeof a.borderColor?a.borderColor:c.borderColor}).style("stroke-width",function(a){return"undefined"!=typeof a.borderWidth?a.borderWidth:c.borderWidth}).style("fill-opacity",function(a){return"undefined"!=typeof a.fillOpacity?a.fillOpacity:c.fillOpacity}).style("fill",function(a){var b=f[a.fillKey];return b||f.defaultFill}).on("mouseover",function(a){var b=m.select(this);if(c.highlightOnHover){var d={fill:b.style("fill"),stroke:b.style("stroke"),"stroke-width":b.style("stroke-width"),"fill-opacity":b.style("fill-opacity")};b.style("fill",c.highlightFillColor).style("stroke",c.highlightBorderColor).style("stroke-width",c.highlightBorderWidth).style("fill-opacity",c.highlightFillOpacity).attr("data-previousAttributes",JSON.stringify(d))}c.popupOnHover&&e.updatePopup(b,a,c,g)}).on("mouseout",function(){var a=m.select(this);if(c.highlightOnHover){var b=JSON.parse(a.attr("data-previousAttributes"));for(var d in b)a.style(d,b[d])}m.selectAll(".datamaps-hoverover").style("display","none")}).transition().duration(400).attr("r",function(a){return a.radius}),h.exit().transition().delay(c.exitDelay).attr("r",0).remove()}function k(a){return Array.prototype.slice.call(arguments,1).forEach(function(b){if(b)for(var c in b)null==a[c]&&(a[c]=b[c])}),a}function l(b){if("undefined"==typeof m||"undefined"==typeof n)throw new Error("Include d3.js (v3.0.3 or greater) and topojson on this page before creating a new map");return this.options=k(b,o),this.options.geographyConfig=k(b.geographyConfig,o.geographyConfig),this.options.projectionConfig=k(b.projectionConfig,o.projectionConfig),this.options.bubblesConfig=k(b.bubblesConfig,o.bubblesConfig),this.options.arcConfig=k(b.arcConfig,o.arcConfig),m.select(this.options.element).select("svg").length>0&&a.call(this,this.options.element,this.options.height,this.options.width),this.addPlugin("bubbles",j),this.addPlugin("legend",f),this.addPlugin("arc",h),this.addPlugin("labels",i),this.addPlugin("graticule",g),this.options.disableDefaultStyles||c(),this.draw()}var m=window.d3,n=window.topojson,o={scope:"world",responsive:!1,setProjection:b,projection:"equirectangular",dataType:"json",done:function(){},fills:{defaultFill:"#ABDDA4"},geographyConfig:{dataUrl:null,hideAntarctica:!0,borderWidth:1,borderColor:"#FDFDFD",popupTemplate:function(a){return'
'+a.properties.name+"
"},popupOnHover:!0,highlightOnHover:!0,highlightFillColor:"#FC8D59",highlightBorderColor:"rgba(250, 15, 160, 0.2)",highlightBorderWidth:2},projectionConfig:{rotation:[97,0]},bubblesConfig:{borderWidth:2,borderColor:"#FFFFFF",popupOnHover:!0,popupTemplate:function(a,b){return'
'+b.name+"
"},fillOpacity:.75,animate:!0,highlightOnHover:!0,highlightFillColor:"#FC8D59",highlightBorderColor:"rgba(250, 15, 160, 0.2)",highlightBorderWidth:2,highlightFillOpacity:.85,exitDelay:100},arcConfig:{strokeColor:"#DD1C77",strokeWidth:1,arcSharpness:1,animationSpeed:600}};l.prototype.resize=function(){var a=this,b=a.options;if(b.responsive){var c="-webkit-transform"in document.body.style?"-webkit-":"-moz-transform"in document.body.style?"-moz-":"-ms-transform"in document.body.style?"-ms-":"",d=b.element.clientWidth,e=m.select(b.element).select("svg").attr("data-width");m.select(b.element).select("svg").selectAll("g").style(c+"transform","scale("+d/e+")")}},l.prototype.draw=function(){function a(a){b.options.dataUrl&&m[b.options.dataType](b.options.dataUrl,function(a){if("csv"===b.options.dataType&&a&&a.slice){for(var c={},d=0;d1)for(var a=1;a=0){var d=t[l-e];null===d[1]||isNaN(d[1])||(n-=d[2][0],o-=d[1],r-=d[2][1],s-=1)}u[l]=s?[t[l][0],1*o/s,[1*n/s,1*r/s]]:[t[l][0],null,[null,null]]}return u},a.default=r,e.exports=a.default},{"./bars":5}],3:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./bars"),n=function(t){return t&&t.__esModule?t:{default:t}}(i),r=function(){};r.prototype=new n.default,r.prototype.extractSeries=function(t,e,a){for(var i,n,r,o,s=[],l=a.get("sigma"),h=a.get("logscale"),u=0;u=0&&(u-=t[r-e][2][2],d-=t[r-e][2][3]);var c=t[r][0],p=d?u/d:0;if(h)if(d){var g=p<0?0:p,f=d,_=l*Math.sqrt(g*(1-g)/f+l*l/(4*f*f)),v=1+l*l/d;i=(g+l*l/(2*d)-_)/v,n=(g+l*l/(2*d)+_)/v,s[r]=[c,100*g,[100*i,100*n]]}else s[r]=[c,0,[0,0]];else o=d?l*Math.sqrt(p*(1-p)/d):1,s[r]=[c,100*p,[100*(p-o),100*(p+o)]]}return s},a.default=r,e.exports=a.default},{"./bars":5}],5:[function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(a,"__esModule",{value:!0});var n=t("./datahandler"),r=i(n),o=t("../dygraph-layout"),s=i(o),l=function(){r.default.call(this)};l.prototype=new r.default,l.prototype.extractSeries=function(t,e,a){},l.prototype.rollingAverage=function(t,e,a){},l.prototype.onPointsCreated_=function(t,e){for(var a=0;ai&&(l=i),hr)&&(r=h),(null===n||l=0&&(r-=t[i-e][2][0],o-=t[i-e][2][1]);var s=t[i][0],l=o?r/o:0;n[i]=[s,100*l]}return n},a.default=s,e.exports=a.default},{"./datahandler":6,"./default":8}],8:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./datahandler"),n=function(t){return t&&t.__esModule?t:{default:t}}(i),r=function(){};r.prototype=new n.default,r.prototype.extractSeries=function(t,e,a){for(var i=[],n=a.get("logscale"),r=0;rr)&&(r=i),(null===n||i=2,_=t.drawingContext;_.save(),f&&_.setLineDash&&_.setLineDash(i);var v=s._drawSeries(t,g,a,l,r,d,u,e);s._drawPointsOnLine(t,v,o,e,l),f&&_.setLineDash&&_.setLineDash([]),_.restore()},s._drawSeries=function(t,e,a,i,n,r,o,s){var l,h,u=null,d=null,c=null,p=[],g=!0,f=t.drawingContext;f.beginPath(),f.strokeStyle=s,f.lineWidth=a;for(var _=e.array_,v=e.end_,y=e.predicate_,x=e.start_;x0;a--){var i=e[a];if(2==i[0]){var n=e[a-1];n[1]==i[1]&&n[2]==i[2]&&e.splice(a,1)}}for(var a=0;a2&&!t){var r=0;2==e[0][0]&&r++;for(var o=null,s=null,a=r;ae[s][2]&&(s=a)}}var h=e[o],u=e[s];e.splice(r,e.length-r),os?(e.push(u),e.push(h)):e.push(h)}}},o=function(a){r(a);for(var o=0,s=e.length;o1,h=s-a>1;o(l||h),a=s}e.push([t,n,r])};return{moveTo:function(t,e){s(2,t,e)},lineTo:function(t,e){s(1,t,e)},stroke:function(){o(!0),t.stroke()},fill:function(){o(!0),t.fill()},beginPath:function(){o(!0),t.beginPath()},closePath:function(){o(!0),t.closePath()},_count:function(){return n}}},s._fillPlotter=function(t){if(!t.singleSeriesName&&0===t.seriesIndex){for(var e=t.dygraph,a=e.getLabels().slice(1),i=a.length;i>=0;i--)e.visibility()[i]||a.splice(i,1);if(function(){for(var t=0;t=0;n--){var r=i[n];t.lineTo(r[0],r[1])}},_=d-1;_>=0;_--){var v=t.drawingContext,y=a[_];if(e.getBooleanOption("fillGraph",y)){var x=e.getNumericOption("fillAlpha",y),m=e.getBooleanOption("stepPlot",y),b=p[_],w=e.axisPropertiesForSeries(y),A=1+w.minyval*w.yscale;A<0?A=0:A>1&&(A=1),A=h.h*A+h.y;var O,D=u[_],E=n.createIterator(D,0,D.length,s._getIteratorPredicate(e.getBooleanOption("connectSeparatedPoints",y))),L=NaN,T=[-1,-1],S=n.toRGB_(b),P="rgba("+S.r+","+S.g+","+S.b+","+x+")";v.fillStyle=P,v.beginPath();var C,M=!0;(D.length>2*e.width_||o.default.FORCE_FAST_PROXY)&&(v=s._fastCanvasProxy(v));for(var N,F=[];E.hasNext;)if(N=E.next(),n.isOK(N.y)||m){if(c){if(!M&&C==N.xval)continue;M=!1,C=N.xval,r=g[N.canvasx];var k;k=void 0===r?A:l?r[0]:r,O=[N.canvasy,k],m?-1===T[0]?g[N.canvasx]=[N.canvasy,A]:g[N.canvasx]=[N.canvasy,T[0]]:g[N.canvasx]=N.canvasy}else O=isNaN(N.canvasy)&&m?[h.y+h.h,A]:[N.canvasy,A];isNaN(L)?(v.moveTo(N.canvasx,O[1]),v.lineTo(N.canvasx,O[0])):(m?(v.lineTo(N.canvasx,T[0]),v.lineTo(N.canvasx,O[0])):v.lineTo(N.canvasx,O[0]),c&&(F.push([L,T[1]]),l&&r?F.push([N.canvasx,r[1]]):F.push([N.canvasx,O[1]]))),T=O,L=N.canvasx}else f(v,L,T[1],F),F=[],L=NaN,null===N.y_stacked||isNaN(N.y_stacked)||(g[N.canvasx]=h.h*N.y_stacked+h.y);l=m,O&&N&&(f(v,N.canvasx,O[1],F),F=[]),v.fill()}}}},a.default=s,e.exports=a.default},{"./dygraph":18,"./dygraph-utils":17}],10:[function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function n(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}Object.defineProperty(a,"__esModule",{value:!0});var r=t("./dygraph-tickers"),o=n(r),s=t("./dygraph-interaction-model"),l=i(s),h=t("./dygraph-canvas"),u=i(h),d=t("./dygraph-utils"),c=n(d),p={highlightCircleSize:3,highlightSeriesOpts:null,highlightSeriesBackgroundAlpha:.5,highlightSeriesBackgroundColor:"rgb(255, 255, 255)",labelsSeparateLines:!1,labelsShowZeroValues:!0,labelsKMB:!1,labelsKMG2:!1,showLabelsOnHighlight:!0,digitsAfterDecimal:2,maxNumberWidth:6,sigFigs:null,strokeWidth:1,strokeBorderWidth:0,strokeBorderColor:"white",axisTickSize:3,axisLabelFontSize:14,rightGap:5,showRoller:!1,xValueParser:void 0,delimiter:",",sigma:2,errorBars:!1,fractions:!1,wilsonInterval:!0,customBars:!1,fillGraph:!1,fillAlpha:.15,connectSeparatedPoints:!1,stackedGraph:!1,stackedGraphNaNFill:"all",hideOverlayOnMouseOut:!0,legend:"onmouseover",stepPlot:!1,xRangePad:0,yRangePad:null,drawAxesAtZero:!1,titleHeight:28,xLabelHeight:18,yLabelWidth:18,axisLineColor:"black",axisLineWidth:.3,gridLineWidth:.3,axisLabelWidth:50,gridLineColor:"rgb(128,128,128)",interactionModel:l.default.defaultModel,animatedZooms:!1,showRangeSelector:!1,rangeSelectorHeight:40,rangeSelectorPlotStrokeColor:"#808FAB",rangeSelectorPlotFillGradientColor:"white",rangeSelectorPlotFillColor:"#A7B1C4",rangeSelectorBackgroundStrokeColor:"gray",rangeSelectorBackgroundLineWidth:1,rangeSelectorPlotLineWidth:1.5,rangeSelectorForegroundStrokeColor:"black",rangeSelectorForegroundLineWidth:1,rangeSelectorAlpha:.6,showInRangeSelector:null,plotter:[u.default._fillPlotter,u.default._errorPlotter,u.default._linePlotter],plugins:[],axes:{x:{pixelsPerLabel:70,axisLabelWidth:60,axisLabelFormatter:c.dateAxisLabelFormatter,valueFormatter:c.dateValueFormatter,drawGrid:!0,drawAxis:!0,independentTicks:!0,ticker:o.dateTicker},y:{axisLabelWidth:50,pixelsPerLabel:30,valueFormatter:c.numberValueFormatter,axisLabelFormatter:c.numberAxisLabelFormatter,drawGrid:!0,drawAxis:!0,independentTicks:!0,ticker:o.numericTicks},y2:{axisLabelWidth:50,pixelsPerLabel:30,valueFormatter:c.numberValueFormatter,axisLabelFormatter:c.numberAxisLabelFormatter,drawAxis:!0,drawGrid:!1,independentTicks:!1,ticker:o.numericTicks}}};a.default=p,e.exports=a.default},{"./dygraph-canvas":9,"./dygraph-interaction-model":12,"./dygraph-tickers":16,"./dygraph-utils":17}],11:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./dygraph"),n=function(t){return t&&t.__esModule?t:{default:t}}(i),r=function(t){this.container=t};r.prototype.draw=function(t,e){this.container.innerHTML="",void 0!==this.date_graph&&this.date_graph.destroy(),this.date_graph=new n.default(this.container,t,e)},r.prototype.setSelection=function(t){var e=!1;t.length&&(e=t[0].row),this.date_graph.setSelection(e)},r.prototype.getSelection=function(){var t=[],e=this.date_graph.getSelection();if(e<0)return t;for(var a=this.date_graph.layout_.points,i=0;ia.boundedDates[1]&&(i-=r-a.boundedDates[1],r=i+a.dateRange),e.getOptionForAxis("logscale","x")?e.dateWindow_=[Math.pow(n.LOG_SCALE,i),Math.pow(n.LOG_SCALE,r)]:e.dateWindow_=[i,r],a.is2DPan)for(var o=a.dragEndY-a.dragStartY,s=0;s=10&&a.dragDirection==n.HORIZONTAL){var o=Math.min(a.dragStartX,a.dragEndX),s=Math.max(a.dragStartX,a.dragEndX);o=Math.max(o,i.x),s=Math.min(s,i.x+i.w),o=10&&a.dragDirection==n.VERTICAL){var l=Math.min(a.dragStartY,a.dragEndY),h=Math.max(a.dragStartY,a.dragEndY);l=Math.max(l,i.y),h=Math.min(h,i.y+i.h),l1&&(a.startTimeForDoubleTapMs=null);for(var i=[],n=0;n=2){a.initialPinchCenter={pageX:.5*(i[0].pageX+i[1].pageX),pageY:.5*(i[0].pageY+i[1].pageY),dataX:.5*(i[0].dataX+i[1].dataX),dataY:.5*(i[0].dataY+i[1].dataY)};var o=180/Math.PI*Math.atan2(a.initialPinchCenter.pageY-i[0].pageY,i[0].pageX-a.initialPinchCenter.pageX);o=Math.abs(o),o>90&&(o=90-o),a.touchDirections={x:o<67.5,y:o>22.5}}a.initialRange={x:e.xAxisRange(),y:e.yAxisRange()}},r.moveTouch=function(t,e,a){a.startTimeForDoubleTapMs=null;var i,n=[];for(i=0;i=2){var g=s[1].pageX-l.pageX;c=(n[1].pageX-o.pageX)/g;var f=s[1].pageY-l.pageY;p=(n[1].pageY-o.pageY)/f}c=Math.min(8,Math.max(.125,c)),p=Math.min(8,Math.max(.125,p));var _=!1;if(a.touchDirections.x&&(e.dateWindow_=[l.dataX-h.dataX+(a.initialRange.x[0]-l.dataX)/c,l.dataX-h.dataX+(a.initialRange.x[1]-l.dataX)/c],_=!0),a.touchDirections.y)for(i=0;i<1;i++){var v=e.axes_[i],y=e.attributes_.getForAxis("logscale",i);y||(v.valueRange=[l.dataY-h.dataY+(a.initialRange.y[0]-l.dataY)/p,l.dataY-h.dataY+(a.initialRange.y[1]-l.dataY)/p],_=!0)}if(e.drawGraph_(!1),_&&n.length>1&&e.getFunctionOption("zoomCallback")){var x=e.xAxisRange();e.getFunctionOption("zoomCallback").call(e,x[0],x[1],e.yAxisRanges())}},r.endTouch=function(t,e,a){if(0!==t.touches.length)r.startTouch(t,e,a);else if(1==t.changedTouches.length){var i=(new Date).getTime(),n=t.changedTouches[0];a.startTimeForDoubleTapMs&&i-a.startTimeForDoubleTapMs<500&&a.doubleTapX&&Math.abs(a.doubleTapX-n.screenX)<50&&a.doubleTapY&&Math.abs(a.doubleTapY-n.screenY)<50?e.resetZoom():(a.startTimeForDoubleTapMs=i,a.doubleTapX=n.screenX,a.doubleTapY=n.screenY)}};var o=function(t,e,a){return ta?t-a:0},s=function(t,e){var a=n.findPos(e.canvas_),i={left:a.x,right:a.x+e.canvas_.offsetWidth,top:a.y,bottom:a.y+e.canvas_.offsetHeight},r={x:n.pageX(t),y:n.pageY(t)},s=o(r.x,i.left,i.right),l=o(r.y,i.top,i.bottom);return Math.max(s,l)};r.defaultModel={mousedown:function(t,e,a){if(!t.button||2!=t.button){a.initializeMouseDown(t,e,a),t.altKey||t.shiftKey?r.startPan(t,e,a):r.startZoom(t,e,a);var i=function(t){if(a.isZooming){s(t,e)<100?r.moveZoom(t,e,a):null!==a.dragEndX&&(a.dragEndX=null,a.dragEndY=null,e.clearZoomRect_())}else a.isPanning&&r.movePan(t,e,a)},o=function t(o){a.isZooming?null!==a.dragEndX?r.endZoom(o,e,a):r.maybeTreatMouseOpAsClick(o,e,a):a.isPanning&&r.endPan(o,e,a),n.removeEvent(document,"mousemove",i),n.removeEvent(document,"mouseup",t),a.destroy()};e.addAndTrackEvent(document,"mousemove",i),e.addAndTrackEvent(document,"mouseup",o)}},willDestroyContextMyself:!0,touchstart:function(t,e,a){r.startTouch(t,e,a)},touchmove:function(t,e,a){r.moveTouch(t,e,a)},touchend:function(t,e,a){r.endTouch(t,e,a)},dblclick:function(t,e,a){if(a.cancelNextDblclick)return void(a.cancelNextDblclick=!1);var i={canvasx:a.dragEndX,canvasy:a.dragEndY,cancelable:!0};e.cascadeEvents_("dblclick",i)||t.altKey||t.shiftKey||e.resetZoom()}},r.nonInteractiveModel_={mousedown:function(t,e,a){a.initializeMouseDown(t,e,a)},mouseup:r.maybeTreatMouseOpAsClick},r.dragIsPanInteractionModel={mousedown:function(t,e,a){a.initializeMouseDown(t,e,a),r.startPan(t,e,a)},mousemove:function(t,e,a){a.isPanning&&r.movePan(t,e,a)},mouseup:function(t,e,a){a.isPanning&&r.endPan(t,e,a)}},a.default=r,e.exports=a.default},{"./dygraph-utils":17}],13:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./dygraph-utils"),n=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(i),r=function(t){this.dygraph_=t,this.points=[],this.setNames=[],this.annotations=[],this.yAxes_=null,this.xTicks_=null,this.yTicks_=null};r.prototype.addDataset=function(t,e){this.points.push(e),this.setNames.push(t)},r.prototype.getPlotArea=function(){return this.area_},r.prototype.computePlotArea=function(){var t={x:0,y:0};t.w=this.dygraph_.width_-t.x-this.dygraph_.getOption("rightGap"),t.h=this.dygraph_.height_;var e={chart_div:this.dygraph_.graphDiv,reserveSpaceLeft:function(e){var a={x:t.x,y:t.y,w:e,h:t.h};return t.x+=e,t.w-=e,a},reserveSpaceRight:function(e){var a={x:t.x+t.w-e,y:t.y,w:e,h:t.h};return t.w-=e,a},reserveSpaceTop:function(e){var a={x:t.x,y:t.y,w:t.w,h:e};return t.y+=e,t.h-=e,a},reserveSpaceBottom:function(e){var a={x:t.x,y:t.y+t.h-e,w:t.w,h:e};return t.h-=e,a},chartRect:function(){return{x:t.x,y:t.y,w:t.w,h:t.h}}};this.dygraph_.cascadeEvents_("layout",e),this.area_=t},r.prototype.setAnnotations=function(t){this.annotations=[];for(var e=this.dygraph_.getOption("xValueParser")||function(t){return t},a=0;a=0&&i<1&&this.xticks.push({pos:i,label:a,has_tick:r});for(this.yticks=[],t=0;t0&&i<=1&&this.yticks.push({axis:t,pos:i,label:a,has_tick:r})},r.prototype._evaluateAnnotations=function(){var t,e={};for(t=0;t1&&o.update(this.yAxes_[1].options,s.y2||{}),o.update(this.xAxis_.options,s.x||{})}},u.prototype.get=function(t){var e=this.getGlobalUser_(t);return null!==e?e:this.getGlobalDefault_(t)},u.prototype.getGlobalUser_=function(t){return this.user_.hasOwnProperty(t)?this.user_[t]:null},u.prototype.getGlobalDefault_=function(t){return this.global_.hasOwnProperty(t)?this.global_[t]:l.default.hasOwnProperty(t)?l.default[t]:null},u.prototype.getForAxis=function(t,e){var a,i;if("number"==typeof e)a=e,i=0===a?"y":"y2";else{if("y1"==e&&(e="y"),"y"==e)a=0;else if("y2"==e)a=1;else{if("x"!=e)throw"Unknown axis "+e;a=-1}i=e}var n=-1==a?this.xAxis_:this.yAxes_[a];if(n){var r=n.options;if(r.hasOwnProperty(t))return r[t]}if("x"!==e||"logscale"!==t){var o=this.getGlobalUser_(t);if(null!==o)return o}var s=l.default.axes[i];return s.hasOwnProperty(t)?s[t]:this.getGlobalDefault_(t)},u.prototype.getForSeries=function(t,e){if(e===this.dygraph_.getHighlightSeries()&&this.highlightSeries_.hasOwnProperty(t))return this.highlightSeries_[t];if(!this.series_.hasOwnProperty(e))throw"Unknown series: "+e;var a=this.series_[e],i=a.options;return i.hasOwnProperty(t)?i[t]:this.getForAxis(t,a.yAxis)},u.prototype.numAxes=function(){return this.yAxes_.length},u.prototype.axisForSeries=function(t){return this.series_[t].yAxis},u.prototype.axisOptions=function(t){return this.yAxes_[t].options},u.prototype.seriesForAxis=function(t){return this.yAxes_[t].series},u.prototype.seriesNames=function(){return this.labels_},void 0!==i);a.default=u,e.exports=a.default}).call(this,t("_process"))},{"./dygraph-default-attrs":10,"./dygraph-options-reference":14,"./dygraph-utils":17,_process:1}],16:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("./dygraph-utils"),n=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(i),r=function(t,e,a,i,n,r){return o(t,e,a,function(t){return"logscale"!==t&&i(t)},n,r)};a.numericLinearTicks=r;var o=function(t,e,a,i,r,o){var s,l,h,u,c=i("pixelsPerLabel"),p=[];if(o)for(s=0;s=u/4){for(var v=f;v>=g;v--){var y=d[v],x=Math.log(y/t)/Math.log(e/t)*a,m={v:y};null===_?_={tickValue:y,pixel_coord:x}:Math.abs(x-_.pixel_coord)>=c?_={tickValue:y,pixel_coord:x}:m.label="",p.push(m)}p.reverse()}}if(0===p.length){var b,w,A=i("labelsKMG2");A?(b=[1,2,4,8,16,32,64,128,256],w=16):(b=[1,2,5,10,20,50,100],w=10);var O,D,E,L=Math.ceil(a/c),T=Math.abs(e-t)/L,S=Math.floor(Math.log(T)/Math.log(w)),P=Math.pow(w,S);for(l=0;lc));l++);for(D>E&&(O*=-1),s=0;s<=u;s++)h=D+s*O,p.push({v:h})}}var C=i("axisLabelFormatter");for(s=0;s=0?g(t,e,o,i,n):[]};a.dateTicker=s;var l={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};a.Granularity=l;var h={DATEFIELD_Y:0,DATEFIELD_M:1,DATEFIELD_D:2,DATEFIELD_HH:3,DATEFIELD_MM:4,DATEFIELD_SS:5,DATEFIELD_MS:6,NUM_DATEFIELDS:7},u=[];u[l.MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:1,spacing:1},u[l.TWO_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:2,spacing:2},u[l.FIVE_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:5,spacing:5},u[l.TEN_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:10,spacing:10},u[l.FIFTY_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:50,spacing:50},u[l.HUNDRED_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:100,spacing:100},u[l.FIVE_HUNDRED_MILLISECONDLY]={datefield:h.DATEFIELD_MS,step:500,spacing:500},u[l.SECONDLY]={datefield:h.DATEFIELD_SS,step:1,spacing:1e3},u[l.TWO_SECONDLY]={datefield:h.DATEFIELD_SS,step:2,spacing:2e3},u[l.FIVE_SECONDLY]={datefield:h.DATEFIELD_SS,step:5,spacing:5e3},u[l.TEN_SECONDLY]={datefield:h.DATEFIELD_SS,step:10,spacing:1e4},u[l.THIRTY_SECONDLY]={datefield:h.DATEFIELD_SS,step:30,spacing:3e4},u[l.MINUTELY]={datefield:h.DATEFIELD_MM,step:1,spacing:6e4},u[l.TWO_MINUTELY]={datefield:h.DATEFIELD_MM,step:2,spacing:12e4},u[l.FIVE_MINUTELY]={datefield:h.DATEFIELD_MM,step:5,spacing:3e5},u[l.TEN_MINUTELY]={datefield:h.DATEFIELD_MM,step:10,spacing:6e5},u[l.THIRTY_MINUTELY]={datefield:h.DATEFIELD_MM,step:30,spacing:18e5},u[l.HOURLY]={datefield:h.DATEFIELD_HH,step:1,spacing:36e5},u[l.TWO_HOURLY]={datefield:h.DATEFIELD_HH,step:2,spacing:72e5},u[l.SIX_HOURLY]={datefield:h.DATEFIELD_HH,step:6,spacing:216e5},u[l.DAILY]={datefield:h.DATEFIELD_D,step:1,spacing:864e5},u[l.TWO_DAILY]={datefield:h.DATEFIELD_D,step:2,spacing:1728e5},u[l.WEEKLY]={datefield:h.DATEFIELD_D,step:7,spacing:6048e5},u[l.MONTHLY]={datefield:h.DATEFIELD_M,step:1,spacing:2629817280},u[l.QUARTERLY]={datefield:h.DATEFIELD_M,step:3,spacing:216e5*365.2524},u[l.BIANNUAL]={datefield:h.DATEFIELD_M,step:6,spacing:432e5*365.2524},u[l.ANNUAL]={datefield:h.DATEFIELD_Y,step:1,spacing:864e5*365.2524},u[l.DECADAL]={datefield:h.DATEFIELD_Y,step:10,spacing:315578073600},u[l.CENTENNIAL]={datefield:h.DATEFIELD_Y,step:100,spacing:3155780736e3};var d=function(){for(var t=[],e=-39;e<=39;e++)for(var a=Math.pow(10,e),i=1;i<=9;i++){var n=a*i;t.push(n)}return t}(),c=function(t,e,a,i){for(var n=i("pixelsPerLabel"),r=0;r=n)return r}return-1},p=function(t,e,a){var i=u[a].spacing;return Math.round(1*(e-t)/i)},g=function(t,e,a,i,r){var o=i("axisLabelFormatter"),s=i("labelsUTC"),d=s?n.DateAccessorsUTC:n.DateAccessorsLocal,c=u[a].datefield,p=u[a].step,g=u[a].spacing,f=new Date(t),_=[];_[h.DATEFIELD_Y]=d.getFullYear(f),_[h.DATEFIELD_M]=d.getMonth(f),_[h.DATEFIELD_D]=d.getDate(f),_[h.DATEFIELD_HH]=d.getHours(f),_[h.DATEFIELD_MM]=d.getMinutes(f),_[h.DATEFIELD_SS]=d.getSeconds(f),_[h.DATEFIELD_MS]=d.getMilliseconds(f);var v=_[c]%p;a==l.WEEKLY&&(v=d.getDay(f)),_[c]-=v;for(var y=c+1;y=l.DAILY||d.getHours(m)%p==0)&&x.push({v:b,label:o.call(r,m,a,i,r)}),_[c]+=p,m=d.makeDate.apply(null,_),b=m.getTime();return x};a.getDateAxis=g},{"./dygraph-utils":17}],17:[function(t,e,a){"use strict";function i(t,e,a){t.removeEventListener(e,a,!1)}function n(t){return t=t||window.event,t.stopPropagation&&t.stopPropagation(),t.preventDefault&&t.preventDefault(),t.cancelBubble=!0,t.cancel=!0,t.returnValue=!1,!1}function r(t,e,a){var i,n,r;if(0===e)i=a,n=a,r=a;else{var o=Math.floor(6*t),s=6*t-o,l=a*(1-e),h=a*(1-e*s),u=a*(1-e*(1-s));switch(o){case 1:i=h,n=a,r=l;break;case 2:i=l,n=a,r=u;break;case 3:i=l,n=h,r=a;break;case 4:i=u,n=l,r=a;break;case 5:i=a,n=l,r=h;break;case 6:case 0:i=a,n=u,r=l}}return i=Math.floor(255*i+.5),n=Math.floor(255*n+.5),r=Math.floor(255*r+.5),"rgb("+i+","+n+","+r+")"}function o(t){var e=t.getBoundingClientRect(),a=window,i=document.documentElement;return{x:e.left+(a.pageXOffset||i.scrollLeft),y:e.top+(a.pageYOffset||i.scrollTop)}}function s(t){return!t.pageX||t.pageX<0?0:t.pageX}function l(t){return!t.pageY||t.pageY<0?0:t.pageY}function h(t,e){return s(t)-e.px}function u(t,e){return l(t)-e.py}function d(t){return!!t&&!isNaN(t)}function c(t,e){return!!t&&(null!==t.yval&&(null!==t.x&&void 0!==t.x&&(null!==t.y&&void 0!==t.y&&!(isNaN(t.x)||!e&&isNaN(t.y)))))}function p(t,e){var a=Math.min(Math.max(1,e||2),21);return Math.abs(t)<.001&&0!==t?t.toExponential(a-1):t.toPrecision(a)}function g(t){return t<10?"0"+t:""+t}function f(t,e,a,i){var n=g(t)+":"+g(e);if(a&&(n+=":"+g(a),i)){var r=""+i;n+="."+("000"+r).substring(r.length)}return n}function _(t,e){var a=e?tt:$,i=new Date(t),n=a.getFullYear(i),r=a.getMonth(i),o=a.getDate(i),s=a.getHours(i),l=a.getMinutes(i),h=a.getSeconds(i),u=a.getMilliseconds(i),d=""+n,c=g(r+1),p=g(o),_=3600*s+60*l+h+.001*u,v=d+"/"+c+"/"+p;return _&&(v+=" "+f(s,l,h,u)),v}function v(t,e){var a=Math.pow(10,e);return Math.round(t*a)/a}function y(t,e,a,i,n){for(var r=!0;r;){var o=t,s=e,l=a,h=i,u=n;if(r=!1,null!==h&&void 0!==h&&null!==u&&void 0!==u||(h=0,u=s.length-1),h>u)return-1;null!==l&&void 0!==l||(l=0);var d,c=function(t){return t>=0&&to){if(l>0&&(d=p-1,c(d)&&s[d]o))return p;t=o,e=s,a=l,i=p+1,n=u,r=!0,c=p=g=d=void 0}}}function x(t){var e,a;if((-1==t.search("-")||-1!=t.search("T")||-1!=t.search("Z"))&&(a=m(t))&&!isNaN(a))return a;if(-1!=t.search("-")){for(e=t.replace("-","/","g");-1!=e.search("-");)e=e.replace("-","/");a=m(e)}else 8==t.length?(e=t.substr(0,4)+"/"+t.substr(4,2)+"/"+t.substr(6,2),a=m(e)):a=m(t);return a&&!isNaN(a)||console.error("Couldn't parse "+t+" as a date"),a}function m(t){return new Date(t).getTime()}function b(t,e){if(void 0!==e&&null!==e)for(var a in e)e.hasOwnProperty(a)&&(t[a]=e[a]);return t}function w(t,e){if(void 0!==e&&null!==e)for(var a in e)e.hasOwnProperty(a)&&(null===e[a]?t[a]=null:A(e[a])?t[a]=e[a].slice():!function(t){return"object"==typeof Node?t instanceof Node:"object"==typeof t&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName}(e[a])&&"object"==typeof e[a]?("object"==typeof t[a]&&null!==t[a]||(t[a]={}),w(t[a],e[a])):t[a]=e[a]);return t}function A(t){var e=typeof t;return("object"==e||"function"==e&&"function"==typeof t.item)&&null!==t&&"number"==typeof t.length&&3!==t.nodeType}function O(t){return"object"==typeof t&&null!==t&&"function"==typeof t.getTime}function D(t){for(var e=[],a=0;a=e||et.call(window,function(){var e=(new Date).getTime(),h=e-o;n=r,r=Math.floor(h/a);var u=r-n;r+u>s||r>=s?(t(s),i()):(0!==u&&t(r),l())})}()}function C(t,e){var a={};if(t)for(var i=1;i=Math.pow(10,r)||Math.abs(t)=0;g--,c/=l)if(d>=c){i=v(t/c,n)+h[g];break}if(s){var f=String(t.toExponential()).split("e-");2===f.length&&f[1]>=3&&f[1]<=24&&(i=f[1]%3>0?v(f[0]/F(10,f[1]%3),n):Number(f[0]).toFixed(2),i+=u[Math.floor(f[1]/3)-1])}}return i}function X(t,e,a){return Y.call(this,t,a)}function V(t,e,a){var i=a("labelsUTC"),n=i?tt:$,r=n.getFullYear(t),o=n.getMonth(t),s=n.getDate(t),l=n.getHours(t),h=n.getMinutes(t),u=n.getSeconds(t),d=n.getMilliseconds(t);if(e>=G.Granularity.DECADAL)return""+r;if(e>=G.Granularity.MONTHLY)return lt[o]+" "+r;if(0===3600*l+60*h+u+.001*d||e>=G.Granularity.DAILY)return g(s)+" "+lt[o];if(eG.Granularity.MINUTELY?f(l,h,u,0):f(l,h,u,d)}function Z(t,e){return _(t,e("labelsUTC"))}Object.defineProperty(a,"__esModule",{value:!0}),a.removeEvent=i,a.cancelEvent=n,a.hsvToRGB=r,a.findPos=o,a.pageX=s,a.pageY=l,a.dragGetX_=h,a.dragGetY_=u,a.isOK=d,a.isValidPoint=c,a.floatFormat=p,a.zeropad=g,a.hmsString_=f,a.dateString_=_,a.round_=v,a.binarySearch=y,a.dateParser=x,a.dateStrToMillis=m,a.update=b,a.updateDeep=w,a.isArrayLike=A,a.isDateLike=O,a.clone=D,a.createCanvas=E,a.getContextPixelRatio=L,a.Iterator=T,a.createIterator=S,a.repeatAndCleanup=P,a.isPixelChangingOptionList=C,a.detectLineDelimiter=M,a.isNodeContainedBy=N,a.pow=F,a.toRGB_=R,a.isCanvasSupported=I,a.parseFloat_=H,a.numberValueFormatter=Y,a.numberAxisLabelFormatter=X,a.dateAxisLabelFormatter=V,a.dateValueFormatter=Z;var B=t("./dygraph-tickers"),G=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(B);a.LOG_SCALE=10;var W=Math.log(10);a.LN_TEN=W;var U=function(t){return Math.log(t)/W};a.log10=U;var z=function(t,e,a){var i=U(t),n=U(e),r=i+a*(n-i);return Math.pow(10,r)};a.logRangeFraction=z;var j=[2,2];a.DOTTED_LINE=j;var K=[7,3];a.DASHED_LINE=K;var q=[7,2,2,2];a.DOT_DASH_LINE=q;a.HORIZONTAL=1;a.VERTICAL=2;var Q=function(t){return t.getContext("2d")};a.getContext=Q;var J=function(t,e,a){t.addEventListener(e,a,!1)};a.addEvent=J;var $={getFullYear:function(t){return t.getFullYear()},getMonth:function(t){return t.getMonth()},getDate:function(t){return t.getDate()},getHours:function(t){return t.getHours()},getMinutes:function(t){return t.getMinutes()},getSeconds:function(t){return t.getSeconds()},getMilliseconds:function(t){return t.getMilliseconds()},getDay:function(t){return t.getDay()},makeDate:function(t,e,a,i,n,r,o){return new Date(t,e,a,i,n,r,o)}};a.DateAccessorsLocal=$;var tt={getFullYear:function(t){return t.getUTCFullYear()},getMonth:function(t){return t.getUTCMonth()},getDate:function(t){return t.getUTCDate()},getHours:function(t){return t.getUTCHours()},getMinutes:function(t){return t.getUTCMinutes()},getSeconds:function(t){return t.getUTCSeconds()},getMilliseconds:function(t){return t.getUTCMilliseconds()},getDay:function(t){return t.getUTCDay()},makeDate:function(t,e,a,i,n,r,o){return new Date(Date.UTC(t,e,a,i,n,r,o))}};a.DateAccessorsUTC=tt,T.prototype.next=function(){if(!this.hasNext)return null;for(var t=this.peek,e=this.nextIdx_+1,a=!1;e=0;n--){var r=i[n][0],o=i[n][1];if(o.call(r,a),a.propagationStopped)break}return a.defaultPrevented},Q.prototype.getPluginInstance_=function(t){for(var e=0;e=0;if(null===t||void 0===t)return e||a;if("y"===t)return a;throw new Error("axis parameter is ["+t+"] must be null, 'x' or 'y'.")},Q.prototype.toString=function(){var t=this.maindiv_;return"[Dygraph "+(t&&t.id?t.id:t)+"]"},Q.prototype.attr_=function(t,e){return e?this.attributes_.getForSeries(t,e):this.attributes_.get(t)},Q.prototype.getOption=function(t,e){return this.attr_(t,e)},Q.prototype.getNumericOption=function(t,e){return this.getOption(t,e)},Q.prototype.getStringOption=function(t,e){return this.getOption(t,e)},Q.prototype.getBooleanOption=function(t,e){return this.getOption(t,e)},Q.prototype.getFunctionOption=function(t,e){return this.getOption(t,e)},Q.prototype.getOptionForAxis=function(t,e){return this.attributes_.getForAxis(t,e)},Q.prototype.optionsViewForAxis_=function(t){var e=this;return function(a){var i=e.user_attrs_.axes;return i&&i[t]&&i[t].hasOwnProperty(a)?i[t][a]:("x"!==t||"logscale"!==a)&&(void 0!==e.user_attrs_[a]?e.user_attrs_[a]:(i=e.attrs_.axes,i&&i[t]&&i[t].hasOwnProperty(a)?i[t][a]:"y"==t&&e.axes_[0].hasOwnProperty(a)?e.axes_[0][a]:"y2"==t&&e.axes_[1].hasOwnProperty(a)?e.axes_[1][a]:e.attr_(a)))}},Q.prototype.rollPeriod=function(){return this.rollPeriod_},Q.prototype.xAxisRange=function(){return this.dateWindow_?this.dateWindow_:this.xAxisExtremes()},Q.prototype.xAxisExtremes=function(){var t=this.getNumericOption("xRangePad")/this.plotter_.area.w;if(0===this.numRows())return[0-t,1+t];var e=this.rawData_[0][0],a=this.rawData_[this.rawData_.length-1][0];if(t){var i=a-e;e-=i*t,a+=i*t}return[e,a]},Q.prototype.yAxisExtremes=function(){var t=this.gatherDatasets_(this.rolledSeries_,null),e=t.extremes,a=this.axes_;this.computeYAxisRanges_(e);var i=this.axes_;return this.axes_=a,i.map(function(t){return t.extremeRange})},Q.prototype.yAxisRange=function(t){if(void 0===t&&(t=0),t<0||t>=this.axes_.length)return null;var e=this.axes_[t];return[e.computedValueRange[0],e.computedValueRange[1]]},Q.prototype.yAxisRanges=function(){for(var t=[],e=0;ethis.rawData_.length?null:e<0||e>this.rawData_[t].length?null:this.rawData_[t][e]},Q.prototype.createInterface_=function(){var t=this.maindiv_;this.graphDiv=document.createElement("div"),this.graphDiv.style.textAlign="left",this.graphDiv.style.position="relative",t.appendChild(this.graphDiv),this.canvas_=x.createCanvas(),this.canvas_.style.position="absolute",this.hidden_=this.createPlotKitCanvas_(this.canvas_),this.canvas_ctx_=x.getContext(this.canvas_),this.hidden_ctx_=x.getContext(this.hidden_),this.resizeElements_(),this.graphDiv.appendChild(this.hidden_),this.graphDiv.appendChild(this.canvas_),this.mouseEventElement_=this.createMouseEventElement_(),this.layout_=new h.default(this);var e=this;this.mouseMoveHandler_=function(t){e.mouseMove_(t)},this.mouseOutHandler_=function(t){var a=t.target||t.fromElement,i=t.relatedTarget||t.toElement;x.isNodeContainedBy(a,e.graphDiv)&&!x.isNodeContainedBy(i,e.graphDiv)&&e.mouseOut_(t)},this.addAndTrackEvent(window,"mouseout",this.mouseOutHandler_),this.addAndTrackEvent(this.mouseEventElement_,"mousemove",this.mouseMoveHandler_),this.resizeHandler_||(this.resizeHandler_=function(t){e.resize()},this.addAndTrackEvent(window,"resize",this.resizeHandler_))},Q.prototype.resizeElements_=function(){this.graphDiv.style.width=this.width_+"px",this.graphDiv.style.height=this.height_+"px";var t=this.getNumericOption("pixelRatio"),e=t||x.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width=this.width_*e,this.canvas_.height=this.height_*e,this.canvas_.style.width=this.width_+"px",this.canvas_.style.height=this.height_+"px",1!==e&&this.canvas_ctx_.scale(e,e);var a=t||x.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width=this.width_*a,this.hidden_.height=this.height_*a,this.hidden_.style.width=this.width_+"px",this.hidden_.style.height=this.height_+"px",1!==a&&this.hidden_ctx_.scale(a,a)},Q.prototype.destroy=function(){this.canvas_ctx_.restore(),this.hidden_ctx_.restore();for(var t=this.plugins_.length-1;t>=0;t--){var e=this.plugins_.pop();e.plugin.destroy&&e.plugin.destroy()}this.removeTrackedEvents_(),x.removeEvent(window,"mouseout",this.mouseOutHandler_),x.removeEvent(this.mouseEventElement_,"mousemove",this.mouseMoveHandler_),x.removeEvent(window,"resize",this.resizeHandler_),this.resizeHandler_=null,function t(e){for(;e.hasChildNodes();)t(e.firstChild),e.removeChild(e.firstChild)}(this.maindiv_);var a=function(t){for(var e in t)"object"==typeof t[e]&&(t[e]=null)};a(this.layout_),a(this.plotter_),a(this)},Q.prototype.createPlotKitCanvas_=function(t){var e=x.createCanvas();return e.style.position="absolute",e.style.top=t.style.top,e.style.left=t.style.left, +e.width=this.width_,e.height=this.height_,e.style.width=this.width_+"px",e.style.height=this.height_+"px",e},Q.prototype.createMouseEventElement_=function(){return this.canvas_},Q.prototype.setColors_=function(){var t=this.getLabels(),e=t.length-1;this.colors_=[],this.colorsMap_={};for(var a=this.getNumericOption("colorSaturation")||1,i=this.getNumericOption("colorValue")||.5,n=Math.ceil(e/2),r=this.getOption("colors"),o=this.visibility(),s=0;s=0;--u)for(var d=this.layout_.points[u],c=0;c=l.length)){var h=l[s];if(x.isValidPoint(h)){var u=h.canvasy;if(t>h.canvasx&&s+10){var p=(t-h.canvasx)/c;u+=p*(d.canvasy-h.canvasy)}}}else if(t0){var g=l[s-1];if(x.isValidPoint(g)){var c=h.canvasx-g.canvasx;if(c>0){var p=(h.canvasx-t)/c;u+=p*(g.canvasy-h.canvasy)}}}(0===r||u=0){var r=0,o=this.attr_("labels");for(e=1;er&&(r=s)}var l=this.previousVerticalX_;a.clearRect(l-r-1,0,2*r+2,this.height_)}if(this.selPoints_.length>0){var h=this.selPoints_[0].canvasx;for(a.save(),e=0;e=0){t!=this.lastRow_&&(i=!0),this.lastRow_=t;for(var n=0;n=0&&o=0&&(i=!0),this.lastRow_=-1;return this.selPoints_.length?this.lastx_=this.selPoints_[0].xval:this.lastx_=-1,void 0!==e&&(this.highlightSet_!==e&&(i=!0),this.highlightSet_=e),void 0!==a&&(this.lockedSet_=a),i&&this.updateSelection_(void 0),i},Q.prototype.mouseOut_=function(t){this.getFunctionOption("unhighlightCallback")&&this.getFunctionOption("unhighlightCallback").call(this,t),this.getBooleanOption("hideOverlayOnMouseOut")&&!this.lockedSet_&&this.clearSelection()},Q.prototype.clearSelection=function(){if(this.cascadeEvents_("deselect",{}),this.lockedSet_=!1,this.fadeLevel)return void this.animateSelection_(-1);this.canvas_ctx_.clearRect(0,0,this.width_,this.height_),this.fadeLevel=0,this.selPoints_=[],this.lastx_=-1,this.lastRow_=-1,this.highlightSet_=null},Q.prototype.getSelection=function(){if(!this.selPoints_||this.selPoints_.length<1)return-1;for(var t=0;t1&&(a=this.dataHandler_.rollingAverage(a,this.rollPeriod_,this.attributes_)),this.rolledSeries_.push(a)}this.drawGraph_();var i=new Date;this.drawingTimeMs_=i-t},Q.PointType=void 0,Q.stackPoints_=function(t,e,a,i){for(var n=null,r=null,o=null,s=-1,l=0;l=e))for(var a=e;aa[1]&&(a[1]=c),c=1;a--)if(this.visibility()[a-1]){if(e){s=t[a];var p=e[0],g=e[1];for(n=null,r=null,i=0;i=p&&null===n&&(n=i),s[i][0]<=g&&(r=i);null===n&&(n=0);for(var f=n,_=!0;_&&f>0;)f--,_=null===s[f][1];null===r&&(r=s.length-1);var v=r;for(_=!0;_&&v0;){var n=this.readyFns_.pop();n(this)}},Q.prototype.computeYAxes_=function(){var t,e,a;for(this.axes_=[],t=0;t0&&(_=0),v<0&&(v=0)),_==1/0&&(_=0),v==-1/0&&(v=1),a=v-_,0===a&&(0!==v?a=Math.abs(v):(v=1,a=1));var m=v,b=_;e&&(u?(m=v+n*a,b=_):(m=v+n*a,b=_-n*a,b<0&&_>=0&&(b=0),m>0&&v<=0&&(m=0))),h.extremeRange=[b,m]}if(h.valueRange){var w=o(h.valueRange[0])?h.extremeRange[0]:h.valueRange[0],A=o(h.valueRange[1])?h.extremeRange[1]:h.valueRange[1];h.computedValueRange=[w,A]}else h.computedValueRange=h.extremeRange;if(!e)if(u){w=h.computedValueRange[0],A=h.computedValueRange[1];var O=n/(2*n-1),D=(n-1)/(2*n-1);h.computedValueRange[0]=x.logRangeFraction(w,A,O),h.computedValueRange[1]=x.logRangeFraction(w,A,D)}else w=h.computedValueRange[0],A=h.computedValueRange[1],a=A-w,h.computedValueRange[0]=w-a*n,h.computedValueRange[1]=A+a*n;if(c){h.independentTicks=c;var E=this.optionsViewForAxis_("y"+(l?"2":"")),L=E("ticker");h.ticks=L(h.computedValueRange[0],h.computedValueRange[1],this.plotter_.area.h,E,this),r||(r=h)}}if(void 0===r)throw'Configuration Error: At least one axis has to have the "independentTicks" option activated.';for(var l=0;l0&&"e"!=t[a-1]&&"E"!=t[a-1]||t.indexOf("/")>=0||isNaN(parseFloat(t))?e=!0:8==t.length&&t>"19700101"&&t<"20371231"&&(e=!0),this.setXAxisOptions_(e)},Q.prototype.setXAxisOptions_=function(t){t?(this.attrs_.xValueParser=x.dateParser,this.attrs_.axes.x.valueFormatter=x.dateValueFormatter,this.attrs_.axes.x.ticker=v.dateTicker,this.attrs_.axes.x.axisLabelFormatter=x.dateAxisLabelFormatter):(this.attrs_.xValueParser=function(t){return parseFloat(t)},this.attrs_.axes.x.valueFormatter=function(t){return t},this.attrs_.axes.x.ticker=v.numericTicks,this.attrs_.axes.x.axisLabelFormatter=this.attrs_.axes.x.valueFormatter)},Q.prototype.parseCSV_=function(t){var e,a,i=[],n=x.detectLineDelimiter(t),r=t.split(n||"\n"),o=this.getStringOption("delimiter");-1==r[0].indexOf(o)&&r[0].indexOf("\t")>=0&&(o="\t");var s=0;"labels"in this.user_attrs_||(s=1,this.attrs_.labels=r[0].split(o),this.attributes_.reparseSeries());for(var l,h=!1,u=this.attr_("labels").length,d=!1,c=s;c0&&f[0]0;)e=String.fromCharCode(65+(t-1)%26)+e.toLowerCase(),t=Math.floor((t-1)/26);return e}(g.length),y.text="";for(var m=0;m0&&f[0]0&&this.setAnnotations(g,!0),this.attributes_.reparseSeries()},Q.prototype.cascadeDataDidUpdateEvent_=function(){this.cascadeEvents_("dataDidUpdate",{})},Q.prototype.start_=function(){var t=this.file_;if("function"==typeof t&&(t=t()),x.isArrayLike(t))this.rawData_=this.parseArray_(t),this.cascadeDataDidUpdateEvent_(),this.predraw_();else if("object"==typeof t&&"function"==typeof t.getColumnRange)this.parseDataTable_(t),this.cascadeDataDidUpdateEvent_(),this.predraw_();else if("string"==typeof t){var e=x.detectLineDelimiter(t);if(e)this.loadedEvent_(t);else{var a;a=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");var i=this;a.onreadystatechange=function(){4==a.readyState&&(200!==a.status&&0!==a.status||i.loadedEvent_(a.responseText))},a.open("GET",t,!0),a.send(null)}}else console.error("Unknown data format: "+typeof t)},Q.prototype.updateOptions=function(t,e){void 0===e&&(e=!1);var a=t.file,i=Q.copyUserAttrs_(t);"rollPeriod"in i&&(this.rollPeriod_=i.rollPeriod),"dateWindow"in i&&(this.dateWindow_=i.dateWindow);var n=x.isPixelChangingOptionList(this.attr_("labels"),i);x.updateDeep(this.user_attrs_,i),this.attributes_.reparseSeries(),a?(this.cascadeEvents_("dataWillUpdate",{}),this.file_=a,e||this.start_()):e||(n?this.predraw_():this.renderGraph_(!1))},Q.copyUserAttrs_=function(t){var e={};for(var a in t)t.hasOwnProperty(a)&&"file"!=a&&t.hasOwnProperty(a)&&(e[a]=t[a]);return e},Q.prototype.resize=function(t,e){if(!this.resize_lock){this.resize_lock=!0,null===t!=(null===e)&&(console.warn("Dygraph.resize() should be called with zero parameters or two non-NULL parameters. Pretending it was zero."),t=e=null);var a=this.width_,i=this.height_;t?(this.maindiv_.style.width=t+"px",this.maindiv_.style.height=e+"px",this.width_=t,this.height_=e):(this.width_=this.maindiv_.clientWidth,this.height_=this.maindiv_.clientHeight),a==this.width_&&i==this.height_||(this.resizeElements_(),this.predraw_()),this.resize_lock=!1}},Q.prototype.adjustRoll=function(t){this.rollPeriod_=t,this.predraw_()},Q.prototype.visibility=function(){for(this.getOption("visibility")||(this.attrs_.visibility=[]);this.getOption("visibility").length=a.length?console.warn("Invalid series number in setVisibility: "+n):a[n]=t[n]);else for(var n=0;n=a.length?console.warn("Invalid series number in setVisibility: "+n):a[n]=t[n]:t[n]<0||t[n]>=a.length?console.warn("Invalid series number in setVisibility: "+t[n]):a[t[n]]=e;this.predraw_()},Q.prototype.size=function(){return{width:this.width_,height:this.height_}},Q.prototype.setAnnotations=function(t,e){if(this.annotations_=t,!this.layout_)return void console.warn("Tried to setAnnotations before dygraph was ready. Try setting them in a ready() block. See dygraphs.com/tests/annotation.html");this.layout_.setAnnotations(this.annotations_),e||this.predraw_()},Q.prototype.annotations=function(){return this.annotations_},Q.prototype.getLabels=function(){var t=this.attr_("labels");return t?t.slice():null},Q.prototype.indexFromSetName=function(t){return this.setIndexByName_[t]},Q.prototype.getRowForX=function(t){for(var e=0,a=this.numRows()-1;e<=a;){var i=a+e>>1,n=this.getValue(i,0);if(nt)a=i-1;else{if(e==i)return i;a=i}}return null},Q.prototype.ready=function(t){this.is_initial_draw_?this.readyFns_.push(t):t.call(this,this)},Q.prototype.addAndTrackEvent=function(t,e,a){x.addEvent(t,e,a),this.registeredEvents_.push({elem:t,type:e,fn:a})},Q.prototype.removeTrackedEvents_=function(){if(this.registeredEvents_)for(var t=0;tr.x+r.w||l.canvasyr.y+r.h)){var h=l.annotation,u=6;h.hasOwnProperty("tickHeight")&&(u=h.tickHeight);var d=document.createElement("div") +;d.style.fontSize=e.getOption("axisLabelFontSize")+"px";var c="dygraph-annotation";h.hasOwnProperty("icon")||(c+=" dygraphDefaultAnnotation dygraph-default-annotation"),h.hasOwnProperty("cssClass")&&(c+=" "+h.cssClass),d.className=c;var p=h.hasOwnProperty("width")?h.width:16,g=h.hasOwnProperty("height")?h.height:16;if(h.hasOwnProperty("icon")){var f=document.createElement("img");f.src=h.icon,f.width=p,f.height=g,d.appendChild(f)}else l.annotation.hasOwnProperty("shortText")&&d.appendChild(document.createTextNode(l.annotation.shortText));var _=l.canvasx-p/2;d.style.left=_+"px";var v=0;if(h.attachAtBottom){var y=r.y+r.h-g-u;o[_]?y-=o[_]:o[_]=0,o[_]+=u+g,v=y}else v=l.canvasy-g-u;d.style.top=v+"px",d.style.width=p+"px",d.style.height=g+"px",d.title=l.annotation.text,d.style.color=e.colorsMap_[l.name],d.style.borderColor=e.colorsMap_[l.name],h.div=d,e.addAndTrackEvent(d,"click",n("clickHandler","annotationClickHandler",l)),e.addAndTrackEvent(d,"mouseover",n("mouseOverHandler","annotationMouseOverHandler",l)),e.addAndTrackEvent(d,"mouseout",n("mouseOutHandler","annotationMouseOutHandler",l)),e.addAndTrackEvent(d,"dblclick",n("dblClickHandler","annotationDblClickHandler",l)),i.appendChild(d),this.annotations_.push(d);var x=t.drawingContext;if(x.save(),x.strokeStyle=h.hasOwnProperty("tickColor")?h.tickColor:e.colorsMap_[l.name],x.lineWidth=h.hasOwnProperty("tickWidth")?h.tickWidth:e.getOption("strokeWidth"),x.beginPath(),h.attachAtBottom){var y=v+g;x.moveTo(l.canvasx,y),x.lineTo(l.canvasx,y+u)}else x.moveTo(l.canvasx,l.canvasy),x.lineTo(l.canvasx,l.canvasy-2-u);x.closePath(),x.stroke(),x.restore()}}},i.prototype.destroy=function(){this.detachLabels()},a.default=i,e.exports=a.default},{}],21:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=t("../dygraph-utils"),n=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(i),r=function(){this.xlabels_=[],this.ylabels_=[]};r.prototype.toString=function(){return"Axes Plugin"},r.prototype.activate=function(t){return{layout:this.layout,clearChart:this.clearChart,willDrawChart:this.willDrawChart}},r.prototype.layout=function(t){var e=t.dygraph;if(e.getOptionForAxis("drawAxis","y")){var a=e.getOptionForAxis("axisLabelWidth","y")+2*e.getOptionForAxis("axisTickSize","y");t.reserveSpaceLeft(a)}if(e.getOptionForAxis("drawAxis","x")){var i;i=e.getOption("xAxisHeight")?e.getOption("xAxisHeight"):e.getOptionForAxis("axisLabelFontSize","x")+2*e.getOptionForAxis("axisTickSize","x"),t.reserveSpaceBottom(i)}if(2==e.numAxes()){if(e.getOptionForAxis("drawAxis","y2")){var a=e.getOptionForAxis("axisLabelWidth","y2")+2*e.getOptionForAxis("axisTickSize","y2");t.reserveSpaceRight(a)}}else e.numAxes()>2&&e.error("Only two y-axes are supported at this time. (Trying to use "+e.numAxes()+")")},r.prototype.detachLabels=function(){function t(t){for(var e=0;e0){var x=r.numAxes(),m=[y("y"),y("y2")];_.yticks.forEach(function(t){if(void 0!==t.label){s=v.x;var e="y1",a=m[0];1==t.axis&&(s=v.x+v.w,-1,e="y2",a=m[1]);var n=a("axisLabelFontSize");l=v.y+t.pos*v.h,o=f(t.label,"y",2==x?e:null);var r=l-n/2;r<0&&(r=0),r+n+3>c?o.style.bottom="0":o.style.top=r+"px",0===t.axis?(o.style.left=v.x-a("axisLabelWidth")-a("axisTickSize")+"px",o.style.textAlign="right"):1==t.axis&&(o.style.left=v.x+v.w+a("axisTickSize")+"px",o.style.textAlign="left"),o.style.width=a("axisLabelWidth")+"px",u.appendChild(o),i.ylabels_.push(o)}});var b=this.ylabels_[0],w=r.getOptionForAxis("axisLabelFontSize","y");parseInt(b.style.top,10)+w>c-w&&(b.style.top=parseInt(b.style.top,10)-w/2+"px")}var A;if(r.getOption("drawAxesAtZero")){var O=r.toPercentXCoord(0);(O>1||O<0||isNaN(O))&&(O=0),A=e(v.x+O*v.w)}else A=e(v.x);h.strokeStyle=r.getOptionForAxis("axisLineColor","y"),h.lineWidth=r.getOptionForAxis("axisLineWidth","y"),h.beginPath(),h.moveTo(A,a(v.y)),h.lineTo(A,a(v.y+v.h)),h.closePath(),h.stroke(),2==r.numAxes()&&(h.strokeStyle=r.getOptionForAxis("axisLineColor","y2"),h.lineWidth=r.getOptionForAxis("axisLineWidth","y2"),h.beginPath(),h.moveTo(a(v.x+v.w),a(v.y)),h.lineTo(a(v.x+v.w),a(v.y+v.h)),h.closePath(),h.stroke())}if(r.getOptionForAxis("drawAxis","x")){if(_.xticks){var D=y("x");_.xticks.forEach(function(t){if(void 0!==t.label){s=v.x+t.pos*v.w,l=v.y+v.h,o=f(t.label,"x"),o.style.textAlign="center",o.style.top=l+D("axisTickSize")+"px";var e=s-D("axisLabelWidth")/2;e+D("axisLabelWidth")>d&&(e=d-D("axisLabelWidth"),o.style.textAlign="right"),e<0&&(e=0,o.style.textAlign="left"),o.style.left=e+"px",o.style.width=D("axisLabelWidth")+"px",u.appendChild(o),i.xlabels_.push(o)}})}h.strokeStyle=r.getOptionForAxis("axisLineColor","x"),h.lineWidth=r.getOptionForAxis("axisLineWidth","x"),h.beginPath();var E;if(r.getOption("drawAxesAtZero")){var O=r.toPercentYCoord(0,0);(O>1||O<0)&&(O=1),E=a(v.y+O*v.h)}else E=a(v.y+v.h);h.moveTo(e(v.x),E),h.lineTo(e(v.x+v.w),E),h.closePath(),h.stroke()}h.restore()}},a.default=r,e.exports=a.default},{"../dygraph-utils":17}],22:[function(t,e,a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var i=function(){this.title_div_=null,this.xlabel_div_=null,this.ylabel_div_=null,this.y2label_div_=null};i.prototype.toString=function(){return"ChartLabels Plugin"},i.prototype.activate=function(t){return{layout:this.layout,didDrawChart:this.didDrawChart}};var n=function(t){var e=document.createElement("div");return e.style.position="absolute",e.style.left=t.x+"px",e.style.top=t.y+"px",e.style.width=t.w+"px",e.style.height=t.h+"px",e};i.prototype.detachLabels_=function(){for(var t=[this.title_div_,this.xlabel_div_,this.ylabel_div_,this.y2label_div_],e=0;e=2);o=h.yticks,l.save(),o.forEach(function(t){if(t.has_tick){var r=t.axis;g[r]&&(l.save(),f[r]&&l.setLineDash&&l.setLineDash(_[r]),l.strokeStyle=c[r],l.lineWidth=p[r],i=e(u.x),n=a(u.y+t.pos*u.h),l.beginPath(),l.moveTo(i,n),l.lineTo(i+u.w,n),l.stroke(),l.restore())}}),l.restore()}if(s.getOptionForAxis("drawGrid","x")){o=h.xticks,l.save();var _=s.getOptionForAxis("gridLinePattern","x"),f=_&&_.length>=2;f&&l.setLineDash&&l.setLineDash(_),l.strokeStyle=s.getOptionForAxis("gridLineColor","x"),l.lineWidth=s.getOptionForAxis("gridLineWidth","x"),o.forEach(function(t){t.has_tick&&(i=e(u.x+t.pos*u.w),n=a(u.y+u.h),l.beginPath(),l.moveTo(i,n),l.lineTo(i,u.y),l.closePath(),l.stroke())}),f&&l.setLineDash&&l.setLineDash([]),l.restore()}},i.prototype.destroy=function(){},a.default=i,e.exports=a.default},{}],24:[function(t,e,a){"use strict";function i(t,e,a){if(!t||t.length<=1)return'
';var i,n,r,o,s,l=0,h=0,u=[];for(i=0;i<=t.length;i++)l+=t[i%t.length];if((s=Math.floor(a/(l-t[0])))>1){for(i=0;i';return d}Object.defineProperty(a,"__esModule",{value:!0});var n=t("../dygraph-utils"),r=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(n),o=function(){this.legend_div_=null,this.is_generated_div_=!1};o.prototype.toString=function(){return"Legend Plugin"},o.prototype.activate=function(t){var e,a=t.getOption("labelsDiv");return a&&null!==a?e="string"==typeof a||a instanceof String?document.getElementById(a):a:(e=document.createElement("div"),e.className="dygraph-legend",t.graphDiv.appendChild(e),this.is_generated_div_=!0),this.legend_div_=e,this.one_em_width_=10,{select:this.select,deselect:this.deselect,predraw:this.predraw,didDrawChart:this.didDrawChart}};var s=function(t){var e=document.createElement("span");e.setAttribute("style","margin: 0; padding: 0 0 0 1em; border: 0;"),t.appendChild(e);var a=e.offsetWidth;return t.removeChild(e),a},l=function(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(//g,">")};o.prototype.select=function(t){var e=t.selectedX,a=t.selectedPoints,i=t.selectedRow,n=t.dygraph.getOption("legend");if("never"===n)return void(this.legend_div_.style.display="none");if("follow"===n){var r=t.dygraph.plotter_.area,s=this.legend_div_.offsetWidth,l=t.dygraph.getOptionForAxis("axisLabelWidth","y"),h=a[0].x*r.w+50,u=a[0].y*r.h-50;h+s+1>r.w&&(h=h-100-s-(l-r.x)),t.dygraph.graphDiv.appendChild(this.legend_div_),this.legend_div_.style.left=l+h+"px",this.legend_div_.style.top=u+"px"}var d=o.generateLegendHTML(t.dygraph,e,a,this.one_em_width_,i);this.legend_div_.innerHTML=d,this.legend_div_.style.display=""},o.prototype.deselect=function(t){"always"!==t.dygraph.getOption("legend")&&(this.legend_div_.style.display="none");var e=s(this.legend_div_);this.one_em_width_=e;var a=o.generateLegendHTML(t.dygraph,void 0,void 0,e,null);this.legend_div_.innerHTML=a},o.prototype.didDrawChart=function(t){this.deselect(t)},o.prototype.predraw=function(t){if(this.is_generated_div_){t.dygraph.graphDiv.appendChild(this.legend_div_);var e=t.dygraph.getArea(),a=this.legend_div_.offsetWidth;this.legend_div_.style.left=e.x+e.w-a-1+"px",this.legend_div_.style.top=e.y+"px"}},o.prototype.destroy=function(){this.legend_div_=null},o.generateLegendHTML=function(t,e,a,n,s){var h={dygraph:t,x:e,series:[]},u={},d=t.getLabels();if(d)for(var c=1;c":" "),a+=""+r.dashHTML+" "+r.labelHTML+"")}return a}a=t.xHTML+":";for(var n=0;n");a+=" "+r.labelHTML+": "+r.yHTML+""}}return a},a.default=o,e.exports=a.default},{"../dygraph-utils":17}],25:[function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(a,"__esModule",{value:!0});var n=t("../dygraph-utils"),r=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e.default=t,e}(n),o=t("../dygraph-interaction-model"),s=i(o),l=t("../iframe-tarp"),h=i(l),u=function(){this.hasTouchInterface_="undefined"!=typeof TouchEvent,this.isMobileDevice_=/mobile|android/gi.test(navigator.appVersion),this.interfaceCreated_=!1};u.prototype.toString=function(){return"RangeSelector Plugin"},u.prototype.activate=function(t){return this.dygraph_=t,this.getOption_("showRangeSelector")&&this.createInterface_(),{layout:this.reserveSpace_,predraw:this.renderStaticLayer_,didDrawChart:this.renderInteractiveLayer_}},u.prototype.destroy=function(){this.bgcanvas_=null,this.fgcanvas_=null,this.leftZoomHandle_=null,this.rightZoomHandle_=null},u.prototype.getOption_=function(t,e){return this.dygraph_.getOption(t,e)},u.prototype.setDefaultOption_=function(t,e){this.dygraph_.attrs_[t]=e},u.prototype.createInterface_=function(){this.createCanvases_(),this.createZoomHandles_(),this.initInteraction_(),this.getOption_("animatedZooms")&&(console.warn("Animated zooms and range selector are not compatible; disabling animatedZooms."),this.dygraph_.updateOptions({animatedZooms:!1},!0)),this.interfaceCreated_=!0,this.addToGraph_()},u.prototype.addToGraph_=function(){var t=this.graphDiv_=this.dygraph_.graphDiv;t.appendChild(this.bgcanvas_),t.appendChild(this.fgcanvas_),t.appendChild(this.leftZoomHandle_),t.appendChild(this.rightZoomHandle_)},u.prototype.removeFromGraph_=function(){var t=this.graphDiv_;t.removeChild(this.bgcanvas_),t.removeChild(this.fgcanvas_),t.removeChild(this.leftZoomHandle_),t.removeChild(this.rightZoomHandle_),this.graphDiv_=null},u.prototype.reserveSpace_=function(t){this.getOption_("showRangeSelector")&&t.reserveSpaceBottom(this.getOption_("rangeSelectorHeight")+4)},u.prototype.renderStaticLayer_=function(){this.updateVisibility_()&&(this.resize_(),this.drawStaticLayer_())},u.prototype.renderInteractiveLayer_=function(){this.updateVisibility_()&&!this.isChangingRange_&&(this.placeZoomHandles_(),this.drawInteractiveLayer_())},u.prototype.updateVisibility_=function(){var t=this.getOption_("showRangeSelector");if(t)this.interfaceCreated_?this.graphDiv_&&this.graphDiv_.parentNode||this.addToGraph_():this.createInterface_();else if(this.graphDiv_){this.removeFromGraph_();var e=this.dygraph_;setTimeout(function(){e.width_=0,e.resize()},1)}return t},u.prototype.resize_=function(){function t(t,e,a,i){var n=i||r.getContextPixelRatio(e);t.style.top=a.y+"px",t.style.left=a.x+"px",t.width=a.w*n,t.height=a.h*n,t.style.width=a.w+"px",t.style.height=a.h+"px",1!=n&&e.scale(n,n)}var e=this.dygraph_.layout_.getPlotArea(),a=0;this.dygraph_.getOptionForAxis("drawAxis","x")&&(a=this.getOption_("xAxisHeight")||this.getOption_("axisLabelFontSize")+2*this.getOption_("axisTickSize")),this.canvasRect_={x:e.x,y:e.y+e.h+a+4,w:e.w,h:this.getOption_("rangeSelectorHeight")};var i=this.dygraph_.getNumericOption("pixelRatio");t(this.bgcanvas_,this.bgcanvas_ctx_,this.canvasRect_,i),t(this.fgcanvas_,this.fgcanvas_ctx_,this.canvasRect_,i)},u.prototype.createCanvases_=function(){this.bgcanvas_=r.createCanvas(),this.bgcanvas_.className="dygraph-rangesel-bgcanvas",this.bgcanvas_.style.position="absolute",this.bgcanvas_.style.zIndex=9,this.bgcanvas_ctx_=r.getContext(this.bgcanvas_),this.fgcanvas_=r.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_=r.getContext(this.fgcanvas_)},u.prototype.createZoomHandles_=function(){var t=new Image;t.className="dygraph-rangesel-zoomhandle",t.style.position="absolute",t.style.zIndex=10,t.style.visibility="hidden",t.style.cursor="col-resize",t.width=9,t.height=16,t.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAAzwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7sqSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=",this.isMobileDevice_&&(t.width*=2,t.height*=2),this.leftZoomHandle_=t,this.rightZoomHandle_=t.cloneNode(!1)},u.prototype.initInteraction_=function(){var t,e,a,i,n,o,l,u,d,c,p,g,f,_,v=this,y=document,x=0,m=null,b=!1,w=!1,A=!this.isMobileDevice_,O=new h.default;t=function(t){var e=v.dygraph_.xAxisExtremes(),a=(e[1]-e[0])/v.canvasRect_.w;return[e[0]+(t.leftHandlePos-v.canvasRect_.x)*a,e[0]+(t.rightHandlePos-v.canvasRect_.x)*a]},e=function(t){return r.cancelEvent(t),b=!0,x=t.clientX,m=t.target?t.target:t.srcElement,"mousedown"!==t.type&&"dragstart"!==t.type||(r.addEvent(y,"mousemove",a),r.addEvent(y,"mouseup",i)),v.fgcanvas_.style.cursor="col-resize",O.cover(),!0},a=function(t){if(!b)return!1;r.cancelEvent(t);var e=t.clientX-x;if(Math.abs(e)<4)return!0;x=t.clientX;var a,i=v.getZoomHandleStatus_();m==v.leftZoomHandle_?(a=i.leftHandlePos+e,a=Math.min(a,i.rightHandlePos-m.width-3),a=Math.max(a,v.canvasRect_.x)):(a=i.rightHandlePos+e,a=Math.min(a,v.canvasRect_.x+v.canvasRect_.w),a=Math.max(a,i.leftHandlePos+m.width+3));var o=m.width/2;return m.style.left=a-o+"px",v.drawInteractiveLayer_(),A&&n(),!0},i=function(t){return!!b&&(b=!1,O.uncover(),r.removeEvent(y,"mousemove",a),r.removeEvent(y,"mouseup",i),v.fgcanvas_.style.cursor="default",A||n(),!0)},n=function(){try{var e=v.getZoomHandleStatus_();if(v.isChangingRange_=!0,e.isZoomed){var a=t(e);v.dygraph_.doZoomXDates_(a[0],a[1])}else v.dygraph_.resetZoom()}finally{v.isChangingRange_=!1}},o=function(t){var e=v.leftZoomHandle_.getBoundingClientRect(),a=e.left+e.width/2;e=v.rightZoomHandle_.getBoundingClientRect();var i=e.left+e.width/2;return t.clientX>a&&t.clientX=v.canvasRect_.x+v.canvasRect_.w?(n=v.canvasRect_.x+v.canvasRect_.w,i=n-o):(i+=e,n+=e);var s=v.leftZoomHandle_.width/2;return v.leftZoomHandle_.style.left=i-s+"px",v.rightZoomHandle_.style.left=n-s+"px",v.drawInteractiveLayer_(),A&&c(),!0},d=function(t){return!!w&&(w=!1,r.removeEvent(y,"mousemove",u),r.removeEvent(y,"mouseup",d),A||c(),!0)},c=function(){try{v.isChangingRange_=!0,v.dygraph_.dateWindow_=t(v.getZoomHandleStatus_()),v.dygraph_.drawGraph_(!1)}finally{v.isChangingRange_=!1}},p=function(t){if(!b&&!w){var e=o(t)?"move":"default";e!=v.fgcanvas_.style.cursor&&(v.fgcanvas_.style.cursor=e)}},g=function(t){"touchstart"==t.type&&1==t.targetTouches.length?e(t.targetTouches[0])&&r.cancelEvent(t):"touchmove"==t.type&&1==t.targetTouches.length?a(t.targetTouches[0])&&r.cancelEvent(t):i(t)},f=function(t){"touchstart"==t.type&&1==t.targetTouches.length?l(t.targetTouches[0])&&r.cancelEvent(t):"touchmove"==t.type&&1==t.targetTouches.length?u(t.targetTouches[0])&&r.cancelEvent(t):d(t)},_=function(t,e){for(var a=["touchstart","touchend","touchmove","touchcancel"],i=0;i1&&(g=c.rollingAverage(g,e.rollPeriod(),p)),d.push(g)}var f=[];for(t=0;t0)&&(m=Math.min(m,w),b=Math.max(b,w))}if(a)for(b=r.log10(b),b+=.25*b,m=r.log10(m),t=0;tthis.canvasRect_.x||a+1t;t++)e=i[t],this._makeFauxMethod(e)}var i;return i=["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"],t.prototype._makeFauxMethod=function(t){return this[t]=function(){var i;return this._log.push(t+"("+function(){var t,n,e;for(e=[],t=0,n=arguments.length;n>t;t++)i=arguments[t],e.push(i.toString());return e}.apply(this,arguments).join(",")+")")}},t.prototype.getImageData=function(){var t;return this._log.push("getImageData("+function(){var i,n,e;for(e=[],i=0,n=arguments.length;n>i;i++)t=arguments[i],e.push(t.toString());return e}.apply(this,arguments).join(",")+")"),{width:0,height:0,resolution:1,data:[]}},t}();var ref,typeFunction,hasProp={}.hasOwnProperty;typeFunction=function(t){return function(i){return Object.prototype.toString.call(i)==="[object "+t+"]"}},Epoch.isArray=null!=(ref=Array.isArray)?ref:typeFunction("Array"),Epoch.isObject=typeFunction("Object"),Epoch.isString=typeFunction("String"),Epoch.isFunction=typeFunction("Function"),Epoch.isNumber=typeFunction("Number"),Epoch.isElement=function(t){return"undefined"!=typeof HTMLElement&&null!==HTMLElement?t instanceof HTMLElement:null!=t&&Epoch.isObject(t)&&1===t.nodeType&&Epoch.isString(t.nodeName)},Epoch.isNonEmptyArray=function(t){return Epoch.isArray(t)&&t.length>0},Epoch.Util.copy=function(t){var i,n,e;if(null==t)return null;i={};for(n in t)hasProp.call(t,n)&&(e=t[n],i[n]=e);return i},Epoch.Util.defaults=function(t,i){var n,e,r,o,s,a;s=Epoch.Util.copy(t);for(r in i)hasProp.call(i,r)&&(a=i[r],o=t[r],e=i[r],n=Epoch.isObject(o)&&Epoch.isObject(e),null!=o&&null!=e?n&&!Epoch.isArray(o)?s[r]=Epoch.Util.defaults(o,e):s[r]=o:null!=o?s[r]=o:s[r]=e);return s},Epoch.Util.formatSI=function(t,i,n){var e,r,o,s,a;if(null==i&&(i=1),null==n&&(n=!1),1e3>t)return s=t,((0|s)!==s||n)&&(s=s.toFixed(i)),s;a=["K","M","G","T","P","E","Z","Y"];for(r in a)if(hasProp.call(a,r)&&(o=a[r],e=Math.pow(10,3*((0|r)+1)),t>=e&&tt)return s=t,(s%1!==0||n)&&(s=s.toFixed(i)),s+" B";a=["KB","MB","GB","TB","PB","EB","ZB","YB"];for(r in a)if(hasProp.call(a,r)&&(o=a[r],e=Math.pow(1024,(0|r)+1),t>=e&&tr;r++)for(s=t[r],p=s.values,o=0,h=p.length;h>o;o++)e=p[o],null==l[e[i]]&&(n.push(e[i]),l[e[i]]=!0);return n},Epoch.Util.trim=function(t){return Epoch.isString(t)?t.replace(/^\s+/g,"").replace(/\s+$/g,""):null},Epoch.Util.getComputedStyle=function(t,i){return Epoch.isFunction(window.getComputedStyle)?window.getComputedStyle(t,i):null!=t.currentStyle?t.currentStyle:void 0},Epoch.Util.toRGBA=function(t,i){var n,e,r,o,s,a,h;return(o=t.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*[0-9\.]+\)/))?(n=o[0],s=o[1],r=o[2],e=o[3],a="rgba("+s+","+r+","+e+","+i+")"):(h=d3.rgb(t))&&(a="rgba("+h.r+","+h.g+","+h.b+","+i+")"),a},Epoch.Util.getContext=function(t,i){return null==i&&(i="2d"),t.getContext(i)},Epoch.Events=function(){function t(){this._events={}}return t.prototype.on=function(t,i){var n;if(null!=i)return null==(n=this._events)[t]&&(n[t]=[]),this._events[t].push(i)},t.prototype.onAll=function(t){var i,n,e;if(Epoch.isObject(t)){e=[];for(n in t)hasProp.call(t,n)&&(i=t[n],e.push(this.on(n,i)));return e}},t.prototype.off=function(t,i){var n,e;if(Epoch.isArray(this._events[t])){if(null==i)return delete this._events[t];for(e=[];(n=this._events[t].indexOf(i))>=0;)e.push(this._events[t].splice(n,1));return e}},t.prototype.offAll=function(t){var i,n,e,r,o,s;if(Epoch.isArray(t)){for(o=[],n=0,e=t.length;e>n;n++)r=t[n],o.push(this.off(r));return o}if(Epoch.isObject(t)){s=[];for(r in t)hasProp.call(t,r)&&(i=t[r],s.push(this.off(r,i)));return s}},t.prototype.trigger=function(t){var i,n,e,r,o,s,a,h;if(null!=this._events[t]){for(i=function(){var t,i,n;for(n=[],r=t=1,i=arguments.length;i>=1?i>t:t>i;r=i>=1?++t:--t)n.push(arguments[r]);return n}.apply(this,arguments),a=this._events[t],h=[],o=0,s=a.length;s>o;o++)n=a[o],e=null,Epoch.isString(n)?e=this[n]:Epoch.isFunction(n)&&(e=n),null==e&&Epoch.exception("Callback for event '"+t+"' is not a function or reference to a method."),h.push(e.apply(this,i));return h}},t}(),Epoch.Util.flatten=function(t){var i,n,e,r,o,s,a;if(!Array.isArray(t))throw new Error("Epoch.Util.flatten only accepts arrays");for(a=[],e=0,o=t.length;o>e;e++)if(i=t[e],Array.isArray(i))for(r=0,s=i.length;s>r;r++)n=i[r],a.push(n);else a.push(i);return a},d3.selection.prototype.width=function(t){return null!=t&&Epoch.isString(t)?this.style("width",t):null!=t&&Epoch.isNumber(t)?this.style("width",t+"px"):+Epoch.Util.getComputedStyle(this.node(),null).width.replace("px","")},d3.selection.prototype.height=function(t){return null!=t&&Epoch.isString(t)?this.style("height",t):null!=t&&Epoch.isNumber(t)?this.style("height",t+"px"):+Epoch.Util.getComputedStyle(this.node(),null).height.replace("px","")};var d3Seconds;Epoch.Formats.regular=function(t){return t},Epoch.Formats.si=function(t){return Epoch.Util.formatSI(t)},Epoch.Formats.percent=function(t){return(100*t).toFixed(1)+"%"},Epoch.Formats.seconds=function(t){return d3Seconds(new Date(1e3*t))},d3Seconds=d3.time.format("%I:%M:%S %p"),Epoch.Formats.bytes=function(t){return Epoch.Util.formatBytes(t)};var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Base=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this),this.options.model?(null!=this.options.model.hasData()?this.setData(this.options.model.getData(this.options.type,this.options.dataFormat)):this.setData(this.options.data||[]),this.options.model.on("data:updated",function(t){return function(){return t.setDataFromModel()}}(this))):this.setData(this.options.data||[]),null!=this.options.el&&(this.el=d3.select(this.options.el)),this.width=this.options.width,this.height=this.options.height,null!=this.el?(null==this.width&&(this.width=this.el.width()),null==this.height&&(this.height=this.el.height())):(null==this.width&&(this.width=n.width),null==this.height&&(this.height=n.height),this.el=d3.select(document.createElement("DIV")).attr("width",this.width).attr("height",this.height)),this.onAll(e)}var n,e;return extend(i,t),n={width:320,height:240,dataFormat:null},e={"option:width":"dimensionsChanged","option:height":"dimensionsChanged","layer:shown":"layerChanged","layer:hidden":"layerChanged"},i.prototype._getAllOptions=function(){return Epoch.Util.defaults({},this.options)},i.prototype._getOption=function(t){var i,n,e;for(i=t.split("."),n=this.options;i.length&&null!=n;)e=i.shift(),n=n[e];return n},i.prototype._setOption=function(t,i){var n,e,r;for(n=t.split("."),e=this.options;n.length;){if(r=n.shift(),0===n.length)return e[r]=arguments[1],void this.trigger("option:"+arguments[0]);null==e[r]&&(e[r]={}),e=e[r]}},i.prototype._setManyOptions=function(t,i){var n,e,r;null==i&&(i=""),e=[];for(n in t)hasProp.call(t,n)&&(r=t[n],Epoch.isObject(r)?e.push(this._setManyOptions(r,i+n+".")):e.push(this._setOption(i+n,r)));return e},i.prototype.option=function(){return 0===arguments.length?this._getAllOptions():1===arguments.length&&Epoch.isString(arguments[0])?this._getOption(arguments[0]):2===arguments.length&&Epoch.isString(arguments[0])?this._setOption(arguments[0],arguments[1]):1===arguments.length&&Epoch.isObject(arguments[0])?this._setManyOptions(arguments[0]):void 0},i.prototype.setDataFromModel=function(){var t;return t=this._prepareData(this.options.model.getData(this.options.type,this.options.dataFormat)),this.data=this._annotateLayers(t),this.draw()},i.prototype.setData=function(t,i){var n;return null==i&&(i={}),n=this._prepareData(this.rawData=this._formatData(t)),this.data=this._annotateLayers(n)},i.prototype._prepareData=function(t){return t},i.prototype._formatData=function(t){return Epoch.Data.formatData(t,this.options.type,this.options.dataFormat)},i.prototype._annotateLayers=function(t){var i,n,e,r,o;for(i=1,e=0,o=t.length;o>e;e++)r=t[e],n=["layer"],n.push("category"+i),r.category=i,r.visible=!0,null!=r.label&&n.push(Epoch.Util.dasherize(r.label)),r.className=n.join(" "),i++;return t},i.prototype._findLayer=function(t){var i,n,e,r,o,s;if(r=null,Epoch.isString(t)){for(s=this.data,i=0,o=s.length;o>i;i++)if(e=s[i],e.label===t){r=e;break}}else Epoch.isNumber(t)&&(n=parseInt(t),0>n||n>=this.data.length||(r=this.data[n]));return r},i.prototype.showLayer=function(t){var i;if((i=this._findLayer(t))&&!i.visible)return i.visible=!0,this.trigger("layer:shown")},i.prototype.hideLayer=function(t){var i;if((i=this._findLayer(t))&&i.visible)return i.visible=!1,this.trigger("layer:hidden")},i.prototype.toggleLayer=function(t){var i;if(i=this._findLayer(t))return i.visible=!i.visible,i.visible?this.trigger("layer:shown"):this.trigger("layer:hidden")},i.prototype.isLayerVisible=function(t){var i;return(i=this._findLayer(t))?i.visible:null},i.prototype.getVisibleLayers=function(){return this.data.filter(function(t){return t.visible})},i.prototype.update=function(t,i){return null==i&&(i=!0),this.setData(t),i?this.draw():void 0},i.prototype.draw=function(){return this.trigger("draw")},i.prototype._getScaleDomain=function(t){var i,n,e,r;return Array.isArray(t)?t:Epoch.isString(t)&&(i=this.getVisibleLayers().filter(function(i){return i.range===t}).map(function(t){return t.values}),null!=i&&i.length)?(r=Epoch.Util.flatten(i).map(function(t){return t.y}),e=function(t,i){return t>i?i:t},n=function(t,i){return i>t?i:t},[r.reduce(e,r[0]),r.reduce(n,r[0])]):Array.isArray(this.options.range)?this.options.range:this.options.range&&Array.isArray(this.options.range.left)?this.options.range.left:this.options.range&&Array.isArray(this.options.range.right)?this.options.range.right:this.extent(function(t){return t.y})},i.prototype.extent=function(t){return[d3.min(this.getVisibleLayers(),function(i){return d3.min(i.values,t)}),d3.max(this.getVisibleLayers(),function(i){return d3.max(i.values,t)})]},i.prototype.dimensionsChanged=function(){return this.width=this.option("width")||this.width,this.height=this.option("height")||this.height,this.el.width(this.width),this.el.height(this.height)},i.prototype.layerChanged=function(){return this.draw()},i}(Epoch.Events),Epoch.Chart.SVG=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options),null!=this.el?this.svg=this.el.append("svg"):this.svg=d3.select(document.createElement("svg")),this.svg.attr({xmlns:"http://www.w3.org/2000/svg",width:this.width,height:this.height})}return extend(i,t),i.prototype.dimensionsChanged=function(){return i.__super__.dimensionsChanged.call(this),this.svg.attr("width",this.width).attr("height",this.height)},i}(Epoch.Chart.Base),Epoch.Chart.Canvas=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options),null!=this.options.pixelRatio?this.pixelRatio=this.options.pixelRatio:null!=window.devicePixelRatio?this.pixelRatio=window.devicePixelRatio: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()}),null!=this.el&&this.el.node().appendChild(this.canvas.node()),this.ctx=Epoch.Util.getContext(this.canvas.node())}return extend(i,t),i.prototype.getWidth=function(){return this.width*this.pixelRatio},i.prototype.getHeight=function(){return this.height*this.pixelRatio},i.prototype.clear=function(){return this.ctx.clearRect(0,0,this.getWidth(),this.getHeight())},i.prototype.getStyles=function(t){return Epoch.QueryCSS.getStyles(t,this.el)},i.prototype.dimensionsChanged=function(){return i.__super__.dimensionsChanged.call(this),this.canvas.style({width:this.width+"px",height:this.height+"px"}),this.canvas.attr({width:this.getWidth(),height:this.getHeight()})},i.prototype.redraw=function(){return Epoch.QueryCSS.purge(),this.draw()},i}(Epoch.Chart.Base);var QueryCSS;QueryCSS=function(){function t(){}var i,n,e,r,o,s,a;return e="_canvas_css_reference",i="data-epoch-container-id",r=0,s=function(){return"epoch-container-"+r++},n=/^([^#. ]+)?(#[^. ]+)?(\.[^# ]+)?$/,o=!1,a=function(t){var i,e,r,o,s,a;return o=t.match(n),null==o?Epoch.error("Query CSS cannot match given selector: "+t):(a=o[0],s=o[1],r=o[2],i=o[3],s=(null!=s?s:"div").toUpperCase(),e=document.createElement(s),null!=r&&(e.id=r.substr(1)),null!=i&&(e.className=i.substr(1).replace(/\./g," ")),e)},t.log=function(t){return o=t},t.cache={},t.styleList=["fill","stroke","stroke-width"],t.container=null,t.purge=function(){return t.cache={}},t.getContainer=function(){var i;return null!=t.container?t.container:(i=document.createElement("DIV"),i.id=e,document.body.appendChild(i),t.container=d3.select(i))},t.hash=function(t,n){var e;return e=n.attr(i),null==e&&(e=s(),n.attr(i,e)),e+"__"+t},t.getStyles=function(i,n){var r,s,h,p,l,u,c,g,d,f,y,m,v,x,_,w,k,b,E,C,A,S;if(s=t.hash(i,n),r=t.cache[s],null!=r)return r;for(x=[],v=n.node().parentNode;null!=v&&"body"!==v.nodeName.toLowerCase();)x.unshift(v),v=v.parentNode;for(x.push(n.node()),C=[],l=0,g=x.length;g>l;l++)p=x[l],E=p.nodeName.toLowerCase(),null!=p.id&&p.id.length>0&&(E+="#"+p.id),null!=p.className&&p.className.length>0&&(E+="."+Epoch.Util.trim(p.className).replace(/\s+/g,".")),C.push(E);for(C.push("svg"),w=Epoch.Util.trim(i).split(/\s+/),u=0,d=w.length;d>u;u++)S=w[u],C.push(S);for(o&&console.log(C),m=b=a(C.shift());C.length;)h=a(C.shift()),m.appendChild(h),m=h;for(o&&console.log(b),t.getContainer().node().appendChild(b),_=d3.select("#"+e+" "+i),A={},k=t.styleList,c=0,f=k.length;f>c;c++)y=k[c],A[y]=_.style(y);return t.cache[s]=A,t.getContainer().html(""),A},t}(),Epoch.QueryCSS=QueryCSS;var applyLayerLabel,base,hasProp={}.hasOwnProperty,slice=[].slice;null==Epoch.Data&&(Epoch.Data={}),null==(base=Epoch.Data).Format&&(base.Format={}),applyLayerLabel=function(t,i,n,e){var r,o,s,a,h;if(null==e&&(e=[]),h=[i.labels,i.autoLabels,i.keyLabels],a=h[0],r=h[1],o=h[2],null!=a&&Epoch.isArray(a)&&a.length>n)t.label=a[n];else if(o&&e.length>n)t.label=e[n];else if(r){for(s=[];n>=0;)s.push(String.fromCharCode(65+n%26)),n-=26;t.label=s.join("")}return t},Epoch.Data.Format.array=function(){var t,i,n,e,r,o,s;return i={x:function(t,i){return i},y:function(t,i){return t},time:function(t,i,n){return parseInt(n)+parseInt(i)},type:"area",autoLabels:!1,labels:[],startTime:parseInt((new Date).getTime()/1e3)},t=function(t,i,n){var e,r,o;if(r=[],Epoch.isArray(t[0]))for(e in t)hasProp.call(t,e)&&(o=t[e],r.push(applyLayerLabel({values:o.map(n)},i,parseInt(e))));else r.push(applyLayerLabel({values:t.map(n)},i,0));return r},e=function(i,n){return t(i,n,function(t,i){return{x:n.x(t,i),y:n.y(t,i)}})},s=function(i,n){return t(i,n,function(t,i){return{time:n.time(t,i,n.startTime),y:n.y(t,i)}})},r=function(i,n){return t(i,n,function(t,i){return{time:n.time(t,i,n.startTime),histogram:t}})},o=function(t,i){var n,e,r;e=[];for(n in t)if(hasProp.call(t,n)){if(r=t[n],!Epoch.isNumber(t[0]))return[];e.push(applyLayerLabel({value:r},i,n))}return e},n=function(t,n){var a;return null==t&&(t=[]),null==n&&(n={}),Epoch.isNonEmptyArray(t)?(a=Epoch.Util.defaults(n,i),"time.heatmap"===a.type?r(t,a):a.type.match(/^time\./)?s(t,a):"pie"===a.type?o(t,a):e(t,a)):[]},n.entry=function(t,e){var r,o,s,a,h,p,l,u;if(null==e&&(e={}),"time.gauge"===e.type)return null==t?0:(p=Epoch.Util.defaults(e,i),r=Epoch.isArray(t)?t[0]:t,p.y(r,0));if(null==t)return[];for(null==e.startTime&&(e.startTime=parseInt((new Date).getTime()/1e3)),o=Epoch.isArray(t)?t.map(function(t){return[t]}):[t],l=n(o,e),u=[],s=0,h=l.length;h>s;s++)a=l[s],u.push(a.values[0]);return u},n}(),Epoch.Data.Format.tuple=function(){var t,i,n;return i={x:function(t,i){return t},y:function(t,i){return t},time:function(t,i){return t},type:"area",autoLabels:!1,labels:[]},t=function(t,i,n){var e,r,o;if(!Epoch.isArray(t[0]))return[];if(r=[],Epoch.isArray(t[0][0]))for(e in t)hasProp.call(t,e)&&(o=t[e],r.push(applyLayerLabel({values:o.map(n)},i,parseInt(e))));else r.push(applyLayerLabel({values:t.map(n)},i,0));return r},n=function(n,e){var r;return null==n&&(n=[]),null==e&&(e={}),Epoch.isNonEmptyArray(n)?(r=Epoch.Util.defaults(e,i),"pie"===r.type||"time.heatmap"===r.type||"time.gauge"===r.type?[]:r.type.match(/^time\./)?t(n,r,function(t,i){return{time:r.time(t[0],parseInt(i)),y:r.y(t[1],parseInt(i))}}):t(n,r,function(t,i){return{x:r.x(t[0],parseInt(i)),y:r.y(t[1],parseInt(i))}})):[]},n.entry=function(t,i){var e,r,o,s,a,h;if(null==i&&(i={}),null==t)return[];for(null==i.startTime&&(i.startTime=parseInt((new Date).getTime()/1e3)),e=Epoch.isArray(t)&&Epoch.isArray(t[0])?t.map(function(t){return[t]}):[t],a=n(e,i),h=[],r=0,s=a.length;s>r;r++)o=a[r],h.push(o.values[0]);return h},n}(),Epoch.Data.Format.keyvalue=function(){var t,i,n,e,r;return i={type:"area",x:function(t,i){return parseInt(i)},y:function(t,i){return t},time:function(t,i,n){return parseInt(n)+parseInt(i)},labels:[],autoLabels:!1,keyLabels:!0,startTime:parseInt((new Date).getTime()/1e3)},t=function(t,i,n,e){var r,o,s,a,h,p;h=[];for(s in i)if(hasProp.call(i,s)){a=i[s],p=[];for(o in t)hasProp.call(t,o)&&(r=t[o],p.push(e(r,a,parseInt(o))));h.push(applyLayerLabel({values:p},n,parseInt(s),i))}return h},e=function(i,n,e){return t(i,n,e,function(t,i,n){var r;return r=Epoch.isString(e.x)?t[e.x]:e.x(t,parseInt(n)),{x:r,y:e.y(t[i],parseInt(n))}})},r=function(i,n,e,r){return null==r&&(r="y"),t(i,n,e,function(t,i,n){var o;return o=Epoch.isString(e.time)?{time:t[e.time]}:{time:e.time(t,parseInt(n),e.startTime)},o[r]=e.y(t[i],parseInt(n)),o})},n=function(t,n,o){var s;return null==t&&(t=[]),null==n&&(n=[]),null==o&&(o={}),Epoch.isNonEmptyArray(t)&&Epoch.isNonEmptyArray(n)?(s=Epoch.Util.defaults(o,i),"pie"===s.type||"time.gauge"===s.type?[]:"time.heatmap"===s.type?r(t,n,s,"histogram"):s.type.match(/^time\./)?r(t,n,s):e(t,n,s)):[]},n.entry=function(t,i,e){var r,o,s,a,h;if(null==i&&(i=[]),null==e&&(e={}),null==t||!Epoch.isNonEmptyArray(i))return[];for(null==e.startTime&&(e.startTime=parseInt((new Date).getTime()/1e3)),a=n([t],i,e),h=[],r=0,s=a.length;s>r;r++)o=a[r],h.push(o.values[0]);return h},n}(),Epoch.data=function(){var t,i,n;return n=arguments[0],t=2<=arguments.length?slice.call(arguments,1):[],null==(i=Epoch.Data.Format[n])?[]:i.apply(i,t)},Epoch.Data.formatData=function(t,i,n){var e,r,o,s,a,h;if(null==t&&(t=[]),!Epoch.isNonEmptyArray(t))return t;if(Epoch.isString(n))return a={type:i},Epoch.data(n,t,a);if(!Epoch.isObject(n))return t;if(null==n.name||!Epoch.isString(n.name))return t;if(null==Epoch.Data.Format[n.name])return t;if(r=[n.name,t],null!=n.arguments&&Epoch.isArray(n.arguments))for(h=n.arguments,o=0,s=h.length;s>o;o++)e=h[o],r.push(e);return null!=n.options?(a=n.options,null!=i&&null==a.type&&(a.type=i),r.push(a)):null!=i&&r.push({type:i}),Epoch.data.apply(Epoch.data,r)},Epoch.Data.formatEntry=function(t,i,n){var e,r,o,s,a,h,p,l;if(null==n)return t;if(Epoch.isString(n))return p={type:i},Epoch.Data.Format[n].entry(t,p);if(!Epoch.isObject(n))return t;if(null==n.name||!Epoch.isString(n.name))return t;if(null==Epoch.Data.Format[n.name])return t;if(o=Epoch.Util.defaults(n,{}),r=[t],null!=o.arguments&&Epoch.isArray(o.arguments))for(l=o.arguments,a=0,h=l.length;h>a;a++)e=l[a],r.push(e);return null!=o.options?(p=o.options,p.type=i,r.push(p)):null!=i&&r.push({type:i}),s=Epoch.Data.Format[o.name].entry,s.apply(s,r)};var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Model=function(t){function i(t){null==t&&(t={}),i.__super__.constructor.call(this),t=Epoch.Util.defaults(t,n),this.dataFormat=t.dataFormat,this.data=t.data,this.loading=!1}var n;return extend(i,t),n={dataFormat:null},i.prototype.setData=function(t){return this.data=t,this.trigger("data:updated")},i.prototype.push=function(t){return this.entry=t,this.trigger("data:push")},i.prototype.hasData=function(){return null!=this.data},i.prototype.getData=function(t,i){return null==i&&(i=this.dataFormat),Epoch.Data.formatData(this.data,t,i)},i.prototype.getNext=function(t,i){return null==i&&(i=this.dataFormat),Epoch.Data.formatEntry(this.entry,t,i)},i}(Epoch.Events);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Plot=function(t){function i(t){var o,s,a,h,p;for(this.options=null!=t?t:{},o=Epoch.Util.copy(this.options.margins)||{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,e)),this.margins={},p=["top","right","bottom","left"],s=0,a=p.length;a>s;s++)h=p[s],this.margins[h]=null!=this.options.margins&&null!=this.options.margins[h]?this.options.margins[h]:this.hasAxis(h)?n[h]:6;this.g=this.svg.append("g").attr("transform","translate("+this.margins.left+", "+this.margins.top+")"),this.onAll(r)}var n,e,r;return extend(i,t),e={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}},n={top:25,right:50,bottom:25,left:50},r={"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"},i.prototype.setTickFormat=function(t,i){return this.options.tickFormats[t]=i},i.prototype.hasAxis=function(t){return this.options.axes.indexOf(t)>-1},i.prototype.innerWidth=function(){return this.width-(this.margins.left+this.margins.right)},i.prototype.innerHeight=function(){return this.height-(this.margins.top+this.margins.bottom)},i.prototype.x=function(){var t,i;return t=null!=(i=this.options.domain)?i:this.extent(function(t){return t.x}),d3.scale.linear().domain(t).range([0,this.innerWidth()])},i.prototype.y=function(t){return d3.scale.linear().domain(this._getScaleDomain(t)).range([this.innerHeight(),0])},i.prototype.bottomAxis=function(){return d3.svg.axis().scale(this.x()).orient("bottom").ticks(this.options.ticks.bottom).tickFormat(this.options.tickFormats.bottom)},i.prototype.topAxis=function(){return d3.svg.axis().scale(this.x()).orient("top").ticks(this.options.ticks.top).tickFormat(this.options.tickFormats.top)},i.prototype.leftAxis=function(){var t;return t=this.options.range?this.options.range.left:null,d3.svg.axis().scale(this.y(t)).orient("left").ticks(this.options.ticks.left).tickFormat(this.options.tickFormats.left)},i.prototype.rightAxis=function(){var t;return t=this.options.range?this.options.range.right:null,d3.svg.axis().scale(this.y(t)).orient("right").ticks(this.options.ticks.right).tickFormat(this.options.tickFormats.right)},i.prototype.draw=function(){return this._axesDrawn?this._redrawAxes():this._drawAxes(),i.__super__.draw.call(this)},i.prototype._redrawAxes=function(){return this.hasAxis("bottom")&&this.g.selectAll(".x.axis.bottom").transition().duration(500).ease("linear").call(this.bottomAxis()),this.hasAxis("top")&&this.g.selectAll(".x.axis.top").transition().duration(500).ease("linear").call(this.topAxis()),this.hasAxis("left")&&this.g.selectAll(".y.axis.left").transition().duration(500).ease("linear").call(this.leftAxis()),this.hasAxis("right")?this.g.selectAll(".y.axis.right").transition().duration(500).ease("linear").call(this.rightAxis()):void 0},i.prototype._drawAxes=function(){return this.hasAxis("bottom")&&this.g.append("g").attr("class","x axis bottom").attr("transform","translate(0, "+this.innerHeight()+")").call(this.bottomAxis()),this.hasAxis("top")&&this.g.append("g").attr("class","x axis top").call(this.topAxis()),this.hasAxis("left")&&this.g.append("g").attr("class","y axis left").call(this.leftAxis()),this.hasAxis("right")&&this.g.append("g").attr("class","y axis right").attr("transform","translate("+this.innerWidth()+", 0)").call(this.rightAxis()),this._axesDrawn=!0},i.prototype.dimensionsChanged=function(){return i.__super__.dimensionsChanged.call(this),this.g.selectAll(".axis").remove(),this._axesDrawn=!1,this.draw()},i.prototype.marginsChanged=function(){var t,i,n;if(null!=this.options.margins){i=this.options.margins;for(t in i)hasProp.call(i,t)&&(n=i[t],null==n?this.margins[t]=6:this.margins[t]=n);return this.g.transition().duration(750).attr("transform","translate("+this.margins.left+", "+this.margins.top+")"),this.draw()}},i.prototype.axesChanged=function(){var t,i,e,r;for(r=["top","right","bottom","left"],t=0,i=r.length;i>t;t++)e=r[t],(null==this.options.margins||null==this.options.margins[e])&&(this.hasAxis(e)?this.margins[e]=n[e]:this.margins[e]=6);return this.g.transition().duration(750).attr("transform","translate("+this.margins.left+", "+this.margins.top+")"),this.g.selectAll(".axis").remove(),this._axesDrawn=!1,this.draw()},i.prototype.ticksChanged=function(){return this.draw()},i.prototype.tickFormatsChanged=function(){return this.draw()},i.prototype.domainChanged=function(){return this.draw()},i.prototype.rangeChanged=function(){return this.draw()},i}(Epoch.Chart.SVG);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Area=function(t){function i(t){var n;this.options=null!=t?t:{},null==(n=this.options).type&&(n.type="area"),i.__super__.constructor.call(this,this.options),this.draw()}return extend(i,t),i.prototype.y=function(){var t,i,n,e,r,o,s,a,h;for(t=[],o=this.getVisibleLayers(),i=0,r=o.length;r>i;i++){e=o[i],s=e.values;for(n in s)hasProp.call(s,n)&&(h=s[n],null!=t[n]&&(t[n]+=h.y),null==t[n]&&(t[n]=h.y))}return d3.scale.linear().domain(null!=(a=this.options.range)?a:[0,d3.max(t)]).range([this.height-this.margins.top-this.margins.bottom,0])},i.prototype.draw=function(){var t,n,e,r,o,s,a,h;return o=[this.x(),this.y(),this.getVisibleLayers()],a=o[0],h=o[1],r=o[2],this.g.selectAll(".layer").remove(),0!==r.length?(t=d3.svg.area().x(function(t){return a(t.x)}).y0(function(t){return h(t.y0)}).y1(function(t){return h(t.y0+t.y)}),s=d3.layout.stack().values(function(t){return t.values}),n=s(r),e=this.g.selectAll(".layer").data(r,function(t){return t.category}),e.select(".area").attr("d",function(i){return t(i.values)}),e.enter().append("g").attr("class",function(t){return t.className}),e.append("path").attr("class","area").attr("d",function(i){return t(i.values)}),i.__super__.draw.call(this)):void 0},i}(Epoch.Chart.Plot);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Bar=function(t){function i(t){this.options=null!=t?t:{},this._isHorizontal()?this.options=Epoch.Util.defaults(this.options,e):this.options=Epoch.Util.defaults(this.options,n),i.__super__.constructor.call(this,this.options),this.onAll(o),this.draw()}var n,e,r,o;return extend(i,t),n={type:"bar",style:"grouped",orientation:"vertical",padding:{bar:.08,group:.1},outerPadding:{bar:.08,group:.1}},r={tickFormats:{top:Epoch.Formats.si,bottom:Epoch.Formats.si,left:Epoch.Formats.regular,right:Epoch.Formats.regular}},e=Epoch.Util.defaults(r,n),o={"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"},i.prototype._isVertical=function(){return"vertical"===this.options.orientation},i.prototype._isHorizontal=function(){return"horizontal"===this.options.orientation},i.prototype.x=function(){var t;return this._isVertical()?d3.scale.ordinal().domain(Epoch.Util.domain(this.getVisibleLayers())).rangeRoundBands([0,this.innerWidth()],this.options.padding.group,this.options.outerPadding.group):(t=this.extent(function(t){return t.y}),t[0]=Math.min(0,t[0]),d3.scale.linear().domain(t).range([0,this.width-this.margins.left-this.margins.right]))},i.prototype.x1=function(t){var i;return d3.scale.ordinal().domain(function(){var t,n,e,r;for(e=this.getVisibleLayers(),r=[],t=0,n=e.length;n>t;t++)i=e[t],r.push(i.category);return r}.call(this)).rangeRoundBands([0,t.rangeBand()],this.options.padding.bar,this.options.outerPadding.bar)},i.prototype.y=function(){var t;return this._isVertical()?(t=this.extent(function(t){return t.y}),t[0]=Math.min(0,t[0]),d3.scale.linear().domain(t).range([this.height-this.margins.top-this.margins.bottom,0])):d3.scale.ordinal().domain(Epoch.Util.domain(this.getVisibleLayers())).rangeRoundBands([0,this.innerHeight()],this.options.padding.group,this.options.outerPadding.group)},i.prototype.y1=function(t){var i;return d3.scale.ordinal().domain(function(){var t,n,e,r;for(e=this.getVisibleLayers(),r=[],t=0,n=e.length;n>t;t++)i=e[t],r.push(i.category);return r}.call(this)).rangeRoundBands([0,t.rangeBand()],this.options.padding.bar,this.options.outerPadding.bar)},i.prototype._remapData=function(){var t,i,n,e,r,o,s,a,h,p,l,u,c,g;for(h={},l=this.getVisibleLayers(),n=0,s=l.length;s>n;n++)for(o=l[n],t="bar "+o.className.replace(/\s*layer\s*/,""),u=o.values,r=0,a=u.length;a>r;r++)i=u[r],null==h[p=i.x]&&(h[p]=[]),h[i.x].push({label:o.category,y:i.y,className:t});c=[];for(e in h)hasProp.call(h,e)&&(g=h[e],c.push({group:e,values:g}));return c},i.prototype.draw=function(){return this._isVertical()?this._drawVertical():this._drawHorizontal(),i.__super__.draw.call(this)},i.prototype._drawVertical=function(){var t,i,n,e,r,o,s,a;return r=[this.x(),this.y()],o=r[0],a=r[1],s=this.x1(o),i=this.height-this.margins.top-this.margins.bottom,t=this._remapData(),n=this.g.selectAll(".layer").data(t,function(t){return t.group}),n.transition().duration(750).attr("transform",function(t){return"translate("+o(t.group)+", 0)"}),n.enter().append("g").attr("class","layer").attr("transform",function(t){return"translate("+o(t.group)+", 0)"}),e=n.selectAll("rect").data(function(t){return t.values}),e.attr("class",function(t){return t.className}),e.transition().duration(600).attr("x",function(t){return s(t.label)}).attr("y",function(t){return a(t.y)}).attr("width",s.rangeBand()).attr("height",function(t){return i-a(t.y)}),e.enter().append("rect").attr("class",function(t){return t.className}).attr("x",function(t){return s(t.label)}).attr("y",function(t){return a(t.y)}).attr("width",s.rangeBand()).attr("height",function(t){ +return i-a(t.y)}),e.exit().transition().duration(150).style("opacity","0").remove(),n.exit().transition().duration(750).style("opacity","0").remove()},i.prototype._drawHorizontal=function(){var t,i,n,e,r,o,s,a;return e=[this.x(),this.y()],o=e[0],s=e[1],a=this.y1(s),r=this.width-this.margins.left-this.margins.right,t=this._remapData(),i=this.g.selectAll(".layer").data(t,function(t){return t.group}),i.transition().duration(750).attr("transform",function(t){return"translate(0, "+s(t.group)+")"}),i.enter().append("g").attr("class","layer").attr("transform",function(t){return"translate(0, "+s(t.group)+")"}),n=i.selectAll("rect").data(function(t){return t.values}),n.attr("class",function(t){return t.className}),n.transition().duration(600).attr("x",function(t){return 0}).attr("y",function(t){return a(t.label)}).attr("height",a.rangeBand()).attr("width",function(t){return o(t.y)}),n.enter().append("rect").attr("class",function(t){return t.className}).attr("x",function(t){return 0}).attr("y",function(t){return a(t.label)}).attr("height",a.rangeBand()).attr("width",function(t){return o(t.y)}),n.exit().transition().duration(150).style("opacity","0").remove(),i.exit().transition().duration(750).style("opacity","0").remove()},i.prototype._getTickValues=function(t,i){var n,e,r,o;return null==i&&(i="x"),null==this.data[0]?[]:(o=this.data[0].values.length,e=0|Math.ceil(o/t),r=function(){var t,i,r,s;for(s=[],n=t=0,i=o,r=e;r>0?i>t:t>i;n=t+=r)s.push(this.data[0].values[n].x);return s}.call(this))},i.prototype.bottomAxis=function(){var t;return t=d3.svg.axis().scale(this.x()).orient("bottom").ticks(this.options.ticks.bottom).tickFormat(this.options.tickFormats.bottom),this._isVertical()&&null!=this.options.ticks.bottom&&t.tickValues(this._getTickValues(this.options.ticks.bottom)),t},i.prototype.topAxis=function(){var t;return t=d3.svg.axis().scale(this.x()).orient("top").ticks(this.options.ticks.top).tickFormat(this.options.tickFormats.top),this._isVertical()&&null!=this.options.ticks.top&&t.tickValues(this._getTickValues(this.options.ticks.top)),t},i.prototype.leftAxis=function(){var t;return t=d3.svg.axis().scale(this.y()).orient("left").ticks(this.options.ticks.left).tickFormat(this.options.tickFormats.left),this._isHorizontal()&&null!=this.options.ticks.left&&t.tickValues(this._getTickValues(this.options.ticks.left)),t},i.prototype.rightAxis=function(){var t;return t=d3.svg.axis().scale(this.y()).orient("right").ticks(this.options.ticks.right).tickFormat(this.options.tickFormats.right),this._isHorizontal()&&null!=this.options.ticks.right&&t.tickValues(this._getTickValues(this.options.ticks.right)),t},i.prototype.orientationChanged=function(){var t,i,n,e;return e=this.options.tickFormats.top,t=this.options.tickFormats.bottom,i=this.options.tickFormats.left,n=this.options.tickFormats.right,this.options.tickFormats.left=e,this.options.tickFormats.right=t,this.options.tickFormats.top=i,this.options.tickFormats.bottom=n,this.draw()},i.prototype.paddingChanged=function(){return this.draw()},i}(Epoch.Chart.Plot);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Histogram=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,n)),this.onAll(e),this.draw()}var n,e;return extend(i,t),n={type:"histogram",domain:[0,100],bucketRange:[0,100],buckets:10,cutOutliers:!1},e={"option:bucketRange":"bucketRangeChanged","option:buckets":"bucketsChanged","option:cutOutliers":"cutOutliersChanged"},i.prototype._prepareData=function(t){var i,n,e,r,o,s,a,h,p,l,u,c,g,d,f;for(i=(this.options.bucketRange[1]-this.options.bucketRange[0])/this.options.buckets,c=[],o=0,p=t.length;p>o;o++){for(h=t[o],n=function(){var t,i,n;for(n=[],e=t=0,i=this.options.buckets;i>=0?i>t:t>i;e=i>=0?++t:--t)n.push(0);return n}.call(this),d=h.values,a=0,l=d.length;l>a;a++)u=d[a],r=parseInt((u.x-this.options.bucketRange[0])/i),this.options.cutOutliers&&(0>r||r>=this.options.buckets)||(0>r?r=0:r>=this.options.buckets&&(r=this.options.buckets-1),n[r]+=parseInt(u.y));g={values:n.map(function(t,n){return{x:parseInt(n)*i,y:t}})};for(s in h)hasProp.call(h,s)&&(f=h[s],"values"!==s&&(g[s]=f));c.push(g)}return c},i.prototype.resetData=function(){return this.setData(this.rawData),this.draw()},i.prototype.bucketRangeChanged=function(){return this.resetData()},i.prototype.bucketsChanged=function(){return this.resetData()},i.prototype.cutOutliersChanged=function(){return this.resetData()},i}(Epoch.Chart.Bar);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Line=function(t){function i(t){var n;this.options=null!=t?t:{},null==(n=this.options).type&&(n.type="line"),i.__super__.constructor.call(this,this.options),this.draw()}return extend(i,t),i.prototype.line=function(t){var i,n,e;return i=[this.x(),this.y(t.range)],n=i[0],e=i[1],d3.svg.line().x(function(t){return n(t.x)}).y(function(t){return e(t.y)})},i.prototype.draw=function(){var t,n,e,r,o;return e=[this.x(),this.y(),this.getVisibleLayers()],r=e[0],o=e[1],n=e[2],0===n.length?this.g.selectAll(".layer").remove():(t=this.g.selectAll(".layer").data(n,function(t){return t.category}),t.select(".line").transition().duration(500).attr("d",function(t){return function(i){return t.line(i)(i.values)}}(this)),t.enter().append("g").attr("class",function(t){return t.className}).append("path").attr("class","line").attr("d",function(t){return function(i){return t.line(i)(i.values)}}(this)),t.exit().transition().duration(750).style("opacity","0").remove(),i.__super__.draw.call(this))},i}(Epoch.Chart.Plot);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Pie=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,n)),this.pie=d3.layout.pie().sort(null).value(function(t){return t.value}),this.arc=d3.svg.arc().outerRadius(function(t){return function(){return Math.max(t.width,t.height)/2-t.options.margin}}(this)).innerRadius(function(t){return function(){return t.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()}var n;return extend(i,t),n={type:"pie",margin:10,inner:0},i.prototype.draw=function(){var t,n,e;return this.g.selectAll(".arc").remove(),t=this.g.selectAll(".arc").data(this.pie(this.getVisibleLayers()),function(t){return t.data.category}),t.enter().append("g").attr("class",function(t){return"arc pie "+t.data.className}),t.select("path").attr("d",this.arc),t.select("text").attr("transform",function(t){return function(i){return"translate("+t.arc.centroid(i)+")"}}(this)).text(function(t){return t.data.label||t.data.category}),n=t.append("path").attr("d",this.arc).each(function(t){return this._current=t}),e=t.append("text").attr("transform",function(t){return function(i){return"translate("+t.arc.centroid(i)+")"}}(this)).attr("dy",".35em").style("text-anchor","middle").text(function(t){return t.data.label||t.data.category}),i.__super__.draw.call(this)},i.prototype.marginChanged=function(){return this.draw()},i.prototype.innerChanged=function(){return this.draw()},i}(Epoch.Chart.SVG);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Chart.Scatter=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,n)),this.on("option:radius","radiusChanged"),this.draw()}var n;return extend(i,t),n={type:"scatter",radius:3.5,axes:["top","bottom","left","right"]},i.prototype.draw=function(){var t,n,e,r,o,s,a;return o=[this.x(),this.y(),this.getVisibleLayers()],s=o[0],a=o[1],e=o[2],r=this.options.radius,0===e.length?this.g.selectAll(".layer").remove():(n=this.g.selectAll(".layer").data(e,function(t){return t.category}),n.enter().append("g").attr("class",function(t){return t.className}),t=n.selectAll(".dot").data(function(t){return t.values}),t.transition().duration(500).attr("r",function(t){var i;return null!=(i=t.r)?i:r}).attr("cx",function(t){return s(t.x)}).attr("cy",function(t){return a(t.y)}),t.enter().append("circle").attr("class","dot").attr("r",function(t){var i;return null!=(i=t.r)?i:r}).attr("cx",function(t){return s(t.x)}).attr("cy",function(t){return a(t.y)}),t.exit().transition().duration(750).style("opacity",0).remove(),n.exit().transition().duration(750).style("opacity",0).remove(),i.__super__.draw.call(this))},i.prototype.radiusChanged=function(){return this.draw()},i}(Epoch.Chart.Plot);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Time.Plot=function(t){function i(t){var o,s,a,h,p;for(this.options=t,o=Epoch.Util.copy(this.options.margins)||{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,e)),this.options.model&&this.options.model.on("data:push",function(t){return function(){return t.pushFromModel()}}(this)),this._queue=[],this.margins={},p=["top","right","bottom","left"],s=0,a=p.length;a>s;s++)h=p[s],this.margins[h]=null!=this.options.margins&&null!=this.options.margins[h]?this.options.margins[h]:this.hasAxis(h)?n[h]:6;this.svg=this.el.insert("svg",":first-child").attr("width",this.width).attr("height",this.height).style("z-index","1000"),"absolute"!==this.el.style("position")&&"relative"!==this.el.style("position")&&this.el.style("position","relative"),this.canvas.style({position:"absolute","z-index":"999"}),this._sizeCanvas(),this.animation={interval:null,active:!1,delta:function(t){return function(){return-(t.w()/t.options.fps)}}(this),tickDelta:function(t){return function(){return-(t.w()/t.pixelRatio/t.options.fps)}}(this),frame:0,duration:this.options.fps},this._buildAxes(),this.animationCallback=function(t){return function(){return t._animate()}}(this),this.onAll(r)}var n,e,r;return extend(i,t),e={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}},n={top:25,right:50,bottom:25,left:50},r={"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"},i.prototype._sizeCanvas=function(){return this.canvas.attr({width:this.innerWidth(),height:this.innerHeight()}),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"})},i.prototype._buildAxes=function(){return this.svg.selectAll(".axis").remove(),this._prepareTimeAxes(),this._prepareRangeAxes()},i.prototype._annotateLayers=function(t){var i,n,e,r,o,s;e=[];for(r in t)hasProp.call(t,r)&&(o=t[r],n=Epoch.Util.copy(o),s=Math.max(0,o.values.length-this.options.historySize),n.values=o.values.slice(s),i=["layer"],i.push("category"+((0|r)+1)),null!=o.label&&i.push(Epoch.Util.dasherize(o.label)),n.className=i.join(" "),n.visible=!0,e.push(n));return e},i.prototype._offsetX=function(){return 0},i.prototype._prepareTimeAxes=function(){var t;return this.hasAxis("bottom")&&(t=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)+")"),t.append("path").attr("class","domain").attr("d","M0,0H"+(this.innerWidth()/this.pixelRatio+1))),this.hasAxis("top")&&(t=this.topAxis=this.svg.append("g").attr("class","x axis top canvas").attr("transform","translate("+(this.margins.left-1)+", "+this.margins.top+")"),t.append("path").attr("class","domain").attr("d","M0,0H"+(this.innerWidth()/this.pixelRatio+1))),this._resetInitialTimeTicks()},i.prototype._resetInitialTimeTicks=function(){var t,i,n,e,r,o,s,a,h;for(h=this.options.ticks.time,this._ticks=[],this._tickTimer=h,null!=this.bottomAxis&&this.bottomAxis.selectAll(".tick").remove(),null!=this.topAxis&&this.topAxis.selectAll(".tick").remove(),o=this.data,a=[],n=0,r=o.length;r>n;n++)if(e=o[n],Epoch.isNonEmptyArray(e.values)){for(s=[this.options.windowSize-1,e.values.length-1],t=s[0],i=s[1];t>=0&&i>=0;)this._pushTick(t,e.values[i].time,!1,!0),t-=h,i-=h;break}return a},i.prototype._prepareRangeAxes=function(){return 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()),this.hasAxis("right")?this.svg.append("g").attr("class","y axis right").attr("transform","translate("+(this.width-this.margins.right)+", "+this.margins.top+")").call(this.rightAxis()):void 0},i.prototype.leftAxis=function(){var t,i;return i=this.options.ticks.left,t=d3.svg.axis().scale(this.ySvgLeft()).orient("left").tickFormat(this.options.tickFormats.left),2===i?t.tickValues(this.extent(function(t){return t.y})):t.ticks(i)},i.prototype.rightAxis=function(){var t,i,n;return i=this.extent(function(t){return t.y}),n=this.options.ticks.right,t=d3.svg.axis().scale(this.ySvgRight()).orient("right").tickFormat(this.options.tickFormats.right),2===n?t.tickValues(this.extent(function(t){return t.y})):t.ticks(n)},i.prototype.hasAxis=function(t){return this.options.axes.indexOf(t)>-1},i.prototype.innerWidth=function(){return(this.width-(this.margins.left+this.margins.right))*this.pixelRatio},i.prototype.innerHeight=function(){return(this.height-(this.margins.top+this.margins.bottom))*this.pixelRatio},i.prototype._prepareEntry=function(t){return t},i.prototype._prepareLayers=function(t){return t},i.prototype._startTransition=function(){return this.animation.active!==!0&&0!==this._queue.length?(this.trigger("transition:start"),this._shift(),this.animation.active=!0,this.animation.interval=setInterval(this.animationCallback,1e3/this.options.fps)):void 0},i.prototype._stopTransition=function(){var t,i,n,e,r,o,s;if(this.inTransition()){for(o=this.data,i=0,r=o.length;r>i;i++)e=o[i],e.values.length>this.options.windowSize+1&&e.values.shift();return s=[this._ticks[0],this._ticks[this._ticks.length-1]],t=s[0],n=s[1],null!=n&&n.enter&&(n.enter=!1,n.opacity=1),null!=t&&t.exit&&this._shiftTick(),this.animation.frame=0,this.trigger("transition:end"),this._queue.length>0?this._shift():(this.animation.active=!1,clearInterval(this.animation.interval))}},i.prototype.inTransition=function(){return this.animation.active},i.prototype.push=function(t){return t=this._prepareLayers(t),this._queue.length>this.options.queueSize&&this._queue.splice(this.options.queueSize,this._queue.length-this.options.queueSize),this._queue.length===this.options.queueSize?!1:(this._queue.push(t.map(function(t){return function(i){return t._prepareEntry(i)}}(this))),this.trigger("push"),this.inTransition()?void 0:this._startTransition())},i.prototype.pushFromModel=function(){return this.push(this.options.model.getNext(this.options.type,this.options.dataFormat))},i.prototype._shift=function(){var t,i,n,e;this.trigger("before:shift"),t=this._queue.shift(),e=this.data;for(i in e)hasProp.call(e,i)&&(n=e[i],n.values.push(t[i]));return this._updateTicks(t[0].time),this._transitionRangeAxes(),this.trigger("after:shift")},i.prototype._transitionRangeAxes=function(){return this.hasAxis("left")&&this.svg.selectAll(".y.axis.left").transition().duration(500).ease("linear").call(this.leftAxis()),this.hasAxis("right")?this.svg.selectAll(".y.axis.right").transition().duration(500).ease("linear").call(this.rightAxis()):void 0},i.prototype._animate=function(){return this.inTransition()?(++this.animation.frame===this.animation.duration&&this._stopTransition(),this.draw(this.animation.frame*this.animation.delta()),this._updateTimeAxes()):void 0},i.prototype.y=function(t){return d3.scale.linear().domain(this._getScaleDomain(t)).range([this.innerHeight(),0])},i.prototype.ySvg=function(t){return d3.scale.linear().domain(this._getScaleDomain(t)).range([this.innerHeight()/this.pixelRatio,0])},i.prototype.ySvgLeft=function(){return null!=this.options.range?this.ySvg(this.options.range.left):this.ySvg()},i.prototype.ySvgRight=function(){return null!=this.options.range?this.ySvg(this.options.range.right):this.ySvg()},i.prototype.w=function(){return this.innerWidth()/this.options.windowSize},i.prototype._updateTicks=function(t){return(this.hasAxis("top")||this.hasAxis("bottom"))&&(++this._tickTimer%this.options.ticks.time||this._pushTick(this.options.windowSize,t,!0),this._ticks.length>0)?this._ticks[0].x-this.w()/this.pixelRatio>=0?void 0:this._ticks[0].exit=!0:void 0},i.prototype._pushTick=function(t,i,n,e){var r,o;return null==n&&(n=!1),null==e&&(e=!1),this.hasAxis("top")||this.hasAxis("bottom")?(o={time:i,x:t*(this.w()/this.pixelRatio)+this._offsetX(),opacity:n?0:1,enter:n?!0:!1,exit:!1},this.hasAxis("bottom")&&(r=this.bottomAxis.append("g").attr("class","tick major").attr("transform","translate("+(o.x+1)+",0)").style("opacity",o.opacity),r.append("line").attr("y2",6),r.append("text").attr("text-anchor","middle").attr("dy",19).text(this.options.tickFormats.bottom(o.time)),o.bottomEl=r),this.hasAxis("top")&&(r=this.topAxis.append("g").attr("class","tick major").attr("transform","translate("+(o.x+1)+",0)").style("opacity",o.opacity),r.append("line").attr("y2",-6),r.append("text").attr("text-anchor","middle").attr("dy",-10).text(this.options.tickFormats.top(o.time)),o.topEl=r),e?this._ticks.unshift(o):this._ticks.push(o),o):void 0},i.prototype._shiftTick=function(){var t;if(this._ticks.length>0)return t=this._ticks.shift(),null!=t.topEl&&t.topEl.remove(),null!=t.bottomEl?t.bottomEl.remove():void 0},i.prototype._updateTimeAxes=function(){var t,i,n,e,r,o,s,a;if(this.hasAxis("top")||this.hasAxis("bottom")){for(r=[this.animation.tickDelta(),1/this.options.fps],i=r[0],t=r[1],o=this._ticks,s=[],n=0,e=o.length;e>n;n++)a=o[n],a.x+=i,this.hasAxis("bottom")&&a.bottomEl.attr("transform","translate("+(a.x+1)+",0)"),this.hasAxis("top")&&a.topEl.attr("transform","translate("+(a.x+1)+",0)"),a.enter?a.opacity+=t:a.exit&&(a.opacity-=t),a.enter||a.exit?(this.hasAxis("bottom")&&a.bottomEl.style("opacity",a.opacity),this.hasAxis("top")?s.push(a.topEl.style("opacity",a.opacity)):s.push(void 0)):s.push(void 0);return s}},i.prototype.draw=function(t){return null==t&&(t=0),i.__super__.draw.call(this)},i.prototype.dimensionsChanged=function(){return i.__super__.dimensionsChanged.call(this),this.svg.attr("width",this.width).attr("height",this.height),this._sizeCanvas(),this._buildAxes(),this.draw(this.animation.frame*this.animation.delta())},i.prototype.axesChanged=function(){var t,i,e,r;for(r=["top","right","bottom","left"],t=0,i=r.length;i>t;t++)e=r[t],(null==this.options.margins||null==this.options.margins[e])&&(this.hasAxis(e)?this.margins[e]=n[e]:this.margins[e]=6);return this._sizeCanvas(),this._buildAxes(),this.draw(this.animation.frame*this.animation.delta())},i.prototype.ticksChanged=function(){return this._resetInitialTimeTicks(),this._transitionRangeAxes(),this.draw(this.animation.frame*this.animation.delta())},i.prototype.tickFormatsChanged=function(){return this._resetInitialTimeTicks(),this._transitionRangeAxes(),this.draw(this.animation.frame*this.animation.delta())},i.prototype.marginsChanged=function(){var t,i,n;if(null!=this.options.margins){i=this.options.margins;for(t in i)hasProp.call(i,t)&&(n=i[t],null==n?this.margins[t]=6:this.margins[t]=n);return this._sizeCanvas(),this.draw(this.animation.frame*this.animation.delta())}},i.prototype.layerChanged=function(){return this._transitionRangeAxes(),i.__super__.layerChanged.call(this)},i}(Epoch.Chart.Canvas),Epoch.Time.Stack=function(t){function i(){return i.__super__.constructor.apply(this,arguments)}return extend(i,t),i.prototype._stackLayers=function(){var t,i,n,e,r,o,s;if((e=this.getVisibleLayers()).length>0){for(o=[],t=i=0,r=e[0].values.length;r>=0?r>i:i>r;t=r>=0?++i:--i)s=0,o.push(function(){var i,r,o;for(o=[],r=0,i=e.length;i>r;r++)n=e[r],n.values[t].y0=s,o.push(s+=n.values[t].y);return o}());return o}},i.prototype._prepareLayers=function(t){var i,n,e;e=0;for(n in t)hasProp.call(t,n)&&(i=t[n],this.data[n].visible&&(i.y0=e,e+=i.y));return t},i.prototype.setData=function(t){return i.__super__.setData.call(this,t),this._stackLayers()},i.prototype.extent=function(){var t,i,n,e,r,o,s,a,h,p;if(s=[0,this.getVisibleLayers()],o=s[0],e=s[1],!e.length)return[0,0];for(t=n=0,a=e[0].values.length;a>=0?a>n:n>a;t=a>=0?++n:--n){for(p=0,i=r=0,h=e.length;h>=0?h>r:r>h;i=h>=0?++r:--r)p+=e[i].values[t].y;p>o&&(o=p)}return[0,o]},i.prototype.layerChanged=function(){var t,n,e,r;for(this._stackLayers(),r=this._queue,t=0,e=r.length;e>t;t++)n=r[t],this._prepareLayers(n);return i.__super__.layerChanged.call(this)},i}(Epoch.Time.Plot);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Time.Area=function(t){function i(t){var n;this.options=null!=t?t:{},null==(n=this.options).type&&(n.type="time.area"),i.__super__.constructor.call(this,this.options),this.draw()}return extend(i,t),i.prototype.setStyles=function(t){var i;return i=null!=t&&null!=t.className?this.getStyles("g."+t.className.replace(/\s/g,".")+" path.area"):this.getStyles("g path.area"),this.ctx.fillStyle=i.fill,null!=i.stroke&&(this.ctx.strokeStyle=i.stroke),null!=i["stroke-width"]?this.ctx.lineWidth=i["stroke-width"].replace("px",""):void 0},i.prototype._drawAreas=function(t){var i,n,e,r,o,s,a,h,p,l,u,c,g,d,f,y,m;for(null==t&&(t=0),u=[this.y(),this.w(),this.getVisibleLayers()],m=u[0],y=u[1],l=u[2],d=[],o=h=c=l.length-1;0>=c?0>=h:h>=0;o=0>=c?++h:--h)if(p=l[o]){for(this.setStyles(p),this.ctx.beginPath(),g=[this.options.windowSize,p.values.length,this.inTransition()],s=g[0],a=g[1],f=g[2],r=null;--s>=-2&&--a>=0;)e=p.values[a],i=[(s+1)*y+t,m(e.y+e.y0)],f&&(i[0]+=y),o===this.options.windowSize-1?this.ctx.moveTo.apply(this.ctx,i):this.ctx.lineTo.apply(this.ctx,i);n=f?(s+3)*y+t:(s+2)*y+t,this.ctx.lineTo(n,this.innerHeight()),this.ctx.lineTo(this.width*this.pixelRatio+y+t,this.innerHeight()),this.ctx.closePath(),d.push(this.ctx.fill())}return d},i.prototype._drawStrokes=function(t){var i,n,e,r,o,s,a,h,p,l,u,c,g,d,f;for(null==t&&(t=0),p=[this.y(),this.w(),this.getVisibleLayers()],f=p[0],d=p[1],h=p[2],c=[],r=s=l=h.length-1;0>=l?0>=s:s>=0;r=0>=l?++s:--s)if(a=h[r]){for(this.setStyles(a),this.ctx.beginPath(),u=[this.options.windowSize,a.values.length,this.inTransition()],r=u[0],o=u[1],g=u[2],e=null;--r>=-2&&--o>=0;)n=a.values[o],i=[(r+1)*d+t,f(n.y+n.y0)],g&&(i[0]+=d),r===this.options.windowSize-1?this.ctx.moveTo.apply(this.ctx,i):this.ctx.lineTo.apply(this.ctx,i);c.push(this.ctx.stroke())}return c},i.prototype.draw=function(t){return null==t&&(t=0),this.clear(),this._drawAreas(t),this._drawStrokes(t),i.__super__.draw.call(this)},i}(Epoch.Time.Stack);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Time.Bar=function(t){function i(t){var n;this.options=null!=t?t:{},null==(n=this.options).type&&(n.type="time.bar"),i.__super__.constructor.call(this,this.options),this.draw()}return extend(i,t),i.prototype._offsetX=function(){return.5*this.w()/this.pixelRatio},i.prototype.setStyles=function(t){var i;return i=this.getStyles("rect.bar."+t.replace(/\s/g,".")),this.ctx.fillStyle=i.fill,null==i.stroke||"none"===i.stroke?this.ctx.strokeStyle="transparent":this.ctx.strokeStyle=i.stroke,null!=i["stroke-width"]?this.ctx.lineWidth=i["stroke-width"].replace("px",""):void 0},i.prototype.draw=function(t){var n,e,r,o,s,a,h,p,l,u,c,g,d,f,y,m,v,x;for(null==t&&(t=0),this.clear(),g=[this.y(),this.w()],x=g[0],v=g[1],d=this.getVisibleLayers(),p=0,c=d.length;c>p;p++)if(u=d[p],Epoch.isNonEmptyArray(u.values))for(this.setStyles(u.className),f=[this.options.windowSize,u.values.length,this.inTransition()],a=f[0],l=f[1],m=f[2],h=m?-1:0;--a>=h&&--l>=0;)e=u.values[l],y=[a*v+t,e.y,e.y0],r=y[0],o=y[1],s=y[2],m&&(r+=v),n=[r+1,x(o+s),v-2,this.innerHeight()-x(o)+.5*this.pixelRatio],this.ctx.fillRect.apply(this.ctx,n),this.ctx.strokeRect.apply(this.ctx,n);return i.__super__.draw.call(this)},i}(Epoch.Time.Stack);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Time.Gauge=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,n)),this.value=this.options.value||0,this.options.model&&this.options.model.on("data:push",function(t){return function(){return t.pushFromModel()}}(this)),"absolute"!==this.el.style("position")&&"relative"!==this.el.style("position")&&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:!1,delta:0,target:0},this._animate=function(t){return function(){return Math.abs(t.animation.target-t.value)=0?l>=s:s>=l;o=l>=0?++s:--s)t=g(o),u=[Math.cos(t),Math.sin(t)],n=u[0],c=u[1],y=n*(a-d)+e,v=c*(a-d)+r,m=n*(a-d-f)+e,x=c*(a-d-f)+r,this.ctx.moveTo(y,v),this.ctx.lineTo(m,x);return this.ctx.stroke(),this.setStyles(".epoch .gauge .arc.outer"),this.ctx.beginPath(),this.ctx.arc(e,r,a,-1.125*Math.PI,1/8*Math.PI,!1),this.ctx.stroke(),this.setStyles(".epoch .gauge .arc.inner"),this.ctx.beginPath(),this.ctx.arc(e,r,a-10,-1.125*Math.PI,1/8*Math.PI,!1),this.ctx.stroke(),this.drawNeedle(),i.__super__.draw.call(this)},i.prototype.drawNeedle=function(){var t,i,n,e,r;return r=[this.centerX(),this.centerY(),this.radius()],t=r[0],i=r[1],n=r[2],e=this.value/this.options.domain[1],this.setStyles(".epoch .gauge .needle"),this.ctx.beginPath(),this.ctx.save(),this.ctx.translate(t,i),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-n),this.ctx.lineTo(1,19-n),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(),this.ctx.restore()},i.prototype.domainChanged=function(){return this.draw()},i.prototype.ticksChanged=function(){return this.draw()},i.prototype.tickSizeChanged=function(){return this.draw()},i.prototype.tickOffsetChanged=function(){return this.draw()},i.prototype.formatChanged=function(){return this.svg.select("text.value").text(this.options.format(this.value))},i}(Epoch.Chart.Canvas);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Time.Heatmap=function(t){function i(t){this.options=null!=t?t:{},i.__super__.constructor.call(this,this.options=Epoch.Util.defaults(this.options,e)),this._setOpacityFunction(),this._setupPaintCanvas(),this.onAll(r),this.draw()}var n,e,r;return extend(i,t),e={type:"time.heatmap",buckets:10,bucketRange:[0,100],opacity:"linear",bucketPadding:2,paintZeroValues:!1,cutOutliers:!1},n={root:function(t,i){return Math.pow(t/i,.5)},linear:function(t,i){return t/i},quadratic:function(t,i){return Math.pow(t/i,2)},cubic:function(t,i){return Math.pow(t/i,3)},quartic:function(t,i){return Math.pow(t/i,4)},quintic:function(t,i){return Math.pow(t/i,5)}},r={"option:buckets":"bucketsChanged","option:bucketRange":"bucketRangeChanged","option:opacity":"opacityChanged","option:bucketPadding":"bucketPaddingChanged","option:paintZeroValues":"paintZeroValuesChanged","option:cutOutliers":"cutOutliersChanged"},i.prototype._setOpacityFunction=function(){return Epoch.isString(this.options.opacity)?(this._opacityFn=n[this.options.opacity],null==this._opacityFn?Epoch.exception("Unknown coloring function provided '"+this.options.opacity+"'"):void 0):Epoch.isFunction(this.options.opacity)?this._opacityFn=this.options.opacity:Epoch.exception("Unknown type for provided coloring function.")},i.prototype.setData=function(t){var n,e,r,o,s;for(i.__super__.setData.call(this,t),o=this.data,s=[],n=0,r=o.length;r>n;n++)e=o[n],s.push(e.values=e.values.map(function(t){return function(i){return t._prepareEntry(i)}}(this)));return s},i.prototype._getBuckets=function(t){var i,n,e,r,o,s,a,h,p;s={time:t.time,max:0,buckets:function(){var t,i,n;for(n=[],e=t=0,i=this.options.buckets;i>=0?i>t:t>i;e=i>=0?++t:--t)n.push(0);return n}.call(this)},i=(this.options.bucketRange[1]-this.options.bucketRange[0])/this.options.buckets, +a=t.histogram;for(p in a)hasProp.call(a,p)&&(n=a[p],r=parseInt((p-this.options.bucketRange[0])/i),this.options.cutOutliers&&(0>r||r>=this.options.buckets)||(0>r?r=0:r>=this.options.buckets&&(r=this.options.buckets-1),s.buckets[r]+=parseInt(n)));for(e=o=0,h=s.buckets.length;h>=0?h>o:o>h;e=h>=0?++o:--o)s.max=Math.max(s.max,s.buckets[e]);return s},i.prototype.y=function(){return d3.scale.linear().domain(this.options.bucketRange).range([this.innerHeight(),0])},i.prototype.ySvg=function(){return d3.scale.linear().domain(this.options.bucketRange).range([this.innerHeight()/this.pixelRatio,0])},i.prototype.h=function(){return this.innerHeight()/this.options.buckets},i.prototype._offsetX=function(){return.5*this.w()/this.pixelRatio},i.prototype._setupPaintCanvas=function(){return 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"),this.on("transition:end",function(t){return function(){return t.draw(t.animation.frame*t.animation.delta())}}(this))},i.prototype.redraw=function(){var t,i;if(Epoch.isNonEmptyArray(this.data)&&Epoch.isNonEmptyArray(this.data[0].values)){for(i=this.data[0].values.length,t=this.options.windowSize,this.inTransition()&&t++;--i>=0&&--t>=0;)this._paintEntry(i,t);return this.draw(this.animation.frame*this.animation.delta())}},i.prototype._computeColor=function(t,i,n){return Epoch.Util.toRGBA(n,this._opacityFn(t,i))},i.prototype._paintEntry=function(t,i){var n,e,r,o,s,a,h,p,l,u,c,g,d,f,y,m,v,x,_,w,k,b,E,C;for(null==t&&(t=null),null==i&&(i=null),v=[this.w(),this.h()],E=v[0],h=v[1],null==t&&(t=this.data[0].values.length-1),null==i&&(i=this.options.windowSize),s=[],e=function(){var t,i,n;for(n=[],p=t=0,i=this.options.buckets;i>=0?i>t:t>i;p=i>=0?++t:--t)n.push(0);return n}.call(this),m=0,x=this.getVisibleLayers(),u=0,g=x.length;g>u;u++){c=x[u],a=this._getBuckets(c.values[t]),_=a.buckets;for(n in _)hasProp.call(_,n)&&(o=_[n],e[n]+=o);m+=a.max,k=this.getStyles("."+c.className.split(" ").join(".")+" rect.bucket"),a.color=k.fill,s.push(a)}C=i*E,this.p.clearRect(C,0,E,this.paintHeight),l=this.options.buckets,w=[];for(n in e)if(hasProp.call(e,n)){for(b=e[n],r=this._avgLab(s,n),y=0,f=0,d=s.length;d>f;f++)a=s[f],y+=a.buckets[n]/b*m;(b>0||this.options.paintZeroValues)&&(this.p.fillStyle=this._computeColor(b,y,r),this.p.fillRect(C,(l-1)*h,E-this.options.bucketPadding,h-this.options.bucketPadding)),w.push(l--)}return w},i.prototype._shiftPaintCanvas=function(){var t;return t=this.p.getImageData(this.w(),0,this.paintWidth-this.w(),this.paintHeight),this.p.putImageData(t,0,0)},i.prototype._avgLab=function(t,i){var n,e,r,o,s,a,h,p,l,u,c,g;for(u=[0,0,0,0],h=u[0],n=u[1],e=u[2],c=u[3],a=0,p=t.length;p>a;a++)o=t[a],null!=o.buckets[i]&&(c+=o.buckets[i]);for(s in t)hasProp.call(t,s)&&(o=t[s],g=null!=o.buckets[i]?0|o.buckets[i]:0,l=g/c,r=d3.lab(o.color),h+=l*r.l,n+=l*r.a,e+=l*r.b);return d3.lab(h,n,e).toString()},i.prototype.draw=function(t){return null==t&&(t=0),this.clear(),this.ctx.drawImage(this.paint,t,0),i.__super__.draw.call(this)},i.prototype.bucketsChanged=function(){return this.redraw()},i.prototype.bucketRangeChanged=function(){return this._transitionRangeAxes(),this.redraw()},i.prototype.opacityChanged=function(){return this._setOpacityFunction(),this.redraw()},i.prototype.bucketPaddingChanged=function(){return this.redraw()},i.prototype.paintZeroValuesChanged=function(){return this.redraw()},i.prototype.cutOutliersChanged=function(){return this.redraw()},i.prototype.layerChanged=function(){return this.redraw()},i}(Epoch.Time.Plot);var extend=function(t,i){function n(){this.constructor=t}for(var e in i)hasProp.call(i,e)&&(t[e]=i[e]);return n.prototype=i.prototype,t.prototype=new n,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty;Epoch.Time.Line=function(t){function i(t){var n;this.options=null!=t?t:{},null==(n=this.options).type&&(n.type="time.line"),i.__super__.constructor.call(this,this.options),this.draw()}return extend(i,t),i.prototype.setStyles=function(t){var i;return i=this.getStyles("g."+t.replace(/\s/g,".")+" path.line"),this.ctx.fillStyle=i.fill,this.ctx.strokeStyle=i.stroke,this.ctx.lineWidth=this.pixelRatio*i["stroke-width"].replace("px","")},i.prototype.draw=function(t){var n,e,r,o,s,a,h,p,l,u,c,g;for(null==t&&(t=0),this.clear(),c=this.w(),p=this.getVisibleLayers(),o=0,h=p.length;h>o;o++)if(a=p[o],Epoch.isNonEmptyArray(a.values)){for(this.setStyles(a.className),this.ctx.beginPath(),g=this.y(a.range),l=[this.options.windowSize,a.values.length,this.inTransition()],r=l[0],s=l[1],u=l[2];--r>=-2&&--s>=0;)e=a.values[s],n=[(r+1)*c+t,g(e.y)],u&&(n[0]+=c),r===this.options.windowSize-1?this.ctx.moveTo.apply(this.ctx,n):this.ctx.lineTo.apply(this.ctx,n);this.ctx.stroke()}return i.__super__.draw.call(this)},i}(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(t){var i;return i="epoch-chart",t.fn.epoch=function(t){var n,e;return t.el=this.get(0),null==(n=this.data(i))&&(e=Epoch._typeMap[t.type],null==e&&Epoch.exception("Unknown chart type '"+t.type+"'"),this.data(i,n=new e(t))),n}},null!=window.jQuery&&jQueryModule(jQuery);var MooToolsModule;MooToolsModule=function(){var t;return t="epoch-chart",Element.implement("epoch",function(i){var n,e,r;return r=$$(this),null==(n=r.retrieve(t)[0])&&(i.el=this,e=Epoch._typeMap[i.type],null==e&&Epoch.exception("Unknown chart type '"+i.type+"'"),r.store(t,n=new e(i))),n})},null!=window.MooTools&&MooToolsModule();var zeptoModule;zeptoModule=function(t){var i,n,e,r;return i="epoch-chart",e={},n=0,r=function(){return i+"-"+ ++n},t.extend(t.fn,{epoch:function(t){var n,o,s;return null!=(o=this.data(i))?e[o]:(t.el=this.get(0),s=Epoch._typeMap[t.type],null==s&&Epoch.exception("Unknown chart type '"+t.type+"'"),this.data(i,o=r()),n=new s(t),e[o]=n,n)}})},null!=window.Zepto&&zeptoModule(Zepto); diff --git a/modules/http/static/epoch.spdx b/modules/http/static/epoch.spdx new file mode 100644 index 0000000..c01b497 --- /dev/null +++ b/modules/http/static/epoch.spdx @@ -0,0 +1,11 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: epoch +DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-4efd8b6e-174f-48e4-a228-3059f191c7e8 + +PackageName: epoch +PackageVersion: 0.8.3 +PackageDownloadLocation: git+https://github.com/epochjs/epoch.git@47aef0a5aa8458bdd5011d108ab92a560215bc57#dist/ +PackageOriginator: Organization: Fastly +PackageLicenseDeclared: MIT diff --git a/modules/http/static/favicon.ico b/modules/http/static/favicon.ico new file mode 100644 index 0000000..8c85535 Binary files /dev/null and b/modules/http/static/favicon.ico differ diff --git a/modules/http/static/glyphicons-halflings-regular.spdx b/modules/http/static/glyphicons-halflings-regular.spdx new file mode 100644 index 0000000..3241fd5 --- /dev/null +++ b/modules/http/static/glyphicons-halflings-regular.spdx @@ -0,0 +1,11 @@ +SPDXVersion: SPDX-2.1 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: bootstrap-glyphicons-halflings-regular +DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-5fdae9d2-c79e-4242-8a82-0909ddd93ae3 + +PackageName: bootstrap-glyphicons-halflings-regular +PackageVersion: 3.3.6 +PackageDownloadLocation: git+https://github.com/twbs/bootstrap.git@81df608a40bf0629a1dc08e584849bb1e43e0b7a#dist/fonts/glyphicons-halflings-regular.woff2 +PackageOriginator: Organization: Twitter +PackageLicenseDeclared: MIT diff --git a/modules/http/static/glyphicons-halflings-regular.woff2 b/modules/http/static/glyphicons-halflings-regular.woff2 new file mode 100644 index 0000000..64539b5 Binary files /dev/null and b/modules/http/static/glyphicons-halflings-regular.woff2 differ diff --git a/modules/http/static/jquery.js b/modules/http/static/jquery.js new file mode 100644 index 0000000..bb692f6 --- /dev/null +++ b/modules/http/static/jquery.js @@ -0,0 +1,5 @@ +/*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ +/* SPDX-License-Identifier: MIT */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ +return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("