From c1769c3923f2bd4aef45286b28743ff21ebfd49b Mon Sep 17 00:00:00 2001 From: Nicholas Trecina Date: Mon, 5 Jul 2021 16:44:15 -0700 Subject: [PATCH] Reverted tempus dominus to the previous version to fix an issue with marking fields as read only --- package-lock.json | 98 +- package.json | 4 +- static/babybuddy/css/app.db0eebca76cc.css | 10179 ++ static/babybuddy/css/app.db0eebca76cc.css.gz | Bin 0 -> 36830 bytes static/babybuddy/js/graph.277fd3f06aa0.js | 104113 +++++++++++++++ static/babybuddy/js/graph.277fd3f06aa0.js.gz | Bin 0 -> 826662 bytes static/babybuddy/js/vendor.218afb8267f7.js | 29240 ++++ static/babybuddy/js/vendor.218afb8267f7.js.gz | Bin 0 -> 207408 bytes static/staticfiles.json | 2 +- 9 files changed, 143593 insertions(+), 43 deletions(-) create mode 100644 static/babybuddy/css/app.db0eebca76cc.css create mode 100644 static/babybuddy/css/app.db0eebca76cc.css.gz create mode 100644 static/babybuddy/js/graph.277fd3f06aa0.js create mode 100644 static/babybuddy/js/graph.277fd3f06aa0.js.gz create mode 100644 static/babybuddy/js/vendor.218afb8267f7.js create mode 100644 static/babybuddy/js/vendor.218afb8267f7.js.gz diff --git a/package-lock.json b/package-lock.json index 76502a30..0838861f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,8 +29,8 @@ "stylelint-config-recommended-scss": "^4.2.0", "stylelint-order": "^4.1.0", "stylelint-scss": "^3.19.0", - "tempusdominus-bootstrap-4": "^5.39.0", - "tempusdominus-core": "^5.19.0" + "tempusdominus-bootstrap-4": "5.1.2", + "tempusdominus-core": "5.0.3" } }, "node_modules/@babel/code-frame": { @@ -11425,40 +11425,47 @@ } }, "node_modules/tempusdominus-bootstrap-4": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.39.0.tgz", - "integrity": "sha512-vYnkmQYQq4+A51WyRc/6e03eM0BHDoPaxd556K1pd4Nhr0eGeB3+Mi9b+3CDx4189fg3gQlrsKzgJiHPRwSX3Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.1.2.tgz", + "integrity": "sha512-ksD8qc4wOJeE19wvryXmEpRzMUSZu4wSOdG6zKSn8l4ccad16249KOX1j0CccyZpuuES/n4FLqLAUB+Dd1LTBA==", "dev": true, "dependencies": { - "bootstrap": "^4.5.2", - "jquery": "^3.5.1", - "moment": "^2.29.0", - "moment-timezone": "^0.5.31", - "popper.js": "^1.16.1" + "bootstrap": ">=4.1.2", + "jquery": "^3.0", + "moment": "^2.22.2", + "moment-timezone": "^0.5.11", + "popper.js": "^1.14.3" }, "peerDependencies": { - "bootstrap": ">=4.5.2", - "jquery": "^3.5.1", - "moment": "^2.29.0", - "moment-timezone": "^0.5.31", - "popper.js": "^1.16.1", - "tempusdominus-core": "5.19.0" + "bootstrap": ">=4.1.2", + "jquery": "^3.0", + "moment": "^2.17", + "moment-timezone": "^0.5.11", + "popper.js": "^1.14.3", + "tempusdominus-core": "5.0.3" } }, "node_modules/tempusdominus-core": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/tempusdominus-core/-/tempusdominus-core-5.19.0.tgz", - "integrity": "sha512-7a4oBQw4cjz6C87BLRg3KHVvzpnPlnRTkuDZ7SwcJayQQ4QgOryX5u6wj0q07TXhgtMQLCntZO6nVhHIKPaeUw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tempusdominus-core/-/tempusdominus-core-5.0.3.tgz", + "integrity": "sha512-52lClmU33gb6J6I/S9uGDrgQwccq3Yw9SlZerTgGLOzOB3Sc9pgIVBirfPMsMcx8nPsg6mA5ItFAH/5BZiQThg==", "dev": true, "dependencies": { - "jquery": "^3.5.0", - "moment": "~2.24.0", - "moment-timezone": "^0.5.28" - }, - "peerDependencies": { "jquery": "^3.0", - "moment": "^2.10", - "moment-timezone": "^0.5.0" + "moment": "^2.22.2", + "moment-timezone": "^0.4.0" + } + }, + "node_modules/tempusdominus-core/node_modules/moment-timezone": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.4.1.tgz", + "integrity": "sha1-gfWYw61eIs2teWtn7NjYjQ9bqgY=", + "dev": true, + "dependencies": { + "moment": ">= 2.6.0" + }, + "engines": { + "node": "*" } }, "node_modules/text-cache": { @@ -22308,27 +22315,38 @@ } }, "tempusdominus-bootstrap-4": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.39.0.tgz", - "integrity": "sha512-vYnkmQYQq4+A51WyRc/6e03eM0BHDoPaxd556K1pd4Nhr0eGeB3+Mi9b+3CDx4189fg3gQlrsKzgJiHPRwSX3Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.1.2.tgz", + "integrity": "sha512-ksD8qc4wOJeE19wvryXmEpRzMUSZu4wSOdG6zKSn8l4ccad16249KOX1j0CccyZpuuES/n4FLqLAUB+Dd1LTBA==", "dev": true, "requires": { - "bootstrap": "^4.5.2", - "jquery": "^3.5.1", - "moment": "^2.29.0", - "moment-timezone": "^0.5.31", - "popper.js": "^1.16.1" + "bootstrap": ">=4.1.2", + "jquery": "^3.0", + "moment": "^2.22.2", + "moment-timezone": "^0.5.11", + "popper.js": "^1.14.3" } }, "tempusdominus-core": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/tempusdominus-core/-/tempusdominus-core-5.19.0.tgz", - "integrity": "sha512-7a4oBQw4cjz6C87BLRg3KHVvzpnPlnRTkuDZ7SwcJayQQ4QgOryX5u6wj0q07TXhgtMQLCntZO6nVhHIKPaeUw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tempusdominus-core/-/tempusdominus-core-5.0.3.tgz", + "integrity": "sha512-52lClmU33gb6J6I/S9uGDrgQwccq3Yw9SlZerTgGLOzOB3Sc9pgIVBirfPMsMcx8nPsg6mA5ItFAH/5BZiQThg==", "dev": true, "requires": { - "jquery": "^3.5.0", - "moment": "~2.24.0", - "moment-timezone": "^0.5.28" + "jquery": "^3.0", + "moment": "^2.22.2", + "moment-timezone": "^0.4.0" + }, + "dependencies": { + "moment-timezone": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.4.1.tgz", + "integrity": "sha1-gfWYw61eIs2teWtn7NjYjQ9bqgY=", + "dev": true, + "requires": { + "moment": ">= 2.6.0" + } + } } }, "text-cache": { diff --git a/package.json b/package.json index 70a85130..3190fe3f 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "stylelint-config-recommended-scss": "^4.2.0", "stylelint-order": "^4.1.0", "stylelint-scss": "^3.19.0", - "tempusdominus-bootstrap-4": "^5.39.0", - "tempusdominus-core": "^5.19.0" + "tempusdominus-bootstrap-4": "5.1.2", + "tempusdominus-core": "5.0.3" } } diff --git a/static/babybuddy/css/app.db0eebca76cc.css b/static/babybuddy/css/app.db0eebca76cc.css new file mode 100644 index 00000000..f0516455 --- /dev/null +++ b/static/babybuddy/css/app.db0eebca76cc.css @@ -0,0 +1,10179 @@ +@charset "UTF-8"; +/*! + * Bootstrap v4.6.0 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + --red: #dc3545; + --orange: #fd7e14; + --yellow: #ffc107; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #226f97; + --secondary: #ff8f00; + --success: #239556; + --info: #15b2d3; + --warning: #ffbe42; + --danger: #a72431; + --light: #f8f9fa; + --dark: #343a40; + --debug: #15b2d3; + --error: #a72431; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } + +*, +*::before, +*::after { + box-sizing: border-box; } + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #ced4da; + text-align: left; + background-color: #212529; } + +[tabindex="-1"]:focus:not(:focus-visible) { + outline: 0 !important; } + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; } + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; } + +p { + margin-top: 0; + margin-bottom: 1rem; } + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + text-decoration-skip-ink: none; } + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; } + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; } + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; } + +dt { + font-weight: 700; } + +dd { + margin-bottom: .5rem; + margin-left: 0; } + +blockquote { + margin: 0 0 1rem; } + +b, +strong { + font-weight: bolder; } + +small { + font-size: 80%; } + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; } + +sub { + bottom: -.25em; } + +sup { + top: -.5em; } + +a { + color: #15b2d3; + text-decoration: none; + background-color: transparent; } + a:hover { + color: #0e778d; + text-decoration: underline; } + +a:not([href]):not([class]) { + color: inherit; + text-decoration: none; } + a:not([href]):not([class]):hover { + color: inherit; + text-decoration: none; } + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; } + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + -ms-overflow-style: scrollbar; } + +figure { + margin: 0 0 1rem; } + +img { + vertical-align: middle; + border-style: none; } + +svg { + overflow: hidden; + vertical-align: middle; } + +table { + border-collapse: collapse; } + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #6c757d; + text-align: left; + caption-side: bottom; } + +th { + text-align: inherit; + text-align: -webkit-match-parent; } + +label { + display: inline-block; + margin-bottom: 0.5rem; } + +button { + border-radius: 0; } + +button:focus:not(:focus-visible) { + outline: 0; } + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } + +button, +input { + overflow: visible; } + +button, +select { + text-transform: none; } + +[role="button"] { + cursor: pointer; } + +select { + word-wrap: normal; } + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; } + +button:not(:disabled), +[type="button"]:not(:disabled), +[type="reset"]:not(:disabled), +[type="submit"]:not(:disabled) { + cursor: pointer; } + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + padding: 0; + border-style: none; } + +input[type="radio"], +input[type="checkbox"] { + box-sizing: border-box; + padding: 0; } + +textarea { + overflow: auto; + resize: vertical; } + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; } + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; } + +progress { + vertical-align: baseline; } + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; } + +[type="search"] { + outline-offset: -2px; + -webkit-appearance: none; } + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; } + +output { + display: inline-block; } + +summary { + display: list-item; + cursor: pointer; } + +template { + display: none; } + +[hidden] { + display: none !important; } + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .card-dashboard .card-header, .card-dashboard .card-body .card-title, .h5, .h6 { + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; } + +h1, .h1 { + font-size: 2.5rem; } + +h2, .h2 { + font-size: 2rem; } + +h3, .h3 { + font-size: 1.75rem; } + +h4, .h4, .card-dashboard .card-header, .card-dashboard .card-body .card-title { + font-size: 1.5rem; } + +h5, .h5 { + font-size: 1.25rem; } + +h6, .h6 { + font-size: 1rem; } + +.lead { + font-size: 1.25rem; + font-weight: 300; } + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2; } + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2; } + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2; } + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; } + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); } + +small, +.small { + font-size: 80%; + font-weight: 400; } + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; } + +.list-unstyled { + padding-left: 0; + list-style: none; } + +.list-inline { + padding-left: 0; + list-style: none; } + +.list-inline-item { + display: inline-block; } + .list-inline-item:not(:last-child) { + margin-right: 0.5rem; } + +.initialism { + font-size: 90%; + text-transform: uppercase; } + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; } + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d; } + .blockquote-footer::before { + content: "\2014\00A0"; } + +.img-fluid { + max-width: 100%; + height: auto; } + +.img-thumbnail { + padding: 0.25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + max-width: 100%; + height: auto; } + +.figure { + display: inline-block; } + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; } + +.figure-caption { + font-size: 90%; + color: #6c757d; } + +code { + font-size: 87.5%; + color: #e83e8c; + word-wrap: break-word; } + a > code { + color: inherit; } + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: 0.2rem; } + kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; } + +pre { + display: block; + font-size: 87.5%; + color: #212529; } + pre code { + font-size: inherit; + color: inherit; + word-break: normal; } + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; } + +.container, +.container-fluid, +.container-sm, +.container-md, +.container-lg, +.container-xl { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } + +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; } } + +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; } } + +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; } } + +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; } } + +.row { + display: flex; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; } + +.no-gutters { + margin-right: 0; + margin-left: 0; } + .no-gutters > .col, + .no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; } + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .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, .col-sm, +.col-sm-auto, .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, .col-md, +.col-md-auto, .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, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px; } + +.col { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; } + +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; } + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; } + +.row-cols-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; } + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; } + +.row-cols-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + +.col-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; } + +.col-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + +.col-3 { + flex: 0 0 25%; + max-width: 25%; } + +.col-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + +.col-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; } + +.col-6 { + flex: 0 0 50%; + max-width: 50%; } + +.col-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; } + +.col-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; } + +.col-9 { + flex: 0 0 75%; + max-width: 75%; } + +.col-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; } + +.col-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; } + +.col-12 { + flex: 0 0 100%; + max-width: 100%; } + +.order-first { + order: -1; } + +.order-last { + order: 13; } + +.order-0 { + order: 0; } + +.order-1 { + order: 1; } + +.order-2 { + order: 2; } + +.order-3 { + order: 3; } + +.order-4 { + order: 4; } + +.order-5 { + order: 5; } + +.order-6 { + order: 6; } + +.order-7 { + order: 7; } + +.order-8 { + order: 8; } + +.order-9 { + order: 9; } + +.order-10 { + order: 10; } + +.order-11 { + order: 11; } + +.order-12 { + order: 12; } + +.offset-1 { + margin-left: 8.33333%; } + +.offset-2 { + margin-left: 16.66667%; } + +.offset-3 { + margin-left: 25%; } + +.offset-4 { + margin-left: 33.33333%; } + +.offset-5 { + margin-left: 41.66667%; } + +.offset-6 { + margin-left: 50%; } + +.offset-7 { + margin-left: 58.33333%; } + +.offset-8 { + margin-left: 66.66667%; } + +.offset-9 { + margin-left: 75%; } + +.offset-10 { + margin-left: 83.33333%; } + +.offset-11 { + margin-left: 91.66667%; } + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; } + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-sm-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-sm-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + .col-sm-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; } + .col-sm-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-sm-3 { + flex: 0 0 25%; + max-width: 25%; } + .col-sm-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .col-sm-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; } + .col-sm-6 { + flex: 0 0 50%; + max-width: 50%; } + .col-sm-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; } + .col-sm-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; } + .col-sm-9 { + flex: 0 0 75%; + max-width: 75%; } + .col-sm-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; } + .col-sm-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; } + .col-sm-12 { + flex: 0 0 100%; + max-width: 100%; } + .order-sm-first { + order: -1; } + .order-sm-last { + order: 13; } + .order-sm-0 { + order: 0; } + .order-sm-1 { + order: 1; } + .order-sm-2 { + order: 2; } + .order-sm-3 { + order: 3; } + .order-sm-4 { + order: 4; } + .order-sm-5 { + order: 5; } + .order-sm-6 { + order: 6; } + .order-sm-7 { + order: 7; } + .order-sm-8 { + order: 8; } + .order-sm-9 { + order: 9; } + .order-sm-10 { + order: 10; } + .order-sm-11 { + order: 11; } + .order-sm-12 { + order: 12; } + .offset-sm-0 { + margin-left: 0; } + .offset-sm-1 { + margin-left: 8.33333%; } + .offset-sm-2 { + margin-left: 16.66667%; } + .offset-sm-3 { + margin-left: 25%; } + .offset-sm-4 { + margin-left: 33.33333%; } + .offset-sm-5 { + margin-left: 41.66667%; } + .offset-sm-6 { + margin-left: 50%; } + .offset-sm-7 { + margin-left: 58.33333%; } + .offset-sm-8 { + margin-left: 66.66667%; } + .offset-sm-9 { + margin-left: 75%; } + .offset-sm-10 { + margin-left: 83.33333%; } + .offset-sm-11 { + margin-left: 91.66667%; } } + +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; } + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-md-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-md-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + .col-md-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; } + .col-md-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-md-3 { + flex: 0 0 25%; + max-width: 25%; } + .col-md-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .col-md-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; } + .col-md-6 { + flex: 0 0 50%; + max-width: 50%; } + .col-md-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; } + .col-md-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; } + .col-md-9 { + flex: 0 0 75%; + max-width: 75%; } + .col-md-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; } + .col-md-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; } + .col-md-12 { + flex: 0 0 100%; + max-width: 100%; } + .order-md-first { + order: -1; } + .order-md-last { + order: 13; } + .order-md-0 { + order: 0; } + .order-md-1 { + order: 1; } + .order-md-2 { + order: 2; } + .order-md-3 { + order: 3; } + .order-md-4 { + order: 4; } + .order-md-5 { + order: 5; } + .order-md-6 { + order: 6; } + .order-md-7 { + order: 7; } + .order-md-8 { + order: 8; } + .order-md-9 { + order: 9; } + .order-md-10 { + order: 10; } + .order-md-11 { + order: 11; } + .order-md-12 { + order: 12; } + .offset-md-0 { + margin-left: 0; } + .offset-md-1 { + margin-left: 8.33333%; } + .offset-md-2 { + margin-left: 16.66667%; } + .offset-md-3 { + margin-left: 25%; } + .offset-md-4 { + margin-left: 33.33333%; } + .offset-md-5 { + margin-left: 41.66667%; } + .offset-md-6 { + margin-left: 50%; } + .offset-md-7 { + margin-left: 58.33333%; } + .offset-md-8 { + margin-left: 66.66667%; } + .offset-md-9 { + margin-left: 75%; } + .offset-md-10 { + margin-left: 83.33333%; } + .offset-md-11 { + margin-left: 91.66667%; } } + +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; } + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-lg-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-lg-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + .col-lg-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; } + .col-lg-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-lg-3 { + flex: 0 0 25%; + max-width: 25%; } + .col-lg-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .col-lg-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; } + .col-lg-6 { + flex: 0 0 50%; + max-width: 50%; } + .col-lg-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; } + .col-lg-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; } + .col-lg-9 { + flex: 0 0 75%; + max-width: 75%; } + .col-lg-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; } + .col-lg-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; } + .col-lg-12 { + flex: 0 0 100%; + max-width: 100%; } + .order-lg-first { + order: -1; } + .order-lg-last { + order: 13; } + .order-lg-0 { + order: 0; } + .order-lg-1 { + order: 1; } + .order-lg-2 { + order: 2; } + .order-lg-3 { + order: 3; } + .order-lg-4 { + order: 4; } + .order-lg-5 { + order: 5; } + .order-lg-6 { + order: 6; } + .order-lg-7 { + order: 7; } + .order-lg-8 { + order: 8; } + .order-lg-9 { + order: 9; } + .order-lg-10 { + order: 10; } + .order-lg-11 { + order: 11; } + .order-lg-12 { + order: 12; } + .offset-lg-0 { + margin-left: 0; } + .offset-lg-1 { + margin-left: 8.33333%; } + .offset-lg-2 { + margin-left: 16.66667%; } + .offset-lg-3 { + margin-left: 25%; } + .offset-lg-4 { + margin-left: 33.33333%; } + .offset-lg-5 { + margin-left: 41.66667%; } + .offset-lg-6 { + margin-left: 50%; } + .offset-lg-7 { + margin-left: 58.33333%; } + .offset-lg-8 { + margin-left: 66.66667%; } + .offset-lg-9 { + margin-left: 75%; } + .offset-lg-10 { + margin-left: 83.33333%; } + .offset-lg-11 { + margin-left: 91.66667%; } } + +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; } + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-xl-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-xl-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + .col-xl-1 { + flex: 0 0 8.33333%; + max-width: 8.33333%; } + .col-xl-2 { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-xl-3 { + flex: 0 0 25%; + max-width: 25%; } + .col-xl-4 { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .col-xl-5 { + flex: 0 0 41.66667%; + max-width: 41.66667%; } + .col-xl-6 { + flex: 0 0 50%; + max-width: 50%; } + .col-xl-7 { + flex: 0 0 58.33333%; + max-width: 58.33333%; } + .col-xl-8 { + flex: 0 0 66.66667%; + max-width: 66.66667%; } + .col-xl-9 { + flex: 0 0 75%; + max-width: 75%; } + .col-xl-10 { + flex: 0 0 83.33333%; + max-width: 83.33333%; } + .col-xl-11 { + flex: 0 0 91.66667%; + max-width: 91.66667%; } + .col-xl-12 { + flex: 0 0 100%; + max-width: 100%; } + .order-xl-first { + order: -1; } + .order-xl-last { + order: 13; } + .order-xl-0 { + order: 0; } + .order-xl-1 { + order: 1; } + .order-xl-2 { + order: 2; } + .order-xl-3 { + order: 3; } + .order-xl-4 { + order: 4; } + .order-xl-5 { + order: 5; } + .order-xl-6 { + order: 6; } + .order-xl-7 { + order: 7; } + .order-xl-8 { + order: 8; } + .order-xl-9 { + order: 9; } + .order-xl-10 { + order: 10; } + .order-xl-11 { + order: 11; } + .order-xl-12 { + order: 12; } + .offset-xl-0 { + margin-left: 0; } + .offset-xl-1 { + margin-left: 8.33333%; } + .offset-xl-2 { + margin-left: 16.66667%; } + .offset-xl-3 { + margin-left: 25%; } + .offset-xl-4 { + margin-left: 33.33333%; } + .offset-xl-5 { + margin-left: 41.66667%; } + .offset-xl-6 { + margin-left: 50%; } + .offset-xl-7 { + margin-left: 58.33333%; } + .offset-xl-8 { + margin-left: 66.66667%; } + .offset-xl-9 { + margin-left: 75%; } + .offset-xl-10 { + margin-left: 83.33333%; } + .offset-xl-11 { + margin-left: 91.66667%; } } + +.table { + width: 100%; + margin-bottom: 1rem; + color: #ced4da; } + .table th, + .table td { + padding: 0.75rem; + vertical-align: top; + border-top: 1px solid #343a40; } + .table thead th { + vertical-align: bottom; + border-bottom: 2px solid #343a40; } + .table tbody + tbody { + border-top: 2px solid #343a40; } + +.table-sm th, +.table-sm td { + padding: 0.3rem; } + +.table-bordered { + border: 1px solid #343a40; } + .table-bordered th, + .table-bordered td { + border: 1px solid #343a40; } + .table-bordered thead th, + .table-bordered thead td { + border-bottom-width: 2px; } + +.table-borderless th, +.table-borderless td, +.table-borderless thead th, +.table-borderless tbody + tbody { + border: 0; } + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(34, 111, 151, 0.1); } + +.table-hover tbody tr:hover { + color: #ced4da; + background-color: rgba(0, 0, 0, 0.075); } + +.table-primary, +.table-primary > th, +.table-primary > td { + background-color: #c1d7e2; } + +.table-primary th, +.table-primary td, +.table-primary thead th, +.table-primary tbody + tbody { + border-color: #8cb4c9; } + +.table-hover .table-primary:hover { + background-color: #b0ccda; } + .table-hover .table-primary:hover > td, + .table-hover .table-primary:hover > th { + background-color: #b0ccda; } + +.table-secondary, +.table-secondary > th, +.table-secondary > td { + background-color: #ffe0b8; } + +.table-secondary th, +.table-secondary td, +.table-secondary thead th, +.table-secondary tbody + tbody { + border-color: #ffc57a; } + +.table-hover .table-secondary:hover { + background-color: #ffd59f; } + .table-hover .table-secondary:hover > td, + .table-hover .table-secondary:hover > th { + background-color: #ffd59f; } + +.table-success, +.table-success > th, +.table-success > td { + background-color: #c1e1d0; } + +.table-success th, +.table-success td, +.table-success thead th, +.table-success tbody + tbody { + border-color: #8dc8a7; } + +.table-hover .table-success:hover { + background-color: #b0d9c3; } + .table-hover .table-success:hover > td, + .table-hover .table-success:hover > th { + background-color: #b0d9c3; } + +.table-info, +.table-info > th, +.table-info > td { + background-color: #bde9f3; } + +.table-info th, +.table-info td, +.table-info thead th, +.table-info tbody + tbody { + border-color: #85d7e8; } + +.table-hover .table-info:hover { + background-color: #a7e2ef; } + .table-hover .table-info:hover > td, + .table-hover .table-info:hover > th { + background-color: #a7e2ef; } + +.table-warning, +.table-warning > th, +.table-warning > td { + background-color: #ffedca; } + +.table-warning th, +.table-warning td, +.table-warning thead th, +.table-warning tbody + tbody { + border-color: #ffdd9d; } + +.table-hover .table-warning:hover { + background-color: #ffe4b1; } + .table-hover .table-warning:hover > td, + .table-hover .table-warning:hover > th { + background-color: #ffe4b1; } + +.table-danger, +.table-danger > th, +.table-danger > td { + background-color: #e6c2c5; } + +.table-danger th, +.table-danger td, +.table-danger thead th, +.table-danger tbody + tbody { + border-color: #d18d94; } + +.table-hover .table-danger:hover { + background-color: #dfb0b4; } + .table-hover .table-danger:hover > td, + .table-hover .table-danger:hover > th { + background-color: #dfb0b4; } + +.table-light, +.table-light > th, +.table-light > td { + background-color: #fdfdfe; } + +.table-light th, +.table-light td, +.table-light thead th, +.table-light tbody + tbody { + border-color: #fbfcfc; } + +.table-hover .table-light:hover { + background-color: #ececf6; } + .table-hover .table-light:hover > td, + .table-hover .table-light:hover > th { + background-color: #ececf6; } + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #c6c8ca; } + +.table-dark th, +.table-dark td, +.table-dark thead th, +.table-dark tbody + tbody { + border-color: #95999c; } + +.table-hover .table-dark:hover { + background-color: #b9bbbe; } + .table-hover .table-dark:hover > td, + .table-hover .table-dark:hover > th { + background-color: #b9bbbe; } + +.table-debug, +.table-debug > th, +.table-debug > td { + background-color: #bde9f3; } + +.table-debug th, +.table-debug td, +.table-debug thead th, +.table-debug tbody + tbody { + border-color: #85d7e8; } + +.table-hover .table-debug:hover { + background-color: #a7e2ef; } + .table-hover .table-debug:hover > td, + .table-hover .table-debug:hover > th { + background-color: #a7e2ef; } + +.table-error, +.table-error > th, +.table-error > td { + background-color: #e6c2c5; } + +.table-error th, +.table-error td, +.table-error thead th, +.table-error tbody + tbody { + border-color: #d18d94; } + +.table-hover .table-error:hover { + background-color: #dfb0b4; } + .table-hover .table-error:hover > td, + .table-hover .table-error:hover > th { + background-color: #dfb0b4; } + +.table-active, +.table-active > th, +.table-active > td { + background-color: rgba(0, 0, 0, 0.075); } + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, 0.075); } + .table-hover .table-active:hover > td, + .table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, 0.075); } + +.table .thead-dark th { + color: #fff; + background-color: #343a40; + border-color: #454d55; } + +.table .thead-light th { + color: #495057; + background-color: #226f97; + border-color: #343a40; } + +.table-dark { + color: #fff; + background-color: #343a40; } + .table-dark th, + .table-dark td, + .table-dark thead th { + border-color: #454d55; } + .table-dark.table-bordered { + border: 0; } + .table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); } + .table-dark.table-hover tbody tr:hover { + color: #fff; + background-color: rgba(255, 255, 255, 0.075); } + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-sm > .table-bordered { + border: 0; } } + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-md > .table-bordered { + border: 0; } } + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-lg > .table-bordered { + border: 0; } } + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-xl > .table-bordered { + border: 0; } } + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive > .table-bordered { + border: 0; } + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #000; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(108, 117, 125, 0.15); + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; } } + .form-control::-ms-expand { + background-color: transparent; + border: 0; } + .form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #000; } + .form-control:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .form-control::placeholder { + color: #6c757d; + opacity: 1; } + .form-control:disabled, .form-control[readonly] { + background-color: #6c757d; + opacity: 1; } + +input[type="date"].form-control, +input[type="time"].form-control, +input[type="datetime-local"].form-control, +input[type="month"].form-control { + appearance: none; } + +select.form-control:focus::-ms-value { + color: #000; + background-color: #fff; } + +.form-control-file, +.form-control-range { + display: block; + width: 100%; } + +.col-form-label { + padding-top: calc(0.375rem + 1px); + padding-bottom: calc(0.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; } + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; } + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; } + +.form-control-plaintext { + display: block; + width: 100%; + padding: 0.375rem 0; + margin-bottom: 0; + font-size: 1rem; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; } + .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; } + +.form-control-sm { + height: calc(1.5em + 0.5rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; } + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; } + +select.form-control[size], select.form-control[multiple] { + height: auto; } + +textarea.form-control { + height: auto; } + +.form-group { + margin-bottom: 1rem; } + +.form-text { + display: block; + margin-top: 0.25rem; } + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; } + .form-row > .col, + .form-row > [class*="col-"] { + padding-right: 5px; + padding-left: 5px; } + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; } + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; } + .form-check-input[disabled] ~ .form-check-label, + .form-check-input:disabled ~ .form-check-label { + color: #6c757d; } + +.form-check-label { + margin-bottom: 0; } + +.form-check-inline { + display: inline-flex; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; } + .form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; } + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #28a745; } + +.valid-tooltip { + position: absolute; + top: 100%; + left: 0; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(40, 167, 69, 0.9); + border-radius: 0.25rem; } + .form-row > .col > .valid-tooltip, + .form-row > [class*="col-"] > .valid-tooltip { + left: 5px; } + +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; } + +.was-validated .form-control:valid, .form-control.is-valid { + border-color: #28a745; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } + +.was-validated .custom-select:valid, .custom-select.is-valid { + border-color: #28a745; + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 0.75rem center/8px 10px no-repeat, #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat; } + .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #28a745; } + +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; } + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #28a745; } + .was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + border-color: #28a745; } + +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + border-color: #34ce57; + background-color: #34ce57; } + +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #28a745; } + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #28a745; } + +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; } + +.invalid-tooltip { + position: absolute; + top: 100%; + left: 0; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(220, 53, 69, 0.9); + border-radius: 0.25rem; } + .form-row > .col > .invalid-tooltip, + .form-row > [class*="col-"] > .invalid-tooltip { + left: 5px; } + +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; } + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } + +.was-validated .custom-select:invalid, .custom-select.is-invalid { + border-color: #dc3545; + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 0.75rem center/8px 10px no-repeat, #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat; } + .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; } + +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; } + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #dc3545; } + .was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + border-color: #dc3545; } + +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + border-color: #e4606d; + background-color: #e4606d; } + +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #dc3545; } + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #dc3545; } + +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.form-inline { + display: flex; + flex-flow: row wrap; + align-items: center; } + .form-inline .form-check { + width: 100%; } + @media (min-width: 576px) { + .form-inline label { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0; } + .form-inline .form-group { + display: flex; + flex: 0 0 auto; + flex-flow: row wrap; + align-items: center; + margin-bottom: 0; } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; } + .form-inline .form-control-plaintext { + display: inline-block; } + .form-inline .input-group, + .form-inline .custom-select { + width: auto; } + .form-inline .form-check { + display: flex; + align-items: center; + justify-content: center; + width: auto; + padding-left: 0; } + .form-inline .form-check-input { + position: relative; + flex-shrink: 0; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; } + .form-inline .custom-control { + align-items: center; + justify-content: center; } + .form-inline .custom-control-label { + margin-bottom: 0; } } + +.btn { + display: inline-block; + font-weight: 400; + color: #ced4da; + text-align: center; + vertical-align: middle; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .btn { + transition: none; } } + .btn:hover { + color: #ced4da; + text-decoration: none; } + .btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .btn.disabled, .btn:disabled { + opacity: 0.65; } + .btn:not(:disabled):not(.disabled) { + cursor: pointer; } + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; } + +.btn-primary { + color: #fff; + background-color: #226f97; + border-color: #226f97; } + .btn-primary:hover { + color: #fff; + background-color: #1b5878; + border-color: #19506d; } + .btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #1b5878; + border-color: #19506d; + box-shadow: 0 0 0 0.2rem rgba(67, 133, 167, 0.5); } + .btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #226f97; + border-color: #226f97; } + .btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, + .show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #19506d; + border-color: #164963; } + .btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, + .show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(67, 133, 167, 0.5); } + +.btn-secondary { + color: #212529; + background-color: #ff8f00; + border-color: #ff8f00; } + .btn-secondary:hover { + color: #fff; + background-color: #d97a00; + border-color: #cc7200; } + .btn-secondary:focus, .btn-secondary.focus { + color: #fff; + background-color: #d97a00; + border-color: #cc7200; + box-shadow: 0 0 0 0.2rem rgba(222, 127, 6, 0.5); } + .btn-secondary.disabled, .btn-secondary:disabled { + color: #212529; + background-color: #ff8f00; + border-color: #ff8f00; } + .btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, + .show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #cc7200; + border-color: #bf6b00; } + .btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, + .show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 127, 6, 0.5); } + +.btn-success { + color: #fff; + background-color: #239556; + border-color: #239556; } + .btn-success:hover { + color: #fff; + background-color: #1c7644; + border-color: #196c3e; } + .btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #1c7644; + border-color: #196c3e; + box-shadow: 0 0 0 0.2rem rgba(68, 165, 111, 0.5); } + .btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #239556; + border-color: #239556; } + .btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, + .show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #196c3e; + border-color: #176138; } + .btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, + .show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(68, 165, 111, 0.5); } + +.btn-info { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-info:hover { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; } + .btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; + box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } + .btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, + .show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #108ba5; + border-color: #0f8199; } + .btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, + .show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } + +.btn-warning { + color: #212529; + background-color: #ffbe42; + border-color: #ffbe42; } + .btn-warning:hover { + color: #212529; + background-color: #ffb11c; + border-color: #ffac0f; } + .btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #ffb11c; + border-color: #ffac0f; + box-shadow: 0 0 0 0.2rem rgba(222, 167, 62, 0.5); } + .btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffbe42; + border-color: #ffbe42; } + .btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, + .show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #ffac0f; + border-color: #ffa802; } + .btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, + .show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 167, 62, 0.5); } + +.btn-danger { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-danger:hover { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; } + .btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; + box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } + .btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, + .show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #7d1b25; + border-color: #731922; } + .btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, + .show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; } + .btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } + .btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, + .show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df; } + .btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, + .show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124; } + .btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } + .btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, + .show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d; } + .btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, + .show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } + +.btn-debug { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-debug:hover { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; } + .btn-debug:focus, .btn-debug.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; + box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } + .btn-debug.disabled, .btn-debug:disabled { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-debug:not(:disabled):not(.disabled):active, .btn-debug:not(:disabled):not(.disabled).active, + .show > .btn-debug.dropdown-toggle { + color: #fff; + background-color: #108ba5; + border-color: #0f8199; } + .btn-debug:not(:disabled):not(.disabled):active:focus, .btn-debug:not(:disabled):not(.disabled).active:focus, + .show > .btn-debug.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } + +.btn-error { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-error:hover { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; } + .btn-error:focus, .btn-error.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; + box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } + .btn-error.disabled, .btn-error:disabled { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-error:not(:disabled):not(.disabled):active, .btn-error:not(:disabled):not(.disabled).active, + .show > .btn-error.dropdown-toggle { + color: #fff; + background-color: #7d1b25; + border-color: #731922; } + .btn-error:not(:disabled):not(.disabled):active:focus, .btn-error:not(:disabled):not(.disabled).active:focus, + .show > .btn-error.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } + +.btn-outline-primary { + color: #226f97; + border-color: #226f97; } + .btn-outline-primary:hover { + color: #fff; + background-color: #226f97; + border-color: #226f97; } + .btn-outline-primary:focus, .btn-outline-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(34, 111, 151, 0.5); } + .btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #226f97; + background-color: transparent; } + .btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, + .show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #226f97; + border-color: #226f97; } + .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(34, 111, 151, 0.5); } + +.btn-outline-secondary { + color: #ff8f00; + border-color: #ff8f00; } + .btn-outline-secondary:hover { + color: #212529; + background-color: #ff8f00; + border-color: #ff8f00; } + .btn-outline-secondary:focus, .btn-outline-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 143, 0, 0.5); } + .btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #ff8f00; + background-color: transparent; } + .btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, + .show > .btn-outline-secondary.dropdown-toggle { + color: #212529; + background-color: #ff8f00; + border-color: #ff8f00; } + .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 143, 0, 0.5); } + +.btn-outline-success { + color: #239556; + border-color: #239556; } + .btn-outline-success:hover { + color: #fff; + background-color: #239556; + border-color: #239556; } + .btn-outline-success:focus, .btn-outline-success.focus { + box-shadow: 0 0 0 0.2rem rgba(35, 149, 86, 0.5); } + .btn-outline-success.disabled, .btn-outline-success:disabled { + color: #239556; + background-color: transparent; } + .btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, + .show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #239556; + border-color: #239556; } + .btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(35, 149, 86, 0.5); } + +.btn-outline-info { + color: #15b2d3; + border-color: #15b2d3; } + .btn-outline-info:hover { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-outline-info:focus, .btn-outline-info.focus { + box-shadow: 0 0 0 0.2rem rgba(21, 178, 211, 0.5); } + .btn-outline-info.disabled, .btn-outline-info:disabled { + color: #15b2d3; + background-color: transparent; } + .btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, + .show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(21, 178, 211, 0.5); } + +.btn-outline-warning { + color: #ffbe42; + border-color: #ffbe42; } + .btn-outline-warning:hover { + color: #212529; + background-color: #ffbe42; + border-color: #ffbe42; } + .btn-outline-warning:focus, .btn-outline-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 190, 66, 0.5); } + .btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffbe42; + background-color: transparent; } + .btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, + .show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffbe42; + border-color: #ffbe42; } + .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 190, 66, 0.5); } + +.btn-outline-danger { + color: #a72431; + border-color: #a72431; } + .btn-outline-danger:hover { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-outline-danger:focus, .btn-outline-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(167, 36, 49, 0.5); } + .btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #a72431; + background-color: transparent; } + .btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, + .show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(167, 36, 49, 0.5); } + +.btn-outline-light { + color: #f8f9fa; + border-color: #f8f9fa; } + .btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-outline-light:focus, .btn-outline-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } + .btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent; } + .btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, + .show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } + +.btn-outline-dark { + color: #343a40; + border-color: #343a40; } + .btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-outline-dark:focus, .btn-outline-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } + .btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent; } + .btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, + .show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } + +.btn-outline-debug { + color: #15b2d3; + border-color: #15b2d3; } + .btn-outline-debug:hover { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-outline-debug:focus, .btn-outline-debug.focus { + box-shadow: 0 0 0 0.2rem rgba(21, 178, 211, 0.5); } + .btn-outline-debug.disabled, .btn-outline-debug:disabled { + color: #15b2d3; + background-color: transparent; } + .btn-outline-debug:not(:disabled):not(.disabled):active, .btn-outline-debug:not(:disabled):not(.disabled).active, + .show > .btn-outline-debug.dropdown-toggle { + color: #fff; + background-color: #15b2d3; + border-color: #15b2d3; } + .btn-outline-debug:not(:disabled):not(.disabled):active:focus, .btn-outline-debug:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-debug.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(21, 178, 211, 0.5); } + +.btn-outline-error { + color: #a72431; + border-color: #a72431; } + .btn-outline-error:hover { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-outline-error:focus, .btn-outline-error.focus { + box-shadow: 0 0 0 0.2rem rgba(167, 36, 49, 0.5); } + .btn-outline-error.disabled, .btn-outline-error:disabled { + color: #a72431; + background-color: transparent; } + .btn-outline-error:not(:disabled):not(.disabled):active, .btn-outline-error:not(:disabled):not(.disabled).active, + .show > .btn-outline-error.dropdown-toggle { + color: #fff; + background-color: #a72431; + border-color: #a72431; } + .btn-outline-error:not(:disabled):not(.disabled):active:focus, .btn-outline-error:not(:disabled):not(.disabled).active:focus, + .show > .btn-outline-error.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(167, 36, 49, 0.5); } + +.btn-link { + font-weight: 400; + color: #15b2d3; + text-decoration: none; } + .btn-link:hover { + color: #0e778d; + text-decoration: underline; } + .btn-link:focus, .btn-link.focus { + text-decoration: underline; } + .btn-link:disabled, .btn-link.disabled { + color: #6c757d; + pointer-events: none; } + +.btn-lg, .btn-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; } + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; } + +.btn-block { + display: block; + width: 100%; } + .btn-block + .btn-block { + margin-top: 0.5rem; } + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; } + +.fade { + transition: opacity 0.15s linear; } + @media (prefers-reduced-motion: reduce) { + .fade { + transition: none; } } + .fade:not(.show) { + opacity: 0; } + +.collapse:not(.show) { + display: none; } + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; } + @media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; } } + +.dropup, +.dropright, +.dropdown, +.dropleft { + position: relative; } + +.dropdown-toggle { + white-space: nowrap; } + .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; } + .dropdown-toggle:empty::after { + margin-left: 0; } + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: #495057; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; } + +.dropdown-menu-left { + right: auto; + left: 0; } + +.dropdown-menu-right { + right: 0; + left: auto; } + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0; } + .dropdown-menu-sm-right { + right: 0; + left: auto; } } + +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0; } + .dropdown-menu-md-right { + right: 0; + left: auto; } } + +@media (min-width: 992px) { + .dropdown-menu-lg-left { + right: auto; + left: 0; } + .dropdown-menu-lg-right { + right: 0; + left: auto; } } + +@media (min-width: 1200px) { + .dropdown-menu-xl-left { + right: auto; + left: 0; } + .dropdown-menu-xl-right { + right: 0; + left: auto; } } + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: 0.125rem; } + +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; } + +.dropup .dropdown-toggle:empty::after { + margin-left: 0; } + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: 0.125rem; } + +.dropright .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; } + +.dropright .dropdown-toggle:empty::after { + margin-left: 0; } + +.dropright .dropdown-toggle::after { + vertical-align: 0; } + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: 0.125rem; } + +.dropleft .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; } + +.dropleft .dropdown-toggle::after { + display: none; } + +.dropleft .dropdown-toggle::before { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; } + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; } + +.dropleft .dropdown-toggle::before { + vertical-align: 0; } + +.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { + right: auto; + bottom: auto; } + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #343a40; } + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #ced4da; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; } + .dropdown-item:hover, .dropdown-item:focus { + color: #bfc7cf; + text-decoration: none; + background-color: #226f97; } + .dropdown-item.active, .dropdown-item:active { + color: #ced4da; + text-decoration: none; + background-color: #226f97; } + .dropdown-item.disabled, .dropdown-item:disabled { + color: #adb5bd; + pointer-events: none; + background-color: transparent; } + +.dropdown-menu.show { + display: block; } + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #8594a3; + white-space: nowrap; } + +.dropdown-item-text { + display: block; + padding: 0.25rem 1.5rem; + color: #ced4da; } + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle; } + .btn-group > .btn, + .btn-group-vertical > .btn { + position: relative; + flex: 1 1 auto; } + .btn-group > .btn:hover, + .btn-group-vertical > .btn:hover { + z-index: 1; } + .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, + .btn-group-vertical > .btn:focus, + .btn-group-vertical > .btn:active, + .btn-group-vertical > .btn.active { + z-index: 1; } + +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } + .btn-toolbar .input-group { + width: auto; } + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) { + margin-left: -1px; } + +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; } + .dropdown-toggle-split::after, + .dropup .dropdown-toggle-split::after, + .dropright .dropdown-toggle-split::after { + margin-left: 0; } + .dropleft .dropdown-toggle-split::before { + margin-right: 0; } + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; } + +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; } + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center; } + .btn-group-vertical > .btn, + .btn-group-vertical > .btn-group { + width: 100%; } + .btn-group-vertical > .btn:not(:first-child), + .btn-group-vertical > .btn-group:not(:first-child) { + margin-top: -1px; } + .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), + .btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } + .btn-group-vertical > .btn:not(:first-child), + .btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.btn-group-toggle > .btn, +.btn-group-toggle > .btn-group > .btn { + margin-bottom: 0; } + .btn-group-toggle > .btn input[type="radio"], + .btn-group-toggle > .btn input[type="checkbox"], + .btn-group-toggle > .btn-group > .btn input[type="radio"], + .btn-group-toggle > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; } + +.input-group { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; } + .input-group > .form-control, + .input-group > .form-control-plaintext, + .input-group > .custom-select, + .input-group > .custom-file { + position: relative; + flex: 1 1 auto; + width: 1%; + min-width: 0; + margin-bottom: 0; } + .input-group > .form-control + .form-control, + .input-group > .form-control + .custom-select, + .input-group > .form-control + .custom-file, + .input-group > .form-control-plaintext + .form-control, + .input-group > .form-control-plaintext + .custom-select, + .input-group > .form-control-plaintext + .custom-file, + .input-group > .custom-select + .form-control, + .input-group > .custom-select + .custom-select, + .input-group > .custom-select + .custom-file, + .input-group > .custom-file + .form-control, + .input-group > .custom-file + .custom-select, + .input-group > .custom-file + .custom-file { + margin-left: -1px; } + .input-group > .form-control:focus, + .input-group > .custom-select:focus, + .input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { + z-index: 3; } + .input-group > .custom-file .custom-file-input:focus { + z-index: 4; } + .input-group > .form-control:not(:first-child), + .input-group > .custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .input-group > .custom-file { + display: flex; + align-items: center; } + .input-group > .custom-file:not(:last-child) .custom-file-label, + .input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .input-group:not(.has-validation) > .form-control:not(:last-child), + .input-group:not(.has-validation) > .custom-select:not(:last-child), + .input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .input-group.has-validation > .form-control:nth-last-child(n + 3), + .input-group.has-validation > .custom-select:nth-last-child(n + 3), + .input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + +.input-group-prepend, +.input-group-append { + display: flex; } + .input-group-prepend .btn, + .input-group-append .btn { + position: relative; + z-index: 2; } + .input-group-prepend .btn:focus, + .input-group-append .btn:focus { + z-index: 3; } + .input-group-prepend .btn + .btn, + .input-group-prepend .btn + .input-group-text, + .input-group-prepend .input-group-text + .input-group-text, + .input-group-prepend .input-group-text + .btn, + .input-group-append .btn + .btn, + .input-group-append .btn + .input-group-text, + .input-group-append .input-group-text + .input-group-text, + .input-group-append .input-group-text + .btn { + margin-left: -1px; } + +.input-group-prepend { + margin-right: -1px; } + +.input-group-append { + margin-left: -1px; } + +.input-group-text { + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #adb5bd; + border: 1px solid #ced4da; + border-radius: 0.25rem; } + .input-group-text input[type="radio"], + .input-group-text input[type="checkbox"] { + margin-top: 0; } + +.input-group-lg > .form-control:not(textarea), +.input-group-lg > .custom-select { + height: calc(1.5em + 1rem + 2px); } + +.input-group-lg > .form-control, +.input-group-lg > .custom-select, +.input-group-lg > .input-group-prepend > .input-group-text, +.input-group-lg > .input-group-append > .input-group-text, +.input-group-lg > .input-group-prepend > .btn, +.input-group-lg > .input-group-append > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; } + +.input-group-sm > .form-control:not(textarea), +.input-group-sm > .custom-select { + height: calc(1.5em + 0.5rem + 2px); } + +.input-group-sm > .form-control, +.input-group-sm > .custom-select, +.input-group-sm > .input-group-prepend > .input-group-text, +.input-group-sm > .input-group-append > .input-group-text, +.input-group-sm > .input-group-prepend > .btn, +.input-group-sm > .input-group-append > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; } + +.input-group-lg > .custom-select, +.input-group-sm > .custom-select { + padding-right: 1.75rem; } + +.input-group > .input-group-prepend > .btn, +.input-group > .input-group-prepend > .input-group-text, +.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn, +.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text, +.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn, +.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text, +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + +.input-group > .input-group-append > .btn, +.input-group > .input-group-append > .input-group-text, +.input-group > .input-group-prepend:not(:first-child) > .btn, +.input-group > .input-group-prepend:not(:first-child) > .input-group-text, +.input-group > .input-group-prepend:first-child > .btn:not(:first-child), +.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + +.custom-control { + position: relative; + z-index: 1; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; + color-adjust: exact; } + +.custom-control-inline { + display: inline-flex; + margin-right: 1rem; } + +.custom-control-input { + position: absolute; + left: 0; + z-index: -1; + width: 1rem; + height: 1.25rem; + opacity: 0; } + .custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #007bff; + background-color: #007bff; } + .custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #80bdff; } + .custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #b3d7ff; + border-color: #b3d7ff; } + .custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { + color: #6c757d; } + .custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { + background-color: #e9ecef; } + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top; } + .custom-control-label::before { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; } + .custom-control-label::after { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: 50% / 50% 50% no-repeat; } + +.custom-checkbox .custom-control-label::before { + border-radius: 0.25rem; } + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); } + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #007bff; + background-color: #007bff; } + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-radio .custom-control-label::before { + border-radius: 50%; } + +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-switch { + padding-left: 2.25rem; } + .custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; } + .custom-switch .custom-control-label::after { + top: calc(0.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: 0.5rem; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none; } } + .custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + transform: translateX(0.75rem); } + .custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 0.75rem center/8px 10px no-repeat; + border: 1px solid #ced4da; + border-radius: 0.25rem; + appearance: none; } + .custom-select:focus { + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-select:focus::-ms-value { + color: #000; + background-color: #fff; } + .custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; } + .custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; } + .custom-select::-ms-expand { + display: none; } + .custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; } + +.custom-select-sm { + height: calc(1.5em + 0.5rem + 2px); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; } + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; } + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin-bottom: 0; } + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin: 0; + overflow: hidden; + opacity: 0; } + .custom-file-input:focus ~ .custom-file-label { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-file-input[disabled] ~ .custom-file-label, + .custom-file-input:disabled ~ .custom-file-label { + background-color: #e9ecef; } + .custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; } + .custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse); } + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + overflow: hidden; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.25rem; } + .custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + 0.75rem); + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 0.25rem 0.25rem 0; } + +.custom-range { + width: 100%; + height: 1.4rem; + padding: 0; + background-color: transparent; + appearance: none; } + .custom-range:focus { + outline: 0; } + .custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-range::-moz-focus-outer { + border: 0; } + .custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; } + @media (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + transition: none; } } + .custom-range::-webkit-slider-thumb:active { + background-color: #b3d7ff; } + .custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; } + .custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; } + @media (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + transition: none; } } + .custom-range::-moz-range-thumb:active { + background-color: #b3d7ff; } + .custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; } + .custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0.2rem; + margin-left: 0.2rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; } + @media (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + transition: none; } } + .custom-range::-ms-thumb:active { + background-color: #b3d7ff; } + .custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; } + .custom-range::-ms-fill-lower { + background-color: #dee2e6; + border-radius: 1rem; } + .custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dee2e6; + border-radius: 1rem; } + .custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd; } + .custom-range:disabled::-webkit-slider-runnable-track { + cursor: default; } + .custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd; } + .custom-range:disabled::-moz-range-track { + cursor: default; } + .custom-range:disabled::-ms-thumb { + background-color: #adb5bd; } + +.custom-control-label::before, +.custom-file-label, +.custom-select { + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .custom-control-label::before, + .custom-file-label, + .custom-select { + transition: none; } } + +.nav { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; } + +.nav-link { + display: block; + padding: 0.5rem 1rem; } + .nav-link:hover, .nav-link:focus { + text-decoration: none; } + .nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default; } + +.nav-tabs { + border-bottom: 1px solid #dee2e6; } + .nav-tabs .nav-link { + margin-bottom: -1px; + border: 1px solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; } + .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #dee2e6; } + .nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; } + .nav-tabs .nav-link.active, + .nav-tabs .nav-item.show .nav-link { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; } + .nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.nav-pills .nav-link { + border-radius: 0.25rem; } + +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; + background-color: #007bff; } + +.nav-fill > .nav-link, +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center; } + +.nav-justified > .nav-link, +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center; } + +.tab-content > .tab-pane { + display: none; } + +.tab-content > .active { + display: block; } + +.navbar { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem; } + .navbar .container, + .navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; } + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; } + .navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; } + +.navbar-nav { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; } + .navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; } + .navbar-nav .dropdown-menu { + position: static; + float: none; } + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; } + +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center; } + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.25rem; } + .navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; } + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: 50% / 100% 100% no-repeat; } + +.navbar-nav-scroll { + max-height: 75vh; + overflow-y: auto; } + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { + padding-right: 0; + padding-left: 0; } } + +@media (min-width: 576px) { + .navbar-expand-sm { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-sm .navbar-nav { + flex-direction: row; } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-sm .navbar-nav-scroll { + overflow: visible; } + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-sm .navbar-toggler { + display: none; } } + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { + padding-right: 0; + padding-left: 0; } } + +@media (min-width: 768px) { + .navbar-expand-md { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-md .navbar-nav { + flex-direction: row; } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-md .navbar-nav-scroll { + overflow: visible; } + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-md .navbar-toggler { + display: none; } } + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { + padding-right: 0; + padding-left: 0; } } + +@media (min-width: 992px) { + .navbar-expand-lg { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-lg .navbar-nav { + flex-direction: row; } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-lg .navbar-nav-scroll { + overflow: visible; } + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-lg .navbar-toggler { + display: none; } } + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { + padding-right: 0; + padding-left: 0; } } + +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-xl .navbar-nav { + flex-direction: row; } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-xl .navbar-nav-scroll { + overflow: visible; } + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-xl .navbar-toggler { + display: none; } } + +.navbar-expand { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand > .container, + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { + padding-right: 0; + padding-left: 0; } + .navbar-expand .navbar-nav { + flex-direction: row; } + .navbar-expand .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand > .container, + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { + flex-wrap: nowrap; } + .navbar-expand .navbar-nav-scroll { + overflow: visible; } + .navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand .navbar-toggler { + display: none; } + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); } + .navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); } + +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); } + .navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); } + .navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); } + +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); } + +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); } + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); } + .navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); } + .navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); } + +.navbar-dark .navbar-brand { + color: #fff; } + .navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; } + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); } + .navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); } + .navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); } + +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; } + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); } + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); } + .navbar-dark .navbar-text a { + color: #fff; } + .navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; } + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #343a40; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; } + .card > hr { + margin-right: 0; + margin-left: 0; } + .card > .list-group { + border-top: inherit; + border-bottom: inherit; } + .card > .list-group:first-child { + border-top-width: 0; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); } + .card > .list-group:last-child { + border-bottom-width: 0; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); } + .card > .card-header + .list-group, + .card > .list-group + .card-footer { + border-top: 0; } + +.card-body { + flex: 1 1 auto; + min-height: 1px; + padding: 1.25rem; } + +.card-title { + margin-bottom: 0.75rem; } + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; } + +.card-text:last-child { + margin-bottom: 0; } + +.card-link:hover { + text-decoration: none; } + +.card-link + .card-link { + margin-left: 1.25rem; } + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(248, 249, 250, 0.05); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); } + .card-header:first-child { + border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; } + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: rgba(248, 249, 250, 0.05); + border-top: 1px solid rgba(0, 0, 0, 0.125); } + .card-footer:last-child { + border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); } + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; } + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; } + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; + border-radius: calc(0.25rem - 1px); } + +.card-img, +.card-img-top, +.card-img-bottom { + flex-shrink: 0; + width: 100%; } + +.card-img, +.card-img-top { + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); } + +.card-img, +.card-img-bottom { + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); } + +.card-deck .card { + margin-bottom: 15px; } + +@media (min-width: 576px) { + .card-deck { + display: flex; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; } + .card-deck .card { + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; } } + +.card-group > .card { + margin-bottom: 15px; } + +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; } } + +.card-columns .card { + margin-bottom: 0.75rem; } + +@media (min-width: 576px) { + .card-columns { + column-count: 3; + column-gap: 1.25rem; + orphans: 1; + widows: 1; } + .card-columns .card { + display: inline-block; + width: 100%; } } + +.accordion { + overflow-anchor: none; } + .accordion > .card { + overflow: hidden; } + .accordion > .card:not(:last-of-type) { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } + .accordion > .card:not(:first-of-type) { + border-top-left-radius: 0; + border-top-right-radius: 0; } + .accordion > .card > .card-header { + border-radius: 0; + margin-bottom: -1px; } + +.breadcrumb { + display: flex; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: none; + border-radius: 0.25rem; } + +.breadcrumb-item + .breadcrumb-item { + padding-left: 0.5rem; } + .breadcrumb-item + .breadcrumb-item::before { + float: left; + padding-right: 0.5rem; + color: #6c757d; + content: "/"; } + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline; } + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none; } + +.breadcrumb-item.active { + color: #6c757d; } + +.pagination { + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.25rem; } + +.page-link { + position: relative; + display: block; + padding: 0.5rem 0.75rem; + margin-left: -1px; + line-height: 1.25; + color: #15b2d3; + background-color: #343a40; + border: 1px solid #3f474e; } + .page-link:hover { + z-index: 2; + color: #0e778d; + text-decoration: none; + background-color: #212529; + border-color: #343a40; } + .page-link:focus { + z-index: 3; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; } + +.page-item:last-child .page-link { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; } + +.page-item.active .page-link { + z-index: 3; + color: #ced4da; + background-color: #226f97; + border-color: #226f97; } + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #343a40; + border-color: #3f474e; } + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5; } + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; } + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; } + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; } + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; } + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; } + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .badge { + transition: none; } } + a.badge:hover, a.badge:focus { + text-decoration: none; } + .badge:empty { + display: none; } + +.btn .badge { + position: relative; + top: -1px; } + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; } + +.badge-primary { + color: #fff; + background-color: #226f97; } + a.badge-primary:hover, a.badge-primary:focus { + color: #fff; + background-color: #19506d; } + a.badge-primary:focus, a.badge-primary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(34, 111, 151, 0.5); } + +.badge-secondary { + color: #212529; + background-color: #ff8f00; } + a.badge-secondary:hover, a.badge-secondary:focus { + color: #212529; + background-color: #cc7200; } + a.badge-secondary:focus, a.badge-secondary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 143, 0, 0.5); } + +.badge-success { + color: #fff; + background-color: #239556; } + a.badge-success:hover, a.badge-success:focus { + color: #fff; + background-color: #196c3e; } + a.badge-success:focus, a.badge-success.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(35, 149, 86, 0.5); } + +.badge-info { + color: #fff; + background-color: #15b2d3; } + a.badge-info:hover, a.badge-info:focus { + color: #fff; + background-color: #108ba5; } + a.badge-info:focus, a.badge-info.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(21, 178, 211, 0.5); } + +.badge-warning { + color: #212529; + background-color: #ffbe42; } + a.badge-warning:hover, a.badge-warning:focus { + color: #212529; + background-color: #ffac0f; } + a.badge-warning:focus, a.badge-warning.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 190, 66, 0.5); } + +.badge-danger { + color: #fff; + background-color: #a72431; } + a.badge-danger:hover, a.badge-danger:focus { + color: #fff; + background-color: #7d1b25; } + a.badge-danger:focus, a.badge-danger.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(167, 36, 49, 0.5); } + +.badge-light { + color: #212529; + background-color: #f8f9fa; } + a.badge-light:hover, a.badge-light:focus { + color: #212529; + background-color: #dae0e5; } + a.badge-light:focus, a.badge-light.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } + +.badge-dark { + color: #fff; + background-color: #343a40; } + a.badge-dark:hover, a.badge-dark:focus { + color: #fff; + background-color: #1d2124; } + a.badge-dark:focus, a.badge-dark.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } + +.badge-debug { + color: #fff; + background-color: #15b2d3; } + a.badge-debug:hover, a.badge-debug:focus { + color: #fff; + background-color: #108ba5; } + a.badge-debug:focus, a.badge-debug.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(21, 178, 211, 0.5); } + +.badge-error { + color: #fff; + background-color: #a72431; } + a.badge-error:hover, a.badge-error:focus { + color: #fff; + background-color: #7d1b25; } + a.badge-error:focus, a.badge-error.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(167, 36, 49, 0.5); } + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #343a40; + border-radius: 0.3rem; } + @media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; } } + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; } + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.25rem; } + +.alert-heading { + color: inherit; } + +.alert-link { + font-weight: 700; } + +.alert-dismissible { + padding-right: 4rem; } + .alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + z-index: 2; + padding: 0.75rem 1.25rem; + color: inherit; } + +.alert-primary { + color: #123a4f; + background-color: #d3e2ea; + border-color: #c1d7e2; } + .alert-primary hr { + border-top-color: #b0ccda; } + .alert-primary .alert-link { + color: #091c25; } + +.alert-secondary { + color: #854a00; + background-color: #ffe9cc; + border-color: #ffe0b8; } + .alert-secondary hr { + border-top-color: #ffd59f; } + .alert-secondary .alert-link { + color: #522e00; } + +.alert-success { + color: #124d2d; + background-color: #d3eadd; + border-color: #c1e1d0; } + .alert-success hr { + border-top-color: #b0d9c3; } + .alert-success .alert-link { + color: #082415; } + +.alert-info { + color: #0b5d6e; + background-color: #d0f0f6; + border-color: #bde9f3; } + .alert-info hr { + border-top-color: #a7e2ef; } + .alert-info .alert-link { + color: #063640; } + +.alert-warning { + color: #856322; + background-color: #fff2d9; + border-color: #ffedca; } + .alert-warning hr { + border-top-color: #ffe4b1; } + .alert-warning .alert-link { + color: #5c4518; } + +.alert-danger { + color: #571319; + background-color: #edd3d6; + border-color: #e6c2c5; } + .alert-danger hr { + border-top-color: #dfb0b4; } + .alert-danger .alert-link { + color: #2d0a0d; } + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; } + .alert-light hr { + border-top-color: #ececf6; } + .alert-light .alert-link { + color: #686868; } + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; } + .alert-dark hr { + border-top-color: #b9bbbe; } + .alert-dark .alert-link { + color: #040505; } + +.alert-debug { + color: #0b5d6e; + background-color: #d0f0f6; + border-color: #bde9f3; } + .alert-debug hr { + border-top-color: #a7e2ef; } + .alert-debug .alert-link { + color: #063640; } + +.alert-error { + color: #571319; + background-color: #edd3d6; + border-color: #e6c2c5; } + .alert-error hr { + border-top-color: #dfb0b4; } + .alert-error .alert-link { + color: #2d0a0d; } + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; } + to { + background-position: 0 0; } } + +.progress { + display: flex; + height: 1rem; + overflow: hidden; + line-height: 0; + font-size: 0.75rem; + background-color: #6c757d; + border-radius: 0.25rem; } + +.progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #007bff; + transition: width 0.6s ease; } + @media (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none; } } + +.progress-bar-striped { + 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); + background-size: 1rem 1rem; } + +.progress-bar-animated { + animation: 1s linear infinite progress-bar-stripes; } + @media (prefers-reduced-motion: reduce) { + .progress-bar-animated { + animation: none; } } + +.media { + display: flex; + align-items: flex-start; } + +.media-body { + flex: 1; } + +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: 0.25rem; } + +.list-group-item-action { + width: 100%; + color: #ced4da; + text-align: inherit; } + .list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: #ced4da; + text-decoration: none; + background-color: #292d32; } + .list-group-item-action:active { + color: #212529; + background-color: #e9ecef; } + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + background-color: #343a40; + border: 1px solid rgba(0, 0, 0, 0.125); } + .list-group-item:first-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; } + .list-group-item:last-child { + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; } + .list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff; } + .list-group-item.active { + z-index: 2; + color: #fff; + background-color: #007bff; + border-color: #007bff; } + .list-group-item + .list-group-item { + border-top-width: 0; } + .list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; } + +.list-group-horizontal { + flex-direction: row; } + .list-group-horizontal > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } + +@media (min-width: 576px) { + .list-group-horizontal-sm { + flex-direction: row; } + .list-group-horizontal-sm > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-sm > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } + +@media (min-width: 768px) { + .list-group-horizontal-md { + flex-direction: row; } + .list-group-horizontal-md > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-md > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } + +@media (min-width: 992px) { + .list-group-horizontal-lg { + flex-direction: row; } + .list-group-horizontal-lg > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-lg > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } + +@media (min-width: 1200px) { + .list-group-horizontal-xl { + flex-direction: row; } + .list-group-horizontal-xl > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-xl > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } + +.list-group-flush { + border-radius: 0; } + .list-group-flush > .list-group-item { + border-width: 0 0 1px; } + .list-group-flush > .list-group-item:last-child { + border-bottom-width: 0; } + +.list-group-item-primary { + color: #123a4f; + background-color: #c1d7e2; } + .list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #123a4f; + background-color: #b0ccda; } + .list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #123a4f; + border-color: #123a4f; } + +.list-group-item-secondary { + color: #854a00; + background-color: #ffe0b8; } + .list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #854a00; + background-color: #ffd59f; } + .list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #854a00; + border-color: #854a00; } + +.list-group-item-success { + color: #124d2d; + background-color: #c1e1d0; } + .list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #124d2d; + background-color: #b0d9c3; } + .list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #124d2d; + border-color: #124d2d; } + +.list-group-item-info { + color: #0b5d6e; + background-color: #bde9f3; } + .list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #0b5d6e; + background-color: #a7e2ef; } + .list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0b5d6e; + border-color: #0b5d6e; } + +.list-group-item-warning { + color: #856322; + background-color: #ffedca; } + .list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #856322; + background-color: #ffe4b1; } + .list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #856322; + border-color: #856322; } + +.list-group-item-danger { + color: #571319; + background-color: #e6c2c5; } + .list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #571319; + background-color: #dfb0b4; } + .list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #571319; + border-color: #571319; } + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe; } + .list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818182; + background-color: #ececf6; } + .list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182; } + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca; } + .list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #1b1e21; + background-color: #b9bbbe; } + .list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21; } + +.list-group-item-debug { + color: #0b5d6e; + background-color: #bde9f3; } + .list-group-item-debug.list-group-item-action:hover, .list-group-item-debug.list-group-item-action:focus { + color: #0b5d6e; + background-color: #a7e2ef; } + .list-group-item-debug.list-group-item-action.active { + color: #fff; + background-color: #0b5d6e; + border-color: #0b5d6e; } + +.list-group-item-error { + color: #571319; + background-color: #e6c2c5; } + .list-group-item-error.list-group-item-action:hover, .list-group-item-error.list-group-item-action:focus { + color: #571319; + background-color: #dfb0b4; } + .list-group-item-error.list-group-item-action.active { + color: #fff; + background-color: #571319; + border-color: #571319; } + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; } + .close:hover { + color: #000; + text-decoration: none; } + .close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { + opacity: .75; } + +button.close { + padding: 0; + background-color: transparent; + border: 0; } + +a.close.disabled { + pointer-events: none; } + +.toast { + flex-basis: 350px; + max-width: 350px; + font-size: 0.875rem; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + opacity: 0; + border-radius: 0.25rem; } + .toast:not(:last-child) { + margin-bottom: 0.75rem; } + .toast.showing { + opacity: 1; } + .toast.show { + display: block; + opacity: 1; } + .toast.hide { + display: none; } + +.toast-header { + display: flex; + align-items: center; + padding: 0.25rem 0.75rem; + color: #6c757d; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); } + +.toast-body { + padding: 0.75rem; } + +.modal-open { + overflow: hidden; } + .modal-open .modal { + overflow-x: hidden; + overflow-y: auto; } + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0; } + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; } + .modal.fade .modal-dialog { + transition: transform 0.3s ease-out; + transform: translate(0, -50px); } + @media (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none; } } + .modal.show .modal-dialog { + transform: none; } + .modal.modal-static .modal-dialog { + transform: scale(1.02); } + +.modal-dialog-scrollable { + display: flex; + max-height: calc(100% - 1rem); } + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 1rem); + overflow: hidden; } + .modal-dialog-scrollable .modal-header, + .modal-dialog-scrollable .modal-footer { + flex-shrink: 0; } + .modal-dialog-scrollable .modal-body { + overflow-y: auto; } + +.modal-dialog-centered { + display: flex; + align-items: center; + min-height: calc(100% - 1rem); } + .modal-dialog-centered::before { + display: block; + height: calc(100vh - 1rem); + height: min-content; + content: ""; } + .modal-dialog-centered.modal-dialog-scrollable { + flex-direction: column; + justify-content: center; + height: 100%; } + .modal-dialog-centered.modal-dialog-scrollable .modal-content { + max-height: none; } + .modal-dialog-centered.modal-dialog-scrollable::before { + content: none; } + +.modal-content { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; + outline: 0; } + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; } + .modal-backdrop.fade { + opacity: 0; } + .modal-backdrop.show { + opacity: 0.5; } + +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 1rem 1rem; + border-bottom: 1px solid #dee2e6; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } + .modal-header .close { + padding: 1rem 1rem; + margin: -1rem -1rem -1rem auto; } + +.modal-title { + margin-bottom: 0; + line-height: 1.5; } + +.modal-body { + position: relative; + flex: 1 1 auto; + padding: 1rem; } + +.modal-footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + padding: 0.75rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); } + .modal-footer > * { + margin: 0.25rem; } + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; } + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; } + .modal-dialog-scrollable { + max-height: calc(100% - 3.5rem); } + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 3.5rem); } + .modal-dialog-centered { + min-height: calc(100% - 3.5rem); } + .modal-dialog-centered::before { + height: calc(100vh - 3.5rem); + height: min-content; } + .modal-sm { + max-width: 300px; } } + +@media (min-width: 992px) { + .modal-lg, + .modal-xl { + max-width: 800px; } } + +@media (min-width: 1200px) { + .modal-xl { + max-width: 1140px; } } + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; } + .tooltip.show { + opacity: 0.9; } + .tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; } + .tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; } + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; } + .bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; } + .bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; } + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; } + .bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; } + .bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; } + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; } + .bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; } + .bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; } + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; } + .bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; } + .bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; } + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.25rem; } + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; } + .popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; } + .popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; } + +.bs-popover-top, .bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.5rem; } + .bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { + bottom: calc(-0.5rem - 1px); } + .bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); } + .bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; } + +.bs-popover-right, .bs-popover-auto[x-placement^="right"] { + margin-left: 0.5rem; } + .bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { + left: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; } + .bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); } + .bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; } + +.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.5rem; } + .bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { + top: calc(-0.5rem - 1px); } + .bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); } + .bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; } + .bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; } + +.bs-popover-left, .bs-popover-auto[x-placement^="left"] { + margin-right: 0.5rem; } + .bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { + right: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; } + .bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); } + .bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; } + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } + .popover-header:empty { + display: none; } + +.popover-body { + padding: 0.5rem 0.75rem; + color: #212529; } + +.carousel { + position: relative; } + +.carousel.pointer-event { + touch-action: pan-y; } + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; } + .carousel-inner::after { + display: block; + clear: both; + content: ""; } + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + backface-visibility: hidden; + transition: transform 0.6s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .carousel-item { + transition: none; } } + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; } + +.carousel-item-next:not(.carousel-item-left), +.active.carousel-item-right { + transform: translateX(100%); } + +.carousel-item-prev:not(.carousel-item-right), +.active.carousel-item-left { + transform: translateX(-100%); } + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none; } + +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-left, +.carousel-fade .carousel-item-prev.carousel-item-right { + z-index: 1; + opacity: 1; } + +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; } + @media (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-left, + .carousel-fade .active.carousel-item-right { + transition: none; } } + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: 0.5; + transition: opacity 0.15s ease; } + @media (prefers-reduced-motion: reduce) { + .carousel-control-prev, + .carousel-control-next { + transition: none; } } + .carousel-control-prev:hover, .carousel-control-prev:focus, + .carousel-control-next:hover, + .carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; } + +.carousel-control-prev { + left: 0; } + +.carousel-control-next { + right: 0; } + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: 50% / 100% 100% no-repeat; } + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); } + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); } + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: flex; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; } + .carousel-indicators li { + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + transition: opacity 0.6s ease; } + @media (prefers-reduced-motion: reduce) { + .carousel-indicators li { + transition: none; } } + .carousel-indicators .active { + opacity: 1; } + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; } + +@keyframes spinner-border { + to { + transform: rotate(360deg); } } + +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + animation: .75s linear infinite spinner-border; } + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.2em; } + +@keyframes spinner-grow { + 0% { + transform: scale(0); } + 50% { + opacity: 1; + transform: none; } } + +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + animation: .75s linear infinite spinner-grow; } + +.spinner-grow-sm { + width: 1rem; + height: 1rem; } + +@media (prefers-reduced-motion: reduce) { + .spinner-border, + .spinner-grow { + animation-duration: 1.5s; } } + +.align-baseline { + vertical-align: baseline !important; } + +.align-top { + vertical-align: top !important; } + +.align-middle { + vertical-align: middle !important; } + +.align-bottom { + vertical-align: bottom !important; } + +.align-text-bottom { + vertical-align: text-bottom !important; } + +.align-text-top { + vertical-align: text-top !important; } + +.bg-primary, .card-feeding .card-header { + background-color: #226f97 !important; } + +a.bg-primary:hover, .card-feeding a.card-header:hover, a.bg-primary:focus, .card-feeding a.card-header:focus, +button.bg-primary:hover, +.card-feeding button.card-header:hover, +button.bg-primary:focus, +.card-feeding button.card-header:focus { + background-color: #19506d !important; } + +.bg-secondary, .card-sleep .card-header { + background-color: #ff8f00 !important; } + +a.bg-secondary:hover, .card-sleep a.card-header:hover, a.bg-secondary:focus, .card-sleep a.card-header:focus, +button.bg-secondary:hover, +.card-sleep button.card-header:hover, +button.bg-secondary:focus, +.card-sleep button.card-header:focus { + background-color: #cc7200 !important; } + +.bg-success, .card-tummytime .card-header { + background-color: #239556 !important; } + +a.bg-success:hover, .card-tummytime a.card-header:hover, a.bg-success:focus, .card-tummytime a.card-header:focus, +button.bg-success:hover, +.card-tummytime button.card-header:hover, +button.bg-success:focus, +.card-tummytime button.card-header:focus { + background-color: #196c3e !important; } + +.bg-info { + background-color: #15b2d3 !important; } + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #108ba5 !important; } + +.bg-warning { + background-color: #ffbe42 !important; } + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #ffac0f !important; } + +.bg-danger, .card-diaperchange .card-header { + background-color: #a72431 !important; } + +a.bg-danger:hover, .card-diaperchange a.card-header:hover, a.bg-danger:focus, .card-diaperchange a.card-header:focus, +button.bg-danger:hover, +.card-diaperchange button.card-header:hover, +button.bg-danger:focus, +.card-diaperchange button.card-header:focus { + background-color: #7d1b25 !important; } + +.bg-light, .card-statistics .card-header, .card-timer .card-header { + background-color: #f8f9fa !important; } + +a.bg-light:hover, .card-statistics a.card-header:hover, .card-timer a.card-header:hover, a.bg-light:focus, .card-statistics a.card-header:focus, .card-timer a.card-header:focus, +button.bg-light:hover, +.card-statistics button.card-header:hover, +.card-timer button.card-header:hover, +button.bg-light:focus, +.card-statistics button.card-header:focus, +.card-timer button.card-header:focus { + background-color: #dae0e5 !important; } + +.bg-dark { + background-color: #343a40 !important; } + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #1d2124 !important; } + +.bg-debug { + background-color: #15b2d3 !important; } + +a.bg-debug:hover, a.bg-debug:focus, +button.bg-debug:hover, +button.bg-debug:focus { + background-color: #108ba5 !important; } + +.bg-error { + background-color: #a72431 !important; } + +a.bg-error:hover, a.bg-error:focus, +button.bg-error:hover, +button.bg-error:focus { + background-color: #7d1b25 !important; } + +.bg-white { + background-color: #fff !important; } + +.bg-transparent { + background-color: transparent !important; } + +.border { + border: 1px solid #e9ecef !important; } + +.border-top { + border-top: 1px solid #e9ecef !important; } + +.border-right { + border-right: 1px solid #e9ecef !important; } + +.border-bottom { + border-bottom: 1px solid #e9ecef !important; } + +.border-left { + border-left: 1px solid #e9ecef !important; } + +.border-0 { + border: 0 !important; } + +.border-top-0 { + border-top: 0 !important; } + +.border-right-0 { + border-right: 0 !important; } + +.border-bottom-0 { + border-bottom: 0 !important; } + +.border-left-0 { + border-left: 0 !important; } + +.border-primary, .card-feeding { + border-color: #226f97 !important; } + +.border-secondary, .card-sleep { + border-color: #ff8f00 !important; } + +.border-success, .card-tummytime { + border-color: #239556 !important; } + +.border-info { + border-color: #15b2d3 !important; } + +.border-warning { + border-color: #ffbe42 !important; } + +.border-danger, .card-diaperchange { + border-color: #a72431 !important; } + +.border-light, .card-statistics, .card-timer { + border-color: #f8f9fa !important; } + +.border-dark { + border-color: #343a40 !important; } + +.border-debug { + border-color: #15b2d3 !important; } + +.border-error { + border-color: #a72431 !important; } + +.border-white { + border-color: #fff !important; } + +.rounded-sm { + border-radius: 0.2rem !important; } + +.rounded { + border-radius: 0.25rem !important; } + +.rounded-top { + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; } + +.rounded-right { + border-top-right-radius: 0.25rem !important; + border-bottom-right-radius: 0.25rem !important; } + +.rounded-bottom { + border-bottom-right-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; } + +.rounded-left { + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; } + +.rounded-lg { + border-radius: 0.3rem !important; } + +.rounded-circle { + border-radius: 50% !important; } + +.rounded-pill { + border-radius: 50rem !important; } + +.rounded-0 { + border-radius: 0 !important; } + +.clearfix::after { + display: block; + clear: both; + content: ""; } + +.d-none { + display: none !important; } + +.d-inline { + display: inline !important; } + +.d-inline-block { + display: inline-block !important; } + +.d-block { + display: block !important; } + +.d-table { + display: table !important; } + +.d-table-row { + display: table-row !important; } + +.d-table-cell { + display: table-cell !important; } + +.d-flex { + display: flex !important; } + +.d-inline-flex { + display: inline-flex !important; } + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; } + .d-sm-inline { + display: inline !important; } + .d-sm-inline-block { + display: inline-block !important; } + .d-sm-block { + display: block !important; } + .d-sm-table { + display: table !important; } + .d-sm-table-row { + display: table-row !important; } + .d-sm-table-cell { + display: table-cell !important; } + .d-sm-flex { + display: flex !important; } + .d-sm-inline-flex { + display: inline-flex !important; } } + +@media (min-width: 768px) { + .d-md-none { + display: none !important; } + .d-md-inline { + display: inline !important; } + .d-md-inline-block { + display: inline-block !important; } + .d-md-block { + display: block !important; } + .d-md-table { + display: table !important; } + .d-md-table-row { + display: table-row !important; } + .d-md-table-cell { + display: table-cell !important; } + .d-md-flex { + display: flex !important; } + .d-md-inline-flex { + display: inline-flex !important; } } + +@media (min-width: 992px) { + .d-lg-none { + display: none !important; } + .d-lg-inline { + display: inline !important; } + .d-lg-inline-block { + display: inline-block !important; } + .d-lg-block { + display: block !important; } + .d-lg-table { + display: table !important; } + .d-lg-table-row { + display: table-row !important; } + .d-lg-table-cell { + display: table-cell !important; } + .d-lg-flex { + display: flex !important; } + .d-lg-inline-flex { + display: inline-flex !important; } } + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; } + .d-xl-inline { + display: inline !important; } + .d-xl-inline-block { + display: inline-block !important; } + .d-xl-block { + display: block !important; } + .d-xl-table { + display: table !important; } + .d-xl-table-row { + display: table-row !important; } + .d-xl-table-cell { + display: table-cell !important; } + .d-xl-flex { + display: flex !important; } + .d-xl-inline-flex { + display: inline-flex !important; } } + +@media print { + .d-print-none { + display: none !important; } + .d-print-inline { + display: inline !important; } + .d-print-inline-block { + display: inline-block !important; } + .d-print-block { + display: block !important; } + .d-print-table { + display: table !important; } + .d-print-table-row { + display: table-row !important; } + .d-print-table-cell { + display: table-cell !important; } + .d-print-flex { + display: flex !important; } + .d-print-inline-flex { + display: inline-flex !important; } } + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; } + .embed-responsive::before { + display: block; + content: ""; } + .embed-responsive .embed-responsive-item, + .embed-responsive iframe, + .embed-responsive embed, + .embed-responsive object, + .embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; } + +.embed-responsive-21by9::before { + padding-top: 42.85714%; } + +.embed-responsive-16by9::before { + padding-top: 56.25%; } + +.embed-responsive-4by3::before { + padding-top: 75%; } + +.embed-responsive-1by1::before { + padding-top: 100%; } + +.embed-responsive-21by9::before { + padding-top: 42.85714%; } + +.embed-responsive-16by9::before { + padding-top: 56.25%; } + +.embed-responsive-4by3::before { + padding-top: 75%; } + +.embed-responsive-1by1::before { + padding-top: 100%; } + +.flex-row { + flex-direction: row !important; } + +.flex-column { + flex-direction: column !important; } + +.flex-row-reverse { + flex-direction: row-reverse !important; } + +.flex-column-reverse { + flex-direction: column-reverse !important; } + +.flex-wrap { + flex-wrap: wrap !important; } + +.flex-nowrap { + flex-wrap: nowrap !important; } + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; } + +.flex-fill { + flex: 1 1 auto !important; } + +.flex-grow-0 { + flex-grow: 0 !important; } + +.flex-grow-1 { + flex-grow: 1 !important; } + +.flex-shrink-0 { + flex-shrink: 0 !important; } + +.flex-shrink-1 { + flex-shrink: 1 !important; } + +.justify-content-start { + justify-content: flex-start !important; } + +.justify-content-end { + justify-content: flex-end !important; } + +.justify-content-center { + justify-content: center !important; } + +.justify-content-between { + justify-content: space-between !important; } + +.justify-content-around { + justify-content: space-around !important; } + +.align-items-start { + align-items: flex-start !important; } + +.align-items-end { + align-items: flex-end !important; } + +.align-items-center { + align-items: center !important; } + +.align-items-baseline { + align-items: baseline !important; } + +.align-items-stretch { + align-items: stretch !important; } + +.align-content-start { + align-content: flex-start !important; } + +.align-content-end { + align-content: flex-end !important; } + +.align-content-center { + align-content: center !important; } + +.align-content-between { + align-content: space-between !important; } + +.align-content-around { + align-content: space-around !important; } + +.align-content-stretch { + align-content: stretch !important; } + +.align-self-auto { + align-self: auto !important; } + +.align-self-start { + align-self: flex-start !important; } + +.align-self-end { + align-self: flex-end !important; } + +.align-self-center { + align-self: center !important; } + +.align-self-baseline { + align-self: baseline !important; } + +.align-self-stretch { + align-self: stretch !important; } + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important; } + .flex-sm-column { + flex-direction: column !important; } + .flex-sm-row-reverse { + flex-direction: row-reverse !important; } + .flex-sm-column-reverse { + flex-direction: column-reverse !important; } + .flex-sm-wrap { + flex-wrap: wrap !important; } + .flex-sm-nowrap { + flex-wrap: nowrap !important; } + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; } + .flex-sm-fill { + flex: 1 1 auto !important; } + .flex-sm-grow-0 { + flex-grow: 0 !important; } + .flex-sm-grow-1 { + flex-grow: 1 !important; } + .flex-sm-shrink-0 { + flex-shrink: 0 !important; } + .flex-sm-shrink-1 { + flex-shrink: 1 !important; } + .justify-content-sm-start { + justify-content: flex-start !important; } + .justify-content-sm-end { + justify-content: flex-end !important; } + .justify-content-sm-center { + justify-content: center !important; } + .justify-content-sm-between { + justify-content: space-between !important; } + .justify-content-sm-around { + justify-content: space-around !important; } + .align-items-sm-start { + align-items: flex-start !important; } + .align-items-sm-end { + align-items: flex-end !important; } + .align-items-sm-center { + align-items: center !important; } + .align-items-sm-baseline { + align-items: baseline !important; } + .align-items-sm-stretch { + align-items: stretch !important; } + .align-content-sm-start { + align-content: flex-start !important; } + .align-content-sm-end { + align-content: flex-end !important; } + .align-content-sm-center { + align-content: center !important; } + .align-content-sm-between { + align-content: space-between !important; } + .align-content-sm-around { + align-content: space-around !important; } + .align-content-sm-stretch { + align-content: stretch !important; } + .align-self-sm-auto { + align-self: auto !important; } + .align-self-sm-start { + align-self: flex-start !important; } + .align-self-sm-end { + align-self: flex-end !important; } + .align-self-sm-center { + align-self: center !important; } + .align-self-sm-baseline { + align-self: baseline !important; } + .align-self-sm-stretch { + align-self: stretch !important; } } + +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important; } + .flex-md-column { + flex-direction: column !important; } + .flex-md-row-reverse { + flex-direction: row-reverse !important; } + .flex-md-column-reverse { + flex-direction: column-reverse !important; } + .flex-md-wrap { + flex-wrap: wrap !important; } + .flex-md-nowrap { + flex-wrap: nowrap !important; } + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; } + .flex-md-fill { + flex: 1 1 auto !important; } + .flex-md-grow-0 { + flex-grow: 0 !important; } + .flex-md-grow-1 { + flex-grow: 1 !important; } + .flex-md-shrink-0 { + flex-shrink: 0 !important; } + .flex-md-shrink-1 { + flex-shrink: 1 !important; } + .justify-content-md-start { + justify-content: flex-start !important; } + .justify-content-md-end { + justify-content: flex-end !important; } + .justify-content-md-center { + justify-content: center !important; } + .justify-content-md-between { + justify-content: space-between !important; } + .justify-content-md-around { + justify-content: space-around !important; } + .align-items-md-start { + align-items: flex-start !important; } + .align-items-md-end { + align-items: flex-end !important; } + .align-items-md-center { + align-items: center !important; } + .align-items-md-baseline { + align-items: baseline !important; } + .align-items-md-stretch { + align-items: stretch !important; } + .align-content-md-start { + align-content: flex-start !important; } + .align-content-md-end { + align-content: flex-end !important; } + .align-content-md-center { + align-content: center !important; } + .align-content-md-between { + align-content: space-between !important; } + .align-content-md-around { + align-content: space-around !important; } + .align-content-md-stretch { + align-content: stretch !important; } + .align-self-md-auto { + align-self: auto !important; } + .align-self-md-start { + align-self: flex-start !important; } + .align-self-md-end { + align-self: flex-end !important; } + .align-self-md-center { + align-self: center !important; } + .align-self-md-baseline { + align-self: baseline !important; } + .align-self-md-stretch { + align-self: stretch !important; } } + +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important; } + .flex-lg-column { + flex-direction: column !important; } + .flex-lg-row-reverse { + flex-direction: row-reverse !important; } + .flex-lg-column-reverse { + flex-direction: column-reverse !important; } + .flex-lg-wrap { + flex-wrap: wrap !important; } + .flex-lg-nowrap { + flex-wrap: nowrap !important; } + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; } + .flex-lg-fill { + flex: 1 1 auto !important; } + .flex-lg-grow-0 { + flex-grow: 0 !important; } + .flex-lg-grow-1 { + flex-grow: 1 !important; } + .flex-lg-shrink-0 { + flex-shrink: 0 !important; } + .flex-lg-shrink-1 { + flex-shrink: 1 !important; } + .justify-content-lg-start { + justify-content: flex-start !important; } + .justify-content-lg-end { + justify-content: flex-end !important; } + .justify-content-lg-center { + justify-content: center !important; } + .justify-content-lg-between { + justify-content: space-between !important; } + .justify-content-lg-around { + justify-content: space-around !important; } + .align-items-lg-start { + align-items: flex-start !important; } + .align-items-lg-end { + align-items: flex-end !important; } + .align-items-lg-center { + align-items: center !important; } + .align-items-lg-baseline { + align-items: baseline !important; } + .align-items-lg-stretch { + align-items: stretch !important; } + .align-content-lg-start { + align-content: flex-start !important; } + .align-content-lg-end { + align-content: flex-end !important; } + .align-content-lg-center { + align-content: center !important; } + .align-content-lg-between { + align-content: space-between !important; } + .align-content-lg-around { + align-content: space-around !important; } + .align-content-lg-stretch { + align-content: stretch !important; } + .align-self-lg-auto { + align-self: auto !important; } + .align-self-lg-start { + align-self: flex-start !important; } + .align-self-lg-end { + align-self: flex-end !important; } + .align-self-lg-center { + align-self: center !important; } + .align-self-lg-baseline { + align-self: baseline !important; } + .align-self-lg-stretch { + align-self: stretch !important; } } + +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important; } + .flex-xl-column { + flex-direction: column !important; } + .flex-xl-row-reverse { + flex-direction: row-reverse !important; } + .flex-xl-column-reverse { + flex-direction: column-reverse !important; } + .flex-xl-wrap { + flex-wrap: wrap !important; } + .flex-xl-nowrap { + flex-wrap: nowrap !important; } + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; } + .flex-xl-fill { + flex: 1 1 auto !important; } + .flex-xl-grow-0 { + flex-grow: 0 !important; } + .flex-xl-grow-1 { + flex-grow: 1 !important; } + .flex-xl-shrink-0 { + flex-shrink: 0 !important; } + .flex-xl-shrink-1 { + flex-shrink: 1 !important; } + .justify-content-xl-start { + justify-content: flex-start !important; } + .justify-content-xl-end { + justify-content: flex-end !important; } + .justify-content-xl-center { + justify-content: center !important; } + .justify-content-xl-between { + justify-content: space-between !important; } + .justify-content-xl-around { + justify-content: space-around !important; } + .align-items-xl-start { + align-items: flex-start !important; } + .align-items-xl-end { + align-items: flex-end !important; } + .align-items-xl-center { + align-items: center !important; } + .align-items-xl-baseline { + align-items: baseline !important; } + .align-items-xl-stretch { + align-items: stretch !important; } + .align-content-xl-start { + align-content: flex-start !important; } + .align-content-xl-end { + align-content: flex-end !important; } + .align-content-xl-center { + align-content: center !important; } + .align-content-xl-between { + align-content: space-between !important; } + .align-content-xl-around { + align-content: space-around !important; } + .align-content-xl-stretch { + align-content: stretch !important; } + .align-self-xl-auto { + align-self: auto !important; } + .align-self-xl-start { + align-self: flex-start !important; } + .align-self-xl-end { + align-self: flex-end !important; } + .align-self-xl-center { + align-self: center !important; } + .align-self-xl-baseline { + align-self: baseline !important; } + .align-self-xl-stretch { + align-self: stretch !important; } } + +.float-left { + float: left !important; } + +.float-right { + float: right !important; } + +.float-none { + float: none !important; } + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; } + .float-sm-right { + float: right !important; } + .float-sm-none { + float: none !important; } } + +@media (min-width: 768px) { + .float-md-left { + float: left !important; } + .float-md-right { + float: right !important; } + .float-md-none { + float: none !important; } } + +@media (min-width: 992px) { + .float-lg-left { + float: left !important; } + .float-lg-right { + float: right !important; } + .float-lg-none { + float: none !important; } } + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; } + .float-xl-right { + float: right !important; } + .float-xl-none { + float: none !important; } } + +.user-select-all { + user-select: all !important; } + +.user-select-auto { + user-select: auto !important; } + +.user-select-none { + user-select: none !important; } + +.overflow-auto { + overflow: auto !important; } + +.overflow-hidden { + overflow: hidden !important; } + +.position-static { + position: static !important; } + +.position-relative { + position: relative !important; } + +.position-absolute { + position: absolute !important; } + +.position-fixed { + position: fixed !important; } + +.position-sticky { + position: sticky !important; } + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; } + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; } + +@supports (position: sticky) { + .sticky-top { + position: sticky; + top: 0; + z-index: 1020; } } + +.sr-only, .bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after, .bootstrap-datetimepicker-widget .btn[data-action="clear"]::after, .bootstrap-datetimepicker-widget .btn[data-action="today"]::after, .bootstrap-datetimepicker-widget .picker-switch::after, .bootstrap-datetimepicker-widget table th.prev::after, .bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; } + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; } + +.shadow-sm { + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } + +.shadow { + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } + +.shadow-none { + box-shadow: none !important; } + +.w-25 { + width: 25% !important; } + +.w-50 { + width: 50% !important; } + +.w-75 { + width: 75% !important; } + +.w-100 { + width: 100% !important; } + +.w-auto { + width: auto !important; } + +.h-25 { + height: 25% !important; } + +.h-50 { + height: 50% !important; } + +.h-75 { + height: 75% !important; } + +.h-100 { + height: 100% !important; } + +.h-auto { + height: auto !important; } + +.mw-100 { + max-width: 100% !important; } + +.mh-100 { + max-height: 100% !important; } + +.min-vw-100 { + min-width: 100vw !important; } + +.min-vh-100 { + min-height: 100vh !important; } + +.vw-100 { + width: 100vw !important; } + +.vh-100 { + height: 100vh !important; } + +.m-0 { + margin: 0 !important; } + +.mt-0, +.my-0 { + margin-top: 0 !important; } + +.mr-0, +.mx-0 { + margin-right: 0 !important; } + +.mb-0, +.my-0 { + margin-bottom: 0 !important; } + +.ml-0, +.mx-0 { + margin-left: 0 !important; } + +.m-1 { + margin: 0.25rem !important; } + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; } + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; } + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; } + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; } + +.m-2 { + margin: 0.5rem !important; } + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; } + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; } + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; } + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; } + +.m-3 { + margin: 1rem !important; } + +.mt-3, +.my-3 { + margin-top: 1rem !important; } + +.mr-3, +.mx-3 { + margin-right: 1rem !important; } + +.mb-3, .card-dashboard, +.my-3 { + margin-bottom: 1rem !important; } + +.ml-3, +.mx-3 { + margin-left: 1rem !important; } + +.m-4 { + margin: 1.5rem !important; } + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; } + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; } + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; } + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; } + +.m-5 { + margin: 3rem !important; } + +.mt-5, +.my-5 { + margin-top: 3rem !important; } + +.mr-5, +.mx-5 { + margin-right: 3rem !important; } + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; } + +.ml-5, +.mx-5 { + margin-left: 3rem !important; } + +.p-0 { + padding: 0 !important; } + +.pt-0, +.py-0 { + padding-top: 0 !important; } + +.pr-0, +.px-0 { + padding-right: 0 !important; } + +.pb-0, +.py-0 { + padding-bottom: 0 !important; } + +.pl-0, +.px-0 { + padding-left: 0 !important; } + +.p-1 { + padding: 0.25rem !important; } + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; } + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; } + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; } + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; } + +.p-2 { + padding: 0.5rem !important; } + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; } + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; } + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; } + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; } + +.p-3 { + padding: 1rem !important; } + +.pt-3, +.py-3 { + padding-top: 1rem !important; } + +.pr-3, +.px-3 { + padding-right: 1rem !important; } + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; } + +.pl-3, +.px-3 { + padding-left: 1rem !important; } + +.p-4 { + padding: 1.5rem !important; } + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; } + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; } + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; } + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; } + +.p-5 { + padding: 3rem !important; } + +.pt-5, +.py-5 { + padding-top: 3rem !important; } + +.pr-5, +.px-5 { + padding-right: 3rem !important; } + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; } + +.pl-5, +.px-5 { + padding-left: 3rem !important; } + +.m-n1 { + margin: -0.25rem !important; } + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; } + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; } + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; } + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; } + +.m-n2 { + margin: -0.5rem !important; } + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; } + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; } + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; } + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; } + +.m-n3 { + margin: -1rem !important; } + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; } + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; } + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; } + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; } + +.m-n4 { + margin: -1.5rem !important; } + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; } + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; } + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; } + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; } + +.m-n5 { + margin: -3rem !important; } + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; } + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; } + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; } + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; } + +.m-auto { + margin: auto !important; } + +.mt-auto, +.my-auto { + margin-top: auto !important; } + +.mr-auto, +.mx-auto { + margin-right: auto !important; } + +.mb-auto, +.my-auto { + margin-bottom: auto !important; } + +.ml-auto, +.mx-auto { + margin-left: auto !important; } + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; } + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; } + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; } + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; } + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; } + .m-sm-1 { + margin: 0.25rem !important; } + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; } + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; } + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; } + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; } + .m-sm-2 { + margin: 0.5rem !important; } + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; } + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; } + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; } + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; } + .m-sm-3 { + margin: 1rem !important; } + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; } + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; } + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; } + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; } + .m-sm-4 { + margin: 1.5rem !important; } + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; } + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; } + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; } + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; } + .m-sm-5 { + margin: 3rem !important; } + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; } + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; } + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; } + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; } + .p-sm-0 { + padding: 0 !important; } + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; } + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; } + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; } + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; } + .p-sm-1 { + padding: 0.25rem !important; } + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; } + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; } + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; } + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; } + .p-sm-2 { + padding: 0.5rem !important; } + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; } + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; } + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; } + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; } + .p-sm-3 { + padding: 1rem !important; } + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; } + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; } + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; } + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; } + .p-sm-4 { + padding: 1.5rem !important; } + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; } + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; } + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; } + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; } + .p-sm-5 { + padding: 3rem !important; } + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; } + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; } + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; } + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; } + .m-sm-n1 { + margin: -0.25rem !important; } + .mt-sm-n1, + .my-sm-n1 { + margin-top: -0.25rem !important; } + .mr-sm-n1, + .mx-sm-n1 { + margin-right: -0.25rem !important; } + .mb-sm-n1, + .my-sm-n1 { + margin-bottom: -0.25rem !important; } + .ml-sm-n1, + .mx-sm-n1 { + margin-left: -0.25rem !important; } + .m-sm-n2 { + margin: -0.5rem !important; } + .mt-sm-n2, + .my-sm-n2 { + margin-top: -0.5rem !important; } + .mr-sm-n2, + .mx-sm-n2 { + margin-right: -0.5rem !important; } + .mb-sm-n2, + .my-sm-n2 { + margin-bottom: -0.5rem !important; } + .ml-sm-n2, + .mx-sm-n2 { + margin-left: -0.5rem !important; } + .m-sm-n3 { + margin: -1rem !important; } + .mt-sm-n3, + .my-sm-n3 { + margin-top: -1rem !important; } + .mr-sm-n3, + .mx-sm-n3 { + margin-right: -1rem !important; } + .mb-sm-n3, + .my-sm-n3 { + margin-bottom: -1rem !important; } + .ml-sm-n3, + .mx-sm-n3 { + margin-left: -1rem !important; } + .m-sm-n4 { + margin: -1.5rem !important; } + .mt-sm-n4, + .my-sm-n4 { + margin-top: -1.5rem !important; } + .mr-sm-n4, + .mx-sm-n4 { + margin-right: -1.5rem !important; } + .mb-sm-n4, + .my-sm-n4 { + margin-bottom: -1.5rem !important; } + .ml-sm-n4, + .mx-sm-n4 { + margin-left: -1.5rem !important; } + .m-sm-n5 { + margin: -3rem !important; } + .mt-sm-n5, + .my-sm-n5 { + margin-top: -3rem !important; } + .mr-sm-n5, + .mx-sm-n5 { + margin-right: -3rem !important; } + .mb-sm-n5, + .my-sm-n5 { + margin-bottom: -3rem !important; } + .ml-sm-n5, + .mx-sm-n5 { + margin-left: -3rem !important; } + .m-sm-auto { + margin: auto !important; } + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; } + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; } + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; } + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; } } + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; } + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; } + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; } + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; } + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; } + .m-md-1 { + margin: 0.25rem !important; } + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; } + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; } + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; } + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; } + .m-md-2 { + margin: 0.5rem !important; } + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; } + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; } + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; } + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; } + .m-md-3 { + margin: 1rem !important; } + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; } + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; } + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; } + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; } + .m-md-4 { + margin: 1.5rem !important; } + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; } + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; } + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; } + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; } + .m-md-5 { + margin: 3rem !important; } + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; } + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; } + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; } + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; } + .p-md-0 { + padding: 0 !important; } + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; } + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; } + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; } + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; } + .p-md-1 { + padding: 0.25rem !important; } + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; } + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; } + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; } + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; } + .p-md-2 { + padding: 0.5rem !important; } + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; } + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; } + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; } + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; } + .p-md-3 { + padding: 1rem !important; } + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; } + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; } + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; } + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; } + .p-md-4 { + padding: 1.5rem !important; } + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; } + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; } + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; } + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; } + .p-md-5 { + padding: 3rem !important; } + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; } + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; } + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; } + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; } + .m-md-n1 { + margin: -0.25rem !important; } + .mt-md-n1, + .my-md-n1 { + margin-top: -0.25rem !important; } + .mr-md-n1, + .mx-md-n1 { + margin-right: -0.25rem !important; } + .mb-md-n1, + .my-md-n1 { + margin-bottom: -0.25rem !important; } + .ml-md-n1, + .mx-md-n1 { + margin-left: -0.25rem !important; } + .m-md-n2 { + margin: -0.5rem !important; } + .mt-md-n2, + .my-md-n2 { + margin-top: -0.5rem !important; } + .mr-md-n2, + .mx-md-n2 { + margin-right: -0.5rem !important; } + .mb-md-n2, + .my-md-n2 { + margin-bottom: -0.5rem !important; } + .ml-md-n2, + .mx-md-n2 { + margin-left: -0.5rem !important; } + .m-md-n3 { + margin: -1rem !important; } + .mt-md-n3, + .my-md-n3 { + margin-top: -1rem !important; } + .mr-md-n3, + .mx-md-n3 { + margin-right: -1rem !important; } + .mb-md-n3, + .my-md-n3 { + margin-bottom: -1rem !important; } + .ml-md-n3, + .mx-md-n3 { + margin-left: -1rem !important; } + .m-md-n4 { + margin: -1.5rem !important; } + .mt-md-n4, + .my-md-n4 { + margin-top: -1.5rem !important; } + .mr-md-n4, + .mx-md-n4 { + margin-right: -1.5rem !important; } + .mb-md-n4, + .my-md-n4 { + margin-bottom: -1.5rem !important; } + .ml-md-n4, + .mx-md-n4 { + margin-left: -1.5rem !important; } + .m-md-n5 { + margin: -3rem !important; } + .mt-md-n5, + .my-md-n5 { + margin-top: -3rem !important; } + .mr-md-n5, + .mx-md-n5 { + margin-right: -3rem !important; } + .mb-md-n5, + .my-md-n5 { + margin-bottom: -3rem !important; } + .ml-md-n5, + .mx-md-n5 { + margin-left: -3rem !important; } + .m-md-auto { + margin: auto !important; } + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; } + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; } + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; } + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; } } + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; } + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; } + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; } + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; } + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; } + .m-lg-1 { + margin: 0.25rem !important; } + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; } + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; } + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; } + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; } + .m-lg-2 { + margin: 0.5rem !important; } + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; } + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; } + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; } + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; } + .m-lg-3 { + margin: 1rem !important; } + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; } + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; } + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; } + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; } + .m-lg-4 { + margin: 1.5rem !important; } + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; } + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; } + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; } + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; } + .m-lg-5 { + margin: 3rem !important; } + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; } + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; } + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; } + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; } + .p-lg-0 { + padding: 0 !important; } + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; } + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; } + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; } + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; } + .p-lg-1 { + padding: 0.25rem !important; } + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; } + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; } + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; } + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; } + .p-lg-2 { + padding: 0.5rem !important; } + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; } + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; } + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; } + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; } + .p-lg-3 { + padding: 1rem !important; } + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; } + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; } + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; } + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; } + .p-lg-4 { + padding: 1.5rem !important; } + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; } + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; } + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; } + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; } + .p-lg-5 { + padding: 3rem !important; } + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; } + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; } + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; } + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; } + .m-lg-n1 { + margin: -0.25rem !important; } + .mt-lg-n1, + .my-lg-n1 { + margin-top: -0.25rem !important; } + .mr-lg-n1, + .mx-lg-n1 { + margin-right: -0.25rem !important; } + .mb-lg-n1, + .my-lg-n1 { + margin-bottom: -0.25rem !important; } + .ml-lg-n1, + .mx-lg-n1 { + margin-left: -0.25rem !important; } + .m-lg-n2 { + margin: -0.5rem !important; } + .mt-lg-n2, + .my-lg-n2 { + margin-top: -0.5rem !important; } + .mr-lg-n2, + .mx-lg-n2 { + margin-right: -0.5rem !important; } + .mb-lg-n2, + .my-lg-n2 { + margin-bottom: -0.5rem !important; } + .ml-lg-n2, + .mx-lg-n2 { + margin-left: -0.5rem !important; } + .m-lg-n3 { + margin: -1rem !important; } + .mt-lg-n3, + .my-lg-n3 { + margin-top: -1rem !important; } + .mr-lg-n3, + .mx-lg-n3 { + margin-right: -1rem !important; } + .mb-lg-n3, + .my-lg-n3 { + margin-bottom: -1rem !important; } + .ml-lg-n3, + .mx-lg-n3 { + margin-left: -1rem !important; } + .m-lg-n4 { + margin: -1.5rem !important; } + .mt-lg-n4, + .my-lg-n4 { + margin-top: -1.5rem !important; } + .mr-lg-n4, + .mx-lg-n4 { + margin-right: -1.5rem !important; } + .mb-lg-n4, + .my-lg-n4 { + margin-bottom: -1.5rem !important; } + .ml-lg-n4, + .mx-lg-n4 { + margin-left: -1.5rem !important; } + .m-lg-n5 { + margin: -3rem !important; } + .mt-lg-n5, + .my-lg-n5 { + margin-top: -3rem !important; } + .mr-lg-n5, + .mx-lg-n5 { + margin-right: -3rem !important; } + .mb-lg-n5, + .my-lg-n5 { + margin-bottom: -3rem !important; } + .ml-lg-n5, + .mx-lg-n5 { + margin-left: -3rem !important; } + .m-lg-auto { + margin: auto !important; } + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; } + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; } + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; } + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; } } + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; } + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; } + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; } + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; } + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; } + .m-xl-1 { + margin: 0.25rem !important; } + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; } + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; } + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; } + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; } + .m-xl-2 { + margin: 0.5rem !important; } + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; } + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; } + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; } + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; } + .m-xl-3 { + margin: 1rem !important; } + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; } + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; } + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; } + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; } + .m-xl-4 { + margin: 1.5rem !important; } + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; } + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; } + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; } + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; } + .m-xl-5 { + margin: 3rem !important; } + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; } + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; } + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; } + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; } + .p-xl-0 { + padding: 0 !important; } + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; } + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; } + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; } + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; } + .p-xl-1 { + padding: 0.25rem !important; } + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; } + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; } + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; } + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; } + .p-xl-2 { + padding: 0.5rem !important; } + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; } + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; } + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; } + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; } + .p-xl-3 { + padding: 1rem !important; } + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; } + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; } + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; } + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; } + .p-xl-4 { + padding: 1.5rem !important; } + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; } + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; } + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; } + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; } + .p-xl-5 { + padding: 3rem !important; } + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; } + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; } + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; } + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; } + .m-xl-n1 { + margin: -0.25rem !important; } + .mt-xl-n1, + .my-xl-n1 { + margin-top: -0.25rem !important; } + .mr-xl-n1, + .mx-xl-n1 { + margin-right: -0.25rem !important; } + .mb-xl-n1, + .my-xl-n1 { + margin-bottom: -0.25rem !important; } + .ml-xl-n1, + .mx-xl-n1 { + margin-left: -0.25rem !important; } + .m-xl-n2 { + margin: -0.5rem !important; } + .mt-xl-n2, + .my-xl-n2 { + margin-top: -0.5rem !important; } + .mr-xl-n2, + .mx-xl-n2 { + margin-right: -0.5rem !important; } + .mb-xl-n2, + .my-xl-n2 { + margin-bottom: -0.5rem !important; } + .ml-xl-n2, + .mx-xl-n2 { + margin-left: -0.5rem !important; } + .m-xl-n3 { + margin: -1rem !important; } + .mt-xl-n3, + .my-xl-n3 { + margin-top: -1rem !important; } + .mr-xl-n3, + .mx-xl-n3 { + margin-right: -1rem !important; } + .mb-xl-n3, + .my-xl-n3 { + margin-bottom: -1rem !important; } + .ml-xl-n3, + .mx-xl-n3 { + margin-left: -1rem !important; } + .m-xl-n4 { + margin: -1.5rem !important; } + .mt-xl-n4, + .my-xl-n4 { + margin-top: -1.5rem !important; } + .mr-xl-n4, + .mx-xl-n4 { + margin-right: -1.5rem !important; } + .mb-xl-n4, + .my-xl-n4 { + margin-bottom: -1.5rem !important; } + .ml-xl-n4, + .mx-xl-n4 { + margin-left: -1.5rem !important; } + .m-xl-n5 { + margin: -3rem !important; } + .mt-xl-n5, + .my-xl-n5 { + margin-top: -3rem !important; } + .mr-xl-n5, + .mx-xl-n5 { + margin-right: -3rem !important; } + .mb-xl-n5, + .my-xl-n5 { + margin-bottom: -3rem !important; } + .ml-xl-n5, + .mx-xl-n5 { + margin-left: -3rem !important; } + .m-xl-auto { + margin: auto !important; } + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; } + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; } + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; } + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; } } + +.stretched-link::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: rgba(0, 0, 0, 0); } + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; } + +.text-justify { + text-align: justify !important; } + +.text-wrap { + white-space: normal !important; } + +.text-nowrap { + white-space: nowrap !important; } + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + +.text-left { + text-align: left !important; } + +.text-right { + text-align: right !important; } + +.text-center { + text-align: center !important; } + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; } + .text-sm-right { + text-align: right !important; } + .text-sm-center { + text-align: center !important; } } + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; } + .text-md-right { + text-align: right !important; } + .text-md-center { + text-align: center !important; } } + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; } + .text-lg-right { + text-align: right !important; } + .text-lg-center { + text-align: center !important; } } + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; } + .text-xl-right { + text-align: right !important; } + .text-xl-center { + text-align: center !important; } } + +.text-lowercase { + text-transform: lowercase !important; } + +.text-uppercase { + text-transform: uppercase !important; } + +.text-capitalize { + text-transform: capitalize !important; } + +.font-weight-light { + font-weight: 300 !important; } + +.font-weight-lighter { + font-weight: lighter !important; } + +.font-weight-normal { + font-weight: 400 !important; } + +.font-weight-bold { + font-weight: 700 !important; } + +.font-weight-bolder { + font-weight: bolder !important; } + +.font-italic { + font-style: italic !important; } + +.text-white, .card-diaperchange .card-header, .card-feeding .card-header, .card-sleep .card-header, .card-tummytime .card-header { + color: #fff !important; } + +.text-primary, .card-feeding .card-body { + color: #226f97 !important; } + +a.text-primary:hover, .card-feeding a.card-body:hover, a.text-primary:focus, .card-feeding a.card-body:focus { + color: #144159 !important; } + +.text-secondary { + color: #ff8f00 !important; } + +a.text-secondary:hover, a.text-secondary:focus { + color: #b36400 !important; } + +.text-success, .card-tummytime .card-body { + color: #239556 !important; } + +a.text-success:hover, .card-tummytime a.card-body:hover, a.text-success:focus, .card-tummytime a.card-body:focus { + color: #145732 !important; } + +.text-info { + color: #15b2d3 !important; } + +a.text-info:hover, a.text-info:focus { + color: #0e778d !important; } + +.text-warning { + color: #ffbe42 !important; } + +a.text-warning:hover, a.text-warning:focus { + color: #f5a000 !important; } + +.text-danger, .card-diaperchange .card-body { + color: #a72431 !important; } + +a.text-danger:hover, .card-diaperchange a.card-body:hover, a.text-danger:focus, .card-diaperchange a.card-body:focus { + color: #68161f !important; } + +.text-light, .card-statistics .card-body, .card-timer .card-body { + color: #f8f9fa !important; } + +a.text-light:hover, .card-statistics a.card-body:hover, .card-timer a.card-body:hover, a.text-light:focus, .card-statistics a.card-body:focus, .card-timer a.card-body:focus { + color: #cbd3da !important; } + +.text-dark, .card-statistics .card-header, .card-timer .card-header { + color: #343a40 !important; } + +a.text-dark:hover, .card-statistics a.card-header:hover, .card-timer a.card-header:hover, a.text-dark:focus, .card-statistics a.card-header:focus, .card-timer a.card-header:focus { + color: #121416 !important; } + +.text-debug { + color: #15b2d3 !important; } + +a.text-debug:hover, a.text-debug:focus { + color: #0e778d !important; } + +.text-error { + color: #a72431 !important; } + +a.text-error:hover, a.text-error:focus { + color: #68161f !important; } + +.text-body { + color: #ced4da !important; } + +.text-muted, .card-dashboard .card-body .card-text { + color: #6c757d !important; } + +.text-black-50 { + color: rgba(0, 0, 0, 0.5) !important; } + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; } + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; } + +.text-decoration-none { + text-decoration: none !important; } + +.text-break { + word-break: break-word !important; + word-wrap: break-word !important; } + +.text-reset { + color: inherit !important; } + +.visible { + visibility: visible !important; } + +.invisible { + visibility: hidden !important; } + +@media print { + *, + *::before, + *::after { + text-shadow: none !important; + box-shadow: none !important; } + a:not(.btn) { + text-decoration: underline; } + abbr[title]::after { + content: " (" attr(title) ")"; } + pre { + white-space: pre-wrap !important; } + pre, + blockquote { + border: 1px solid #adb5bd; + page-break-inside: avoid; } + thead { + display: table-header-group; } + tr, + img { + page-break-inside: avoid; } + p, + h2, + h3 { + orphans: 3; + widows: 3; } + h2, + h3 { + page-break-after: avoid; } + @page { + size: a3; } + body { + min-width: 992px !important; } + .container { + min-width: 992px !important; } + .navbar { + display: none; } + .badge { + 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 #dee2e6 !important; } + .table-dark { + color: inherit; } + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody + tbody { + border-color: #343a40; } + .table .thead-dark th { + color: inherit; + border-color: #343a40; } } + +.bootstrap-datetimepicker-widget { + list-style: none; } + .bootstrap-datetimepicker-widget.dropdown-menu { + display: block; + margin: 2px 0; + padding: 4px; + width: 14rem; } + @media (min-width: 576px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; } } + @media (min-width: 768px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; } } + @media (min-width: 992px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; } } + .bootstrap-datetimepicker-widget.dropdown-menu:before, .bootstrap-datetimepicker-widget.dropdown-menu:after { + content: ''; + display: inline-block; + position: absolute; } + .bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + top: -7px; + left: 7px; } + .bootstrap-datetimepicker-widget.dropdown-menu.bottom:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + top: -6px; + left: 8px; } + .bootstrap-datetimepicker-widget.dropdown-menu.top:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + bottom: -7px; + left: 6px; } + .bootstrap-datetimepicker-widget.dropdown-menu.top:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid white; + bottom: -6px; + left: 7px; } + .bootstrap-datetimepicker-widget.dropdown-menu.float-right:before { + left: auto; + right: 6px; } + .bootstrap-datetimepicker-widget.dropdown-menu.float-right:after { + left: auto; + right: 7px; } + .bootstrap-datetimepicker-widget.dropdown-menu.wider { + width: 16rem; } + .bootstrap-datetimepicker-widget .list-unstyled { + margin: 0; } + .bootstrap-datetimepicker-widget a[data-action] { + padding: 6px 0; } + .bootstrap-datetimepicker-widget a[data-action]:active { + box-shadow: none; } + .bootstrap-datetimepicker-widget .timepicker-hour, .bootstrap-datetimepicker-widget .timepicker-minute, .bootstrap-datetimepicker-widget .timepicker-second { + width: 54px; + font-weight: bold; + font-size: 1.2em; + margin: 0; } + .bootstrap-datetimepicker-widget button[data-action] { + padding: 6px; } + .bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { + content: "Increment Hours"; } + .bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { + content: "Increment Minutes"; } + .bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { + content: "Decrement Hours"; } + .bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { + content: "Decrement Minutes"; } + .bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { + content: "Show Hours"; } + .bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { + content: "Show Minutes"; } + .bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { + content: "Toggle AM/PM"; } + .bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { + content: "Clear the picker"; } + .bootstrap-datetimepicker-widget .btn[data-action="today"]::after { + content: "Set the date to today"; } + .bootstrap-datetimepicker-widget .picker-switch { + text-align: center; } + .bootstrap-datetimepicker-widget .picker-switch::after { + content: "Toggle Date and Time Screens"; } + .bootstrap-datetimepicker-widget .picker-switch td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; } + .bootstrap-datetimepicker-widget .picker-switch td span { + line-height: 2.5; + height: 2.5em; + width: 100%; } + .bootstrap-datetimepicker-widget table { + width: 100%; + margin: 0; } + .bootstrap-datetimepicker-widget table td, + .bootstrap-datetimepicker-widget table th { + text-align: center; + border-radius: 0.25rem; } + .bootstrap-datetimepicker-widget table th { + height: 20px; + line-height: 20px; + width: 20px; } + .bootstrap-datetimepicker-widget table th.picker-switch { + width: 145px; } + .bootstrap-datetimepicker-widget table th.disabled, .bootstrap-datetimepicker-widget table th.disabled:hover { + background: none; + color: #6c757d; + cursor: not-allowed; } + .bootstrap-datetimepicker-widget table th.prev::after { + content: "Previous Month"; } + .bootstrap-datetimepicker-widget table th.next::after { + content: "Next Month"; } + .bootstrap-datetimepicker-widget table thead tr:first-child th { + cursor: pointer; } + .bootstrap-datetimepicker-widget table thead tr:first-child th:hover { + background: #343a40; } + .bootstrap-datetimepicker-widget table td { + height: 54px; + line-height: 54px; + width: 54px; } + .bootstrap-datetimepicker-widget table td.cw { + font-size: .8em; + height: 20px; + line-height: 20px; + color: #6c757d; } + .bootstrap-datetimepicker-widget table td.day { + height: 20px; + line-height: 20px; + width: 20px; } + .bootstrap-datetimepicker-widget table td.day:hover, .bootstrap-datetimepicker-widget table td.hour:hover, .bootstrap-datetimepicker-widget table td.minute:hover, .bootstrap-datetimepicker-widget table td.second:hover { + background: #343a40; + cursor: pointer; } + .bootstrap-datetimepicker-widget table td.old, .bootstrap-datetimepicker-widget table td.new { + color: #6c757d; } + .bootstrap-datetimepicker-widget table td.today { + position: relative; } + .bootstrap-datetimepicker-widget table td.today:before { + content: ''; + display: inline-block; + border: solid transparent; + border-width: 0 0 7px 7px; + border-bottom-color: #226f97; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; } + .bootstrap-datetimepicker-widget table td.active, .bootstrap-datetimepicker-widget table td.active:hover { + background-color: #226f97; + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } + .bootstrap-datetimepicker-widget table td.active.today:before { + border-bottom-color: #fff; } + .bootstrap-datetimepicker-widget table td.disabled, .bootstrap-datetimepicker-widget table td.disabled:hover { + background: none; + color: #6c757d; + cursor: not-allowed; } + .bootstrap-datetimepicker-widget table td span { + display: inline-block; + width: 54px; + height: 54px; + line-height: 54px; + margin: 2px 1.5px; + cursor: pointer; + border-radius: 0.25rem; } + .bootstrap-datetimepicker-widget table td span:hover { + background: #343a40; } + .bootstrap-datetimepicker-widget table td span.active { + background-color: #226f97; + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } + .bootstrap-datetimepicker-widget table td span.old { + color: #6c757d; } + .bootstrap-datetimepicker-widget table td span.disabled, .bootstrap-datetimepicker-widget table td span.disabled:hover { + background: none; + color: #6c757d; + cursor: not-allowed; } + .bootstrap-datetimepicker-widget.usetwentyfour td.hour { + height: 27px; + line-height: 27px; } + +.input-group [data-toggle="datetimepicker"] { + cursor: pointer; } + +.breadcrumb { + margin-bottom: 0; } + +.btn-xs { + padding: 0.2rem 0.12rem; + font-size: 0.75rem; + line-height: 1; + border-radius: 0.2rem; } + +.dropdown-menu-right { + right: 0; + left: auto; } + +.invalid-feedback { + display: block; } + +.btn-no-hover:hover { + color: inherit; + background-color: inherit; + border-color: inherit; } + +.boolean-label { + pointer-events: none; } + +.bootstrap-datetimepicker-widget { + color: #ced4da; } + +.input-group.datetimepicker .input-group-text { + background: none; + border: 0; + color: #226f97; + padding-left: 0; + padding-right: 0; } + +.input-group.datetimepicker .datetimepicker-input[readonly] { + background: none; + border: 0; + color: #fff; + cursor: pointer; + font-weight: bold; } + +.ptr--ptr { + background: #343a40; } + .ptr--ptr .ptr--text, .ptr--ptr .ptr--icon { + color: #f8f9fa !important; } + +.table-instances tr td, .table-instances tr th { + vertical-align: middle; } + +.table-instances tr.odd { + background: rgba(34, 111, 151, 0.1); } + +.table-instances tr.even { + background: none; } + +.table-instances tr.row-details td { + color: #adb5bd; + padding-top: 0; + border-top: 0; } + +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url("../fonts/fontawesome-webfont.674f50d287a8.eot?v=4.7.0"); + src: url("../fonts/fontawesome-webfont.674f50d287a8.eot?#iefix&v=4.7.0") format("embedded-opentype"), url("../fonts/fontawesome-webfont.af7ae505a9ee.woff2?v=4.7.0") format("woff2"), url("../fonts/fontawesome-webfont.fee66e712a8a.woff?v=4.7.0") format("woff"), url("../fonts/fontawesome-webfont.b06871f281fe.ttf?v=4.7.0") format("truetype"), url("../fonts/fontawesome-webfont.912ec66d7572.svg?v=4.7.0#fontawesomeregular") format("svg"); + font-weight: normal; + font-style: normal; } + +.fa, .icon { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } + +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333em; + line-height: 0.75em; + vertical-align: -15%; } + +.fa-2x, .icon-2x { + font-size: 2em; } + +.fa-3x { + font-size: 3em; } + +.fa-4x { + font-size: 4em; } + +.fa-5x { + font-size: 5em; } + +.fa-fw { + width: 1.28571em; + text-align: center; } + +.fa-ul { + padding-left: 0; + margin-left: 2.14286em; + list-style-type: none; } + .fa-ul > li { + position: relative; } + +.fa-li { + position: absolute; + left: -2.14286em; + width: 2.14286em; + top: 0.14286em; + text-align: center; } + .fa-li.fa-lg { + left: -1.85714em; } + +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eee; + border-radius: .1em; } + +.fa-pull-left { + float: left; } + +.fa-pull-right { + float: right; } + +.fa.fa-pull-left, .fa-pull-left.icon { + margin-right: .3em; } + +.fa.fa-pull-right, .fa-pull-right.icon { + margin-left: .3em; } + +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; } + +.pull-left { + float: left; } + +.fa.pull-left, .pull-left.icon { + margin-right: .3em; } + +.fa.pull-right, .pull-right.icon { + margin-left: .3em; } + +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; } + +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); } + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); } } + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); } } + +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); } + +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); } + +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); } + +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); } + +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); } + +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; } + +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; } + +.fa-stack-1x, .fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; } + +.fa-stack-1x { + line-height: inherit; } + +.fa-stack-2x { + font-size: 2em; } + +.fa-inverse { + color: #fff; } + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: ""; } + +.fa-music:before { + content: ""; } + +.fa-search:before { + content: ""; } + +.fa-envelope-o:before { + content: ""; } + +.fa-heart:before { + content: ""; } + +.fa-star:before { + content: ""; } + +.fa-star-o:before { + content: ""; } + +.fa-user:before, .icon-user:before { + content: ""; } + +.fa-film:before { + content: ""; } + +.fa-th-large:before { + content: ""; } + +.fa-th:before { + content: ""; } + +.fa-th-list:before { + content: ""; } + +.fa-check:before { + content: ""; } + +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: ""; } + +.fa-search-plus:before { + content: ""; } + +.fa-search-minus:before { + content: ""; } + +.fa-power-off:before { + content: ""; } + +.fa-signal:before { + content: ""; } + +.fa-gear:before, +.fa-cog:before { + content: ""; } + +.fa-trash-o:before, .icon-delete:before { + content: ""; } + +.fa-home:before { + content: ""; } + +.fa-file-o:before { + content: ""; } + +.fa-clock-o:before, .icon-timer:before { + content: ""; } + +.fa-road:before { + content: ""; } + +.fa-download:before { + content: ""; } + +.fa-arrow-circle-o-down:before { + content: ""; } + +.fa-arrow-circle-o-up:before { + content: ""; } + +.fa-inbox:before { + content: ""; } + +.fa-play-circle-o:before { + content: ""; } + +.fa-rotate-right:before, +.fa-repeat:before { + content: ""; } + +.fa-refresh:before, .icon-refresh:before { + content: ""; } + +.fa-list-alt:before { + content: ""; } + +.fa-lock:before, .icon-lock:before { + content: ""; } + +.fa-flag:before { + content: ""; } + +.fa-headphones:before { + content: ""; } + +.fa-volume-off:before { + content: ""; } + +.fa-volume-down:before { + content: ""; } + +.fa-volume-up:before { + content: ""; } + +.fa-qrcode:before { + content: ""; } + +.fa-barcode:before { + content: ""; } + +.fa-tag:before { + content: ""; } + +.fa-tags:before { + content: ""; } + +.fa-book:before { + content: ""; } + +.fa-bookmark:before { + content: ""; } + +.fa-print:before { + content: ""; } + +.fa-camera:before, .icon-camera:before { + content: ""; } + +.fa-font:before { + content: ""; } + +.fa-bold:before { + content: ""; } + +.fa-italic:before { + content: ""; } + +.fa-text-height:before { + content: ""; } + +.fa-text-width:before { + content: ""; } + +.fa-align-left:before { + content: ""; } + +.fa-align-center:before { + content: ""; } + +.fa-align-right:before { + content: ""; } + +.fa-align-justify:before { + content: ""; } + +.fa-list:before, .icon-list:before { + content: ""; } + +.fa-dedent:before, +.fa-outdent:before { + content: ""; } + +.fa-indent:before { + content: ""; } + +.fa-video-camera:before { + content: ""; } + +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: ""; } + +.fa-pencil:before, .icon-update:before { + content: ""; } + +.fa-map-marker:before { + content: ""; } + +.fa-adjust:before { + content: ""; } + +.fa-tint:before { + content: ""; } + +.fa-edit:before, +.fa-pencil-square-o:before { + content: ""; } + +.fa-share-square-o:before { + content: ""; } + +.fa-check-square-o:before { + content: ""; } + +.fa-arrows:before { + content: ""; } + +.fa-step-backward:before { + content: ""; } + +.fa-fast-backward:before { + content: ""; } + +.fa-backward:before { + content: ""; } + +.fa-play:before { + content: ""; } + +.fa-pause:before { + content: ""; } + +.fa-stop:before, .icon-stop:before { + content: ""; } + +.fa-forward:before { + content: ""; } + +.fa-fast-forward:before { + content: ""; } + +.fa-step-forward:before { + content: ""; } + +.fa-eject:before { + content: ""; } + +.fa-chevron-left:before, .icon-chevron-left:before { + content: ""; } + +.fa-chevron-right:before, .icon-chevron-right:before { + content: ""; } + +.fa-plus-circle:before { + content: ""; } + +.fa-minus-circle:before { + content: ""; } + +.fa-times-circle:before { + content: ""; } + +.fa-check-circle:before { + content: ""; } + +.fa-question-circle:before { + content: ""; } + +.fa-info-circle:before { + content: ""; } + +.fa-crosshairs:before { + content: ""; } + +.fa-times-circle-o:before, .icon-false:before { + content: ""; } + +.fa-check-circle-o:before, .icon-true:before { + content: ""; } + +.fa-ban:before { + content: ""; } + +.fa-arrow-left:before { + content: ""; } + +.fa-arrow-right:before { + content: ""; } + +.fa-arrow-up:before { + content: ""; } + +.fa-arrow-down:before { + content: ""; } + +.fa-mail-forward:before, +.fa-share:before { + content: ""; } + +.fa-expand:before { + content: ""; } + +.fa-compress:before { + content: ""; } + +.fa-plus:before, .icon-add:before { + content: ""; } + +.fa-minus:before { + content: ""; } + +.fa-asterisk:before { + content: ""; } + +.fa-exclamation-circle:before { + content: ""; } + +.fa-gift:before { + content: ""; } + +.fa-leaf:before { + content: ""; } + +.fa-fire:before { + content: ""; } + +.fa-eye:before { + content: ""; } + +.fa-eye-slash:before { + content: ""; } + +.fa-warning:before, +.fa-exclamation-triangle:before { + content: ""; } + +.fa-plane:before { + content: ""; } + +.fa-calendar:before { + content: ""; } + +.fa-random:before { + content: ""; } + +.fa-comment:before { + content: ""; } + +.fa-magnet:before { + content: ""; } + +.fa-chevron-up:before { + content: ""; } + +.fa-chevron-down:before { + content: ""; } + +.fa-retweet:before { + content: ""; } + +.fa-shopping-cart:before { + content: ""; } + +.fa-folder:before { + content: ""; } + +.fa-folder-open:before { + content: ""; } + +.fa-arrows-v:before { + content: ""; } + +.fa-arrows-h:before { + content: ""; } + +.fa-bar-chart-o:before, .icon-graph:before, +.fa-bar-chart:before { + content: ""; } + +.fa-twitter-square:before { + content: ""; } + +.fa-facebook-square:before { + content: ""; } + +.fa-camera-retro:before { + content: ""; } + +.fa-key:before { + content: ""; } + +.fa-gears:before, +.fa-cogs:before { + content: ""; } + +.fa-comments:before { + content: ""; } + +.fa-thumbs-o-up:before { + content: ""; } + +.fa-thumbs-o-down:before { + content: ""; } + +.fa-star-half:before { + content: ""; } + +.fa-heart-o:before { + content: ""; } + +.fa-sign-out:before { + content: ""; } + +.fa-linkedin-square:before { + content: ""; } + +.fa-thumb-tack:before { + content: ""; } + +.fa-external-link:before { + content: ""; } + +.fa-sign-in:before { + content: ""; } + +.fa-trophy:before { + content: ""; } + +.fa-github-square:before { + content: ""; } + +.fa-upload:before { + content: ""; } + +.fa-lemon-o:before { + content: ""; } + +.fa-phone:before { + content: ""; } + +.fa-square-o:before { + content: ""; } + +.fa-bookmark-o:before { + content: ""; } + +.fa-phone-square:before { + content: ""; } + +.fa-twitter:before { + content: ""; } + +.fa-facebook-f:before, +.fa-facebook:before { + content: ""; } + +.fa-github:before { + content: ""; } + +.fa-unlock:before { + content: ""; } + +.fa-credit-card:before { + content: ""; } + +.fa-feed:before, +.fa-rss:before { + content: ""; } + +.fa-hdd-o:before { + content: ""; } + +.fa-bullhorn:before { + content: ""; } + +.fa-bell:before { + content: ""; } + +.fa-certificate:before { + content: ""; } + +.fa-hand-o-right:before { + content: ""; } + +.fa-hand-o-left:before { + content: ""; } + +.fa-hand-o-up:before { + content: ""; } + +.fa-hand-o-down:before { + content: ""; } + +.fa-arrow-circle-left:before { + content: ""; } + +.fa-arrow-circle-right:before { + content: ""; } + +.fa-arrow-circle-up:before { + content: ""; } + +.fa-arrow-circle-down:before { + content: ""; } + +.fa-globe:before { + content: ""; } + +.fa-wrench:before { + content: ""; } + +.fa-tasks:before { + content: ""; } + +.fa-filter:before { + content: ""; } + +.fa-briefcase:before { + content: ""; } + +.fa-arrows-alt:before { + content: ""; } + +.fa-group:before, +.fa-users:before, +.icon-chat:before { + content: ""; } + +.fa-chain:before, +.fa-link:before { + content: ""; } + +.fa-cloud:before { + content: ""; } + +.fa-flask:before { + content: ""; } + +.fa-cut:before, +.fa-scissors:before { + content: ""; } + +.fa-copy:before, +.fa-files-o:before { + content: ""; } + +.fa-paperclip:before { + content: ""; } + +.fa-save:before, +.fa-floppy-o:before { + content: ""; } + +.fa-square:before { + content: ""; } + +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: ""; } + +.fa-list-ul:before { + content: ""; } + +.fa-list-ol:before { + content: ""; } + +.fa-strikethrough:before { + content: ""; } + +.fa-underline:before { + content: ""; } + +.fa-table:before { + content: ""; } + +.fa-magic:before { + content: ""; } + +.fa-truck:before { + content: ""; } + +.fa-pinterest:before { + content: ""; } + +.fa-pinterest-square:before { + content: ""; } + +.fa-google-plus-square:before { + content: ""; } + +.fa-google-plus:before { + content: ""; } + +.fa-money:before { + content: ""; } + +.fa-caret-down:before { + content: ""; } + +.fa-caret-up:before { + content: ""; } + +.fa-caret-left:before { + content: ""; } + +.fa-caret-right:before { + content: ""; } + +.fa-columns:before { + content: ""; } + +.fa-unsorted:before, +.fa-sort:before { + content: ""; } + +.fa-sort-down:before, +.fa-sort-desc:before { + content: ""; } + +.fa-sort-up:before, +.fa-sort-asc:before { + content: ""; } + +.fa-envelope:before, .icon-envelope:before { + content: ""; } + +.fa-linkedin:before { + content: ""; } + +.fa-rotate-left:before, +.fa-undo:before { + content: ""; } + +.fa-legal:before, +.fa-gavel:before { + content: ""; } + +.fa-dashboard:before, +.fa-tachometer:before, +.icon-dashboard:before { + content: ""; } + +.fa-comment-o:before { + content: ""; } + +.fa-comments-o:before { + content: ""; } + +.fa-flash:before, +.fa-bolt:before { + content: ""; } + +.fa-sitemap:before { + content: ""; } + +.fa-umbrella:before { + content: ""; } + +.fa-paste:before, +.fa-clipboard:before { + content: ""; } + +.fa-lightbulb-o:before { + content: ""; } + +.fa-exchange:before { + content: ""; } + +.fa-cloud-download:before { + content: ""; } + +.fa-cloud-upload:before { + content: ""; } + +.fa-user-md:before { + content: ""; } + +.fa-stethoscope:before { + content: ""; } + +.fa-suitcase:before { + content: ""; } + +.fa-bell-o:before { + content: ""; } + +.fa-coffee:before { + content: ""; } + +.fa-cutlery:before { + content: ""; } + +.fa-file-text-o:before { + content: ""; } + +.fa-building-o:before { + content: ""; } + +.fa-hospital-o:before { + content: ""; } + +.fa-ambulance:before { + content: ""; } + +.fa-medkit:before { + content: ""; } + +.fa-fighter-jet:before { + content: ""; } + +.fa-beer:before { + content: ""; } + +.fa-h-square:before { + content: ""; } + +.fa-plus-square:before { + content: ""; } + +.fa-angle-double-left:before { + content: ""; } + +.fa-angle-double-right:before { + content: ""; } + +.fa-angle-double-up:before { + content: ""; } + +.fa-angle-double-down:before { + content: ""; } + +.fa-angle-left:before { + content: ""; } + +.fa-angle-right:before { + content: ""; } + +.fa-angle-up:before { + content: ""; } + +.fa-angle-down:before { + content: ""; } + +.fa-desktop:before { + content: ""; } + +.fa-laptop:before { + content: ""; } + +.fa-tablet:before { + content: ""; } + +.fa-mobile-phone:before, +.fa-mobile:before { + content: ""; } + +.fa-circle-o:before { + content: ""; } + +.fa-quote-left:before { + content: ""; } + +.fa-quote-right:before { + content: ""; } + +.fa-spinner:before { + content: ""; } + +.fa-circle:before { + content: ""; } + +.fa-mail-reply:before, +.fa-reply:before { + content: ""; } + +.fa-github-alt:before { + content: ""; } + +.fa-folder-o:before { + content: ""; } + +.fa-folder-open-o:before { + content: ""; } + +.fa-smile-o:before, .icon-tummytime:before { + content: ""; } + +.fa-frown-o:before, .icon-sad:before { + content: ""; } + +.fa-meh-o:before { + content: ""; } + +.fa-gamepad:before { + content: ""; } + +.fa-keyboard-o:before { + content: ""; } + +.fa-flag-o:before { + content: ""; } + +.fa-flag-checkered:before { + content: ""; } + +.fa-terminal:before { + content: ""; } + +.fa-code:before { + content: ""; } + +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: ""; } + +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: ""; } + +.fa-location-arrow:before { + content: ""; } + +.fa-crop:before { + content: ""; } + +.fa-code-fork:before, .icon-source:before { + content: ""; } + +.fa-unlink:before, +.fa-chain-broken:before { + content: ""; } + +.fa-question:before { + content: ""; } + +.fa-info:before { + content: ""; } + +.fa-exclamation:before { + content: ""; } + +.fa-superscript:before { + content: ""; } + +.fa-subscript:before { + content: ""; } + +.fa-eraser:before { + content: ""; } + +.fa-puzzle-piece:before { + content: ""; } + +.fa-microphone:before { + content: ""; } + +.fa-microphone-slash:before { + content: ""; } + +.fa-shield:before { + content: ""; } + +.fa-calendar-o:before { + content: ""; } + +.fa-fire-extinguisher:before { + content: ""; } + +.fa-rocket:before { + content: ""; } + +.fa-maxcdn:before { + content: ""; } + +.fa-chevron-circle-left:before { + content: ""; } + +.fa-chevron-circle-right:before { + content: ""; } + +.fa-chevron-circle-up:before { + content: ""; } + +.fa-chevron-circle-down:before { + content: ""; } + +.fa-html5:before { + content: ""; } + +.fa-css3:before { + content: ""; } + +.fa-anchor:before { + content: ""; } + +.fa-unlock-alt:before { + content: ""; } + +.fa-bullseye:before { + content: ""; } + +.fa-ellipsis-h:before { + content: ""; } + +.fa-ellipsis-v:before { + content: ""; } + +.fa-rss-square:before { + content: ""; } + +.fa-play-circle:before { + content: ""; } + +.fa-ticket:before { + content: ""; } + +.fa-minus-square:before { + content: ""; } + +.fa-minus-square-o:before { + content: ""; } + +.fa-level-up:before { + content: ""; } + +.fa-level-down:before { + content: ""; } + +.fa-check-square:before { + content: ""; } + +.fa-pencil-square:before { + content: ""; } + +.fa-external-link-square:before { + content: ""; } + +.fa-share-square:before { + content: ""; } + +.fa-compass:before { + content: ""; } + +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: ""; } + +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: ""; } + +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: ""; } + +.fa-euro:before, +.fa-eur:before { + content: ""; } + +.fa-gbp:before { + content: ""; } + +.fa-dollar:before, +.fa-usd:before { + content: ""; } + +.fa-rupee:before, +.fa-inr:before { + content: ""; } + +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: ""; } + +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: ""; } + +.fa-won:before, +.fa-krw:before { + content: ""; } + +.fa-bitcoin:before, +.fa-btc:before { + content: ""; } + +.fa-file:before { + content: ""; } + +.fa-file-text:before { + content: ""; } + +.fa-sort-alpha-asc:before { + content: ""; } + +.fa-sort-alpha-desc:before { + content: ""; } + +.fa-sort-amount-asc:before { + content: ""; } + +.fa-sort-amount-desc:before { + content: ""; } + +.fa-sort-numeric-asc:before { + content: ""; } + +.fa-sort-numeric-desc:before { + content: ""; } + +.fa-thumbs-up:before { + content: ""; } + +.fa-thumbs-down:before { + content: ""; } + +.fa-youtube-square:before { + content: ""; } + +.fa-youtube:before { + content: ""; } + +.fa-xing:before { + content: ""; } + +.fa-xing-square:before { + content: ""; } + +.fa-youtube-play:before { + content: ""; } + +.fa-dropbox:before { + content: ""; } + +.fa-stack-overflow:before { + content: ""; } + +.fa-instagram:before { + content: ""; } + +.fa-flickr:before { + content: ""; } + +.fa-adn:before { + content: ""; } + +.fa-bitbucket:before { + content: ""; } + +.fa-bitbucket-square:before { + content: ""; } + +.fa-tumblr:before { + content: ""; } + +.fa-tumblr-square:before { + content: ""; } + +.fa-long-arrow-down:before { + content: ""; } + +.fa-long-arrow-up:before { + content: ""; } + +.fa-long-arrow-left:before { + content: ""; } + +.fa-long-arrow-right:before { + content: ""; } + +.fa-apple:before { + content: ""; } + +.fa-windows:before { + content: ""; } + +.fa-android:before { + content: ""; } + +.fa-linux:before { + content: ""; } + +.fa-dribbble:before { + content: ""; } + +.fa-skype:before { + content: ""; } + +.fa-foursquare:before { + content: ""; } + +.fa-trello:before { + content: ""; } + +.fa-female:before { + content: ""; } + +.fa-male:before { + content: ""; } + +.fa-gittip:before, +.fa-gratipay:before { + content: ""; } + +.fa-sun-o:before { + content: ""; } + +.fa-moon-o:before { + content: ""; } + +.fa-archive:before { + content: ""; } + +.fa-bug:before { + content: ""; } + +.fa-vk:before { + content: ""; } + +.fa-weibo:before { + content: ""; } + +.fa-renren:before { + content: ""; } + +.fa-pagelines:before { + content: ""; } + +.fa-stack-exchange:before { + content: ""; } + +.fa-arrow-circle-o-right:before { + content: ""; } + +.fa-arrow-circle-o-left:before { + content: ""; } + +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: ""; } + +.fa-dot-circle-o:before { + content: ""; } + +.fa-wheelchair:before { + content: ""; } + +.fa-vimeo-square:before { + content: ""; } + +.fa-turkish-lira:before, +.fa-try:before { + content: ""; } + +.fa-plus-square-o:before { + content: ""; } + +.fa-space-shuttle:before { + content: ""; } + +.fa-slack:before { + content: ""; } + +.fa-envelope-square:before { + content: ""; } + +.fa-wordpress:before { + content: ""; } + +.fa-openid:before { + content: ""; } + +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: ""; } + +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: ""; } + +.fa-yahoo:before { + content: ""; } + +.fa-google:before { + content: ""; } + +.fa-reddit:before { + content: ""; } + +.fa-reddit-square:before { + content: ""; } + +.fa-stumbleupon-circle:before { + content: ""; } + +.fa-stumbleupon:before { + content: ""; } + +.fa-delicious:before { + content: ""; } + +.fa-digg:before { + content: ""; } + +.fa-pied-piper-pp:before { + content: ""; } + +.fa-pied-piper-alt:before { + content: ""; } + +.fa-drupal:before { + content: ""; } + +.fa-joomla:before { + content: ""; } + +.fa-language:before { + content: ""; } + +.fa-fax:before { + content: ""; } + +.fa-building:before { + content: ""; } + +.fa-child:before, .icon-child:before { + content: ""; } + +.fa-paw:before { + content: ""; } + +.fa-spoon:before, .icon-feeding:before { + content: ""; } + +.fa-cube:before { + content: ""; } + +.fa-cubes:before, .icon-activities:before { + content: ""; } + +.fa-behance:before { + content: ""; } + +.fa-behance-square:before { + content: ""; } + +.fa-steam:before { + content: ""; } + +.fa-steam-square:before { + content: ""; } + +.fa-recycle:before { + content: ""; } + +.fa-automobile:before, +.fa-car:before { + content: ""; } + +.fa-cab:before, +.fa-taxi:before { + content: ""; } + +.fa-tree:before { + content: ""; } + +.fa-spotify:before { + content: ""; } + +.fa-deviantart:before { + content: ""; } + +.fa-soundcloud:before { + content: ""; } + +.fa-database:before { + content: ""; } + +.fa-file-pdf-o:before { + content: ""; } + +.fa-file-word-o:before { + content: ""; } + +.fa-file-excel-o:before { + content: ""; } + +.fa-file-powerpoint-o:before { + content: ""; } + +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: ""; } + +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: ""; } + +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: ""; } + +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: ""; } + +.fa-file-code-o:before { + content: ""; } + +.fa-vine:before { + content: ""; } + +.fa-codepen:before { + content: ""; } + +.fa-jsfiddle:before { + content: ""; } + +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: ""; } + +.fa-circle-o-notch:before { + content: ""; } + +.fa-ra:before, +.fa-resistance:before, +.fa-rebel:before { + content: ""; } + +.fa-ge:before, +.fa-empire:before { + content: ""; } + +.fa-git-square:before { + content: ""; } + +.fa-git:before { + content: ""; } + +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: ""; } + +.fa-tencent-weibo:before { + content: ""; } + +.fa-qq:before { + content: ""; } + +.fa-wechat:before, +.fa-weixin:before { + content: ""; } + +.fa-send:before, +.fa-paper-plane:before { + content: ""; } + +.fa-send-o:before, +.fa-paper-plane-o:before { + content: ""; } + +.fa-history:before { + content: ""; } + +.fa-circle-thin:before { + content: ""; } + +.fa-header:before { + content: ""; } + +.fa-paragraph:before { + content: ""; } + +.fa-sliders:before { + content: ""; } + +.fa-share-alt:before { + content: ""; } + +.fa-share-alt-square:before { + content: ""; } + +.fa-bomb:before { + content: ""; } + +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: ""; } + +.fa-tty:before { + content: ""; } + +.fa-binoculars:before { + content: ""; } + +.fa-plug:before { + content: ""; } + +.fa-slideshare:before { + content: ""; } + +.fa-twitch:before { + content: ""; } + +.fa-yelp:before { + content: ""; } + +.fa-newspaper-o:before { + content: ""; } + +.fa-wifi:before { + content: ""; } + +.fa-calculator:before { + content: ""; } + +.fa-paypal:before { + content: ""; } + +.fa-google-wallet:before { + content: ""; } + +.fa-cc-visa:before { + content: ""; } + +.fa-cc-mastercard:before { + content: ""; } + +.fa-cc-discover:before { + content: ""; } + +.fa-cc-amex:before { + content: ""; } + +.fa-cc-paypal:before { + content: ""; } + +.fa-cc-stripe:before { + content: ""; } + +.fa-bell-slash:before { + content: ""; } + +.fa-bell-slash-o:before { + content: ""; } + +.fa-trash:before, .icon-diaperchange:before { + content: ""; } + +.fa-copyright:before { + content: ""; } + +.fa-at:before { + content: ""; } + +.fa-eyedropper:before { + content: ""; } + +.fa-paint-brush:before { + content: ""; } + +.fa-birthday-cake:before { + content: ""; } + +.fa-area-chart:before { + content: ""; } + +.fa-pie-chart:before { + content: ""; } + +.fa-line-chart:before { + content: ""; } + +.fa-lastfm:before { + content: ""; } + +.fa-lastfm-square:before { + content: ""; } + +.fa-toggle-off:before { + content: ""; } + +.fa-toggle-on:before { + content: ""; } + +.fa-bicycle:before { + content: ""; } + +.fa-bus:before { + content: ""; } + +.fa-ioxhost:before { + content: ""; } + +.fa-angellist:before { + content: ""; } + +.fa-cc:before { + content: ""; } + +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: ""; } + +.fa-meanpath:before { + content: ""; } + +.fa-buysellads:before { + content: ""; } + +.fa-connectdevelop:before { + content: ""; } + +.fa-dashcube:before { + content: ""; } + +.fa-forumbee:before { + content: ""; } + +.fa-leanpub:before { + content: ""; } + +.fa-sellsy:before { + content: ""; } + +.fa-shirtsinbulk:before { + content: ""; } + +.fa-simplybuilt:before { + content: ""; } + +.fa-skyatlas:before { + content: ""; } + +.fa-cart-plus:before { + content: ""; } + +.fa-cart-arrow-down:before { + content: ""; } + +.fa-diamond:before { + content: ""; } + +.fa-ship:before { + content: ""; } + +.fa-user-secret:before { + content: ""; } + +.fa-motorcycle:before { + content: ""; } + +.fa-street-view:before { + content: ""; } + +.fa-heartbeat:before { + content: ""; } + +.fa-venus:before { + content: ""; } + +.fa-mars:before { + content: ""; } + +.fa-mercury:before { + content: ""; } + +.fa-intersex:before, +.fa-transgender:before { + content: ""; } + +.fa-transgender-alt:before { + content: ""; } + +.fa-venus-double:before { + content: ""; } + +.fa-mars-double:before { + content: ""; } + +.fa-venus-mars:before { + content: ""; } + +.fa-mars-stroke:before { + content: ""; } + +.fa-mars-stroke-v:before { + content: ""; } + +.fa-mars-stroke-h:before { + content: ""; } + +.fa-neuter:before { + content: ""; } + +.fa-genderless:before { + content: ""; } + +.fa-facebook-official:before { + content: ""; } + +.fa-pinterest-p:before { + content: ""; } + +.fa-whatsapp:before { + content: ""; } + +.fa-server:before { + content: ""; } + +.fa-user-plus:before { + content: ""; } + +.fa-user-times:before { + content: ""; } + +.fa-hotel:before, +.fa-bed:before, +.icon-sleep:before { + content: ""; } + +.fa-viacoin:before { + content: ""; } + +.fa-train:before { + content: ""; } + +.fa-subway:before { + content: ""; } + +.fa-medium:before { + content: ""; } + +.fa-yc:before, +.fa-y-combinator:before { + content: ""; } + +.fa-optin-monster:before { + content: ""; } + +.fa-opencart:before { + content: ""; } + +.fa-expeditedssl:before { + content: ""; } + +.fa-battery-4:before, +.fa-battery:before, +.fa-battery-full:before { + content: ""; } + +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: ""; } + +.fa-battery-2:before, +.fa-battery-half:before { + content: ""; } + +.fa-battery-1:before, +.fa-battery-quarter:before { + content: ""; } + +.fa-battery-0:before, +.fa-battery-empty:before { + content: ""; } + +.fa-mouse-pointer:before { + content: ""; } + +.fa-i-cursor:before { + content: ""; } + +.fa-object-group:before { + content: ""; } + +.fa-object-ungroup:before { + content: ""; } + +.fa-sticky-note:before, .icon-note:before { + content: ""; } + +.fa-sticky-note-o:before { + content: ""; } + +.fa-cc-jcb:before { + content: ""; } + +.fa-cc-diners-club:before { + content: ""; } + +.fa-clone:before { + content: ""; } + +.fa-balance-scale:before, .icon-weight:before { + content: ""; } + +.fa-hourglass-o:before { + content: ""; } + +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: ""; } + +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: ""; } + +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: ""; } + +.fa-hourglass:before { + content: ""; } + +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: ""; } + +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: ""; } + +.fa-hand-scissors-o:before { + content: ""; } + +.fa-hand-lizard-o:before { + content: ""; } + +.fa-hand-spock-o:before { + content: ""; } + +.fa-hand-pointer-o:before { + content: ""; } + +.fa-hand-peace-o:before { + content: ""; } + +.fa-trademark:before { + content: ""; } + +.fa-registered:before { + content: ""; } + +.fa-creative-commons:before { + content: ""; } + +.fa-gg:before { + content: ""; } + +.fa-gg-circle:before { + content: ""; } + +.fa-tripadvisor:before { + content: ""; } + +.fa-odnoklassniki:before { + content: ""; } + +.fa-odnoklassniki-square:before { + content: ""; } + +.fa-get-pocket:before { + content: ""; } + +.fa-wikipedia-w:before { + content: ""; } + +.fa-safari:before { + content: ""; } + +.fa-chrome:before { + content: ""; } + +.fa-firefox:before { + content: ""; } + +.fa-opera:before { + content: ""; } + +.fa-internet-explorer:before { + content: ""; } + +.fa-tv:before, +.fa-television:before { + content: ""; } + +.fa-contao:before { + content: ""; } + +.fa-500px:before { + content: ""; } + +.fa-amazon:before { + content: ""; } + +.fa-calendar-plus-o:before { + content: ""; } + +.fa-calendar-minus-o:before { + content: ""; } + +.fa-calendar-times-o:before { + content: ""; } + +.fa-calendar-check-o:before { + content: ""; } + +.fa-industry:before { + content: ""; } + +.fa-map-pin:before { + content: ""; } + +.fa-map-signs:before { + content: ""; } + +.fa-map-o:before { + content: ""; } + +.fa-map:before { + content: ""; } + +.fa-commenting:before { + content: ""; } + +.fa-commenting-o:before { + content: ""; } + +.fa-houzz:before { + content: ""; } + +.fa-vimeo:before { + content: ""; } + +.fa-black-tie:before { + content: ""; } + +.fa-fonticons:before { + content: ""; } + +.fa-reddit-alien:before { + content: ""; } + +.fa-edge:before { + content: ""; } + +.fa-credit-card-alt:before { + content: ""; } + +.fa-codiepie:before { + content: ""; } + +.fa-modx:before { + content: ""; } + +.fa-fort-awesome:before { + content: ""; } + +.fa-usb:before { + content: ""; } + +.fa-product-hunt:before { + content: ""; } + +.fa-mixcloud:before { + content: ""; } + +.fa-scribd:before { + content: ""; } + +.fa-pause-circle:before { + content: ""; } + +.fa-pause-circle-o:before { + content: ""; } + +.fa-stop-circle:before { + content: ""; } + +.fa-stop-circle-o:before { + content: ""; } + +.fa-shopping-bag:before { + content: ""; } + +.fa-shopping-basket:before { + content: ""; } + +.fa-hashtag:before { + content: ""; } + +.fa-bluetooth:before { + content: ""; } + +.fa-bluetooth-b:before { + content: ""; } + +.fa-percent:before { + content: ""; } + +.fa-gitlab:before { + content: ""; } + +.fa-wpbeginner:before { + content: ""; } + +.fa-wpforms:before { + content: ""; } + +.fa-envira:before { + content: ""; } + +.fa-universal-access:before { + content: ""; } + +.fa-wheelchair-alt:before { + content: ""; } + +.fa-question-circle-o:before { + content: ""; } + +.fa-blind:before { + content: ""; } + +.fa-audio-description:before { + content: ""; } + +.fa-volume-control-phone:before { + content: ""; } + +.fa-braille:before { + content: ""; } + +.fa-assistive-listening-systems:before { + content: ""; } + +.fa-asl-interpreting:before, +.fa-american-sign-language-interpreting:before { + content: ""; } + +.fa-deafness:before, +.fa-hard-of-hearing:before, +.fa-deaf:before { + content: ""; } + +.fa-glide:before { + content: ""; } + +.fa-glide-g:before { + content: ""; } + +.fa-signing:before, +.fa-sign-language:before { + content: ""; } + +.fa-low-vision:before { + content: ""; } + +.fa-viadeo:before { + content: ""; } + +.fa-viadeo-square:before { + content: ""; } + +.fa-snapchat:before { + content: ""; } + +.fa-snapchat-ghost:before { + content: ""; } + +.fa-snapchat-square:before { + content: ""; } + +.fa-pied-piper:before { + content: ""; } + +.fa-first-order:before { + content: ""; } + +.fa-yoast:before { + content: ""; } + +.fa-themeisle:before { + content: ""; } + +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: ""; } + +.fa-fa:before, +.fa-font-awesome:before { + content: ""; } + +.fa-handshake-o:before { + content: ""; } + +.fa-envelope-open:before { + content: ""; } + +.fa-envelope-open-o:before { + content: ""; } + +.fa-linode:before { + content: ""; } + +.fa-address-book:before { + content: ""; } + +.fa-address-book-o:before { + content: ""; } + +.fa-vcard:before, +.fa-address-card:before { + content: ""; } + +.fa-vcard-o:before, +.fa-address-card-o:before { + content: ""; } + +.fa-user-circle:before { + content: ""; } + +.fa-user-circle-o:before { + content: ""; } + +.fa-user-o:before { + content: ""; } + +.fa-id-badge:before { + content: ""; } + +.fa-drivers-license:before, +.fa-id-card:before { + content: ""; } + +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: ""; } + +.fa-quora:before { + content: ""; } + +.fa-free-code-camp:before { + content: ""; } + +.fa-telegram:before { + content: ""; } + +.fa-thermometer-4:before, +.fa-thermometer:before, +.fa-thermometer-full:before { + content: ""; } + +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before, +.icon-temperature:before { + content: ""; } + +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: ""; } + +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: ""; } + +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: ""; } + +.fa-shower:before { + content: ""; } + +.fa-bathtub:before, +.fa-s15:before, +.fa-bath:before { + content: ""; } + +.fa-podcast:before { + content: ""; } + +.fa-window-maximize:before { + content: ""; } + +.fa-window-minimize:before { + content: ""; } + +.fa-window-restore:before { + content: ""; } + +.fa-times-rectangle:before, +.fa-window-close:before { + content: ""; } + +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: ""; } + +.fa-bandcamp:before { + content: ""; } + +.fa-grav:before { + content: ""; } + +.fa-etsy:before { + content: ""; } + +.fa-imdb:before { + content: ""; } + +.fa-ravelry:before { + content: ""; } + +.fa-eercast:before { + content: ""; } + +.fa-microchip:before { + content: ""; } + +.fa-snowflake-o:before { + content: ""; } + +.fa-superpowers:before { + content: ""; } + +.fa-wpexplorer:before { + content: ""; } + +.fa-meetup:before { + content: ""; } + +.sr-only, .bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after, .bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after, .bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after, .bootstrap-datetimepicker-widget .btn[data-action="clear"]::after, .bootstrap-datetimepicker-widget .btn[data-action="today"]::after, .bootstrap-datetimepicker-widget .picker-switch::after, .bootstrap-datetimepicker-widget table th.prev::after, .bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; } + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; } + +#view-core\:child .child-photo { + max-width: 150px; } + +#view-core\:child-list .picture-column { + text-align: center; + width: 65px; + min-width: 65px; } + +#view-core\:child-list .child-list th, +#view-core\:child-list .child-list td { + vertical-align: middle; } + +@media (min-width: 768px) { + #view-core\:child .child-detail-column { + position: fixed; + width: 100%; } } + +.timeline { + list-style: none; + padding: 10px 0; + position: relative; } + .timeline::before { + top: 0; + bottom: 0; + position: absolute; + content: ' '; + width: 3px; + background-color: #226f97; + left: 50%; + margin-left: -1.5px; } + .timeline li { + margin-bottom: 10px; + position: relative; } + .timeline li::before { + content: ' '; + display: table; } + .timeline li::after { + content: ' '; + display: table; + clear: both; } + .timeline li .card { + width: 47%; + float: left; + position: relative; + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); } + .timeline li .card::before { + position: absolute; + top: 26px; + right: -15px; + display: inline-block; + border-top: 15px solid transparent; + border-left: 15px solid #343a40; + border-right: 0 solid #343a40; + border-bottom: 15px solid transparent; + content: ' '; } + .timeline li .card::after { + position: absolute; + top: 27px; + right: -14px; + display: inline-block; + border-top: 14px solid transparent; + border-left: 14px solid #343a40; + border-right: 0 solid #343a40; + border-bottom: 14px solid transparent; + content: ' '; } + .timeline li.timeline-inverted .card { + float: right; } + .timeline li.timeline-inverted .card::before { + border-left-width: 0; + border-right-width: 15px; + left: -15px; + right: auto; } + .timeline li.timeline-inverted .card::after { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; } + .timeline li .timeline-badge { + color: #fff; + width: 50px; + height: 50px; + line-height: 50px; + font-size: 1.4em; + text-align: center; + position: absolute; + top: 16px; + left: 50%; + margin-left: -25px; + background-color: #343a40; + z-index: 100; + border-radius: 50%; } + .timeline li .timeline-badge .arrow { + color: #fff; + width: 50px; + height: 50px; + line-height: 50px; + font-size: 1.4em; + text-align: center; + position: absolute; + top: 26px; + left: 50%; + margin-left: -25px; + background-color: #343a40; + z-index: 100; + border-radius: 50%; } + +@media (max-width: 991.98px) { + .timeline::before { + left: 40px; } + .timeline li .card { + float: right; + width: calc(100% - 90px); } + .timeline li .card::before { + border-left-width: 0; + border-right-width: 15px; + left: -15px; + right: auto; } + .timeline li .card::after { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; } + .timeline li .timeline-badge { + left: 15px; + margin-left: 0; + top: 16px; } } + +#timer-status { + font-size: 2.5rem; + font-weight: 300; } + #timer-status.timer-stopped { + color: #a72431; } + +@media (min-width: 576px) { + #view-core\:timer-detail #timer-status { + font-size: 6rem; } } + +.card-dashboard .card-header { + margin-bottom: 0; } + +.card-dashboard .card-body .carousel { + margin-left: -1.25rem; + margin-right: -1.25rem; + padding-left: 1.25rem; + padding-right: 1.25rem; } + +.card-diaperchange .progress { + height: 2rem; } + +.card-feeding .card-body .last-feeding-method { + font-size: 2em; } + +.card-statistics .card-body .container { + padding: 0; } + +@media (min-width: 576px) and (max-width: 991.98px) { + #dashboard-child.card-columns { + column-count: 2; } } diff --git a/static/babybuddy/css/app.db0eebca76cc.css.gz b/static/babybuddy/css/app.db0eebca76cc.css.gz new file mode 100644 index 0000000000000000000000000000000000000000..2ed2ee62515344236265654065ec7e1819115784 GIT binary patch literal 36830 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 eFPvXIxSU4fFY!:not(.watermark)": "opacity:0;-webkit-transition:opacity .3s ease 0s;-moz-transition:opacity .3s ease 0s;-ms-transition:opacity .3s ease 0s;-o-transition:opacity .3s ease 0s;transition:opacity .3s ease 0s;", + "X:hover .modebar--hover .modebar-group": "opacity:1;", + "X .modebar-group": "float:left;display:inline-block;box-sizing:border-box;padding-left:8px;position:relative;vertical-align:middle;white-space:nowrap;", + "X .modebar-btn": "position:relative;font-size:16px;padding:3px 4px;height:22px;cursor:pointer;line-height:normal;box-sizing:border-box;", + "X .modebar-btn svg": "position:relative;top:2px;", + "X .modebar.vertical": "display:flex;flex-direction:column;flex-wrap:wrap;align-content:flex-end;max-height:100%;", + "X .modebar.vertical svg": "top:-1px;", + "X .modebar.vertical .modebar-group": "display:block;float:none;padding-left:0px;padding-bottom:8px;", + "X .modebar.vertical .modebar-group .modebar-btn": "display:block;text-align:center;", + "X [data-title]:before,X [data-title]:after": "position:absolute;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);-ms-transform:translate3d(0, 0, 0);-o-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);display:none;opacity:0;z-index:1001;pointer-events:none;top:110%;right:50%;", + "X [data-title]:hover:before,X [data-title]:hover:after": "display:block;opacity:1;", + "X [data-title]:before": "content:\"\";position:absolute;background:transparent;border:6px solid transparent;z-index:1002;margin-top:-12px;border-bottom-color:#69738a;margin-right:-6px;", + "X [data-title]:after": "content:attr(data-title);background:#69738a;color:#fff;padding:8px 10px;font-size:12px;line-height:12px;white-space:nowrap;margin-right:-18px;border-radius:2px;", + "X .vertical [data-title]:before,X .vertical [data-title]:after": "top:0%;right:200%;", + "X .vertical [data-title]:before": "border:6px solid transparent;border-left-color:#69738a;margin-top:8px;margin-right:-30px;", + "X .select-outline": "fill:none;stroke-width:1;shape-rendering:crispEdges;", + "X .select-outline-1": "stroke:#fff;", + "X .select-outline-2": "stroke:#000;stroke-dasharray:2px 2px;", + Y: "font-family:\"Open Sans\",verdana,arial,sans-serif;position:fixed;top:50px;right:20px;z-index:10000;font-size:10pt;max-width:180px;", + "Y p": "margin:0;", + "Y .notifier-note": "min-width:180px;max-width:250px;border:1px solid #fff;z-index:3000;margin:0;background-color:#8c97af;background-color:rgba(140,151,175,.9);color:#fff;padding:10px;overflow-wrap:break-word;word-wrap:break-word;-ms-hyphens:auto;-webkit-hyphens:auto;hyphens:auto;", + "Y .notifier-close": "color:#fff;opacity:.8;float:right;padding:0 5px;background:none;border:none;font-size:20px;font-weight:bold;line-height:20px;", + "Y .notifier-close:hover": "color:#444;text-decoration:none;cursor:pointer;" +}; + +for(var selector in rules) { + var fullSelector = selector.replace(/^,/,' ,') + .replace(/X/g, '.js-plotly-plot .plotly') + .replace(/Y/g, '.plotly-notifier'); + Lib.addStyleRule(fullSelector, rules[selector]); +} + +},{"../src/lib":285}],2:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/transforms/aggregate'); + +},{"../src/transforms/aggregate":541}],3:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/bar'); + +},{"../src/traces/bar":391}],4:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/box'); + +},{"../src/traces/box":406}],5:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/components/calendars'); + +},{"../src/components/calendars":153}],6:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/contour'); + +},{"../src/traces/contour":426}],7:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/core'); + +},{"../src/core":267}],8:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/transforms/filter'); + +},{"../src/transforms/filter":542}],9:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/transforms/groupby'); + +},{"../src/transforms/groupby":543}],10:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/heatmap'); + +},{"../src/traces/heatmap":442}],11:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/histogram'); + +},{"../src/traces/histogram":460}],12:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/histogram2d'); + +},{"../src/traces/histogram2d":466}],13:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/histogram2dcontour'); + +},{"../src/traces/histogram2dcontour":470}],14:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/image'); + +},{"../src/traces/image":478}],15:[function(_dereq_,module,exports){ +'use strict'; + +var Plotly = _dereq_('./core'); + +Plotly.register([ + // traces + _dereq_('./bar'), + _dereq_('./box'), + _dereq_('./heatmap'), + _dereq_('./histogram'), + _dereq_('./histogram2d'), + _dereq_('./histogram2dcontour'), + _dereq_('./contour'), + _dereq_('./scatterternary'), + _dereq_('./violin'), + _dereq_('./image'), + _dereq_('./pie'), + + // transforms + _dereq_('./aggregate'), + _dereq_('./filter'), + _dereq_('./groupby'), + _dereq_('./sort'), + + // components + _dereq_('./calendars'), +]); + +module.exports = Plotly; + +},{"./aggregate":2,"./bar":3,"./box":4,"./calendars":5,"./contour":6,"./core":7,"./filter":8,"./groupby":9,"./heatmap":10,"./histogram":11,"./histogram2d":12,"./histogram2dcontour":13,"./image":14,"./pie":16,"./scatterternary":17,"./sort":18,"./violin":19}],16:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/pie'); + +},{"../src/traces/pie":487}],17:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/scatterternary'); + +},{"../src/traces/scatterternary":528}],18:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/transforms/sort'); + +},{"../src/transforms/sort":545}],19:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = _dereq_('../src/traces/violin'); + +},{"../src/traces/violin":536}],20:[function(_dereq_,module,exports){ +!function() { + var d3 = { + version: "3.6.1" + }; + var d3_arraySlice = [].slice, d3_array = function(list) { + return d3_arraySlice.call(list); + }; + var d3_document = this.document; + function d3_documentElement(node) { + return node && (node.ownerDocument || node.document || node).documentElement; + } + function d3_window(node) { + return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); + } + if (d3_document) { + try { + d3_array(d3_document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = function(list) { + var i = list.length, array = new Array(i); + while (i--) array[i] = list[i]; + return array; + }; + } + } + if (!Date.now) Date.now = function() { + return +new Date(); + }; + if (d3_document) { + try { + d3_document.createElement("DIV").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_element_prototype.setAttribute = function(name, value) { + d3_element_setAttribute.call(this, name, value + ""); + }; + d3_element_prototype.setAttributeNS = function(space, local, value) { + d3_element_setAttributeNS.call(this, space, local, value + ""); + }; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + } + d3.ascending = d3_ascending; + function d3_ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + function d3_number(x) { + return x === null ? NaN : +x; + } + function d3_numeric(x) { + return !isNaN(x); + } + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = +array[i])) s += a; + } else { + while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.mean = function(array, f) { + var s = 0, n = array.length, a, i = -1, j = n; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; + } + if (j) return s / j; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.median = function(array, f) { + var numbers = [], n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); + } + if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); + }; + d3.variance = function(array, f) { + var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) { + if (d3_numeric(a = d3_number(array[i]))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } else { + while (++i < n) { + if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } + if (j > 1) return s / (j - 1); + }; + d3.deviation = function() { + var v = d3.variance.apply(this, arguments); + return v ? Math.sqrt(v) : v; + }; + function d3_bisector(compare) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + } + var d3_bisect = d3_bisector(d3_ascending); + d3.bisectLeft = d3_bisect.left; + d3.bisect = d3.bisectRight = d3_bisect.right; + d3.bisector = function(f) { + return d3_bisector(f.length === 1 ? function(d, x) { + return d3_ascending(f(d), x); + } : f); + }; + d3.shuffle = function(array, i0, i1) { + if ((m = arguments.length) < 3) { + i1 = array.length; + if (m < 2) i0 = 0; + } + var m = i1 - i0, t, i; + while (m) { + i = Math.random() * m-- | 0; + t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t; + } + return array; + }; + d3.permute = function(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.pairs = function(array) { + var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; + return pairs; + }; + d3.transpose = function(matrix) { + if (!(n = matrix.length)) return []; + for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) { + for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) { + row[j] = matrix[j][i]; + } + } + return transpose; + }; + function d3_transposeLength(d) { + return d.length; + } + d3.zip = function() { + return d3.transpose(arguments); + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.merge = function(arrays) { + var n = arrays.length, m, i = -1, j = 0, merged, array; + while (++i < n) j += arrays[i].length; + merged = new Array(j); + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + return merged; + }; + var abs = Math.abs; + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + function d3_class(ctor, properties) { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } + d3.map = function(object, f) { + var map = new d3_Map(); + if (object instanceof d3_Map) { + object.forEach(function(key, value) { + map.set(key, value); + }); + } else if (Array.isArray(object)) { + var i = -1, n = object.length, o; + if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o); + } else { + for (var key in object) map.set(key, object[key]); + } + return map; + }; + function d3_Map() { + this._ = Object.create(null); + } + var d3_map_proto = "__proto__", d3_map_zero = "\x00"; + d3_class(d3_Map, { + has: d3_map_has, + get: function(key) { + return this._[d3_map_escape(key)]; + }, + set: function(key, value) { + return this._[d3_map_escape(key)] = value; + }, + remove: d3_map_remove, + keys: d3_map_keys, + values: function() { + var values = []; + for (var key in this._) values.push(this._[key]); + return values; + }, + entries: function() { + var entries = []; + for (var key in this._) entries.push({ + key: d3_map_unescape(key), + value: this._[key] + }); + return entries; + }, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); + } + }); + function d3_map_escape(key) { + return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; + } + function d3_map_unescape(key) { + return (key += "")[0] === d3_map_zero ? key.slice(1) : key; + } + function d3_map_has(key) { + return d3_map_escape(key) in this._; + } + function d3_map_remove(key) { + return (key = d3_map_escape(key)) in this._ && delete this._[key]; + } + function d3_map_keys() { + var keys = []; + for (var key in this._) keys.push(d3_map_unescape(key)); + return keys; + } + function d3_map_size() { + var size = 0; + for (var key in this._) ++size; + return size; + } + function d3_map_empty() { + for (var key in this._) return false; + return true; + } + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(mapType, array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + if (mapType) { + object = mapType(); + setter = function(keyValue, values) { + object.set(keyValue, map(mapType, values, depth)); + }; + } else { + object = {}; + setter = function(keyValue, values) { + object[keyValue] = map(mapType, values, depth); + }; + } + valuesByKey.forEach(setter); + return object; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var array = [], sortKey = sortKeys[depth++]; + map.forEach(function(key, keyMap) { + array.push({ + key: key, + values: entries(keyMap, depth) + }); + }); + return sortKey ? array.sort(function(a, b) { + return sortKey(a.key, b.key); + }) : array; + } + nest.map = function(array, mapType) { + return map(mapType, array, 0); + }; + nest.entries = function(array) { + return entries(map(d3.map, array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.set = function(array) { + var set = new d3_Set(); + if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); + return set; + }; + function d3_Set() { + this._ = Object.create(null); + } + d3_class(d3_Set, { + has: d3_map_has, + add: function(key) { + this._[d3_map_escape(key += "")] = true; + return key; + }, + remove: d3_map_remove, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key)); + } + }); + d3.behavior = {}; + function d3_identity(d) { + return d; + } + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return value === source ? target : value; + }; + } + function d3_vendorSymbol(object, name) { + if (name in object) return name; + name = name.charAt(0).toUpperCase() + name.slice(1); + for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { + var prefixName = d3_vendorPrefixes[i] + name; + if (prefixName in object) return prefixName; + } + } + var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; + function d3_noop() {} + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i >= 0) { + name = type.slice(i + 1); + type = type.slice(0, i); + } + if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + if (arguments.length === 2) { + if (listener == null) for (type in this) { + if (this.hasOwnProperty(type)) this[type].on(name, null); + } + return this; + } + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.event = null; + function d3_eventPreventDefault() { + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + var d3_subclass = {}.__proto__ ? function(object, prototype) { + object.__proto__ = prototype; + } : function(object, prototype) { + for (var property in prototype) object[property] = prototype[property]; + }; + function d3_selection(groups) { + d3_subclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectMatches = function(n, s) { + var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; + d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + return d3_selectMatches(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = Sizzle; + d3_selectMatches = Sizzle.matchesSelector; + } + d3.selection = function() { + return d3.select(d3_document.documentElement); + }; + var d3_selectionPrototype = d3.selection.prototype = []; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i, j)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return typeof selector === "function" ? selector : function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return typeof selector === "function" ? selector : function() { + return d3_selectAll(selector, this); + }; + } + var d3_nsXhtml = "http://www.w3.org/1999/xhtml"; + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: d3_nsXhtml, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.getAttribute("class"); + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classes(name) { + return (name + "").trim().split(/^|\s+/); + } + function d3_selection_classed(name, value) { + name = d3_selection_classes(name).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.getAttribute("class") || ""; + if (value) { + re.lastIndex = 0; + if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); + } else { + node.setAttribute("class", d3_collapse(c.replace(re, " "))); + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) { + var node = this.node(); + return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); + } + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3_selection_creator(name); + return this.select(function() { + return this.appendChild(name.apply(this, arguments)); + }); + }; + function d3_selection_creator(name) { + function create() { + var document = this.ownerDocument, namespace = this.namespaceURI; + return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name); + } + function createNS() { + return this.ownerDocument.createElementNS(name.space, name.local); + } + return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create; + } + d3_selectionPrototype.insert = function(name, before) { + name = d3_selection_creator(name); + before = d3_selection_selector(before); + return this.select(function() { + return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); + }); + }; + d3_selectionPrototype.remove = function() { + return this.each(d3_selectionRemove); + }; + function d3_selectionRemove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + } + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; + for (i = -1; ++i < n; ) { + if (node = group[i]) { + if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues[i] = keyValue; + } + } + for (i = -1; ++i < m; ) { + if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } else if (node !== true) { + updateNodes[i] = node; + node.__data__ = nodeData; + } + nodeByKeyValue.set(keyValue, true); + } + for (i = -1; ++i < n; ) { + if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3_ascending; + return function(a, b) { + return a && b ? comparator(a.__data__, b.__data__) : !a - !b; + }; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.size = function() { + var n = 0; + d3_selection_each(this, function() { + ++n; + }); + return n; + }; + function d3_selection_enter(selection) { + d3_subclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.call = d3_selectionPrototype.call; + d3_selection_enterPrototype.size = d3_selectionPrototype.size; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + d3_selection_enterPrototype.insert = function(name, before) { + if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); + return d3_selectionPrototype.insert.call(this, name, before); + }; + function d3_selection_enterInsertBefore(enter) { + var i0, j0; + return function(d, i, j) { + var group = enter[j].update, n = group.length, node; + if (j != j0) j0 = j, i0 = 0; + if (i >= i0) i0 = i + 1; + while (!(node = group[i0]) && ++i0 < n) ; + return node; + }; + } + d3.select = function(node) { + var group; + if (typeof node === "string") { + group = [ d3_select(node, d3_document) ]; + group.parentNode = d3_document.documentElement; + } else { + group = [ node ]; + group.parentNode = d3_documentElement(node); + } + return d3_selection([ group ]); + }; + d3.selectAll = function(nodes) { + var group; + if (typeof nodes === "string") { + group = d3_array(d3_selectAll(nodes, d3_document)); + group.parentNode = d3_document.documentElement; + } else { + group = d3_array(nodes); + group.parentNode = null; + } + return d3_selection([ group ]); + }; + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; + if (i > 0) type = type.slice(0, i); + var filter = d3_selection_onFilters.get(type); + if (filter) type = filter, wrap = d3_selection_onFilter; + function onRemove() { + var l = this[name]; + if (l) { + this.removeEventListener(type, l, l.$); + delete this[name]; + } + } + function onAdd() { + var l = wrap(listener, d3_array(arguments)); + onRemove.call(this); + this.addEventListener(type, this[name] = l, l.$ = capture); + l._ = listener; + } + function removeAll() { + var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; + for (var name in this) { + if (match = name.match(re)) { + var l = this[name]; + this.removeEventListener(match[1], l, l.$); + delete this[name]; + } + } + } + return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; + } + var d3_selection_onFilters = d3.map({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }); + if (d3_document) { + d3_selection_onFilters.forEach(function(k) { + if ("on" + k in d3_document) d3_selection_onFilters.remove(k); + }); + } + function d3_selection_onListener(listener, argumentz) { + return function(e) { + var o = d3.event; + d3.event = e; + argumentz[0] = this.__data__; + try { + listener.apply(this, argumentz); + } finally { + d3.event = o; + } + }; + } + function d3_selection_onFilter(listener, argumentz) { + var l = d3_selection_onListener(listener, argumentz); + return function(e) { + var target = this, related = e.relatedTarget; + if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { + l.call(target, e); + } + }; + } + var d3_event_dragSelect, d3_event_dragId = 0; + function d3_event_dragSuppress(node) { + var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); + if (d3_event_dragSelect == null) { + d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect"); + } + if (d3_event_dragSelect) { + var style = d3_documentElement(node).style, select = style[d3_event_dragSelect]; + style[d3_event_dragSelect] = "none"; + } + return function(suppressClick) { + w.on(name, null); + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; + if (suppressClick) { + var off = function() { + w.on(click, null); + }; + w.on(click, function() { + d3_eventPreventDefault(); + off(); + }, true); + setTimeout(off, 0); + } + }; + } + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; + function d3_mousePoint(container, e) { + if (e.changedTouches) e = e.changedTouches[0]; + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0) { + var window = d3_window(container); + if (window.scrollX || window.scrollY) { + svg = d3.select("body").append("svg").style({ + position: "absolute", + top: 0, + left: 0, + margin: 0, + padding: 0, + border: "none" + }, "important"); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + } + if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, + point.y = e.clientY; + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } + }; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend"); + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); + } + function dragstart(id, position, subject, move, end) { + return function() { + var that = this, target = d3.event.target.correspondingElement || d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); + if (origin) { + dragOffset = origin.apply(that, arguments); + dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; + } else { + dragOffset = [ 0, 0 ]; + } + dispatch({ + type: "dragstart" + }); + function moved() { + var position1 = position(parent, dragId), dx, dy; + if (!position1) return; + dx = position1[0] - position0[0]; + dy = position1[1] - position0[1]; + dragged |= dx | dy; + position0 = position1; + dispatch({ + type: "drag", + x: position1[0] + dragOffset[0], + y: position1[1] + dragOffset[1], + dx: dx, + dy: dy + }); + } + function ended() { + if (!position(parent, dragId)) return; + dragSubject.on(move + dragName, null).on(end + dragName, null); + dragRestore(dragged); + dispatch({ + type: "dragend" + }); + } + }; + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + function d3_behavior_dragTouchId() { + return d3.event.changedTouches[0].identifier; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π; + function d3_sgn(x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } + function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + function d3_acos(x) { + return x > 1 ? 0 : x < -1 ? π : Math.acos(x); + } + function d3_asin(x) { + return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); + } + function d3_sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; + } + function d3_cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; + } + function d3_tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); + } + function d3_haversin(x) { + return (x = Math.sin(x / 2)) * x; + } + var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; + d3.interpolateZoom = function(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i, S; + if (d2 < ε2) { + S = Math.log(w1 / w0) / ρ; + i = function(t) { + return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ]; + }; + } else { + var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / ρ; + i = function(t) { + var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); + return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; + }; + } + i.duration = S * 1e3; + return i; + }; + d3.behavior.zoom = function() { + var view = { + x: 0, + y: 0, + k: 1 + }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; + if (!d3_behavior_zoomWheel) { + d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + } + function zoom(g) { + g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); + } + zoom.event = function(g) { + g.each(function() { + var dispatch = event.of(this, arguments), view1 = view; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.zoom", function() { + view = this.__chart__ || { + x: 0, + y: 0, + k: 1 + }; + zoomstarted(dispatch); + }).tween("zoom:zoom", function() { + var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = { + x: cx - l[0] * k, + y: cy - l[1] * k, + k: k + }; + zoomed(dispatch); + }; + }).each("interrupt.zoom", function() { + zoomended(dispatch); + }).each("end.zoom", function() { + zoomended(dispatch); + }); + } else { + this.__chart__ = view; + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); + } + }); + }; + zoom.translate = function(_) { + if (!arguments.length) return [ view.x, view.y ]; + view = { + x: +_[0], + y: +_[1], + k: view.k + }; + rescale(); + return zoom; + }; + zoom.scale = function(_) { + if (!arguments.length) return view.k; + view = { + x: view.x, + y: view.y, + k: null + }; + scaleTo(+_); + rescale(); + return zoom; + }; + zoom.scaleExtent = function(_) { + if (!arguments.length) return scaleExtent; + scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; + return zoom; + }; + zoom.center = function(_) { + if (!arguments.length) return center; + center = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.size = function(_) { + if (!arguments.length) return size; + size = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.duration = function(_) { + if (!arguments.length) return duration; + duration = +_; + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + function location(p) { + return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; + } + function point(l) { + return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; + } + function scaleTo(s) { + view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + view.x += p[0] - l[0]; + view.y += p[1] - l[1]; + } + function zoomTo(that, p, l, k) { + that.__chart__ = { + x: view.x, + y: view.y, + k: view.k + }; + scaleTo(Math.pow(2, k)); + translateTo(center0 = p, l); + that = d3.select(that); + if (duration > 0) that = that.transition().duration(duration); + that.call(zoom.event); + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - view.x) / view.k; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - view.y) / view.k; + }).map(y0.invert)); + } + function zoomstarted(dispatch) { + if (!zooming++) dispatch({ + type: "zoomstart" + }); + } + function zoomed(dispatch) { + rescale(); + dispatch({ + type: "zoom", + scale: view.k, + translate: [ view.x, view.y ] + }); + } + function zoomended(dispatch) { + if (!--zooming) dispatch({ + type: "zoomend" + }), center0 = null; + } + function mousedowned() { + var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); + function moved() { + dragged = 1; + translateTo(d3.mouse(that), location0); + zoomed(dispatch); + } + function ended() { + subject.on(mousemove, null).on(mouseup, null); + dragRestore(dragged); + zoomended(dispatch); + } + } + function touchstarted() { + var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that); + started(); + zoomstarted(dispatch); + subject.on(mousedown, null).on(touchstart, started); + function relocate() { + var touches = d3.touches(that); + scale0 = view.k; + touches.forEach(function(t) { + if (t.identifier in locations0) locations0[t.identifier] = location(t); + }); + return touches; + } + function started() { + var target = d3.event.target; + d3.select(target).on(touchmove, moved).on(touchend, ended); + targets.push(target); + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + locations0[changed[i].identifier] = null; + } + var touches = relocate(), now = Date.now(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0]; + zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); + d3_eventPreventDefault(); + } + touchtime = now; + } else if (touches.length > 1) { + var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; + distance0 = dx * dx + dy * dy; + } + } + function moved() { + var touches = d3.touches(that), p0, l0, p1, l1; + d3_selection_interrupt.call(that); + for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { + p1 = touches[i]; + if (l1 = locations0[p1.identifier]) { + if (l0) break; + p0 = p1, l0 = l1; + } + } + if (l1) { + var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(scale1 * scale0); + } + touchtime = null; + translateTo(p0, l0); + zoomed(dispatch); + } + function ended() { + if (d3.event.touches.length) { + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + delete locations0[changed[i].identifier]; + } + for (var identifier in locations0) { + return void relocate(); + } + } + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); + dragRestore(); + zoomended(dispatch); + } + } + function mousewheeled() { + var dispatch = event.of(this, arguments); + if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), + translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { + mousewheelTimer = null; + zoomended(dispatch); + }, 50); + d3_eventPreventDefault(); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); + translateTo(center0, translate0); + zoomed(dispatch); + } + function dblclicked() { + var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; + zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel; + d3.color = d3_color; + function d3_color() {} + d3_color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.hsl = d3_hsl; + function d3_hsl(h, s, l) { + return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l); + } + var d3_hslPrototype = d3_hsl.prototype = new d3_color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; + s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = d3_hcl; + function d3_hcl(h, c, l) { + return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l); + } + var d3_hclPrototype = d3_hcl.prototype = new d3_color(); + d3_hclPrototype.brighter = function(k) { + return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + if (isNaN(h)) h = 0; + if (isNaN(c)) c = 0; + return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = d3_lab; + function d3_lab(l, a, b) { + return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b); + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_lab.prototype = new d3_color(); + d3_labPrototype.brighter = function(k) { + return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + d3.rgb = d3_rgb; + function d3_rgb(r, g, b) { + return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b); + } + function d3_rgbNumber(value) { + return new d3_rgb(value >> 16, value >> 8 & 255, value & 255); + } + function d3_rgbString(value) { + return d3_rgbNumber(value) + ""; + } + var d3_rgbPrototype = d3_rgb.prototype = new d3_color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return new d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_rgb(k * this.r, k * this.g, k * this.b); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, color; + m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase()); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (color = d3_rgb_names.get(format)) { + return rgb(color.r, color.g, color.b); + } + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { + if (format.length === 4) { + r = (color & 3840) >> 4; + r = r >> 4 | r; + g = color & 240; + g = g >> 4 | g; + b = color & 15; + b = b << 4 | b; + } else if (format.length === 7) { + r = (color & 16711680) >> 16; + g = (color & 65280) >> 8; + b = color & 255; + } + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + h = NaN; + s = l > 0 && l < 1 ? 0 : h; + } + return new d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgbNumber(value)); + }); + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + d3.xhr = d3_xhrType(d3_identity); + function d3_xhrType(response) { + return function(url, mimeType, callback) { + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return d3_xhr(url, mimeType, response, callback); + }; + } + function d3_xhr(url, mimeType, response, callback) { + var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; + if (self.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var status = request.status, result; + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + dispatch.beforesend.call(xhr, request); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + } + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" ? request.response : request.responseText; + } + d3.dsv = function(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); + xhr.row = function(_) { + return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; + }; + return xhr; + } + function response(request) { + return dsv.parse(request.responseText); + } + function typedResponse(f) { + return function(request) { + return dsv.parse(request.responseText, f); + }; + } + dsv.parse = function(text, f) { + var o; + return dsv.parseRows(text, function(row, i) { + if (o) return o(row, i - 1); + var a = function(d) { + var obj = {}; + var len = row.length; + for (var k = 0; k < len; ++k) { + obj[row[k]] = d[k]; + } + return obj; + }; + o = f ? function(row, i) { + return f(a(row), i); + } : a; + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.slice(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.slice(j, I - k); + } + return text.slice(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && (a = f(a, n++)) == null) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + if (Array.isArray(rows[0])) return dsv.formatRows(rows); + var fieldSet = new d3_Set(), fields = []; + rows.forEach(function(row) { + for (var field in row) { + if (!fieldSet.has(field)) { + fields.push(fieldSet.add(field)); + } + } + }); + return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { + return fields.map(function(field) { + return formatValue(row[field]); + }).join(delimiter); + })).join("\n"); + }; + dsv.formatRows = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + }; + d3.csv = d3.dsv(",", "text/csv"); + d3.tsv = d3.dsv(" ", "text/tab-separated-values"); + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { + setTimeout(callback, 17); + }; + d3.timer = function() { + d3_timer.apply(this, arguments); + }; + function d3_timer(callback, delay, then) { + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); + var time = then + delay, timer = { + c: callback, + t: time, + n: null + }; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + return timer; + } + function d3_timer_step() { + var now = d3_timer_mark(), delay = d3_timer_sweep() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + d3_timer_mark(); + d3_timer_sweep(); + }; + function d3_timer_mark() { + var now = Date.now(), timer = d3_timer_queueHead; + while (timer) { + if (now >= timer.t && timer.c(now - timer.t)) timer.c = null; + timer = timer.n; + } + return now; + } + function d3_timer_sweep() { + var t0, t1 = d3_timer_queueHead, time = Infinity; + while (t1) { + if (t1.c) { + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; + } else { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; + } + } + d3_timer_queueTail = t0; + return time; + } + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value = +value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_locale_numberFormat(locale) { + var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { + var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0; + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = locale_grouping[j = (j + 1) % locale_grouping.length]; + } + return t.reverse().join(locale_thousands); + } : d3_identity; + return function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (symbol === "#") prefix = "0" + type.toLowerCase(); + + case "c": + exponent = false; + + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; + if (type == "r" && !precision) type = "g"; + if (precision != null) { + if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); + } + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + var fullSuffix = suffix; + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; + if (scale < 0) { + var unit = d3.formatPrefix(value, precision); + value = unit.scale(value); + fullSuffix = unit.symbol + suffix; + } else { + value *= scale; + } + value = type(value, precision); + var i = value.lastIndexOf("."), before, after; + if (i < 0) { + var j = exponent ? value.lastIndexOf("e") : -1; + if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); + } else { + before = value.substring(0, i); + after = locale_decimal + value.substring(i + 1); + } + if (!zfill && comma) before = formatGroup(before, Infinity); + var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); + negative += prefix; + value = before + after; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; + }; + }; + } + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_time = d3.time = {}, d3_date = Date; + function d3_date_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_date_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_date(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_date(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_date = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_date = Date; + } + }; + } + d3_time.year = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3_time.years = d3_time.year.range; + d3_time.years.utc = d3_time.year.utc.range; + d3_time.day = d3_time_interval(function(date) { + var day = new d3_date(2e3, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3_time.days = d3_time.day.range; + d3_time.days.utc = d3_time.day.utc.range; + d3_time.dayOfYear = function(date) { + var year = d3_time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { + i = 7 - i; + var interval = d3_time[day] = d3_time_interval(function(date) { + (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3_time[day + "s"] = interval.range; + d3_time[day + "s"].utc = interval.utc.range; + d3_time[day + "OfYear"] = function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3_time.week = d3_time.sunday; + d3_time.weeks = d3_time.sunday.range; + d3_time.weeks.utc = d3_time.sunday.utc.range; + d3_time.weekOfYear = d3_time.sundayOfYear; + function d3_locale_timeFormat(locale) { + var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; + function d3_time_format(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.slice(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.slice(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "W" in d ? 1 : 0; + date.setFullYear(d.y, 0, 1); + date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); + } else date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); + return localZ ? date._ : date; + }; + format.toString = function() { + return template; + }; + return format; + } + function d3_time_parse(date, template, string, j) { + var c, p, t, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + t = template.charAt(i++); + p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + d3_time_format.utc = function(template) { + var local = d3_time_format(template); + function format(date) { + try { + d3_date = d3_date_utc; + var utc = new d3_date(); + utc._ = date; + return local(utc); + } finally { + d3_date = Date; + } + } + format.parse = function(string) { + try { + d3_date = d3_date_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_date = Date; + } + }; + format.toString = local.toString; + return format; + }; + d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; + var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); + locale_periods.forEach(function(p, i) { + d3_time_periodLookup.set(p.toLowerCase(), i); + }); + var d3_time_formats = { + a: function(d) { + return locale_shortDays[d.getDay()]; + }, + A: function(d) { + return locale_days[d.getDay()]; + }, + b: function(d) { + return locale_shortMonths[d.getMonth()]; + }, + B: function(d) { + return locale_months[d.getMonth()]; + }, + c: d3_time_format(locale_dateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return locale_periods[+(d.getHours() >= 12)]; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); + }, + x: d3_time_format(locale_date), + X: d3_time_format(locale_time), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + j: d3_time_parseDayOfYear, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + U: d3_time_parseWeekNumberSunday, + w: d3_time_parseWeekdayNumber, + W: d3_time_parseWeekNumberMonday, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear, + Z: d3_time_parseZone, + "%": d3_time_parseLiteralPercent + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + return d3_time_format; + } + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; + function d3_time_formatPad(value, fill, width) { + var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 1)); + return n ? (date.w = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberSunday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.U = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberMonday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.W = +n[0], i + n[0].length) : -1; + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 4)); + return n ? (date.y = +n[0], i + n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; + } + function d3_time_parseZone(date, string, i) { + return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, + i + 5) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.m = n[0] - 1, i + n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.d = +n[0], i + n[0].length) : -1; + } + function d3_time_parseDayOfYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.j = +n[0], i + n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.H = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.M = +n[0], i + n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.S = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.L = +n[0], i + n[0].length) : -1; + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + function d3_time_parseLiteralPercent(date, string, i) { + d3_time_percentRe.lastIndex = 0; + var n = d3_time_percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; + } + function d3_time_formatMulti(formats) { + var n = formats.length, i = -1; + while (++i < n) formats[i][0] = this(formats[i][0]); + return function(date) { + var i = 0, f = formats[i]; + while (!f[1](date)) f = formats[++i]; + return f[0](date); + }; + } + d3.locale = function(locale) { + return { + numberFormat: d3_locale_numberFormat(locale), + timeFormat: d3_locale_timeFormat(locale) + }; + }; + var d3_locale_enUS = d3.locale({ + decimal: ".", + thousands: ",", + grouping: [ 3 ], + currency: [ "$", "" ], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + periods: [ "AM", "PM" ], + days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], + shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] + }); + d3.format = d3_locale_enUS.numberFormat; + d3.geo = {}; + function d3_adder() {} + d3_adder.prototype = { + s: 0, + t: 0, + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } + }; + var d3_adderTemp = new d3_adder(); + function d3_adderSum(a, b, o) { + var x = o.s = a + b, bv = x - a, av = x - bv; + o.t = a - av + (b - bv); + } + d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; + } + function d3_geo_sphericalEqual(a, b) { + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; + } + d3.geo.bounds = function() { + var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; + } + }; + function point(λ, φ) { + ranges.push(range = [ λ0 = λ, λ1 = λ ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + function linePoint(λ, φ) { + var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); + } + p0 = p, λ_ = λ; + } + function lineStart() { + bound.point = linePoint; + } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } + function ringStart() { + d3_geo_area.lineStart(); + } + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } + function angle(λ0, λ1) { + return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; + } + function compareRanges(a, b) { + return a[0] - b[0]; + } + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + d3.geo.stream(feature, bound); + var n = ranges.length; + if (n) { + ranges.sort(compareRanges); + for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; + } + } + ranges = range = null; + return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; + }; + }(); + d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); + var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + if (m < ε2) return [ NaN, NaN ]; + } + return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; + }; + var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; + var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); + } + function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + function d3_geo_centroidRingStart() { + var λ00, φ00, x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_true() { + return true; + } + function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n]; + if (d3_geo_sphericalEqual(p0, p1)) { + listener.lineStart(); + for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); + listener.lineEnd(); + return; + } + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; + subject.push(a); + clip.push(b); + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; + subject.push(a); + clip.push(b); + }); + clip.sort(compare); + d3_geo_clipPolygonLinkCircular(subject); + d3_geo_clipPolygonLinkCircular(clip); + if (!subject.length) return; + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; + } + var start = subject[0], points, point; + while (1) { + var current = start, isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + listener.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, listener); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, listener); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + listener.lineEnd(); + } + } + function d3_geo_clipPolygonLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; + } + function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; + this.e = entry; + this.v = false; + this.n = this.p = null; + } + function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); + if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); + } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; + function pointRing(λ, φ) { + ring.push([ λ, φ ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; + if (clean & 1) { + segment = ringSegments[0]; + var n = segment.length - 1, i = -1, point; + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + }, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + } + }; + } + function d3_geo_clipSort(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); + if (abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfπ; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; + d3_geo_areaRingSum.reset(); + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], m = ring.length; + if (!m) continue; + var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1; + } + function d3_geo_clipCircle(radius) { + var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, c0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + } else { + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { + listener.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b, two) { + var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return !two && a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); + if (t2 < 0) return; + var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + q = d3_geo_spherical(q); + if (!two) return q; + var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; + if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; + var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; + if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; + if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { + var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); + d3_geo_cartesianAdd(q1, A); + return [ q, d3_geo_spherical(q1) ]; + } + } + function code(λ, φ) { + var r = smallRadius ? radius : π - radius, code = 0; + if (λ < -r) code |= 1; else if (λ > r) code |= 2; + if (φ < -r) code |= 4; else if (φ > r) code |= 8; + return code; + } + } + function d3_geom_clipLine(x0, y0, x1, y1) { + return function(line) { + var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + if (t0 > 0) line.a = { + x: ax + t0 * dx, + y: ay + t0 * dy + }; + if (t1 < 1) line.b = { + x: ax + t1 * dx, + y: ay + t1 * dy + }; + return line; + }; + } + var d3_geo_clipExtentMAX = 1e9; + d3.geo.clipExtent = function() { + var x0, y0, x1, y1, stream, clip, clipExtent = { + stream: function(output) { + if (stream) stream.valid = false; + stream = clip(output); + stream.valid = true; + return stream; + }, + extent: function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); + if (stream) stream.valid = false, stream = null; + return clipExtent; + } + }; + return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); + }; + function d3_geo_clipExtent(x0, y0, x1, y1) { + return function(listener) { + var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + listener = bufferListener; + segments = []; + polygon = []; + clean = true; + }, + polygonEnd: function() { + listener = listener_; + segments = d3.merge(segments); + var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; + if (inside || visible) { + listener.polygonStart(); + if (inside) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (visible) { + d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); + } + listener.polygonEnd(); + } + segments = polygon = ring = null; + } + }; + function insidePolygon(p) { + var wn = 0, n = polygon.length, y = p[1]; + for (var i = 0; i < n; ++i) { + for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { + b = v[j]; + if (a[1] <= y) { + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; + } else { + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; + } + a = b; + } + } + return wn !== 0; + } + function interpolate(from, to, direction, listener) { + var a = 0, a1 = 0; + if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { + do { + listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + } while ((a = (a + direction + 4) % 4) !== a1); + } else { + listener.point(to[0], to[1]); + } + } + function pointVisible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + function point(x, y) { + if (pointVisible(x, y)) listener.point(x, y); + } + var x__, y__, v__, x_, y_, v_, first, clean; + function lineStart() { + clip.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferListener.rejoin(); + segments.push(bufferListener.buffer()); + } + clip.point = point; + if (v_) listener.lineEnd(); + } + function linePoint(x, y) { + x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); + y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); + var v = pointVisible(x, y); + if (polygon) ring.push([ x, y ]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + listener.lineStart(); + listener.point(x, y); + } + } else { + if (v && v_) listener.point(x, y); else { + var l = { + a: { + x: x_, + y: y_ + }, + b: { + x: x, + y: y + } + }; + if (clipLine(l)) { + if (!v_) { + listener.lineStart(); + listener.point(l.a.x, l.a.y); + } + listener.point(l.b.x, l.b.y); + if (!v) listener.lineEnd(); + clean = false; + } else if (v) { + listener.lineStart(); + listener.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + return clip; + }; + function corner(p, direction) { + return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; + } + function compare(a, b) { + return comparePoints(a.x, b.x); + } + function comparePoints(a, b) { + var ca = corner(a, 1), cb = corner(b, 1); + return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; + } + } + function d3_geo_conic(projectAt) { + var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; + return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); + }; + return p; + } + function d3_geo_conicEqualArea(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; + function forward(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; + }; + return forward; + } + (d3.geo.conicEqualArea = function() { + return d3_geo_conic(d3_geo_conicEqualArea); + }).raw = d3_geo_conicEqualArea; + d3.geo.albers = function() { + return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); + }; + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); + var point, pointStream = { + point: function(x, y) { + point = [ x, y ]; + } + }, lower48Point, alaskaPoint, hawaiiPoint; + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + point = null; + (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); + return point; + } + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; + return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); + }; + albersUsa.stream = function(stream) { + var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); + return { + point: function(x, y) { + lower48Stream.point(x, y); + alaskaStream.point(x, y); + hawaiiStream.point(x, y); + }, + sphere: function() { + lower48Stream.sphere(); + alaskaStream.sphere(); + hawaiiStream.sphere(); + }, + lineStart: function() { + lower48Stream.lineStart(); + alaskaStream.lineStart(); + hawaiiStream.lineStart(); + }, + lineEnd: function() { + lower48Stream.lineEnd(); + alaskaStream.lineEnd(); + hawaiiStream.lineEnd(); + }, + polygonStart: function() { + lower48Stream.polygonStart(); + alaskaStream.polygonStart(); + hawaiiStream.polygonStart(); + }, + polygonEnd: function() { + lower48Stream.polygonEnd(); + alaskaStream.polygonEnd(); + hawaiiStream.polygonEnd(); + } + }; + }; + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + return albersUsa; + }; + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * .35); + hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; + lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; + alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + return albersUsa; + }; + return albersUsa.scale(1070); + }; + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; + var d3_geo_pathBounds = { + point: d3_geo_pathBoundsPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_pathBoundsPoint(x, y) { + if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; + if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; + if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; + if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathBufferCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathBufferCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + d3_geo_centroidX0 += x; + d3_geo_centroidY0 += y; + ++d3_geo_centroidZ0; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + z = y0 * x - x0 * y; + d3_geo_centroidX2 += z * (x0 + x); + d3_geo_centroidY2 += z * (y0 + y); + d3_geo_centroidZ2 += z * 3; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x + pointRadius, y); + context.arc(x, y, pointRadius, 0, τ); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + function d3_geo_resample(project) { + var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; + function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } + function resampleRecursive(stream) { + var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = ringStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function ringStart() { + lineStart(); + resample.point = ringPoint; + resample.lineEnd = ringEnd; + } + function ringPoint(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + } + function ringEnd() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); + d3.geo.stream(object, cacheStream); + } + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; + }; + path.bounds = function(object) { + d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); + d3.geo.stream(object, projectStream(d3_geo_pathBounds)); + return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return reset(); + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return reset(); + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + function reset() { + cacheStream = null; + return path; + } + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(x, y) { + return project([ x * d3_degrees, y * d3_degrees ]); + }); + return function(stream) { + return d3_geo_projectionRadians(resample(stream)); + }; + } + d3.geo.transform = function(methods) { + return { + stream: function(stream) { + var transform = new d3_geo_transform(stream); + for (var k in methods) transform[k] = methods[k]; + return transform; + } + }; + }; + function d3_geo_transform(stream) { + this.stream = stream; + } + d3_geo_transform.prototype = { + point: function(x, y) { + this.stream.point(x, y); + }, + sphere: function() { + this.stream.sphere(); + }, + lineStart: function() { + this.stream.lineStart(); + }, + lineEnd: function() { + this.stream.lineEnd(); + }, + polygonStart: function() { + this.stream.polygonStart(); + }, + polygonEnd: function() { + this.stream.polygonEnd(); + } + }; + function d3_geo_transformPoint(stream, point) { + return { + point: point, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(output) { + if (stream) stream.valid = false; + stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); + stream.valid = true; + return stream; + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); + return invalidate(); + }; + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; + return invalidate(); + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return invalidate(); + } + function invalidate() { + if (stream) stream.valid = false, stream = null; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadians(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + d3.geo.rotation = function(rotate) { + rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); + function forward(coordinates) { + coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + } + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + }; + return forward; + }; + function d3_geo_identityRotation(λ, φ) { + return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + } + d3_geo_identityRotation.invert = d3_geo_equirectangular; + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; + }; + return rotation; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radius, precision) { + var cr = Math.cos(radius), sr = Math.sin(radius); + return function(from, to, direction, listener) { + var step = direction * precision; + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * τ; + } else { + from = radius + direction * τ; + to = radius - .5 * step; + } + for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = d3_acos(-a[1]); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + d3.geo.distance = function(a, b) { + var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; + return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); + }; + d3.geo.graticule = function() { + var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; + function graticule() { + return { + type: "MultiLineString", + coordinates: lines() + }; + } + function lines() { + return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { + return abs(x % DX) > ε; + }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { + return abs(y % DY) > ε; + }).map(y)); + } + graticule.lines = function() { + return lines().map(function(coordinates) { + return { + type: "LineString", + coordinates: coordinates + }; + }); + }; + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] + }; + }; + graticule.extent = function(_) { + if (!arguments.length) return graticule.minorExtent(); + return graticule.majorExtent(_).minorExtent(_); + }; + graticule.majorExtent = function(_) { + if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; + X0 = +_[0][0], X1 = +_[1][0]; + Y0 = +_[0][1], Y1 = +_[1][1]; + if (X0 > X1) _ = X0, X0 = X1, X1 = _; + if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; + return graticule.precision(precision); + }; + graticule.minorExtent = function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + graticule.step = function(_) { + if (!arguments.length) return graticule.minorStep(); + return graticule.majorStep(_).minorStep(_); + }; + graticule.majorStep = function(_) { + if (!arguments.length) return [ DX, DY ]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + graticule.minorStep = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, 90); + y = d3_geo_graticuleY(x0, x1, precision); + X = d3_geo_graticuleX(Y0, Y1, 90); + Y = d3_geo_graticuleY(X0, X1, precision); + return graticule; + }; + return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + function d3_source(d) { + return d.source; + } + function d3_target(d) { + return d.target; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_; + function greatArc() { + return { + type: "LineString", + coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] + }; + } + greatArc.distance = function() { + return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.precision = function() { + return arguments.length ? greatArc : 0; + }; + return greatArc; + }; + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); + var interpolate = d ? function(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; + } : function() { + return [ x0 * d3_degrees, y0 * d3_degrees ]; + }; + interpolate.distance = d; + return interpolate; + } + d3.geo.length = function(object) { + d3_geo_lengthSum = 0; + d3.geo.stream(object, d3_geo_length); + return d3_geo_lengthSum; + }; + var d3_geo_lengthSum; + var d3_geo_length = { + sphere: d3_noop, + point: d3_noop, + lineStart: d3_geo_lengthLineStart, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_lengthLineStart() { + var λ0, sinφ0, cosφ0; + d3_geo_length.point = function(λ, φ) { + λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); + d3_geo_length.point = nextPoint; + }; + d3_geo_length.lineEnd = function() { + d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; + }; + function nextPoint(λ, φ) { + var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); + d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; + } + } + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; + }; + return azimuthal; + } + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(ρ) { + return 2 * Math.asin(ρ / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + function d3_geo_conicConformal(φ0, φ1) { + var cosφ0 = Math.cos(φ0), t = function(φ) { + return Math.tan(π / 4 + φ / 2); + }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; + if (!n) return d3_geo_mercator; + function forward(λ, φ) { + if (F > 0) { + if (φ < -halfπ + ε) φ = -halfπ + ε; + } else { + if (φ > halfπ - ε) φ = halfπ - ε; + } + var ρ = F / Math.pow(t(φ), n); + return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); + return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; + }; + return forward; + } + (d3.geo.conicConformal = function() { + return d3_geo_conic(d3_geo_conicConformal); + }).raw = d3_geo_conicConformal; + function d3_geo_conicEquidistant(φ0, φ1) { + var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; + if (abs(n) < ε) return d3_geo_equirectangular; + function forward(λ, φ) { + var ρ = G - φ; + return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = G - y; + return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; + }; + return forward; + } + (d3.geo.conicEquidistant = function() { + return d3_geo_conic(d3_geo_conicEquidistant); + }).raw = d3_geo_conicEquidistant; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + function d3_geo_mercator(λ, φ) { + return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; + }; + function d3_geo_mercatorProjection(project) { + var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; + m.scale = function() { + var v = scale.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.translate = function() { + var v = translate.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.clipExtent = function(_) { + var v = clipExtent.apply(m, arguments); + if (v === m) { + if (clipAuto = _ == null) { + var k = π * scale(), t = translate(); + clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); + } + } else if (clipAuto) { + v = null; + } + return v; + }; + return m.clipExtent(null); + } + (d3.geo.mercator = function() { + return d3_geo_mercatorProjection(d3_geo_mercator); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(ρ) { + return 2 * Math.atan(ρ); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_transverseMercator(λ, φ) { + return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; + } + d3_geo_transverseMercator.invert = function(x, y) { + return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; + }; + (d3.geo.transverseMercator = function() { + var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; + projection.center = function(_) { + return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]); + }; + projection.rotate = function(_) { + return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), + [ _[0], _[1], _[2] - 90 ]); + }; + return rotate([ 0, 0, 90 ]); + }).raw = d3_geo_transverseMercator; + d3.geom = {}; + function d3_geom_pointX(d) { + return d[0]; + } + function d3_geom_pointY(d) { + return d[1]; + } + d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, y = d3_geom_pointY; + if (arguments.length) return hull(vertices); + function hull(data) { + if (data.length < 3) return []; + var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; + for (i = 0; i < n; i++) { + points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); + } + points.sort(d3_geom_hullOrder); + for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); + var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); + var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; + for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); + for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); + return polygon; + } + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; + }; + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; + }; + return hull; + }; + function d3_geom_hullUpper(points) { + var n = points.length, hull = [ 0, 1 ], hs = 2; + for (var i = 2; i < n; i++) { + while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; + hull[hs++] = i; + } + return hull.slice(0, hs); + } + function d3_geom_hullOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; + } + d3.geom.polygon = function(coordinates) { + d3_subclass(coordinates, d3_geom_polygonPrototype); + return coordinates; + }; + var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; + d3_geom_polygonPrototype.area = function() { + var i = -1, n = this.length, a, b = this[n - 1], area = 0; + while (++i < n) { + a = b; + b = this[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + return area * .5; + }; + d3_geom_polygonPrototype.centroid = function(k) { + var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; + if (!arguments.length) k = -1 / (6 * this.area()); + while (++i < n) { + a = b; + b = this[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + d3_geom_polygonPrototype.clip = function(subject) { + var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = this[i]; + c = input[(m = input.length - closed) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + if (closed) subject.push(subject[0]); + a = b; + } + return subject; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + function d3_geom_polygonClosed(coordinates) { + var a = coordinates[0], b = coordinates[coordinates.length - 1]; + return !(a[0] - b[0] || a[1] - b[1]); + } + var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; + function d3_geom_voronoiBeach() { + d3_geom_voronoiRedBlackNode(this); + this.edge = this.site = this.circle = null; + } + function d3_geom_voronoiCreateBeach(site) { + var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); + beach.site = site; + return beach; + } + function d3_geom_voronoiDetachBeach(beach) { + d3_geom_voronoiDetachCircle(beach); + d3_geom_voronoiBeaches.remove(beach); + d3_geom_voronoiBeachPool.push(beach); + d3_geom_voronoiRedBlackNode(beach); + } + function d3_geom_voronoiRemoveBeach(beach) { + var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { + x: x, + y: y + }, previous = beach.P, next = beach.N, disappearing = [ beach ]; + d3_geom_voronoiDetachBeach(beach); + var lArc = previous; + while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { + previous = lArc.P; + disappearing.unshift(lArc); + d3_geom_voronoiDetachBeach(lArc); + lArc = previous; + } + disappearing.unshift(lArc); + d3_geom_voronoiDetachCircle(lArc); + var rArc = next; + while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { + next = rArc.N; + disappearing.push(rArc); + d3_geom_voronoiDetachBeach(rArc); + rArc = next; + } + disappearing.push(rArc); + d3_geom_voronoiDetachCircle(rArc); + var nArcs = disappearing.length, iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiAddBeach(site) { + var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; + while (node) { + dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; + if (dxl > ε) node = node.L; else { + dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); + if (dxr > ε) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -ε) { + lArc = node.P; + rArc = node; + } else if (dxr > -ε) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + var newArc = d3_geom_voronoiCreateBeach(site); + d3_geom_voronoiBeaches.insert(lArc, newArc); + if (!lArc && !rArc) return; + if (lArc === rArc) { + d3_geom_voronoiDetachCircle(lArc); + rArc = d3_geom_voronoiCreateBeach(lArc.site); + d3_geom_voronoiBeaches.insert(newArc, rArc); + newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + return; + } + if (!rArc) { + newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + return; + } + d3_geom_voronoiDetachCircle(lArc); + d3_geom_voronoiDetachCircle(rArc); + var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { + x: (cy * hb - by * hc) / d + ax, + y: (bx * hc - cx * hb) / d + ay + }; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); + rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiLeftBreakPoint(arc, directrix) { + var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; + if (!pby2) return rfocx; + var lArc = arc.P; + if (!lArc) return -Infinity; + site = lArc.site; + var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; + if (!plby2) return lfocx; + var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; + if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; + return (rfocx + lfocx) / 2; + } + function d3_geom_voronoiRightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); + var site = arc.site; + return site.y === directrix ? site.x : Infinity; + } + function d3_geom_voronoiCell(site) { + this.site = site; + this.edges = []; + } + d3_geom_voronoiCell.prototype.prepare = function() { + var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; + while (iHalfEdge--) { + edge = halfEdges[iHalfEdge].edge; + if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); + } + halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); + return halfEdges.length; + }; + function d3_geom_voronoiCloseCells(extent) { + var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; + while (iCell--) { + cell = cells[iCell]; + if (!cell || !cell.prepare()) continue; + halfEdges = cell.edges; + nHalfEdges = halfEdges.length; + iHalfEdge = 0; + while (iHalfEdge < nHalfEdges) { + end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; + start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; + if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { + halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { + x: x0, + y: abs(x2 - x0) < ε ? y2 : y1 + } : abs(y3 - y1) < ε && x1 - x3 > ε ? { + x: abs(y2 - y1) < ε ? x2 : x1, + y: y1 + } : abs(x3 - x1) < ε && y3 - y0 > ε ? { + x: x1, + y: abs(x2 - x1) < ε ? y2 : y0 + } : abs(y3 - y0) < ε && x3 - x0 > ε ? { + x: abs(y2 - y0) < ε ? x2 : x0, + y: y0 + } : null), cell.site, null)); + ++nHalfEdges; + } + } + } + } + function d3_geom_voronoiHalfEdgeOrder(a, b) { + return b.angle - a.angle; + } + function d3_geom_voronoiCircle() { + d3_geom_voronoiRedBlackNode(this); + this.x = this.y = this.arc = this.site = this.cy = null; + } + function d3_geom_voronoiAttachCircle(arc) { + var lArc = arc.P, rArc = arc.N; + if (!lArc || !rArc) return; + var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; + if (lSite === rSite) return; + var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; + var d = 2 * (ax * cy - ay * cx); + if (d >= -ε2) return; + var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; + var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = cy + Math.sqrt(x * x + y * y); + circle.cy = cy; + arc.circle = circle; + var before = null, node = d3_geom_voronoiCircles._; + while (node) { + if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { + if (node.L) node = node.L; else { + before = node.P; + break; + } + } else { + if (node.R) node = node.R; else { + before = node; + break; + } + } + } + d3_geom_voronoiCircles.insert(before, circle); + if (!before) d3_geom_voronoiFirstCircle = circle; + } + function d3_geom_voronoiDetachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; + d3_geom_voronoiCircles.remove(circle); + d3_geom_voronoiCirclePool.push(circle); + d3_geom_voronoiRedBlackNode(circle); + arc.circle = null; + } + } + function d3_geom_voronoiClipEdges(extent) { + var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; + while (i--) { + e = edges[i]; + if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { + e.a = e.b = null; + edges.splice(i, 1); + } + } + } + function d3_geom_voronoiConnectEdge(edge, extent) { + var vb = edge.b; + if (vb) return true; + var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!va) va = { + x: fx, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: fx, + y: y1 + }; + } else { + if (!va) va = { + x: fx, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: fx, + y: y0 + }; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!va) va = { + x: (y0 - fb) / fm, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: (y1 - fb) / fm, + y: y1 + }; + } else { + if (!va) va = { + x: (y1 - fb) / fm, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: (y0 - fb) / fm, + y: y0 + }; + } + } else { + if (ly < ry) { + if (!va) va = { + x: x0, + y: fm * x0 + fb + }; else if (va.x >= x1) return; + vb = { + x: x1, + y: fm * x1 + fb + }; + } else { + if (!va) va = { + x: x1, + y: fm * x1 + fb + }; else if (va.x < x0) return; + vb = { + x: x0, + y: fm * x0 + fb + }; + } + } + } + edge.a = va; + edge.b = vb; + return true; + } + function d3_geom_voronoiEdge(lSite, rSite) { + this.l = lSite; + this.r = rSite; + this.a = this.b = null; + } + function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, rSite); + d3_geom_voronoiEdges.push(edge); + if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); + if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); + d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); + d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); + return edge; + } + function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, null); + edge.a = va; + edge.b = vb; + d3_geom_voronoiEdges.push(edge); + return edge; + } + function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { + if (!edge.a && !edge.b) { + edge.a = vertex; + edge.l = lSite; + edge.r = rSite; + } else if (edge.l === rSite) { + edge.b = vertex; + } else { + edge.a = vertex; + } + } + function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { + var va = edge.a, vb = edge.b; + this.edge = edge; + this.site = lSite; + this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); + } + d3_geom_voronoiHalfEdge.prototype = { + start: function() { + return this.edge.l === this.site ? this.edge.a : this.edge.b; + }, + end: function() { + return this.edge.l === this.site ? this.edge.b : this.edge.a; + } + }; + function d3_geom_voronoiRedBlackTree() { + this._ = null; + } + function d3_geom_voronoiRedBlackNode(node) { + node.U = node.C = node.L = node.R = node.P = node.N = null; + } + d3_geom_voronoiRedBlackTree.prototype = { + insert: function(after, node) { + var parent, grandpa, uncle; + if (after) { + node.P = after; + node.N = after.N; + if (after.N) after.N.P = node; + after.N = node; + if (after.R) { + after = after.R; + while (after.L) after = after.L; + after.L = node; + } else { + after.R = node; + } + parent = after; + } else if (this._) { + after = d3_geom_voronoiRedBlackFirst(this._); + node.P = null; + node.N = after; + after.P = after.L = node; + parent = after; + } else { + node.P = node.N = null; + this._ = node; + parent = null; + } + node.L = node.R = null; + node.U = parent; + node.C = true; + after = node; + while (parent && parent.C) { + grandpa = parent.U; + if (parent === grandpa.L) { + uncle = grandpa.R; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.R) { + d3_geom_voronoiRedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateRight(this, grandpa); + } + } else { + uncle = grandpa.L; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.L) { + d3_geom_voronoiRedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, grandpa); + } + } + parent = after.U; + } + this._.C = false; + }, + remove: function(node) { + if (node.N) node.N.P = node.P; + if (node.P) node.P.N = node.N; + node.N = node.P = null; + var parent = node.U, sibling, left = node.L, right = node.R, next, red; + if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); + if (parent) { + if (parent.L === node) parent.L = next; else parent.R = next; + } else { + this._ = next; + } + if (left && right) { + red = next.C; + next.C = node.C; + next.L = left; + left.U = next; + if (next !== right) { + parent = next.U; + next.U = node.U; + node = next.R; + parent.L = node; + next.R = right; + right.U = next; + } else { + next.U = parent; + parent = next; + node = next.R; + } + } else { + red = node.C; + node = next; + } + if (node) node.U = parent; + if (red) return; + if (node && node.C) { + node.C = false; + return; + } + do { + if (node === this._) break; + if (node === parent.L) { + sibling = parent.R; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + sibling = parent.R; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.R || !sibling.R.C) { + sibling.L.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateRight(this, parent); + sibling = parent.L; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.L || !sibling.L.C) { + sibling.R.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + d3_geom_voronoiRedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + if (node) node.C = false; + } + }; + function d3_geom_voronoiRedBlackRotateLeft(tree, node) { + var p = node, q = node.R, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.R = q.L; + if (p.R) p.R.U = p; + q.L = p; + } + function d3_geom_voronoiRedBlackRotateRight(tree, node) { + var p = node, q = node.L, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.L = q.R; + if (p.L) p.L.U = p; + q.R = p; + } + function d3_geom_voronoiRedBlackFirst(node) { + while (node.L) node = node.L; + return node; + } + function d3_geom_voronoi(sites, bbox) { + var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; + d3_geom_voronoiEdges = []; + d3_geom_voronoiCells = new Array(sites.length); + d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); + d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); + while (true) { + circle = d3_geom_voronoiFirstCircle; + if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { + if (site.x !== x0 || site.y !== y0) { + d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); + d3_geom_voronoiAddBeach(site); + x0 = site.x, y0 = site.y; + } + site = sites.pop(); + } else if (circle) { + d3_geom_voronoiRemoveBeach(circle.arc); + } else { + break; + } + } + if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); + var diagram = { + cells: d3_geom_voronoiCells, + edges: d3_geom_voronoiEdges + }; + d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; + return diagram; + } + function d3_geom_voronoiVertexOrder(a, b) { + return b.y - a.y || b.x - a.x; + } + d3.geom.voronoi = function(points) { + var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; + if (points) return voronoi(points); + function voronoi(data) { + var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; + d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { + var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { + var s = e.start(); + return [ s.x, s.y ]; + }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; + polygon.point = data[i]; + }); + return polygons; + } + function sites(data) { + return data.map(function(d, i) { + return { + x: Math.round(fx(d, i) / ε) * ε, + y: Math.round(fy(d, i) / ε) * ε, + i: i + }; + }); + } + voronoi.links = function(data) { + return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { + return edge.l && edge.r; + }).map(function(edge) { + return { + source: data[edge.l.i], + target: data[edge.r.i] + }; + }); + }; + voronoi.triangles = function(data) { + var triangles = []; + d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { + var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; + while (++j < m) { + e0 = e1; + s0 = s1; + e1 = edges[j].edge; + s1 = e1.l === site ? e1.r : e1.l; + if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { + triangles.push([ data[i], data[s0.i], data[s1.i] ]); + } + } + }); + return triangles; + }; + voronoi.x = function(_) { + return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; + }; + voronoi.y = function(_) { + return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; + }; + voronoi.clipExtent = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; + clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; + return voronoi; + }; + voronoi.size = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; + return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); + }; + return voronoi; + }; + var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; + function d3_geom_voronoiTriangleArea(a, b, c) { + return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); + } + d3.geom.delaunay = function(vertices) { + return d3.geom.voronoi().triangles(vertices); + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var x = d3_geom_pointX, y = d3_geom_pointY, compat; + if (compat = arguments.length) { + x = d3_geom_quadtreeCompatX; + y = d3_geom_quadtreeCompatY; + if (compat === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } + return quadtree(points); + } + function quadtree(data) { + var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; + if (x1 != null) { + x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; + } else { + x2_ = y2_ = -(x1_ = y1_ = Infinity); + xs = [], ys = []; + n = data.length; + if (compat) for (i = 0; i < n; ++i) { + d = data[i]; + if (d.x < x1_) x1_ = d.x; + if (d.y < y1_) y1_ = d.y; + if (d.x > x2_) x2_ = d.x; + if (d.y > y2_) y2_ = d.y; + xs.push(d.x); + ys.push(d.y); + } else for (i = 0; i < n; ++i) { + var x_ = +fx(d = data[i], i), y_ = +fy(d, i); + if (x_ < x1_) x1_ = x_; + if (y_ < y1_) y1_ = y_; + if (x_ > x2_) x2_ = x_; + if (y_ > y2_) y2_ = y_; + xs.push(x_); + ys.push(y_); + } + } + var dx = x2_ - x1_, dy = y2_ - y1_; + if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; + function insert(n, d, x, y, x1, y1, x2, y2) { + if (isNaN(x) || isNaN(y)) return; + if (n.leaf) { + var nx = n.x, ny = n.y; + if (nx != null) { + if (abs(nx - x) + abs(ny - y) < .01) { + insertChild(n, d, x, y, x1, y1, x2, y2); + } else { + var nPoint = n.point; + n.x = n.y = n.point = null; + insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } else { + n.x = x, n.y = y, n.point = d; + } + } else { + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } + function insertChild(n, d, x, y, x1, y1, x2, y2) { + var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = xm; else x2 = xm; + if (below) y1 = ym; else y2 = ym; + insert(n, d, x, y, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(d) { + insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); + }; + root.find = function(point) { + return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_); + }; + i = -1; + if (x1 == null) { + while (++i < n) { + insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); + } + --i; + } else data.forEach(root.add); + xs = ys = data = d = null; + return root; + } + quadtree.x = function(_) { + return arguments.length ? (x = _, quadtree) : x; + }; + quadtree.y = function(_) { + return arguments.length ? (y = _, quadtree) : y; + }; + quadtree.extent = function(_) { + if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], + y2 = +_[1][1]; + return quadtree; + }; + quadtree.size = function(_) { + if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; + return quadtree; + }; + return quadtree; + }; + function d3_geom_quadtreeCompatX(d) { + return d.x; + } + function d3_geom_quadtreeCompatY(d) { + return d.y; + } + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null, + x: null, + y: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) { + var minDistance2 = Infinity, closestPoint; + (function find(node, x1, y1, x2, y2) { + if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return; + if (point = node.point) { + var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy; + if (distance2 < minDistance2) { + var distance = Math.sqrt(minDistance2 = distance2); + x0 = x - distance, y0 = y - distance; + x3 = x + distance, y3 = y + distance; + closestPoint = point; + } + } + var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym; + for (var i = below << 1 | right, j = i + 4; i < j; ++i) { + if (node = children[i & 3]) switch (i & 3) { + case 0: + find(node, x1, y1, xm, ym); + break; + + case 1: + find(node, xm, y1, x2, ym); + break; + + case 2: + find(node, x1, ym, xm, y2); + break; + + case 3: + find(node, xm, ym, x2, y2); + break; + } + } + })(root, x0, y0, x3, y3); + return closestPoint; + } + d3.interpolateRgb = d3_interpolateRgb; + function d3_interpolateRgb(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + } + d3.interpolateObject = d3_interpolateObject; + function d3_interpolateObject(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolate(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + } + d3.interpolateNumber = d3_interpolateNumber; + function d3_interpolateNumber(a, b) { + a = +a, b = +b; + return function(t) { + return a * (1 - t) + b * t; + }; + } + d3.interpolateString = d3_interpolateString; + function d3_interpolateString(a, b) { + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; + a = a + "", b = b + ""; + while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { + if (s[i]) s[i] += bm; else s[++i] = bm; + } else { + s[++i] = null; + q.push({ + i: i, + x: d3_interpolateNumber(am, bm) + }); + } + bi = d3_interpolate_numberB.lastIndex; + } + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { + return b(t) + ""; + }) : function() { + return b; + } : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); + } + var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); + d3.interpolate = d3_interpolate; + function d3_interpolate(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + } + d3.interpolators = [ function(a, b) { + var t = typeof b; + return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); + } ]; + d3.interpolateArray = d3_interpolateArray; + function d3_interpolateArray(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * halfπ); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.interpolateHcl = d3_interpolateHcl; + function d3_interpolateHcl(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + } + d3.interpolateHsl = d3_interpolateHsl; + function d3_interpolateHsl(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; + if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; + }; + } + d3.interpolateLab = d3_interpolateLab; + function d3_interpolateLab(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + } + d3.interpolateRound = d3_interpolateRound; + function d3_interpolateRound(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + if (string != null) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + } + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolateTransform = d3_interpolateTransform; + function d3_interpolateTransformPop(s) { + return s.length ? s.pop() + "," : ""; + } + function d3_interpolateTranslate(ta, tb, s, q) { + if (ta[0] !== tb[0] || ta[1] !== tb[1]) { + var i = s.push("translate(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(ta[0], tb[0]) + }, { + i: i - 2, + x: d3_interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } + } + function d3_interpolateRotate(ra, rb, s, q) { + if (ra !== rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2, + x: d3_interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")"); + } + } + function d3_interpolateSkew(wa, wb, s, q) { + if (wa !== wb) { + q.push({ + i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2, + x: d3_interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")"); + } + } + function d3_interpolateScale(ka, kb, s, q) { + if (ka[0] !== kb[0] || ka[1] !== kb[1]) { + var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(ka[0], kb[0]) + }, { + i: i - 2, + x: d3_interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] !== 1 || kb[1] !== 1) { + s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")"); + } + } + function d3_interpolateTransform(a, b) { + var s = [], q = []; + a = d3.transform(a), b = d3.transform(b); + d3_interpolateTranslate(a.translate, b.translate, s, q); + d3_interpolateRotate(a.rotate, b.rotate, s, q); + d3_interpolateSkew(a.skew, b.skew, s, q); + d3_interpolateScale(a.scale, b.scale, s, q); + a = b = null; + return function(t) { + var i = -1, n = q.length, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + } + function d3_uninterpolateNumber(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return (x - a) / b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return Math.max(0, Math.min(1, (x - a) / b)); + }; + } + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (τ - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: groupSums[di] + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; + if (dw * dw / theta2 < dn) { + if (dn < chargeDistance2) { + var k = quad.charge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + return true; + } + if (quad.point && dn && dn < chargeDistance2) { + var k = quad.pointCharge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + timer = null; + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.chargeDistance = function(x) { + if (!arguments.length) return Math.sqrt(chargeDistance2); + chargeDistance2 = x * x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return Math.sqrt(theta2); + theta2 = x * x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) { + alpha = x; + } else { + timer.c = null, timer.t = NaN, timer = null; + event.end({ + type: "end", + alpha: alpha = 0 + }); + } + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + timer = d3_timer(force.tick); + } + return force; + }; + force.start = function() { + var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + if (!neighbors) { + neighbors = new Array(n); + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + var candidates = neighbors[i], j = -1, l = candidates.length, x; + while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x; + return Math.random() * size; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function hierarchy(root) { + var stack = [ root ], nodes = [], node; + root.depth = 0; + while ((node = stack.pop()) != null) { + nodes.push(node); + if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { + var n, childs, child; + while (--n >= 0) { + stack.push(child = childs[n]); + child.parent = node; + child.depth = node.depth + 1; + } + if (value) node.value = 0; + node.children = childs; + } else { + if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; + delete node.children; + } + } + d3_layout_hierarchyVisitAfter(root, function(node) { + var childs, parent; + if (sort && (childs = node.children)) childs.sort(sort); + if (value && (parent = node.parent)) parent.value += node.value; + }); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + if (value) { + d3_layout_hierarchyVisitBefore(root, function(node) { + if (node.children) node.value = 0; + }); + d3_layout_hierarchyVisitAfter(root, function(node) { + var parent; + if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; + if (parent = node.parent) parent.value += node.value; + }); + } + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyVisitBefore(node, callback) { + var nodes = [ node ]; + while ((node = nodes.pop()) != null) { + callback(node); + if ((children = node.children) && (n = children.length)) { + var n, children; + while (--n >= 0) nodes.push(children[n]); + } + } + } + function d3_layout_hierarchyVisitAfter(node, callback) { + var nodes = [ node ], nodes2 = []; + while ((node = nodes.pop()) != null) { + nodes2.push(node); + if ((children = node.children) && (n = children.length)) { + var i = -1, n, children; + while (++i < n) nodes.push(children[i]); + } + } + while ((node = nodes2.pop()) != null) { + callback(node); + } + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0; + function pie(data) { + var n = data.length, values = data.map(function(d, i) { + return +value.call(pie, d, i); + }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v; + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + index.forEach(function(i) { + arcs[i] = { + data: data[i], + value: v = values[i], + startAngle: a, + endAngle: a += v * k + pa, + padAngle: p + }; + }); + return arcs; + } + pie.value = function(_) { + if (!arguments.length) return value; + value = _; + return pie; + }; + pie.sort = function(_) { + if (!arguments.length) return sort; + sort = _; + return pie; + }; + pie.startAngle = function(_) { + if (!arguments.length) return startAngle; + startAngle = _; + return pie; + }; + pie.endAngle = function(_) { + if (!arguments.length) return endAngle; + endAngle = _; + return pie; + }; + pie.padAngle = function(_) { + if (!arguments.length) return padAngle; + padAngle = _; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + if (!(n = data.length)) return data; + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var m = series[0].length, n, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { + return radius; + }; + root.x = root.y = 0; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r = +r(d.value); + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + if (padding) { + var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r -= dr; + }); + } + d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); + return nodes; + } + pack.size = function(_) { + if (!arguments.length) return size; + size = _; + return pack; + }; + pack.radius = function(_) { + if (!arguments.length) return radius; + radius = _ == null || typeof _ === "function" ? _ : +_; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return .999 * dr * dr > dx * dx + dy * dy; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); + d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; + d3_layout_hierarchyVisitBefore(root1, secondWalk); + if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { + var left = root0, right = root0, bottom = root0; + d3_layout_hierarchyVisitBefore(root0, function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); + d3_layout_hierarchyVisitBefore(root0, function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + return nodes; + } + function wrapTree(root0) { + var root1 = { + A: null, + children: [ root0 ] + }, queue = [ root1 ], node1; + while ((node1 = queue.pop()) != null) { + for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { + queue.push((children[i] = child = { + _: children[i], + parent: node1, + children: (child = children[i].children) && child.slice() || [], + A: null, + a: null, + z: 0, + m: 0, + c: 0, + s: 0, + t: null, + i: i + }).a = child); + } + } + return root1.children[0]; + } + function firstWalk(v) { + var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; + if (children.length) { + d3_layout_treeShift(v); + var midpoint = (children[0].z + children[children.length - 1].z) / 2; + if (w) { + v.z = w.z + separation(v._, w._); + v.m = v.z - midpoint; + } else { + v.z = midpoint; + } + } else if (w) { + v.z = w.z + separation(v._, w._); + } + v.parent.A = apportion(v, w, v.parent.A || siblings[0]); + } + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + function apportion(v, w, ancestor) { + if (w) { + var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !d3_layout_treeRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + function sizeNode(node) { + node.x *= size[0]; + node.y = node.depth * size[1]; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null ? sizeNode : null; + return tree; + }; + tree.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) == null ? null : sizeNode; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(v) { + var children = v.children; + return children.length ? children[0] : v.t; + } + function d3_layout_treeRight(v) { + var children = v.children, n; + return (n = children.length) ? children[n - 1] : v.t; + } + function d3_layout_treeMove(wm, wp, shift) { + var change = shift / (wp.i - wm.i); + wp.c -= change; + wp.s += shift; + wm.c += change; + wp.z += shift; + wp.m += shift; + } + function d3_layout_treeShift(v) { + var shift = 0, change = 0, children = v.children, i = children.length, w; + while (--i >= 0) { + w = children[i]; + w.z += shift; + w.m += shift; + shift += w.s + (change += w.c); + } + } + function d3_layout_treeAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_hierarchyVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { + node.x = (node.x - root.x) * size[0]; + node.y = (root.y - node.y) * size[1]; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null; + return cluster; + }; + cluster.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) != null; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = root.y = 0; + if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + bates: function(m) { + var random = d3.random.irwinHall(m); + return function() { + return random() / m; + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s; + }; + } + }; + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + return domain; + } + function d3_scale_niceStep(step) { + return step ? { + floor: function(x) { + return Math.floor(x / step) * step; + }, + ceil: function(x) { + return Math.ceil(x / step) * step; + } + } : d3_scale_niceIdentity; + } + var d3_scale_niceIdentity = { + floor: d3_identity, + ceil: d3_identity + }; + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3_interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3_interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + d3_scale_linearNice(domain, m); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(domain, m) { + d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + return domain; + } + function d3_scale_linearTickRange(domain, m) { + if (m == null) m = 10; + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m, format) { + var range = d3_scale_linearTickRange(domain, m); + if (format) { + var match = d3_format_re.exec(format); + match.shift(); + if (match[8] === "s") { + var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); + if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); + match[8] = "f"; + format = d3.format(match.join("")); + return function(d) { + return format(prefix.scale(d)) + prefix.symbol; + }; + } + if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); + format = match.join(""); + } else { + format = ",." + d3_scale_linearPrecision(range[2]) + "f"; + } + return d3.format(format); + } + var d3_scale_linearFormatSignificant = { + s: 1, + g: 1, + p: 1, + r: 1, + e: 1 + }; + function d3_scale_linearPrecision(value) { + return -Math.floor(Math.log(value) / Math.LN10 + .01); + } + function d3_scale_linearFormatPrecision(type, range) { + var p = d3_scale_linearPrecision(range[2]); + return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); + }; + function d3_scale_log(linear, base, positive, domain) { + function log(x) { + return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); + } + function pow(x) { + return positive ? Math.pow(base, x) : -Math.pow(base, -x); + } + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + positive = x[0] >= 0; + linear.domain((domain = x.map(Number)).map(log)); + return scale; + }; + scale.base = function(_) { + if (!arguments.length) return base; + base = +_; + linear.domain(domain.map(log)); + return scale; + }; + scale.nice = function() { + var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); + linear.domain(niced); + domain = niced.map(pow); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; + if (isFinite(j - i)) { + if (positive) { + for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } else { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (!arguments.length) return d3_scale_logFormat; + if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); + var k = Math.max(1, base * n / scale.ticks().length); + return function(d) { + var i = d / pow(Math.round(log(d))); + if (i * base < base - .5) i *= base; + return i <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), base, positive, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { + floor: function(x) { + return -Math.ceil(-x); + }, + ceil: function(x) { + return -Math.floor(-x); + } + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); + }; + function d3_scale_pow(linear, exponent, domain) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + linear.domain((domain = x.map(Number)).map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + return scale.domain(d3_scale_linearNice(domain, m)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + linear.domain(domain.map(powp)); + return scale; + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, + 0) : (stop - start) / (domain.length - 1 + padding); + range = steps(start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeRoundPoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), + 0) : (stop - start) / (domain.length - 1 + padding) | 0; + range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step); + rangeBand = 0; + ranger = { + t: "rangeRoundPoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)); + range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); + var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); + var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); + var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + y = y < 0 ? NaN : y / kx + x0; + return [ y, y + 1 / kx ]; + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + if (x <= x) return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return [ domain[y - 1], domain[y] ]; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + function d3_zero() { + return 0; + } + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle; + function arc() { + var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1; + if (r1 < r0) rc = r1, r1 = r0, r0 = rc; + if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z"; + var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = []; + if (ap = (+padAngle.apply(this, arguments) || 0) / 2) { + rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments); + if (!cw) p1 *= -1; + if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap)); + if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap)); + } + if (r1) { + x0 = r1 * Math.cos(a0 + p1); + y0 = r1 * Math.sin(a0 + p1); + x1 = r1 * Math.cos(a1 - p1); + y1 = r1 * Math.sin(a1 - p1); + var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1; + if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) { + var h1 = (a0 + a1) / 2; + x0 = r1 * Math.cos(h1); + y0 = r1 * Math.sin(h1); + x1 = y1 = null; + } + } else { + x0 = y0 = 0; + } + if (r0) { + x2 = r0 * Math.cos(a1 - p0); + y2 = r0 * Math.sin(a1 - p0); + x3 = r0 * Math.cos(a0 + p0); + y3 = r0 * Math.sin(a0 + p0); + var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1; + if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) { + var h0 = (a0 + a1) / 2; + x2 = r0 * Math.cos(h0); + y2 = r0 * Math.sin(h0); + x3 = y3 = null; + } + } else { + x2 = y2 = 0; + } + if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { + cr = r0 < r1 ^ cw ? 0 : 1; + var rc1 = rc, rc0 = rc; + if (da < π) { + var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); + rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); + rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); + } + if (x1 != null) { + var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); + if (rc === rc1) { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); + } else { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]); + } + } else { + path.push("M", x0, ",", y0); + } + if (x3 != null) { + var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); + if (rc === rc0) { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } else { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } + } else { + path.push("L", x2, ",", y2); + } + } else { + path.push("M", x0, ",", y0); + if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1); + path.push("L", x2, ",", y2); + if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3); + } + path.push("Z"); + return path.join(""); + } + function circleSegment(r1, cw) { + return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.cornerRadius = function(v) { + if (!arguments.length) return cornerRadius; + cornerRadius = d3_functor(v); + return arc; + }; + arc.padRadius = function(v) { + if (!arguments.length) return padRadius; + padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.padAngle = function(v) { + if (!arguments.length) return padAngle; + padAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcAuto = "auto"; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_arcPadAngle(d) { + return d && d.padAngle; + } + function d3_svg_arcSweep(x0, y0, x1, y1) { + return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; + } + function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { + var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; + return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; + } + function d3_svg_line(projection) { + var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + step: d3_svg_lineStep, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.length > 1 ? points.join("L") : points + "Z"; + } + function d3_svg_lineLinearClosed(points) { + return points.join("L") + "Z"; + } + function d3_svg_lineStep(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); + if (n > 1) path.push("H", p[0]); + return path.join(""); + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + points.push(points[n - 1]); + while (++i <= n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + points.pop(); + path.push("L", pi); + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (abs(d) < ε) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] - halfπ; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / π); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + d3_selectionPrototype.transition = function(name) { + var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || { + time: Date.now(), + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id); + }; + d3_selectionPrototype.interrupt = function(name) { + return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name))); + }; + var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); + function d3_selection_interruptNS(ns) { + return function() { + var lock, activeId, active; + if ((lock = this[ns]) && (active = lock[activeId = lock.active])) { + active.timer.c = null; + active.timer.t = NaN; + if (--lock.count) delete lock[activeId]; else delete this[ns]; + lock.active += .5; + active.event && active.event.interrupt.call(this, this.__data__, active.index); + } + }; + } + function d3_transition(groups, ns, id) { + d3_subclass(groups, d3_transitionPrototype); + groups.namespace = ns; + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3_transitionPrototype.size = d3_selectionPrototype.size; + d3.transition = function(selection, name) { + return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection); + }; + d3.transition.prototype = d3_transitionPrototype; + d3_transitionPrototype.select = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, ns, id, node[ns][id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node[ns][id]; + subnodes = selector.call(node, node.__data__, i, j); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.namespace, this.id); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) return this.node()[ns][id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node[ns][id].tween.remove(name); + } : function(node) { + node[ns][id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id, ns = groups.namespace; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node[ns][id].tween.set(name, value); + })); + } + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrTween(b) { + return b == null ? attrNull : (b += "", function() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + }); + } + function attrTweenNS(b) { + return b == null ? attrNullNS : (b += "", function() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + }); + } + return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + function styleNull() { + this.style.removeProperty(name); + } + function styleString(b) { + return b == null ? styleNull : (b += "", function() { + var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = d3_interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + }); + } + return d3_transition_tween(this, "style." + name, value, styleString); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + function styleTween(d, i) { + var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + } + return this.tween("style." + name, styleTween); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + var ns = this.namespace; + return this.each("end.transition", function() { + var p; + if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node[ns][id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].delay; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].delay = +value.call(node, node.__data__, i, j); + } : (value = +value, function(node) { + node[ns][id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].duration; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); + } : (value = Math.max(1, value), function(node) { + node[ns][id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + try { + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node[ns][id]; + type.call(node, node.__data__, i, j); + }); + } finally { + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } + } else { + d3_selection_each(this, function(node) { + var transition = node[ns][id]; + (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = node[ns][id0]; + d3_transitionNode(node, i, ns, id1, { + time: transition.time, + ease: transition.ease, + delay: transition.delay + transition.duration, + duration: transition.duration + }); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id1); + }; + function d3_transitionNamespace(name) { + return name == null ? "__transition__" : "__transition_" + name + "__"; + } + function d3_transitionNode(node, i, ns, id, inherit) { + var lock = node[ns] || (node[ns] = { + active: 0, + count: 0 + }), transition = lock[id], time, timer, duration, ease, tweens; + function schedule(elapsed) { + var delay = transition.delay; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; + } + function start(elapsed) { + var activeId = lock.active, active = lock[activeId]; + if (active) { + active.timer.c = null; + active.timer.t = NaN; + --lock.count; + delete lock[activeId]; + active.event && active.event.interrupt.call(node, node.__data__, active.index); + } + for (var cancelId in lock) { + if (+cancelId < id) { + var cancel = lock[cancelId]; + cancel.timer.c = null; + cancel.timer.t = NaN; + --lock.count; + delete lock[cancelId]; + } + } + timer.c = tick; + d3_timer(function() { + if (timer.c && tick(elapsed || 1)) { + timer.c = null; + timer.t = NaN; + } + return 1; + }, 0, time); + lock.active = id; + transition.event && transition.event.start.call(node, node.__data__, i); + tweens = []; + transition.tween.forEach(function(key, value) { + if (value = value.call(node, node.__data__, i)) { + tweens.push(value); + } + }); + ease = transition.ease; + duration = transition.duration; + } + function tick(elapsed) { + var t = elapsed / duration, e = ease(t), n = tweens.length; + while (n > 0) { + tweens[--n].call(node, e); + } + if (t >= 1) { + transition.event && transition.event.end.call(node, node.__data__, i); + if (--lock.count) delete lock[id]; else delete node[ns]; + return 1; + } + } + if (!transition) { + time = inherit.time; + timer = d3_timer(schedule, 0, time); + transition = lock[id] = { + tween: new d3_Map(), + time: time, + timer: timer, + delay: inherit.delay, + duration: inherit.duration, + ease: inherit.ease, + index: i + }; + inherit = null; + ++lock.count; + } + } + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); + var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform; + var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2; + if (orient === "bottom" || orient === "top") { + tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2"; + text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize); + } else { + tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2"; + text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start"); + pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize); + } + lineEnter.attr(y2, sign * innerTickSize); + textEnter.attr(y1, sign * tickSpacing); + lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize); + textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing); + if (scale1.rangeBand) { + var x = scale1, dx = x.rangeBand() / 2; + scale0 = scale1 = function(d) { + return x(d) + dx; + }; + } else if (scale0.rangeBand) { + scale0 = scale1; + } else { + tickExit.call(tickTransform, scale1, scale0); + } + tickEnter.call(tickTransform, scale0, scale1); + tickUpdate.call(tickTransform, scale1, scale1); + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = d3_array(arguments); + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x) { + var n = arguments.length; + if (!n) return innerTickSize; + innerTickSize = +x; + outerTickSize = +arguments[n - 1]; + return axis; + }; + axis.innerTickSize = function(x) { + if (!arguments.length) return innerTickSize; + innerTickSize = +x; + return axis; + }; + axis.outerTickSize = function(x) { + if (!arguments.length) return outerTickSize; + outerTickSize = +x; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function() { + return arguments.length && axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x0, x1) { + selection.attr("transform", function(d) { + var v0 = x0(d); + return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)"; + }); + } + function d3_svg_axisY(selection, y0, y1) { + selection.attr("transform", function(d) { + var v0 = y0(d); + return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")"; + }); + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; + function brush(g) { + g.each(function() { + var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + var background = g.selectAll(".background").data([ 0 ]); + background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); + var resize = g.selectAll(".resize").data(resizes, d3_identity); + resize.exit().remove(); + resize.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + resize.style("display", brush.empty() ? "none" : null); + var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; + if (x) { + range = d3_scaleRange(x); + backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); + redrawX(gUpdate); + } + if (y) { + range = d3_scaleRange(y); + backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); + redrawY(gUpdate); + } + redraw(gUpdate); + }); + } + brush.event = function(g) { + g.each(function() { + var event_ = event.of(this, arguments), extent1 = { + x: xExtent, + y: yExtent, + i: xExtentDomain, + j: yExtentDomain + }, extent0 = this.__chart__ || extent1; + this.__chart__ = extent1; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.brush", function() { + xExtentDomain = extent0.i; + yExtentDomain = extent0.j; + xExtent = extent0.x; + yExtent = extent0.y; + event_({ + type: "brushstart" + }); + }).tween("brush:brush", function() { + var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); + xExtentDomain = yExtentDomain = null; + return function(t) { + xExtent = extent1.x = xi(t); + yExtent = extent1.y = yi(t); + event_({ + type: "brush", + mode: "resize" + }); + }; + }).each("end.brush", function() { + xExtentDomain = extent1.i; + yExtentDomain = extent1.j; + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + }); + } else { + event_({ + type: "brushstart" + }); + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + } + }); + }; + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", xExtent[0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); + } + function redrawY(g) { + g.select(".extent").attr("y", yExtent[0]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset; + var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (d3.event.changedTouches) { + w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); + } else { + w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); + } + g.interrupt().selectAll("*").interrupt(); + if (dragging) { + origin[0] = xExtent[0] - origin[0]; + origin[1] = yExtent[0] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; + origin[0] = xExtent[ex]; + origin[1] = yExtent[ey]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= xExtent[1]; + origin[1] -= yExtent[1]; + dragging = 2; + } + d3_eventPreventDefault(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += xExtent[1]; + origin[1] += yExtent[1]; + dragging = 0; + d3_eventPreventDefault(); + } + } + function brushmove() { + var point = d3.mouse(target), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; + origin[0] = xExtent[+(point[0] < center[0])]; + origin[1] = yExtent[+(point[1] < center[1])]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0] != min || extent[1] != max) { + if (i) yExtentDomain = null; else xExtentDomain = null; + extent[0] = min; + extent[1] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + dragRestore(); + event_({ + type: "brushend" + }); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.clamp = function(z) { + if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; + if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + if (x) { + if (xExtentDomain) { + x0 = xExtentDomain[0], x1 = xExtentDomain[1]; + } else { + x0 = xExtent[0], x1 = xExtent[1]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + if (yExtentDomain) { + y0 = yExtentDomain[0], y1 = yExtentDomain[1]; + } else { + y0 = yExtent[0], y1 = yExtent[1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + xExtentDomain = [ x0, x1 ]; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + yExtentDomain = [ y0, y1 ]; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; + } + return brush; + }; + brush.clear = function() { + if (!brush.empty()) { + xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; + xExtentDomain = yExtentDomain = null; + } + return brush; + }; + brush.empty = function() { + return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; + var d3_time_formatUtc = d3_time_format.utc; + var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + d3_time.second = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3_time.seconds = d3_time.second.range; + d3_time.seconds.utc = d3_time.second.utc.range; + d3_time.minute = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3_time.minutes = d3_time.minute.range; + d3_time.minutes.utc = d3_time.minute.utc.range; + d3_time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3_time.hours = d3_time.hour.range; + d3_time.hours.utc = d3_time.hour.utc.range; + d3_time.month = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3_time.months = d3_time.month.range; + d3_time.months.utc = d3_time.month.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + function tickMethod(extent, count) { + var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); + return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { + return d / 31536e6; + }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; + } + scale.nice = function(interval, skip) { + var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); + if (method) interval = method[0], skip = method[1]; + function skipped(date) { + return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; + } + return scale.domain(d3_scale_nice(domain, skip > 1 ? { + floor: function(date) { + while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); + return date; + }, + ceil: function(date) { + while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); + return date; + } + } : interval)); + }; + scale.ticks = function(interval, skip) { + var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { + range: interval + }, skip ]; + if (method) interval = method[0], skip = method[1]; + return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_time_scaleDate(t) { + return new Date(t); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; + var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { + return d.getMilliseconds(); + } ], [ ":%S", function(d) { + return d.getSeconds(); + } ], [ "%I:%M", function(d) { + return d.getMinutes(); + } ], [ "%I %p", function(d) { + return d.getHours(); + } ], [ "%a %d", function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ "%b %d", function(d) { + return d.getDate() != 1; + } ], [ "%B", function(d) { + return d.getMonth(); + } ], [ "%Y", d3_true ] ]); + var d3_time_scaleMilliseconds = { + range: function(start, stop, step) { + return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); + }, + floor: d3_identity, + ceil: d3_identity + }; + d3_time_scaleLocalMethods.year = d3_time.year; + d3_time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { + return d.getUTCMilliseconds(); + } ], [ ":%S", function(d) { + return d.getUTCSeconds(); + } ], [ "%I:%M", function(d) { + return d.getUTCMinutes(); + } ], [ "%I %p", function(d) { + return d.getUTCHours(); + } ], [ "%a %d", function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ "%b %d", function(d) { + return d.getUTCDate() != 1; + } ], [ "%B", function(d) { + return d.getUTCMonth(); + } ], [ "%Y", d3_true ] ]); + d3_time_scaleUtcMethods.year = d3_time.year.utc; + d3_time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); + }; + d3.text = d3_xhrType(function(request) { + return request.responseText; + }); + d3.json = function(url, callback) { + return d3_xhr(url, "application/json", d3_json, callback); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3_xhr(url, "text/html", d3_html, callback); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = d3_xhrType(function(request) { + return request.responseXML; + }); + if (typeof define === "function" && define.amd) this.d3 = d3, define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3; +}.apply(self); +},{}],21:[function(_dereq_,module,exports){ +(function (global){(function (){ +'use strict'; + +var objectAssign = _dereq_('object-assign'); + +// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js +// original notice: + +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +function compare(a, b) { + if (a === b) { + return 0; + } + + var x = a.length; + var y = b.length; + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; +} +function isBuffer(b) { + if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { + return global.Buffer.isBuffer(b); + } + return !!(b != null && b._isBuffer); +} + +// based on node assert, original notice: +// NB: The URL to the CommonJS spec is kept just for tradition. +// node-assert has evolved a lot since then, both in API and behavior. + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +var util = _dereq_('util/'); +var hasOwn = Object.prototype.hasOwnProperty; +var pSlice = Array.prototype.slice; +var functionsHaveNames = (function () { + return function foo() {}.name === 'foo'; +}()); +function pToString (obj) { + return Object.prototype.toString.call(obj); +} +function isView(arrbuf) { + if (isBuffer(arrbuf)) { + return false; + } + if (typeof global.ArrayBuffer !== 'function') { + return false; + } + if (typeof ArrayBuffer.isView === 'function') { + return ArrayBuffer.isView(arrbuf); + } + if (!arrbuf) { + return false; + } + if (arrbuf instanceof DataView) { + return true; + } + if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { + return true; + } + return false; +} +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +var regex = /\s*function\s+([^\(\s]*)\s*/; +// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js +function getName(func) { + if (!util.isFunction(func)) { + return; + } + if (functionsHaveNames) { + return func.name; + } + var str = func.toString(); + var match = str.match(regex); + return match && match[1]; +} +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = getName(stackStartFunction); + var idx = out.indexOf('\n' + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf('\n', idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function truncate(s, n) { + if (typeof s === 'string') { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} +function inspect(something) { + if (functionsHaveNames || !util.isFunction(something)) { + return util.inspect(something); + } + var rawname = getName(something); + var name = rawname ? ': ' + rawname : ''; + return '[Function' + name + ']'; +} +function getMessage(self) { + return truncate(inspect(self.actual), 128) + ' ' + + self.operator + ' ' + + truncate(inspect(self.expected), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); + } +}; + +function _deepEqual(actual, expected, strict, memos) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if (isBuffer(actual) && isBuffer(expected)) { + return compare(actual, expected) === 0; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if ((actual === null || typeof actual !== 'object') && + (expected === null || typeof expected !== 'object')) { + return strict ? actual === expected : actual == expected; + + // If both values are instances of typed arrays, wrap their underlying + // ArrayBuffers in a Buffer each to increase performance + // This optimization requires the arrays to have the same type as checked by + // Object.prototype.toString (aka pToString). Never perform binary + // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their + // bit patterns are not identical. + } else if (isView(actual) && isView(expected) && + pToString(actual) === pToString(expected) && + !(actual instanceof Float32Array || + actual instanceof Float64Array)) { + return compare(new Uint8Array(actual.buffer), + new Uint8Array(expected.buffer)) === 0; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else if (isBuffer(actual) !== isBuffer(expected)) { + return false; + } else { + memos = memos || {actual: [], expected: []}; + + var actualIndex = memos.actual.indexOf(actual); + if (actualIndex !== -1) { + if (actualIndex === memos.expected.indexOf(expected)) { + return true; + } + } + + memos.actual.push(actual); + memos.expected.push(expected); + + return objEquiv(actual, expected, strict, memos); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b, strict, actualVisitedObjects) { + if (a === null || a === undefined || b === null || b === undefined) + return false; + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) + return a === b; + if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) + return false; + var aIsArgs = isArguments(a); + var bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b, strict); + } + var ka = objectKeys(a); + var kb = objectKeys(b); + var key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length !== kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] !== kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected, false)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +assert.notDeepStrictEqual = notDeepStrictEqual; +function notDeepStrictEqual(actual, expected, message) { + if (_deepEqual(actual, expected, true)) { + fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); + } +} + + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } + + try { + if (actual instanceof expected) { + return true; + } + } catch (e) { + // Ignore. The instanceof check doesn't work for arrow functions. + } + + if (Error.isPrototypeOf(expected)) { + return false; + } + + return expected.call({}, actual) === true; +} + +function _tryBlock(block) { + var error; + try { + block(); + } catch (e) { + error = e; + } + return error; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (typeof block !== 'function') { + throw new TypeError('"block" argument must be a function'); + } + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + actual = _tryBlock(block); + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + var userProvidedMessage = typeof message === 'string'; + var isUnwantedException = !shouldThrow && util.isError(actual); + var isUnexpectedException = !shouldThrow && actual && !expected; + + if ((isUnwantedException && + userProvidedMessage && + expectedException(actual, expected)) || + isUnexpectedException) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws(true, block, error, message); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws(false, block, error, message); +}; + +assert.ifError = function(err) { if (err) throw err; }; + +// Expose a strict only variant of assert +function strict(value, message) { + if (!value) fail(value, true, message, '==', strict); +} +assert.strict = objectAssign(strict, assert, { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + notEqual: assert.notStrictEqual, + notDeepEqual: assert.notDeepStrictEqual +}); +assert.strict.strict = assert.strict; + +var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) keys.push(key); + } + return keys; +}; + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"object-assign":71,"util/":24}],22:[function(_dereq_,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],23:[function(_dereq_,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],24:[function(_dereq_,module,exports){ +(function (process,global){(function (){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = _dereq_('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = _dereq_('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this)}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":23,"_process":96,"inherits":22}],25:[function(_dereq_,module,exports){ +'use strict' + +exports.byteLength = byteLength +exports.toByteArray = toByteArray +exports.fromByteArray = fromByteArray + +var lookup = [] +var revLookup = [] +var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + +var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i +} + +// Support decoding URL-safe base64 strings, as Node.js does. +// See: https://en.wikipedia.org/wiki/Base64#URL_applications +revLookup['-'.charCodeAt(0)] = 62 +revLookup['_'.charCodeAt(0)] = 63 + +function getLens (b64) { + var len = b64.length + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // Trim off extra bytes after placeholder bytes are found + // See: https://github.com/beatgammit/base64-js/issues/42 + var validLen = b64.indexOf('=') + if (validLen === -1) validLen = len + + var placeHoldersLen = validLen === len + ? 0 + : 4 - (validLen % 4) + + return [validLen, placeHoldersLen] +} + +// base64 is 4/3 + up to two characters of the original data +function byteLength (b64) { + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function _byteLength (b64, validLen, placeHoldersLen) { + return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen +} + +function toByteArray (b64) { + var tmp + var lens = getLens(b64) + var validLen = lens[0] + var placeHoldersLen = lens[1] + + var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) + + var curByte = 0 + + // if there are placeholders, only get up to the last complete 4 chars + var len = placeHoldersLen > 0 + ? validLen - 4 + : validLen + + var i + for (i = 0; i < len; i += 4) { + tmp = + (revLookup[b64.charCodeAt(i)] << 18) | + (revLookup[b64.charCodeAt(i + 1)] << 12) | + (revLookup[b64.charCodeAt(i + 2)] << 6) | + revLookup[b64.charCodeAt(i + 3)] + arr[curByte++] = (tmp >> 16) & 0xFF + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 2) { + tmp = + (revLookup[b64.charCodeAt(i)] << 2) | + (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[curByte++] = tmp & 0xFF + } + + if (placeHoldersLen === 1) { + tmp = + (revLookup[b64.charCodeAt(i)] << 10) | + (revLookup[b64.charCodeAt(i + 1)] << 4) | + (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[curByte++] = (tmp >> 8) & 0xFF + arr[curByte++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + + lookup[num >> 12 & 0x3F] + + lookup[num >> 6 & 0x3F] + + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk( + uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength) + )) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + parts.push( + lookup[tmp >> 2] + + lookup[(tmp << 4) & 0x3F] + + '==' + ) + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1] + parts.push( + lookup[tmp >> 10] + + lookup[(tmp >> 4) & 0x3F] + + lookup[(tmp << 2) & 0x3F] + + '=' + ) + } + + return parts.join('') +} + +},{}],26:[function(_dereq_,module,exports){ + +},{}],27:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +var R = typeof Reflect === 'object' ? Reflect : null +var ReflectApply = R && typeof R.apply === 'function' + ? R.apply + : function ReflectApply(target, receiver, args) { + return Function.prototype.apply.call(target, receiver, args); + } + +var ReflectOwnKeys +if (R && typeof R.ownKeys === 'function') { + ReflectOwnKeys = R.ownKeys +} else if (Object.getOwnPropertySymbols) { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target) + .concat(Object.getOwnPropertySymbols(target)); + }; +} else { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target); + }; +} + +function ProcessEmitWarning(warning) { + if (console && console.warn) console.warn(warning); +} + +var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { + return value !== value; +} + +function EventEmitter() { + EventEmitter.init.call(this); +} +module.exports = EventEmitter; +module.exports.once = once; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +var defaultMaxListeners = 10; + +function checkListener(listener) { + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } +} + +Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { + throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); + } + defaultMaxListeners = arg; + } +}); + +EventEmitter.init = function() { + + if (this._events === undefined || + this._events === Object.getPrototypeOf(this)._events) { + this._events = Object.create(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +}; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { + if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { + throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); + } + this._maxListeners = n; + return this; +}; + +function _getMaxListeners(that) { + if (that._maxListeners === undefined) + return EventEmitter.defaultMaxListeners; + return that._maxListeners; +} + +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return _getMaxListeners(this); +}; + +EventEmitter.prototype.emit = function emit(type) { + var args = []; + for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); + var doError = (type === 'error'); + + var events = this._events; + if (events !== undefined) + doError = (doError && events.error === undefined); + else if (!doError) + return false; + + // If there is no 'error' event listener then throw. + if (doError) { + var er; + if (args.length > 0) + er = args[0]; + if (er instanceof Error) { + // Note: The comments on the `throw` lines are intentional, they show + // up in Node's output if this results in an unhandled exception. + throw er; // Unhandled 'error' event + } + // At least give some kind of context to the user + var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); + err.context = er; + throw err; // Unhandled 'error' event + } + + var handler = events[type]; + + if (handler === undefined) + return false; + + if (typeof handler === 'function') { + ReflectApply(handler, this, args); + } else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + ReflectApply(listeners[i], this, args); + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + var m; + var events; + var existing; + + checkListener(listener); + + events = target._events; + if (events === undefined) { + events = target._events = Object.create(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener !== undefined) { + target.emit('newListener', type, + listener.listener ? listener.listener : listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (existing === undefined) { + // Optimize the case of one listener. Don't need the extra array object. + existing = events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === 'function') { + // Adding the second element, need to change to array. + existing = events[type] = + prepend ? [listener, existing] : [existing, listener]; + // If we've already got an array, just append. + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + + // Check for listener leak + m = _getMaxListeners(target); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + var w = new Error('Possible EventEmitter memory leak detected. ' + + existing.length + ' ' + String(type) + ' listeners ' + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = existing.length; + ProcessEmitWarning(w); + } + } + + return target; +} + +EventEmitter.prototype.addListener = function addListener(type, listener) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.prependListener = + function prependListener(type, listener) { + return _addListener(this, type, listener, true); + }; + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + if (arguments.length === 0) + return this.listener.call(this.target); + return this.listener.apply(this.target, arguments); + } +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; + var wrapped = onceWrapper.bind(state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type, listener) { + checkListener(listener); + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = + function prependOnceListener(type, listener) { + checkListener(listener); + this.prependListener(type, _onceWrap(this, type, listener)); + return this; + }; + +// Emits a 'removeListener' event if and only if the listener was removed. +EventEmitter.prototype.removeListener = + function removeListener(type, listener) { + var list, events, position, i, originalListener; + + checkListener(listener); + + events = this._events; + if (events === undefined) + return this; + + list = events[type]; + if (list === undefined) + return this; + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) + this._events = Object.create(null); + else { + delete events[type]; + if (events.removeListener) + this.emit('removeListener', type, list.listener || listener); + } + } else if (typeof list !== 'function') { + position = -1; + + for (i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + originalListener = list[i].listener; + position = i; + break; + } + } + + if (position < 0) + return this; + + if (position === 0) + list.shift(); + else { + spliceOne(list, position); + } + + if (list.length === 1) + events[type] = list[0]; + + if (events.removeListener !== undefined) + this.emit('removeListener', type, originalListener || listener); + } + + return this; + }; + +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + +EventEmitter.prototype.removeAllListeners = + function removeAllListeners(type) { + var listeners, events, i; + + events = this._events; + if (events === undefined) + return this; + + // not listening for removeListener, no need to emit + if (events.removeListener === undefined) { + if (arguments.length === 0) { + this._events = Object.create(null); + this._eventsCount = 0; + } else if (events[type] !== undefined) { + if (--this._eventsCount === 0) + this._events = Object.create(null); + else + delete events[type]; + } + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + var keys = Object.keys(events); + var key; + for (i = 0; i < keys.length; ++i) { + key = keys[i]; + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = Object.create(null); + this._eventsCount = 0; + return this; + } + + listeners = events[type]; + + if (typeof listeners === 'function') { + this.removeListener(type, listeners); + } else if (listeners !== undefined) { + // LIFO order + for (i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; + }; + +function _listeners(target, type, unwrap) { + var events = target._events; + + if (events === undefined) + return []; + + var evlistener = events[type]; + if (evlistener === undefined) + return []; + + if (typeof evlistener === 'function') + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + + return unwrap ? + unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); +} + +EventEmitter.prototype.listeners = function listeners(type) { + return _listeners(this, type, true); +}; + +EventEmitter.prototype.rawListeners = function rawListeners(type) { + return _listeners(this, type, false); +}; + +EventEmitter.listenerCount = function(emitter, type) { + if (typeof emitter.listenerCount === 'function') { + return emitter.listenerCount(type); + } else { + return listenerCount.call(emitter, type); + } +}; + +EventEmitter.prototype.listenerCount = listenerCount; +function listenerCount(type) { + var events = this._events; + + if (events !== undefined) { + var evlistener = events[type]; + + if (typeof evlistener === 'function') { + return 1; + } else if (evlistener !== undefined) { + return evlistener.length; + } + } + + return 0; +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; +}; + +function arrayClone(arr, n) { + var copy = new Array(n); + for (var i = 0; i < n; ++i) + copy[i] = arr[i]; + return copy; +} + +function spliceOne(list, index) { + for (; index + 1 < list.length; index++) + list[index] = list[index + 1]; + list.pop(); +} + +function unwrapListeners(arr) { + var ret = new Array(arr.length); + for (var i = 0; i < ret.length; ++i) { + ret[i] = arr[i].listener || arr[i]; + } + return ret; +} + +function once(emitter, name) { + return new Promise(function (resolve, reject) { + function eventListener() { + if (errorListener !== undefined) { + emitter.removeListener('error', errorListener); + } + resolve([].slice.call(arguments)); + }; + var errorListener; + + // Adding an error listener is not optional because + // if an error is thrown on an event emitter we cannot + // guarantee that the actual event we are waiting will + // be fired. The result could be a silent way to create + // memory or file descriptor leaks, which is something + // we should avoid. + if (name !== 'error') { + errorListener = function errorListener(err) { + emitter.removeListener(name, eventListener); + reject(err); + }; + + emitter.once('error', errorListener); + } + + emitter.once(name, eventListener); + }); +} + +},{}],28:[function(_dereq_,module,exports){ +(function (Buffer){(function (){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +var base64 = _dereq_('base64-js') +var ieee754 = _dereq_('ieee754') + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +var K_MAX_LENGTH = 0x7fffffff +exports.kMaxLength = K_MAX_LENGTH + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Print warning and recommend using `buffer` v4.x which has an Object + * implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * We report that the browser does not support typed arrays if the are not subclassable + * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` + * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support + * for __proto__ and has a buggy typed array implementation. + */ +Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() + +if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && + typeof console.error === 'function') { + console.error( + 'This browser lacks typed array (Uint8Array) support which is required by ' + + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' + ) +} + +function typedArraySupport () { + // Can typed array instances can be augmented? + try { + var arr = new Uint8Array(1) + arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } + return arr.foo() === 42 + } catch (e) { + return false + } +} + +Object.defineProperty(Buffer.prototype, 'parent', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.buffer + } +}) + +Object.defineProperty(Buffer.prototype, 'offset', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.byteOffset + } +}) + +function createBuffer (length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('The value "' + length + '" is invalid for option "size"') + } + // Return an augmented `Uint8Array` instance + var buf = new Uint8Array(length) + buf.__proto__ = Buffer.prototype + return buf +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer (arg, encodingOrOffset, length) { + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new TypeError( + 'The "string" argument must be of type string. Received type number' + ) + } + return allocUnsafe(arg) + } + return from(arg, encodingOrOffset, length) +} + +// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 +if (typeof Symbol !== 'undefined' && Symbol.species != null && + Buffer[Symbol.species] === Buffer) { + Object.defineProperty(Buffer, Symbol.species, { + value: null, + configurable: true, + enumerable: false, + writable: false + }) +} + +Buffer.poolSize = 8192 // not used by this implementation + +function from (value, encodingOrOffset, length) { + if (typeof value === 'string') { + return fromString(value, encodingOrOffset) + } + + if (ArrayBuffer.isView(value)) { + return fromArrayLike(value) + } + + if (value == null) { + throw TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) + } + + if (isInstance(value, ArrayBuffer) || + (value && isInstance(value.buffer, ArrayBuffer))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof value === 'number') { + throw new TypeError( + 'The "value" argument must not be of type number. Received type number' + ) + } + + var valueOf = value.valueOf && value.valueOf() + if (valueOf != null && valueOf !== value) { + return Buffer.from(valueOf, encodingOrOffset, length) + } + + var b = fromObject(value) + if (b) return b + + if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && + typeof value[Symbol.toPrimitive] === 'function') { + return Buffer.from( + value[Symbol.toPrimitive]('string'), encodingOrOffset, length + ) + } + + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length) +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +Buffer.prototype.__proto__ = Uint8Array.prototype +Buffer.__proto__ = Uint8Array + +function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be of type number') + } else if (size < 0) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } +} + +function alloc (size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpretted as a start offset. + return typeof encoding === 'string' + ? createBuffer(size).fill(fill, encoding) + : createBuffer(size).fill(fill) + } + return createBuffer(size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(size, fill, encoding) +} + +function allocUnsafe (size) { + assertSize(size) + return createBuffer(size < 0 ? 0 : checked(size) | 0) +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(size) +} + +function fromString (string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + + var length = byteLength(string, encoding) | 0 + var buf = createBuffer(length) + + var actual = buf.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual) + } + + return buf +} + +function fromArrayLike (array) { + var length = array.length < 0 ? 0 : checked(array.length) | 0 + var buf = createBuffer(length) + for (var i = 0; i < length; i += 1) { + buf[i] = array[i] & 255 + } + return buf +} + +function fromArrayBuffer (array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds') + } + + var buf + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array) + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset) + } else { + buf = new Uint8Array(array, byteOffset, length) + } + + // Return an augmented `Uint8Array` instance + buf.__proto__ = Buffer.prototype + return buf +} + +function fromObject (obj) { + if (Buffer.isBuffer(obj)) { + var len = checked(obj.length) | 0 + var buf = createBuffer(len) + + if (buf.length === 0) { + return buf + } + + obj.copy(buf, 0, 0, len) + return buf + } + + if (obj.length !== undefined) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return createBuffer(0) + } + return fromArrayLike(obj) + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data) + } +} + +function checked (length) { + // Note: cannot use `length < K_MAX_LENGTH` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= K_MAX_LENGTH) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer (b) { + return b != null && b._isBuffer === true && + b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false +} + +Buffer.compare = function compare (a, b) { + if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) + if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' + ) + } + + if (a === b) return 0 + + var x = a.length + var y = b.length + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat (list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + var i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + var buffer = Buffer.allocUnsafe(length) + var pos = 0 + for (i = 0; i < list.length; ++i) { + var buf = list[i] + if (isInstance(buf, Uint8Array)) { + buf = Buffer.from(buf) + } + if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + buf.copy(buffer, pos) + pos += buf.length + } + return buffer +} + +function byteLength (string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + 'Received type ' + typeof string + ) + } + + var len = string.length + var mustMatch = (arguments.length > 2 && arguments[2] === true) + if (!mustMatch && len === 0) return 0 + + // Use a for loop to avoid recursion + var loweredCase = false + for (;;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 + } + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString (encoding, start, end) { + var loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coersion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) +// to detect a Buffer instance. It's not possible to use `instanceof Buffer` +// reliably in a browserify context because there could be multiple different +// copies of the 'buffer' package in use. This method works even for Buffer +// instances that were created from another copy of the `buffer` package. +// See: https://github.com/feross/buffer/issues/154 +Buffer.prototype._isBuffer = true + +function swap (b, n, m) { + var i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16 () { + var len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (var i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32 () { + var len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (var i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64 () { + var len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (var i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString () { + var length = this.length + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.toLocaleString = Buffer.prototype.toString + +Buffer.prototype.equals = function equals (b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect () { + var str = '' + var max = exports.INSPECT_MAX_BYTES + str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() + if (this.length > max) str += ' ... ' + return '' +} + +Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength) + } + if (!Buffer.isBuffer(target)) { + throw new TypeError( + 'The "target" argument must be one of type Buffer or Uint8Array. ' + + 'Received type ' + (typeof target) + ) + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + var x = thisEnd - thisStart + var y = end - start + var len = Math.min(x, y) + + var thisCopy = this.slice(thisStart, thisEnd) + var targetCopy = target.slice(start, end) + + for (var i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + var indexSize = 1 + var arrLength = arr.length + var valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + var i + if (dir) { + var foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + var found = true + for (var j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + var strLen = string.length + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; ++i) { + var parsed = parseInt(string.substr(i * 2, 2), 16) + if (numberIsNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write (buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite (buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function latin1Write (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write (buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write (string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0 + if (isFinite(length)) { + length = length >>> 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + var remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + var loweredCase = false + for (;;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + return asciiWrite(this, string, offset, length) + + case 'latin1': + case 'binary': + return latin1Write(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; ++i) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) + } + return res +} + +Buffer.prototype.slice = function slice (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + var newBuf = this.subarray(start, end) + // Return an augmented `Uint8Array` instance + newBuf.__proto__ = Buffer.prototype + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset (offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val +} + +Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + var val = this[offset + --byteLength] + var mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val +} + +Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] +} + +Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) +} + +Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] +} + +Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) +} + +Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) +} + +Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var val = this[offset] + var mul = 1 + var i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + var i = byteLength + var mul = 1 + var val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + var val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt (buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var mul = 1 + var i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + var i = byteLength - 1 + var mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = 0 + var mul = 1 + var sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + var limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + var i = byteLength - 1 + var mul = 1 + var sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +function checkIEEE754 (buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy (target, targetStart, start, end) { + if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('Index out of range') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + var len = end - start + + if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { + // Use built-in when available, missing from IE11 + this.copyWithin(targetStart, start, end) + } else if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (var i = len - 1; i >= 0; --i) { + target[i + targetStart] = this[i + start] + } + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, end), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + if (val.length === 1) { + var code = val.charCodeAt(0) + if ((encoding === 'utf8' && code < 128) || + encoding === 'latin1') { + // Fast path: If `val` fits into a single byte, use that numeric value. + val = code + } + } + } else if (typeof val === 'number') { + val = val & 255 + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + var i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + var bytes = Buffer.isBuffer(val) + ? val + : Buffer.from(val, encoding) + var len = bytes.length + if (len === 0) { + throw new TypeError('The value "' + val + + '" is invalid for argument "value"') + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// HELPER FUNCTIONS +// ================ + +var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + +function base64clean (str) { + // Node takes equal signs as end of the Base64 encoding + str = str.split('=')[0] + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (string, units) { + units = units || Infinity + var codePoint + var length = string.length + var leadSurrogate = null + var bytes = [] + + for (var i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str, units) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass +// the `instanceof` check but they should be treated as of that type. +// See: https://github.com/feross/buffer/issues/166 +function isInstance (obj, type) { + return obj instanceof type || + (obj != null && obj.constructor != null && obj.constructor.name != null && + obj.constructor.name === type.name) +} +function numberIsNaN (obj) { + // For IE11 support + return obj !== obj // eslint-disable-line no-self-compare +} + +}).call(this)}).call(this,_dereq_("buffer").Buffer) +},{"base64-js":25,"buffer":28,"ieee754":64}],29:[function(_dereq_,module,exports){ +// https://d3js.org/d3-time-format/ v2.2.3 Copyright 2019 Mike Bostock +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, _dereq_('d3-time')) : +typeof define === 'function' && define.amd ? define(['exports', 'd3-time'], factory) : +(global = global || self, factory(global.d3 = global.d3 || {}, global.d3)); +}(this, function (exports, d3Time) { 'use strict'; + +function localDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); + date.setFullYear(d.y); + return date; + } + return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); +} + +function utcDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); + date.setUTCFullYear(d.y); + return date; + } + return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); +} + +function newDate(y, m, d) { + return {y: y, m: m, d: d, H: 0, M: 0, S: 0, L: 0}; +} + +function formatLocale(locale) { + var locale_dateTime = locale.dateTime, + locale_date = locale.date, + locale_time = locale.time, + locale_periods = locale.periods, + locale_weekdays = locale.days, + locale_shortWeekdays = locale.shortDays, + locale_months = locale.months, + locale_shortMonths = locale.shortMonths; + + var periodRe = formatRe(locale_periods), + periodLookup = formatLookup(locale_periods), + weekdayRe = formatRe(locale_weekdays), + weekdayLookup = formatLookup(locale_weekdays), + shortWeekdayRe = formatRe(locale_shortWeekdays), + shortWeekdayLookup = formatLookup(locale_shortWeekdays), + monthRe = formatRe(locale_months), + monthLookup = formatLookup(locale_months), + shortMonthRe = formatRe(locale_shortMonths), + shortMonthLookup = formatLookup(locale_shortMonths); + + var formats = { + "a": formatShortWeekday, + "A": formatWeekday, + "b": formatShortMonth, + "B": formatMonth, + "c": null, + "d": formatDayOfMonth, + "e": formatDayOfMonth, + "f": formatMicroseconds, + "H": formatHour24, + "I": formatHour12, + "j": formatDayOfYear, + "L": formatMilliseconds, + "m": formatMonthNumber, + "M": formatMinutes, + "p": formatPeriod, + "q": formatQuarter, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatSeconds, + "u": formatWeekdayNumberMonday, + "U": formatWeekNumberSunday, + "V": formatWeekNumberISO, + "w": formatWeekdayNumberSunday, + "W": formatWeekNumberMonday, + "x": null, + "X": null, + "y": formatYear, + "Y": formatFullYear, + "Z": formatZone, + "%": formatLiteralPercent + }; + + var utcFormats = { + "a": formatUTCShortWeekday, + "A": formatUTCWeekday, + "b": formatUTCShortMonth, + "B": formatUTCMonth, + "c": null, + "d": formatUTCDayOfMonth, + "e": formatUTCDayOfMonth, + "f": formatUTCMicroseconds, + "H": formatUTCHour24, + "I": formatUTCHour12, + "j": formatUTCDayOfYear, + "L": formatUTCMilliseconds, + "m": formatUTCMonthNumber, + "M": formatUTCMinutes, + "p": formatUTCPeriod, + "q": formatUTCQuarter, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatUTCSeconds, + "u": formatUTCWeekdayNumberMonday, + "U": formatUTCWeekNumberSunday, + "V": formatUTCWeekNumberISO, + "w": formatUTCWeekdayNumberSunday, + "W": formatUTCWeekNumberMonday, + "x": null, + "X": null, + "y": formatUTCYear, + "Y": formatUTCFullYear, + "Z": formatUTCZone, + "%": formatLiteralPercent + }; + + var parses = { + "a": parseShortWeekday, + "A": parseWeekday, + "b": parseShortMonth, + "B": parseMonth, + "c": parseLocaleDateTime, + "d": parseDayOfMonth, + "e": parseDayOfMonth, + "f": parseMicroseconds, + "H": parseHour24, + "I": parseHour24, + "j": parseDayOfYear, + "L": parseMilliseconds, + "m": parseMonthNumber, + "M": parseMinutes, + "p": parsePeriod, + "q": parseQuarter, + "Q": parseUnixTimestamp, + "s": parseUnixTimestampSeconds, + "S": parseSeconds, + "u": parseWeekdayNumberMonday, + "U": parseWeekNumberSunday, + "V": parseWeekNumberISO, + "w": parseWeekdayNumberSunday, + "W": parseWeekNumberMonday, + "x": parseLocaleDate, + "X": parseLocaleTime, + "y": parseYear, + "Y": parseFullYear, + "Z": parseZone, + "%": parseLiteralPercent + }; + + // These recursive directive definitions must be deferred. + formats.x = newFormat(locale_date, formats); + formats.X = newFormat(locale_time, formats); + formats.c = newFormat(locale_dateTime, formats); + utcFormats.x = newFormat(locale_date, utcFormats); + utcFormats.X = newFormat(locale_time, utcFormats); + utcFormats.c = newFormat(locale_dateTime, utcFormats); + + function newFormat(specifier, formats) { + return function(date) { + var string = [], + i = -1, + j = 0, + n = specifier.length, + c, + pad, + format; + + if (!(date instanceof Date)) date = new Date(+date); + + while (++i < n) { + if (specifier.charCodeAt(i) === 37) { + string.push(specifier.slice(j, i)); + if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); + else pad = c === "e" ? " " : "0"; + if (format = formats[c]) c = format(date, pad); + string.push(c); + j = i + 1; + } + } + + string.push(specifier.slice(j, i)); + return string.join(""); + }; + } + + function newParse(specifier, Z) { + return function(string) { + var d = newDate(1900, undefined, 1), + i = parseSpecifier(d, specifier, string += "", 0), + week, day; + if (i != string.length) return null; + + // If a UNIX timestamp is specified, return it. + if ("Q" in d) return new Date(d.Q); + if ("s" in d) return new Date(d.s * 1000 + ("L" in d ? d.L : 0)); + + // If this is utcParse, never use the local timezone. + if (Z && !("Z" in d)) d.Z = 0; + + // The am-pm flag is 0 for AM, and 1 for PM. + if ("p" in d) d.H = d.H % 12 + d.p * 12; + + // If the month was not specified, inherit from the quarter. + if (d.m === undefined) d.m = "q" in d ? d.q : 0; + + // Convert day-of-week and week-of-year to day-of-year. + if ("V" in d) { + if (d.V < 1 || d.V > 53) return null; + if (!("w" in d)) d.w = 1; + if ("Z" in d) { + week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay(); + week = day > 4 || day === 0 ? d3Time.utcMonday.ceil(week) : d3Time.utcMonday(week); + week = d3Time.utcDay.offset(week, (d.V - 1) * 7); + d.y = week.getUTCFullYear(); + d.m = week.getUTCMonth(); + d.d = week.getUTCDate() + (d.w + 6) % 7; + } else { + week = localDate(newDate(d.y, 0, 1)), day = week.getDay(); + week = day > 4 || day === 0 ? d3Time.timeMonday.ceil(week) : d3Time.timeMonday(week); + week = d3Time.timeDay.offset(week, (d.V - 1) * 7); + d.y = week.getFullYear(); + d.m = week.getMonth(); + d.d = week.getDate() + (d.w + 6) % 7; + } + } else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; + day = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay(); + d.m = 0; + d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7; + } + + // If a time zone is specified, all fields are interpreted as UTC and then + // offset according to the specified time zone. + if ("Z" in d) { + d.H += d.Z / 100 | 0; + d.M += d.Z % 100; + return utcDate(d); + } + + // Otherwise, all fields are in local time. + return localDate(d); + }; + } + + function parseSpecifier(d, specifier, string, j) { + var i = 0, + n = specifier.length, + m = string.length, + c, + parse; + + while (i < n) { + if (j >= m) return -1; + c = specifier.charCodeAt(i++); + if (c === 37) { + c = specifier.charAt(i++); + parse = parses[c in pads ? specifier.charAt(i++) : c]; + if (!parse || ((j = parse(d, string, j)) < 0)) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + + return j; + } + + function parsePeriod(d, string, i) { + var n = periodRe.exec(string.slice(i)); + return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseShortWeekday(d, string, i) { + var n = shortWeekdayRe.exec(string.slice(i)); + return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseWeekday(d, string, i) { + var n = weekdayRe.exec(string.slice(i)); + return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseShortMonth(d, string, i) { + var n = shortMonthRe.exec(string.slice(i)); + return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseMonth(d, string, i) { + var n = monthRe.exec(string.slice(i)); + return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseLocaleDateTime(d, string, i) { + return parseSpecifier(d, locale_dateTime, string, i); + } + + function parseLocaleDate(d, string, i) { + return parseSpecifier(d, locale_date, string, i); + } + + function parseLocaleTime(d, string, i) { + return parseSpecifier(d, locale_time, string, i); + } + + function formatShortWeekday(d) { + return locale_shortWeekdays[d.getDay()]; + } + + function formatWeekday(d) { + return locale_weekdays[d.getDay()]; + } + + function formatShortMonth(d) { + return locale_shortMonths[d.getMonth()]; + } + + function formatMonth(d) { + return locale_months[d.getMonth()]; + } + + function formatPeriod(d) { + return locale_periods[+(d.getHours() >= 12)]; + } + + function formatQuarter(d) { + return 1 + ~~(d.getMonth() / 3); + } + + function formatUTCShortWeekday(d) { + return locale_shortWeekdays[d.getUTCDay()]; + } + + function formatUTCWeekday(d) { + return locale_weekdays[d.getUTCDay()]; + } + + function formatUTCShortMonth(d) { + return locale_shortMonths[d.getUTCMonth()]; + } + + function formatUTCMonth(d) { + return locale_months[d.getUTCMonth()]; + } + + function formatUTCPeriod(d) { + return locale_periods[+(d.getUTCHours() >= 12)]; + } + + function formatUTCQuarter(d) { + return 1 + ~~(d.getUTCMonth() / 3); + } + + return { + format: function(specifier) { + var f = newFormat(specifier += "", formats); + f.toString = function() { return specifier; }; + return f; + }, + parse: function(specifier) { + var p = newParse(specifier += "", false); + p.toString = function() { return specifier; }; + return p; + }, + utcFormat: function(specifier) { + var f = newFormat(specifier += "", utcFormats); + f.toString = function() { return specifier; }; + return f; + }, + utcParse: function(specifier) { + var p = newParse(specifier += "", true); + p.toString = function() { return specifier; }; + return p; + } + }; +} + +var pads = {"-": "", "_": " ", "0": "0"}, + numberRe = /^\s*\d+/, // note: ignores next directive + percentRe = /^%/, + requoteRe = /[\\^$*+?|[\]().{}]/g; + +function pad(value, fill, width) { + var sign = value < 0 ? "-" : "", + string = (sign ? -value : value) + "", + length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); +} + +function requote(s) { + return s.replace(requoteRe, "\\$&"); +} + +function formatRe(names) { + return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); +} + +function formatLookup(names) { + var map = {}, i = -1, n = names.length; + while (++i < n) map[names[i].toLowerCase()] = i; + return map; +} + +function parseWeekdayNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.w = +n[0], i + n[0].length) : -1; +} + +function parseWeekdayNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.u = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.U = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberISO(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.V = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.W = +n[0], i + n[0].length) : -1; +} + +function parseFullYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 4)); + return n ? (d.y = +n[0], i + n[0].length) : -1; +} + +function parseYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1; +} + +function parseZone(d, string, i) { + var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); + return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; +} + +function parseQuarter(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1; +} + +function parseMonthNumber(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.m = n[0] - 1, i + n[0].length) : -1; +} + +function parseDayOfMonth(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.d = +n[0], i + n[0].length) : -1; +} + +function parseDayOfYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; +} + +function parseHour24(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.H = +n[0], i + n[0].length) : -1; +} + +function parseMinutes(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.M = +n[0], i + n[0].length) : -1; +} + +function parseSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.S = +n[0], i + n[0].length) : -1; +} + +function parseMilliseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.L = +n[0], i + n[0].length) : -1; +} + +function parseMicroseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 6)); + return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1; +} + +function parseLiteralPercent(d, string, i) { + var n = percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; +} + +function parseUnixTimestamp(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.Q = +n[0], i + n[0].length) : -1; +} + +function parseUnixTimestampSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.s = +n[0], i + n[0].length) : -1; +} + +function formatDayOfMonth(d, p) { + return pad(d.getDate(), p, 2); +} + +function formatHour24(d, p) { + return pad(d.getHours(), p, 2); +} + +function formatHour12(d, p) { + return pad(d.getHours() % 12 || 12, p, 2); +} + +function formatDayOfYear(d, p) { + return pad(1 + d3Time.timeDay.count(d3Time.timeYear(d), d), p, 3); +} + +function formatMilliseconds(d, p) { + return pad(d.getMilliseconds(), p, 3); +} + +function formatMicroseconds(d, p) { + return formatMilliseconds(d, p) + "000"; +} + +function formatMonthNumber(d, p) { + return pad(d.getMonth() + 1, p, 2); +} + +function formatMinutes(d, p) { + return pad(d.getMinutes(), p, 2); +} + +function formatSeconds(d, p) { + return pad(d.getSeconds(), p, 2); +} + +function formatWeekdayNumberMonday(d) { + var day = d.getDay(); + return day === 0 ? 7 : day; +} + +function formatWeekNumberSunday(d, p) { + return pad(d3Time.timeSunday.count(d3Time.timeYear(d) - 1, d), p, 2); +} + +function formatWeekNumberISO(d, p) { + var day = d.getDay(); + d = (day >= 4 || day === 0) ? d3Time.timeThursday(d) : d3Time.timeThursday.ceil(d); + return pad(d3Time.timeThursday.count(d3Time.timeYear(d), d) + (d3Time.timeYear(d).getDay() === 4), p, 2); +} + +function formatWeekdayNumberSunday(d) { + return d.getDay(); +} + +function formatWeekNumberMonday(d, p) { + return pad(d3Time.timeMonday.count(d3Time.timeYear(d) - 1, d), p, 2); +} + +function formatYear(d, p) { + return pad(d.getFullYear() % 100, p, 2); +} + +function formatFullYear(d, p) { + return pad(d.getFullYear() % 10000, p, 4); +} + +function formatZone(d) { + var z = d.getTimezoneOffset(); + return (z > 0 ? "-" : (z *= -1, "+")) + + pad(z / 60 | 0, "0", 2) + + pad(z % 60, "0", 2); +} + +function formatUTCDayOfMonth(d, p) { + return pad(d.getUTCDate(), p, 2); +} + +function formatUTCHour24(d, p) { + return pad(d.getUTCHours(), p, 2); +} + +function formatUTCHour12(d, p) { + return pad(d.getUTCHours() % 12 || 12, p, 2); +} + +function formatUTCDayOfYear(d, p) { + return pad(1 + d3Time.utcDay.count(d3Time.utcYear(d), d), p, 3); +} + +function formatUTCMilliseconds(d, p) { + return pad(d.getUTCMilliseconds(), p, 3); +} + +function formatUTCMicroseconds(d, p) { + return formatUTCMilliseconds(d, p) + "000"; +} + +function formatUTCMonthNumber(d, p) { + return pad(d.getUTCMonth() + 1, p, 2); +} + +function formatUTCMinutes(d, p) { + return pad(d.getUTCMinutes(), p, 2); +} + +function formatUTCSeconds(d, p) { + return pad(d.getUTCSeconds(), p, 2); +} + +function formatUTCWeekdayNumberMonday(d) { + var dow = d.getUTCDay(); + return dow === 0 ? 7 : dow; +} + +function formatUTCWeekNumberSunday(d, p) { + return pad(d3Time.utcSunday.count(d3Time.utcYear(d) - 1, d), p, 2); +} + +function formatUTCWeekNumberISO(d, p) { + var day = d.getUTCDay(); + d = (day >= 4 || day === 0) ? d3Time.utcThursday(d) : d3Time.utcThursday.ceil(d); + return pad(d3Time.utcThursday.count(d3Time.utcYear(d), d) + (d3Time.utcYear(d).getUTCDay() === 4), p, 2); +} + +function formatUTCWeekdayNumberSunday(d) { + return d.getUTCDay(); +} + +function formatUTCWeekNumberMonday(d, p) { + return pad(d3Time.utcMonday.count(d3Time.utcYear(d) - 1, d), p, 2); +} + +function formatUTCYear(d, p) { + return pad(d.getUTCFullYear() % 100, p, 2); +} + +function formatUTCFullYear(d, p) { + return pad(d.getUTCFullYear() % 10000, p, 4); +} + +function formatUTCZone() { + return "+0000"; +} + +function formatLiteralPercent() { + return "%"; +} + +function formatUnixTimestamp(d) { + return +d; +} + +function formatUnixTimestampSeconds(d) { + return Math.floor(+d / 1000); +} + +var locale; + +defaultLocale({ + dateTime: "%x, %X", + date: "%-m/%-d/%Y", + time: "%-I:%M:%S %p", + periods: ["AM", "PM"], + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] +}); + +function defaultLocale(definition) { + locale = formatLocale(definition); + exports.timeFormat = locale.format; + exports.timeParse = locale.parse; + exports.utcFormat = locale.utcFormat; + exports.utcParse = locale.utcParse; + return locale; +} + +var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ"; + +function formatIsoNative(date) { + return date.toISOString(); +} + +var formatIso = Date.prototype.toISOString + ? formatIsoNative + : exports.utcFormat(isoSpecifier); + +function parseIsoNative(string) { + var date = new Date(string); + return isNaN(date) ? null : date; +} + +var parseIso = +new Date("2000-01-01T00:00:00.000Z") + ? parseIsoNative + : exports.utcParse(isoSpecifier); + +exports.isoFormat = formatIso; +exports.isoParse = parseIso; +exports.timeFormatDefaultLocale = defaultLocale; +exports.timeFormatLocale = formatLocale; + +Object.defineProperty(exports, '__esModule', { value: true }); + +})); + +},{"d3-time":30}],30:[function(_dereq_,module,exports){ +// https://d3js.org/d3-time/ v1.1.0 Copyright 2019 Mike Bostock +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : +typeof define === 'function' && define.amd ? define(['exports'], factory) : +(global = global || self, factory(global.d3 = global.d3 || {})); +}(this, function (exports) { 'use strict'; + +var t0 = new Date, + t1 = new Date; + +function newInterval(floori, offseti, count, field) { + + function interval(date) { + return floori(date = arguments.length === 0 ? new Date : new Date(+date)), date; + } + + interval.floor = function(date) { + return floori(date = new Date(+date)), date; + }; + + interval.ceil = function(date) { + return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; + }; + + interval.round = function(date) { + var d0 = interval(date), + d1 = interval.ceil(date); + return date - d0 < d1 - date ? d0 : d1; + }; + + interval.offset = function(date, step) { + return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date; + }; + + interval.range = function(start, stop, step) { + var range = [], previous; + start = interval.ceil(start); + step = step == null ? 1 : Math.floor(step); + if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date + do range.push(previous = new Date(+start)), offseti(start, step), floori(start); + while (previous < start && start < stop); + return range; + }; + + interval.filter = function(test) { + return newInterval(function(date) { + if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); + }, function(date, step) { + if (date >= date) { + if (step < 0) while (++step <= 0) { + while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty + } else while (--step >= 0) { + while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty + } + } + }); + }; + + if (count) { + interval.count = function(start, end) { + t0.setTime(+start), t1.setTime(+end); + floori(t0), floori(t1); + return Math.floor(count(t0, t1)); + }; + + interval.every = function(step) { + step = Math.floor(step); + return !isFinite(step) || !(step > 0) ? null + : !(step > 1) ? interval + : interval.filter(field + ? function(d) { return field(d) % step === 0; } + : function(d) { return interval.count(0, d) % step === 0; }); + }; + } + + return interval; +} + +var millisecond = newInterval(function() { + // noop +}, function(date, step) { + date.setTime(+date + step); +}, function(start, end) { + return end - start; +}); + +// An optimized implementation for this simple case. +millisecond.every = function(k) { + k = Math.floor(k); + if (!isFinite(k) || !(k > 0)) return null; + if (!(k > 1)) return millisecond; + return newInterval(function(date) { + date.setTime(Math.floor(date / k) * k); + }, function(date, step) { + date.setTime(+date + step * k); + }, function(start, end) { + return (end - start) / k; + }); +}; +var milliseconds = millisecond.range; + +var durationSecond = 1e3; +var durationMinute = 6e4; +var durationHour = 36e5; +var durationDay = 864e5; +var durationWeek = 6048e5; + +var second = newInterval(function(date) { + date.setTime(date - date.getMilliseconds()); +}, function(date, step) { + date.setTime(+date + step * durationSecond); +}, function(start, end) { + return (end - start) / durationSecond; +}, function(date) { + return date.getUTCSeconds(); +}); +var seconds = second.range; + +var minute = newInterval(function(date) { + date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond); +}, function(date, step) { + date.setTime(+date + step * durationMinute); +}, function(start, end) { + return (end - start) / durationMinute; +}, function(date) { + return date.getMinutes(); +}); +var minutes = minute.range; + +var hour = newInterval(function(date) { + date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond - date.getMinutes() * durationMinute); +}, function(date, step) { + date.setTime(+date + step * durationHour); +}, function(start, end) { + return (end - start) / durationHour; +}, function(date) { + return date.getHours(); +}); +var hours = hour.range; + +var day = newInterval(function(date) { + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setDate(date.getDate() + step); +}, function(start, end) { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay; +}, function(date) { + return date.getDate() - 1; +}); +var days = day.range; + +function weekday(i) { + return newInterval(function(date) { + date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); + date.setHours(0, 0, 0, 0); + }, function(date, step) { + date.setDate(date.getDate() + step * 7); + }, function(start, end) { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; + }); +} + +var sunday = weekday(0); +var monday = weekday(1); +var tuesday = weekday(2); +var wednesday = weekday(3); +var thursday = weekday(4); +var friday = weekday(5); +var saturday = weekday(6); + +var sundays = sunday.range; +var mondays = monday.range; +var tuesdays = tuesday.range; +var wednesdays = wednesday.range; +var thursdays = thursday.range; +var fridays = friday.range; +var saturdays = saturday.range; + +var month = newInterval(function(date) { + date.setDate(1); + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setMonth(date.getMonth() + step); +}, function(start, end) { + return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; +}, function(date) { + return date.getMonth(); +}); +var months = month.range; + +var year = newInterval(function(date) { + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setFullYear(date.getFullYear() + step); +}, function(start, end) { + return end.getFullYear() - start.getFullYear(); +}, function(date) { + return date.getFullYear(); +}); + +// An optimized implementation for this simple case. +year.every = function(k) { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { + date.setFullYear(Math.floor(date.getFullYear() / k) * k); + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); + }, function(date, step) { + date.setFullYear(date.getFullYear() + step * k); + }); +}; +var years = year.range; + +var utcMinute = newInterval(function(date) { + date.setUTCSeconds(0, 0); +}, function(date, step) { + date.setTime(+date + step * durationMinute); +}, function(start, end) { + return (end - start) / durationMinute; +}, function(date) { + return date.getUTCMinutes(); +}); +var utcMinutes = utcMinute.range; + +var utcHour = newInterval(function(date) { + date.setUTCMinutes(0, 0, 0); +}, function(date, step) { + date.setTime(+date + step * durationHour); +}, function(start, end) { + return (end - start) / durationHour; +}, function(date) { + return date.getUTCHours(); +}); +var utcHours = utcHour.range; + +var utcDay = newInterval(function(date) { + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCDate(date.getUTCDate() + step); +}, function(start, end) { + return (end - start) / durationDay; +}, function(date) { + return date.getUTCDate() - 1; +}); +var utcDays = utcDay.range; + +function utcWeekday(i) { + return newInterval(function(date) { + date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); + date.setUTCHours(0, 0, 0, 0); + }, function(date, step) { + date.setUTCDate(date.getUTCDate() + step * 7); + }, function(start, end) { + return (end - start) / durationWeek; + }); +} + +var utcSunday = utcWeekday(0); +var utcMonday = utcWeekday(1); +var utcTuesday = utcWeekday(2); +var utcWednesday = utcWeekday(3); +var utcThursday = utcWeekday(4); +var utcFriday = utcWeekday(5); +var utcSaturday = utcWeekday(6); + +var utcSundays = utcSunday.range; +var utcMondays = utcMonday.range; +var utcTuesdays = utcTuesday.range; +var utcWednesdays = utcWednesday.range; +var utcThursdays = utcThursday.range; +var utcFridays = utcFriday.range; +var utcSaturdays = utcSaturday.range; + +var utcMonth = newInterval(function(date) { + date.setUTCDate(1); + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCMonth(date.getUTCMonth() + step); +}, function(start, end) { + return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; +}, function(date) { + return date.getUTCMonth(); +}); +var utcMonths = utcMonth.range; + +var utcYear = newInterval(function(date) { + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCFullYear(date.getUTCFullYear() + step); +}, function(start, end) { + return end.getUTCFullYear() - start.getUTCFullYear(); +}, function(date) { + return date.getUTCFullYear(); +}); + +// An optimized implementation for this simple case. +utcYear.every = function(k) { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { + date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); + }, function(date, step) { + date.setUTCFullYear(date.getUTCFullYear() + step * k); + }); +}; +var utcYears = utcYear.range; + +exports.timeDay = day; +exports.timeDays = days; +exports.timeFriday = friday; +exports.timeFridays = fridays; +exports.timeHour = hour; +exports.timeHours = hours; +exports.timeInterval = newInterval; +exports.timeMillisecond = millisecond; +exports.timeMilliseconds = milliseconds; +exports.timeMinute = minute; +exports.timeMinutes = minutes; +exports.timeMonday = monday; +exports.timeMondays = mondays; +exports.timeMonth = month; +exports.timeMonths = months; +exports.timeSaturday = saturday; +exports.timeSaturdays = saturdays; +exports.timeSecond = second; +exports.timeSeconds = seconds; +exports.timeSunday = sunday; +exports.timeSundays = sundays; +exports.timeThursday = thursday; +exports.timeThursdays = thursdays; +exports.timeTuesday = tuesday; +exports.timeTuesdays = tuesdays; +exports.timeWednesday = wednesday; +exports.timeWednesdays = wednesdays; +exports.timeWeek = sunday; +exports.timeWeeks = sundays; +exports.timeYear = year; +exports.timeYears = years; +exports.utcDay = utcDay; +exports.utcDays = utcDays; +exports.utcFriday = utcFriday; +exports.utcFridays = utcFridays; +exports.utcHour = utcHour; +exports.utcHours = utcHours; +exports.utcMillisecond = millisecond; +exports.utcMilliseconds = milliseconds; +exports.utcMinute = utcMinute; +exports.utcMinutes = utcMinutes; +exports.utcMonday = utcMonday; +exports.utcMondays = utcMondays; +exports.utcMonth = utcMonth; +exports.utcMonths = utcMonths; +exports.utcSaturday = utcSaturday; +exports.utcSaturdays = utcSaturdays; +exports.utcSecond = second; +exports.utcSeconds = seconds; +exports.utcSunday = utcSunday; +exports.utcSundays = utcSundays; +exports.utcThursday = utcThursday; +exports.utcThursdays = utcThursdays; +exports.utcTuesday = utcTuesday; +exports.utcTuesdays = utcTuesdays; +exports.utcWednesday = utcWednesday; +exports.utcWednesdays = utcWednesdays; +exports.utcWeek = utcSunday; +exports.utcWeeks = utcSundays; +exports.utcYear = utcYear; +exports.utcYears = utcYears; + +Object.defineProperty(exports, '__esModule', { value: true }); + +})); + +},{}],31:[function(_dereq_,module,exports){ +/** + * inspired by is-number + * but significantly simplified and sped up by ignoring number and string constructors + * ie these return false: + * new Number(1) + * new String('1') + */ + +'use strict'; + +var allBlankCharCodes = _dereq_('is-string-blank'); + +module.exports = function(n) { + var type = typeof n; + if(type === 'string') { + var original = n; + n = +n; + // whitespace strings cast to zero - filter them out + if(n===0 && allBlankCharCodes(original)) return false; + } + else if(type !== 'number') return false; + + return n - n < 1; +}; + +},{"is-string-blank":68}],32:[function(_dereq_,module,exports){ +module.exports = adjoint; + +/** + * Calculates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function adjoint(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; +}; +},{}],33:[function(_dereq_,module,exports){ +module.exports = clone; + +/** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ +function clone(a) { + var out = new Float32Array(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; +},{}],34:[function(_dereq_,module,exports){ +module.exports = copy; + +/** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function copy(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; +},{}],35:[function(_dereq_,module,exports){ +module.exports = create; + +/** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ +function create() { + var out = new Float32Array(16); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],36:[function(_dereq_,module,exports){ +module.exports = determinant; + +/** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ +function determinant(a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +}; +},{}],37:[function(_dereq_,module,exports){ +module.exports = fromQuat; + +/** + * Creates a matrix from a quaternion rotation. + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @returns {mat4} out + */ +function fromQuat(out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + yx = y * x2, + yy = y * y2, + zx = z * x2, + zy = z * y2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - yy - zz; + out[1] = yx + wz; + out[2] = zx - wy; + out[3] = 0; + + out[4] = yx - wz; + out[5] = 1 - xx - zz; + out[6] = zy + wx; + out[7] = 0; + + out[8] = zx + wy; + out[9] = zy - wx; + out[10] = 1 - xx - yy; + out[11] = 0; + + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + + return out; +}; +},{}],38:[function(_dereq_,module,exports){ +module.exports = fromRotation + +/** + * Creates a matrix from a given angle around a given axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest) + * mat4.rotate(dest, dest, rad, axis) + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function fromRotation(out, rad, axis) { + var s, c, t + var x = axis[0] + var y = axis[1] + var z = axis[2] + var len = Math.sqrt(x * x + y * y + z * z) + + if (Math.abs(len) < 0.000001) { + return null + } + + len = 1 / len + x *= len + y *= len + z *= len + + s = Math.sin(rad) + c = Math.cos(rad) + t = 1 - c + + // Perform rotation-specific matrix multiplication + out[0] = x * x * t + c + out[1] = y * x * t + z * s + out[2] = z * x * t - y * s + out[3] = 0 + out[4] = x * y * t - z * s + out[5] = y * y * t + c + out[6] = z * y * t + x * s + out[7] = 0 + out[8] = x * z * t + y * s + out[9] = y * z * t - x * s + out[10] = z * z * t + c + out[11] = 0 + out[12] = 0 + out[13] = 0 + out[14] = 0 + out[15] = 1 + return out +} + +},{}],39:[function(_dereq_,module,exports){ +module.exports = fromRotationTranslation; + +/** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ +function fromRotationTranslation(out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; +}; +},{}],40:[function(_dereq_,module,exports){ +module.exports = fromScaling + +/** + * Creates a matrix from a vector scaling + * This is equivalent to (but much faster than): + * + * mat4.identity(dest) + * mat4.scale(dest, dest, vec) + * + * @param {mat4} out mat4 receiving operation result + * @param {vec3} v Scaling vector + * @returns {mat4} out + */ +function fromScaling(out, v) { + out[0] = v[0] + out[1] = 0 + out[2] = 0 + out[3] = 0 + out[4] = 0 + out[5] = v[1] + out[6] = 0 + out[7] = 0 + out[8] = 0 + out[9] = 0 + out[10] = v[2] + out[11] = 0 + out[12] = 0 + out[13] = 0 + out[14] = 0 + out[15] = 1 + return out +} + +},{}],41:[function(_dereq_,module,exports){ +module.exports = fromTranslation + +/** + * Creates a matrix from a vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest) + * mat4.translate(dest, dest, vec) + * + * @param {mat4} out mat4 receiving operation result + * @param {vec3} v Translation vector + * @returns {mat4} out + */ +function fromTranslation(out, v) { + out[0] = 1 + out[1] = 0 + out[2] = 0 + out[3] = 0 + out[4] = 0 + out[5] = 1 + out[6] = 0 + out[7] = 0 + out[8] = 0 + out[9] = 0 + out[10] = 1 + out[11] = 0 + out[12] = v[0] + out[13] = v[1] + out[14] = v[2] + out[15] = 1 + return out +} + +},{}],42:[function(_dereq_,module,exports){ +module.exports = fromXRotation + +/** + * Creates a matrix from the given angle around the X axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest) + * mat4.rotateX(dest, dest, rad) + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +function fromXRotation(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad) + + // Perform axis-specific matrix multiplication + out[0] = 1 + out[1] = 0 + out[2] = 0 + out[3] = 0 + out[4] = 0 + out[5] = c + out[6] = s + out[7] = 0 + out[8] = 0 + out[9] = -s + out[10] = c + out[11] = 0 + out[12] = 0 + out[13] = 0 + out[14] = 0 + out[15] = 1 + return out +} +},{}],43:[function(_dereq_,module,exports){ +module.exports = fromYRotation + +/** + * Creates a matrix from the given angle around the Y axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest) + * mat4.rotateY(dest, dest, rad) + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +function fromYRotation(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad) + + // Perform axis-specific matrix multiplication + out[0] = c + out[1] = 0 + out[2] = -s + out[3] = 0 + out[4] = 0 + out[5] = 1 + out[6] = 0 + out[7] = 0 + out[8] = s + out[9] = 0 + out[10] = c + out[11] = 0 + out[12] = 0 + out[13] = 0 + out[14] = 0 + out[15] = 1 + return out +} +},{}],44:[function(_dereq_,module,exports){ +module.exports = fromZRotation + +/** + * Creates a matrix from the given angle around the Z axis + * This is equivalent to (but much faster than): + * + * mat4.identity(dest) + * mat4.rotateZ(dest, dest, rad) + * + * @param {mat4} out mat4 receiving operation result + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +function fromZRotation(out, rad) { + var s = Math.sin(rad), + c = Math.cos(rad) + + // Perform axis-specific matrix multiplication + out[0] = c + out[1] = s + out[2] = 0 + out[3] = 0 + out[4] = -s + out[5] = c + out[6] = 0 + out[7] = 0 + out[8] = 0 + out[9] = 0 + out[10] = 1 + out[11] = 0 + out[12] = 0 + out[13] = 0 + out[14] = 0 + out[15] = 1 + return out +} +},{}],45:[function(_dereq_,module,exports){ +module.exports = frustum; + +/** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ +function frustum(out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; +}; +},{}],46:[function(_dereq_,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],47:[function(_dereq_,module,exports){ +module.exports = { + create: _dereq_('./create') + , clone: _dereq_('./clone') + , copy: _dereq_('./copy') + , identity: _dereq_('./identity') + , transpose: _dereq_('./transpose') + , invert: _dereq_('./invert') + , adjoint: _dereq_('./adjoint') + , determinant: _dereq_('./determinant') + , multiply: _dereq_('./multiply') + , translate: _dereq_('./translate') + , scale: _dereq_('./scale') + , rotate: _dereq_('./rotate') + , rotateX: _dereq_('./rotateX') + , rotateY: _dereq_('./rotateY') + , rotateZ: _dereq_('./rotateZ') + , fromRotation: _dereq_('./fromRotation') + , fromRotationTranslation: _dereq_('./fromRotationTranslation') + , fromScaling: _dereq_('./fromScaling') + , fromTranslation: _dereq_('./fromTranslation') + , fromXRotation: _dereq_('./fromXRotation') + , fromYRotation: _dereq_('./fromYRotation') + , fromZRotation: _dereq_('./fromZRotation') + , fromQuat: _dereq_('./fromQuat') + , frustum: _dereq_('./frustum') + , perspective: _dereq_('./perspective') + , perspectiveFromFieldOfView: _dereq_('./perspectiveFromFieldOfView') + , ortho: _dereq_('./ortho') + , lookAt: _dereq_('./lookAt') + , str: _dereq_('./str') +} + +},{"./adjoint":32,"./clone":33,"./copy":34,"./create":35,"./determinant":36,"./fromQuat":37,"./fromRotation":38,"./fromRotationTranslation":39,"./fromScaling":40,"./fromTranslation":41,"./fromXRotation":42,"./fromYRotation":43,"./fromZRotation":44,"./frustum":45,"./identity":46,"./invert":48,"./lookAt":49,"./multiply":50,"./ortho":51,"./perspective":52,"./perspectiveFromFieldOfView":53,"./rotate":54,"./rotateX":55,"./rotateY":56,"./rotateZ":57,"./scale":58,"./str":59,"./translate":60,"./transpose":61}],48:[function(_dereq_,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],49:[function(_dereq_,module,exports){ +var identity = _dereq_('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":46}],50:[function(_dereq_,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],51:[function(_dereq_,module,exports){ +module.exports = ortho; + +/** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function ortho(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; +}; +},{}],52:[function(_dereq_,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],53:[function(_dereq_,module,exports){ +module.exports = perspectiveFromFieldOfView; + +/** + * Generates a perspective projection matrix with the given field of view. + * This is primarily useful for generating projection matrices to be used + * with the still experiemental WebVR API. + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspectiveFromFieldOfView(out, fov, near, far) { + var upTan = Math.tan(fov.upDegrees * Math.PI/180.0), + downTan = Math.tan(fov.downDegrees * Math.PI/180.0), + leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0), + rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0), + xScale = 2.0 / (leftTan + rightTan), + yScale = 2.0 / (upTan + downTan); + + out[0] = xScale; + out[1] = 0.0; + out[2] = 0.0; + out[3] = 0.0; + out[4] = 0.0; + out[5] = yScale; + out[6] = 0.0; + out[7] = 0.0; + out[8] = -((leftTan - rightTan) * xScale * 0.5); + out[9] = ((upTan - downTan) * yScale * 0.5); + out[10] = far / (near - far); + out[11] = -1.0; + out[12] = 0.0; + out[13] = 0.0; + out[14] = (far * near) / (near - far); + out[15] = 0.0; + return out; +} + + +},{}],54:[function(_dereq_,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],55:[function(_dereq_,module,exports){ +module.exports = rotateX; + +/** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +function rotateX(out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; +}; +},{}],56:[function(_dereq_,module,exports){ +module.exports = rotateY; + +/** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +function rotateY(out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; +}; +},{}],57:[function(_dereq_,module,exports){ +module.exports = rotateZ; + +/** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ +function rotateZ(out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; +}; +},{}],58:[function(_dereq_,module,exports){ +module.exports = scale; + +/** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ +function scale(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +}; +},{}],59:[function(_dereq_,module,exports){ +module.exports = str; + +/** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ +function str(a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; +}; +},{}],60:[function(_dereq_,module,exports){ +module.exports = translate; + +/** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ +function translate(out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; +}; +},{}],61:[function(_dereq_,module,exports){ +module.exports = transpose; + +/** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function transpose(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; +}; +},{}],62:[function(_dereq_,module,exports){ +(function (global){(function (){ +'use strict' + +var isBrowser = _dereq_('is-browser') +var hasHover + +if (typeof global.matchMedia === 'function') { + hasHover = !global.matchMedia('(hover: none)').matches +} +else { + hasHover = isBrowser +} + +module.exports = hasHover + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"is-browser":66}],63:[function(_dereq_,module,exports){ +'use strict' + +var isBrowser = _dereq_('is-browser') + +function detect() { + var supported = false + + try { + var opts = Object.defineProperty({}, 'passive', { + get: function() { + supported = true + } + }) + + window.addEventListener('test', null, opts) + window.removeEventListener('test', null, opts) + } catch(e) { + supported = false + } + + return supported +} + +module.exports = isBrowser && detect() + +},{"is-browser":66}],64:[function(_dereq_,module,exports){ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],65:[function(_dereq_,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }) + } + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } +} + +},{}],66:[function(_dereq_,module,exports){ +module.exports = true; +},{}],67:[function(_dereq_,module,exports){ +'use strict' + +module.exports = isMobile +module.exports.isMobile = isMobile +module.exports.default = isMobile + +var mobileRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i + +var tabletRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|android|ipad|playbook|silk/i + +function isMobile (opts) { + if (!opts) opts = {} + var ua = opts.ua + if (!ua && typeof navigator !== 'undefined') ua = navigator.userAgent + if (ua && ua.headers && typeof ua.headers['user-agent'] === 'string') { + ua = ua.headers['user-agent'] + } + if (typeof ua !== 'string') return false + + var result = opts.tablet ? tabletRE.test(ua) : mobileRE.test(ua) + + if ( + !result && + opts.tablet && + opts.featureDetect && + navigator && + navigator.maxTouchPoints > 1 && + ua.indexOf('Macintosh') !== -1 && + ua.indexOf('Safari') !== -1 + ) { + result = true + } + + return result +} + +},{}],68:[function(_dereq_,module,exports){ +'use strict'; + +/** + * Is this string all whitespace? + * This solution kind of makes my brain hurt, but it's significantly faster + * than !str.trim() or any other solution I could find. + * + * whitespace codes from: http://en.wikipedia.org/wiki/Whitespace_character + * and verified with: + * + * for(var i = 0; i < 65536; i++) { + * var s = String.fromCharCode(i); + * if(+s===0 && !s.trim()) console.log(i, s); + * } + * + * which counts a couple of these as *not* whitespace, but finds nothing else + * that *is* whitespace. Note that charCodeAt stops at 16 bits, but it appears + * that there are no whitespace characters above this, and code points above + * this do not map onto white space characters. + */ + +module.exports = function(str){ + var l = str.length, + a; + for(var i = 0; i < l; i++) { + a = str.charCodeAt(i); + if((a < 9 || a > 13) && (a !== 32) && (a !== 133) && (a !== 160) && + (a !== 5760) && (a !== 6158) && (a < 8192 || a > 8205) && + (a !== 8232) && (a !== 8233) && (a !== 8239) && (a !== 8287) && + (a !== 8288) && (a !== 12288) && (a !== 65279)) { + return false; + } + } + return true; +} + +},{}],69:[function(_dereq_,module,exports){ +var rootPosition = { left: 0, top: 0 } + +module.exports = mouseEventOffset +function mouseEventOffset (ev, target, out) { + target = target || ev.currentTarget || ev.srcElement + if (!Array.isArray(out)) { + out = [ 0, 0 ] + } + var cx = ev.clientX || 0 + var cy = ev.clientY || 0 + var rect = getBoundingClientOffset(target) + out[0] = cx - rect.left + out[1] = cy - rect.top + return out +} + +function getBoundingClientOffset (element) { + if (element === window || + element === document || + element === document.body) { + return rootPosition + } else { + return element.getBoundingClientRect() + } +} + +},{}],70:[function(_dereq_,module,exports){ +(function (global,setImmediate){(function (){ +/*! Native Promise Only + v0.8.1 (c) Kyle Simpson + MIT License: http://getify.mit-license.org +*/ + +(function UMD(name,context,definition){ + // special form of UMD for polyfilling across evironments + context[name] = context[name] || definition(); + if (typeof module != "undefined" && module.exports) { module.exports = context[name]; } + else if (typeof define == "function" && define.amd) { define(function $AMD$(){ return context[name]; }); } +})("Promise",typeof global != "undefined" ? global : this,function DEF(){ + /*jshint validthis:true */ + "use strict"; + + var builtInProp, cycle, scheduling_queue, + ToString = Object.prototype.toString, + timer = (typeof setImmediate != "undefined") ? + function timer(fn) { return setImmediate(fn); } : + setTimeout + ; + + // dammit, IE8. + try { + Object.defineProperty({},"x",{}); + builtInProp = function builtInProp(obj,name,val,config) { + return Object.defineProperty(obj,name,{ + value: val, + writable: true, + configurable: config !== false + }); + }; + } + catch (err) { + builtInProp = function builtInProp(obj,name,val) { + obj[name] = val; + return obj; + }; + } + + // Note: using a queue instead of array for efficiency + scheduling_queue = (function Queue() { + var first, last, item; + + function Item(fn,self) { + this.fn = fn; + this.self = self; + this.next = void 0; + } + + return { + add: function add(fn,self) { + item = new Item(fn,self); + if (last) { + last.next = item; + } + else { + first = item; + } + last = item; + item = void 0; + }, + drain: function drain() { + var f = first; + first = last = cycle = void 0; + + while (f) { + f.fn.call(f.self); + f = f.next; + } + } + }; + })(); + + function schedule(fn,self) { + scheduling_queue.add(fn,self); + if (!cycle) { + cycle = timer(scheduling_queue.drain); + } + } + + // promise duck typing + function isThenable(o) { + var _then, o_type = typeof o; + + if (o != null && + ( + o_type == "object" || o_type == "function" + ) + ) { + _then = o.then; + } + return typeof _then == "function" ? _then : false; + } + + function notify() { + for (var i=0; i 0) { + schedule(notify,self); + } + } + } + catch (err) { + reject.call(new MakeDefWrapper(self),err); + } + } + + function reject(msg) { + var self = this; + + // already triggered? + if (self.triggered) { return; } + + self.triggered = true; + + // unwrap + if (self.def) { + self = self.def; + } + + self.msg = msg; + self.state = 2; + if (self.chain.length > 0) { + schedule(notify,self); + } + } + + function iteratePromises(Constructor,arr,resolver,rejecter) { + for (var idx=0; idx 2) { + data.push([command].concat(args.splice(0, 2))) + type = 'l' + command = command == 'm' ? 'l' : 'L' + } + + while (true) { + if (args.length == length[type]) { + args.unshift(command) + return data.push(args) + } + if (args.length < length[type]) throw new Error('malformed path data') + data.push([command].concat(args.splice(0, length[type]))) + } + }) + return data +} + +var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig + +function parseValues(args) { + var numbers = args.match(number) + return numbers ? numbers.map(Number) : [] +} + +},{}],73:[function(_dereq_,module,exports){ +/* + * @copyright 2016 Sean Connelly (@voidqk), http://syntheti.cc + * @license MIT + * @preserve Project Home: https://github.com/voidqk/polybooljs + */ + +var BuildLog = _dereq_('./lib/build-log'); +var Epsilon = _dereq_('./lib/epsilon'); +var Intersecter = _dereq_('./lib/intersecter'); +var SegmentChainer = _dereq_('./lib/segment-chainer'); +var SegmentSelector = _dereq_('./lib/segment-selector'); +var GeoJSON = _dereq_('./lib/geojson'); + +var buildLog = false; +var epsilon = Epsilon(); + +var PolyBool; +PolyBool = { + // getter/setter for buildLog + buildLog: function(bl){ + if (bl === true) + buildLog = BuildLog(); + else if (bl === false) + buildLog = false; + return buildLog === false ? false : buildLog.list; + }, + // getter/setter for epsilon + epsilon: function(v){ + return epsilon.epsilon(v); + }, + + // core API + segments: function(poly){ + var i = Intersecter(true, epsilon, buildLog); + poly.regions.forEach(i.addRegion); + return { + segments: i.calculate(poly.inverted), + inverted: poly.inverted + }; + }, + combine: function(segments1, segments2){ + var i3 = Intersecter(false, epsilon, buildLog); + return { + combined: i3.calculate( + segments1.segments, segments1.inverted, + segments2.segments, segments2.inverted + ), + inverted1: segments1.inverted, + inverted2: segments2.inverted + }; + }, + selectUnion: function(combined){ + return { + segments: SegmentSelector.union(combined.combined, buildLog), + inverted: combined.inverted1 || combined.inverted2 + } + }, + selectIntersect: function(combined){ + return { + segments: SegmentSelector.intersect(combined.combined, buildLog), + inverted: combined.inverted1 && combined.inverted2 + } + }, + selectDifference: function(combined){ + return { + segments: SegmentSelector.difference(combined.combined, buildLog), + inverted: combined.inverted1 && !combined.inverted2 + } + }, + selectDifferenceRev: function(combined){ + return { + segments: SegmentSelector.differenceRev(combined.combined, buildLog), + inverted: !combined.inverted1 && combined.inverted2 + } + }, + selectXor: function(combined){ + return { + segments: SegmentSelector.xor(combined.combined, buildLog), + inverted: combined.inverted1 !== combined.inverted2 + } + }, + polygon: function(segments){ + return { + regions: SegmentChainer(segments.segments, epsilon, buildLog), + inverted: segments.inverted + }; + }, + + // GeoJSON converters + polygonFromGeoJSON: function(geojson){ + return GeoJSON.toPolygon(PolyBool, geojson); + }, + polygonToGeoJSON: function(poly){ + return GeoJSON.fromPolygon(PolyBool, epsilon, poly); + }, + + // helper functions for common operations + union: function(poly1, poly2){ + return operate(poly1, poly2, PolyBool.selectUnion); + }, + intersect: function(poly1, poly2){ + return operate(poly1, poly2, PolyBool.selectIntersect); + }, + difference: function(poly1, poly2){ + return operate(poly1, poly2, PolyBool.selectDifference); + }, + differenceRev: function(poly1, poly2){ + return operate(poly1, poly2, PolyBool.selectDifferenceRev); + }, + xor: function(poly1, poly2){ + return operate(poly1, poly2, PolyBool.selectXor); + } +}; + +function operate(poly1, poly2, selector){ + var seg1 = PolyBool.segments(poly1); + var seg2 = PolyBool.segments(poly2); + var comb = PolyBool.combine(seg1, seg2); + var seg3 = selector(comb); + return PolyBool.polygon(seg3); +} + +if (typeof window === 'object') + window.PolyBool = PolyBool; + +module.exports = PolyBool; + +},{"./lib/build-log":74,"./lib/epsilon":75,"./lib/geojson":76,"./lib/intersecter":77,"./lib/segment-chainer":79,"./lib/segment-selector":80}],74:[function(_dereq_,module,exports){ +// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// used strictly for logging the processing of the algorithm... only useful if you intend on +// looking under the covers (for pretty UI's or debugging) +// + +function BuildLog(){ + var my; + var nextSegmentId = 0; + var curVert = false; + + function push(type, data){ + my.list.push({ + type: type, + data: data ? JSON.parse(JSON.stringify(data)) : void 0 + }); + return my; + } + + my = { + list: [], + segmentId: function(){ + return nextSegmentId++; + }, + checkIntersection: function(seg1, seg2){ + return push('check', { seg1: seg1, seg2: seg2 }); + }, + segmentChop: function(seg, end){ + push('div_seg', { seg: seg, pt: end }); + return push('chop', { seg: seg, pt: end }); + }, + statusRemove: function(seg){ + return push('pop_seg', { seg: seg }); + }, + segmentUpdate: function(seg){ + return push('seg_update', { seg: seg }); + }, + segmentNew: function(seg, primary){ + return push('new_seg', { seg: seg, primary: primary }); + }, + segmentRemove: function(seg){ + return push('rem_seg', { seg: seg }); + }, + tempStatus: function(seg, above, below){ + return push('temp_status', { seg: seg, above: above, below: below }); + }, + rewind: function(seg){ + return push('rewind', { seg: seg }); + }, + status: function(seg, above, below){ + return push('status', { seg: seg, above: above, below: below }); + }, + vert: function(x){ + if (x === curVert) + return my; + curVert = x; + return push('vert', { x: x }); + }, + log: function(data){ + if (typeof data !== 'string') + data = JSON.stringify(data, false, ' '); + return push('log', { txt: data }); + }, + reset: function(){ + return push('reset'); + }, + selected: function(segs){ + return push('selected', { segs: segs }); + }, + chainStart: function(seg){ + return push('chain_start', { seg: seg }); + }, + chainRemoveHead: function(index, pt){ + return push('chain_rem_head', { index: index, pt: pt }); + }, + chainRemoveTail: function(index, pt){ + return push('chain_rem_tail', { index: index, pt: pt }); + }, + chainNew: function(pt1, pt2){ + return push('chain_new', { pt1: pt1, pt2: pt2 }); + }, + chainMatch: function(index){ + return push('chain_match', { index: index }); + }, + chainClose: function(index){ + return push('chain_close', { index: index }); + }, + chainAddHead: function(index, pt){ + return push('chain_add_head', { index: index, pt: pt }); + }, + chainAddTail: function(index, pt){ + return push('chain_add_tail', { index: index, pt: pt, }); + }, + chainConnect: function(index1, index2){ + return push('chain_con', { index1: index1, index2: index2 }); + }, + chainReverse: function(index){ + return push('chain_rev', { index: index }); + }, + chainJoin: function(index1, index2){ + return push('chain_join', { index1: index1, index2: index2 }); + }, + done: function(){ + return push('done'); + } + }; + return my; +} + +module.exports = BuildLog; + +},{}],75:[function(_dereq_,module,exports){ +// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// provides the raw computation functions that takes epsilon into account +// +// zero is defined to be between (-epsilon, epsilon) exclusive +// + +function Epsilon(eps){ + if (typeof eps !== 'number') + eps = 0.0000000001; // sane default? sure why not + var my = { + epsilon: function(v){ + if (typeof v === 'number') + eps = v; + return eps; + }, + pointAboveOrOnLine: function(pt, left, right){ + var Ax = left[0]; + var Ay = left[1]; + var Bx = right[0]; + var By = right[1]; + var Cx = pt[0]; + var Cy = pt[1]; + return (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax) >= -eps; + }, + pointBetween: function(p, left, right){ + // p must be collinear with left->right + // returns false if p == left, p == right, or left == right + var d_py_ly = p[1] - left[1]; + var d_rx_lx = right[0] - left[0]; + var d_px_lx = p[0] - left[0]; + var d_ry_ly = right[1] - left[1]; + + var dot = d_px_lx * d_rx_lx + d_py_ly * d_ry_ly; + // if `dot` is 0, then `p` == `left` or `left` == `right` (reject) + // if `dot` is less than 0, then `p` is to the left of `left` (reject) + if (dot < eps) + return false; + + var sqlen = d_rx_lx * d_rx_lx + d_ry_ly * d_ry_ly; + // if `dot` > `sqlen`, then `p` is to the right of `right` (reject) + // therefore, if `dot - sqlen` is greater than 0, then `p` is to the right of `right` (reject) + if (dot - sqlen > -eps) + return false; + + return true; + }, + pointsSameX: function(p1, p2){ + return Math.abs(p1[0] - p2[0]) < eps; + }, + pointsSameY: function(p1, p2){ + return Math.abs(p1[1] - p2[1]) < eps; + }, + pointsSame: function(p1, p2){ + return my.pointsSameX(p1, p2) && my.pointsSameY(p1, p2); + }, + pointsCompare: function(p1, p2){ + // returns -1 if p1 is smaller, 1 if p2 is smaller, 0 if equal + if (my.pointsSameX(p1, p2)) + return my.pointsSameY(p1, p2) ? 0 : (p1[1] < p2[1] ? -1 : 1); + return p1[0] < p2[0] ? -1 : 1; + }, + pointsCollinear: function(pt1, pt2, pt3){ + // does pt1->pt2->pt3 make a straight line? + // essentially this is just checking to see if the slope(pt1->pt2) === slope(pt2->pt3) + // if slopes are equal, then they must be collinear, because they share pt2 + var dx1 = pt1[0] - pt2[0]; + var dy1 = pt1[1] - pt2[1]; + var dx2 = pt2[0] - pt3[0]; + var dy2 = pt2[1] - pt3[1]; + return Math.abs(dx1 * dy2 - dx2 * dy1) < eps; + }, + linesIntersect: function(a0, a1, b0, b1){ + // returns false if the lines are coincident (e.g., parallel or on top of each other) + // + // returns an object if the lines intersect: + // { + // pt: [x, y], where the intersection point is at + // alongA: where intersection point is along A, + // alongB: where intersection point is along B + // } + // + // alongA and alongB will each be one of: -2, -1, 0, 1, 2 + // + // with the following meaning: + // + // -2 intersection point is before segment's first point + // -1 intersection point is directly on segment's first point + // 0 intersection point is between segment's first and second points (exclusive) + // 1 intersection point is directly on segment's second point + // 2 intersection point is after segment's second point + var adx = a1[0] - a0[0]; + var ady = a1[1] - a0[1]; + var bdx = b1[0] - b0[0]; + var bdy = b1[1] - b0[1]; + + var axb = adx * bdy - ady * bdx; + if (Math.abs(axb) < eps) + return false; // lines are coincident + + var dx = a0[0] - b0[0]; + var dy = a0[1] - b0[1]; + + var A = (bdx * dy - bdy * dx) / axb; + var B = (adx * dy - ady * dx) / axb; + + var ret = { + alongA: 0, + alongB: 0, + pt: [ + a0[0] + A * adx, + a0[1] + A * ady + ] + }; + + // categorize where intersection point is along A and B + + if (A <= -eps) + ret.alongA = -2; + else if (A < eps) + ret.alongA = -1; + else if (A - 1 <= -eps) + ret.alongA = 0; + else if (A - 1 < eps) + ret.alongA = 1; + else + ret.alongA = 2; + + if (B <= -eps) + ret.alongB = -2; + else if (B < eps) + ret.alongB = -1; + else if (B - 1 <= -eps) + ret.alongB = 0; + else if (B - 1 < eps) + ret.alongB = 1; + else + ret.alongB = 2; + + return ret; + }, + pointInsideRegion: function(pt, region){ + var x = pt[0]; + var y = pt[1]; + var last_x = region[region.length - 1][0]; + var last_y = region[region.length - 1][1]; + var inside = false; + for (var i = 0; i < region.length; i++){ + var curr_x = region[i][0]; + var curr_y = region[i][1]; + + // if y is between curr_y and last_y, and + // x is to the right of the boundary created by the line + if ((curr_y - y > eps) != (last_y - y > eps) && + (last_x - curr_x) * (y - curr_y) / (last_y - curr_y) + curr_x - x > eps) + inside = !inside + + last_x = curr_x; + last_y = curr_y; + } + return inside; + } + }; + return my; +} + +module.exports = Epsilon; + +},{}],76:[function(_dereq_,module,exports){ +// (c) Copyright 2017, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// convert between PolyBool polygon format and GeoJSON formats (Polygon and MultiPolygon) +// + +var GeoJSON = { + // convert a GeoJSON object to a PolyBool polygon + toPolygon: function(PolyBool, geojson){ + + // converts list of LineString's to segments + function GeoPoly(coords){ + // check for empty coords + if (coords.length <= 0) + return PolyBool.segments({ inverted: false, regions: [] }); + + // convert LineString to segments + function LineString(ls){ + // remove tail which should be the same as head + var reg = ls.slice(0, ls.length - 1); + return PolyBool.segments({ inverted: false, regions: [reg] }); + } + + // the first LineString is considered the outside + var out = LineString(coords[0]); + + // the rest of the LineStrings are considered interior holes, so subtract them from the + // current result + for (var i = 1; i < coords.length; i++) + out = PolyBool.selectDifference(PolyBool.combine(out, LineString(coords[i]))); + + return out; + } + + if (geojson.type === 'Polygon'){ + // single polygon, so just convert it and we're done + return PolyBool.polygon(GeoPoly(geojson.coordinates)); + } + else if (geojson.type === 'MultiPolygon'){ + // multiple polygons, so union all the polygons together + var out = PolyBool.segments({ inverted: false, regions: [] }); + for (var i = 0; i < geojson.coordinates.length; i++) + out = PolyBool.selectUnion(PolyBool.combine(out, GeoPoly(geojson.coordinates[i]))); + return PolyBool.polygon(out); + } + throw new Error('PolyBool: Cannot convert GeoJSON object to PolyBool polygon'); + }, + + // convert a PolyBool polygon to a GeoJSON object + fromPolygon: function(PolyBool, eps, poly){ + // make sure out polygon is clean + poly = PolyBool.polygon(PolyBool.segments(poly)); + + // test if r1 is inside r2 + function regionInsideRegion(r1, r2){ + // we're guaranteed no lines intersect (because the polygon is clean), but a vertex + // could be on the edge -- so we just average pt[0] and pt[1] to produce a point on the + // edge of the first line, which cannot be on an edge + return eps.pointInsideRegion([ + (r1[0][0] + r1[1][0]) * 0.5, + (r1[0][1] + r1[1][1]) * 0.5 + ], r2); + } + + // calculate inside heirarchy + // + // _____________________ _______ roots -> A -> F + // | A | | F | | | + // | _______ _______ | | ___ | +-- B +-- G + // | | B | | C | | | | | | | | + // | | ___ | | ___ | | | | | | | +-- D + // | | | D | | | | E | | | | | G | | | + // | | |___| | | |___| | | | | | | +-- C + // | |_______| |_______| | | |___| | | + // |_____________________| |_______| +-- E + + function newNode(region){ + return { + region: region, + children: [] + }; + } + + var roots = newNode(null); + + function addChild(root, region){ + // first check if we're inside any children + for (var i = 0; i < root.children.length; i++){ + var child = root.children[i]; + if (regionInsideRegion(region, child.region)){ + // we are, so insert inside them instead + addChild(child, region); + return; + } + } + + // not inside any children, so check to see if any children are inside us + var node = newNode(region); + for (var i = 0; i < root.children.length; i++){ + var child = root.children[i]; + if (regionInsideRegion(child.region, region)){ + // oops... move the child beneath us, and remove them from root + node.children.push(child); + root.children.splice(i, 1); + i--; + } + } + + // now we can add ourselves + root.children.push(node); + } + + // add all regions to the root + for (var i = 0; i < poly.regions.length; i++){ + var region = poly.regions[i]; + if (region.length < 3) // regions must have at least 3 points (sanity check) + continue; + addChild(roots, region); + } + + // with our heirarchy, we can distinguish between exterior borders, and interior holes + // the root nodes are exterior, children are interior, children's children are exterior, + // children's children's children are interior, etc + + // while we're at it, exteriors are counter-clockwise, and interiors are clockwise + + function forceWinding(region, clockwise){ + // first, see if we're clockwise or counter-clockwise + // https://en.wikipedia.org/wiki/Shoelace_formula + var winding = 0; + var last_x = region[region.length - 1][0]; + var last_y = region[region.length - 1][1]; + var copy = []; + for (var i = 0; i < region.length; i++){ + var curr_x = region[i][0]; + var curr_y = region[i][1]; + copy.push([curr_x, curr_y]); // create a copy while we're at it + winding += curr_y * last_x - curr_x * last_y; + last_x = curr_x; + last_y = curr_y; + } + // this assumes Cartesian coordinates (Y is positive going up) + var isclockwise = winding < 0; + if (isclockwise !== clockwise) + copy.reverse(); + // while we're here, the last point must be the first point... + copy.push([copy[0][0], copy[0][1]]); + return copy; + } + + var geopolys = []; + + function addExterior(node){ + var poly = [forceWinding(node.region, false)]; + geopolys.push(poly); + // children of exteriors are interior + for (var i = 0; i < node.children.length; i++) + poly.push(getInterior(node.children[i])); + } + + function getInterior(node){ + // children of interiors are exterior + for (var i = 0; i < node.children.length; i++) + addExterior(node.children[i]); + // return the clockwise interior + return forceWinding(node.region, true); + } + + // root nodes are exterior + for (var i = 0; i < roots.children.length; i++) + addExterior(roots.children[i]); + + // lastly, construct the approrpriate GeoJSON object + + if (geopolys.length <= 0) // empty GeoJSON Polygon + return { type: 'Polygon', coordinates: [] }; + if (geopolys.length == 1) // use a GeoJSON Polygon + return { type: 'Polygon', coordinates: geopolys[0] }; + return { // otherwise, use a GeoJSON MultiPolygon + type: 'MultiPolygon', + coordinates: geopolys + }; + } +}; + +module.exports = GeoJSON; + +},{}],77:[function(_dereq_,module,exports){ +// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// this is the core work-horse +// + +var LinkedList = _dereq_('./linked-list'); + +function Intersecter(selfIntersection, eps, buildLog){ + // selfIntersection is true/false depending on the phase of the overall algorithm + + // + // segment creation + // + + function segmentNew(start, end){ + return { + id: buildLog ? buildLog.segmentId() : -1, + start: start, + end: end, + myFill: { + above: null, // is there fill above us? + below: null // is there fill below us? + }, + otherFill: null + }; + } + + function segmentCopy(start, end, seg){ + return { + id: buildLog ? buildLog.segmentId() : -1, + start: start, + end: end, + myFill: { + above: seg.myFill.above, + below: seg.myFill.below + }, + otherFill: null + }; + } + + // + // event logic + // + + var event_root = LinkedList.create(); + + function eventCompare(p1_isStart, p1_1, p1_2, p2_isStart, p2_1, p2_2){ + // compare the selected points first + var comp = eps.pointsCompare(p1_1, p2_1); + if (comp !== 0) + return comp; + // the selected points are the same + + if (eps.pointsSame(p1_2, p2_2)) // if the non-selected points are the same too... + return 0; // then the segments are equal + + if (p1_isStart !== p2_isStart) // if one is a start and the other isn't... + return p1_isStart ? 1 : -1; // favor the one that isn't the start + + // otherwise, we'll have to calculate which one is below the other manually + return eps.pointAboveOrOnLine(p1_2, + p2_isStart ? p2_1 : p2_2, // order matters + p2_isStart ? p2_2 : p2_1 + ) ? 1 : -1; + } + + function eventAdd(ev, other_pt){ + event_root.insertBefore(ev, function(here){ + // should ev be inserted before here? + var comp = eventCompare( + ev .isStart, ev .pt, other_pt, + here.isStart, here.pt, here.other.pt + ); + return comp < 0; + }); + } + + function eventAddSegmentStart(seg, primary){ + var ev_start = LinkedList.node({ + isStart: true, + pt: seg.start, + seg: seg, + primary: primary, + other: null, + status: null + }); + eventAdd(ev_start, seg.end); + return ev_start; + } + + function eventAddSegmentEnd(ev_start, seg, primary){ + var ev_end = LinkedList.node({ + isStart: false, + pt: seg.end, + seg: seg, + primary: primary, + other: ev_start, + status: null + }); + ev_start.other = ev_end; + eventAdd(ev_end, ev_start.pt); + } + + function eventAddSegment(seg, primary){ + var ev_start = eventAddSegmentStart(seg, primary); + eventAddSegmentEnd(ev_start, seg, primary); + return ev_start; + } + + function eventUpdateEnd(ev, end){ + // slides an end backwards + // (start)------------(end) to: + // (start)---(end) + + if (buildLog) + buildLog.segmentChop(ev.seg, end); + + ev.other.remove(); + ev.seg.end = end; + ev.other.pt = end; + eventAdd(ev.other, ev.pt); + } + + function eventDivide(ev, pt){ + var ns = segmentCopy(pt, ev.seg.end, ev.seg); + eventUpdateEnd(ev, pt); + return eventAddSegment(ns, ev.primary); + } + + function calculate(primaryPolyInverted, secondaryPolyInverted){ + // if selfIntersection is true then there is no secondary polygon, so that isn't used + + // + // status logic + // + + var status_root = LinkedList.create(); + + function statusCompare(ev1, ev2){ + var a1 = ev1.seg.start; + var a2 = ev1.seg.end; + var b1 = ev2.seg.start; + var b2 = ev2.seg.end; + + if (eps.pointsCollinear(a1, b1, b2)){ + if (eps.pointsCollinear(a2, b1, b2)) + return 1;//eventCompare(true, a1, a2, true, b1, b2); + return eps.pointAboveOrOnLine(a2, b1, b2) ? 1 : -1; + } + return eps.pointAboveOrOnLine(a1, b1, b2) ? 1 : -1; + } + + function statusFindSurrounding(ev){ + return status_root.findTransition(function(here){ + var comp = statusCompare(ev, here.ev); + return comp > 0; + }); + } + + function checkIntersection(ev1, ev2){ + // returns the segment equal to ev1, or false if nothing equal + + var seg1 = ev1.seg; + var seg2 = ev2.seg; + var a1 = seg1.start; + var a2 = seg1.end; + var b1 = seg2.start; + var b2 = seg2.end; + + if (buildLog) + buildLog.checkIntersection(seg1, seg2); + + var i = eps.linesIntersect(a1, a2, b1, b2); + + if (i === false){ + // segments are parallel or coincident + + // if points aren't collinear, then the segments are parallel, so no intersections + if (!eps.pointsCollinear(a1, a2, b1)) + return false; + // otherwise, segments are on top of each other somehow (aka coincident) + + if (eps.pointsSame(a1, b2) || eps.pointsSame(a2, b1)) + return false; // segments touch at endpoints... no intersection + + var a1_equ_b1 = eps.pointsSame(a1, b1); + var a2_equ_b2 = eps.pointsSame(a2, b2); + + if (a1_equ_b1 && a2_equ_b2) + return ev2; // segments are exactly equal + + var a1_between = !a1_equ_b1 && eps.pointBetween(a1, b1, b2); + var a2_between = !a2_equ_b2 && eps.pointBetween(a2, b1, b2); + + // handy for debugging: + // buildLog.log({ + // a1_equ_b1: a1_equ_b1, + // a2_equ_b2: a2_equ_b2, + // a1_between: a1_between, + // a2_between: a2_between + // }); + + if (a1_equ_b1){ + if (a2_between){ + // (a1)---(a2) + // (b1)----------(b2) + eventDivide(ev2, a2); + } + else{ + // (a1)----------(a2) + // (b1)---(b2) + eventDivide(ev1, b2); + } + return ev2; + } + else if (a1_between){ + if (!a2_equ_b2){ + // make a2 equal to b2 + if (a2_between){ + // (a1)---(a2) + // (b1)-----------------(b2) + eventDivide(ev2, a2); + } + else{ + // (a1)----------(a2) + // (b1)----------(b2) + eventDivide(ev1, b2); + } + } + + // (a1)---(a2) + // (b1)----------(b2) + eventDivide(ev2, a1); + } + } + else{ + // otherwise, lines intersect at i.pt, which may or may not be between the endpoints + + // is A divided between its endpoints? (exclusive) + if (i.alongA === 0){ + if (i.alongB === -1) // yes, at exactly b1 + eventDivide(ev1, b1); + else if (i.alongB === 0) // yes, somewhere between B's endpoints + eventDivide(ev1, i.pt); + else if (i.alongB === 1) // yes, at exactly b2 + eventDivide(ev1, b2); + } + + // is B divided between its endpoints? (exclusive) + if (i.alongB === 0){ + if (i.alongA === -1) // yes, at exactly a1 + eventDivide(ev2, a1); + else if (i.alongA === 0) // yes, somewhere between A's endpoints (exclusive) + eventDivide(ev2, i.pt); + else if (i.alongA === 1) // yes, at exactly a2 + eventDivide(ev2, a2); + } + } + return false; + } + + // + // main event loop + // + var segments = []; + while (!event_root.isEmpty()){ + var ev = event_root.getHead(); + + if (buildLog) + buildLog.vert(ev.pt[0]); + + if (ev.isStart){ + + if (buildLog) + buildLog.segmentNew(ev.seg, ev.primary); + + var surrounding = statusFindSurrounding(ev); + var above = surrounding.before ? surrounding.before.ev : null; + var below = surrounding.after ? surrounding.after.ev : null; + + if (buildLog){ + buildLog.tempStatus( + ev.seg, + above ? above.seg : false, + below ? below.seg : false + ); + } + + function checkBothIntersections(){ + if (above){ + var eve = checkIntersection(ev, above); + if (eve) + return eve; + } + if (below) + return checkIntersection(ev, below); + return false; + } + + var eve = checkBothIntersections(); + if (eve){ + // ev and eve are equal + // we'll keep eve and throw away ev + + // merge ev.seg's fill information into eve.seg + + if (selfIntersection){ + var toggle; // are we a toggling edge? + if (ev.seg.myFill.below === null) + toggle = true; + else + toggle = ev.seg.myFill.above !== ev.seg.myFill.below; + + // merge two segments that belong to the same polygon + // think of this as sandwiching two segments together, where `eve.seg` is + // the bottom -- this will cause the above fill flag to toggle + if (toggle) + eve.seg.myFill.above = !eve.seg.myFill.above; + } + else{ + // merge two segments that belong to different polygons + // each segment has distinct knowledge, so no special logic is needed + // note that this can only happen once per segment in this phase, because we + // are guaranteed that all self-intersections are gone + eve.seg.otherFill = ev.seg.myFill; + } + + if (buildLog) + buildLog.segmentUpdate(eve.seg); + + ev.other.remove(); + ev.remove(); + } + + if (event_root.getHead() !== ev){ + // something was inserted before us in the event queue, so loop back around and + // process it before continuing + if (buildLog) + buildLog.rewind(ev.seg); + continue; + } + + // + // calculate fill flags + // + if (selfIntersection){ + var toggle; // are we a toggling edge? + if (ev.seg.myFill.below === null) // if we are a new segment... + toggle = true; // then we toggle + else // we are a segment that has previous knowledge from a division + toggle = ev.seg.myFill.above !== ev.seg.myFill.below; // calculate toggle + + // next, calculate whether we are filled below us + if (!below){ // if nothing is below us... + // we are filled below us if the polygon is inverted + ev.seg.myFill.below = primaryPolyInverted; + } + else{ + // otherwise, we know the answer -- it's the same if whatever is below + // us is filled above it + ev.seg.myFill.below = below.seg.myFill.above; + } + + // since now we know if we're filled below us, we can calculate whether + // we're filled above us by applying toggle to whatever is below us + if (toggle) + ev.seg.myFill.above = !ev.seg.myFill.below; + else + ev.seg.myFill.above = ev.seg.myFill.below; + } + else{ + // now we fill in any missing transition information, since we are all-knowing + // at this point + + if (ev.seg.otherFill === null){ + // if we don't have other information, then we need to figure out if we're + // inside the other polygon + var inside; + if (!below){ + // if nothing is below us, then we're inside if the other polygon is + // inverted + inside = + ev.primary ? secondaryPolyInverted : primaryPolyInverted; + } + else{ // otherwise, something is below us + // so copy the below segment's other polygon's above + if (ev.primary === below.primary) + inside = below.seg.otherFill.above; + else + inside = below.seg.myFill.above; + } + ev.seg.otherFill = { + above: inside, + below: inside + }; + } + } + + if (buildLog){ + buildLog.status( + ev.seg, + above ? above.seg : false, + below ? below.seg : false + ); + } + + // insert the status and remember it for later removal + ev.other.status = surrounding.insert(LinkedList.node({ ev: ev })); + } + else{ + var st = ev.status; + + if (st === null){ + throw new Error('PolyBool: Zero-length segment detected; your epsilon is ' + + 'probably too small or too large'); + } + + // removing the status will create two new adjacent edges, so we'll need to check + // for those + if (status_root.exists(st.prev) && status_root.exists(st.next)) + checkIntersection(st.prev.ev, st.next.ev); + + if (buildLog) + buildLog.statusRemove(st.ev.seg); + + // remove the status + st.remove(); + + // if we've reached this point, we've calculated everything there is to know, so + // save the segment for reporting + if (!ev.primary){ + // make sure `seg.myFill` actually points to the primary polygon though + var s = ev.seg.myFill; + ev.seg.myFill = ev.seg.otherFill; + ev.seg.otherFill = s; + } + segments.push(ev.seg); + } + + // remove the event and continue + event_root.getHead().remove(); + } + + if (buildLog) + buildLog.done(); + + return segments; + } + + // return the appropriate API depending on what we're doing + if (!selfIntersection){ + // performing combination of polygons, so only deal with already-processed segments + return { + calculate: function(segments1, inverted1, segments2, inverted2){ + // segmentsX come from the self-intersection API, or this API + // invertedX is whether we treat that list of segments as an inverted polygon or not + // returns segments that can be used for further operations + segments1.forEach(function(seg){ + eventAddSegment(segmentCopy(seg.start, seg.end, seg), true); + }); + segments2.forEach(function(seg){ + eventAddSegment(segmentCopy(seg.start, seg.end, seg), false); + }); + return calculate(inverted1, inverted2); + } + }; + } + + // otherwise, performing self-intersection, so deal with regions + return { + addRegion: function(region){ + // regions are a list of points: + // [ [0, 0], [100, 0], [50, 100] ] + // you can add multiple regions before running calculate + var pt1; + var pt2 = region[region.length - 1]; + for (var i = 0; i < region.length; i++){ + pt1 = pt2; + pt2 = region[i]; + + var forward = eps.pointsCompare(pt1, pt2); + if (forward === 0) // points are equal, so we have a zero-length segment + continue; // just skip it + + eventAddSegment( + segmentNew( + forward < 0 ? pt1 : pt2, + forward < 0 ? pt2 : pt1 + ), + true + ); + } + }, + calculate: function(inverted){ + // is the polygon inverted? + // returns segments + return calculate(inverted, false); + } + }; +} + +module.exports = Intersecter; + +},{"./linked-list":78}],78:[function(_dereq_,module,exports){ +// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// simple linked list implementation that allows you to traverse down nodes and save positions +// + +var LinkedList = { + create: function(){ + var my = { + root: { root: true, next: null }, + exists: function(node){ + if (node === null || node === my.root) + return false; + return true; + }, + isEmpty: function(){ + return my.root.next === null; + }, + getHead: function(){ + return my.root.next; + }, + insertBefore: function(node, check){ + var last = my.root; + var here = my.root.next; + while (here !== null){ + if (check(here)){ + node.prev = here.prev; + node.next = here; + here.prev.next = node; + here.prev = node; + return; + } + last = here; + here = here.next; + } + last.next = node; + node.prev = last; + node.next = null; + }, + findTransition: function(check){ + var prev = my.root; + var here = my.root.next; + while (here !== null){ + if (check(here)) + break; + prev = here; + here = here.next; + } + return { + before: prev === my.root ? null : prev, + after: here, + insert: function(node){ + node.prev = prev; + node.next = here; + prev.next = node; + if (here !== null) + here.prev = node; + return node; + } + }; + } + }; + return my; + }, + node: function(data){ + data.prev = null; + data.next = null; + data.remove = function(){ + data.prev.next = data.next; + if (data.next) + data.next.prev = data.prev; + data.prev = null; + data.next = null; + }; + return data; + } +}; + +module.exports = LinkedList; + +},{}],79:[function(_dereq_,module,exports){ +// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// converts a list of segments into a list of regions, while also removing unnecessary verticies +// + +function SegmentChainer(segments, eps, buildLog){ + var chains = []; + var regions = []; + + segments.forEach(function(seg){ + var pt1 = seg.start; + var pt2 = seg.end; + if (eps.pointsSame(pt1, pt2)){ + console.warn('PolyBool: Warning: Zero-length segment detected; your epsilon is ' + + 'probably too small or too large'); + return; + } + + if (buildLog) + buildLog.chainStart(seg); + + // search for two chains that this segment matches + var first_match = { + index: 0, + matches_head: false, + matches_pt1: false + }; + var second_match = { + index: 0, + matches_head: false, + matches_pt1: false + }; + var next_match = first_match; + function setMatch(index, matches_head, matches_pt1){ + // return true if we've matched twice + next_match.index = index; + next_match.matches_head = matches_head; + next_match.matches_pt1 = matches_pt1; + if (next_match === first_match){ + next_match = second_match; + return false; + } + next_match = null; + return true; // we've matched twice, we're done here + } + for (var i = 0; i < chains.length; i++){ + var chain = chains[i]; + var head = chain[0]; + var head2 = chain[1]; + var tail = chain[chain.length - 1]; + var tail2 = chain[chain.length - 2]; + if (eps.pointsSame(head, pt1)){ + if (setMatch(i, true, true)) + break; + } + else if (eps.pointsSame(head, pt2)){ + if (setMatch(i, true, false)) + break; + } + else if (eps.pointsSame(tail, pt1)){ + if (setMatch(i, false, true)) + break; + } + else if (eps.pointsSame(tail, pt2)){ + if (setMatch(i, false, false)) + break; + } + } + + if (next_match === first_match){ + // we didn't match anything, so create a new chain + chains.push([ pt1, pt2 ]); + if (buildLog) + buildLog.chainNew(pt1, pt2); + return; + } + + if (next_match === second_match){ + // we matched a single chain + + if (buildLog) + buildLog.chainMatch(first_match.index); + + // add the other point to the apporpriate end, and check to see if we've closed the + // chain into a loop + + var index = first_match.index; + var pt = first_match.matches_pt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc + var addToHead = first_match.matches_head; // if we matched at head, then add to the head + + var chain = chains[index]; + var grow = addToHead ? chain[0] : chain[chain.length - 1]; + var grow2 = addToHead ? chain[1] : chain[chain.length - 2]; + var oppo = addToHead ? chain[chain.length - 1] : chain[0]; + var oppo2 = addToHead ? chain[chain.length - 2] : chain[1]; + + if (eps.pointsCollinear(grow2, grow, pt)){ + // grow isn't needed because it's directly between grow2 and pt: + // grow2 ---grow---> pt + if (addToHead){ + if (buildLog) + buildLog.chainRemoveHead(first_match.index, pt); + chain.shift(); + } + else{ + if (buildLog) + buildLog.chainRemoveTail(first_match.index, pt); + chain.pop(); + } + grow = grow2; // old grow is gone... new grow is what grow2 was + } + + if (eps.pointsSame(oppo, pt)){ + // we're closing the loop, so remove chain from chains + chains.splice(index, 1); + + if (eps.pointsCollinear(oppo2, oppo, grow)){ + // oppo isn't needed because it's directly between oppo2 and grow: + // oppo2 ---oppo--->grow + if (addToHead){ + if (buildLog) + buildLog.chainRemoveTail(first_match.index, grow); + chain.pop(); + } + else{ + if (buildLog) + buildLog.chainRemoveHead(first_match.index, grow); + chain.shift(); + } + } + + if (buildLog) + buildLog.chainClose(first_match.index); + + // we have a closed chain! + regions.push(chain); + return; + } + + // not closing a loop, so just add it to the apporpriate side + if (addToHead){ + if (buildLog) + buildLog.chainAddHead(first_match.index, pt); + chain.unshift(pt); + } + else{ + if (buildLog) + buildLog.chainAddTail(first_match.index, pt); + chain.push(pt); + } + return; + } + + // otherwise, we matched two chains, so we need to combine those chains together + + function reverseChain(index){ + if (buildLog) + buildLog.chainReverse(index); + chains[index].reverse(); // gee, that's easy + } + + function appendChain(index1, index2){ + // index1 gets index2 appended to it, and index2 is removed + var chain1 = chains[index1]; + var chain2 = chains[index2]; + var tail = chain1[chain1.length - 1]; + var tail2 = chain1[chain1.length - 2]; + var head = chain2[0]; + var head2 = chain2[1]; + + if (eps.pointsCollinear(tail2, tail, head)){ + // tail isn't needed because it's directly between tail2 and head + // tail2 ---tail---> head + if (buildLog) + buildLog.chainRemoveTail(index1, tail); + chain1.pop(); + tail = tail2; // old tail is gone... new tail is what tail2 was + } + + if (eps.pointsCollinear(tail, head, head2)){ + // head isn't needed because it's directly between tail and head2 + // tail ---head---> head2 + if (buildLog) + buildLog.chainRemoveHead(index2, head); + chain2.shift(); + } + + if (buildLog) + buildLog.chainJoin(index1, index2); + chains[index1] = chain1.concat(chain2); + chains.splice(index2, 1); + } + + var F = first_match.index; + var S = second_match.index; + + if (buildLog) + buildLog.chainConnect(F, S); + + var reverseF = chains[F].length < chains[S].length; // reverse the shorter chain, if needed + if (first_match.matches_head){ + if (second_match.matches_head){ + if (reverseF){ + // <<<< F <<<< --- >>>> S >>>> + reverseChain(F); + // >>>> F >>>> --- >>>> S >>>> + appendChain(F, S); + } + else{ + // <<<< F <<<< --- >>>> S >>>> + reverseChain(S); + // <<<< F <<<< --- <<<< S <<<< logically same as: + // >>>> S >>>> --- >>>> F >>>> + appendChain(S, F); + } + } + else{ + // <<<< F <<<< --- <<<< S <<<< logically same as: + // >>>> S >>>> --- >>>> F >>>> + appendChain(S, F); + } + } + else{ + if (second_match.matches_head){ + // >>>> F >>>> --- >>>> S >>>> + appendChain(F, S); + } + else{ + if (reverseF){ + // >>>> F >>>> --- <<<< S <<<< + reverseChain(F); + // <<<< F <<<< --- <<<< S <<<< logically same as: + // >>>> S >>>> --- >>>> F >>>> + appendChain(S, F); + } + else{ + // >>>> F >>>> --- <<<< S <<<< + reverseChain(S); + // >>>> F >>>> --- >>>> S >>>> + appendChain(F, S); + } + } + } + }); + + return regions; +} + +module.exports = SegmentChainer; + +},{}],80:[function(_dereq_,module,exports){ +// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc +// MIT License +// Project Home: https://github.com/voidqk/polybooljs + +// +// filter a list of segments based on boolean operations +// + +function select(segments, selection, buildLog){ + var result = []; + segments.forEach(function(seg){ + var index = + (seg.myFill.above ? 8 : 0) + + (seg.myFill.below ? 4 : 0) + + ((seg.otherFill && seg.otherFill.above) ? 2 : 0) + + ((seg.otherFill && seg.otherFill.below) ? 1 : 0); + if (selection[index] !== 0){ + // copy the segment to the results, while also calculating the fill status + result.push({ + id: buildLog ? buildLog.segmentId() : -1, + start: seg.start, + end: seg.end, + myFill: { + above: selection[index] === 1, // 1 if filled above + below: selection[index] === 2 // 2 if filled below + }, + otherFill: null + }); + } + }); + + if (buildLog) + buildLog.selected(result); + + return result; +} + +var SegmentSelector = { + union: function(segments, buildLog){ // primary | secondary + // above1 below1 above2 below2 Keep? Value + // 0 0 0 0 => no 0 + // 0 0 0 1 => yes filled below 2 + // 0 0 1 0 => yes filled above 1 + // 0 0 1 1 => no 0 + // 0 1 0 0 => yes filled below 2 + // 0 1 0 1 => yes filled below 2 + // 0 1 1 0 => no 0 + // 0 1 1 1 => no 0 + // 1 0 0 0 => yes filled above 1 + // 1 0 0 1 => no 0 + // 1 0 1 0 => yes filled above 1 + // 1 0 1 1 => no 0 + // 1 1 0 0 => no 0 + // 1 1 0 1 => no 0 + // 1 1 1 0 => no 0 + // 1 1 1 1 => no 0 + return select(segments, [ + 0, 2, 1, 0, + 2, 2, 0, 0, + 1, 0, 1, 0, + 0, 0, 0, 0 + ], buildLog); + }, + intersect: function(segments, buildLog){ // primary & secondary + // above1 below1 above2 below2 Keep? Value + // 0 0 0 0 => no 0 + // 0 0 0 1 => no 0 + // 0 0 1 0 => no 0 + // 0 0 1 1 => no 0 + // 0 1 0 0 => no 0 + // 0 1 0 1 => yes filled below 2 + // 0 1 1 0 => no 0 + // 0 1 1 1 => yes filled below 2 + // 1 0 0 0 => no 0 + // 1 0 0 1 => no 0 + // 1 0 1 0 => yes filled above 1 + // 1 0 1 1 => yes filled above 1 + // 1 1 0 0 => no 0 + // 1 1 0 1 => yes filled below 2 + // 1 1 1 0 => yes filled above 1 + // 1 1 1 1 => no 0 + return select(segments, [ + 0, 0, 0, 0, + 0, 2, 0, 2, + 0, 0, 1, 1, + 0, 2, 1, 0 + ], buildLog); + }, + difference: function(segments, buildLog){ // primary - secondary + // above1 below1 above2 below2 Keep? Value + // 0 0 0 0 => no 0 + // 0 0 0 1 => no 0 + // 0 0 1 0 => no 0 + // 0 0 1 1 => no 0 + // 0 1 0 0 => yes filled below 2 + // 0 1 0 1 => no 0 + // 0 1 1 0 => yes filled below 2 + // 0 1 1 1 => no 0 + // 1 0 0 0 => yes filled above 1 + // 1 0 0 1 => yes filled above 1 + // 1 0 1 0 => no 0 + // 1 0 1 1 => no 0 + // 1 1 0 0 => no 0 + // 1 1 0 1 => yes filled above 1 + // 1 1 1 0 => yes filled below 2 + // 1 1 1 1 => no 0 + return select(segments, [ + 0, 0, 0, 0, + 2, 0, 2, 0, + 1, 1, 0, 0, + 0, 1, 2, 0 + ], buildLog); + }, + differenceRev: function(segments, buildLog){ // secondary - primary + // above1 below1 above2 below2 Keep? Value + // 0 0 0 0 => no 0 + // 0 0 0 1 => yes filled below 2 + // 0 0 1 0 => yes filled above 1 + // 0 0 1 1 => no 0 + // 0 1 0 0 => no 0 + // 0 1 0 1 => no 0 + // 0 1 1 0 => yes filled above 1 + // 0 1 1 1 => yes filled above 1 + // 1 0 0 0 => no 0 + // 1 0 0 1 => yes filled below 2 + // 1 0 1 0 => no 0 + // 1 0 1 1 => yes filled below 2 + // 1 1 0 0 => no 0 + // 1 1 0 1 => no 0 + // 1 1 1 0 => no 0 + // 1 1 1 1 => no 0 + return select(segments, [ + 0, 2, 1, 0, + 0, 0, 1, 1, + 0, 2, 0, 2, + 0, 0, 0, 0 + ], buildLog); + }, + xor: function(segments, buildLog){ // primary ^ secondary + // above1 below1 above2 below2 Keep? Value + // 0 0 0 0 => no 0 + // 0 0 0 1 => yes filled below 2 + // 0 0 1 0 => yes filled above 1 + // 0 0 1 1 => no 0 + // 0 1 0 0 => yes filled below 2 + // 0 1 0 1 => no 0 + // 0 1 1 0 => no 0 + // 0 1 1 1 => yes filled above 1 + // 1 0 0 0 => yes filled above 1 + // 1 0 0 1 => no 0 + // 1 0 1 0 => no 0 + // 1 0 1 1 => yes filled below 2 + // 1 1 0 0 => no 0 + // 1 1 0 1 => yes filled above 1 + // 1 1 1 0 => yes filled below 2 + // 1 1 1 1 => no 0 + return select(segments, [ + 0, 2, 1, 0, + 2, 0, 0, 1, + 1, 0, 0, 2, + 0, 1, 2, 0 + ], buildLog); + } +}; + +module.exports = SegmentSelector; + +},{}],81:[function(_dereq_,module,exports){ +'use strict'; + + +var Transform = _dereq_('stream').Transform; +var streamParser = _dereq_('stream-parser'); + + +function ParserStream() { + Transform.call(this, { readableObjectMode: true }); +} + +// Inherit from Transform +ParserStream.prototype = Object.create(Transform.prototype); +ParserStream.prototype.constructor = ParserStream; + +streamParser(ParserStream.prototype); + + +exports.ParserStream = ParserStream; + + +exports.sliceEq = function (src, start, dest) { + for (var i = start, j = 0; j < dest.length;) { + if (src[i++] !== dest[j++]) return false; + } + return true; +}; + +exports.str2arr = function (str, format) { + var arr = [], i = 0; + + if (format && format === 'hex') { + while (i < str.length) { + arr.push(parseInt(str.slice(i, i + 2), 16)); + i += 2; + } + } else { + for (; i < str.length; i++) { + /* eslint-disable no-bitwise */ + arr.push(str.charCodeAt(i) & 0xFF); + } + } + + return arr; +}; + +exports.readUInt16LE = function (data, offset) { + return data[offset] | (data[offset + 1] << 8); +}; + +exports.readUInt16BE = function (data, offset) { + return data[offset + 1] | (data[offset] << 8); +}; + +exports.readUInt32LE = function (data, offset) { + return data[offset] | + (data[offset + 1] << 8) | + (data[offset + 2] << 16) | + (data[offset + 3] * 0x1000000); +}; + +exports.readUInt32BE = function (data, offset) { + return data[offset + 3] | + (data[offset + 2] << 8) | + (data[offset + 1] << 16) | + (data[offset] * 0x1000000); +}; + + +function ProbeError(message, code, statusCode) { + Error.call(this); + Error.captureStackTrace(this, this.constructor); + + this.name = this.constructor.name; + + this.message = message; + if (code) this.code = code; + if (statusCode) this.statusCode = statusCode; +} + +// Inherit from Error +ProbeError.prototype = Object.create(Error.prototype); +ProbeError.prototype.constructor = ProbeError; + + +exports.ProbeError = ProbeError; + +},{"stream":98,"stream-parser":114}],82:[function(_dereq_,module,exports){ + +/* eslint-disable no-bitwise */ +/* eslint-disable consistent-return */ + +'use strict'; + +////////////////////////////////////////////////////////////////////////// +// Helpers +// +function error(message, code) { + var err = new Error(message); + err.code = code; + return err; +} + + +function utf8_decode(str) { + try { + return decodeURIComponent(escape(str)); + } catch (_) { + return str; + } +} + + +////////////////////////////////////////////////////////////////////////// +// Exif parser +// +// Input: +// - jpeg_bin: Uint8Array - jpeg file +// - exif_start: Number - start of TIFF header (after Exif\0\0) +// - exif_end: Number - end of Exif segment +// - on_entry: Number - callback +// +function ExifParser(jpeg_bin, exif_start, exif_end) { + // Uint8Array, exif without signature (which isn't included in offsets) + this.input = jpeg_bin.subarray(exif_start, exif_end); + + // offset correction for `on_entry` callback + this.start = exif_start; + + // Check TIFF header (includes byte alignment and first IFD offset) + var sig = String.fromCharCode.apply(null, this.input.subarray(0, 4)); + + if (sig !== 'II\x2A\0' && sig !== 'MM\0\x2A') { + throw error('invalid TIFF signature', 'EBADDATA'); + } + + // true if motorola (big endian) byte alignment, false if intel + this.big_endian = sig[0] === 'M'; +} + + +ExifParser.prototype.each = function (on_entry) { + // allow premature exit + this.aborted = false; + + var offset = this.read_uint32(4); + + this.ifds_to_read = [ { + id: 0, + offset: offset + } ]; + + while (this.ifds_to_read.length > 0 && !this.aborted) { + var i = this.ifds_to_read.shift(); + if (!i.offset) continue; + this.scan_ifd(i.id, i.offset, on_entry); + } +}; + + +ExifParser.prototype.read_uint16 = function (offset) { + var d = this.input; + if (offset + 2 > d.length) throw error('unexpected EOF', 'EBADDATA'); + + return this.big_endian ? + d[offset] * 0x100 + d[offset + 1] : + d[offset] + d[offset + 1] * 0x100; +}; + + +ExifParser.prototype.read_uint32 = function (offset) { + var d = this.input; + if (offset + 4 > d.length) throw error('unexpected EOF', 'EBADDATA'); + + return this.big_endian ? + d[offset] * 0x1000000 + d[offset + 1] * 0x10000 + d[offset + 2] * 0x100 + d[offset + 3] : + d[offset] + d[offset + 1] * 0x100 + d[offset + 2] * 0x10000 + d[offset + 3] * 0x1000000; +}; + + +ExifParser.prototype.is_subifd_link = function (ifd, tag) { + return (ifd === 0 && tag === 0x8769) || // SubIFD + (ifd === 0 && tag === 0x8825) || // GPS Info + (ifd === 0x8769 && tag === 0xA005); // Interop IFD +}; + + +// Returns byte length of a single component of a given format +// +ExifParser.prototype.exif_format_length = function (format) { + switch (format) { + case 1: // byte + case 2: // ascii + case 6: // sbyte + case 7: // undefined + return 1; + + case 3: // short + case 8: // sshort + return 2; + + case 4: // long + case 9: // slong + case 11: // float + return 4; + + case 5: // rational + case 10: // srational + case 12: // double + return 8; + + default: + // unknown type + return 0; + } +}; + + +// Reads Exif data +// +ExifParser.prototype.exif_format_read = function (format, offset) { + var v; + + switch (format) { + case 1: // byte + case 2: // ascii + v = this.input[offset]; + return v; + + case 6: // sbyte + v = this.input[offset]; + return v | (v & 0x80) * 0x1fffffe; + + case 3: // short + v = this.read_uint16(offset); + return v; + + case 8: // sshort + v = this.read_uint16(offset); + return v | (v & 0x8000) * 0x1fffe; + + case 4: // long + v = this.read_uint32(offset); + return v; + + case 9: // slong + v = this.read_uint32(offset); + return v | 0; + + case 5: // rational + case 10: // srational + case 11: // float + case 12: // double + return null; // not implemented + + case 7: // undefined + return null; // blob + + default: + // unknown type + return null; + } +}; + + +ExifParser.prototype.scan_ifd = function (ifd_no, offset, on_entry) { + var entry_count = this.read_uint16(offset); + + offset += 2; + + for (var i = 0; i < entry_count; i++) { + var tag = this.read_uint16(offset); + var format = this.read_uint16(offset + 2); + var count = this.read_uint32(offset + 4); + + var comp_length = this.exif_format_length(format); + var data_length = count * comp_length; + var data_offset = data_length <= 4 ? offset + 8 : this.read_uint32(offset + 8); + var is_subifd_link = false; + + if (data_offset + data_length > this.input.length) { + throw error('unexpected EOF', 'EBADDATA'); + } + + var value = []; + var comp_offset = data_offset; + + for (var j = 0; j < count; j++, comp_offset += comp_length) { + var item = this.exif_format_read(format, comp_offset); + if (item === null) { + value = null; + break; + } + value.push(item); + } + + if (Array.isArray(value) && format === 2) { + value = utf8_decode(String.fromCharCode.apply(null, value)); + if (value && value[value.length - 1] === '\0') value = value.slice(0, -1); + } + + if (this.is_subifd_link(ifd_no, tag)) { + if (Array.isArray(value) && Number.isInteger(value[0]) && value[0] > 0) { + this.ifds_to_read.push({ + id: tag, + offset: value[0] + }); + is_subifd_link = true; + } + } + + var entry = { + is_big_endian: this.big_endian, + ifd: ifd_no, + tag: tag, + format: format, + count: count, + entry_offset: offset + this.start, + data_length: data_length, + data_offset: data_offset + this.start, + value: value, + is_subifd_link: is_subifd_link + }; + + if (on_entry(entry) === false) { + this.aborted = true; + return; + } + + offset += 12; + } + + if (ifd_no === 0) { + this.ifds_to_read.push({ + id: 1, + offset: this.read_uint32(offset) + }); + } +}; + + +module.exports.ExifParser = ExifParser; + +// returns orientation stored in Exif (1-8), 0 if none was found, -1 if error +module.exports.get_orientation = function (data) { + var orientation = 0; + try { + new ExifParser(data, 0, data.length).each(function (entry) { + if (entry.ifd === 0 && entry.tag === 0x112 && Array.isArray(entry.value)) { + orientation = entry.value[0]; + return false; + } + }); + return orientation; + } catch (err) { + return -1; + } +}; + +},{}],83:[function(_dereq_,module,exports){ +// Utils used to parse miaf-based files (avif/heic/heif) +// +// ISO media file spec: +// https://web.archive.org/web/20180219054429/http://l.web.umkc.edu/lizhu/teaching/2016sp.video-communication/ref/mp4.pdf +// +// ISO image file format spec: +// https://standards.iso.org/ittf/PubliclyAvailableStandards/c066067_ISO_IEC_23008-12_2017.zip +// + +'use strict'; + +/* eslint-disable consistent-return */ +/* eslint-disable no-bitwise */ + +var readUInt16BE = _dereq_('./common').readUInt16BE; +var readUInt32BE = _dereq_('./common').readUInt32BE; + +/* + * interface Box { + * size: uint32; // if size == 0, box lasts until EOF + * boxtype: char[4]; + * largesize?: uint64; // only if size == 1 + * usertype?: char[16]; // only if boxtype == 'uuid' + * } + */ +function unbox(data, offset) { + if (data.length < 4 + offset) return null; + + var size = readUInt32BE(data, offset); + + // size includes first 4 bytes (length) + if (data.length < size + offset || size < 8) return null; + + // if size === 1, real size is following uint64 (only for big boxes, not needed) + // if size === 0, real size is until the end of the file (only for big boxes, not needed) + + return { + boxtype: String.fromCharCode.apply(null, data.slice(offset + 4, offset + 8)), + data: data.slice(offset + 8, offset + size), + end: offset + size + }; +} + + +module.exports.unbox = unbox; + + +// parses `meta` -> `iprp` -> `ipco` box, returns: +// { +// sizes: [ { width, height } ], +// transforms: [ { type, value } ] +// } +function scan_ipco(data, sandbox) { + var offset = 0; + + for (;;) { + var box = unbox(data, offset); + if (!box) break; + + switch (box.boxtype) { + case 'ispe': + sandbox.sizes.push({ + width: readUInt32BE(box.data, 4), + height: readUInt32BE(box.data, 8) + }); + break; + + case 'irot': + sandbox.transforms.push({ + type: 'irot', + value: box.data[0] & 3 + }); + break; + + case 'imir': + sandbox.transforms.push({ + type: 'imir', + value: box.data[0] & 1 + }); + break; + } + + offset = box.end; + } +} + + +function readUIntBE(data, offset, size) { + var result = 0; + + for (var i = 0; i < size; i++) { + result = result * 256 + (data[offset + i] || 0); + } + + return result; +} + + +// parses `meta` -> `iloc` box +function scan_iloc(data, sandbox) { + var offset_size = (data[4] >> 4) & 0xF; + var length_size = data[4] & 0xF; + var base_offset_size = (data[5] >> 4) & 0xF; + var item_count = readUInt16BE(data, 6); + var offset = 8; + + for (var i = 0; i < item_count; i++) { + var item_ID = readUInt16BE(data, offset); + offset += 2; + + var data_reference_index = readUInt16BE(data, offset); + offset += 2; + + var base_offset = readUIntBE(data, offset, base_offset_size); + offset += base_offset_size; + + var extent_count = readUInt16BE(data, offset); + offset += 2; + + if (data_reference_index === 0 && extent_count === 1) { + var first_extent_offset = readUIntBE(data, offset, offset_size); + var first_extent_length = readUIntBE(data, offset + offset_size, length_size); + sandbox.item_loc[item_ID] = { length: first_extent_length, offset: first_extent_offset + base_offset }; + } + + offset += extent_count * (offset_size + length_size); + } +} + + +// parses `meta` -> `iinf` box +function scan_iinf(data, sandbox) { + var item_count = readUInt16BE(data, 4); + var offset = 6; + + for (var i = 0; i < item_count; i++) { + var box = unbox(data, offset); + if (!box) break; + if (box.boxtype === 'infe') { + var item_id = readUInt16BE(box.data, 4); + var item_name = ''; + + for (var pos = 8; pos < box.data.length && box.data[pos]; pos++) { + item_name += String.fromCharCode(box.data[pos]); + } + + sandbox.item_inf[item_name] = item_id; + } + offset = box.end; + } +} + + +// parses `meta` -> `iprp` box +function scan_iprp(data, sandbox) { + var offset = 0; + + for (;;) { + var box = unbox(data, offset); + if (!box) break; + if (box.boxtype === 'ipco') scan_ipco(box.data, sandbox); + offset = box.end; + } +} + + +// parses `meta` box +function scan_meta(data, sandbox) { + var offset = 4; // version + flags + + for (;;) { + var box = unbox(data, offset); + if (!box) break; + if (box.boxtype === 'iprp') scan_iprp(box.data, sandbox); + if (box.boxtype === 'iloc') scan_iloc(box.data, sandbox); + if (box.boxtype === 'iinf') scan_iinf(box.data, sandbox); + offset = box.end; + } +} + + +// get image with largest single dimension as base +function getMaxSize(sizes) { + var maxWidthSize = sizes.reduce(function (a, b) { + return a.width > b.width || (a.width === b.width && a.height > b.height) ? a : b; + }); + + var maxHeightSize = sizes.reduce(function (a, b) { + return a.height > b.height || (a.height === b.height && a.width > b.width) ? a : b; + }); + + var maxSize; + + if (maxWidthSize.width > maxHeightSize.height || + (maxWidthSize.width === maxHeightSize.height && maxWidthSize.height > maxHeightSize.width)) { + maxSize = maxWidthSize; + } else { + maxSize = maxHeightSize; + } + + return maxSize; +} + + +module.exports.readSizeFromMeta = function (data) { + var sandbox = { + sizes: [], + transforms: [], + item_inf: {}, + item_loc: {} + }; + + scan_meta(data, sandbox); + + if (!sandbox.sizes.length) return; + + var maxSize = getMaxSize(sandbox.sizes); + + var orientation = 1; + + // convert imir/irot to exif orientation + sandbox.transforms.forEach(function (transform) { + var rotate_ccw = { 1: 6, 2: 5, 3: 8, 4: 7, 5: 4, 6: 3, 7: 2, 8: 1 }; + var mirror_vert = { 1: 4, 2: 3, 3: 2, 4: 1, 5: 6, 6: 5, 7: 8, 8: 7 }; + + if (transform.type === 'imir') { + if (transform.value === 0) { + // vertical flip + orientation = mirror_vert[orientation]; + } else { + // horizontal flip = vertical flip + 180 deg rotation + orientation = mirror_vert[orientation]; + orientation = rotate_ccw[orientation]; + orientation = rotate_ccw[orientation]; + } + } + + if (transform.type === 'irot') { + // counter-clockwise rotation 90 deg 0-3 times + for (var i = 0; i < transform.value; i++) { + orientation = rotate_ccw[orientation]; + } + } + }); + + var exif_location = null; + + if (sandbox.item_inf.Exif) { + exif_location = sandbox.item_loc[sandbox.item_inf.Exif]; + } + + return { + width: maxSize.width, + height: maxSize.height, + orientation: sandbox.transforms.length ? orientation : null, + variants: sandbox.sizes, + exif_location: exif_location + }; +}; + + +module.exports.getMimeType = function (data) { + var brand = String.fromCharCode.apply(null, data.slice(0, 4)); + var compat = {}; + + compat[brand] = true; + + for (var i = 8; i < data.length; i += 4) { + compat[String.fromCharCode.apply(null, data.slice(i, i + 4))] = true; + } + + // heic and avif are superset of miaf, so they should all list mif1 as compatible + if (!compat.mif1 && !compat.msf1 && !compat.miaf) return; + + if (brand === 'avif' || brand === 'avis' || brand === 'avio') { + // `.avifs` and `image/avif-sequence` are removed from spec, all files have single type + return { type: 'avif', mime: 'image/avif' }; + } + + // https://nokiatech.github.io/heif/technical.html + if (brand === 'heic' || brand === 'heix') { + return { type: 'heic', mime: 'image/heic' }; + } + + if (brand === 'hevc' || brand === 'hevx') { + return { type: 'heic', mime: 'image/heic-sequence' }; + } + + if (compat.avif || compat.avis) { + return { type: 'avif', mime: 'image/avif' }; + } + + if (compat.heic || compat.heix || compat.hevc || compat.hevx || compat.heis) { + if (compat.msf1) { + return { type: 'heif', mime: 'image/heif-sequence' }; + } + return { type: 'heif', mime: 'image/heif' }; + } + + return { type: 'avif', mime: 'image/avif' }; +}; + +},{"./common":81}],84:[function(_dereq_,module,exports){ +// Utils used to parse miaf-based files (avif/heic/heif) +// +// - image collections are not supported (only last size is reported) +// - images with metadata encoded after image data are not supported +// - images without any `ispe` box are not supported +// + +/* eslint-disable consistent-return */ + +'use strict'; + + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt32BE = _dereq_('../common').readUInt32BE; +var miaf = _dereq_('../miaf_utils'); +var exif = _dereq_('../exif_utils'); + +var SIG_FTYP = str2arr('ftyp'); + + +module.exports = function (data) { + // ISO media file (avif format) starts with ftyp box: + // 0000 0020 6674 7970 6176 6966 + // (length) f t y p a v i f + // + if (!sliceEq(data, 4, SIG_FTYP)) return; + + var firstBox = miaf.unbox(data, 0); + if (!firstBox) return; + + var fileType = miaf.getMimeType(firstBox.data); + if (!fileType) return; + + var meta, offset = firstBox.end; + + for (;;) { + var box = miaf.unbox(data, offset); + if (!box) break; + offset = box.end; + + // mdat block SHOULD be last (but not strictly required), + // so it's unlikely that metadata is after it + if (box.boxtype === 'mdat') return; + if (box.boxtype === 'meta') { + meta = box.data; + break; + } + } + + if (!meta) return; + + var imgSize = miaf.readSizeFromMeta(meta); + + if (!imgSize) return; + + var result = { + width: imgSize.width, + height: imgSize.height, + type: fileType.type, + mime: fileType.mime, + wUnits: 'px', + hUnits: 'px' + }; + + if (imgSize.variants.length > 1) { + result.variants = imgSize.variants; + } + + if (imgSize.orientation) { + result.orientation = imgSize.orientation; + } + + if (imgSize.exif_location && + imgSize.exif_location.offset + imgSize.exif_location.length <= data.length) { + + var sig_offset = readUInt32BE(data, imgSize.exif_location.offset); + var exif_data = data.slice( + imgSize.exif_location.offset + sig_offset + 4, + imgSize.exif_location.offset + imgSize.exif_location.length); + + var orientation = exif.get_orientation(exif_data); + + if (orientation > 0) result.orientation = orientation; + } + + return result; +}; + +},{"../common":81,"../exif_utils":82,"../miaf_utils":83}],85:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt16LE = _dereq_('../common').readUInt16LE; + +var SIG_BM = str2arr('BM'); + + +module.exports = function (data) { + if (data.length < 26) return; + + if (!sliceEq(data, 0, SIG_BM)) return; + + return { + width: readUInt16LE(data, 18), + height: readUInt16LE(data, 22), + type: 'bmp', + mime: 'image/bmp', + wUnits: 'px', + hUnits: 'px' + }; +}; + +},{"../common":81}],86:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt16LE = _dereq_('../common').readUInt16LE; + + +var SIG_GIF87a = str2arr('GIF87a'); +var SIG_GIF89a = str2arr('GIF89a'); + + +module.exports = function (data) { + if (data.length < 10) return; + + if (!sliceEq(data, 0, SIG_GIF87a) && !sliceEq(data, 0, SIG_GIF89a)) return; + + return { + width: readUInt16LE(data, 6), + height: readUInt16LE(data, 8), + type: 'gif', + mime: 'image/gif', + wUnits: 'px', + hUnits: 'px' + }; +}; + +},{"../common":81}],87:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var readUInt16LE = _dereq_('../common').readUInt16LE; + +var HEADER = 0; +var TYPE_ICO = 1; +var INDEX_SIZE = 16; + +// Format specification: +// https://en.wikipedia.org/wiki/ICO_(file_format)#Icon_resource_structure +module.exports = function (data) { + var header = readUInt16LE(data, 0); + var type = readUInt16LE(data, 2); + var numImages = readUInt16LE(data, 4); + + if (header !== HEADER || type !== TYPE_ICO || !numImages) { + return; + } + + var variants = []; + var maxSize = { width: 0, height: 0 }; + + for (var i = 0; i < numImages; i++) { + var width = data[6 + INDEX_SIZE * i] || 256; + var height = data[6 + INDEX_SIZE * i + 1] || 256; + var size = { width: width, height: height }; + variants.push(size); + + if (width > maxSize.width || height > maxSize.height) { + maxSize = size; + } + } + + return { + width: maxSize.width, + height: maxSize.height, + variants: variants, + type: 'ico', + mime: 'image/x-icon', + wUnits: 'px', + hUnits: 'px' + }; +}; + +},{"../common":81}],88:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var readUInt16BE = _dereq_('../common').readUInt16BE; +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var exif = _dereq_('../exif_utils'); + + +var SIG_EXIF = str2arr('Exif\0\0'); + + +module.exports = function (data) { + if (data.length < 2) return; + + // first marker of the file MUST be 0xFFD8 + if (data[0] !== 0xFF || data[1] !== 0xD8) return; + + var offset = 2; + + for (;;) { + if (data.length - offset < 2) return; + // not a JPEG marker + if (data[offset++] !== 0xFF) return; + + var code = data[offset++]; + var length; + + // skip padding bytes + while (code === 0xFF) code = data[offset++]; + + // standalone markers, according to JPEG 1992, + // http://www.w3.org/Graphics/JPEG/itu-t81.pdf, see Table B.1 + if ((0xD0 <= code && code <= 0xD9) || code === 0x01) { + length = 0; + } else if (0xC0 <= code && code <= 0xFE) { + // the rest of the unreserved markers + if (data.length - offset < 2) return; + + length = readUInt16BE(data, offset) - 2; + offset += 2; + } else { + // unknown markers + return; + } + + if (code === 0xD9 /* EOI */ || code === 0xDA /* SOS */) { + // end of the datastream + return; + } + + var orientation; + + // try to get orientation from Exif segment + if (code === 0xE1 && length >= 10 && sliceEq(data, offset, SIG_EXIF)) { + orientation = exif.get_orientation(data.slice(offset + 6, offset + length)); + } + + if (length >= 5 && + (0xC0 <= code && code <= 0xCF) && + code !== 0xC4 && code !== 0xC8 && code !== 0xCC) { + + if (data.length - offset < length) return; + + var result = { + width: readUInt16BE(data, offset + 3), + height: readUInt16BE(data, offset + 1), + type: 'jpg', + mime: 'image/jpeg', + wUnits: 'px', + hUnits: 'px' + }; + + if (orientation > 0) { + result.orientation = orientation; + } + + return result; + } + + offset += length; + } +}; + +},{"../common":81,"../exif_utils":82}],89:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt32BE = _dereq_('../common').readUInt32BE; + + +var SIG_PNG = str2arr('\x89PNG\r\n\x1a\n'); +var SIG_IHDR = str2arr('IHDR'); + + +module.exports = function (data) { + if (data.length < 24) return; + + // check PNG signature + if (!sliceEq(data, 0, SIG_PNG)) return; + + // check that first chunk is IHDR + if (!sliceEq(data, 12, SIG_IHDR)) return; + + return { + width: readUInt32BE(data, 16), + height: readUInt32BE(data, 20), + type: 'png', + mime: 'image/png', + wUnits: 'px', + hUnits: 'px' + }; +}; + +},{"../common":81}],90:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt32BE = _dereq_('../common').readUInt32BE; + + +var SIG_8BPS = str2arr('8BPS\x00\x01'); + + +module.exports = function (data) { + if (data.length < 6 + 16) return; + + // signature + version + if (!sliceEq(data, 0, SIG_8BPS)) return; + + return { + width: readUInt32BE(data, 6 + 12), + height: readUInt32BE(data, 6 + 8), + type: 'psd', + mime: 'image/vnd.adobe.photoshop', + wUnits: 'px', + hUnits: 'px' + }; +}; + +},{"../common":81}],91:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +function isWhiteSpace(chr) { + return chr === 0x20 || chr === 0x09 || chr === 0x0D || chr === 0x0A; +} + +// Filter NaN, Infinity, < 0 +function isFinitePositive(val) { + return typeof val === 'number' && isFinite(val) && val > 0; +} + +function canBeSvg(buf) { + var i = 0, max = buf.length; + + // byte order mark, https://github.com/nodeca/probe-image-size/issues/57 + if (buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF) i = 3; + + while (i < max && isWhiteSpace(buf[i])) i++; + + if (i === max) return false; + return buf[i] === 0x3c; /* < */ +} + + +// skip `` or `` +var SVG_HEADER_RE = /<[-_.:a-zA-Z0-9][^>]*>/; + +// test if the top level element is svg + optional namespace, +// used to skip svg embedded in html +var SVG_TAG_RE = /^<([-_.:a-zA-Z0-9]+:)?svg\s/; + +var SVG_WIDTH_RE = /[^-]\bwidth="([^%]+?)"|[^-]\bwidth='([^%]+?)'/; +var SVG_HEIGHT_RE = /\bheight="([^%]+?)"|\bheight='([^%]+?)'/; +var SVG_VIEWBOX_RE = /\bview[bB]ox="(.+?)"|\bview[bB]ox='(.+?)'/; +var SVG_UNITS_RE = /in$|mm$|cm$|pt$|pc$|px$|em$|ex$/; + +function svgAttrs(str) { + var width = str.match(SVG_WIDTH_RE); + var height = str.match(SVG_HEIGHT_RE); + var viewbox = str.match(SVG_VIEWBOX_RE); + + return { + width: width && (width[1] || width[2]), + height: height && (height[1] || height[2]), + viewbox: viewbox && (viewbox[1] || viewbox[2]) + }; +} + + +function units(str) { + if (!SVG_UNITS_RE.test(str)) return 'px'; + + return str.match(SVG_UNITS_RE)[0]; +} + + +module.exports = function (data) { + if (!canBeSvg(data)) return; + + var str = ''; + + for (var i = 0; i < data.length; i++) { + // 1. We can't rely on buffer features + // 2. Don't care about UTF16 because ascii is enougth for our goals + str += String.fromCharCode(data[i]); + } + + // get top level element + var svgTag = (str.match(SVG_HEADER_RE) || [ '' ])[0]; + + // test if top level element is + if (!SVG_TAG_RE.test(svgTag)) return; + + var attrs = svgAttrs(svgTag); + var width = parseFloat(attrs.width); + var height = parseFloat(attrs.height); + + // Extract from direct values + + if (attrs.width && attrs.height) { + if (!isFinitePositive(width) || !isFinitePositive(height)) return; + + return { + width: width, + height: height, + type: 'svg', + mime: 'image/svg+xml', + wUnits: units(attrs.width), + hUnits: units(attrs.height) + }; + } + + // Extract from viewbox + + var parts = (attrs.viewbox || '').split(' '); + var viewbox = { + width: parts[2], + height: parts[3] + }; + var vbWidth = parseFloat(viewbox.width); + var vbHeight = parseFloat(viewbox.height); + + if (!isFinitePositive(vbWidth) || !isFinitePositive(vbHeight)) return; + if (units(viewbox.width) !== units(viewbox.height)) return; + + var ratio = vbWidth / vbHeight; + + if (attrs.width) { + if (!isFinitePositive(width)) return; + + return { + width: width, + height: width / ratio, + type: 'svg', + mime: 'image/svg+xml', + wUnits: units(attrs.width), + hUnits: units(attrs.width) + }; + } + + if (attrs.height) { + if (!isFinitePositive(height)) return; + + return { + width: height * ratio, + height: height, + type: 'svg', + mime: 'image/svg+xml', + wUnits: units(attrs.height), + hUnits: units(attrs.height) + }; + } + + return { + width: vbWidth, + height: vbHeight, + type: 'svg', + mime: 'image/svg+xml', + wUnits: units(viewbox.width), + hUnits: units(viewbox.height) + }; +}; + +},{}],92:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable consistent-return */ + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt16LE = _dereq_('../common').readUInt16LE; +var readUInt16BE = _dereq_('../common').readUInt16BE; +var readUInt32LE = _dereq_('../common').readUInt32LE; +var readUInt32BE = _dereq_('../common').readUInt32BE; + + +var SIG_1 = str2arr('II\x2A\0'); +var SIG_2 = str2arr('MM\0\x2A'); + + +function readUInt16(buffer, offset, is_big_endian) { + return is_big_endian ? readUInt16BE(buffer, offset) : readUInt16LE(buffer, offset); +} + +function readUInt32(buffer, offset, is_big_endian) { + return is_big_endian ? readUInt32BE(buffer, offset) : readUInt32LE(buffer, offset); +} + +function readIFDValue(data, data_offset, is_big_endian) { + var type = readUInt16(data, data_offset + 2, is_big_endian); + var values = readUInt32(data, data_offset + 4, is_big_endian); + + if (values !== 1 || (type !== 3 && type !== 4)) return null; + + if (type === 3) { + return readUInt16(data, data_offset + 8, is_big_endian); + } + + return readUInt32(data, data_offset + 8, is_big_endian); +} + +module.exports = function (data) { + if (data.length < 8) return; + + // check TIFF signature + if (!sliceEq(data, 0, SIG_1) && !sliceEq(data, 0, SIG_2)) return; + + var is_big_endian = (data[0] === 77 /* 'MM' */); + var count = readUInt32(data, 4, is_big_endian) - 8; + + if (count < 0) return; + + // skip until IFD + var offset = count + 8; + + if (data.length - offset < 2) return; + + // read number of IFD entries + var ifd_size = readUInt16(data, offset + 0, is_big_endian) * 12; + + if (ifd_size <= 0) return; + + offset += 2; + + // read all IFD entries + if (data.length - offset < ifd_size) return; + + var i, width, height, tag; + + for (i = 0; i < ifd_size; i += 12) { + tag = readUInt16(data, offset + i, is_big_endian); + + if (tag === 256) { + width = readIFDValue(data, offset + i, is_big_endian); + } else if (tag === 257) { + height = readIFDValue(data, offset + i, is_big_endian); + } + } + + if (width && height) { + return { + width: width, + height: height, + type: 'tiff', + mime: 'image/tiff', + wUnits: 'px', + hUnits: 'px' + }; + } +}; + +},{"../common":81}],93:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable no-bitwise */ +/* eslint-disable consistent-return */ + +var str2arr = _dereq_('../common').str2arr; +var sliceEq = _dereq_('../common').sliceEq; +var readUInt16LE = _dereq_('../common').readUInt16LE; +var readUInt32LE = _dereq_('../common').readUInt32LE; +var exif = _dereq_('../exif_utils'); + + +var SIG_RIFF = str2arr('RIFF'); +var SIG_WEBP = str2arr('WEBP'); + + +function parseVP8(data, offset) { + if (data[offset + 3] !== 0x9D || data[offset + 4] !== 0x01 || data[offset + 5] !== 0x2A) { + // bad code block signature + return; + } + + return { + width: readUInt16LE(data, offset + 6) & 0x3FFF, + height: readUInt16LE(data, offset + 8) & 0x3FFF, + type: 'webp', + mime: 'image/webp', + wUnits: 'px', + hUnits: 'px' + }; +} + + +function parseVP8L(data, offset) { + if (data[offset] !== 0x2F) return; + + var bits = readUInt32LE(data, offset + 1); + + return { + width: (bits & 0x3FFF) + 1, + height: ((bits >> 14) & 0x3FFF) + 1, + type: 'webp', + mime: 'image/webp', + wUnits: 'px', + hUnits: 'px' + }; +} + + +function parseVP8X(data, offset) { + return { + // TODO: replace with `data.readUIntLE(8, 3) + 1` + // when 0.10 support is dropped + width: ((data[offset + 6] << 16) | (data[offset + 5] << 8) | data[offset + 4]) + 1, + height: ((data[offset + 9] << offset) | (data[offset + 8] << 8) | data[offset + 7]) + 1, + type: 'webp', + mime: 'image/webp', + wUnits: 'px', + hUnits: 'px' + }; +} + + +module.exports = function (data) { + if (data.length < 16) return; + + // check /^RIFF....WEBPVP8([ LX])$/ signature + if (!sliceEq(data, 0, SIG_RIFF) && !sliceEq(data, 8, SIG_WEBP)) return; + + var offset = 12; + var result = null; + var exif_orientation = 0; + var fileLength = readUInt32LE(data, 4) + 8; + + if (fileLength > data.length) return; + + while (offset + 8 < fileLength) { + if (data[offset] === 0) { + // after each chunk of odd size there should be 0 byte of padding, skip those + offset++; + continue; + } + + var header = String.fromCharCode.apply(null, data.slice(offset, offset + 4)); + var length = readUInt32LE(data, offset + 4); + + if (header === 'VP8 ' && length >= 10) { + result = result || parseVP8(data, offset + 8); + } else if (header === 'VP8L' && length >= 9) { + result = result || parseVP8L(data, offset + 8); + } else if (header === 'VP8X' && length >= 10) { + result = result || parseVP8X(data, offset + 8); + } else if (header === 'EXIF') { + exif_orientation = exif.get_orientation(data.slice(offset + 8, offset + 8 + length)); + + // exif is the last chunk we care about, stop after it + offset = Infinity; + } + + offset += 8 + length; + } + + if (!result) return; + + if (exif_orientation > 0) { + result.orientation = exif_orientation; + } + + return result; +}; + +},{"../common":81,"../exif_utils":82}],94:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + avif: _dereq_('./parse_sync/avif'), + bmp: _dereq_('./parse_sync/bmp'), + gif: _dereq_('./parse_sync/gif'), + ico: _dereq_('./parse_sync/ico'), + jpeg: _dereq_('./parse_sync/jpeg'), + png: _dereq_('./parse_sync/png'), + psd: _dereq_('./parse_sync/psd'), + svg: _dereq_('./parse_sync/svg'), + tiff: _dereq_('./parse_sync/tiff'), + webp: _dereq_('./parse_sync/webp') +}; + +},{"./parse_sync/avif":84,"./parse_sync/bmp":85,"./parse_sync/gif":86,"./parse_sync/ico":87,"./parse_sync/jpeg":88,"./parse_sync/png":89,"./parse_sync/psd":90,"./parse_sync/svg":91,"./parse_sync/tiff":92,"./parse_sync/webp":93}],95:[function(_dereq_,module,exports){ +'use strict'; + + +var parsers = _dereq_('./lib/parsers_sync'); + + +function probeBuffer(buffer) { + var parser_names = Object.keys(parsers); + + for (var i = 0; i < parser_names.length; i++) { + var result = parsers[parser_names[i]](buffer); + + if (result) return result; + } + + return null; +} + + +/////////////////////////////////////////////////////////////////////// +// Exports +// + +module.exports = function get_image_size(src) { + return probeBuffer(src); +}; + +module.exports.parsers = parsers; + +},{"./lib/parsers_sync":94}],96:[function(_dereq_,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],97:[function(_dereq_,module,exports){ +/* eslint-disable node/no-deprecated-api */ +var buffer = _dereq_('buffer') +var Buffer = buffer.Buffer + +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} + +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.prototype = Object.create(Buffer.prototype) + +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} + +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf +} + +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} + +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} + +},{"buffer":28}],98:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Stream; + +var EE = _dereq_('events').EventEmitter; +var inherits = _dereq_('inherits'); + +inherits(Stream, EE); +Stream.Readable = _dereq_('readable-stream/lib/_stream_readable.js'); +Stream.Writable = _dereq_('readable-stream/lib/_stream_writable.js'); +Stream.Duplex = _dereq_('readable-stream/lib/_stream_duplex.js'); +Stream.Transform = _dereq_('readable-stream/lib/_stream_transform.js'); +Stream.PassThrough = _dereq_('readable-stream/lib/_stream_passthrough.js'); +Stream.finished = _dereq_('readable-stream/lib/internal/streams/end-of-stream.js') +Stream.pipeline = _dereq_('readable-stream/lib/internal/streams/pipeline.js') + +// Backwards-compat with node 0.4.x +Stream.Stream = Stream; + + + +// old-style streams. Note that the pipe method (the only relevant +// part of this class) is overridden in the Readable class. + +function Stream() { + EE.call(this); +} + +Stream.prototype.pipe = function(dest, options) { + var source = this; + + function ondata(chunk) { + if (dest.writable) { + if (false === dest.write(chunk) && source.pause) { + source.pause(); + } + } + } + + source.on('data', ondata); + + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + + dest.on('drain', ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); + } + + var didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); + } + + + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, 'error') === 0) { + throw er; // Unhandled stream error in pipe. + } + } + + source.on('error', onerror); + dest.on('error', onerror); + + // remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); + + source.removeListener('end', onend); + source.removeListener('close', onclose); + + source.removeListener('error', onerror); + dest.removeListener('error', onerror); + + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); + + dest.removeListener('close', cleanup); + } + + source.on('end', cleanup); + source.on('close', cleanup); + + dest.on('close', cleanup); + + dest.emit('pipe', source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; + +},{"events":27,"inherits":65,"readable-stream/lib/_stream_duplex.js":100,"readable-stream/lib/_stream_passthrough.js":101,"readable-stream/lib/_stream_readable.js":102,"readable-stream/lib/_stream_transform.js":103,"readable-stream/lib/_stream_writable.js":104,"readable-stream/lib/internal/streams/end-of-stream.js":108,"readable-stream/lib/internal/streams/pipeline.js":110}],99:[function(_dereq_,module,exports){ +'use strict'; + +function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } + +var codes = {}; + +function createErrorType(code, message, Base) { + if (!Base) { + Base = Error; + } + + function getMessage(arg1, arg2, arg3) { + if (typeof message === 'string') { + return message; + } else { + return message(arg1, arg2, arg3); + } + } + + var NodeError = + /*#__PURE__*/ + function (_Base) { + _inheritsLoose(NodeError, _Base); + + function NodeError(arg1, arg2, arg3) { + return _Base.call(this, getMessage(arg1, arg2, arg3)) || this; + } + + return NodeError; + }(Base); + + NodeError.prototype.name = Base.name; + NodeError.prototype.code = code; + codes[code] = NodeError; +} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js + + +function oneOf(expected, thing) { + if (Array.isArray(expected)) { + var len = expected.length; + expected = expected.map(function (i) { + return String(i); + }); + + if (len > 2) { + return "one of ".concat(thing, " ").concat(expected.slice(0, len - 1).join(', '), ", or ") + expected[len - 1]; + } else if (len === 2) { + return "one of ".concat(thing, " ").concat(expected[0], " or ").concat(expected[1]); + } else { + return "of ".concat(thing, " ").concat(expected[0]); + } + } else { + return "of ".concat(thing, " ").concat(String(expected)); + } +} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith + + +function startsWith(str, search, pos) { + return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; +} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith + + +function endsWith(str, search, this_len) { + if (this_len === undefined || this_len > str.length) { + this_len = str.length; + } + + return str.substring(this_len - search.length, this_len) === search; +} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes + + +function includes(str, search, start) { + if (typeof start !== 'number') { + start = 0; + } + + if (start + search.length > str.length) { + return false; + } else { + return str.indexOf(search, start) !== -1; + } +} + +createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) { + return 'The value "' + value + '" is invalid for option "' + name + '"'; +}, TypeError); +createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) { + // determiner: 'must be' or 'must not be' + var determiner; + + if (typeof expected === 'string' && startsWith(expected, 'not ')) { + determiner = 'must not be'; + expected = expected.replace(/^not /, ''); + } else { + determiner = 'must be'; + } + + var msg; + + if (endsWith(name, ' argument')) { + // For cases like 'first argument' + msg = "The ".concat(name, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); + } else { + var type = includes(name, '.') ? 'property' : 'argument'; + msg = "The \"".concat(name, "\" ").concat(type, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); + } + + msg += ". Received type ".concat(typeof actual); + return msg; +}, TypeError); +createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF'); +createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) { + return 'The ' + name + ' method is not implemented'; +}); +createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close'); +createErrorType('ERR_STREAM_DESTROYED', function (name) { + return 'Cannot call ' + name + ' after a stream was destroyed'; +}); +createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times'); +createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable'); +createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end'); +createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); +createErrorType('ERR_UNKNOWN_ENCODING', function (arg) { + return 'Unknown encoding: ' + arg; +}, TypeError); +createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); +module.exports.codes = codes; + +},{}],100:[function(_dereq_,module,exports){ +(function (process){(function (){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. +'use strict'; +/**/ + +var objectKeys = Object.keys || function (obj) { + var keys = []; + + for (var key in obj) { + keys.push(key); + } + + return keys; +}; +/**/ + + +module.exports = Duplex; + +var Readable = _dereq_('./_stream_readable'); + +var Writable = _dereq_('./_stream_writable'); + +_dereq_('inherits')(Duplex, Readable); + +{ + // Allow the keys array to be GC'ed. + var keys = objectKeys(Writable.prototype); + + for (var v = 0; v < keys.length; v++) { + var method = keys[v]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; + } +} + +function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); + Readable.call(this, options); + Writable.call(this, options); + this.allowHalfOpen = true; + + if (options) { + if (options.readable === false) this.readable = false; + if (options.writable === false) this.writable = false; + + if (options.allowHalfOpen === false) { + this.allowHalfOpen = false; + this.once('end', onend); + } + } +} + +Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.highWaterMark; + } +}); +Object.defineProperty(Duplex.prototype, 'writableBuffer', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState && this._writableState.getBuffer(); + } +}); +Object.defineProperty(Duplex.prototype, 'writableLength', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.length; + } +}); // the no-half-open enforcer + +function onend() { + // If the writable side ended, then we're ok. + if (this._writableState.ended) return; // no more data can be written. + // But allow more writes to happen in this tick. + + process.nextTick(onEndNT, this); +} + +function onEndNT(self) { + self.end(); +} + +Object.defineProperty(Duplex.prototype, 'destroyed', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + if (this._readableState === undefined || this._writableState === undefined) { + return false; + } + + return this._readableState.destroyed && this._writableState.destroyed; + }, + set: function set(value) { + // we ignore the value if the stream + // has not been initialized yet + if (this._readableState === undefined || this._writableState === undefined) { + return; + } // backward compatibility, the user is explicitly + // managing destroyed + + + this._readableState.destroyed = value; + this._writableState.destroyed = value; + } +}); +}).call(this)}).call(this,_dereq_('_process')) +},{"./_stream_readable":102,"./_stream_writable":104,"_process":96,"inherits":65}],101:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. +'use strict'; + +module.exports = PassThrough; + +var Transform = _dereq_('./_stream_transform'); + +_dereq_('inherits')(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); + Transform.call(this, options); +} + +PassThrough.prototype._transform = function (chunk, encoding, cb) { + cb(null, chunk); +}; +},{"./_stream_transform":103,"inherits":65}],102:[function(_dereq_,module,exports){ +(function (process,global){(function (){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +'use strict'; + +module.exports = Readable; +/**/ + +var Duplex; +/**/ + +Readable.ReadableState = ReadableState; +/**/ + +var EE = _dereq_('events').EventEmitter; + +var EElistenerCount = function EElistenerCount(emitter, type) { + return emitter.listeners(type).length; +}; +/**/ + +/**/ + + +var Stream = _dereq_('./internal/streams/stream'); +/**/ + + +var Buffer = _dereq_('buffer').Buffer; + +var OurUint8Array = global.Uint8Array || function () {}; + +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} + +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} +/**/ + + +var debugUtil = _dereq_('util'); + +var debug; + +if (debugUtil && debugUtil.debuglog) { + debug = debugUtil.debuglog('stream'); +} else { + debug = function debug() {}; +} +/**/ + + +var BufferList = _dereq_('./internal/streams/buffer_list'); + +var destroyImpl = _dereq_('./internal/streams/destroy'); + +var _require = _dereq_('./internal/streams/state'), + getHighWaterMark = _require.getHighWaterMark; + +var _require$codes = _dereq_('../errors').codes, + ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, + ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, + ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, + ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance. + + +var StringDecoder; +var createReadableStreamAsyncIterator; +var from; + +_dereq_('inherits')(Readable, Stream); + +var errorOrDestroy = destroyImpl.errorOrDestroy; +var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; + +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + + if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; +} + +function ReadableState(options, stream, isDuplex) { + Duplex = Duplex || _dereq_('./_stream_duplex'); + options = options || {}; // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + + if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + + this.objectMode = !!options.objectMode; + if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + + this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + + this.buffer = new BufferList(); + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + + this.sync = true; // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; + this.paused = true; // Should close be emitted on destroy. Defaults to true. + + this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish') + + this.autoDestroy = !!options.autoDestroy; // has it been destroyed + + this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + + this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s + + this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled + + this.readingMore = false; + this.decoder = null; + this.encoding = null; + + if (options.encoding) { + if (!StringDecoder) StringDecoder = _dereq_('string_decoder/').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + Duplex = Duplex || _dereq_('./_stream_duplex'); + if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside + // the ReadableState constructor, at least with V8 6.5 + + var isDuplex = this instanceof Duplex; + this._readableState = new ReadableState(options, this, isDuplex); // legacy + + this.readable = true; + + if (options) { + if (typeof options.read === 'function') this._read = options.read; + if (typeof options.destroy === 'function') this._destroy = options.destroy; + } + + Stream.call(this); +} + +Object.defineProperty(Readable.prototype, 'destroyed', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + if (this._readableState === undefined) { + return false; + } + + return this._readableState.destroyed; + }, + set: function set(value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._readableState) { + return; + } // backward compatibility, the user is explicitly + // managing destroyed + + + this._readableState.destroyed = value; + } +}); +Readable.prototype.destroy = destroyImpl.destroy; +Readable.prototype._undestroy = destroyImpl.undestroy; + +Readable.prototype._destroy = function (err, cb) { + cb(err); +}; // Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. + + +Readable.prototype.push = function (chunk, encoding) { + var state = this._readableState; + var skipChunkCheck; + + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding = encoding || state.defaultEncoding; + + if (encoding !== state.encoding) { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + + skipChunkCheck = true; + } + } else { + skipChunkCheck = true; + } + + return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); +}; // Unshift should *always* be something directly out of read() + + +Readable.prototype.unshift = function (chunk) { + return readableAddChunk(this, chunk, null, true, false); +}; + +function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { + debug('readableAddChunk', chunk); + var state = stream._readableState; + + if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else { + var er; + if (!skipChunkCheck) er = chunkInvalid(state, chunk); + + if (er) { + errorOrDestroy(stream, er); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (addToFront) { + if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); + } else if (state.ended) { + errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); + } else if (state.destroyed) { + return false; + } else { + state.reading = false; + + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + maybeReadMore(stream, state); + } + } // We can push more data if we are below the highWaterMark. + // Also, if we have no data yet, we can stand some more bytes. + // This is to work around cases where hwm=0, such as the repl. + + + return !state.ended && (state.length < state.highWaterMark || state.length === 0); +} + +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync) { + state.awaitDrain = 0; + stream.emit('data', chunk); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); + if (state.needReadable) emitReadable(stream); + } + + maybeReadMore(stream, state); +} + +function chunkInvalid(state, chunk) { + var er; + + if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { + er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); + } + + return er; +} + +Readable.prototype.isPaused = function () { + return this._readableState.flowing === false; +}; // backwards compatibility. + + +Readable.prototype.setEncoding = function (enc) { + if (!StringDecoder) StringDecoder = _dereq_('string_decoder/').StringDecoder; + var decoder = new StringDecoder(enc); + this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8 + + this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers: + + var p = this._readableState.buffer.head; + var content = ''; + + while (p !== null) { + content += decoder.write(p.data); + p = p.next; + } + + this._readableState.buffer.clear(); + + if (content !== '') this._readableState.buffer.push(content); + this._readableState.length = content.length; + return this; +}; // Don't raise the hwm > 1GB + + +var MAX_HWM = 0x40000000; + +function computeNewHighWaterMark(n) { + if (n >= MAX_HWM) { + // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. + n = MAX_HWM; + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + + return n; +} // This function is designed to be inlinable, so please take care when making +// changes to the function body. + + +function howMuchToRead(n, state) { + if (n <= 0 || state.length === 0 && state.ended) return 0; + if (state.objectMode) return 1; + + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; + } // If we're asking for more than the current hwm, then raise the hwm. + + + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n <= state.length) return n; // Don't have enough + + if (!state.ended) { + state.needReadable = true; + return 0; + } + + return state.length; +} // you can override either this method, or the async _read(n) below. + + +Readable.prototype.read = function (n) { + debug('read', n); + n = parseInt(n, 10); + var state = this._readableState; + var nOrig = n; + if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + + if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. + + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; + } // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + // if we need a readable event, then we need to do some reading. + + + var doRead = state.needReadable; + debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some + + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + + + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } else if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; // if the length is currently zero, then we *need* a readable event. + + if (state.length === 0) state.needReadable = true; // call internal read method + + this._read(state.highWaterMark); + + state.sync = false; // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + + if (!state.reading) n = howMuchToRead(nOrig, state); + } + + var ret; + if (n > 0) ret = fromList(n, state);else ret = null; + + if (ret === null) { + state.needReadable = state.length <= state.highWaterMark; + n = 0; + } else { + state.length -= n; + state.awaitDrain = 0; + } + + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. + + if (nOrig !== n && state.ended) endReadable(this); + } + + if (ret !== null) this.emit('data', ret); + return ret; +}; + +function onEofChunk(stream, state) { + debug('onEofChunk'); + if (state.ended) return; + + if (state.decoder) { + var chunk = state.decoder.end(); + + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + + state.ended = true; + + if (state.sync) { + // if we are sync, wait until next tick to emit the data. + // Otherwise we risk emitting data in the flow() + // the readable code triggers during a read() call + emitReadable(stream); + } else { + // emit 'readable' now to make sure it gets picked up. + state.needReadable = false; + + if (!state.emittedReadable) { + state.emittedReadable = true; + emitReadable_(stream); + } + } +} // Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. + + +function emitReadable(stream) { + var state = stream._readableState; + debug('emitReadable', state.needReadable, state.emittedReadable); + state.needReadable = false; + + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + process.nextTick(emitReadable_, stream); + } +} + +function emitReadable_(stream) { + var state = stream._readableState; + debug('emitReadable_', state.destroyed, state.length, state.ended); + + if (!state.destroyed && (state.length || state.ended)) { + stream.emit('readable'); + state.emittedReadable = false; + } // The stream needs another readable event if + // 1. It is not flowing, as the flow mechanism will take + // care of it. + // 2. It is not ended. + // 3. It is below the highWaterMark, so we can schedule + // another readable later. + + + state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; + flow(stream); +} // at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. + + +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + process.nextTick(maybeReadMore_, stream, state); + } +} + +function maybeReadMore_(stream, state) { + // Attempt to read more data if we should. + // + // The conditions for reading more data are (one of): + // - Not enough data buffered (state.length < state.highWaterMark). The loop + // is responsible for filling the buffer with enough data if such data + // is available. If highWaterMark is 0 and we are not in the flowing mode + // we should _not_ attempt to buffer any extra data. We'll get more data + // when the stream consumer calls read() instead. + // - No data in the buffer, and the stream is in flowing mode. In this mode + // the loop below is responsible for ensuring read() is called. Failing to + // call read here would abort the flow and there's no other mechanism for + // continuing the flow if the stream consumer has just subscribed to the + // 'data' event. + // + // In addition to the above conditions to keep reading data, the following + // conditions prevent the data from being read: + // - The stream has ended (state.ended). + // - There is already a pending 'read' operation (state.reading). This is a + // case where the the stream has called the implementation defined _read() + // method, but they are processing the call asynchronously and have _not_ + // called push() with new data. In this case we skip performing more + // read()s. The execution ends in this method again after the _read() ends + // up calling push() with more data. + while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { + var len = state.length; + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) // didn't get any data, stop spinning. + break; + } + + state.readingMore = false; +} // abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. + + +Readable.prototype._read = function (n) { + errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); +}; + +Readable.prototype.pipe = function (dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + + case 1: + state.pipes = [state.pipes, dest]; + break; + + default: + state.pipes.push(dest); + break; + } + + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; + var endFn = doEnd ? onend : unpipe; + if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); + dest.on('unpipe', onunpipe); + + function onunpipe(readable, unpipeInfo) { + debug('onunpipe'); + + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } + } + + function onend() { + debug('onend'); + dest.end(); + } // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + + + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + var cleanedUp = false; + + function cleanup() { + debug('cleanup'); // cleanup event handlers once the pipe is broken + + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); + cleanedUp = true; // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + + if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); + } + + src.on('data', ondata); + + function ondata(chunk) { + debug('ondata'); + var ret = dest.write(chunk); + debug('dest.write', ret); + + if (ret === false) { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { + debug('false write response, pause', state.awaitDrain); + state.awaitDrain++; + } + + src.pause(); + } + } // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + + + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); + } // Make sure our error handler is attached before userland ones. + + + prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. + + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + + dest.once('close', onclose); + + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } // tell the dest that it's being piped to + + + dest.emit('pipe', src); // start the flow if it hasn't been started already. + + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function pipeOnDrainFunctionResult() { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) state.awaitDrain--; + + if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + +Readable.prototype.unpipe = function (dest) { + var state = this._readableState; + var unpipeInfo = { + hasUnpiped: false + }; // if we're not piping anywhere, then do nothing. + + if (state.pipesCount === 0) return this; // just one destination. most common case. + + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) return this; + if (!dest) dest = state.pipes; // got a match. + + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) dest.emit('unpipe', this, unpipeInfo); + return this; + } // slow case. multiple pipe destinations. + + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) { + dests[i].emit('unpipe', this, { + hasUnpiped: false + }); + } + + return this; + } // try to find the right one. + + + var index = indexOf(state.pipes, dest); + if (index === -1) return this; + state.pipes.splice(index, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) state.pipes = state.pipes[0]; + dest.emit('unpipe', this, unpipeInfo); + return this; +}; // set up data events if they are asked for +// Ensure readable listeners eventually get something + + +Readable.prototype.on = function (ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + var state = this._readableState; + + if (ev === 'data') { + // update readableListening so that resume() may be a no-op + // a few lines down. This is needed to support once('readable'). + state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused + + if (state.flowing !== false) this.resume(); + } else if (ev === 'readable') { + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.flowing = false; + state.emittedReadable = false; + debug('on readable', state.length, state.reading); + + if (state.length) { + emitReadable(this); + } else if (!state.reading) { + process.nextTick(nReadingNextTick, this); + } + } + } + + return res; +}; + +Readable.prototype.addListener = Readable.prototype.on; + +Readable.prototype.removeListener = function (ev, fn) { + var res = Stream.prototype.removeListener.call(this, ev, fn); + + if (ev === 'readable') { + // We need to check if there is someone still listening to + // readable and reset the state. However this needs to happen + // after readable has been emitted but before I/O (nextTick) to + // support once('readable', fn) cycles. This means that calling + // resume within the same tick will have no + // effect. + process.nextTick(updateReadableListening, this); + } + + return res; +}; + +Readable.prototype.removeAllListeners = function (ev) { + var res = Stream.prototype.removeAllListeners.apply(this, arguments); + + if (ev === 'readable' || ev === undefined) { + // We need to check if there is someone still listening to + // readable and reset the state. However this needs to happen + // after readable has been emitted but before I/O (nextTick) to + // support once('readable', fn) cycles. This means that calling + // resume within the same tick will have no + // effect. + process.nextTick(updateReadableListening, this); + } + + return res; +}; + +function updateReadableListening(self) { + var state = self._readableState; + state.readableListening = self.listenerCount('readable') > 0; + + if (state.resumeScheduled && !state.paused) { + // flowing needs to be set to true now, otherwise + // the upcoming resume will not flow. + state.flowing = true; // crude way to check if we should resume + } else if (self.listenerCount('data') > 0) { + self.resume(); + } +} + +function nReadingNextTick(self) { + debug('readable nexttick read 0'); + self.read(0); +} // pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. + + +Readable.prototype.resume = function () { + var state = this._readableState; + + if (!state.flowing) { + debug('resume'); // we flow only if there is no one listening + // for readable, but we still have to call + // resume() + + state.flowing = !state.readableListening; + resume(this, state); + } + + state.paused = false; + return this; +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + process.nextTick(resume_, stream, state); + } +} + +function resume_(stream, state) { + debug('resume', state.reading); + + if (!state.reading) { + stream.read(0); + } + + state.resumeScheduled = false; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + debug('call pause flowing=%j', this._readableState.flowing); + + if (this._readableState.flowing !== false) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } + + this._readableState.paused = true; + return this; +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + + while (state.flowing && stream.read() !== null) { + ; + } +} // wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. + + +Readable.prototype.wrap = function (stream) { + var _this = this; + + var state = this._readableState; + var paused = false; + stream.on('end', function () { + debug('wrapped end'); + + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) _this.push(chunk); + } + + _this.push(null); + }); + stream.on('data', function (chunk) { + debug('wrapped data'); + if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode + + if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; + + var ret = _this.push(chunk); + + if (!ret) { + paused = true; + stream.pause(); + } + }); // proxy all the other methods. + // important when wrapping filters and duplexes. + + for (var i in stream) { + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = function methodWrap(method) { + return function methodWrapReturnFunction() { + return stream[method].apply(stream, arguments); + }; + }(i); + } + } // proxy certain important events. + + + for (var n = 0; n < kProxyEvents.length; n++) { + stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); + } // when we try to consume some more bytes, simply unpause the + // underlying stream. + + + this._read = function (n) { + debug('wrapped _read', n); + + if (paused) { + paused = false; + stream.resume(); + } + }; + + return this; +}; + +if (typeof Symbol === 'function') { + Readable.prototype[Symbol.asyncIterator] = function () { + if (createReadableStreamAsyncIterator === undefined) { + createReadableStreamAsyncIterator = _dereq_('./internal/streams/async_iterator'); + } + + return createReadableStreamAsyncIterator(this); + }; +} + +Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState.highWaterMark; + } +}); +Object.defineProperty(Readable.prototype, 'readableBuffer', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState && this._readableState.buffer; + } +}); +Object.defineProperty(Readable.prototype, 'readableFlowing', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState.flowing; + }, + set: function set(state) { + if (this._readableState) { + this._readableState.flowing = state; + } + } +}); // exposed for testing purposes only. + +Readable._fromList = fromList; +Object.defineProperty(Readable.prototype, 'readableLength', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._readableState.length; + } +}); // Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. + +function fromList(n, state) { + // nothing buffered + if (state.length === 0) return null; + var ret; + if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = state.buffer.consume(n, state.decoder); + } + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + debug('endReadable', state.endEmitted); + + if (!state.endEmitted) { + state.ended = true; + process.nextTick(endReadableNT, state, stream); + } +} + +function endReadableNT(state, stream) { + debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift. + + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + + if (state.autoDestroy) { + // In case of duplex streams we need a way to detect + // if the writable side is ready for autoDestroy as well + var wState = stream._writableState; + + if (!wState || wState.autoDestroy && wState.finished) { + stream.destroy(); + } + } + } +} + +if (typeof Symbol === 'function') { + Readable.from = function (iterable, opts) { + if (from === undefined) { + from = _dereq_('./internal/streams/from'); + } + + return from(Readable, iterable, opts); + }; +} + +function indexOf(xs, x) { + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) return i; + } + + return -1; +} +}).call(this)}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../errors":99,"./_stream_duplex":100,"./internal/streams/async_iterator":105,"./internal/streams/buffer_list":106,"./internal/streams/destroy":107,"./internal/streams/from":109,"./internal/streams/state":111,"./internal/streams/stream":112,"_process":96,"buffer":28,"events":27,"inherits":65,"string_decoder/":113,"util":26}],103:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. +'use strict'; + +module.exports = Transform; + +var _require$codes = _dereq_('../errors').codes, + ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, + ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, + ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING, + ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0; + +var Duplex = _dereq_('./_stream_duplex'); + +_dereq_('inherits')(Transform, Duplex); + +function afterTransform(er, data) { + var ts = this._transformState; + ts.transforming = false; + var cb = ts.writecb; + + if (cb === null) { + return this.emit('error', new ERR_MULTIPLE_CALLBACK()); + } + + ts.writechunk = null; + ts.writecb = null; + if (data != null) // single equals check for both `null` and `undefined` + this.push(data); + cb(er); + var rs = this._readableState; + rs.reading = false; + + if (rs.needReadable || rs.length < rs.highWaterMark) { + this._read(rs.highWaterMark); + } +} + +function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); + Duplex.call(this, options); + this._transformState = { + afterTransform: afterTransform.bind(this), + needTransform: false, + transforming: false, + writecb: null, + writechunk: null, + writeencoding: null + }; // start out asking for a readable event once data is transformed. + + this._readableState.needReadable = true; // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + + this._readableState.sync = false; + + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; + if (typeof options.flush === 'function') this._flush = options.flush; + } // When the writable side finishes, then flush out anything remaining. + + + this.on('prefinish', prefinish); +} + +function prefinish() { + var _this = this; + + if (typeof this._flush === 'function' && !this._readableState.destroyed) { + this._flush(function (er, data) { + done(_this, er, data); + }); + } else { + done(this, null, null); + } +} + +Transform.prototype.push = function (chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; // This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. + + +Transform.prototype._transform = function (chunk, encoding, cb) { + cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()')); +}; + +Transform.prototype._write = function (chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); + } +}; // Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. + + +Transform.prototype._read = function (n) { + var ts = this._transformState; + + if (ts.writechunk !== null && !ts.transforming) { + ts.transforming = true; + + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + +Transform.prototype._destroy = function (err, cb) { + Duplex.prototype._destroy.call(this, err, function (err2) { + cb(err2); + }); +}; + +function done(stream, er, data) { + if (er) return stream.emit('error', er); + if (data != null) // single equals check for both `null` and `undefined` + stream.push(data); // TODO(BridgeAR): Write a test for these two error cases + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + + if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0(); + if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING(); + return stream.push(null); +} +},{"../errors":99,"./_stream_duplex":100,"inherits":65}],104:[function(_dereq_,module,exports){ +(function (process,global){(function (){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// A bit simpler than readable streams. +// Implement an async ._write(chunk, encoding, cb), and it'll handle all +// the drain event emission and buffering. +'use strict'; + +module.exports = Writable; +/* */ + +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; + this.next = null; +} // It seems a linked list but it is not +// there will be only 2 of these for each stream + + +function CorkedRequest(state) { + var _this = this; + + this.next = null; + this.entry = null; + + this.finish = function () { + onCorkedFinish(_this, state); + }; +} +/* */ + +/**/ + + +var Duplex; +/**/ + +Writable.WritableState = WritableState; +/**/ + +var internalUtil = { + deprecate: _dereq_('util-deprecate') +}; +/**/ + +/**/ + +var Stream = _dereq_('./internal/streams/stream'); +/**/ + + +var Buffer = _dereq_('buffer').Buffer; + +var OurUint8Array = global.Uint8Array || function () {}; + +function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk); +} + +function _isUint8Array(obj) { + return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +} + +var destroyImpl = _dereq_('./internal/streams/destroy'); + +var _require = _dereq_('./internal/streams/state'), + getHighWaterMark = _require.getHighWaterMark; + +var _require$codes = _dereq_('../errors').codes, + ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, + ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, + ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, + ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, + ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, + ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, + ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, + ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; + +var errorOrDestroy = destroyImpl.errorOrDestroy; + +_dereq_('inherits')(Writable, Stream); + +function nop() {} + +function WritableState(options, stream, isDuplex) { + Duplex = Duplex || _dereq_('./_stream_duplex'); + options = options || {}; // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream, + // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. + + if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream + // contains buffers or objects. + + this.objectMode = !!options.objectMode; + if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + + this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called + + this.finalCalled = false; // drain event flag. + + this.needDrain = false; // at the start of calling end() + + this.ending = false; // when end() has been called, and returned + + this.ended = false; // when 'finish' is emitted + + this.finished = false; // has it been destroyed + + this.destroyed = false; // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + + this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + + this.length = 0; // a flag to see when we're in the middle of a write. + + this.writing = false; // when true all writes will be buffered until .uncork() call + + this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + + this.sync = true; // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + + this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) + + this.onwrite = function (er) { + onwrite(stream, er); + }; // the callback that the user supplies to write(chunk,encoding,cb) + + + this.writecb = null; // the amount that is being written when _write is called. + + this.writelen = 0; + this.bufferedRequest = null; + this.lastBufferedRequest = null; // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + + this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + + this.prefinished = false; // True if the error was already emitted and should not be thrown again + + this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true. + + this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end') + + this.autoDestroy = !!options.autoDestroy; // count buffered requests + + this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always + // one allocated and free to use, and we maintain at most two + + this.corkedRequestsFree = new CorkedRequest(this); +} + +WritableState.prototype.getBuffer = function getBuffer() { + var current = this.bufferedRequest; + var out = []; + + while (current) { + out.push(current); + current = current.next; + } + + return out; +}; + +(function () { + try { + Object.defineProperty(WritableState.prototype, 'buffer', { + get: internalUtil.deprecate(function writableStateBufferGetter() { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') + }); + } catch (_) {} +})(); // Test _writableState for inheritance to account for Duplex streams, +// whose prototype chain only points to Readable. + + +var realHasInstance; + +if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { + realHasInstance = Function.prototype[Symbol.hasInstance]; + Object.defineProperty(Writable, Symbol.hasInstance, { + value: function value(object) { + if (realHasInstance.call(this, object)) return true; + if (this !== Writable) return false; + return object && object._writableState instanceof WritableState; + } + }); +} else { + realHasInstance = function realHasInstance(object) { + return object instanceof this; + }; +} + +function Writable(options) { + Duplex = Duplex || _dereq_('./_stream_duplex'); // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + // Checking for a Stream.Duplex instance is faster here instead of inside + // the WritableState constructor, at least with V8 6.5 + + var isDuplex = this instanceof Duplex; + if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); + this._writableState = new WritableState(options, this, isDuplex); // legacy. + + this.writable = true; + + if (options) { + if (typeof options.write === 'function') this._write = options.write; + if (typeof options.writev === 'function') this._writev = options.writev; + if (typeof options.destroy === 'function') this._destroy = options.destroy; + if (typeof options.final === 'function') this._final = options.final; + } + + Stream.call(this); +} // Otherwise people can pipe Writable streams, which is just wrong. + + +Writable.prototype.pipe = function () { + errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); +}; + +function writeAfterEnd(stream, cb) { + var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb + + errorOrDestroy(stream, er); + process.nextTick(cb, er); +} // Checks that a user-supplied chunk is valid, especially for the particular +// mode the stream is in. Currently this means that `null` is never accepted +// and undefined/non-string values are only allowed in object mode. + + +function validChunk(stream, state, chunk, cb) { + var er; + + if (chunk === null) { + er = new ERR_STREAM_NULL_VALUES(); + } else if (typeof chunk !== 'string' && !state.objectMode) { + er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); + } + + if (er) { + errorOrDestroy(stream, er); + process.nextTick(cb, er); + return false; + } + + return true; +} + +Writable.prototype.write = function (chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + + var isBuf = !state.objectMode && _isUint8Array(chunk); + + if (isBuf && !Buffer.isBuffer(chunk)) { + chunk = _uint8ArrayToBuffer(chunk); + } + + if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; + if (typeof cb !== 'function') cb = nop; + if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); + } + return ret; +}; + +Writable.prototype.cork = function () { + this._writableState.corked++; +}; + +Writable.prototype.uncork = function () { + var state = this._writableState; + + if (state.corked) { + state.corked--; + if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); + } +}; + +Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { + // node::ParseEncoding() requires lower case. + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); + this._writableState.defaultEncoding = encoding; + return this; +}; + +Object.defineProperty(Writable.prototype, 'writableBuffer', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState && this._writableState.getBuffer(); + } +}); + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { + chunk = Buffer.from(chunk, encoding); + } + + return chunk; +} + +Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.highWaterMark; + } +}); // if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. + +function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { + if (!isBuf) { + var newChunk = decodeChunk(state, chunk, encoding); + + if (chunk !== newChunk) { + isBuf = true; + encoding = 'buffer'; + chunk = newChunk; + } + } + + var len = state.objectMode ? 1 : chunk.length; + state.length += len; + var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. + + if (!ret) state.needDrain = true; + + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = { + chunk: chunk, + encoding: encoding, + isBuf: isBuf, + callback: cb, + next: null + }; + + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + + state.bufferedRequestCount += 1; + } else { + doWrite(stream, state, false, len, chunk, encoding, cb); + } + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + --state.pendingcb; + + if (sync) { + // defer the callback if we are being called synchronously + // to avoid piling up things on the stack + process.nextTick(cb, er); // this can emit finish, and it will always happen + // after error + + process.nextTick(finishMaybe, stream, state); + stream._writableState.errorEmitted = true; + errorOrDestroy(stream, er); + } else { + // the caller expect this to happen before if + // it is async + cb(er); + stream._writableState.errorEmitted = true; + errorOrDestroy(stream, er); // this can emit finish, but finish must + // always follow error + + finishMaybe(stream, state); + } +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); + onwriteStateUpdate(state); + if (er) onwriteError(stream, state, sync, er, cb);else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(state) || stream.destroyed; + + if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { + clearBuffer(stream, state); + } + + if (sync) { + process.nextTick(afterWrite, stream, state, finished, cb); + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} // Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. + + +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} // if there's something in the buffer waiting, then process it + + +function clearBuffer(stream, state) { + state.bufferProcessing = true; + var entry = state.bufferedRequest; + + if (stream._writev && entry && entry.next) { + // Fast case, write everything using _writev() + var l = state.bufferedRequestCount; + var buffer = new Array(l); + var holder = state.corkedRequestsFree; + holder.entry = entry; + var count = 0; + var allBuffers = true; + + while (entry) { + buffer[count] = entry; + if (!entry.isBuf) allBuffers = false; + entry = entry.next; + count += 1; + } + + buffer.allBuffers = allBuffers; + doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time + // as the hot path ends with doWrite + + state.pendingcb++; + state.lastBufferedRequest = null; + + if (holder.next) { + state.corkedRequestsFree = holder.next; + holder.next = null; + } else { + state.corkedRequestsFree = new CorkedRequest(state); + } + + state.bufferedRequestCount = 0; + } else { + // Slow case, write chunks one-by-one + while (entry) { + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + doWrite(stream, state, false, len, chunk, encoding, cb); + entry = entry.next; + state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + + if (state.writing) { + break; + } + } + + if (entry === null) state.lastBufferedRequest = null; + } + + state.bufferedRequest = entry; + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function (chunk, encoding, cb) { + var state = this._writableState; + + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks + + if (state.corked) { + state.corked = 1; + this.uncork(); + } // ignore unnecessary end() calls. + + + if (!state.ending) endWritable(this, state, cb); + return this; +}; + +Object.defineProperty(Writable.prototype, 'writableLength', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + return this._writableState.length; + } +}); + +function needFinish(state) { + return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; +} + +function callFinal(stream, state) { + stream._final(function (err) { + state.pendingcb--; + + if (err) { + errorOrDestroy(stream, err); + } + + state.prefinished = true; + stream.emit('prefinish'); + finishMaybe(stream, state); + }); +} + +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function' && !state.destroyed) { + state.pendingcb++; + state.finalCalled = true; + process.nextTick(callFinal, stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state) { + var need = needFinish(state); + + if (need) { + prefinish(stream, state); + + if (state.pendingcb === 0) { + state.finished = true; + stream.emit('finish'); + + if (state.autoDestroy) { + // In case of duplex streams we need a way to detect + // if the readable side is ready for autoDestroy as well + var rState = stream._readableState; + + if (!rState || rState.autoDestroy && rState.endEmitted) { + stream.destroy(); + } + } + } + } + + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + + if (cb) { + if (state.finished) process.nextTick(cb);else stream.once('finish', cb); + } + + state.ended = true; + stream.writable = false; +} + +function onCorkedFinish(corkReq, state, err) { + var entry = corkReq.entry; + corkReq.entry = null; + + while (entry) { + var cb = entry.callback; + state.pendingcb--; + cb(err); + entry = entry.next; + } // reuse the free corkReq. + + + state.corkedRequestsFree.next = corkReq; +} + +Object.defineProperty(Writable.prototype, 'destroyed', { + // making it explicit this property is not enumerable + // because otherwise some prototype manipulation in + // userland will fail + enumerable: false, + get: function get() { + if (this._writableState === undefined) { + return false; + } + + return this._writableState.destroyed; + }, + set: function set(value) { + // we ignore the value if the stream + // has not been initialized yet + if (!this._writableState) { + return; + } // backward compatibility, the user is explicitly + // managing destroyed + + + this._writableState.destroyed = value; + } +}); +Writable.prototype.destroy = destroyImpl.destroy; +Writable.prototype._undestroy = destroyImpl.undestroy; + +Writable.prototype._destroy = function (err, cb) { + cb(err); +}; +}).call(this)}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../errors":99,"./_stream_duplex":100,"./internal/streams/destroy":107,"./internal/streams/state":111,"./internal/streams/stream":112,"_process":96,"buffer":28,"inherits":65,"util-deprecate":120}],105:[function(_dereq_,module,exports){ +(function (process){(function (){ +'use strict'; + +var _Object$setPrototypeO; + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var finished = _dereq_('./end-of-stream'); + +var kLastResolve = Symbol('lastResolve'); +var kLastReject = Symbol('lastReject'); +var kError = Symbol('error'); +var kEnded = Symbol('ended'); +var kLastPromise = Symbol('lastPromise'); +var kHandlePromise = Symbol('handlePromise'); +var kStream = Symbol('stream'); + +function createIterResult(value, done) { + return { + value: value, + done: done + }; +} + +function readAndResolve(iter) { + var resolve = iter[kLastResolve]; + + if (resolve !== null) { + var data = iter[kStream].read(); // we defer if data is null + // we can be expecting either 'end' or + // 'error' + + if (data !== null) { + iter[kLastPromise] = null; + iter[kLastResolve] = null; + iter[kLastReject] = null; + resolve(createIterResult(data, false)); + } + } +} + +function onReadable(iter) { + // we wait for the next tick, because it might + // emit an error with process.nextTick + process.nextTick(readAndResolve, iter); +} + +function wrapForNext(lastPromise, iter) { + return function (resolve, reject) { + lastPromise.then(function () { + if (iter[kEnded]) { + resolve(createIterResult(undefined, true)); + return; + } + + iter[kHandlePromise](resolve, reject); + }, reject); + }; +} + +var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); +var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { + get stream() { + return this[kStream]; + }, + + next: function next() { + var _this = this; + + // if we have detected an error in the meanwhile + // reject straight away + var error = this[kError]; + + if (error !== null) { + return Promise.reject(error); + } + + if (this[kEnded]) { + return Promise.resolve(createIterResult(undefined, true)); + } + + if (this[kStream].destroyed) { + // We need to defer via nextTick because if .destroy(err) is + // called, the error will be emitted via nextTick, and + // we cannot guarantee that there is no error lingering around + // waiting to be emitted. + return new Promise(function (resolve, reject) { + process.nextTick(function () { + if (_this[kError]) { + reject(_this[kError]); + } else { + resolve(createIterResult(undefined, true)); + } + }); + }); + } // if we have multiple next() calls + // we will wait for the previous Promise to finish + // this logic is optimized to support for await loops, + // where next() is only called once at a time + + + var lastPromise = this[kLastPromise]; + var promise; + + if (lastPromise) { + promise = new Promise(wrapForNext(lastPromise, this)); + } else { + // fast path needed to support multiple this.push() + // without triggering the next() queue + var data = this[kStream].read(); + + if (data !== null) { + return Promise.resolve(createIterResult(data, false)); + } + + promise = new Promise(this[kHandlePromise]); + } + + this[kLastPromise] = promise; + return promise; + } +}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { + return this; +}), _defineProperty(_Object$setPrototypeO, "return", function _return() { + var _this2 = this; + + // destroy(err, cb) is a private API + // we can guarantee we have that here, because we control the + // Readable class this is attached to + return new Promise(function (resolve, reject) { + _this2[kStream].destroy(null, function (err) { + if (err) { + reject(err); + return; + } + + resolve(createIterResult(undefined, true)); + }); + }); +}), _Object$setPrototypeO), AsyncIteratorPrototype); + +var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { + var _Object$create; + + var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { + value: stream, + writable: true + }), _defineProperty(_Object$create, kLastResolve, { + value: null, + writable: true + }), _defineProperty(_Object$create, kLastReject, { + value: null, + writable: true + }), _defineProperty(_Object$create, kError, { + value: null, + writable: true + }), _defineProperty(_Object$create, kEnded, { + value: stream._readableState.endEmitted, + writable: true + }), _defineProperty(_Object$create, kHandlePromise, { + value: function value(resolve, reject) { + var data = iterator[kStream].read(); + + if (data) { + iterator[kLastPromise] = null; + iterator[kLastResolve] = null; + iterator[kLastReject] = null; + resolve(createIterResult(data, false)); + } else { + iterator[kLastResolve] = resolve; + iterator[kLastReject] = reject; + } + }, + writable: true + }), _Object$create)); + iterator[kLastPromise] = null; + finished(stream, function (err) { + if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { + var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise + // returned by next() and store the error + + if (reject !== null) { + iterator[kLastPromise] = null; + iterator[kLastResolve] = null; + iterator[kLastReject] = null; + reject(err); + } + + iterator[kError] = err; + return; + } + + var resolve = iterator[kLastResolve]; + + if (resolve !== null) { + iterator[kLastPromise] = null; + iterator[kLastResolve] = null; + iterator[kLastReject] = null; + resolve(createIterResult(undefined, true)); + } + + iterator[kEnded] = true; + }); + stream.on('readable', onReadable.bind(null, iterator)); + return iterator; +}; + +module.exports = createReadableStreamAsyncIterator; +}).call(this)}).call(this,_dereq_('_process')) +},{"./end-of-stream":108,"_process":96}],106:[function(_dereq_,module,exports){ +'use strict'; + +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var _require = _dereq_('buffer'), + Buffer = _require.Buffer; + +var _require2 = _dereq_('util'), + inspect = _require2.inspect; + +var custom = inspect && inspect.custom || 'inspect'; + +function copyBuffer(src, target, offset) { + Buffer.prototype.copy.call(src, target, offset); +} + +module.exports = +/*#__PURE__*/ +function () { + function BufferList() { + _classCallCheck(this, BufferList); + + this.head = null; + this.tail = null; + this.length = 0; + } + + _createClass(BufferList, [{ + key: "push", + value: function push(v) { + var entry = { + data: v, + next: null + }; + if (this.length > 0) this.tail.next = entry;else this.head = entry; + this.tail = entry; + ++this.length; + } + }, { + key: "unshift", + value: function unshift(v) { + var entry = { + data: v, + next: this.head + }; + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + } + }, { + key: "shift", + value: function shift() { + if (this.length === 0) return; + var ret = this.head.data; + if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; + --this.length; + return ret; + } + }, { + key: "clear", + value: function clear() { + this.head = this.tail = null; + this.length = 0; + } + }, { + key: "join", + value: function join(s) { + if (this.length === 0) return ''; + var p = this.head; + var ret = '' + p.data; + + while (p = p.next) { + ret += s + p.data; + } + + return ret; + } + }, { + key: "concat", + value: function concat(n) { + if (this.length === 0) return Buffer.alloc(0); + var ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + + while (p) { + copyBuffer(p.data, ret, i); + i += p.data.length; + p = p.next; + } + + return ret; + } // Consumes a specified amount of bytes or characters from the buffered data. + + }, { + key: "consume", + value: function consume(n, hasStrings) { + var ret; + + if (n < this.head.data.length) { + // `slice` is the same for buffers and strings. + ret = this.head.data.slice(0, n); + this.head.data = this.head.data.slice(n); + } else if (n === this.head.data.length) { + // First chunk is a perfect match. + ret = this.shift(); + } else { + // Result spans more than one buffer. + ret = hasStrings ? this._getString(n) : this._getBuffer(n); + } + + return ret; + } + }, { + key: "first", + value: function first() { + return this.head.data; + } // Consumes a specified amount of characters from the buffered data. + + }, { + key: "_getString", + value: function _getString(n) { + var p = this.head; + var c = 1; + var ret = p.data; + n -= ret.length; + + while (p = p.next) { + var str = p.data; + var nb = n > str.length ? str.length : n; + if (nb === str.length) ret += str;else ret += str.slice(0, n); + n -= nb; + + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) this.head = p.next;else this.head = this.tail = null; + } else { + this.head = p; + p.data = str.slice(nb); + } + + break; + } + + ++c; + } + + this.length -= c; + return ret; + } // Consumes a specified amount of bytes from the buffered data. + + }, { + key: "_getBuffer", + value: function _getBuffer(n) { + var ret = Buffer.allocUnsafe(n); + var p = this.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + + while (p = p.next) { + var buf = p.data; + var nb = n > buf.length ? buf.length : n; + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) this.head = p.next;else this.head = this.tail = null; + } else { + this.head = p; + p.data = buf.slice(nb); + } + + break; + } + + ++c; + } + + this.length -= c; + return ret; + } // Make sure the linked list only shows the minimal necessary information. + + }, { + key: custom, + value: function value(_, options) { + return inspect(this, _objectSpread({}, options, { + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false + })); + } + }]); + + return BufferList; +}(); +},{"buffer":28,"util":26}],107:[function(_dereq_,module,exports){ +(function (process){(function (){ +'use strict'; // undocumented cb() API, needed for core, not for public API + +function destroy(err, cb) { + var _this = this; + + var readableDestroyed = this._readableState && this._readableState.destroyed; + var writableDestroyed = this._writableState && this._writableState.destroyed; + + if (readableDestroyed || writableDestroyed) { + if (cb) { + cb(err); + } else if (err) { + if (!this._writableState) { + process.nextTick(emitErrorNT, this, err); + } else if (!this._writableState.errorEmitted) { + this._writableState.errorEmitted = true; + process.nextTick(emitErrorNT, this, err); + } + } + + return this; + } // we set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + + + if (this._readableState) { + this._readableState.destroyed = true; + } // if this is a duplex stream mark the writable part as destroyed as well + + + if (this._writableState) { + this._writableState.destroyed = true; + } + + this._destroy(err || null, function (err) { + if (!cb && err) { + if (!_this._writableState) { + process.nextTick(emitErrorAndCloseNT, _this, err); + } else if (!_this._writableState.errorEmitted) { + _this._writableState.errorEmitted = true; + process.nextTick(emitErrorAndCloseNT, _this, err); + } else { + process.nextTick(emitCloseNT, _this); + } + } else if (cb) { + process.nextTick(emitCloseNT, _this); + cb(err); + } else { + process.nextTick(emitCloseNT, _this); + } + }); + + return this; +} + +function emitErrorAndCloseNT(self, err) { + emitErrorNT(self, err); + emitCloseNT(self); +} + +function emitCloseNT(self) { + if (self._writableState && !self._writableState.emitClose) return; + if (self._readableState && !self._readableState.emitClose) return; + self.emit('close'); +} + +function undestroy() { + if (this._readableState) { + this._readableState.destroyed = false; + this._readableState.reading = false; + this._readableState.ended = false; + this._readableState.endEmitted = false; + } + + if (this._writableState) { + this._writableState.destroyed = false; + this._writableState.ended = false; + this._writableState.ending = false; + this._writableState.finalCalled = false; + this._writableState.prefinished = false; + this._writableState.finished = false; + this._writableState.errorEmitted = false; + } +} + +function emitErrorNT(self, err) { + self.emit('error', err); +} + +function errorOrDestroy(stream, err) { + // We have tests that rely on errors being emitted + // in the same tick, so changing this is semver major. + // For now when you opt-in to autoDestroy we allow + // the error to be emitted nextTick. In a future + // semver major update we should change the default to this. + var rState = stream._readableState; + var wState = stream._writableState; + if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); +} + +module.exports = { + destroy: destroy, + undestroy: undestroy, + errorOrDestroy: errorOrDestroy +}; +}).call(this)}).call(this,_dereq_('_process')) +},{"_process":96}],108:[function(_dereq_,module,exports){ +// Ported from https://github.com/mafintosh/end-of-stream with +// permission from the author, Mathias Buus (@mafintosh). +'use strict'; + +var ERR_STREAM_PREMATURE_CLOSE = _dereq_('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE; + +function once(callback) { + var called = false; + return function () { + if (called) return; + called = true; + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + callback.apply(this, args); + }; +} + +function noop() {} + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +} + +function eos(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; + callback = once(callback || noop); + var readable = opts.readable || opts.readable !== false && stream.readable; + var writable = opts.writable || opts.writable !== false && stream.writable; + + var onlegacyfinish = function onlegacyfinish() { + if (!stream.writable) onfinish(); + }; + + var writableEnded = stream._writableState && stream._writableState.finished; + + var onfinish = function onfinish() { + writable = false; + writableEnded = true; + if (!readable) callback.call(stream); + }; + + var readableEnded = stream._readableState && stream._readableState.endEmitted; + + var onend = function onend() { + readable = false; + readableEnded = true; + if (!writable) callback.call(stream); + }; + + var onerror = function onerror(err) { + callback.call(stream, err); + }; + + var onclose = function onclose() { + var err; + + if (readable && !readableEnded) { + if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); + return callback.call(stream, err); + } + + if (writable && !writableEnded) { + if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); + return callback.call(stream, err); + } + }; + + var onrequest = function onrequest() { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest();else stream.on('request', onrequest); + } else if (writable && !stream._writableState) { + // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + return function () { + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; +} + +module.exports = eos; +},{"../../../errors":99}],109:[function(_dereq_,module,exports){ +module.exports = function () { + throw new Error('Readable.from is not available in the browser') +}; + +},{}],110:[function(_dereq_,module,exports){ +// Ported from https://github.com/mafintosh/pump with +// permission from the author, Mathias Buus (@mafintosh). +'use strict'; + +var eos; + +function once(callback) { + var called = false; + return function () { + if (called) return; + called = true; + callback.apply(void 0, arguments); + }; +} + +var _require$codes = _dereq_('../../../errors').codes, + ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, + ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED; + +function noop(err) { + // Rethrow the error if it exists to avoid swallowing it + if (err) throw err; +} + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +} + +function destroyer(stream, reading, writing, callback) { + callback = once(callback); + var closed = false; + stream.on('close', function () { + closed = true; + }); + if (eos === undefined) eos = _dereq_('./end-of-stream'); + eos(stream, { + readable: reading, + writable: writing + }, function (err) { + if (err) return callback(err); + closed = true; + callback(); + }); + var destroyed = false; + return function (err) { + if (closed) return; + if (destroyed) return; + destroyed = true; // request.destroy just do .end - .abort is what we want + + if (isRequest(stream)) return stream.abort(); + if (typeof stream.destroy === 'function') return stream.destroy(); + callback(err || new ERR_STREAM_DESTROYED('pipe')); + }; +} + +function call(fn) { + fn(); +} + +function pipe(from, to) { + return from.pipe(to); +} + +function popCallback(streams) { + if (!streams.length) return noop; + if (typeof streams[streams.length - 1] !== 'function') return noop; + return streams.pop(); +} + +function pipeline() { + for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) { + streams[_key] = arguments[_key]; + } + + var callback = popCallback(streams); + if (Array.isArray(streams[0])) streams = streams[0]; + + if (streams.length < 2) { + throw new ERR_MISSING_ARGS('streams'); + } + + var error; + var destroys = streams.map(function (stream, i) { + var reading = i < streams.length - 1; + var writing = i > 0; + return destroyer(stream, reading, writing, function (err) { + if (!error) error = err; + if (err) destroys.forEach(call); + if (reading) return; + destroys.forEach(call); + callback(error); + }); + }); + return streams.reduce(pipe); +} + +module.exports = pipeline; +},{"../../../errors":99,"./end-of-stream":108}],111:[function(_dereq_,module,exports){ +'use strict'; + +var ERR_INVALID_OPT_VALUE = _dereq_('../../../errors').codes.ERR_INVALID_OPT_VALUE; + +function highWaterMarkFrom(options, isDuplex, duplexKey) { + return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; +} + +function getHighWaterMark(state, options, duplexKey, isDuplex) { + var hwm = highWaterMarkFrom(options, isDuplex, duplexKey); + + if (hwm != null) { + if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) { + var name = isDuplex ? duplexKey : 'highWaterMark'; + throw new ERR_INVALID_OPT_VALUE(name, hwm); + } + + return Math.floor(hwm); + } // Default value + + + return state.objectMode ? 16 : 16 * 1024; +} + +module.exports = { + getHighWaterMark: getHighWaterMark +}; +},{"../../../errors":99}],112:[function(_dereq_,module,exports){ +module.exports = _dereq_('events').EventEmitter; + +},{"events":27}],113:[function(_dereq_,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +/**/ + +var Buffer = _dereq_('safe-buffer').Buffer; +/**/ + +var isEncoding = Buffer.isEncoding || function (encoding) { + encoding = '' + encoding; + switch (encoding && encoding.toLowerCase()) { + case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw': + return true; + default: + return false; + } +}; + +function _normalizeEncoding(enc) { + if (!enc) return 'utf8'; + var retried; + while (true) { + switch (enc) { + case 'utf8': + case 'utf-8': + return 'utf8'; + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return 'utf16le'; + case 'latin1': + case 'binary': + return 'latin1'; + case 'base64': + case 'ascii': + case 'hex': + return enc; + default: + if (retried) return; // undefined + enc = ('' + enc).toLowerCase(); + retried = true; + } + } +}; + +// Do not cache `Buffer.isEncoding` when checking encoding names as some +// modules monkey-patch it to support additional encodings +function normalizeEncoding(enc) { + var nenc = _normalizeEncoding(enc); + if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc); + return nenc || enc; +} + +// StringDecoder provides an interface for efficiently splitting a series of +// buffers into a series of JS strings without breaking apart multi-byte +// characters. +exports.StringDecoder = StringDecoder; +function StringDecoder(encoding) { + this.encoding = normalizeEncoding(encoding); + var nb; + switch (this.encoding) { + case 'utf16le': + this.text = utf16Text; + this.end = utf16End; + nb = 4; + break; + case 'utf8': + this.fillLast = utf8FillLast; + nb = 4; + break; + case 'base64': + this.text = base64Text; + this.end = base64End; + nb = 3; + break; + default: + this.write = simpleWrite; + this.end = simpleEnd; + return; + } + this.lastNeed = 0; + this.lastTotal = 0; + this.lastChar = Buffer.allocUnsafe(nb); +} + +StringDecoder.prototype.write = function (buf) { + if (buf.length === 0) return ''; + var r; + var i; + if (this.lastNeed) { + r = this.fillLast(buf); + if (r === undefined) return ''; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); + return r || ''; +}; + +StringDecoder.prototype.end = utf8End; + +// Returns only complete characters in a Buffer +StringDecoder.prototype.text = utf8Text; + +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer +StringDecoder.prototype.fillLast = function (buf) { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +}; + +// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a +// continuation byte. If an invalid byte is detected, -2 is returned. +function utf8CheckByte(byte) { + if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. +function utf8CheckIncomplete(self, buf, i) { + var j = buf.length - 1; + if (j < i) return 0; + var nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0;else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'; + } + } + } +} + +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + var p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a +// partial character, the character's bytes are buffered until the required +// number of bytes are available. +function utf8Text(buf, i) { + var total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString('utf8', i); + this.lastTotal = total; + var end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString('utf8', i, end); +} + +// For UTF-8, a replacement character is added when ending on a partial +// character. +function utf8End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + '\ufffd'; + return r; +} + +// UTF-16LE typically needs two bytes per character, but even if we have an even +// number of bytes available, we need to check if we end on a leading/high +// surrogate. In that case, we need to wait for the next two bytes in order to +// decode the last character properly. +function utf16Text(buf, i) { + if ((buf.length - i) % 2 === 0) { + var r = buf.toString('utf16le', i); + if (r) { + var c = r.charCodeAt(r.length - 1); + if (c >= 0xD800 && c <= 0xDBFF) { + this.lastNeed = 2; + this.lastTotal = 4; + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + return r.slice(0, -1); + } + } + return r; + } + this.lastNeed = 1; + this.lastTotal = 2; + this.lastChar[0] = buf[buf.length - 1]; + return buf.toString('utf16le', i, buf.length - 1); +} + +// For UTF-16LE we do not explicitly append special replacement characters if we +// end on a partial character, we simply let v8 handle that. +function utf16End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) { + var end = this.lastTotal - this.lastNeed; + return r + this.lastChar.toString('utf16le', 0, end); + } + return r; +} + +function base64Text(buf, i) { + var n = (buf.length - i) % 3; + if (n === 0) return buf.toString('base64', i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString('base64', i, buf.length - n); +} + +function base64End(buf) { + var r = buf && buf.length ? this.write(buf) : ''; + if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + return r; +} + +// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex) +function simpleWrite(buf) { + return buf.toString(this.encoding); +} + +function simpleEnd(buf) { + return buf && buf.length ? this.write(buf) : ''; +} +},{"safe-buffer":97}],114:[function(_dereq_,module,exports){ +(function (process,Buffer){(function (){ + +/** + * Module dependencies. + */ + +var assert = _dereq_('assert'); +var debug = _dereq_('debug')('stream-parser'); + +/** + * Module exports. + */ + +module.exports = Parser; + +/** + * Parser states. + */ + +var INIT = -1; +var BUFFERING = 0; +var SKIPPING = 1; +var PASSTHROUGH = 2; + +/** + * The `Parser` stream mixin works with either `Writable` or `Transform` stream + * instances/subclasses. Provides a convenient generic "parsing" API: + * + * _bytes(n, cb) - buffers "n" bytes and then calls "cb" with the "chunk" + * _skipBytes(n, cb) - skips "n" bytes and then calls "cb" when done + * + * If you extend a `Transform` stream, then the `_passthrough()` function is also + * added: + * + * _passthrough(n, cb) - passes through "n" bytes untouched and then calls "cb" + * + * @param {Stream} stream Transform or Writable stream instance to extend + * @api public + */ + +function Parser (stream) { + var isTransform = stream && 'function' == typeof stream._transform; + var isWritable = stream && 'function' == typeof stream._write; + + if (!isTransform && !isWritable) throw new Error('must pass a Writable or Transform stream in'); + debug('extending Parser into stream'); + + // Transform streams and Writable streams get `_bytes()` and `_skipBytes()` + stream._bytes = _bytes; + stream._skipBytes = _skipBytes; + + // only Transform streams get the `_passthrough()` function + if (isTransform) stream._passthrough = _passthrough; + + // take control of the streams2 callback functions for this stream + if (isTransform) { + stream._transform = transform; + } else { + stream._write = write; + } +} + +function init (stream) { + debug('initializing parser stream'); + + // number of bytes left to parser for the next "chunk" + stream._parserBytesLeft = 0; + + // array of Buffer instances that make up the next "chunk" + stream._parserBuffers = []; + + // number of bytes parsed so far for the next "chunk" + stream._parserBuffered = 0; + + // flag that keeps track of if what the parser should do with bytes received + stream._parserState = INIT; + + // the callback for the next "chunk" + stream._parserCallback = null; + + // XXX: backwards compat with the old Transform API... remove at some point.. + if ('function' == typeof stream.push) { + stream._parserOutput = stream.push.bind(stream); + } + + stream._parserInit = true; +} + +/** + * Buffers `n` bytes and then invokes `fn` once that amount has been collected. + * + * @param {Number} n the number of bytes to buffer + * @param {Function} fn callback function to invoke when `n` bytes are buffered + * @api public + */ + +function _bytes (n, fn) { + assert(!this._parserCallback, 'there is already a "callback" set!'); + assert(isFinite(n) && n > 0, 'can only buffer a finite number of bytes > 0, got "' + n + '"'); + if (!this._parserInit) init(this); + debug('buffering %o bytes', n); + this._parserBytesLeft = n; + this._parserCallback = fn; + this._parserState = BUFFERING; +} + +/** + * Skips over the next `n` bytes, then invokes `fn` once that amount has + * been discarded. + * + * @param {Number} n the number of bytes to discard + * @param {Function} fn callback function to invoke when `n` bytes have been skipped + * @api public + */ + +function _skipBytes (n, fn) { + assert(!this._parserCallback, 'there is already a "callback" set!'); + assert(n > 0, 'can only skip > 0 bytes, got "' + n + '"'); + if (!this._parserInit) init(this); + debug('skipping %o bytes', n); + this._parserBytesLeft = n; + this._parserCallback = fn; + this._parserState = SKIPPING; +} + +/** + * Passes through `n` bytes to the readable side of this stream untouched, + * then invokes `fn` once that amount has been passed through. + * + * @param {Number} n the number of bytes to pass through + * @param {Function} fn callback function to invoke when `n` bytes have passed through + * @api public + */ + +function _passthrough (n, fn) { + assert(!this._parserCallback, 'There is already a "callback" set!'); + assert(n > 0, 'can only pass through > 0 bytes, got "' + n + '"'); + if (!this._parserInit) init(this); + debug('passing through %o bytes', n); + this._parserBytesLeft = n; + this._parserCallback = fn; + this._parserState = PASSTHROUGH; +} + +/** + * The `_write()` callback function implementation. + * + * @api private + */ + +function write (chunk, encoding, fn) { + if (!this._parserInit) init(this); + debug('write(%o bytes)', chunk.length); + + // XXX: old Writable stream API compat... remove at some point... + if ('function' == typeof encoding) fn = encoding; + + data(this, chunk, null, fn); +} + +/** + * The `_transform()` callback function implementation. + * + * @api private + */ + + +function transform (chunk, output, fn) { + if (!this._parserInit) init(this); + debug('transform(%o bytes)', chunk.length); + + // XXX: old Transform stream API compat... remove at some point... + if ('function' != typeof output) { + output = this._parserOutput; + } + + data(this, chunk, output, fn); +} + +/** + * The internal buffering/passthrough logic... + * + * This `_data` function get's "trampolined" to prevent stack overflows for tight + * loops. This technique requires us to return a "thunk" function for any + * synchronous action. Async stuff breaks the trampoline, but that's ok since it's + * working with a new stack at that point anyway. + * + * @api private + */ + +function _data (stream, chunk, output, fn) { + if (stream._parserBytesLeft <= 0) { + return fn(new Error('got data but not currently parsing anything')); + } + + if (chunk.length <= stream._parserBytesLeft) { + // small buffer fits within the "_parserBytesLeft" window + return function () { + return process(stream, chunk, output, fn); + }; + } else { + // large buffer needs to be sliced on "_parserBytesLeft" and processed + return function () { + var b = chunk.slice(0, stream._parserBytesLeft); + return process(stream, b, output, function (err) { + if (err) return fn(err); + if (chunk.length > b.length) { + return function () { + return _data(stream, chunk.slice(b.length), output, fn); + }; + } + }); + }; + } +} + +/** + * The internal `process` function gets called by the `data` function when + * something "interesting" happens. This function takes care of buffering the + * bytes when buffering, passing through the bytes when doing that, and invoking + * the user callback when the number of bytes has been reached. + * + * @api private + */ + +function process (stream, chunk, output, fn) { + stream._parserBytesLeft -= chunk.length; + debug('%o bytes left for stream piece', stream._parserBytesLeft); + + if (stream._parserState === BUFFERING) { + // buffer + stream._parserBuffers.push(chunk); + stream._parserBuffered += chunk.length; + } else if (stream._parserState === PASSTHROUGH) { + // passthrough + output(chunk); + } + // don't need to do anything for the SKIPPING case + + if (0 === stream._parserBytesLeft) { + // done with stream "piece", invoke the callback + var cb = stream._parserCallback; + if (cb && stream._parserState === BUFFERING && stream._parserBuffers.length > 1) { + chunk = Buffer.concat(stream._parserBuffers, stream._parserBuffered); + } + if (stream._parserState !== BUFFERING) { + chunk = null; + } + stream._parserCallback = null; + stream._parserBuffered = 0; + stream._parserState = INIT; + stream._parserBuffers.splice(0); // empty + + if (cb) { + var args = []; + if (chunk) { + // buffered + args.push(chunk); + } else { + // passthrough + } + if (output) { + // on a Transform stream, has "output" function + args.push(output); + } + var async = cb.length > args.length; + if (async) { + args.push(trampoline(fn)); + } + // invoke cb + var rtn = cb.apply(stream, args); + if (!async || fn === rtn) return fn; + } + } else { + // need more bytes + return fn; + } +} + +var data = trampoline(_data); + +/** + * Generic thunk-based "trampoline" helper function. + * + * @param {Function} input function + * @return {Function} "trampolined" function + * @api private + */ + +function trampoline (fn) { + return function () { + var result = fn.apply(this, arguments); + + while ('function' == typeof result) { + result = result(); + } + + return result; + }; +} + +}).call(this)}).call(this,_dereq_('_process'),_dereq_("buffer").Buffer) +},{"_process":96,"assert":21,"buffer":28,"debug":115}],115:[function(_dereq_,module,exports){ +(function (process){(function (){ +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = _dereq_('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { + return true; + } + + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; + } +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs(args) { + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return; + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit') + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage() { + try { + return window.localStorage; + } catch (e) {} +} + +}).call(this)}).call(this,_dereq_('_process')) +},{"./debug":116,"_process":96}],116:[function(_dereq_,module,exports){ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = _dereq_('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + +exports.formatters = {}; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * @param {String} namespace + * @return {Number} + * @api private + */ + +function selectColor(namespace) { + var hash = 0, i; + + for (i in namespace) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return exports.colors[Math.abs(hash) % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function createDebug(namespace) { + + function debug() { + // disabled? + if (!debug.enabled) return; + + var self = debug; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // turn the `arguments` into a proper Array + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %O + args.unshift('%O'); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // apply env-specific formatting (colors, etc.) + exports.formatArgs.call(self, args); + + var logFn = debug.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = exports.enabled(namespace); + debug.useColors = exports.useColors(); + debug.color = selectColor(namespace); + + // env-specific initialization logic for debug instances + if ('function' === typeof exports.init) { + exports.init(debug); + } + + return debug; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + exports.names = []; + exports.skips = []; + + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + +},{"ms":117}],117:[function(_dereq_,module,exports){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; +} + +},{}],118:[function(_dereq_,module,exports){ +(function (setImmediate,clearImmediate){(function (){ +var nextTick = _dereq_('process/browser.js').nextTick; +var apply = Function.prototype.apply; +var slice = Array.prototype.slice; +var immediateIds = {}; +var nextImmediateId = 0; + +// DOM APIs, for completeness + +exports.setTimeout = function() { + return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout); +}; +exports.setInterval = function() { + return new Timeout(apply.call(setInterval, window, arguments), clearInterval); +}; +exports.clearTimeout = +exports.clearInterval = function(timeout) { timeout.close(); }; + +function Timeout(id, clearFn) { + this._id = id; + this._clearFn = clearFn; +} +Timeout.prototype.unref = Timeout.prototype.ref = function() {}; +Timeout.prototype.close = function() { + this._clearFn.call(window, this._id); +}; + +// Does not start the time, just sets up the members needed. +exports.enroll = function(item, msecs) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = msecs; +}; + +exports.unenroll = function(item) { + clearTimeout(item._idleTimeoutId); + item._idleTimeout = -1; +}; + +exports._unrefActive = exports.active = function(item) { + clearTimeout(item._idleTimeoutId); + + var msecs = item._idleTimeout; + if (msecs >= 0) { + item._idleTimeoutId = setTimeout(function onTimeout() { + if (item._onTimeout) + item._onTimeout(); + }, msecs); + } +}; + +// That's not how node.js implements it but the exposed api is the same. +exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) { + var id = nextImmediateId++; + var args = arguments.length < 2 ? false : slice.call(arguments, 1); + + immediateIds[id] = true; + + nextTick(function onNextTick() { + if (immediateIds[id]) { + // fn.call() is faster so we optimize for the common use-case + // @see http://jsperf.com/call-apply-segu + if (args) { + fn.apply(null, args); + } else { + fn.call(null); + } + // Prevent ids from leaking + exports.clearImmediate(id); + } + }); + + return id; +}; + +exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) { + delete immediateIds[id]; +}; +}).call(this)}).call(this,_dereq_("timers").setImmediate,_dereq_("timers").clearImmediate) +},{"process/browser.js":96,"timers":118}],119:[function(_dereq_,module,exports){ +// TinyColor v1.4.2 +// https://github.com/bgrins/TinyColor +// Brian Grinstead, MIT License + +(function(Math) { + +var trimLeft = /^\s+/, + trimRight = /\s+$/, + tinyCounter = 0, + mathRound = Math.round, + mathMin = Math.min, + mathMax = Math.max, + mathRandom = Math.random; + +function tinycolor (color, opts) { + + color = (color) ? color : ''; + opts = opts || { }; + + // If input is already a tinycolor, return itself + if (color instanceof tinycolor) { + return color; + } + // If we are called as a function, call using new instead + if (!(this instanceof tinycolor)) { + return new tinycolor(color, opts); + } + + var rgb = inputToRGB(color); + this._originalInput = color, + this._r = rgb.r, + this._g = rgb.g, + this._b = rgb.b, + this._a = rgb.a, + this._roundA = mathRound(100*this._a) / 100, + this._format = opts.format || rgb.format; + this._gradientType = opts.gradientType; + + // Don't let the range of [0,255] come back in [0,1]. + // Potentially lose a little bit of precision here, but will fix issues where + // .5 gets interpreted as half of the total, instead of half of 1 + // If it was supposed to be 128, this was already taken care of by `inputToRgb` + if (this._r < 1) { this._r = mathRound(this._r); } + if (this._g < 1) { this._g = mathRound(this._g); } + if (this._b < 1) { this._b = mathRound(this._b); } + + this._ok = rgb.ok; + this._tc_id = tinyCounter++; +} + +tinycolor.prototype = { + isDark: function() { + return this.getBrightness() < 128; + }, + isLight: function() { + return !this.isDark(); + }, + isValid: function() { + return this._ok; + }, + getOriginalInput: function() { + return this._originalInput; + }, + getFormat: function() { + return this._format; + }, + getAlpha: function() { + return this._a; + }, + getBrightness: function() { + //http://www.w3.org/TR/AERT#color-contrast + var rgb = this.toRgb(); + return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; + }, + getLuminance: function() { + //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + var rgb = this.toRgb(); + var RsRGB, GsRGB, BsRGB, R, G, B; + RsRGB = rgb.r/255; + GsRGB = rgb.g/255; + BsRGB = rgb.b/255; + + if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);} + if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);} + if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);} + return (0.2126 * R) + (0.7152 * G) + (0.0722 * B); + }, + setAlpha: function(value) { + this._a = boundAlpha(value); + this._roundA = mathRound(100*this._a) / 100; + return this; + }, + toHsv: function() { + var hsv = rgbToHsv(this._r, this._g, this._b); + return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; + }, + toHsvString: function() { + var hsv = rgbToHsv(this._r, this._g, this._b); + var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); + return (this._a == 1) ? + "hsv(" + h + ", " + s + "%, " + v + "%)" : + "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; + }, + toHsl: function() { + var hsl = rgbToHsl(this._r, this._g, this._b); + return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; + }, + toHslString: function() { + var hsl = rgbToHsl(this._r, this._g, this._b); + var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); + return (this._a == 1) ? + "hsl(" + h + ", " + s + "%, " + l + "%)" : + "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; + }, + toHex: function(allow3Char) { + return rgbToHex(this._r, this._g, this._b, allow3Char); + }, + toHexString: function(allow3Char) { + return '#' + this.toHex(allow3Char); + }, + toHex8: function(allow4Char) { + return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char); + }, + toHex8String: function(allow4Char) { + return '#' + this.toHex8(allow4Char); + }, + toRgb: function() { + return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; + }, + toRgbString: function() { + return (this._a == 1) ? + "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : + "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; + }, + toPercentageRgb: function() { + return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a }; + }, + toPercentageRgbString: function() { + return (this._a == 1) ? + "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : + "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; + }, + toName: function() { + if (this._a === 0) { + return "transparent"; + } + + if (this._a < 1) { + return false; + } + + return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; + }, + toFilter: function(secondColor) { + var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a); + var secondHex8String = hex8String; + var gradientType = this._gradientType ? "GradientType = 1, " : ""; + + if (secondColor) { + var s = tinycolor(secondColor); + secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a); + } + + return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; + }, + toString: function(format) { + var formatSet = !!format; + format = format || this._format; + + var formattedString = false; + var hasAlpha = this._a < 1 && this._a >= 0; + var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name"); + + if (needsAlphaFormat) { + // Special case for "transparent", all other non-alpha formats + // will return rgba when there is transparency. + if (format === "name" && this._a === 0) { + return this.toName(); + } + return this.toRgbString(); + } + if (format === "rgb") { + formattedString = this.toRgbString(); + } + if (format === "prgb") { + formattedString = this.toPercentageRgbString(); + } + if (format === "hex" || format === "hex6") { + formattedString = this.toHexString(); + } + if (format === "hex3") { + formattedString = this.toHexString(true); + } + if (format === "hex4") { + formattedString = this.toHex8String(true); + } + if (format === "hex8") { + formattedString = this.toHex8String(); + } + if (format === "name") { + formattedString = this.toName(); + } + if (format === "hsl") { + formattedString = this.toHslString(); + } + if (format === "hsv") { + formattedString = this.toHsvString(); + } + + return formattedString || this.toHexString(); + }, + clone: function() { + return tinycolor(this.toString()); + }, + + _applyModification: function(fn, args) { + var color = fn.apply(null, [this].concat([].slice.call(args))); + this._r = color._r; + this._g = color._g; + this._b = color._b; + this.setAlpha(color._a); + return this; + }, + lighten: function() { + return this._applyModification(lighten, arguments); + }, + brighten: function() { + return this._applyModification(brighten, arguments); + }, + darken: function() { + return this._applyModification(darken, arguments); + }, + desaturate: function() { + return this._applyModification(desaturate, arguments); + }, + saturate: function() { + return this._applyModification(saturate, arguments); + }, + greyscale: function() { + return this._applyModification(greyscale, arguments); + }, + spin: function() { + return this._applyModification(spin, arguments); + }, + + _applyCombination: function(fn, args) { + return fn.apply(null, [this].concat([].slice.call(args))); + }, + analogous: function() { + return this._applyCombination(analogous, arguments); + }, + complement: function() { + return this._applyCombination(complement, arguments); + }, + monochromatic: function() { + return this._applyCombination(monochromatic, arguments); + }, + splitcomplement: function() { + return this._applyCombination(splitcomplement, arguments); + }, + triad: function() { + return this._applyCombination(triad, arguments); + }, + tetrad: function() { + return this._applyCombination(tetrad, arguments); + } +}; + +// If input is an object, force 1 into "1.0" to handle ratios properly +// String input requires "1.0" as input, so 1 will be treated as 1 +tinycolor.fromRatio = function(color, opts) { + if (typeof color == "object") { + var newColor = {}; + for (var i in color) { + if (color.hasOwnProperty(i)) { + if (i === "a") { + newColor[i] = color[i]; + } + else { + newColor[i] = convertToPercentage(color[i]); + } + } + } + color = newColor; + } + + return tinycolor(color, opts); +}; + +// Given a string or object, convert that input to RGB +// Possible string inputs: +// +// "red" +// "#f00" or "f00" +// "#ff0000" or "ff0000" +// "#ff000000" or "ff000000" +// "rgb 255 0 0" or "rgb (255, 0, 0)" +// "rgb 1.0 0 0" or "rgb (1, 0, 0)" +// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" +// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" +// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" +// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" +// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" +// +function inputToRGB(color) { + + var rgb = { r: 0, g: 0, b: 0 }; + var a = 1; + var s = null; + var v = null; + var l = null; + var ok = false; + var format = false; + + if (typeof color == "string") { + color = stringInputToObject(color); + } + + if (typeof color == "object") { + if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) { + rgb = rgbToRgb(color.r, color.g, color.b); + ok = true; + format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; + } + else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) { + s = convertToPercentage(color.s); + v = convertToPercentage(color.v); + rgb = hsvToRgb(color.h, s, v); + ok = true; + format = "hsv"; + } + else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) { + s = convertToPercentage(color.s); + l = convertToPercentage(color.l); + rgb = hslToRgb(color.h, s, l); + ok = true; + format = "hsl"; + } + + if (color.hasOwnProperty("a")) { + a = color.a; + } + } + + a = boundAlpha(a); + + return { + ok: ok, + format: color.format || format, + r: mathMin(255, mathMax(rgb.r, 0)), + g: mathMin(255, mathMax(rgb.g, 0)), + b: mathMin(255, mathMax(rgb.b, 0)), + a: a + }; +} + + +// Conversion Functions +// -------------------- + +// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: +// + +// `rgbToRgb` +// Handle bounds / percentage checking to conform to CSS color spec +// +// *Assumes:* r, g, b in [0, 255] or [0, 1] +// *Returns:* { r, g, b } in [0, 255] +function rgbToRgb(r, g, b){ + return { + r: bound01(r, 255) * 255, + g: bound01(g, 255) * 255, + b: bound01(b, 255) * 255 + }; +} + +// `rgbToHsl` +// Converts an RGB color value to HSL. +// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] +// *Returns:* { h, s, l } in [0,1] +function rgbToHsl(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, l = (max + min) / 2; + + if(max == min) { + h = s = 0; // achromatic + } + else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + return { h: h, s: s, l: l }; +} + +// `hslToRgb` +// Converts an HSL color value to RGB. +// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] +// *Returns:* { r, g, b } in the set [0, 255] +function hslToRgb(h, s, l) { + var r, g, b; + + h = bound01(h, 360); + s = bound01(s, 100); + l = bound01(l, 100); + + function hue2rgb(p, q, t) { + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + if(s === 0) { + r = g = b = l; // achromatic + } + else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return { r: r * 255, g: g * 255, b: b * 255 }; +} + +// `rgbToHsv` +// Converts an RGB color value to HSV +// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] +// *Returns:* { h, s, v } in [0,1] +function rgbToHsv(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, v = max; + + var d = max - min; + s = max === 0 ? 0 : d / max; + + if(max == min) { + h = 0; // achromatic + } + else { + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h, s: s, v: v }; +} + +// `hsvToRgb` +// Converts an HSV color value to RGB. +// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] +// *Returns:* { r, g, b } in the set [0, 255] + function hsvToRgb(h, s, v) { + + h = bound01(h, 360) * 6; + s = bound01(s, 100); + v = bound01(v, 100); + + var i = Math.floor(h), + f = h - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + mod = i % 6, + r = [v, q, p, p, t, v][mod], + g = [t, v, v, q, p, p][mod], + b = [p, p, t, v, v, q][mod]; + + return { r: r * 255, g: g * 255, b: b * 255 }; +} + +// `rgbToHex` +// Converts an RGB color to hex +// Assumes r, g, and b are contained in the set [0, 255] +// Returns a 3 or 6 character hex +function rgbToHex(r, g, b, allow3Char) { + + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + // Return a 3 character hex if possible + if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + } + + return hex.join(""); +} + +// `rgbaToHex` +// Converts an RGBA color plus alpha transparency to hex +// Assumes r, g, b are contained in the set [0, 255] and +// a in [0, 1]. Returns a 4 or 8 character rgba hex +function rgbaToHex(r, g, b, a, allow4Char) { + + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)), + pad2(convertDecimalToHex(a)) + ]; + + // Return a 4 character hex if possible + if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0); + } + + return hex.join(""); +} + +// `rgbaToArgbHex` +// Converts an RGBA color to an ARGB Hex8 string +// Rarely used, but required for "toFilter()" +function rgbaToArgbHex(r, g, b, a) { + + var hex = [ + pad2(convertDecimalToHex(a)), + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + return hex.join(""); +} + +// `equals` +// Can be called with any tinycolor input +tinycolor.equals = function (color1, color2) { + if (!color1 || !color2) { return false; } + return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); +}; + +tinycolor.random = function() { + return tinycolor.fromRatio({ + r: mathRandom(), + g: mathRandom(), + b: mathRandom() + }); +}; + + +// Modification Functions +// ---------------------- +// Thanks to less.js for some of the basics here +// + +function desaturate(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s -= amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); +} + +function saturate(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s += amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); +} + +function greyscale(color) { + return tinycolor(color).desaturate(100); +} + +function lighten (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l += amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); +} + +function brighten(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var rgb = tinycolor(color).toRgb(); + rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); + rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); + rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); + return tinycolor(rgb); +} + +function darken (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l -= amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); +} + +// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. +// Values outside of this range will be wrapped into this range. +function spin(color, amount) { + var hsl = tinycolor(color).toHsl(); + var hue = (hsl.h + amount) % 360; + hsl.h = hue < 0 ? 360 + hue : hue; + return tinycolor(hsl); +} + +// Combination Functions +// --------------------- +// Thanks to jQuery xColor for some of the ideas behind these +// + +function complement(color) { + var hsl = tinycolor(color).toHsl(); + hsl.h = (hsl.h + 180) % 360; + return tinycolor(hsl); +} + +function triad(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) + ]; +} + +function tetrad(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) + ]; +} + +function splitcomplement(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), + tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) + ]; +} + +function analogous(color, results, slices) { + results = results || 6; + slices = slices || 30; + + var hsl = tinycolor(color).toHsl(); + var part = 360 / slices; + var ret = [tinycolor(color)]; + + for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { + hsl.h = (hsl.h + part) % 360; + ret.push(tinycolor(hsl)); + } + return ret; +} + +function monochromatic(color, results) { + results = results || 6; + var hsv = tinycolor(color).toHsv(); + var h = hsv.h, s = hsv.s, v = hsv.v; + var ret = []; + var modification = 1 / results; + + while (results--) { + ret.push(tinycolor({ h: h, s: s, v: v})); + v = (v + modification) % 1; + } + + return ret; +} + +// Utility Functions +// --------------------- + +tinycolor.mix = function(color1, color2, amount) { + amount = (amount === 0) ? 0 : (amount || 50); + + var rgb1 = tinycolor(color1).toRgb(); + var rgb2 = tinycolor(color2).toRgb(); + + var p = amount / 100; + + var rgba = { + r: ((rgb2.r - rgb1.r) * p) + rgb1.r, + g: ((rgb2.g - rgb1.g) * p) + rgb1.g, + b: ((rgb2.b - rgb1.b) * p) + rgb1.b, + a: ((rgb2.a - rgb1.a) * p) + rgb1.a + }; + + return tinycolor(rgba); +}; + + +// Readability Functions +// --------------------- +// false +// tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false +tinycolor.isReadable = function(color1, color2, wcag2) { + var readability = tinycolor.readability(color1, color2); + var wcag2Parms, out; + + out = false; + + wcag2Parms = validateWCAG2Parms(wcag2); + switch (wcag2Parms.level + wcag2Parms.size) { + case "AAsmall": + case "AAAlarge": + out = readability >= 4.5; + break; + case "AAlarge": + out = readability >= 3; + break; + case "AAAsmall": + out = readability >= 7; + break; + } + return out; + +}; + +// `mostReadable` +// Given a base color and a list of possible foreground or background +// colors for that base, returns the most readable color. +// Optionally returns Black or White if the most readable color is unreadable. +// *Example* +// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" +// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" +// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3" +// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff" +tinycolor.mostReadable = function(baseColor, colorList, args) { + var bestColor = null; + var bestScore = 0; + var readability; + var includeFallbackColors, level, size ; + args = args || {}; + includeFallbackColors = args.includeFallbackColors ; + level = args.level; + size = args.size; + + for (var i= 0; i < colorList.length ; i++) { + readability = tinycolor.readability(baseColor, colorList[i]); + if (readability > bestScore) { + bestScore = readability; + bestColor = tinycolor(colorList[i]); + } + } + + if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) { + return bestColor; + } + else { + args.includeFallbackColors=false; + return tinycolor.mostReadable(baseColor,["#fff", "#000"],args); + } +}; + + +// Big List of Colors +// ------------------ +// +var names = tinycolor.names = { + aliceblue: "f0f8ff", + antiquewhite: "faebd7", + aqua: "0ff", + aquamarine: "7fffd4", + azure: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "000", + blanchedalmond: "ffebcd", + blue: "00f", + blueviolet: "8a2be2", + brown: "a52a2a", + burlywood: "deb887", + burntsienna: "ea7e5d", + cadetblue: "5f9ea0", + chartreuse: "7fff00", + chocolate: "d2691e", + coral: "ff7f50", + cornflowerblue: "6495ed", + cornsilk: "fff8dc", + crimson: "dc143c", + cyan: "0ff", + darkblue: "00008b", + darkcyan: "008b8b", + darkgoldenrod: "b8860b", + darkgray: "a9a9a9", + darkgreen: "006400", + darkgrey: "a9a9a9", + darkkhaki: "bdb76b", + darkmagenta: "8b008b", + darkolivegreen: "556b2f", + darkorange: "ff8c00", + darkorchid: "9932cc", + darkred: "8b0000", + darksalmon: "e9967a", + darkseagreen: "8fbc8f", + darkslateblue: "483d8b", + darkslategray: "2f4f4f", + darkslategrey: "2f4f4f", + darkturquoise: "00ced1", + darkviolet: "9400d3", + deeppink: "ff1493", + deepskyblue: "00bfff", + dimgray: "696969", + dimgrey: "696969", + dodgerblue: "1e90ff", + firebrick: "b22222", + floralwhite: "fffaf0", + forestgreen: "228b22", + fuchsia: "f0f", + gainsboro: "dcdcdc", + ghostwhite: "f8f8ff", + gold: "ffd700", + goldenrod: "daa520", + gray: "808080", + green: "008000", + greenyellow: "adff2f", + grey: "808080", + honeydew: "f0fff0", + hotpink: "ff69b4", + indianred: "cd5c5c", + indigo: "4b0082", + ivory: "fffff0", + khaki: "f0e68c", + lavender: "e6e6fa", + lavenderblush: "fff0f5", + lawngreen: "7cfc00", + lemonchiffon: "fffacd", + lightblue: "add8e6", + lightcoral: "f08080", + lightcyan: "e0ffff", + lightgoldenrodyellow: "fafad2", + lightgray: "d3d3d3", + lightgreen: "90ee90", + lightgrey: "d3d3d3", + lightpink: "ffb6c1", + lightsalmon: "ffa07a", + lightseagreen: "20b2aa", + lightskyblue: "87cefa", + lightslategray: "789", + lightslategrey: "789", + lightsteelblue: "b0c4de", + lightyellow: "ffffe0", + lime: "0f0", + limegreen: "32cd32", + linen: "faf0e6", + magenta: "f0f", + maroon: "800000", + mediumaquamarine: "66cdaa", + mediumblue: "0000cd", + mediumorchid: "ba55d3", + mediumpurple: "9370db", + mediumseagreen: "3cb371", + mediumslateblue: "7b68ee", + mediumspringgreen: "00fa9a", + mediumturquoise: "48d1cc", + mediumvioletred: "c71585", + midnightblue: "191970", + mintcream: "f5fffa", + mistyrose: "ffe4e1", + moccasin: "ffe4b5", + navajowhite: "ffdead", + navy: "000080", + oldlace: "fdf5e6", + olive: "808000", + olivedrab: "6b8e23", + orange: "ffa500", + orangered: "ff4500", + orchid: "da70d6", + palegoldenrod: "eee8aa", + palegreen: "98fb98", + paleturquoise: "afeeee", + palevioletred: "db7093", + papayawhip: "ffefd5", + peachpuff: "ffdab9", + peru: "cd853f", + pink: "ffc0cb", + plum: "dda0dd", + powderblue: "b0e0e6", + purple: "800080", + rebeccapurple: "663399", + red: "f00", + rosybrown: "bc8f8f", + royalblue: "4169e1", + saddlebrown: "8b4513", + salmon: "fa8072", + sandybrown: "f4a460", + seagreen: "2e8b57", + seashell: "fff5ee", + sienna: "a0522d", + silver: "c0c0c0", + skyblue: "87ceeb", + slateblue: "6a5acd", + slategray: "708090", + slategrey: "708090", + snow: "fffafa", + springgreen: "00ff7f", + steelblue: "4682b4", + tan: "d2b48c", + teal: "008080", + thistle: "d8bfd8", + tomato: "ff6347", + turquoise: "40e0d0", + violet: "ee82ee", + wheat: "f5deb3", + white: "fff", + whitesmoke: "f5f5f5", + yellow: "ff0", + yellowgreen: "9acd32" +}; + +// Make it easy to access colors via `hexNames[hex]` +var hexNames = tinycolor.hexNames = flip(names); + + +// Utilities +// --------- + +// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` +function flip(o) { + var flipped = { }; + for (var i in o) { + if (o.hasOwnProperty(i)) { + flipped[o[i]] = i; + } + } + return flipped; +} + +// Return a valid alpha value [0,1] with all invalid values being set to 1 +function boundAlpha(a) { + a = parseFloat(a); + + if (isNaN(a) || a < 0 || a > 1) { + a = 1; + } + + return a; +} + +// Take input from [0, n] and return it as [0, 1] +function bound01(n, max) { + if (isOnePointZero(n)) { n = "100%"; } + + var processPercent = isPercentage(n); + n = mathMin(max, mathMax(0, parseFloat(n))); + + // Automatically convert percentage into number + if (processPercent) { + n = parseInt(n * max, 10) / 100; + } + + // Handle floating point rounding errors + if ((Math.abs(n - max) < 0.000001)) { + return 1; + } + + // Convert into [0, 1] range if it isn't already + return (n % max) / parseFloat(max); +} + +// Force a number between 0 and 1 +function clamp01(val) { + return mathMin(1, mathMax(0, val)); +} + +// Parse a base-16 hex value into a base-10 integer +function parseIntFromHex(val) { + return parseInt(val, 16); +} + +// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 +// +function isOnePointZero(n) { + return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; +} + +// Check to see if string passed in is a percentage +function isPercentage(n) { + return typeof n === "string" && n.indexOf('%') != -1; +} + +// Force a hex value to have 2 characters +function pad2(c) { + return c.length == 1 ? '0' + c : '' + c; +} + +// Replace a decimal with it's percentage value +function convertToPercentage(n) { + if (n <= 1) { + n = (n * 100) + "%"; + } + + return n; +} + +// Converts a decimal to a hex value +function convertDecimalToHex(d) { + return Math.round(parseFloat(d) * 255).toString(16); +} +// Converts a hex value to a decimal +function convertHexToDecimal(h) { + return (parseIntFromHex(h) / 255); +} + +var matchers = (function() { + + // + var CSS_INTEGER = "[-\\+]?\\d+%?"; + + // + var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; + + // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. + var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; + + // Actual matching. + // Parentheses and commas are optional, but not required. + // Whitespace can take the place of commas or opening paren + var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + + return { + CSS_UNIT: new RegExp(CSS_UNIT), + rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), + rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), + hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), + hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), + hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), + hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), + hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, + hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ + }; +})(); + +// `isValidCSSUnit` +// Take in a single string / number and check to see if it looks like a CSS unit +// (see `matchers` above for definition). +function isValidCSSUnit(color) { + return !!matchers.CSS_UNIT.exec(color); +} + +// `stringInputToObject` +// Permissive string parsing. Take in a number of formats, and output an object +// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` +function stringInputToObject(color) { + + color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); + var named = false; + if (names[color]) { + color = names[color]; + named = true; + } + else if (color == 'transparent') { + return { r: 0, g: 0, b: 0, a: 0, format: "name" }; + } + + // Try to match string input using regular expressions. + // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] + // Just return an object and let the conversion functions handle that. + // This way the result will be the same whether the tinycolor is initialized with string or object. + var match; + if ((match = matchers.rgb.exec(color))) { + return { r: match[1], g: match[2], b: match[3] }; + } + if ((match = matchers.rgba.exec(color))) { + return { r: match[1], g: match[2], b: match[3], a: match[4] }; + } + if ((match = matchers.hsl.exec(color))) { + return { h: match[1], s: match[2], l: match[3] }; + } + if ((match = matchers.hsla.exec(color))) { + return { h: match[1], s: match[2], l: match[3], a: match[4] }; + } + if ((match = matchers.hsv.exec(color))) { + return { h: match[1], s: match[2], v: match[3] }; + } + if ((match = matchers.hsva.exec(color))) { + return { h: match[1], s: match[2], v: match[3], a: match[4] }; + } + if ((match = matchers.hex8.exec(color))) { + return { + r: parseIntFromHex(match[1]), + g: parseIntFromHex(match[2]), + b: parseIntFromHex(match[3]), + a: convertHexToDecimal(match[4]), + format: named ? "name" : "hex8" + }; + } + if ((match = matchers.hex6.exec(color))) { + return { + r: parseIntFromHex(match[1]), + g: parseIntFromHex(match[2]), + b: parseIntFromHex(match[3]), + format: named ? "name" : "hex" + }; + } + if ((match = matchers.hex4.exec(color))) { + return { + r: parseIntFromHex(match[1] + '' + match[1]), + g: parseIntFromHex(match[2] + '' + match[2]), + b: parseIntFromHex(match[3] + '' + match[3]), + a: convertHexToDecimal(match[4] + '' + match[4]), + format: named ? "name" : "hex8" + }; + } + if ((match = matchers.hex3.exec(color))) { + return { + r: parseIntFromHex(match[1] + '' + match[1]), + g: parseIntFromHex(match[2] + '' + match[2]), + b: parseIntFromHex(match[3] + '' + match[3]), + format: named ? "name" : "hex" + }; + } + + return false; +} + +function validateWCAG2Parms(parms) { + // return valid WCAG2 parms for isReadable. + // If input parms are invalid, return {"level":"AA", "size":"small"} + var level, size; + parms = parms || {"level":"AA", "size":"small"}; + level = (parms.level || "AA").toUpperCase(); + size = (parms.size || "small").toLowerCase(); + if (level !== "AA" && level !== "AAA") { + level = "AA"; + } + if (size !== "small" && size !== "large") { + size = "small"; + } + return {"level":level, "size":size}; +} + +// Node: Export function +if (typeof module !== "undefined" && module.exports) { + module.exports = tinycolor; +} +// AMD/requirejs: Define the module +else if (typeof define === 'function' && define.amd) { + define(function () {return tinycolor;}); +} +// Browser: Expose to window +else { + window.tinycolor = tinycolor; +} + +})(Math); + +},{}],120:[function(_dereq_,module,exports){ +(function (global){(function (){ + +/** + * Module exports. + */ + +module.exports = deprecate; + +/** + * Mark that a method should not be used. + * Returns a modified function which warns once by default. + * + * If `localStorage.noDeprecation = true` is set, then it is a no-op. + * + * If `localStorage.throwDeprecation = true` is set, then deprecated functions + * will throw an Error when invoked. + * + * If `localStorage.traceDeprecation = true` is set, then deprecated functions + * will invoke `console.trace()` instead of `console.error()`. + * + * @param {Function} fn - the function to deprecate + * @param {String} msg - the string to print to the console when `fn` is invoked + * @returns {Function} a new "deprecated" version of `fn` + * @api public + */ + +function deprecate (fn, msg) { + if (config('noDeprecation')) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (config('throwDeprecation')) { + throw new Error(msg); + } else if (config('traceDeprecation')) { + console.trace(msg); + } else { + console.warn(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +} + +/** + * Checks `localStorage` for boolean values for the given `name`. + * + * @param {String} name + * @returns {Boolean} + * @api private + */ + +function config (name) { + // accessing global.localStorage can trigger a DOMException in sandboxed iframes + try { + if (!global.localStorage) return false; + } catch (_) { + return false; + } + var val = global.localStorage[name]; + if (null == val) return false; + return String(val).toLowerCase() === 'true'; +} + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],121:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Traditional Chinese calendar for jQuery v2.0.2. + Written by Nicolas Riesco (enquiries@nicolasriesco.net) December 2016. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +var gregorianCalendar = main.instance(); + +/** Implementation of the traditional Chinese calendar. + Source of calendar tables https://github.com/isee15/Lunar-Solar-Calendar-Converter . + @class ChineseCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function ChineseCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +ChineseCalendar.prototype = new main.baseCalendar; + +assign(ChineseCalendar.prototype, { + /** The calendar name. + @memberof ChineseCalendar */ + name: 'Chinese', + /** Julian date of start of Gregorian epoch: 1 January 0001 CE. + @memberof GregorianCalendar */ + jdEpoch: 1721425.5, + /** true if has a year zero, false if not. + @memberof ChineseCalendar */ + hasYearZero: false, + /** The minimum month number. + This calendar uses month indices to account for intercalary months. + @memberof ChineseCalendar */ + minMonth: 0, + /** The first month in the year. + This calendar uses month indices to account for intercalary months. + @memberof ChineseCalendar */ + firstMonth: 0, + /** The minimum day number. + @memberof ChineseCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof ChineseCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Chinese', + epochs: ['BEC', 'EC'], + monthNumbers: function(date, padded) { + if (typeof date === 'string') { + var match = date.match(MONTH_NUMBER_REGEXP); + return (match) ? match[0] : ''; + } + + var year = this._validateYear(date); + var monthIndex = date.month(); + + var month = '' + this.toChineseMonth(year, monthIndex); + + if (padded && month.length < 2) { + month = "0" + month; + } + + if (this.isIntercalaryMonth(year, monthIndex)) { + month += 'i'; + } + + return month; + }, + monthNames: function(date) { + if (typeof date === 'string') { + var match = date.match(MONTH_NAME_REGEXP); + return (match) ? match[0] : ''; + } + + var year = this._validateYear(date); + var monthIndex = date.month(); + + var month = this.toChineseMonth(year, monthIndex); + + var monthName = ['一月','二月','三月','四月','五月','六月', + '七月','八月','九月','十月','十一月','十二月'][month - 1]; + + if (this.isIntercalaryMonth(year, monthIndex)) { + monthName = '闰' + monthName; + } + + return monthName; + }, + monthNamesShort: function(date) { + if (typeof date === 'string') { + var match = date.match(MONTH_SHORT_NAME_REGEXP); + return (match) ? match[0] : ''; + } + + var year = this._validateYear(date); + var monthIndex = date.month(); + + var month = this.toChineseMonth(year, monthIndex); + + var monthName = ['一','二','三','四','五','六', + '七','八','九','十','十一','十二'][month - 1]; + + if (this.isIntercalaryMonth(year, monthIndex)) { + monthName = '闰' + monthName; + } + + return monthName; + }, + parseMonth: function(year, monthString) { + year = this._validateYear(year); + var month = parseInt(monthString); + var isIntercalary; + + if (!isNaN(month)) { + var i = monthString[monthString.length - 1]; + isIntercalary = (i === 'i' || i === 'I'); + } else { + if (monthString[0] === '闰') { + isIntercalary = true; + monthString = monthString.substring(1); + } + if (monthString[monthString.length - 1] === '月') { + monthString = monthString.substring(0, monthString.length - 1); + } + month = 1 + + ['一','二','三','四','五','六', + '七','八','九','十','十一','十二'].indexOf(monthString); + } + + var monthIndex = this.toMonthIndex(year, month, isIntercalary); + return monthIndex; + }, + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + digits: null, + dateFormat: 'yyyy/mm/dd', + firstDay: 1, + isRTL: false + } + }, + + /** Check that a candidate date is from the same calendar and is valid. + @memberof BaseCalendar + @private + @param year {CDate|number} The date or the year to validate. + @param error {string} Error message if invalid. + @return {number} The year. + @throws Error if year out of range. */ + _validateYear: function(year, error) { + if (year.year) { + year = year.year(); + } + + if (typeof year !== 'number' || year < 1888 || year > 2111) { + throw error.replace(/\{0\}/, this.local.name); + } + + return year; + }, + + /** Retrieve the month index (i.e. accounting for intercalary months). + @memberof ChineseCalendar + @param year {number} The year. + @param month {number} The month (1 for first month). + @param [isIntercalary=false] {boolean} If month is intercalary. + @return {number} The month index (0 for first month). + @throws Error if an invalid month/year or a different calendar used. */ + toMonthIndex: function(year, month, isIntercalary) { + // compute intercalary month in the year (0 if none) + var intercalaryMonth = this.intercalaryMonth(year); + + // validate month + var invalidIntercalaryMonth = + (isIntercalary && month !== intercalaryMonth); + if (invalidIntercalaryMonth || month < 1 || month > 12) { + throw main.local.invalidMonth + .replace(/\{0\}/, this.local.name); + } + + // compute month index + var monthIndex; + + if (!intercalaryMonth) { + monthIndex = month - 1; + } else if(!isIntercalary && month <= intercalaryMonth) { + monthIndex = month - 1; + } else { + monthIndex = month; + } + + return monthIndex; + }, + + /** Retrieve the month (i.e. accounting for intercalary months). + @memberof ChineseCalendar + @param year {CDate|number} The date or the year to examine. + @param monthIndex {number} The month index (0 for first month). + @return {number} The month (1 for first month). + @throws Error if an invalid month/year or a different calendar used. */ + toChineseMonth: function(year, monthIndex) { + if (year.year) { + year = year.year(); + monthIndex = year.month(); + } + + // compute intercalary month in the year (0 if none) + var intercalaryMonth = this.intercalaryMonth(year); + + // validate month + var maxMonthIndex = (intercalaryMonth) ? 12 : 11; + if (monthIndex < 0 || monthIndex > maxMonthIndex) { + throw main.local.invalidMonth + .replace(/\{0\}/, this.local.name); + } + + // compute Chinese month + var month; + + if (!intercalaryMonth) { + month = monthIndex + 1; + } else if(monthIndex < intercalaryMonth) { + month = monthIndex + 1; + } else { + month = monthIndex; + } + + return month; + }, + + /** Determine the intercalary month of a year (if any). + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The intercalary month number, or 0 if none. + @throws Error if an invalid year or a different calendar used. */ + intercalaryMonth: function(year) { + year = this._validateYear(year); + + var monthDaysTable = LUNAR_MONTH_DAYS[year - LUNAR_MONTH_DAYS[0]]; + var intercalaryMonth = monthDaysTable >> 13; + + return intercalaryMonth; + }, + + /** Determine whether this date is an intercalary month. + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [monthIndex] {number} The month index to examine. + @return {boolean} true if this is an intercalary month, false if not. + @throws Error if an invalid year or a different calendar used. */ + isIntercalaryMonth: function(year, monthIndex) { + if (year.year) { + year = year.year(); + monthIndex = year.month(); + } + + var intercalaryMonth = this.intercalaryMonth(year); + + return !!intercalaryMonth && intercalaryMonth === monthIndex; + }, + + /** Determine whether this date is in a leap year. + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + return (this.intercalaryMonth(year) !== 0); + }, + + /** Determine the week of the year for a date - ISO 8601. + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [monthIndex] {number} The month index to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, monthIndex, day) { + // compute Chinese new year + var validatedYear = + this._validateYear(year, main.local.invalidyear); + var packedDate = + CHINESE_NEW_YEAR[validatedYear - CHINESE_NEW_YEAR[0]]; + + var y = (packedDate >> 9) & 0xFFF; + var m = (packedDate >> 5) & 0x0F; + var d = packedDate & 0x1F; + + // find first Thrusday of the year + var firstThursday; + firstThursday = gregorianCalendar.newDate(y, m, d); + firstThursday.add(4 - (firstThursday.dayOfWeek() || 7), 'd'); + + // compute days from first Thursday + var offset = + this.toJD(year, monthIndex, day) - firstThursday.toJD(); + return 1 + Math.floor(offset / 7); + }, + + /** Retrieve the number of months in a year. + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + return (this.leapYear(year)) ? 13 : 12; + }, + + /** Retrieve the number of days in a month. + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [monthIndex] {number} The month index. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, monthIndex) { + if (year.year) { + monthIndex = year.month(); + year = year.year(); + } + + year = this._validateYear(year); + + var monthDaysTable = LUNAR_MONTH_DAYS[year - LUNAR_MONTH_DAYS[0]]; + + var intercalaryMonth = monthDaysTable >> 13; + var maxMonthIndex = (intercalaryMonth) ? 12 : 11; + if (monthIndex > maxMonthIndex) { + throw main.local.invalidMonth + .replace(/\{0\}/, this.local.name); + } + + var daysInMonth = (monthDaysTable & (1 << (12 - monthIndex))) ? + 30 : 29; + + return daysInMonth; + }, + + /** Determine whether this date is a week day. + @memberof ChineseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [monthIndex] {number} The month index to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, monthIndex, day) { + return (this.dayOfWeek(year, monthIndex, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof ChineseCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [monthIndex] {number} The month index to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, monthIndex, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = this._validateYear(date.year()); + monthIndex = date.month(); + day = date.day(); + + var isIntercalary = this.isIntercalaryMonth(year, monthIndex); + var month = this.toChineseMonth(year, monthIndex); + + var solar = toSolar(year, month, day, isIntercalary); + + return gregorianCalendar.toJD(solar.year, solar.month, solar.day); + }, + + /** Create a new date from a Julian date. + @memberof ChineseCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + var date = gregorianCalendar.fromJD(jd); + var lunar = toLunar(date.year(), date.month(), date.day()); + var monthIndex = this.toMonthIndex( + lunar.year, lunar.month, lunar.isIntercalary); + return this.newDate(lunar.year, monthIndex, lunar.day); + }, + + /** Create a new date from a string. + @memberof ChineseCalendar + @param dateString {string} String representing a Chinese date + @return {CDate} The new date. + @throws Error if an invalid date. */ + fromString: function(dateString) { + var match = dateString.match(DATE_REGEXP); + + var year = this._validateYear(+match[1]); + + var month = +match[2]; + var isIntercalary = !!match[3]; + var monthIndex = this.toMonthIndex(year, month, isIntercalary); + + var day = +match[4]; + + return this.newDate(year, monthIndex, day); + }, + + /** Add period(s) to a date. + Cater for no year zero. + @memberof ChineseCalendar + @param date {CDate} The starting date. + @param offset {number} The number of periods to adjust by. + @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day. + @return {CDate} The updated date. + @throws Error if a different calendar used. */ + add: function(date, offset, period) { + var year = date.year(); + var monthIndex = date.month(); + var isIntercalary = this.isIntercalaryMonth(year, monthIndex); + var month = this.toChineseMonth(year, monthIndex); + + var cdate = Object.getPrototypeOf(ChineseCalendar.prototype) + .add.call(this, date, offset, period); + + if (period === 'y') { + // Resync month + var resultYear = cdate.year(); + var resultMonthIndex = cdate.month(); + + // Using the fact the month index of an intercalary month + // equals its month number: + var resultCanBeIntercalaryMonth = + this.isIntercalaryMonth(resultYear, month); + + var correctedMonthIndex = + (isIntercalary && resultCanBeIntercalaryMonth) ? + this.toMonthIndex(resultYear, month, true) : + this.toMonthIndex(resultYear, month, false); + + if (correctedMonthIndex !== resultMonthIndex) { + cdate.month(correctedMonthIndex); + } + } + + return cdate; + }, +}); + +// Used by ChineseCalendar.prototype.fromString +var DATE_REGEXP = /^\s*(-?\d\d\d\d|\d\d)[-/](\d?\d)([iI]?)[-/](\d?\d)/m; +var MONTH_NUMBER_REGEXP = /^\d?\d[iI]?/m; +var MONTH_NAME_REGEXP = /^闰?十?[一二三四五六七八九]?月/m; +var MONTH_SHORT_NAME_REGEXP = /^闰?十?[一二三四五六七八九]?/m; + +// Chinese calendar implementation +main.calendars.chinese = ChineseCalendar; + +// Chinese calendar tables from year 1888 to 2111 +// +// Source: +// https://github.com/isee15/Lunar-Solar-Calendar-Converter.git + +// Table of intercalary months and days per month from year 1888 to 2111 +// +// bit (12 - i): days in the i^th month +// (= 0 if i^th lunar month has 29 days) +// (= 1 if i^th lunar month has 30 days) +// (first month in lunar year is i = 0) +// bits (13,14,15,16): intercalary month +// (= 0 if lunar year has no intercalary month) +var LUNAR_MONTH_DAYS = [1887, 0x1694, 0x16aa, 0x4ad5, + 0xab6, 0xc4b7, 0x4ae, 0xa56, 0xb52a, 0x1d2a, 0xd54, 0x75aa, 0x156a, + 0x1096d, 0x95c, 0x14ae, 0xaa4d, 0x1a4c, 0x1b2a, 0x8d55, 0xad4, + 0x135a, 0x495d, 0x95c, 0xd49b, 0x149a, 0x1a4a, 0xbaa5, 0x16a8, + 0x1ad4, 0x52da, 0x12b6, 0xe937, 0x92e, 0x1496, 0xb64b, 0xd4a, + 0xda8, 0x95b5, 0x56c, 0x12ae, 0x492f, 0x92e, 0xcc96, 0x1a94, + 0x1d4a, 0xada9, 0xb5a, 0x56c, 0x726e, 0x125c, 0xf92d, 0x192a, + 0x1a94, 0xdb4a, 0x16aa, 0xad4, 0x955b, 0x4ba, 0x125a, 0x592b, + 0x152a, 0xf695, 0xd94, 0x16aa, 0xaab5, 0x9b4, 0x14b6, 0x6a57, + 0xa56, 0x1152a, 0x1d2a, 0xd54, 0xd5aa, 0x156a, 0x96c, 0x94ae, + 0x14ae, 0xa4c, 0x7d26, 0x1b2a, 0xeb55, 0xad4, 0x12da, 0xa95d, + 0x95a, 0x149a, 0x9a4d, 0x1a4a, 0x11aa5, 0x16a8, 0x16d4, 0xd2da, + 0x12b6, 0x936, 0x9497, 0x1496, 0x1564b, 0xd4a, 0xda8, 0xd5b4, + 0x156c, 0x12ae, 0xa92f, 0x92e, 0xc96, 0x6d4a, 0x1d4a, 0x10d65, + 0xb58, 0x156c, 0xb26d, 0x125c, 0x192c, 0x9a95, 0x1a94, 0x1b4a, + 0x4b55, 0xad4, 0xf55b, 0x4ba, 0x125a, 0xb92b, 0x152a, 0x1694, + 0x96aa, 0x15aa, 0x12ab5, 0x974, 0x14b6, 0xca57, 0xa56, 0x1526, + 0x8e95, 0xd54, 0x15aa, 0x49b5, 0x96c, 0xd4ae, 0x149c, 0x1a4c, + 0xbd26, 0x1aa6, 0xb54, 0x6d6a, 0x12da, 0x1695d, 0x95a, 0x149a, + 0xda4b, 0x1a4a, 0x1aa4, 0xbb54, 0x16b4, 0xada, 0x495b, 0x936, + 0xf497, 0x1496, 0x154a, 0xb6a5, 0xda4, 0x15b4, 0x6ab6, 0x126e, + 0x1092f, 0x92e, 0xc96, 0xcd4a, 0x1d4a, 0xd64, 0x956c, 0x155c, + 0x125c, 0x792e, 0x192c, 0xfa95, 0x1a94, 0x1b4a, 0xab55, 0xad4, + 0x14da, 0x8a5d, 0xa5a, 0x1152b, 0x152a, 0x1694, 0xd6aa, 0x15aa, + 0xab4, 0x94ba, 0x14b6, 0xa56, 0x7527, 0xd26, 0xee53, 0xd54, 0x15aa, + 0xa9b5, 0x96c, 0x14ae, 0x8a4e, 0x1a4c, 0x11d26, 0x1aa4, 0x1b54, + 0xcd6a, 0xada, 0x95c, 0x949d, 0x149a, 0x1a2a, 0x5b25, 0x1aa4, + 0xfb52, 0x16b4, 0xaba, 0xa95b, 0x936, 0x1496, 0x9a4b, 0x154a, + 0x136a5, 0xda4, 0x15ac]; + +// Table of Chinese New Years from year 1888 to 2111 +// +// bits (0 to 4): solar day +// bits (5 to 8): solar month +// bits (9 to 20): solar year +var CHINESE_NEW_YEAR = [1887, 0xec04c, 0xec23f, 0xec435, 0xec649, + 0xec83e, 0xeca51, 0xecc46, 0xece3a, 0xed04d, 0xed242, 0xed436, + 0xed64a, 0xed83f, 0xeda53, 0xedc48, 0xede3d, 0xee050, 0xee244, + 0xee439, 0xee64d, 0xee842, 0xeea36, 0xeec4a, 0xeee3e, 0xef052, + 0xef246, 0xef43a, 0xef64e, 0xef843, 0xefa37, 0xefc4b, 0xefe41, + 0xf0054, 0xf0248, 0xf043c, 0xf0650, 0xf0845, 0xf0a38, 0xf0c4d, + 0xf0e42, 0xf1037, 0xf124a, 0xf143e, 0xf1651, 0xf1846, 0xf1a3a, + 0xf1c4e, 0xf1e44, 0xf2038, 0xf224b, 0xf243f, 0xf2653, 0xf2848, + 0xf2a3b, 0xf2c4f, 0xf2e45, 0xf3039, 0xf324d, 0xf3442, 0xf3636, + 0xf384a, 0xf3a3d, 0xf3c51, 0xf3e46, 0xf403b, 0xf424e, 0xf4443, + 0xf4638, 0xf484c, 0xf4a3f, 0xf4c52, 0xf4e48, 0xf503c, 0xf524f, + 0xf5445, 0xf5639, 0xf584d, 0xf5a42, 0xf5c35, 0xf5e49, 0xf603e, + 0xf6251, 0xf6446, 0xf663b, 0xf684f, 0xf6a43, 0xf6c37, 0xf6e4b, + 0xf703f, 0xf7252, 0xf7447, 0xf763c, 0xf7850, 0xf7a45, 0xf7c39, + 0xf7e4d, 0xf8042, 0xf8254, 0xf8449, 0xf863d, 0xf8851, 0xf8a46, + 0xf8c3b, 0xf8e4f, 0xf9044, 0xf9237, 0xf944a, 0xf963f, 0xf9853, + 0xf9a47, 0xf9c3c, 0xf9e50, 0xfa045, 0xfa238, 0xfa44c, 0xfa641, + 0xfa836, 0xfaa49, 0xfac3d, 0xfae52, 0xfb047, 0xfb23a, 0xfb44e, + 0xfb643, 0xfb837, 0xfba4a, 0xfbc3f, 0xfbe53, 0xfc048, 0xfc23c, + 0xfc450, 0xfc645, 0xfc839, 0xfca4c, 0xfcc41, 0xfce36, 0xfd04a, + 0xfd23d, 0xfd451, 0xfd646, 0xfd83a, 0xfda4d, 0xfdc43, 0xfde37, + 0xfe04b, 0xfe23f, 0xfe453, 0xfe648, 0xfe83c, 0xfea4f, 0xfec44, + 0xfee38, 0xff04c, 0xff241, 0xff436, 0xff64a, 0xff83e, 0xffa51, + 0xffc46, 0xffe3a, 0x10004e, 0x100242, 0x100437, 0x10064b, 0x100841, + 0x100a53, 0x100c48, 0x100e3c, 0x10104f, 0x101244, 0x101438, + 0x10164c, 0x101842, 0x101a35, 0x101c49, 0x101e3d, 0x102051, + 0x102245, 0x10243a, 0x10264e, 0x102843, 0x102a37, 0x102c4b, + 0x102e3f, 0x103053, 0x103247, 0x10343b, 0x10364f, 0x103845, + 0x103a38, 0x103c4c, 0x103e42, 0x104036, 0x104249, 0x10443d, + 0x104651, 0x104846, 0x104a3a, 0x104c4e, 0x104e43, 0x105038, + 0x10524a, 0x10543e, 0x105652, 0x105847, 0x105a3b, 0x105c4f, + 0x105e45, 0x106039, 0x10624c, 0x106441, 0x106635, 0x106849, + 0x106a3d, 0x106c51, 0x106e47, 0x10703c, 0x10724f, 0x107444, + 0x107638, 0x10784c, 0x107a3f, 0x107c53, 0x107e48]; + +function toLunar(yearOrDate, monthOrResult, day, result) { + var solarDate; + var lunarDate; + + if(typeof yearOrDate === 'object') { + solarDate = yearOrDate; + lunarDate = monthOrResult || {}; + + } else { + var isValidYear = (typeof yearOrDate === 'number') && + (yearOrDate >= 1888) && (yearOrDate <= 2111); + if(!isValidYear) + throw new Error("Solar year outside range 1888-2111"); + + var isValidMonth = (typeof monthOrResult === 'number') && + (monthOrResult >= 1) && (monthOrResult <= 12); + if(!isValidMonth) + throw new Error("Solar month outside range 1 - 12"); + + var isValidDay = (typeof day === 'number') && (day >= 1) && (day <= 31); + if(!isValidDay) + throw new Error("Solar day outside range 1 - 31"); + + solarDate = { + year: yearOrDate, + month: monthOrResult, + day: day, + }; + lunarDate = result || {}; + } + + // Compute Chinese new year and lunar year + var chineseNewYearPackedDate = + CHINESE_NEW_YEAR[solarDate.year - CHINESE_NEW_YEAR[0]]; + + var packedDate = (solarDate.year << 9) | (solarDate.month << 5) + | solarDate.day; + + lunarDate.year = (packedDate >= chineseNewYearPackedDate) ? + solarDate.year : + solarDate.year - 1; + + chineseNewYearPackedDate = + CHINESE_NEW_YEAR[lunarDate.year - CHINESE_NEW_YEAR[0]]; + + var y = (chineseNewYearPackedDate >> 9) & 0xFFF; + var m = (chineseNewYearPackedDate >> 5) & 0x0F; + var d = chineseNewYearPackedDate & 0x1F; + + // Compute days from new year + var daysFromNewYear; + + var chineseNewYearJSDate = new Date(y, m -1, d); + var jsDate = new Date(solarDate.year, solarDate.month - 1, solarDate.day); + + daysFromNewYear = Math.round( + (jsDate - chineseNewYearJSDate) / (24 * 3600 * 1000)); + + // Compute lunar month and day + var monthDaysTable = LUNAR_MONTH_DAYS[lunarDate.year - LUNAR_MONTH_DAYS[0]]; + + var i; + for(i = 0; i < 13; i++) { + var daysInMonth = (monthDaysTable & (1 << (12 - i))) ? 30 : 29; + + if (daysFromNewYear < daysInMonth) { + break; + } + + daysFromNewYear -= daysInMonth; + } + + var intercalaryMonth = monthDaysTable >> 13; + if (!intercalaryMonth || i < intercalaryMonth) { + lunarDate.isIntercalary = false; + lunarDate.month = 1 + i; + } else if (i === intercalaryMonth) { + lunarDate.isIntercalary = true; + lunarDate.month = i; + } else { + lunarDate.isIntercalary = false; + lunarDate.month = i; + } + + lunarDate.day = 1 + daysFromNewYear; + + return lunarDate; +} + +function toSolar(yearOrDate, monthOrResult, day, isIntercalaryOrResult, result) { + var solarDate; + var lunarDate; + + if(typeof yearOrDate === 'object') { + lunarDate = yearOrDate; + solarDate = monthOrResult || {}; + + } else { + var isValidYear = (typeof yearOrDate === 'number') && + (yearOrDate >= 1888) && (yearOrDate <= 2111); + if(!isValidYear) + throw new Error("Lunar year outside range 1888-2111"); + + var isValidMonth = (typeof monthOrResult === 'number') && + (monthOrResult >= 1) && (monthOrResult <= 12); + if(!isValidMonth) + throw new Error("Lunar month outside range 1 - 12"); + + var isValidDay = (typeof day === 'number') && (day >= 1) && (day <= 30); + if(!isValidDay) + throw new Error("Lunar day outside range 1 - 30"); + + var isIntercalary; + if(typeof isIntercalaryOrResult === 'object') { + isIntercalary = false; + solarDate = isIntercalaryOrResult; + } else { + isIntercalary = !!isIntercalaryOrResult; + solarDate = result || {}; + } + + lunarDate = { + year: yearOrDate, + month: monthOrResult, + day: day, + isIntercalary: isIntercalary, + }; + } + + // Compute days from new year + var daysFromNewYear; + + daysFromNewYear = lunarDate.day - 1; + + var monthDaysTable = LUNAR_MONTH_DAYS[lunarDate.year - LUNAR_MONTH_DAYS[0]]; + var intercalaryMonth = monthDaysTable >> 13; + + var monthsFromNewYear; + if (!intercalaryMonth) { + monthsFromNewYear = lunarDate.month - 1; + } else if (lunarDate.month > intercalaryMonth) { + monthsFromNewYear = lunarDate.month; + } else if (lunarDate.isIntercalary) { + monthsFromNewYear = lunarDate.month; + } else { + monthsFromNewYear = lunarDate.month - 1; + } + + for(var i = 0; i < monthsFromNewYear; i++) { + var daysInMonth = (monthDaysTable & (1 << (12 - i))) ? 30 : 29; + daysFromNewYear += daysInMonth; + } + + // Compute Chinese new year + var packedDate = CHINESE_NEW_YEAR[lunarDate.year - CHINESE_NEW_YEAR[0]]; + + var y = (packedDate >> 9) & 0xFFF; + var m = (packedDate >> 5) & 0x0F; + var d = packedDate & 0x1F; + + // Compute solar date + var jsDate = new Date(y, m - 1, d + daysFromNewYear); + + solarDate.year = jsDate.getFullYear(); + solarDate.month = 1 + jsDate.getMonth(); + solarDate.day = jsDate.getDate(); + + return solarDate; +} + + +},{"../main":135,"object-assign":71}],122:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Coptic calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Coptic calendar. + See http://en.wikipedia.org/wiki/Coptic_calendar. + See also Calendrical Calculations: The Millennium Edition + (http://emr.cs.iit.edu/home/reingold/calendar-book/index.shtml). + @class CopticCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function CopticCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +CopticCalendar.prototype = new main.baseCalendar; + +assign(CopticCalendar.prototype, { + /** The calendar name. + @memberof CopticCalendar */ + name: 'Coptic', + /** Julian date of start of Coptic epoch: 29 August 284 CE (Gregorian). + @memberof CopticCalendar */ + jdEpoch: 1825029.5, + /** Days per month in a common year. + @memberof CopticCalendar */ + daysPerMonth: [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 5], + /** true if has a year zero, false if not. + @memberof CopticCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof CopticCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof CopticCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof CopticCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof CopticCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Coptic', + epochs: ['BAM', 'AM'], + monthNames: ['Thout', 'Paopi', 'Hathor', 'Koiak', 'Tobi', 'Meshir', + 'Paremhat', 'Paremoude', 'Pashons', 'Paoni', 'Epip', 'Mesori', 'Pi Kogi Enavot'], + monthNamesShort: ['Tho', 'Pao', 'Hath', 'Koi', 'Tob', 'Mesh', + 'Pat', 'Pad', 'Pash', 'Pao', 'Epi', 'Meso', 'PiK'], + dayNames: ['Tkyriaka', 'Pesnau', 'Pshoment', 'Peftoou', 'Ptiou', 'Psoou', 'Psabbaton'], + dayNamesShort: ['Tky', 'Pes', 'Psh', 'Pef', 'Pti', 'Pso', 'Psa'], + dayNamesMin: ['Tk', 'Pes', 'Psh', 'Pef', 'Pt', 'Pso', 'Psa'], + digits: null, + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof CopticCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = date.year() + (date.year() < 0 ? 1 : 0); // No year zero + return year % 4 === 3 || year % 4 === -1; + }, + + /** Retrieve the number of months in a year. + @memberof CopticCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, + main.local.invalidYear || main.regionalOptions[''].invalidYear); + return 13; + }, + + /** Determine the week of the year for a date. + @memberof CopticCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number) the month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof CopticCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 13 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof CopticCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param month {number} The month to examine. + @param day {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof CopticCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number) the month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year(); + if (year < 0) { year++; } // No year zero + return date.day() + (date.month() - 1) * 30 + + (year - 1) * 365 + Math.floor(year / 4) + this.jdEpoch - 1; + }, + + /** Create a new date from a Julian date. + @memberof CopticCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + var c = Math.floor(jd) + 0.5 - this.jdEpoch; + var year = Math.floor((c - Math.floor((c + 366) / 1461)) / 365) + 1; + if (year <= 0) { year--; } // No year zero + c = Math.floor(jd) + 0.5 - this.newDate(year, 1, 1).toJD(); + var month = Math.floor(c / 30) + 1; + var day = c - (month - 1) * 30 + 1; + return this.newDate(year, month, day); + } +}); + +// Coptic calendar implementation +main.calendars.coptic = CopticCalendar; + + +},{"../main":135,"object-assign":71}],123:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Discworld calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) January 2016. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Discworld calendar - Unseen University version. + See also http://wiki.lspace.org/mediawiki/Discworld_calendar + and http://discworld.wikia.com/wiki/Discworld_calendar. + @class DiscworldCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function DiscworldCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +DiscworldCalendar.prototype = new main.baseCalendar; + +assign(DiscworldCalendar.prototype, { + /** The calendar name. + @memberof DiscworldCalendar */ + name: 'Discworld', + /** Julian date of start of Discworld epoch: 1 January 0001 CE. + @memberof DiscworldCalendar */ + jdEpoch: 1721425.5, + /** Days per month in a common year. + @memberof DiscworldCalendar */ + daysPerMonth: [16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32], + /** true if has a year zero, false if not. + @memberof DiscworldCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof DiscworldCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof DiscworldCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof DiscworldCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof DiscworldCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Discworld', + epochs: ['BUC', 'UC'], + monthNames: ['Ick', 'Offle', 'February', 'March', 'April', 'May', 'June', + 'Grune', 'August', 'Spune', 'Sektober', 'Ember', 'December'], + monthNamesShort: ['Ick', 'Off', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Gru', 'Aug', 'Spu', 'Sek', 'Emb', 'Dec'], + dayNames: ['Sunday', 'Octeday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Oct', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Oc', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + digits: null, + dateFormat: 'yyyy/mm/dd', + firstDay: 2, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return false; + }, + + /** Retrieve the number of months in a year. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return 13; + }, + + /** Retrieve the number of days in a year. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return 400; + }, + + /** Determine the week of the year for a date. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 8) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1]; + }, + + /** Retrieve the number of days in a week. + @memberof DiscworldCalendar + @return {number} The number of days. */ + daysInWeek: function() { + return 8; + }, + + /** Retrieve the day of the week for a date. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The day of the week: 0 to number of days - 1. + @throws Error if an invalid date or a different calendar used. */ + dayOfWeek: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + return (date.day() + 1) % 8; + }, + + /** Determine whether this date is a week day. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + var dow = this.dayOfWeek(year, month, day); + return (dow >= 2 && dow <= 6); + }, + + /** Retrieve additional information about a date. + @memberof DiscworldCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {object} Additional information - contents depends on calendar. + @throws Error if an invalid date or a different calendar used. */ + extraInfo: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + return {century: centuries[Math.floor((date.year() - 1) / 100) + 1] || ''}; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof DiscworldCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year() + (date.year() < 0 ? 1 : 0); + month = date.month(); + day = date.day(); + return day + (month > 1 ? 16 : 0) + (month > 2 ? (month - 2) * 32 : 0) + + (year - 1) * 400 + this.jdEpoch - 1; + }, + + /** Create a new date from a Julian date. + @memberof DiscworldCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + jd = Math.floor(jd + 0.5) - Math.floor(this.jdEpoch) - 1; + var year = Math.floor(jd / 400) + 1; + jd -= (year - 1) * 400; + jd += (jd > 15 ? 16 : 0); + var month = Math.floor(jd / 32) + 1; + var day = jd - (month - 1) * 32 + 1; + return this.newDate(year <= 0 ? year - 1 : year, month, day); + } +}); + +// Names of the centuries +var centuries = { + 20: 'Fruitbat', + 21: 'Anchovy' +}; + +// Discworld calendar implementation +main.calendars.discworld = DiscworldCalendar; + + +},{"../main":135,"object-assign":71}],124:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Ethiopian calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Ethiopian calendar. + See http://en.wikipedia.org/wiki/Ethiopian_calendar. + See also Calendrical Calculations: The Millennium Edition + (http://emr.cs.iit.edu/home/reingold/calendar-book/index.shtml). + @class EthiopianCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function EthiopianCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +EthiopianCalendar.prototype = new main.baseCalendar; + +assign(EthiopianCalendar.prototype, { + /** The calendar name. + @memberof EthiopianCalendar */ + name: 'Ethiopian', + /** Julian date of start of Ethiopian epoch: 27 August 8 CE (Gregorian). + @memberof EthiopianCalendar */ + jdEpoch: 1724220.5, + /** Days per month in a common year. + @memberof EthiopianCalendar */ + daysPerMonth: [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 5], + /** true if has a year zero, false if not. + @memberof EthiopianCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof EthiopianCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof EthiopianCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof EthiopianCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof EthiopianCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Ethiopian', + epochs: ['BEE', 'EE'], + monthNames: ['Meskerem', 'Tikemet', 'Hidar', 'Tahesas', 'Tir', 'Yekatit', + 'Megabit', 'Miazia', 'Genbot', 'Sene', 'Hamle', 'Nehase', 'Pagume'], + monthNamesShort: ['Mes', 'Tik', 'Hid', 'Tah', 'Tir', 'Yek', + 'Meg', 'Mia', 'Gen', 'Sen', 'Ham', 'Neh', 'Pag'], + dayNames: ['Ehud', 'Segno', 'Maksegno', 'Irob', 'Hamus', 'Arb', 'Kidame'], + dayNamesShort: ['Ehu', 'Seg', 'Mak', 'Iro', 'Ham', 'Arb', 'Kid'], + dayNamesMin: ['Eh', 'Se', 'Ma', 'Ir', 'Ha', 'Ar', 'Ki'], + digits: null, + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof EthiopianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = date.year() + (date.year() < 0 ? 1 : 0); // No year zero + return year % 4 === 3 || year % 4 === -1; + }, + + /** Retrieve the number of months in a year. + @memberof EthiopianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, + main.local.invalidYear || main.regionalOptions[''].invalidYear); + return 13; + }, + + /** Determine the week of the year for a date. + @memberof EthiopianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof EthiopianCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 13 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof EthiopianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof EthiopianCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year(); + if (year < 0) { year++; } // No year zero + return date.day() + (date.month() - 1) * 30 + + (year - 1) * 365 + Math.floor(year / 4) + this.jdEpoch - 1; + }, + + /** Create a new date from a Julian date. + @memberof EthiopianCalendar + @param jd {number} the Julian date to convert. + @return {CDate} the equivalent date. */ + fromJD: function(jd) { + var c = Math.floor(jd) + 0.5 - this.jdEpoch; + var year = Math.floor((c - Math.floor((c + 366) / 1461)) / 365) + 1; + if (year <= 0) { year--; } // No year zero + c = Math.floor(jd) + 0.5 - this.newDate(year, 1, 1).toJD(); + var month = Math.floor(c / 30) + 1; + var day = c - (month - 1) * 30 + 1; + return this.newDate(year, month, day); + } +}); + +// Ethiopian calendar implementation +main.calendars.ethiopian = EthiopianCalendar; + + +},{"../main":135,"object-assign":71}],125:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Hebrew calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Hebrew civil calendar. + Based on code from http://www.fourmilab.ch/documents/calendar/. + See also http://en.wikipedia.org/wiki/Hebrew_calendar. + @class HebrewCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function HebrewCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +HebrewCalendar.prototype = new main.baseCalendar; + +assign(HebrewCalendar.prototype, { + /** The calendar name. + @memberof HebrewCalendar */ + name: 'Hebrew', + /** Julian date of start of Hebrew epoch: 7 October 3761 BCE. + @memberof HebrewCalendar */ + jdEpoch: 347995.5, + /** Days per month in a common year. + @memberof HebrewCalendar */ + daysPerMonth: [30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 29], + /** true if has a year zero, false if not. + @memberof HebrewCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof HebrewCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof HebrewCalendar */ + firstMonth: 7, + /** The minimum day number. + @memberof HebrewCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof HebrewCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Hebrew', + epochs: ['BAM', 'AM'], + monthNames: ['Nisan', 'Iyar', 'Sivan', 'Tammuz', 'Av', 'Elul', + 'Tishrei', 'Cheshvan', 'Kislev', 'Tevet', 'Shevat', 'Adar', 'Adar II'], + monthNamesShort: ['Nis', 'Iya', 'Siv', 'Tam', 'Av', 'Elu', 'Tis', 'Che', 'Kis', 'Tev', 'She', 'Ada', 'Ad2'], + dayNames: ['Yom Rishon', 'Yom Sheni', 'Yom Shlishi', 'Yom Revi\'i', 'Yom Chamishi', 'Yom Shishi', 'Yom Shabbat'], + dayNamesShort: ['Ris', 'She', 'Shl', 'Rev', 'Cha', 'Shi', 'Sha'], + dayNamesMin: ['Ri','She','Shl','Re','Ch','Shi','Sha'], + digits: null, + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return this._leapYear(date.year()); + }, + + /** Determine whether this date is in a leap year. + @memberof HebrewCalendar + @private + @param year {number} The year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + _leapYear: function(year) { + year = (year < 0 ? year + 1 : year); + return mod(year * 7 + 1, 19) < 7; + }, + + /** Retrieve the number of months in a year. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return this._leapYear(year.year ? year.year() : year) ? 13 : 12; + }, + + /** Determine the week of the year for a date. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a year. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + year = date.year(); + return this.toJD((year === -1 ? +1 : year + 1), 7, 1) - this.toJD(year, 7, 1); + }, + + /** Retrieve the number of days in a month. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + if (year.year) { + month = year.month(); + year = year.year(); + } + this._validate(year, month, this.minDay, main.local.invalidMonth); + return (month === 12 && this.leapYear(year) ? 30 : // Adar I + (month === 8 && mod(this.daysInYear(year), 10) === 5 ? 30 : // Cheshvan in shlemah year + (month === 9 && mod(this.daysInYear(year), 10) === 3 ? 29 : // Kislev in chaserah year + this.daysPerMonth[month - 1]))); + }, + + /** Determine whether this date is a week day. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return this.dayOfWeek(year, month, day) !== 6; + }, + + /** Retrieve additional information about a date - year type. + @memberof HebrewCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {object} Additional information - contents depends on calendar. + @throws Error if an invalid date or a different calendar used. */ + extraInfo: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + return {yearType: (this.leapYear(date) ? 'embolismic' : 'common') + ' ' + + ['deficient', 'regular', 'complete'][this.daysInYear(date) % 10 - 3]}; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof HebrewCalendar + @param year {CDate)|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year(); + month = date.month(); + day = date.day(); + var adjYear = (year <= 0 ? year + 1 : year); + var jd = this.jdEpoch + this._delay1(adjYear) + + this._delay2(adjYear) + day + 1; + if (month < 7) { + for (var m = 7; m <= this.monthsInYear(year); m++) { + jd += this.daysInMonth(year, m); + } + for (var m = 1; m < month; m++) { + jd += this.daysInMonth(year, m); + } + } + else { + for (var m = 7; m < month; m++) { + jd += this.daysInMonth(year, m); + } + } + return jd; + }, + + /** Test for delay of start of new year and to avoid + Sunday, Wednesday, or Friday as start of the new year. + @memberof HebrewCalendar + @private + @param year {number} The year to examine. + @return {number} The days to offset by. */ + _delay1: function(year) { + var months = Math.floor((235 * year - 234) / 19); + var parts = 12084 + 13753 * months; + var day = months * 29 + Math.floor(parts / 25920); + if (mod(3 * (day + 1), 7) < 3) { + day++; + } + return day; + }, + + /** Check for delay in start of new year due to length of adjacent years. + @memberof HebrewCalendar + @private + @param year {number} The year to examine. + @return {number} The days to offset by. */ + _delay2: function(year) { + var last = this._delay1(year - 1); + var present = this._delay1(year); + var next = this._delay1(year + 1); + return ((next - present) === 356 ? 2 : ((present - last) === 382 ? 1 : 0)); + }, + + /** Create a new date from a Julian date. + @memberof HebrewCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + jd = Math.floor(jd) + 0.5; + var year = Math.floor(((jd - this.jdEpoch) * 98496.0) / 35975351.0) - 1; + while (jd >= this.toJD((year === -1 ? +1 : year + 1), 7, 1)) { + year++; + } + var month = (jd < this.toJD(year, 1, 1)) ? 7 : 1; + while (jd > this.toJD(year, month, this.daysInMonth(year, month))) { + month++; + } + var day = jd - this.toJD(year, month, 1) + 1; + return this.newDate(year, month, day); + } +}); + +// Modulus function which works for non-integers. +function mod(a, b) { + return a - (b * Math.floor(a / b)); +} + +// Hebrew calendar implementation +main.calendars.hebrew = HebrewCalendar; + + +},{"../main":135,"object-assign":71}],126:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Islamic calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Islamic or '16 civil' calendar. + Based on code from http://www.iranchamber.com/calendar/converter/iranian_calendar_converter.php. + See also http://en.wikipedia.org/wiki/Islamic_calendar. + @class IslamicCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function IslamicCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +IslamicCalendar.prototype = new main.baseCalendar; + +assign(IslamicCalendar.prototype, { + /** The calendar name. + @memberof IslamicCalendar */ + name: 'Islamic', + /** Julian date of start of Islamic epoch: 16 July 622 CE. + @memberof IslamicCalendar */ + jdEpoch: 1948439.5, + /** Days per month in a common year. + @memberof IslamicCalendar */ + daysPerMonth: [30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29], + /** true if has a year zero, false if not. + @memberof IslamicCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof IslamicCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof IslamicCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof IslamicCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof IslamicCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Islamic', + epochs: ['BH', 'AH'], + monthNames: ['Muharram', 'Safar', 'Rabi\' al-awwal', 'Rabi\' al-thani', 'Jumada al-awwal', 'Jumada al-thani', + 'Rajab', 'Sha\'aban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'], + monthNamesShort: ['Muh', 'Saf', 'Rab1', 'Rab2', 'Jum1', 'Jum2', 'Raj', 'Sha\'', 'Ram', 'Shaw', 'DhuQ', 'DhuH'], + dayNames: ['Yawm al-ahad', 'Yawm al-ithnayn', 'Yawm ath-thulaathaa\'', + 'Yawm al-arbi\'aa\'', 'Yawm al-khamīs', 'Yawm al-jum\'a', 'Yawm as-sabt'], + dayNamesShort: ['Aha', 'Ith', 'Thu', 'Arb', 'Kha', 'Jum', 'Sab'], + dayNamesMin: ['Ah','It','Th','Ar','Kh','Ju','Sa'], + digits: null, + dateFormat: 'yyyy/mm/dd', + firstDay: 6, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof IslamicCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return (date.year() * 11 + 14) % 30 < 11; + }, + + /** Determine the week of the year for a date. + @memberof IslamicCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a year. + @memberof IslamicCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function(year) { + return (this.leapYear(year) ? 355 : 354); + }, + + /** Retrieve the number of days in a month. + @memberof IslamicCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 12 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof IslamicCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return this.dayOfWeek(year, month, day) !== 5; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof IslamicCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year(); + month = date.month(); + day = date.day(); + year = (year <= 0 ? year + 1 : year); + return day + Math.ceil(29.5 * (month - 1)) + (year - 1) * 354 + + Math.floor((3 + (11 * year)) / 30) + this.jdEpoch - 1; + }, + + /** Create a new date from a Julian date. + @memberof IslamicCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + jd = Math.floor(jd) + 0.5; + var year = Math.floor((30 * (jd - this.jdEpoch) + 10646) / 10631); + year = (year <= 0 ? year - 1 : year); + var month = Math.min(12, Math.ceil((jd - 29 - this.toJD(year, 1, 1)) / 29.5) + 1); + var day = jd - this.toJD(year, month, 1) + 1; + return this.newDate(year, month, day); + } +}); + +// Islamic (16 civil) calendar implementation +main.calendars.islamic = IslamicCalendar; + + +},{"../main":135,"object-assign":71}],127:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Julian calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Julian calendar. + Based on code from http://www.fourmilab.ch/documents/calendar/. + See also http://en.wikipedia.org/wiki/Julian_calendar. + @class JulianCalendar + @augments BaseCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function JulianCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +JulianCalendar.prototype = new main.baseCalendar; + +assign(JulianCalendar.prototype, { + /** The calendar name. + @memberof JulianCalendar */ + name: 'Julian', + /** Julian date of start of Julian epoch: 1 January 0001 AD = 30 December 0001 BCE. + @memberof JulianCalendar */ + jdEpoch: 1721423.5, + /** Days per month in a common year. + @memberof JulianCalendar */ + daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + /** true if has a year zero, false if not. + @memberof JulianCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof JulianCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof JulianCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof JulianCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof JulianCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Julian', + epochs: ['BC', 'AD'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + digits: null, + dateFormat: 'mm/dd/yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof JulianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = (date.year() < 0 ? date.year() + 1 : date.year()); // No year zero + return (year % 4) === 0; + }, + + /** Determine the week of the year for a date - ISO 8601. + @memberof JulianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Thursday of this week starting on Monday + var checkDate = this.newDate(year, month, day); + checkDate.add(4 - (checkDate.dayOfWeek() || 7), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof JulianCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof JulianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} True if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof JulianCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year(); + month = date.month(); + day = date.day(); + if (year < 0) { year++; } // No year zero + // Jean Meeus algorithm, "Astronomical Algorithms", 1991 + if (month <= 2) { + year--; + month += 12; + } + return Math.floor(365.25 * (year + 4716)) + + Math.floor(30.6001 * (month + 1)) + day - 1524.5; + }, + + /** Create a new date from a Julian date. + @memberof JulianCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + // Jean Meeus algorithm, "Astronomical Algorithms", 1991 + var a = Math.floor(jd + 0.5); + var b = a + 1524; + var c = Math.floor((b - 122.1) / 365.25); + var d = Math.floor(365.25 * c); + var e = Math.floor((b - d) / 30.6001); + var month = e - Math.floor(e < 14 ? 1 : 13); + var year = c - Math.floor(month > 2 ? 4716 : 4715); + var day = b - d - Math.floor(30.6001 * e); + if (year <= 0) { year--; } // No year zero + return this.newDate(year, month, day); + } +}); + +// Julian calendar implementation +main.calendars.julian = JulianCalendar; + + +},{"../main":135,"object-assign":71}],128:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Mayan calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Mayan Long Count calendar. + See also http://en.wikipedia.org/wiki/Mayan_calendar. + @class MayanCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function MayanCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +MayanCalendar.prototype = new main.baseCalendar; + +assign(MayanCalendar.prototype, { + /** The calendar name. + @memberof MayanCalendar */ + name: 'Mayan', + /** Julian date of start of Mayan epoch: 11 August 3114 BCE. + @memberof MayanCalendar */ + jdEpoch: 584282.5, + /** true if has a year zero, false if not. + @memberof MayanCalendar */ + hasYearZero: true, + /** The minimum month number. + @memberof MayanCalendar */ + minMonth: 0, + /** The first month in the year. + @memberof MayanCalendar */ + firstMonth: 0, + /** The minimum day number. + @memberof MayanCalendar */ + minDay: 0, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof MayanCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. + @property haabMonths {string[]} The names of the Haab months. + @property tzolkinMonths {string[]} The names of the Tzolkin months. */ + regionalOptions: { // Localisations + '': { + name: 'Mayan', + epochs: ['', ''], + monthNames: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17'], + monthNamesShort: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17'], + dayNames: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'], + dayNamesShort: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'], + dayNamesMin: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'], + digits: null, + dateFormat: 'YYYY.m.d', + firstDay: 0, + isRTL: false, + haabMonths: ['Pop', 'Uo', 'Zip', 'Zotz', 'Tzec', 'Xul', 'Yaxkin', 'Mol', 'Chen', 'Yax', + 'Zac', 'Ceh', 'Mac', 'Kankin', 'Muan', 'Pax', 'Kayab', 'Cumku', 'Uayeb'], + tzolkinMonths: ['Imix', 'Ik', 'Akbal', 'Kan', 'Chicchan', 'Cimi', 'Manik', 'Lamat', 'Muluc', 'Oc', + 'Chuen', 'Eb', 'Ben', 'Ix', 'Men', 'Cib', 'Caban', 'Etznab', 'Cauac', 'Ahau'] + } + }, + + /** Determine whether this date is in a leap year. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return false; + }, + + /** Format the year, if not a simple sequential number. + @memberof MayanCalendar + @param year {CDate|number} The date to format or the year to format. + @return {string} The formatted year. + @throws Error if an invalid year or a different calendar used. */ + formatYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + year = date.year(); + var baktun = Math.floor(year / 400); + year = year % 400; + year += (year < 0 ? 400 : 0); + var katun = Math.floor(year / 20); + return baktun + '.' + katun + '.' + (year % 20); + }, + + /** Convert from the formatted year back to a single number. + @memberof MayanCalendar + @param years {string} The year as n.n.n. + @return {number} The sequential year. + @throws Error if an invalid value is supplied. */ + forYear: function(years) { + years = years.split('.'); + if (years.length < 3) { + throw 'Invalid Mayan year'; + } + var year = 0; + for (var i = 0; i < years.length; i++) { + var y = parseInt(years[i], 10); + if (Math.abs(y) > 19 || (i > 0 && y < 0)) { + throw 'Invalid Mayan year'; + } + year = year * 20 + y; + } + return year; + }, + + /** Retrieve the number of months in a year. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return 18; + }, + + /** Determine the week of the year for a date. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + this._validate(year, month, day, main.local.invalidDate); + return 0; + }, + + /** Retrieve the number of days in a year. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return 360; + }, + + /** Retrieve the number of days in a month. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + this._validate(year, month, this.minDay, main.local.invalidMonth); + return 20; + }, + + /** Retrieve the number of days in a week. + @memberof MayanCalendar + @return {number} The number of days. */ + daysInWeek: function() { + return 5; // Just for formatting + }, + + /** Retrieve the day of the week for a date. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The day of the week: 0 to number of days - 1. + @throws Error if an invalid date or a different calendar used. */ + dayOfWeek: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + return date.day(); + }, + + /** Determine whether this date is a week day. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + this._validate(year, month, day, main.local.invalidDate); + return true; + }, + + /** Retrieve additional information about a date - Haab and Tzolkin equivalents. + @memberof MayanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {object} Additional information - contents depends on calendar. + @throws Error if an invalid date or a different calendar used. */ + extraInfo: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + var jd = date.toJD(); + var haab = this._toHaab(jd); + var tzolkin = this._toTzolkin(jd); + return {haabMonthName: this.local.haabMonths[haab[0] - 1], + haabMonth: haab[0], haabDay: haab[1], + tzolkinDayName: this.local.tzolkinMonths[tzolkin[0] - 1], + tzolkinDay: tzolkin[0], tzolkinTrecena: tzolkin[1]}; + }, + + /** Retrieve Haab date from a Julian date. + @memberof MayanCalendar + @private + @param jd {number} The Julian date. + @return {number[]} Corresponding Haab month and day. */ + _toHaab: function(jd) { + jd -= this.jdEpoch; + var day = mod(jd + 8 + ((18 - 1) * 20), 365); + return [Math.floor(day / 20) + 1, mod(day, 20)]; + }, + + /** Retrieve Tzolkin date from a Julian date. + @memberof MayanCalendar + @private + @param jd {number} The Julian date. + @return {number[]} Corresponding Tzolkin day and trecena. */ + _toTzolkin: function(jd) { + jd -= this.jdEpoch; + return [amod(jd + 20, 20), amod(jd + 4, 13)]; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof MayanCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + return date.day() + (date.month() * 20) + (date.year() * 360) + this.jdEpoch; + }, + + /** Create a new date from a Julian date. + @memberof MayanCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + jd = Math.floor(jd) + 0.5 - this.jdEpoch; + var year = Math.floor(jd / 360); + jd = jd % 360; + jd += (jd < 0 ? 360 : 0); + var month = Math.floor(jd / 20); + var day = jd % 20; + return this.newDate(year, month, day); + } +}); + +// Modulus function which works for non-integers. +function mod(a, b) { + return a - (b * Math.floor(a / b)); +} + +// Modulus function which returns numerator if modulus is zero. +function amod(a, b) { + return mod(a - 1, b) + 1; +} + +// Mayan calendar implementation +main.calendars.mayan = MayanCalendar; + + +},{"../main":135,"object-assign":71}],129:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Nanakshahi calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) January 2016. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Nanakshahi calendar. + See also https://en.wikipedia.org/wiki/Nanakshahi_calendar. + @class NanakshahiCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function NanakshahiCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +NanakshahiCalendar.prototype = new main.baseCalendar; + +var gregorian = main.instance('gregorian'); + +assign(NanakshahiCalendar.prototype, { + /** The calendar name. + @memberof NanakshahiCalendar */ + name: 'Nanakshahi', + /** Julian date of start of Nanakshahi epoch: 14 March 1469 CE. + @memberof NanakshahiCalendar */ + jdEpoch: 2257673.5, + /** Days per month in a common year. + @memberof NanakshahiCalendar */ + daysPerMonth: [31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30], + /** true if has a year zero, false if not. + @memberof NanakshahiCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof NanakshahiCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof NanakshahiCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof NanakshahiCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof NanakshahiCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Nanakshahi', + epochs: ['BN', 'AN'], + monthNames: ['Chet', 'Vaisakh', 'Jeth', 'Harh', 'Sawan', 'Bhadon', + 'Assu', 'Katak', 'Maghar', 'Poh', 'Magh', 'Phagun'], + monthNamesShort: ['Che', 'Vai', 'Jet', 'Har', 'Saw', 'Bha', 'Ass', 'Kat', 'Mgr', 'Poh', 'Mgh', 'Pha'], + dayNames: ['Somvaar', 'Mangalvar', 'Budhvaar', 'Veervaar', 'Shukarvaar', 'Sanicharvaar', 'Etvaar'], + dayNamesShort: ['Som', 'Mangal', 'Budh', 'Veer', 'Shukar', 'Sanichar', 'Et'], + dayNamesMin: ['So', 'Ma', 'Bu', 'Ve', 'Sh', 'Sa', 'Et'], + digits: null, + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof NanakshahiCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, + main.local.invalidYear || main.regionalOptions[''].invalidYear); + return gregorian.leapYear(date.year() + (date.year() < 1 ? 1 : 0) + 1469); + }, + + /** Determine the week of the year for a date. + @memberof NanakshahiCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Monday of this week starting on Monday + var checkDate = this.newDate(year, month, day); + checkDate.add(1 - (checkDate.dayOfWeek() || 7), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof NanakshahiCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 12 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof NanakshahiCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof NanakshahiCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidMonth); + var year = date.year(); + if (year < 0) { year++; } // No year zero + var doy = date.day(); + for (var m = 1; m < date.month(); m++) { + doy += this.daysPerMonth[m - 1]; + } + return doy + gregorian.toJD(year + 1468, 3, 13); + }, + + /** Create a new date from a Julian date. + @memberof NanakshahiCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + jd = Math.floor(jd + 0.5); + var year = Math.floor((jd - (this.jdEpoch - 1)) / 366); + while (jd >= this.toJD(year + 1, 1, 1)) { + year++; + } + var day = jd - Math.floor(this.toJD(year, 1, 1) + 0.5) + 1; + var month = 1; + while (day > this.daysInMonth(year, month)) { + day -= this.daysInMonth(year, month); + month++; + } + return this.newDate(year, month, day); + } +}); + +// Nanakshahi calendar implementation +main.calendars.nanakshahi = NanakshahiCalendar; + + +},{"../main":135,"object-assign":71}],130:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Nepali calendar for jQuery v2.0.2. + Written by Artur Neumann (ict.projects{at}nepal.inf.org) April 2013. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Nepali civil calendar. + Based on the ideas from + http://codeissue.com/articles/a04e050dea7468f/algorithm-to-convert-english-date-to-nepali-date-using-c-net + and http://birenj2ee.blogspot.com/2011/04/nepali-calendar-in-java.html + See also http://en.wikipedia.org/wiki/Nepali_calendar + and https://en.wikipedia.org/wiki/Bikram_Samwat. + @class NepaliCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function NepaliCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +NepaliCalendar.prototype = new main.baseCalendar; + +assign(NepaliCalendar.prototype, { + /** The calendar name. + @memberof NepaliCalendar */ + name: 'Nepali', + /** Julian date of start of Nepali epoch: 14 April 57 BCE. + @memberof NepaliCalendar */ + jdEpoch: 1700709.5, + /** Days per month in a common year. + @memberof NepaliCalendar */ + daysPerMonth: [31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + /** true if has a year zero, false if not. + @memberof NepaliCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof NepaliCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof NepaliCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof NepaliCalendar */ + minDay: 1, + /** The number of days in the year. + @memberof NepaliCalendar */ + daysPerYear: 365, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof NepaliCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Nepali', + epochs: ['BBS', 'ABS'], + monthNames: ['Baisakh', 'Jestha', 'Ashadh', 'Shrawan', 'Bhadra', 'Ashwin', + 'Kartik', 'Mangsir', 'Paush', 'Mangh', 'Falgun', 'Chaitra'], + monthNamesShort: ['Bai', 'Je', 'As', 'Shra', 'Bha', 'Ash', 'Kar', 'Mang', 'Pau', 'Ma', 'Fal', 'Chai'], + dayNames: ['Aaitabaar', 'Sombaar', 'Manglbaar', 'Budhabaar', 'Bihibaar', 'Shukrabaar', 'Shanibaar'], + dayNamesShort: ['Aaita', 'Som', 'Mangl', 'Budha', 'Bihi', 'Shukra', 'Shani'], + dayNamesMin: ['Aai', 'So', 'Man', 'Bu', 'Bi', 'Shu', 'Sha'], + digits: null, + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof NepaliCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + return this.daysInYear(year) !== this.daysPerYear; + }, + + /** Determine the week of the year for a date. + @memberof NepaliCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a year. + @memberof NepaliCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + year = date.year(); + if (typeof this.NEPALI_CALENDAR_DATA[year] === 'undefined') { + return this.daysPerYear; + } + var daysPerYear = 0; + for (var month_number = this.minMonth; month_number <= 12; month_number++) { + daysPerYear += this.NEPALI_CALENDAR_DATA[year][month_number]; + } + return daysPerYear; + }, + + /** Retrieve the number of days in a month. + @memberof NepaliCalendar + @param year {CDate|number| The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + if (year.year) { + month = year.month(); + year = year.year(); + } + this._validate(year, month, this.minDay, main.local.invalidMonth); + return (typeof this.NEPALI_CALENDAR_DATA[year] === 'undefined' ? + this.daysPerMonth[month - 1] : this.NEPALI_CALENDAR_DATA[year][month]); + }, + + /** Determine whether this date is a week day. + @memberof NepaliCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return this.dayOfWeek(year, month, day) !== 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof NepaliCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(nepaliYear, nepaliMonth, nepaliDay) { + var date = this._validate(nepaliYear, nepaliMonth, nepaliDay, main.local.invalidDate); + nepaliYear = date.year(); + nepaliMonth = date.month(); + nepaliDay = date.day(); + var gregorianCalendar = main.instance(); + var gregorianDayOfYear = 0; // We will add all the days that went by since + // the 1st. January and then we can get the Gregorian Date + var nepaliMonthToCheck = nepaliMonth; + var nepaliYearToCheck = nepaliYear; + this._createMissingCalendarData(nepaliYear); + // Get the correct year + var gregorianYear = nepaliYear - (nepaliMonthToCheck > 9 || (nepaliMonthToCheck === 9 && + nepaliDay >= this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][0]) ? 56 : 57); + // First we add the amount of days in the actual Nepali month as the day of year in the + // Gregorian one because at least this days are gone since the 1st. Jan. + if (nepaliMonth !== 9) { + gregorianDayOfYear = nepaliDay; + nepaliMonthToCheck--; + } + // Now we loop throw all Nepali month and add the amount of days to gregorianDayOfYear + // we do this till we reach Paush (9th month). 1st. January always falls in this month + while (nepaliMonthToCheck !== 9) { + if (nepaliMonthToCheck <= 0) { + nepaliMonthToCheck = 12; + nepaliYearToCheck--; + } + gregorianDayOfYear += this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][nepaliMonthToCheck]; + nepaliMonthToCheck--; + } + // If the date that has to be converted is in Paush (month no. 9) we have to do some other calculation + if (nepaliMonth === 9) { + // Add the days that are passed since the first day of Paush and substract the + // amount of days that lie between 1st. Jan and 1st Paush + gregorianDayOfYear += nepaliDay - this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][0]; + // For the first days of Paush we are now in negative values, + // because in the end of the gregorian year we substract + // 365 / 366 days (P.S. remember math in school + - gives -) + if (gregorianDayOfYear < 0) { + gregorianDayOfYear += gregorianCalendar.daysInYear(gregorianYear); + } + } + else { + gregorianDayOfYear += this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][9] - + this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][0]; + } + return gregorianCalendar.newDate(gregorianYear, 1 ,1).add(gregorianDayOfYear, 'd').toJD(); + }, + + /** Create a new date from a Julian date. + @memberof NepaliCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + var gregorianCalendar = main.instance(); + var gregorianDate = gregorianCalendar.fromJD(jd); + var gregorianYear = gregorianDate.year(); + var gregorianDayOfYear = gregorianDate.dayOfYear(); + var nepaliYear = gregorianYear + 56; //this is not final, it could be also +57 but +56 is always true for 1st Jan. + this._createMissingCalendarData(nepaliYear); + var nepaliMonth = 9; // Jan 1 always fall in Nepali month Paush which is the 9th month of Nepali calendar. + // Get the Nepali day in Paush (month 9) of 1st January + var dayOfFirstJanInPaush = this.NEPALI_CALENDAR_DATA[nepaliYear][0]; + // Check how many days are left of Paush . + // Days calculated from 1st Jan till the end of the actual Nepali month, + // we use this value to check if the gregorian Date is in the actual Nepali month. + var daysSinceJanFirstToEndOfNepaliMonth = + this.NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth] - dayOfFirstJanInPaush + 1; + // If the gregorian day-of-year is smaller o equal than the sum of days between the 1st January and + // the end of the actual nepali month we found the correct nepali month. + // Example: + // The 4th February 2011 is the gregorianDayOfYear 35 (31 days of January + 4) + // 1st January 2011 is in the nepali year 2067, where 1st. January is in the 17th day of Paush (9th month) + // In 2067 Paush has 30days, This means (30-17+1=14) there are 14days between 1st January and end of Paush + // (including 17th January) + // The gregorianDayOfYear (35) is bigger than 14, so we check the next month + // The next nepali month (Mangh) has 29 days + // 29+14=43, this is bigger than gregorianDayOfYear(35) so, we found the correct nepali month + while (gregorianDayOfYear > daysSinceJanFirstToEndOfNepaliMonth) { + nepaliMonth++; + if (nepaliMonth > 12) { + nepaliMonth = 1; + nepaliYear++; + } + daysSinceJanFirstToEndOfNepaliMonth += this.NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth]; + } + // The last step is to calculate the nepali day-of-month + // to continue our example from before: + // we calculated there are 43 days from 1st. January (17 Paush) till end of Mangh (29 days) + // when we subtract from this 43 days the day-of-year of the the Gregorian date (35), + // we know how far the searched day is away from the end of the Nepali month. + // So we simply subtract this number from the amount of days in this month (30) + var nepaliDayOfMonth = this.NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth] - + (daysSinceJanFirstToEndOfNepaliMonth - gregorianDayOfYear); + return this.newDate(nepaliYear, nepaliMonth, nepaliDayOfMonth); + }, + + /** Creates missing data in the NEPALI_CALENDAR_DATA table. + This data will not be correct but just give an estimated result. Mostly -/+ 1 day + @private + @param nepaliYear {number} The missing year number. */ + _createMissingCalendarData: function(nepaliYear) { + var tmp_calendar_data = this.daysPerMonth.slice(0); + tmp_calendar_data.unshift(17); + for (var nepaliYearToCreate = (nepaliYear - 1); nepaliYearToCreate < (nepaliYear + 2); nepaliYearToCreate++) { + if (typeof this.NEPALI_CALENDAR_DATA[nepaliYearToCreate] === 'undefined') { + this.NEPALI_CALENDAR_DATA[nepaliYearToCreate] = tmp_calendar_data; + } + } + }, + + NEPALI_CALENDAR_DATA: { + // These data are from http://www.ashesh.com.np + 1970: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1971: [18, 31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30], + 1972: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + 1973: [19, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 1974: [19, 31, 31, 32, 30, 31, 31, 30, 29, 30, 29, 30, 30], + 1975: [18, 31, 31, 32, 32, 30, 31, 30, 29, 30, 29, 30, 30], + 1976: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 1977: [18, 31, 32, 31, 32, 31, 31, 29, 30, 29, 30, 29, 31], + 1978: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1979: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 1980: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 1981: [18, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 1982: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1983: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 1984: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 1985: [18, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 1986: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1987: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 1988: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 1989: [18, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 1990: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1991: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], + // These data are from http://nepalicalendar.rat32.com/index.php + 1992: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 1993: [18, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 1994: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1995: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + 1996: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 1997: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1998: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 1999: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2000: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2001: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2002: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2003: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2004: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2005: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2006: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2007: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2008: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], + 2009: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2010: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2011: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2012: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 2013: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2014: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2015: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2016: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 2017: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2018: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2019: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2020: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 2021: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2022: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + 2023: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2024: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 2025: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2026: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2027: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2028: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2029: [18, 31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30], + 2030: [17, 31, 32, 31, 32, 31, 30, 30, 30, 30, 30, 30, 31], + 2031: [17, 31, 32, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31], + 2032: [17, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32], + 2033: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2034: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2035: [17, 30, 32, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], + 2036: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2037: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2038: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2039: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 2040: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2041: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2042: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2043: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 2044: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2045: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2046: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2047: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 2048: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2049: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + 2050: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2051: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 2052: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2053: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + 2054: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2055: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 30, 29, 30], + 2056: [17, 31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30], + 2057: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2058: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2059: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2060: [17, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2061: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2062: [17, 30, 32, 31, 32, 31, 31, 29, 30, 29, 30, 29, 31], + 2063: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2064: [17, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2065: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2066: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], + 2067: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2068: [17, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2069: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2070: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], + 2071: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2072: [17, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2073: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], + 2074: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 2075: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2076: [16, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + 2077: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], + 2078: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], + 2079: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], + 2080: [16, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], + // These data are from http://www.ashesh.com.np/nepali-calendar/ + 2081: [17, 31, 31, 32, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2082: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2083: [17, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30], + 2084: [17, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30], + 2085: [17, 31, 32, 31, 32, 31, 31, 30, 30, 29, 30, 30, 30], + 2086: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2087: [16, 31, 31, 32, 31, 31, 31, 30, 30, 29, 30, 30, 30], + 2088: [16, 30, 31, 32, 32, 30, 31, 30, 30, 29, 30, 30, 30], + 2089: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2090: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2091: [16, 31, 31, 32, 31, 31, 31, 30, 30, 29, 30, 30, 30], + 2092: [16, 31, 31, 32, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2093: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2094: [17, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30], + 2095: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 30, 30, 30], + 2096: [17, 30, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], + 2097: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30], + 2098: [17, 31, 31, 32, 31, 31, 31, 29, 30, 29, 30, 30, 31], + 2099: [17, 31, 31, 32, 31, 31, 31, 30, 29, 29, 30, 30, 30], + 2100: [17, 31, 32, 31, 32, 30, 31, 30, 29, 30, 29, 30, 30] + } +}); + +// Nepali calendar implementation +main.calendars.nepali = NepaliCalendar; + + +},{"../main":135,"object-assign":71}],131:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Persian calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the Persian or Jalali calendar. + Based on code from http://www.iranchamber.com/calendar/converter/iranian_calendar_converter.php. + See also http://en.wikipedia.org/wiki/Iranian_calendar. + @class PersianCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function PersianCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +PersianCalendar.prototype = new main.baseCalendar; + +assign(PersianCalendar.prototype, { + /** The calendar name. + @memberof PersianCalendar */ + name: 'Persian', + /** Julian date of start of Persian epoch: 19 March 622 CE. + @memberof PersianCalendar */ + jdEpoch: 1948320.5, + /** Days per month in a common year. + @memberof PersianCalendar */ + daysPerMonth: [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29], + /** true if has a year zero, false if not. + @memberof PersianCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof PersianCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof PersianCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof PersianCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof PersianCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Persian', + epochs: ['BP', 'AP'], + monthNames: ['Farvardin', 'Ordibehesht', 'Khordad', 'Tir', 'Mordad', 'Shahrivar', + 'Mehr', 'Aban', 'Azar', 'Day', 'Bahman', 'Esfand'], + monthNamesShort: ['Far', 'Ord', 'Kho', 'Tir', 'Mor', 'Sha', 'Meh', 'Aba', 'Aza', 'Day', 'Bah', 'Esf'], + dayNames: ['Yekshambe', 'Doshambe', 'Seshambe', 'Chæharshambe', 'Panjshambe', 'Jom\'e', 'Shambe'], + dayNamesShort: ['Yek', 'Do', 'Se', 'Chæ', 'Panj', 'Jom', 'Sha'], + dayNamesMin: ['Ye','Do','Se','Ch','Pa','Jo','Sh'], + digits: null, + dateFormat: 'yyyy/mm/dd', + firstDay: 6, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof PersianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return (((((date.year() - (date.year() > 0 ? 474 : 473)) % 2820) + + 474 + 38) * 682) % 2816) < 682; + }, + + /** Determine the week of the year for a date. + @memberof PersianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Saturday of this week starting on Saturday + var checkDate = this.newDate(year, month, day); + checkDate.add(-((checkDate.dayOfWeek() + 1) % 7), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof PersianCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 12 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof PersianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return this.dayOfWeek(year, month, day) !== 5; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof PersianCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + year = date.year(); + month = date.month(); + day = date.day(); + var epBase = year - (year >= 0 ? 474 : 473); + var epYear = 474 + mod(epBase, 2820); + return day + (month <= 7 ? (month - 1) * 31 : (month - 1) * 30 + 6) + + Math.floor((epYear * 682 - 110) / 2816) + (epYear - 1) * 365 + + Math.floor(epBase / 2820) * 1029983 + this.jdEpoch - 1; + }, + + /** Create a new date from a Julian date. + @memberof PersianCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + jd = Math.floor(jd) + 0.5; + var depoch = jd - this.toJD(475, 1, 1); + var cycle = Math.floor(depoch / 1029983); + var cyear = mod(depoch, 1029983); + var ycycle = 2820; + if (cyear !== 1029982) { + var aux1 = Math.floor(cyear / 366); + var aux2 = mod(cyear, 366); + ycycle = Math.floor(((2134 * aux1) + (2816 * aux2) + 2815) / 1028522) + aux1 + 1; + } + var year = ycycle + (2820 * cycle) + 474; + year = (year <= 0 ? year - 1 : year); + var yday = jd - this.toJD(year, 1, 1) + 1; + var month = (yday <= 186 ? Math.ceil(yday / 31) : Math.ceil((yday - 6) / 30)); + var day = jd - this.toJD(year, month, 1) + 1; + return this.newDate(year, month, day); + } +}); + +// Modulus function which works for non-integers. +function mod(a, b) { + return a - (b * Math.floor(a / b)); +} + +// Persian (Jalali) calendar implementation +main.calendars.persian = PersianCalendar; +main.calendars.jalali = PersianCalendar; + + +},{"../main":135,"object-assign":71}],132:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Taiwanese (Minguo) calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +var gregorianCalendar = main.instance(); + +/** Implementation of the Taiwanese calendar. + See http://en.wikipedia.org/wiki/Minguo_calendar. + @class TaiwanCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function TaiwanCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +TaiwanCalendar.prototype = new main.baseCalendar; + +assign(TaiwanCalendar.prototype, { + /** The calendar name. + @memberof TaiwanCalendar */ + name: 'Taiwan', + /** Julian date of start of Taiwan epoch: 1 January 1912 CE (Gregorian). + @memberof TaiwanCalendar */ + jdEpoch: 2419402.5, + /** Difference in years between Taiwan and Gregorian calendars. + @memberof TaiwanCalendar */ + yearsOffset: 1911, + /** Days per month in a common year. + @memberof TaiwanCalendar */ + daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + /** true if has a year zero, false if not. + @memberof TaiwanCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof TaiwanCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof TaiwanCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof TaiwanCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof TaiwanCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Taiwan', + epochs: ['BROC', 'ROC'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + digits: null, + dateFormat: 'yyyy/mm/dd', + firstDay: 1, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof TaiwanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = this._t2gYear(date.year()); + return gregorianCalendar.leapYear(year); + }, + + /** Determine the week of the year for a date - ISO 8601. + @memberof TaiwanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = this._t2gYear(date.year()); + return gregorianCalendar.weekOfYear(year, date.month(), date.day()); + }, + + /** Retrieve the number of days in a month. + @memberof TaiwanCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof TaiwanCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof TaiwanCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + var year = this._t2gYear(date.year()); + return gregorianCalendar.toJD(year, date.month(), date.day()); + }, + + /** Create a new date from a Julian date. + @memberof TaiwanCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + var date = gregorianCalendar.fromJD(jd); + var year = this._g2tYear(date.year()); + return this.newDate(year, date.month(), date.day()); + }, + + /** Convert Taiwanese to Gregorian year. + @memberof TaiwanCalendar + @private + @param year {number} The Taiwanese year. + @return {number} The corresponding Gregorian year. */ + _t2gYear: function(year) { + return year + this.yearsOffset + (year >= -this.yearsOffset && year <= -1 ? 1 : 0); + }, + + /** Convert Gregorian to Taiwanese year. + @memberof TaiwanCalendar + @private + @param year {number} The Gregorian year. + @return {number} The corresponding Taiwanese year. */ + _g2tYear: function(year) { + return year - this.yearsOffset - (year >= 1 && year <= this.yearsOffset ? 1 : 0); + } +}); + +// Taiwan calendar implementation +main.calendars.taiwan = TaiwanCalendar; + + +},{"../main":135,"object-assign":71}],133:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Thai calendar for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +var gregorianCalendar = main.instance(); + +/** Implementation of the Thai calendar. + See http://en.wikipedia.org/wiki/Thai_calendar. + @class ThaiCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function ThaiCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +ThaiCalendar.prototype = new main.baseCalendar; + +assign(ThaiCalendar.prototype, { + /** The calendar name. + @memberof ThaiCalendar */ + name: 'Thai', + /** Julian date of start of Thai epoch: 1 January 543 BCE (Gregorian). + @memberof ThaiCalendar */ + jdEpoch: 1523098.5, + /** Difference in years between Thai and Gregorian calendars. + @memberof ThaiCalendar */ + yearsOffset: 543, + /** Days per month in a common year. + @memberof ThaiCalendar */ + daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + /** true if has a year zero, false if not. + @memberof ThaiCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof ThaiCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof ThaiCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof ThaiCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof ThaiCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Thai', + epochs: ['BBE', 'BE'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + digits: null, + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof ThaiCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = this._t2gYear(date.year()); + return gregorianCalendar.leapYear(year); + }, + + /** Determine the week of the year for a date - ISO 8601. + @memberof ThaiCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + var year = this._t2gYear(date.year()); + return gregorianCalendar.weekOfYear(year, date.month(), date.day()); + }, + + /** Retrieve the number of days in a month. + @memberof ThaiCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof ThaiCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof ThaiCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + var year = this._t2gYear(date.year()); + return gregorianCalendar.toJD(year, date.month(), date.day()); + }, + + /** Create a new date from a Julian date. + @memberof ThaiCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + var date = gregorianCalendar.fromJD(jd); + var year = this._g2tYear(date.year()); + return this.newDate(year, date.month(), date.day()); + }, + + /** Convert Thai to Gregorian year. + @memberof ThaiCalendar + @private + @param year {number} The Thai year. + @return {number} The corresponding Gregorian year. */ + _t2gYear: function(year) { + return year - this.yearsOffset - (year >= 1 && year <= this.yearsOffset ? 1 : 0); + }, + + /** Convert Gregorian to Thai year. + @memberof ThaiCalendar + @private + @param year {number} The Gregorian year. + @return {number} The corresponding Thai year. */ + _g2tYear: function(year) { + return year + this.yearsOffset + (year >= -this.yearsOffset && year <= -1 ? 1 : 0); + } +}); + +// Thai calendar implementation +main.calendars.thai = ThaiCalendar; + + +},{"../main":135,"object-assign":71}],134:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + UmmAlQura calendar for jQuery v2.0.2. + Written by Amro Osama March 2013. + Modified by Binnooh.com & www.elm.sa - 2014 - Added dates back to 1276 Hijri year. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var main = _dereq_('../main'); +var assign = _dereq_('object-assign'); + + +/** Implementation of the UmmAlQura or 'saudi' calendar. + See also http://en.wikipedia.org/wiki/Islamic_calendar#Saudi_Arabia.27s_Umm_al-Qura_calendar. + http://www.ummulqura.org.sa/About.aspx + http://www.staff.science.uu.nl/~gent0113/islam/ummalqura.htm + @class UmmAlQuraCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function UmmAlQuraCalendar(language) { + this.local = this.regionalOptions[language || ''] || this.regionalOptions['']; +} + +UmmAlQuraCalendar.prototype = new main.baseCalendar; + +assign(UmmAlQuraCalendar.prototype, { + /** The calendar name. + @memberof UmmAlQuraCalendar */ + name: 'UmmAlQura', + //jdEpoch: 1948440, // Julian date of start of UmmAlQura epoch: 14 March 1937 CE + //daysPerMonth: // Days per month in a common year, replaced by a method. + /** true if has a year zero, false if not. + @memberof UmmAlQuraCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof UmmAlQuraCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof UmmAlQuraCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof UmmAlQuraCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof UmmAlQuraCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Umm al-Qura', + epochs: ['BH', 'AH'], + monthNames: ['Al-Muharram', 'Safar', 'Rabi\' al-awwal', 'Rabi\' Al-Thani', 'Jumada Al-Awwal', 'Jumada Al-Thani', + 'Rajab', 'Sha\'aban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'], + monthNamesShort: ['Muh', 'Saf', 'Rab1', 'Rab2', 'Jum1', 'Jum2', 'Raj', 'Sha\'', 'Ram', 'Shaw', 'DhuQ', 'DhuH'], + dayNames: ['Yawm al-Ahad', 'Yawm al-Ithnain', 'Yawm al-Thalāthā’', 'Yawm al-Arba‘ā’', 'Yawm al-Khamīs', 'Yawm al-Jum‘a', 'Yawm al-Sabt'], + dayNamesMin: ['Ah', 'Ith', 'Th', 'Ar', 'Kh', 'Ju', 'Sa'], + digits: null, + dateFormat: 'yyyy/mm/dd', + firstDay: 6, + isRTL: true + } + }, + + /** Determine whether this date is in a leap year. + @memberof UmmAlQuraCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function (year) { + var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear); + return (this.daysInYear(date.year()) === 355); + }, + + /** Determine the week of the year for a date. + @memberof UmmAlQuraCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function (year, month, day) { + // Find Sunday of this week starting on Sunday + var checkDate = this.newDate(year, month, day); + checkDate.add(-checkDate.dayOfWeek(), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a year. + @memberof UmmAlQuraCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function (year) { + var daysCount = 0; + for (var i = 1; i <= 12; i++) { + daysCount += this.daysInMonth(year, i); + } + return daysCount; + }, + + /** Retrieve the number of days in a month. + @memberof UmmAlQuraCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function (year, month) { + var date = this._validate(year, month, this.minDay, main.local.invalidMonth); + var mcjdn = date.toJD() - 2400000 + 0.5; // Modified Chronological Julian Day Number (MCJDN) + // the MCJDN's of the start of the lunations in the Umm al-Qura calendar are stored in the 'ummalqura_dat' array + var index = 0; + for (var i = 0; i < ummalqura_dat.length; i++) { + if (ummalqura_dat[i] > mcjdn) { + return (ummalqura_dat[index] - ummalqura_dat[index - 1]); + } + index++; + } + return 30; // Unknown outside + }, + + /** Determine whether this date is a week day. + @memberof UmmAlQuraCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function (year, month, day) { + return this.dayOfWeek(year, month, day) !== 5; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof UmmAlQuraCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function (year, month, day) { + var date = this._validate(year, month, day, main.local.invalidDate); + var index = (12 * (date.year() - 1)) + date.month() - 15292; + var mcjdn = date.day() + ummalqura_dat[index - 1] - 1; + return mcjdn + 2400000 - 0.5; // Modified Chronological Julian Day Number (MCJDN) + }, + + /** Create a new date from a Julian date. + @memberof UmmAlQuraCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function (jd) { + var mcjdn = jd - 2400000 + 0.5; // Modified Chronological Julian Day Number (MCJDN) + // the MCJDN's of the start of the lunations in the Umm al-Qura calendar + // are stored in the 'ummalqura_dat' array + var index = 0; + for (var i = 0; i < ummalqura_dat.length; i++) { + if (ummalqura_dat[i] > mcjdn) break; + index++; + } + var lunation = index + 15292; //UmmAlQura Lunation Number + var ii = Math.floor((lunation - 1) / 12); + var year = ii + 1; + var month = lunation - 12 * ii; + var day = mcjdn - ummalqura_dat[index - 1] + 1; + return this.newDate(year, month, day); + }, + + /** Determine whether a date is valid for this calendar. + @memberof UmmAlQuraCalendar + @param year {number} The year to examine. + @param month {number} The month to examine. + @param day {number} The day to examine. + @return {boolean} true if a valid date, false if not. */ + isValid: function(year, month, day) { + var valid = main.baseCalendar.prototype.isValid.apply(this, arguments); + if (valid) { + year = (year.year != null ? year.year : year); + valid = (year >= 1276 && year <= 1500); + } + return valid; + }, + + /** Check that a candidate date is from the same calendar and is valid. + @memberof UmmAlQuraCalendar + @private + @param year {CDate|number} The date to validate or the year to validate. + @param month {number} The month to validate. + @param day {number} The day to validate. + @param error {string} Error message if invalid. + @throws Error if different calendars used or invalid date. */ + _validate: function(year, month, day, error) { + var date = main.baseCalendar.prototype._validate.apply(this, arguments); + if (date.year < 1276 || date.year > 1500) { + throw error.replace(/\{0\}/, this.local.name); + } + return date; + } +}); + +// UmmAlQura calendar implementation +main.calendars.ummalqura = UmmAlQuraCalendar; + +var ummalqura_dat = [ + 20, 50, 79, 109, 138, 168, 197, 227, 256, 286, 315, 345, 374, 404, 433, 463, 492, 522, 551, 581, + 611, 641, 670, 700, 729, 759, 788, 818, 847, 877, 906, 936, 965, 995, 1024, 1054, 1083, 1113, 1142, 1172, + 1201, 1231, 1260, 1290, 1320, 1350, 1379, 1409, 1438, 1468, 1497, 1527, 1556, 1586, 1615, 1645, 1674, 1704, 1733, 1763, + 1792, 1822, 1851, 1881, 1910, 1940, 1969, 1999, 2028, 2058, 2087, 2117, 2146, 2176, 2205, 2235, 2264, 2294, 2323, 2353, + 2383, 2413, 2442, 2472, 2501, 2531, 2560, 2590, 2619, 2649, 2678, 2708, 2737, 2767, 2796, 2826, 2855, 2885, 2914, 2944, + 2973, 3003, 3032, 3062, 3091, 3121, 3150, 3180, 3209, 3239, 3268, 3298, 3327, 3357, 3386, 3416, 3446, 3476, 3505, 3535, + 3564, 3594, 3623, 3653, 3682, 3712, 3741, 3771, 3800, 3830, 3859, 3889, 3918, 3948, 3977, 4007, 4036, 4066, 4095, 4125, + 4155, 4185, 4214, 4244, 4273, 4303, 4332, 4362, 4391, 4421, 4450, 4480, 4509, 4539, 4568, 4598, 4627, 4657, 4686, 4716, + 4745, 4775, 4804, 4834, 4863, 4893, 4922, 4952, 4981, 5011, 5040, 5070, 5099, 5129, 5158, 5188, 5218, 5248, 5277, 5307, + 5336, 5366, 5395, 5425, 5454, 5484, 5513, 5543, 5572, 5602, 5631, 5661, 5690, 5720, 5749, 5779, 5808, 5838, 5867, 5897, + 5926, 5956, 5985, 6015, 6044, 6074, 6103, 6133, 6162, 6192, 6221, 6251, 6281, 6311, 6340, 6370, 6399, 6429, 6458, 6488, + 6517, 6547, 6576, 6606, 6635, 6665, 6694, 6724, 6753, 6783, 6812, 6842, 6871, 6901, 6930, 6960, 6989, 7019, 7048, 7078, + 7107, 7137, 7166, 7196, 7225, 7255, 7284, 7314, 7344, 7374, 7403, 7433, 7462, 7492, 7521, 7551, 7580, 7610, 7639, 7669, + 7698, 7728, 7757, 7787, 7816, 7846, 7875, 7905, 7934, 7964, 7993, 8023, 8053, 8083, 8112, 8142, 8171, 8201, 8230, 8260, + 8289, 8319, 8348, 8378, 8407, 8437, 8466, 8496, 8525, 8555, 8584, 8614, 8643, 8673, 8702, 8732, 8761, 8791, 8821, 8850, + 8880, 8909, 8938, 8968, 8997, 9027, 9056, 9086, 9115, 9145, 9175, 9205, 9234, 9264, 9293, 9322, 9352, 9381, 9410, 9440, + 9470, 9499, 9529, 9559, 9589, 9618, 9648, 9677, 9706, 9736, 9765, 9794, 9824, 9853, 9883, 9913, 9943, 9972, 10002, 10032, + 10061, 10090, 10120, 10149, 10178, 10208, 10237, 10267, 10297, 10326, 10356, 10386, 10415, 10445, 10474, 10504, 10533, 10562, 10592, 10621, + 10651, 10680, 10710, 10740, 10770, 10799, 10829, 10858, 10888, 10917, 10947, 10976, 11005, 11035, 11064, 11094, 11124, 11153, 11183, 11213, + 11242, 11272, 11301, 11331, 11360, 11389, 11419, 11448, 11478, 11507, 11537, 11567, 11596, 11626, 11655, 11685, 11715, 11744, 11774, 11803, + 11832, 11862, 11891, 11921, 11950, 11980, 12010, 12039, 12069, 12099, 12128, 12158, 12187, 12216, 12246, 12275, 12304, 12334, 12364, 12393, + 12423, 12453, 12483, 12512, 12542, 12571, 12600, 12630, 12659, 12688, 12718, 12747, 12777, 12807, 12837, 12866, 12896, 12926, 12955, 12984, + 13014, 13043, 13072, 13102, 13131, 13161, 13191, 13220, 13250, 13280, 13310, 13339, 13368, 13398, 13427, 13456, 13486, 13515, 13545, 13574, + 13604, 13634, 13664, 13693, 13723, 13752, 13782, 13811, 13840, 13870, 13899, 13929, 13958, 13988, 14018, 14047, 14077, 14107, 14136, 14166, + 14195, 14224, 14254, 14283, 14313, 14342, 14372, 14401, 14431, 14461, 14490, 14520, 14550, 14579, 14609, 14638, 14667, 14697, 14726, 14756, + 14785, 14815, 14844, 14874, 14904, 14933, 14963, 14993, 15021, 15051, 15081, 15110, 15140, 15169, 15199, 15228, 15258, 15287, 15317, 15347, + 15377, 15406, 15436, 15465, 15494, 15524, 15553, 15582, 15612, 15641, 15671, 15701, 15731, 15760, 15790, 15820, 15849, 15878, 15908, 15937, + 15966, 15996, 16025, 16055, 16085, 16114, 16144, 16174, 16204, 16233, 16262, 16292, 16321, 16350, 16380, 16409, 16439, 16468, 16498, 16528, + 16558, 16587, 16617, 16646, 16676, 16705, 16734, 16764, 16793, 16823, 16852, 16882, 16912, 16941, 16971, 17001, 17030, 17060, 17089, 17118, + 17148, 17177, 17207, 17236, 17266, 17295, 17325, 17355, 17384, 17414, 17444, 17473, 17502, 17532, 17561, 17591, 17620, 17650, 17679, 17709, + 17738, 17768, 17798, 17827, 17857, 17886, 17916, 17945, 17975, 18004, 18034, 18063, 18093, 18122, 18152, 18181, 18211, 18241, 18270, 18300, + 18330, 18359, 18388, 18418, 18447, 18476, 18506, 18535, 18565, 18595, 18625, 18654, 18684, 18714, 18743, 18772, 18802, 18831, 18860, 18890, + 18919, 18949, 18979, 19008, 19038, 19068, 19098, 19127, 19156, 19186, 19215, 19244, 19274, 19303, 19333, 19362, 19392, 19422, 19452, 19481, + 19511, 19540, 19570, 19599, 19628, 19658, 19687, 19717, 19746, 19776, 19806, 19836, 19865, 19895, 19924, 19954, 19983, 20012, 20042, 20071, + 20101, 20130, 20160, 20190, 20219, 20249, 20279, 20308, 20338, 20367, 20396, 20426, 20455, 20485, 20514, 20544, 20573, 20603, 20633, 20662, + 20692, 20721, 20751, 20780, 20810, 20839, 20869, 20898, 20928, 20957, 20987, 21016, 21046, 21076, 21105, 21135, 21164, 21194, 21223, 21253, + 21282, 21312, 21341, 21371, 21400, 21430, 21459, 21489, 21519, 21548, 21578, 21607, 21637, 21666, 21696, 21725, 21754, 21784, 21813, 21843, + 21873, 21902, 21932, 21962, 21991, 22021, 22050, 22080, 22109, 22138, 22168, 22197, 22227, 22256, 22286, 22316, 22346, 22375, 22405, 22434, + 22464, 22493, 22522, 22552, 22581, 22611, 22640, 22670, 22700, 22730, 22759, 22789, 22818, 22848, 22877, 22906, 22936, 22965, 22994, 23024, + 23054, 23083, 23113, 23143, 23173, 23202, 23232, 23261, 23290, 23320, 23349, 23379, 23408, 23438, 23467, 23497, 23527, 23556, 23586, 23616, + 23645, 23674, 23704, 23733, 23763, 23792, 23822, 23851, 23881, 23910, 23940, 23970, 23999, 24029, 24058, 24088, 24117, 24147, 24176, 24206, + 24235, 24265, 24294, 24324, 24353, 24383, 24413, 24442, 24472, 24501, 24531, 24560, 24590, 24619, 24648, 24678, 24707, 24737, 24767, 24796, + 24826, 24856, 24885, 24915, 24944, 24974, 25003, 25032, 25062, 25091, 25121, 25150, 25180, 25210, 25240, 25269, 25299, 25328, 25358, 25387, + 25416, 25446, 25475, 25505, 25534, 25564, 25594, 25624, 25653, 25683, 25712, 25742, 25771, 25800, 25830, 25859, 25888, 25918, 25948, 25977, + 26007, 26037, 26067, 26096, 26126, 26155, 26184, 26214, 26243, 26272, 26302, 26332, 26361, 26391, 26421, 26451, 26480, 26510, 26539, 26568, + 26598, 26627, 26656, 26686, 26715, 26745, 26775, 26805, 26834, 26864, 26893, 26923, 26952, 26982, 27011, 27041, 27070, 27099, 27129, 27159, + 27188, 27218, 27248, 27277, 27307, 27336, 27366, 27395, 27425, 27454, 27484, 27513, 27542, 27572, 27602, 27631, 27661, 27691, 27720, 27750, + 27779, 27809, 27838, 27868, 27897, 27926, 27956, 27985, 28015, 28045, 28074, 28104, 28134, 28163, 28193, 28222, 28252, 28281, 28310, 28340, + 28369, 28399, 28428, 28458, 28488, 28517, 28547, 28577, + // From 1356 + 28607, 28636, 28665, 28695, 28724, 28754, 28783, 28813, 28843, 28872, 28901, 28931, 28960, 28990, 29019, 29049, 29078, 29108, 29137, 29167, + 29196, 29226, 29255, 29285, 29315, 29345, 29375, 29404, 29434, 29463, 29492, 29522, 29551, 29580, 29610, 29640, 29669, 29699, 29729, 29759, + 29788, 29818, 29847, 29876, 29906, 29935, 29964, 29994, 30023, 30053, 30082, 30112, 30141, 30171, 30200, 30230, 30259, 30289, 30318, 30348, + 30378, 30408, 30437, 30467, 30496, 30526, 30555, 30585, 30614, 30644, 30673, 30703, 30732, 30762, 30791, 30821, 30850, 30880, 30909, 30939, + 30968, 30998, 31027, 31057, 31086, 31116, 31145, 31175, 31204, 31234, 31263, 31293, 31322, 31352, 31381, 31411, 31441, 31471, 31500, 31530, + 31559, 31589, 31618, 31648, 31676, 31706, 31736, 31766, 31795, 31825, 31854, 31884, 31913, 31943, 31972, 32002, 32031, 32061, 32090, 32120, + 32150, 32180, 32209, 32239, 32268, 32298, 32327, 32357, 32386, 32416, 32445, 32475, 32504, 32534, 32563, 32593, 32622, 32652, 32681, 32711, + 32740, 32770, 32799, 32829, 32858, 32888, 32917, 32947, 32976, 33006, 33035, 33065, 33094, 33124, 33153, 33183, 33213, 33243, 33272, 33302, + 33331, 33361, 33390, 33420, 33450, 33479, 33509, 33539, 33568, 33598, 33627, 33657, 33686, 33716, 33745, 33775, 33804, 33834, 33863, 33893, + 33922, 33952, 33981, 34011, 34040, 34069, 34099, 34128, 34158, 34187, 34217, 34247, 34277, 34306, 34336, 34365, 34395, 34424, 34454, 34483, + 34512, 34542, 34571, 34601, 34631, 34660, 34690, 34719, 34749, 34778, 34808, 34837, 34867, 34896, 34926, 34955, 34985, 35015, 35044, 35074, + 35103, 35133, 35162, 35192, 35222, 35251, 35280, 35310, 35340, 35370, 35399, 35429, 35458, 35488, 35517, 35547, 35576, 35605, 35635, 35665, + 35694, 35723, 35753, 35782, 35811, 35841, 35871, 35901, 35930, 35960, 35989, 36019, 36048, 36078, 36107, 36136, 36166, 36195, 36225, 36254, + 36284, 36314, 36343, 36373, 36403, 36433, 36462, 36492, 36521, 36551, 36580, 36610, 36639, 36669, 36698, 36728, 36757, 36786, 36816, 36845, + 36875, 36904, 36934, 36963, 36993, 37022, 37052, 37081, 37111, 37141, 37170, 37200, 37229, 37259, 37288, 37318, 37347, 37377, 37406, 37436, + 37465, 37495, 37524, 37554, 37584, 37613, 37643, 37672, 37701, 37731, 37760, 37790, 37819, 37849, 37878, 37908, 37938, 37967, 37997, 38027, + 38056, 38085, 38115, 38144, 38174, 38203, 38233, 38262, 38292, 38322, 38351, 38381, 38410, 38440, 38469, 38499, 38528, 38558, 38587, 38617, + 38646, 38676, 38705, 38735, 38764, 38794, 38823, 38853, 38882, 38912, 38941, 38971, 39001, 39030, 39059, 39089, 39118, 39148, 39178, 39208, + 39237, 39267, 39297, 39326, 39355, 39385, 39414, 39444, 39473, 39503, 39532, 39562, 39592, 39621, 39650, 39680, 39709, 39739, 39768, 39798, + 39827, 39857, 39886, 39916, 39946, 39975, 40005, 40035, 40064, 40094, 40123, 40153, 40182, 40212, 40241, 40271, 40300, 40330, 40359, 40389, + 40418, 40448, 40477, 40507, 40536, 40566, 40595, 40625, 40655, 40685, 40714, 40744, 40773, 40803, 40832, 40862, 40892, 40921, 40951, 40980, + 41009, 41039, 41068, 41098, 41127, 41157, 41186, 41216, 41245, 41275, 41304, 41334, 41364, 41393, 41422, 41452, 41481, 41511, 41540, 41570, + 41599, 41629, 41658, 41688, 41718, 41748, 41777, 41807, 41836, 41865, 41894, 41924, 41953, 41983, 42012, 42042, 42072, 42102, 42131, 42161, + 42190, 42220, 42249, 42279, 42308, 42337, 42367, 42397, 42426, 42456, 42485, 42515, 42545, 42574, 42604, 42633, 42662, 42692, 42721, 42751, + 42780, 42810, 42839, 42869, 42899, 42929, 42958, 42988, 43017, 43046, 43076, 43105, 43135, 43164, 43194, 43223, 43253, 43283, 43312, 43342, + 43371, 43401, 43430, 43460, 43489, 43519, 43548, 43578, 43607, 43637, 43666, 43696, 43726, 43755, 43785, 43814, 43844, 43873, 43903, 43932, + 43962, 43991, 44021, 44050, 44080, 44109, 44139, 44169, 44198, 44228, 44258, 44287, 44317, 44346, 44375, 44405, 44434, 44464, 44493, 44523, + 44553, 44582, 44612, 44641, 44671, 44700, 44730, 44759, 44788, 44818, 44847, 44877, 44906, 44936, 44966, 44996, 45025, 45055, 45084, 45114, + 45143, 45172, 45202, 45231, 45261, 45290, 45320, 45350, 45380, 45409, 45439, 45468, 45498, 45527, 45556, 45586, 45615, 45644, 45674, 45704, + 45733, 45763, 45793, 45823, 45852, 45882, 45911, 45940, 45970, 45999, 46028, 46058, 46088, 46117, 46147, 46177, 46206, 46236, 46265, 46295, + 46324, 46354, 46383, 46413, 46442, 46472, 46501, 46531, 46560, 46590, 46620, 46649, 46679, 46708, 46738, 46767, 46797, 46826, 46856, 46885, + 46915, 46944, 46974, 47003, 47033, 47063, 47092, 47122, 47151, 47181, 47210, 47240, 47269, 47298, 47328, 47357, 47387, 47417, 47446, 47476, + 47506, 47535, 47565, 47594, 47624, 47653, 47682, 47712, 47741, 47771, 47800, 47830, 47860, 47890, 47919, 47949, 47978, 48008, 48037, 48066, + 48096, 48125, 48155, 48184, 48214, 48244, 48273, 48303, 48333, 48362, 48392, 48421, 48450, 48480, 48509, 48538, 48568, 48598, 48627, 48657, + 48687, 48717, 48746, 48776, 48805, 48834, 48864, 48893, 48922, 48952, 48982, 49011, 49041, 49071, 49100, 49130, 49160, 49189, 49218, 49248, + 49277, 49306, 49336, 49365, 49395, 49425, 49455, 49484, 49514, 49543, 49573, 49602, 49632, 49661, 49690, 49720, 49749, 49779, 49809, 49838, + 49868, 49898, 49927, 49957, 49986, 50016, 50045, 50075, 50104, 50133, 50163, 50192, 50222, 50252, 50281, 50311, 50340, 50370, 50400, 50429, + 50459, 50488, 50518, 50547, 50576, 50606, 50635, 50665, 50694, 50724, 50754, 50784, 50813, 50843, 50872, 50902, 50931, 50960, 50990, 51019, + 51049, 51078, 51108, 51138, 51167, 51197, 51227, 51256, 51286, 51315, 51345, 51374, 51403, 51433, 51462, 51492, 51522, 51552, 51582, 51611, + 51641, 51670, 51699, 51729, 51758, 51787, 51816, 51846, 51876, 51906, 51936, 51965, 51995, 52025, 52054, 52083, 52113, 52142, 52171, 52200, + 52230, 52260, 52290, 52319, 52349, 52379, 52408, 52438, 52467, 52497, 52526, 52555, 52585, 52614, 52644, 52673, 52703, 52733, 52762, 52792, + 52822, 52851, 52881, 52910, 52939, 52969, 52998, 53028, 53057, 53087, 53116, 53146, 53176, 53205, 53235, 53264, 53294, 53324, 53353, 53383, + 53412, 53441, 53471, 53500, 53530, 53559, 53589, 53619, 53648, 53678, 53708, 53737, 53767, 53796, 53825, 53855, 53884, 53913, 53943, 53973, + 54003, 54032, 54062, 54092, 54121, 54151, 54180, 54209, 54239, 54268, 54297, 54327, 54357, 54387, 54416, 54446, 54476, 54505, 54535, 54564, + 54593, 54623, 54652, 54681, 54711, 54741, 54770, 54800, 54830, 54859, 54889, 54919, 54948, 54977, 55007, 55036, 55066, 55095, 55125, 55154, + 55184, 55213, 55243, 55273, 55302, 55332, 55361, 55391, 55420, 55450, 55479, 55508, 55538, 55567, 55597, 55627, 55657, 55686, 55716, 55745, + 55775, 55804, 55834, 55863, 55892, 55922, 55951, 55981, 56011, 56040, 56070, 56100, 56129, 56159, 56188, 56218, 56247, 56276, 56306, 56335, + 56365, 56394, 56424, 56454, 56483, 56513, 56543, 56572, 56601, 56631, 56660, 56690, 56719, 56749, 56778, 56808, 56837, 56867, 56897, 56926, + 56956, 56985, 57015, 57044, 57074, 57103, 57133, 57162, 57192, 57221, 57251, 57280, 57310, 57340, 57369, 57399, 57429, 57458, 57487, 57517, + 57546, 57576, 57605, 57634, 57664, 57694, 57723, 57753, 57783, 57813, 57842, 57871, 57901, 57930, 57959, 57989, 58018, 58048, 58077, 58107, + 58137, 58167, 58196, 58226, 58255, 58285, 58314, 58343, 58373, 58402, 58432, 58461, 58491, 58521, 58551, 58580, 58610, 58639, 58669, 58698, + 58727, 58757, 58786, 58816, 58845, 58875, 58905, 58934, 58964, 58994, 59023, 59053, 59082, 59111, 59141, 59170, 59200, 59229, 59259, 59288, + 59318, 59348, 59377, 59407, 59436, 59466, 59495, 59525, 59554, 59584, 59613, 59643, 59672, 59702, 59731, 59761, 59791, 59820, 59850, 59879, + 59909, 59939, 59968, 59997, 60027, 60056, 60086, 60115, 60145, 60174, 60204, 60234, 60264, 60293, 60323, 60352, 60381, 60411, 60440, 60469, + 60499, 60528, 60558, 60588, 60618, 60648, 60677, 60707, 60736, 60765, 60795, 60824, 60853, 60883, 60912, 60942, 60972, 61002, 61031, 61061, + 61090, 61120, 61149, 61179, 61208, 61237, 61267, 61296, 61326, 61356, 61385, 61415, 61445, 61474, 61504, 61533, 61563, 61592, 61621, 61651, + 61680, 61710, 61739, 61769, 61799, 61828, 61858, 61888, 61917, 61947, 61976, 62006, 62035, 62064, 62094, 62123, 62153, 62182, 62212, 62242, + 62271, 62301, 62331, 62360, 62390, 62419, 62448, 62478, 62507, 62537, 62566, 62596, 62625, 62655, 62685, 62715, 62744, 62774, 62803, 62832, + 62862, 62891, 62921, 62950, 62980, 63009, 63039, 63069, 63099, 63128, 63157, 63187, 63216, 63246, 63275, 63305, 63334, 63363, 63393, 63423, + 63453, 63482, 63512, 63541, 63571, 63600, 63630, 63659, 63689, 63718, 63747, 63777, 63807, 63836, 63866, 63895, 63925, 63955, 63984, 64014, + 64043, 64073, 64102, 64131, 64161, 64190, 64220, 64249, 64279, 64309, 64339, 64368, 64398, 64427, 64457, 64486, 64515, 64545, 64574, 64603, + 64633, 64663, 64692, 64722, 64752, 64782, 64811, 64841, 64870, 64899, 64929, 64958, 64987, 65017, 65047, 65076, 65106, 65136, 65166, 65195, + 65225, 65254, 65283, 65313, 65342, 65371, 65401, 65431, 65460, 65490, 65520, 65549, 65579, 65608, 65638, 65667, 65697, 65726, 65755, 65785, + 65815, 65844, 65874, 65903, 65933, 65963, 65992, 66022, 66051, 66081, 66110, 66140, 66169, 66199, 66228, 66258, 66287, 66317, 66346, 66376, + 66405, 66435, 66465, 66494, 66524, 66553, 66583, 66612, 66641, 66671, 66700, 66730, 66760, 66789, 66819, 66849, 66878, 66908, 66937, 66967, + 66996, 67025, 67055, 67084, 67114, 67143, 67173, 67203, 67233, 67262, 67292, 67321, 67351, 67380, 67409, 67439, 67468, 67497, 67527, 67557, + 67587, 67617, 67646, 67676, 67705, 67735, 67764, 67793, 67823, 67852, 67882, 67911, 67941, 67971, 68000, 68030, 68060, 68089, 68119, 68148, + 68177, 68207, 68236, 68266, 68295, 68325, 68354, 68384, 68414, 68443, 68473, 68502, 68532, 68561, 68591, 68620, 68650, 68679, 68708, 68738, + 68768, 68797, 68827, 68857, 68886, 68916, 68946, 68975, 69004, 69034, 69063, 69092, 69122, 69152, 69181, 69211, 69240, 69270, 69300, 69330, + 69359, 69388, 69418, 69447, 69476, 69506, 69535, 69565, 69595, 69624, 69654, 69684, 69713, 69743, 69772, 69802, 69831, 69861, 69890, 69919, + 69949, 69978, 70008, 70038, 70067, 70097, 70126, 70156, 70186, 70215, 70245, 70274, 70303, 70333, 70362, 70392, 70421, 70451, 70481, 70510, + 70540, 70570, 70599, 70629, 70658, 70687, 70717, 70746, 70776, 70805, 70835, 70864, 70894, 70924, 70954, 70983, 71013, 71042, 71071, 71101, + 71130, 71159, 71189, 71218, 71248, 71278, 71308, 71337, 71367, 71397, 71426, 71455, 71485, 71514, 71543, 71573, 71602, 71632, 71662, 71691, + 71721, 71751, 71781, 71810, 71839, 71869, 71898, 71927, 71957, 71986, 72016, 72046, 72075, 72105, 72135, 72164, 72194, 72223, 72253, 72282, + 72311, 72341, 72370, 72400, 72429, 72459, 72489, 72518, 72548, 72577, 72607, 72637, 72666, 72695, 72725, 72754, 72784, 72813, 72843, 72872, + 72902, 72931, 72961, 72991, 73020, 73050, 73080, 73109, 73139, 73168, 73197, 73227, 73256, 73286, 73315, 73345, 73375, 73404, 73434, 73464, + 73493, 73523, 73552, 73581, 73611, 73640, 73669, 73699, 73729, 73758, 73788, 73818, 73848, 73877, 73907, 73936, 73965, 73995, 74024, 74053, + 74083, 74113, 74142, 74172, 74202, 74231, 74261, 74291, 74320, 74349, 74379, 74408, 74437, 74467, 74497, 74526, 74556, 74586, 74615, 74645, + 74675, 74704, 74733, 74763, 74792, 74822, 74851, 74881, 74910, 74940, 74969, 74999, 75029, 75058, 75088, 75117, 75147, 75176, 75206, 75235, + 75264, 75294, 75323, 75353, 75383, 75412, 75442, 75472, 75501, 75531, 75560, 75590, 75619, 75648, 75678, 75707, 75737, 75766, 75796, 75826, + 75856, 75885, 75915, 75944, 75974, 76003, 76032, 76062, 76091, 76121, 76150, 76180, 76210, 76239, 76269, 76299, 76328, 76358, 76387, 76416, + 76446, 76475, 76505, 76534, 76564, 76593, 76623, 76653, 76682, 76712, 76741, 76771, 76801, 76830, 76859, 76889, 76918, 76948, 76977, 77007, + 77036, 77066, 77096, 77125, 77155, 77185, 77214, 77243, 77273, 77302, 77332, 77361, 77390, 77420, 77450, 77479, 77509, 77539, 77569, 77598, + 77627, 77657, 77686, 77715, 77745, 77774, 77804, 77833, 77863, 77893, 77923, 77952, 77982, 78011, 78041, 78070, 78099, 78129, 78158, 78188, + 78217, 78247, 78277, 78307, 78336, 78366, 78395, 78425, 78454, 78483, 78513, 78542, 78572, 78601, 78631, 78661, 78690, 78720, 78750, 78779, + 78808, 78838, 78867, 78897, 78926, 78956, 78985, 79015, 79044, 79074, 79104, 79133, 79163, 79192, 79222, 79251, 79281, 79310, 79340, 79369, + 79399, 79428, 79458, 79487, 79517, 79546, 79576, 79606, 79635, 79665, 79695, 79724, 79753, 79783, 79812, 79841, 79871, 79900, 79930, 79960, + 79990]; + + +},{"../main":135,"object-assign":71}],135:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Calendars for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var assign = _dereq_('object-assign'); + + +function Calendars() { + this.regionalOptions = []; + this.regionalOptions[''] = { + invalidCalendar: 'Calendar {0} not found', + invalidDate: 'Invalid {0} date', + invalidMonth: 'Invalid {0} month', + invalidYear: 'Invalid {0} year', + differentCalendars: 'Cannot mix {0} and {1} dates' + }; + this.local = this.regionalOptions['']; + this.calendars = {}; + this._localCals = {}; +} + +/** Create the calendars plugin. +

Provides support for various world calendars in a consistent manner.

+ @class Calendars + @example _exports.instance('julian').newDate(2014, 12, 25) */ +assign(Calendars.prototype, { + + /** Obtain a calendar implementation and localisation. + @memberof Calendars + @param [name='gregorian'] {string} The name of the calendar, e.g. 'gregorian', 'persian', 'islamic'. + @param [language=''] {string} The language code to use for localisation (default is English). + @return {Calendar} The calendar and localisation. + @throws Error if calendar not found. */ + instance: function(name, language) { + name = (name || 'gregorian').toLowerCase(); + language = language || ''; + var cal = this._localCals[name + '-' + language]; + if (!cal && this.calendars[name]) { + cal = new this.calendars[name](language); + this._localCals[name + '-' + language] = cal; + } + if (!cal) { + throw (this.local.invalidCalendar || this.regionalOptions[''].invalidCalendar). + replace(/\{0\}/, name); + } + return cal; + }, + + /** Create a new date - for today if no other parameters given. + @memberof Calendars + @param year {CDate|number} The date to copy or the year for the date. + @param [month] {number} The month for the date. + @param [day] {number} The day for the date. + @param [calendar='gregorian'] {BaseCalendar|string} The underlying calendar or the name of the calendar. + @param [language=''] {string} The language to use for localisation (default English). + @return {CDate} The new date. + @throws Error if an invalid date. */ + newDate: function(year, month, day, calendar, language) { + calendar = (year != null && year.year ? year.calendar() : (typeof calendar === 'string' ? + this.instance(calendar, language) : calendar)) || this.instance(); + return calendar.newDate(year, month, day); + }, + + /** A simple digit substitution function for localising numbers via the Calendar digits option. + @member Calendars + @param digits {string[]} The substitute digits, for 0 through 9. + @return {function} The substitution function. */ + substituteDigits: function(digits) { + return function(value) { + return (value + '').replace(/[0-9]/g, function(digit) { + return digits[digit]; + }); + } + }, + + /** Digit substitution function for localising Chinese style numbers via the Calendar digits option. + @member Calendars + @param digits {string[]} The substitute digits, for 0 through 9. + @param powers {string[]} The characters denoting powers of 10, i.e. 1, 10, 100, 1000. + @return {function} The substitution function. */ + substituteChineseDigits: function(digits, powers) { + return function(value) { + var localNumber = ''; + var power = 0; + while (value > 0) { + var units = value % 10; + localNumber = (units === 0 ? '' : digits[units] + powers[power]) + localNumber; + power++; + value = Math.floor(value / 10); + } + if (localNumber.indexOf(digits[1] + powers[1]) === 0) { + localNumber = localNumber.substr(1); + } + return localNumber || digits[0]; + } + } +}); + +/** Generic date, based on a particular calendar. + @class CDate + @param calendar {BaseCalendar} The underlying calendar implementation. + @param year {number} The year for this date. + @param month {number} The month for this date. + @param day {number} The day for this date. + @return {CDate} The date object. + @throws Error if an invalid date. */ +function CDate(calendar, year, month, day) { + this._calendar = calendar; + this._year = year; + this._month = month; + this._day = day; + if (this._calendar._validateLevel === 0 && + !this._calendar.isValid(this._year, this._month, this._day)) { + throw (_exports.local.invalidDate || _exports.regionalOptions[''].invalidDate). + replace(/\{0\}/, this._calendar.local.name); + } +} + +/** Pad a numeric value with leading zeroes. + @private + @param value {number} The number to format. + @param length {number} The minimum length. + @return {string} The formatted number. */ +function pad(value, length) { + value = '' + value; + return '000000'.substring(0, length - value.length) + value; +} + +assign(CDate.prototype, { + + /** Create a new date. + @memberof CDate + @param [year] {CDate|number} The date to copy or the year for the date (default this date). + @param [month] {number} The month for the date. + @param [day] {number} The day for the date. + @return {CDate} The new date. + @throws Error if an invalid date. */ + newDate: function(year, month, day) { + return this._calendar.newDate((year == null ? this : year), month, day); + }, + + /** Set or retrieve the year for this date. + @memberof CDate + @param [year] {number} The year for the date. + @return {number|CDate} The date's year (if no parameter) or the updated date. + @throws Error if an invalid date. */ + year: function(year) { + return (arguments.length === 0 ? this._year : this.set(year, 'y')); + }, + + /** Set or retrieve the month for this date. + @memberof CDate + @param [month] {number} The month for the date. + @return {number|CDate} The date's month (if no parameter) or the updated date. + @throws Error if an invalid date. */ + month: function(month) { + return (arguments.length === 0 ? this._month : this.set(month, 'm')); + }, + + /** Set or retrieve the day for this date. + @memberof CDate + @param [day] {number} The day for the date. + @return {number|CData} The date's day (if no parameter) or the updated date. + @throws Error if an invalid date. */ + day: function(day) { + return (arguments.length === 0 ? this._day : this.set(day, 'd')); + }, + + /** Set new values for this date. + @memberof CDate + @param year {number} The year for the date. + @param month {number} The month for the date. + @param day {number} The day for the date. + @return {CDate} The updated date. + @throws Error if an invalid date. */ + date: function(year, month, day) { + if (!this._calendar.isValid(year, month, day)) { + throw (_exports.local.invalidDate || _exports.regionalOptions[''].invalidDate). + replace(/\{0\}/, this._calendar.local.name); + } + this._year = year; + this._month = month; + this._day = day; + return this; + }, + + /** Determine whether this date is in a leap year. + @memberof CDate + @return {boolean} true if this is a leap year, false if not. */ + leapYear: function() { + return this._calendar.leapYear(this); + }, + + /** Retrieve the epoch designator for this date, e.g. BCE or CE. + @memberof CDate + @return {string} The current epoch. */ + epoch: function() { + return this._calendar.epoch(this); + }, + + /** Format the year, if not a simple sequential number. + @memberof CDate + @return {string} The formatted year. */ + formatYear: function() { + return this._calendar.formatYear(this); + }, + + /** Retrieve the month of the year for this date, + i.e. the month's position within a numbered year. + @memberof CDate + @return {number} The month of the year: minMonth to months per year. */ + monthOfYear: function() { + return this._calendar.monthOfYear(this); + }, + + /** Retrieve the week of the year for this date. + @memberof CDate + @return {number} The week of the year: 1 to weeks per year. */ + weekOfYear: function() { + return this._calendar.weekOfYear(this); + }, + + /** Retrieve the number of days in the year for this date. + @memberof CDate + @return {number} The number of days in this year. */ + daysInYear: function() { + return this._calendar.daysInYear(this); + }, + + /** Retrieve the day of the year for this date. + @memberof CDate + @return {number} The day of the year: 1 to days per year. */ + dayOfYear: function() { + return this._calendar.dayOfYear(this); + }, + + /** Retrieve the number of days in the month for this date. + @memberof CDate + @return {number} The number of days. */ + daysInMonth: function() { + return this._calendar.daysInMonth(this); + }, + + /** Retrieve the day of the week for this date. + @memberof CDate + @return {number} The day of the week: 0 to number of days - 1. */ + dayOfWeek: function() { + return this._calendar.dayOfWeek(this); + }, + + /** Determine whether this date is a week day. + @memberof CDate + @return {boolean} true if a week day, false if not. */ + weekDay: function() { + return this._calendar.weekDay(this); + }, + + /** Retrieve additional information about this date. + @memberof CDate + @return {object} Additional information - contents depends on calendar. */ + extraInfo: function() { + return this._calendar.extraInfo(this); + }, + + /** Add period(s) to a date. + @memberof CDate + @param offset {number} The number of periods to adjust by. + @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day. + @return {CDate} The updated date. */ + add: function(offset, period) { + return this._calendar.add(this, offset, period); + }, + + /** Set a portion of the date. + @memberof CDate + @param value {number} The new value for the period. + @param period {string} One of 'y' for year, 'm' for month, 'd' for day. + @return {CDate} The updated date. + @throws Error if not a valid date. */ + set: function(value, period) { + return this._calendar.set(this, value, period); + }, + + /** Compare this date to another date. + @memberof CDate + @param date {CDate} The other date. + @return {number} -1 if this date is before the other date, + 0 if they are equal, or +1 if this date is after the other date. */ + compareTo: function(date) { + if (this._calendar.name !== date._calendar.name) { + throw (_exports.local.differentCalendars || _exports.regionalOptions[''].differentCalendars). + replace(/\{0\}/, this._calendar.local.name).replace(/\{1\}/, date._calendar.local.name); + } + var c = (this._year !== date._year ? this._year - date._year : + this._month !== date._month ? this.monthOfYear() - date.monthOfYear() : + this._day - date._day); + return (c === 0 ? 0 : (c < 0 ? -1 : +1)); + }, + + /** Retrieve the calendar backing this date. + @memberof CDate + @return {BaseCalendar} The calendar implementation. */ + calendar: function() { + return this._calendar; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof CDate + @return {number} The equivalent Julian date. */ + toJD: function() { + return this._calendar.toJD(this); + }, + + /** Create a new date from a Julian date. + @memberof CDate + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + return this._calendar.fromJD(jd); + }, + + /** Convert this date to a standard (Gregorian) JavaScript Date. + @memberof CDate + @return {Date} The equivalent JavaScript date. */ + toJSDate: function() { + return this._calendar.toJSDate(this); + }, + + /** Create a new date from a standard (Gregorian) JavaScript Date. + @memberof CDate + @param jsd {Date} The JavaScript date to convert. + @return {CDate} The equivalent date. */ + fromJSDate: function(jsd) { + return this._calendar.fromJSDate(jsd); + }, + + /** Convert to a string for display. + @memberof CDate + @return {string} This date as a string. */ + toString: function() { + return (this.year() < 0 ? '-' : '') + pad(Math.abs(this.year()), 4) + + '-' + pad(this.month(), 2) + '-' + pad(this.day(), 2); + } +}); + +/** Basic functionality for all calendars. + Other calendars should extend this: +
OtherCalendar.prototype = new BaseCalendar;
+ @class BaseCalendar */ +function BaseCalendar() { + this.shortYearCutoff = '+10'; +} + +assign(BaseCalendar.prototype, { + _validateLevel: 0, // "Stack" to turn validation on/off + + /** Create a new date within this calendar - today if no parameters given. + @memberof BaseCalendar + @param year {CDate|number} The date to duplicate or the year for the date. + @param [month] {number} The month for the date. + @param [day] {number} The day for the date. + @return {CDate} The new date. + @throws Error if not a valid date or a different calendar used. */ + newDate: function(year, month, day) { + if (year == null) { + return this.today(); + } + if (year.year) { + this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + day = year.day(); + month = year.month(); + year = year.year(); + } + return new CDate(this, year, month, day); + }, + + /** Create a new date for today. + @memberof BaseCalendar + @return {CDate} Today's date. */ + today: function() { + return this.fromJSDate(new Date()); + }, + + /** Retrieve the epoch designator for this date. + @memberof BaseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {string} The current epoch. + @throws Error if an invalid year or a different calendar used. */ + epoch: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, + _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear); + return (date.year() < 0 ? this.local.epochs[0] : this.local.epochs[1]); + }, + + /** Format the year, if not a simple sequential number + @memberof BaseCalendar + @param year {CDate|number} The date to format or the year to format. + @return {string} The formatted year. + @throws Error if an invalid year or a different calendar used. */ + formatYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, + _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear); + return (date.year() < 0 ? '-' : '') + pad(Math.abs(date.year()), 4) + }, + + /** Retrieve the number of months in a year. + @memberof BaseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of months. + @throws Error if an invalid year or a different calendar used. */ + monthsInYear: function(year) { + this._validate(year, this.minMonth, this.minDay, + _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear); + return 12; + }, + + /** Calculate the month's ordinal position within the year - + for those calendars that don't start at month 1! + @memberof BaseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param month {number} The month to examine. + @return {number} The ordinal position, starting from minMonth. + @throws Error if an invalid year/month or a different calendar used. */ + monthOfYear: function(year, month) { + var date = this._validate(year, month, this.minDay, + _exports.local.invalidMonth || _exports.regionalOptions[''].invalidMonth); + return (date.month() + this.monthsInYear(date) - this.firstMonth) % + this.monthsInYear(date) + this.minMonth; + }, + + /** Calculate actual month from ordinal position, starting from minMonth. + @memberof BaseCalendar + @param year {number} The year to examine. + @param ord {number} The month's ordinal position. + @return {number} The month's number. + @throws Error if an invalid year/month. */ + fromMonthOfYear: function(year, ord) { + var m = (ord + this.firstMonth - 2 * this.minMonth) % + this.monthsInYear(year) + this.minMonth; + this._validate(year, m, this.minDay, + _exports.local.invalidMonth || _exports.regionalOptions[''].invalidMonth); + return m; + }, + + /** Retrieve the number of days in a year. + @memberof BaseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {number} The number of days. + @throws Error if an invalid year or a different calendar used. */ + daysInYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, + _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear); + return (this.leapYear(date) ? 366 : 365); + }, + + /** Retrieve the day of the year for a date. + @memberof BaseCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The day of the year. + @throws Error if an invalid date or a different calendar used. */ + dayOfYear: function(year, month, day) { + var date = this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + return date.toJD() - this.newDate(date.year(), + this.fromMonthOfYear(date.year(), this.minMonth), this.minDay).toJD() + 1; + }, + + /** Retrieve the number of days in a week. + @memberof BaseCalendar + @return {number} The number of days. */ + daysInWeek: function() { + return 7; + }, + + /** Retrieve the day of the week for a date. + @memberof BaseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The day of the week: 0 to number of days - 1. + @throws Error if an invalid date or a different calendar used. */ + dayOfWeek: function(year, month, day) { + var date = this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + return (Math.floor(this.toJD(date)) + 2) % this.daysInWeek(); + }, + + /** Retrieve additional information about a date. + @memberof BaseCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {object} Additional information - contents depends on calendar. + @throws Error if an invalid date or a different calendar used. */ + extraInfo: function(year, month, day) { + this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + return {}; + }, + + /** Add period(s) to a date. + Cater for no year zero. + @memberof BaseCalendar + @param date {CDate} The starting date. + @param offset {number} The number of periods to adjust by. + @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day. + @return {CDate} The updated date. + @throws Error if a different calendar used. */ + add: function(date, offset, period) { + this._validate(date, this.minMonth, this.minDay, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + return this._correctAdd(date, this._add(date, offset, period), offset, period); + }, + + /** Add period(s) to a date. + @memberof BaseCalendar + @private + @param date {CDate} The starting date. + @param offset {number} The number of periods to adjust by. + @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day. + @return {CDate} The updated date. */ + _add: function(date, offset, period) { + this._validateLevel++; + if (period === 'd' || period === 'w') { + var jd = date.toJD() + offset * (period === 'w' ? this.daysInWeek() : 1); + var d = date.calendar().fromJD(jd); + this._validateLevel--; + return [d.year(), d.month(), d.day()]; + } + try { + var y = date.year() + (period === 'y' ? offset : 0); + var m = date.monthOfYear() + (period === 'm' ? offset : 0); + var d = date.day();// + (period === 'd' ? offset : 0) + + //(period === 'w' ? offset * this.daysInWeek() : 0); + var resyncYearMonth = function(calendar) { + while (m < calendar.minMonth) { + y--; + m += calendar.monthsInYear(y); + } + var yearMonths = calendar.monthsInYear(y); + while (m > yearMonths - 1 + calendar.minMonth) { + y++; + m -= yearMonths; + yearMonths = calendar.monthsInYear(y); + } + }; + if (period === 'y') { + if (date.month() !== this.fromMonthOfYear(y, m)) { // Hebrew + m = this.newDate(y, date.month(), this.minDay).monthOfYear(); + } + m = Math.min(m, this.monthsInYear(y)); + d = Math.min(d, this.daysInMonth(y, this.fromMonthOfYear(y, m))); + } + else if (period === 'm') { + resyncYearMonth(this); + d = Math.min(d, this.daysInMonth(y, this.fromMonthOfYear(y, m))); + } + var ymd = [y, this.fromMonthOfYear(y, m), d]; + this._validateLevel--; + return ymd; + } + catch (e) { + this._validateLevel--; + throw e; + } + }, + + /** Correct a candidate date after adding period(s) to a date. + Handle no year zero if necessary. + @memberof BaseCalendar + @private + @param date {CDate} The starting date. + @param ymd {number[]} The added date. + @param offset {number} The number of periods to adjust by. + @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day. + @return {CDate} The updated date. */ + _correctAdd: function(date, ymd, offset, period) { + if (!this.hasYearZero && (period === 'y' || period === 'm')) { + if (ymd[0] === 0 || // In year zero + (date.year() > 0) !== (ymd[0] > 0)) { // Crossed year zero + var adj = {y: [1, 1, 'y'], m: [1, this.monthsInYear(-1), 'm'], + w: [this.daysInWeek(), this.daysInYear(-1), 'd'], + d: [1, this.daysInYear(-1), 'd']}[period]; + var dir = (offset < 0 ? -1 : +1); + ymd = this._add(date, offset * adj[0] + dir * adj[1], adj[2]); + } + } + return date.date(ymd[0], ymd[1], ymd[2]); + }, + + /** Set a portion of the date. + @memberof BaseCalendar + @param date {CDate} The starting date. + @param value {number} The new value for the period. + @param period {string} One of 'y' for year, 'm' for month, 'd' for day. + @return {CDate} The updated date. + @throws Error if an invalid date or a different calendar used. */ + set: function(date, value, period) { + this._validate(date, this.minMonth, this.minDay, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + var y = (period === 'y' ? value : date.year()); + var m = (period === 'm' ? value : date.month()); + var d = (period === 'd' ? value : date.day()); + if (period === 'y' || period === 'm') { + d = Math.min(d, this.daysInMonth(y, m)); + } + return date.date(y, m, d); + }, + + /** Determine whether a date is valid for this calendar. + @memberof BaseCalendar + @param year {number} The year to examine. + @param month {number} The month to examine. + @param day {number} The day to examine. + @return {boolean} true if a valid date, false if not. */ + isValid: function(year, month, day) { + this._validateLevel++; + var valid = (this.hasYearZero || year !== 0); + if (valid) { + var date = this.newDate(year, month, this.minDay); + valid = (month >= this.minMonth && month - this.minMonth < this.monthsInYear(date)) && + (day >= this.minDay && day - this.minDay < this.daysInMonth(date)); + } + this._validateLevel--; + return valid; + }, + + /** Convert the date to a standard (Gregorian) JavaScript Date. + @memberof BaseCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {Date} The equivalent JavaScript date. + @throws Error if an invalid date or a different calendar used. */ + toJSDate: function(year, month, day) { + var date = this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + return _exports.instance().fromJD(this.toJD(date)).toJSDate(); + }, + + /** Convert the date from a standard (Gregorian) JavaScript Date. + @memberof BaseCalendar + @param jsd {Date} The JavaScript date. + @return {CDate} The equivalent calendar date. */ + fromJSDate: function(jsd) { + return this.fromJD(_exports.instance().fromJSDate(jsd).toJD()); + }, + + /** Check that a candidate date is from the same calendar and is valid. + @memberof BaseCalendar + @private + @param year {CDate|number} The date to validate or the year to validate. + @param [month] {number} The month to validate. + @param [day] {number} The day to validate. + @param error {string} Rrror message if invalid. + @throws Error if different calendars used or invalid date. */ + _validate: function(year, month, day, error) { + if (year.year) { + if (this._validateLevel === 0 && this.name !== year.calendar().name) { + throw (_exports.local.differentCalendars || _exports.regionalOptions[''].differentCalendars). + replace(/\{0\}/, this.local.name).replace(/\{1\}/, year.calendar().local.name); + } + return year; + } + try { + this._validateLevel++; + if (this._validateLevel === 1 && !this.isValid(year, month, day)) { + throw error.replace(/\{0\}/, this.local.name); + } + var date = this.newDate(year, month, day); + this._validateLevel--; + return date; + } + catch (e) { + this._validateLevel--; + throw e; + } + } +}); + +/** Implementation of the Proleptic Gregorian Calendar. + See http://en.wikipedia.org/wiki/Gregorian_calendar + and http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar. + @class GregorianCalendar + @augments BaseCalendar + @param [language=''] {string} The language code (default English) for localisation. */ +function GregorianCalendar(language) { + this.local = this.regionalOptions[language] || this.regionalOptions['']; +} + +GregorianCalendar.prototype = new BaseCalendar; + +assign(GregorianCalendar.prototype, { + /** The calendar name. + @memberof GregorianCalendar */ + name: 'Gregorian', + /** Julian date of start of Gregorian epoch: 1 January 0001 CE. + @memberof GregorianCalendar */ + jdEpoch: 1721425.5, + /** Days per month in a common year. + @memberof GregorianCalendar */ + daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + /** true if has a year zero, false if not. + @memberof GregorianCalendar */ + hasYearZero: false, + /** The minimum month number. + @memberof GregorianCalendar */ + minMonth: 1, + /** The first month in the year. + @memberof GregorianCalendar */ + firstMonth: 1, + /** The minimum day number. + @memberof GregorianCalendar */ + minDay: 1, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @memberof GregorianCalendar + @property name {string} The calendar name. + @property epochs {string[]} The epoch names. + @property monthNames {string[]} The long names of the months of the year. + @property monthNamesShort {string[]} The short names of the months of the year. + @property dayNames {string[]} The long names of the days of the week. + @property dayNamesShort {string[]} The short names of the days of the week. + @property dayNamesMin {string[]} The minimal names of the days of the week. + @property dateFormat {string} The date format for this calendar. + See the options on formatDate for details. + @property firstDay {number} The number of the first day of the week, starting at 0. + @property isRTL {number} true if this localisation reads right-to-left. */ + regionalOptions: { // Localisations + '': { + name: 'Gregorian', + epochs: ['BCE', 'CE'], + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + digits: null, + dateFormat: 'mm/dd/yyyy', + firstDay: 0, + isRTL: false + } + }, + + /** Determine whether this date is in a leap year. + @memberof GregorianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @return {boolean} true if this is a leap year, false if not. + @throws Error if an invalid year or a different calendar used. */ + leapYear: function(year) { + var date = this._validate(year, this.minMonth, this.minDay, + _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear); + var year = date.year() + (date.year() < 0 ? 1 : 0); // No year zero + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + }, + + /** Determine the week of the year for a date - ISO 8601. + @memberof GregorianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {number} The week of the year, starting from 1. + @throws Error if an invalid date or a different calendar used. */ + weekOfYear: function(year, month, day) { + // Find Thursday of this week starting on Monday + var checkDate = this.newDate(year, month, day); + checkDate.add(4 - (checkDate.dayOfWeek() || 7), 'd'); + return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1; + }, + + /** Retrieve the number of days in a month. + @memberof GregorianCalendar + @param year {CDate|number} The date to examine or the year of the month. + @param [month] {number} The month. + @return {number} The number of days in this month. + @throws Error if an invalid month/year or a different calendar used. */ + daysInMonth: function(year, month) { + var date = this._validate(year, month, this.minDay, + _exports.local.invalidMonth || _exports.regionalOptions[''].invalidMonth); + return this.daysPerMonth[date.month() - 1] + + (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0); + }, + + /** Determine whether this date is a week day. + @memberof GregorianCalendar + @param year {CDate|number} The date to examine or the year to examine. + @param [month] {number} The month to examine. + @param [day] {number} The day to examine. + @return {boolean} true if a week day, false if not. + @throws Error if an invalid date or a different calendar used. */ + weekDay: function(year, month, day) { + return (this.dayOfWeek(year, month, day) || 7) < 6; + }, + + /** Retrieve the Julian date equivalent for this date, + i.e. days since January 1, 4713 BCE Greenwich noon. + @memberof GregorianCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {number} The equivalent Julian date. + @throws Error if an invalid date or a different calendar used. */ + toJD: function(year, month, day) { + var date = this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + year = date.year(); + month = date.month(); + day = date.day(); + if (year < 0) { year++; } // No year zero + // Jean Meeus algorithm, "Astronomical Algorithms", 1991 + if (month < 3) { + month += 12; + year--; + } + var a = Math.floor(year / 100); + var b = 2 - a + Math.floor(a / 4); + return Math.floor(365.25 * (year + 4716)) + + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5; + }, + + /** Create a new date from a Julian date. + @memberof GregorianCalendar + @param jd {number} The Julian date to convert. + @return {CDate} The equivalent date. */ + fromJD: function(jd) { + // Jean Meeus algorithm, "Astronomical Algorithms", 1991 + var z = Math.floor(jd + 0.5); + var a = Math.floor((z - 1867216.25) / 36524.25); + a = z + 1 + a - Math.floor(a / 4); + var b = a + 1524; + var c = Math.floor((b - 122.1) / 365.25); + var d = Math.floor(365.25 * c); + var e = Math.floor((b - d) / 30.6001); + var day = b - d - Math.floor(e * 30.6001); + var month = e - (e > 13.5 ? 13 : 1); + var year = c - (month > 2.5 ? 4716 : 4715); + if (year <= 0) { year--; } // No year zero + return this.newDate(year, month, day); + }, + + /** Convert this date to a standard (Gregorian) JavaScript Date. + @memberof GregorianCalendar + @param year {CDate|number} The date to convert or the year to convert. + @param [month] {number} The month to convert. + @param [day] {number} The day to convert. + @return {Date} The equivalent JavaScript date. + @throws Error if an invalid date or a different calendar used. */ + toJSDate: function(year, month, day) { + var date = this._validate(year, month, day, + _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate); + var jsd = new Date(date.year(), date.month() - 1, date.day()); + jsd.setHours(0); + jsd.setMinutes(0); + jsd.setSeconds(0); + jsd.setMilliseconds(0); + // Hours may be non-zero on daylight saving cut-over: + // > 12 when midnight changeover, but then cannot generate + // midnight datetime, so jump to 1AM, otherwise reset. + jsd.setHours(jsd.getHours() > 12 ? jsd.getHours() + 2 : 0); + return jsd; + }, + + /** Create a new date from a standard (Gregorian) JavaScript Date. + @memberof GregorianCalendar + @param jsd {Date} The JavaScript date to convert. + @return {CDate} The equivalent date. */ + fromJSDate: function(jsd) { + return this.newDate(jsd.getFullYear(), jsd.getMonth() + 1, jsd.getDate()); + } +}); + +// Singleton manager +var _exports = module.exports = new Calendars(); + +// Date template +_exports.cdate = CDate; + +// Base calendar template +_exports.baseCalendar = BaseCalendar; + +// Gregorian calendar implementation +_exports.calendars.gregorian = GregorianCalendar; + + +},{"object-assign":71}],136:[function(_dereq_,module,exports){ +/* + * World Calendars + * https://github.com/alexcjohnson/world-calendars + * + * Batch-converted from kbwood/calendars + * Many thanks to Keith Wood and all of the contributors to the original project! + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* http://keith-wood.name/calendars.html + Calendars extras for jQuery v2.0.2. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Available under the MIT (http://keith-wood.name/licence.html) license. + Please attribute the author if you use it. */ + +var assign = _dereq_('object-assign'); +var main = _dereq_('./main'); + + +assign(main.regionalOptions[''], { + invalidArguments: 'Invalid arguments', + invalidFormat: 'Cannot format a date from another calendar', + missingNumberAt: 'Missing number at position {0}', + unknownNameAt: 'Unknown name at position {0}', + unexpectedLiteralAt: 'Unexpected literal at position {0}', + unexpectedText: 'Additional text found at end' +}); +main.local = main.regionalOptions['']; + +assign(main.cdate.prototype, { + + /** Format this date. + Found in the jquery.calendars.plus.js module. + @memberof CDate + @param [format] {string} The date format to use (see formatDate). + @param [settings] {object} Options for the formatDate function. + @return {string} The formatted date. */ + formatDate: function(format, settings) { + if (typeof format !== 'string') { + settings = format; + format = ''; + } + return this._calendar.formatDate(format || '', this, settings); + } +}); + +assign(main.baseCalendar.prototype, { + + UNIX_EPOCH: main.instance().newDate(1970, 1, 1).toJD(), + SECS_PER_DAY: 24 * 60 * 60, + TICKS_EPOCH: main.instance().jdEpoch, // 1 January 0001 CE + TICKS_PER_DAY: 24 * 60 * 60 * 10000000, + + /** Date form for ATOM (RFC 3339/ISO 8601). + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + ATOM: 'yyyy-mm-dd', + /** Date form for cookies. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + COOKIE: 'D, dd M yyyy', + /** Date form for full date. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + FULL: 'DD, MM d, yyyy', + /** Date form for ISO 8601. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + ISO_8601: 'yyyy-mm-dd', + /** Date form for Julian date. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + JULIAN: 'J', + /** Date form for RFC 822. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + RFC_822: 'D, d M yy', + /** Date form for RFC 850. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + RFC_850: 'DD, dd-M-yy', + /** Date form for RFC 1036. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + RFC_1036: 'D, d M yy', + /** Date form for RFC 1123. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + RFC_1123: 'D, d M yyyy', + /** Date form for RFC 2822. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + RFC_2822: 'D, d M yyyy', + /** Date form for RSS (RFC 822). + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + RSS: 'D, d M yy', + /** Date form for Windows ticks. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + TICKS: '!', + /** Date form for Unix timestamp. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + TIMESTAMP: '@', + /** Date form for W3c (ISO 8601). + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar */ + W3C: 'yyyy-mm-dd', + + /** Format a date object into a string value. + The format can be combinations of the following: +
    +
  • d - day of month (no leading zero)
  • +
  • dd - day of month (two digit)
  • +
  • o - day of year (no leading zeros)
  • +
  • oo - day of year (three digit)
  • +
  • D - day name short
  • +
  • DD - day name long
  • +
  • w - week of year (no leading zero)
  • +
  • ww - week of year (two digit)
  • +
  • m - month of year (no leading zero)
  • +
  • mm - month of year (two digit)
  • +
  • M - month name short
  • +
  • MM - month name long
  • +
  • yy - year (two digit)
  • +
  • yyyy - year (four digit)
  • +
  • YYYY - formatted year
  • +
  • J - Julian date (days since January 1, 4713 BCE Greenwich noon)
  • +
  • @ - Unix timestamp (s since 01/01/1970)
  • +
  • ! - Windows ticks (100ns since 01/01/0001)
  • +
  • '...' - literal text
  • +
  • '' - single quote
  • +
+ Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar + @param [format] {string} The desired format of the date (defaults to calendar format). + @param date {CDate} The date value to format. + @param [settings] {object} Addition options, whose attributes include: + @property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday. + @property [dayNames] {string[]} Names of the days from Sunday. + @property [monthNamesShort] {string[]} Abbreviated names of the months. + @property [monthNames] {string[]} Names of the months. + @property [calculateWeek] {CalendarsPickerCalculateWeek} Function that determines week of the year. + @property [localNumbers=false] {boolean} true to localise numbers (if available), + false to use normal Arabic numerals. + @return {string} The date in the above format. + @throws Errors if the date is from a different calendar. */ + formatDate: function(format, date, settings) { + if (typeof format !== 'string') { + settings = date; + date = format; + format = ''; + } + if (!date) { + return ''; + } + if (date.calendar() !== this) { + throw main.local.invalidFormat || main.regionalOptions[''].invalidFormat; + } + format = format || this.local.dateFormat; + settings = settings || {}; + var dayNamesShort = settings.dayNamesShort || this.local.dayNamesShort; + var dayNames = settings.dayNames || this.local.dayNames; + var monthNumbers = settings.monthNumbers || this.local.monthNumbers; + var monthNamesShort = settings.monthNamesShort || this.local.monthNamesShort; + var monthNames = settings.monthNames || this.local.monthNames; + var calculateWeek = settings.calculateWeek || this.local.calculateWeek; + // Check whether a format character is doubled + var doubled = function(match, step) { + var matches = 1; + while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) { + matches++; + } + iFormat += matches - 1; + return Math.floor(matches / (step || 1)) > 1; + }; + // Format a number, with leading zeroes if necessary + var formatNumber = function(match, value, len, step) { + var num = '' + value; + if (doubled(match, step)) { + while (num.length < len) { + num = '0' + num; + } + } + return num; + }; + // Format a name, short or long as requested + var formatName = function(match, value, shortNames, longNames) { + return (doubled(match) ? longNames[value] : shortNames[value]); + }; + // Format month number + // (e.g. Chinese calendar needs to account for intercalary months) + var calendar = this; + var formatMonth = function(date) { + return (typeof monthNumbers === 'function') ? + monthNumbers.call(calendar, date, doubled('m')) : + localiseNumbers(formatNumber('m', date.month(), 2)); + }; + // Format a month name, short or long as requested + var formatMonthName = function(date, useLongName) { + if (useLongName) { + return (typeof monthNames === 'function') ? + monthNames.call(calendar, date) : + monthNames[date.month() - calendar.minMonth]; + } else { + return (typeof monthNamesShort === 'function') ? + monthNamesShort.call(calendar, date) : + monthNamesShort[date.month() - calendar.minMonth]; + } + }; + // Localise numbers if requested and available + var digits = this.local.digits; + var localiseNumbers = function(value) { + return (settings.localNumbers && digits ? digits(value) : value); + }; + var output = ''; + var literal = false; + for (var iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !doubled("'")) { + literal = false; + } + else { + output += format.charAt(iFormat); + } + } + else { + switch (format.charAt(iFormat)) { + case 'd': output += localiseNumbers(formatNumber('d', date.day(), 2)); break; + case 'D': output += formatName('D', date.dayOfWeek(), + dayNamesShort, dayNames); break; + case 'o': output += formatNumber('o', date.dayOfYear(), 3); break; + case 'w': output += formatNumber('w', date.weekOfYear(), 2); break; + case 'm': output += formatMonth(date); break; + case 'M': output += formatMonthName(date, doubled('M')); break; + case 'y': + output += (doubled('y', 2) ? date.year() : + (date.year() % 100 < 10 ? '0' : '') + date.year() % 100); + break; + case 'Y': + doubled('Y', 2); + output += date.formatYear(); + break; + case 'J': output += date.toJD(); break; + case '@': output += (date.toJD() - this.UNIX_EPOCH) * this.SECS_PER_DAY; break; + case '!': output += (date.toJD() - this.TICKS_EPOCH) * this.TICKS_PER_DAY; break; + case "'": + if (doubled("'")) { + output += "'"; + } + else { + literal = true; + } + break; + default: + output += format.charAt(iFormat); + } + } + } + return output; + }, + + /** Parse a string value into a date object. + See formatDate for the possible formats, plus: +
    +
  • * - ignore rest of string
  • +
+ Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar + @param format {string} The expected format of the date ('' for default calendar format). + @param value {string} The date in the above format. + @param [settings] {object} Additional options whose attributes include: + @property [shortYearCutoff] {number} The cutoff year for determining the century. + @property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday. + @property [dayNames] {string[]} Names of the days from Sunday. + @property [monthNamesShort] {string[]} Abbreviated names of the months. + @property [monthNames] {string[]} Names of the months. + @return {CDate} The extracted date value or null if value is blank. + @throws Errors if the format and/or value are missing, + if the value doesn't match the format, or if the date is invalid. */ + parseDate: function(format, value, settings) { + if (value == null) { + throw main.local.invalidArguments || main.regionalOptions[''].invalidArguments; + } + value = (typeof value === 'object' ? value.toString() : value + ''); + if (value === '') { + return null; + } + format = format || this.local.dateFormat; + settings = settings || {}; + var shortYearCutoff = settings.shortYearCutoff || this.shortYearCutoff; + shortYearCutoff = (typeof shortYearCutoff !== 'string' ? shortYearCutoff : + this.today().year() % 100 + parseInt(shortYearCutoff, 10)); + var dayNamesShort = settings.dayNamesShort || this.local.dayNamesShort; + var dayNames = settings.dayNames || this.local.dayNames; + var parseMonth = settings.parseMonth || this.local.parseMonth; + var monthNumbers = settings.monthNumbers || this.local.monthNumbers; + var monthNamesShort = settings.monthNamesShort || this.local.monthNamesShort; + var monthNames = settings.monthNames || this.local.monthNames; + var jd = -1; + var year = -1; + var month = -1; + var day = -1; + var doy = -1; + var shortYear = false; + var literal = false; + // Check whether a format character is doubled + var doubled = function(match, step) { + var matches = 1; + while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) { + matches++; + } + iFormat += matches - 1; + return Math.floor(matches / (step || 1)) > 1; + }; + // Extract a number from the string value + var getNumber = function(match, step) { + var isDoubled = doubled(match, step); + var size = [2, 3, isDoubled ? 4 : 2, isDoubled ? 4 : 2, 10, 11, 20]['oyYJ@!'.indexOf(match) + 1]; + var digits = new RegExp('^-?\\d{1,' + size + '}'); + var num = value.substring(iValue).match(digits); + if (!num) { + throw (main.local.missingNumberAt || main.regionalOptions[''].missingNumberAt). + replace(/\{0\}/, iValue); + } + iValue += num[0].length; + return parseInt(num[0], 10); + }; + // Extract a month number from the string value + var calendar = this; + var getMonthNumber = function() { + if (typeof monthNumbers === 'function') { + doubled('m'); // update iFormat + var month = monthNumbers.call(calendar, value.substring(iValue)); + iValue += month.length; + return month; + } + + return getNumber('m'); + }; + // Extract a name from the string value and convert to an index + var getName = function(match, shortNames, longNames, step) { + var names = (doubled(match, step) ? longNames : shortNames); + for (var i = 0; i < names.length; i++) { + if (value.substr(iValue, names[i].length).toLowerCase() === names[i].toLowerCase()) { + iValue += names[i].length; + return i + calendar.minMonth; + } + } + throw (main.local.unknownNameAt || main.regionalOptions[''].unknownNameAt). + replace(/\{0\}/, iValue); + }; + // Extract a month number from the string value + var getMonthName = function() { + if (typeof monthNames === 'function') { + var month = doubled('M') ? + monthNames.call(calendar, value.substring(iValue)) : + monthNamesShort.call(calendar, value.substring(iValue)); + iValue += month.length; + return month; + } + + return getName('M', monthNamesShort, monthNames); + }; + // Confirm that a literal character matches the string value + var checkLiteral = function() { + if (value.charAt(iValue) !== format.charAt(iFormat)) { + throw (main.local.unexpectedLiteralAt || + main.regionalOptions[''].unexpectedLiteralAt).replace(/\{0\}/, iValue); + } + iValue++; + }; + var iValue = 0; + for (var iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !doubled("'")) { + literal = false; + } + else { + checkLiteral(); + } + } + else { + switch (format.charAt(iFormat)) { + case 'd': day = getNumber('d'); break; + case 'D': getName('D', dayNamesShort, dayNames); break; + case 'o': doy = getNumber('o'); break; + case 'w': getNumber('w'); break; + case 'm': month = getMonthNumber(); break; + case 'M': month = getMonthName(); break; + case 'y': + var iSave = iFormat; + shortYear = !doubled('y', 2); + iFormat = iSave; + year = getNumber('y', 2); + break; + case 'Y': year = getNumber('Y', 2); break; + case 'J': + jd = getNumber('J') + 0.5; + if (value.charAt(iValue) === '.') { + iValue++; + getNumber('J'); + } + break; + case '@': jd = getNumber('@') / this.SECS_PER_DAY + this.UNIX_EPOCH; break; + case '!': jd = getNumber('!') / this.TICKS_PER_DAY + this.TICKS_EPOCH; break; + case '*': iValue = value.length; break; + case "'": + if (doubled("'")) { + checkLiteral(); + } + else { + literal = true; + } + break; + default: checkLiteral(); + } + } + } + if (iValue < value.length) { + throw main.local.unexpectedText || main.regionalOptions[''].unexpectedText; + } + if (year === -1) { + year = this.today().year(); + } + else if (year < 100 && shortYear) { + year += (shortYearCutoff === -1 ? 1900 : this.today().year() - + this.today().year() % 100 - (year <= shortYearCutoff ? 0 : 100)); + } + if (typeof month === 'string') { + month = parseMonth.call(this, year, month); + } + if (doy > -1) { + month = 1; + day = doy; + for (var dim = this.daysInMonth(year, month); day > dim; dim = this.daysInMonth(year, month)) { + month++; + day -= dim; + } + } + return (jd > -1 ? this.fromJD(jd) : this.newDate(year, month, day)); + }, + + /** A date may be specified as an exact value or a relative one. + Found in the jquery.calendars.plus.js module. + @memberof BaseCalendar + @param dateSpec {CDate|number|string} The date as an object or string in the given format or + an offset - numeric days from today, or string amounts and periods, e.g. '+1m +2w'. + @param defaultDate {CDate} The date to use if no other supplied, may be null. + @param currentDate {CDate} The current date as a possible basis for relative dates, + if null today is used (optional) + @param [dateFormat] {string} The expected date format - see formatDate. + @param [settings] {object} Additional options whose attributes include: + @property [shortYearCutoff] {number} The cutoff year for determining the century. + @property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday. + @property [dayNames] {string[]} Names of the days from Sunday. + @property [monthNamesShort] {string[]} Abbreviated names of the months. + @property [monthNames] {string[]} Names of the months. + @return {CDate} The decoded date. */ + determineDate: function(dateSpec, defaultDate, currentDate, dateFormat, settings) { + if (currentDate && typeof currentDate !== 'object') { + settings = dateFormat; + dateFormat = currentDate; + currentDate = null; + } + if (typeof dateFormat !== 'string') { + settings = dateFormat; + dateFormat = ''; + } + var calendar = this; + var offsetString = function(offset) { + try { + return calendar.parseDate(dateFormat, offset, settings); + } + catch (e) { + // Ignore + } + offset = offset.toLowerCase(); + var date = (offset.match(/^c/) && currentDate ? + currentDate.newDate() : null) || calendar.today(); + var pattern = /([+-]?[0-9]+)\s*(d|w|m|y)?/g; + var matches = pattern.exec(offset); + while (matches) { + date.add(parseInt(matches[1], 10), matches[2] || 'd'); + matches = pattern.exec(offset); + } + return date; + }; + defaultDate = (defaultDate ? defaultDate.newDate() : null); + dateSpec = (dateSpec == null ? defaultDate : + (typeof dateSpec === 'string' ? offsetString(dateSpec) : (typeof dateSpec === 'number' ? + (isNaN(dateSpec) || dateSpec === Infinity || dateSpec === -Infinity ? defaultDate : + calendar.today().add(dateSpec, 'd')) : calendar.newDate(dateSpec)))); + return dateSpec; + } +}); + + +},{"./main":135,"object-assign":71}],137:[function(_dereq_,module,exports){ +'use strict'; + +/** + * All paths are tuned for maximum scalability of the arrowhead, + * ie throughout arrowwidth=0.3..3 the head is joined smoothly + * to the line, with the line coming from the left and ending at (0, 0). + * + * `backoff` is the distance to move the arrowhead and the end of the line, + * in order that the arrowhead points to the desired place, either at + * the tip of the arrow or (in the case of circle or square) + * the center of the symbol. + * + * `noRotate`, if truthy, says that this arrowhead should not rotate with the + * arrow. That's the case for squares, which should always be straight, and + * circles, for which it's irrelevant. + */ + +module.exports = [ + // no arrow + { + path: '', + backoff: 0 + }, + // wide with flat back + { + path: 'M-2.4,-3V3L0.6,0Z', + backoff: 0.6 + }, + // narrower with flat back + { + path: 'M-3.7,-2.5V2.5L1.3,0Z', + backoff: 1.3 + }, + // barbed + { + path: 'M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z', + backoff: 1.55 + }, + // wide line-drawn + { + path: 'M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z', + backoff: 1.6 + }, + // narrower line-drawn + { + path: 'M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z', + backoff: 2 + }, + // circle + { + path: 'M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z', + backoff: 0, + noRotate: true + }, + // square + { + path: 'M2,2V-2H-2V2Z', + backoff: 0, + noRotate: true + } +]; + +},{}],138:[function(_dereq_,module,exports){ +'use strict'; + +var ARROWPATHS = _dereq_('./arrow_paths'); +var fontAttrs = _dereq_('../../plots/font_attributes'); +var cartesianConstants = _dereq_('../../plots/cartesian/constants'); +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; +var axisPlaceableObjs = _dereq_('../../constants/axis_placeable_objects'); + +function arrowAxisRefDescription(axis) { + return [ + 'In order for absolute positioning of the arrow to work, *a' + axis + + 'ref* must be exactly the same as *' + axis + 'ref*, otherwise *a' + axis + + 'ref* will revert to *pixel* (explained next).', + 'For relative positioning, *a' + axis + 'ref* can be set to *pixel*,', + 'in which case the *a' + axis + '* value is specified in pixels', + 'relative to *' + axis + '*.', + 'Absolute positioning is useful', + 'for trendline annotations which should continue to indicate', + 'the correct trend when zoomed. Relative positioning is useful', + 'for specifying the text offset for an annotated point.' + ].join(' '); +} + +function arrowCoordinateDescription(axis, lower, upper) { + return [ + 'Sets the', axis, 'component of the arrow tail about the arrow head.', + 'If `a' + axis + 'ref` is `pixel`, a positive (negative)', + 'component corresponds to an arrow pointing', + 'from', upper, 'to', lower, '(' + lower, 'to', upper + ').', + 'If `a' + axis + 'ref` is not `pixel` and is exactly the same as `' + axis + 'ref`,', + 'this is an absolute value on that axis,', + 'like `' + axis + '`, specified in the same coordinates as `' + axis + 'ref`.' + ].join(' '); +} + +module.exports = templatedArray('annotation', { + visible: { + valType: 'boolean', + dflt: true, + editType: 'calc+arraydraw', + }, + + text: { + valType: 'string', + editType: 'calc+arraydraw', + }, + textangle: { + valType: 'angle', + dflt: 0, + editType: 'calc+arraydraw', + }, + font: fontAttrs({ + editType: 'calc+arraydraw', + colorEditType: 'arraydraw', + }), + width: { + valType: 'number', + min: 1, + dflt: null, + editType: 'calc+arraydraw', + }, + height: { + valType: 'number', + min: 1, + dflt: null, + editType: 'calc+arraydraw', + }, + opacity: { + valType: 'number', + min: 0, + max: 1, + dflt: 1, + editType: 'arraydraw', + }, + align: { + valType: 'enumerated', + values: ['left', 'center', 'right'], + dflt: 'center', + editType: 'arraydraw', + }, + valign: { + valType: 'enumerated', + values: ['top', 'middle', 'bottom'], + dflt: 'middle', + editType: 'arraydraw', + }, + bgcolor: { + valType: 'color', + dflt: 'rgba(0,0,0,0)', + editType: 'arraydraw', + }, + bordercolor: { + valType: 'color', + dflt: 'rgba(0,0,0,0)', + editType: 'arraydraw', + }, + borderpad: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'calc+arraydraw', + }, + borderwidth: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'calc+arraydraw', + }, + // arrow + showarrow: { + valType: 'boolean', + dflt: true, + editType: 'calc+arraydraw', + }, + arrowcolor: { + valType: 'color', + editType: 'arraydraw', + }, + arrowhead: { + valType: 'integer', + min: 0, + max: ARROWPATHS.length, + dflt: 1, + editType: 'arraydraw', + }, + startarrowhead: { + valType: 'integer', + min: 0, + max: ARROWPATHS.length, + dflt: 1, + editType: 'arraydraw', + }, + arrowside: { + valType: 'flaglist', + flags: ['end', 'start'], + extras: ['none'], + dflt: 'end', + editType: 'arraydraw', + }, + arrowsize: { + valType: 'number', + min: 0.3, + dflt: 1, + editType: 'calc+arraydraw', + }, + startarrowsize: { + valType: 'number', + min: 0.3, + dflt: 1, + editType: 'calc+arraydraw', + }, + arrowwidth: { + valType: 'number', + min: 0.1, + editType: 'calc+arraydraw', + }, + standoff: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'calc+arraydraw', + }, + startstandoff: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'calc+arraydraw', + }, + ax: { + valType: 'any', + editType: 'calc+arraydraw', + }, + ay: { + valType: 'any', + editType: 'calc+arraydraw', + }, + axref: { + valType: 'enumerated', + dflt: 'pixel', + values: [ + 'pixel', + cartesianConstants.idRegex.x.toString() + ], + editType: 'calc', + }, + ayref: { + valType: 'enumerated', + dflt: 'pixel', + values: [ + 'pixel', + cartesianConstants.idRegex.y.toString() + ], + editType: 'calc', + }, + // positioning + xref: { + valType: 'enumerated', + values: [ + 'paper', + cartesianConstants.idRegex.x.toString() + ], + editType: 'calc', + }, + x: { + valType: 'any', + editType: 'calc+arraydraw', + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'auto', + editType: 'calc+arraydraw', + }, + xshift: { + valType: 'number', + dflt: 0, + editType: 'calc+arraydraw', + }, + yref: { + valType: 'enumerated', + values: [ + 'paper', + cartesianConstants.idRegex.y.toString() + ], + editType: 'calc', + }, + y: { + valType: 'any', + editType: 'calc+arraydraw', + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + dflt: 'auto', + editType: 'calc+arraydraw', + }, + yshift: { + valType: 'number', + dflt: 0, + editType: 'calc+arraydraw', + }, + clicktoshow: { + valType: 'enumerated', + values: [false, 'onoff', 'onout'], + dflt: false, + editType: 'arraydraw', + }, + xclick: { + valType: 'any', + editType: 'arraydraw', + }, + yclick: { + valType: 'any', + editType: 'arraydraw', + }, + hovertext: { + valType: 'string', + editType: 'arraydraw', + }, + hoverlabel: { + bgcolor: { + valType: 'color', + editType: 'arraydraw', + }, + bordercolor: { + valType: 'color', + editType: 'arraydraw', + }, + font: fontAttrs({ + editType: 'arraydraw', + }), + editType: 'arraydraw' + }, + captureevents: { + valType: 'boolean', + editType: 'arraydraw', + }, + editType: 'calc', + + _deprecated: { + ref: { + valType: 'string', + editType: 'calc', + } + } +}); + +},{"../../constants/axis_placeable_objects":261,"../../plot_api/plot_template":320,"../../plots/cartesian/constants":338,"../../plots/font_attributes":360,"./arrow_paths":137}],139:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +var draw = _dereq_('./draw').draw; + + +module.exports = function calcAutorange(gd) { + var fullLayout = gd._fullLayout; + var annotationList = Lib.filterVisible(fullLayout.annotations); + + if(annotationList.length && gd._fullData.length) { + return Lib.syncOrAsync([draw, annAutorange], gd); + } +}; + +function annAutorange(gd) { + var fullLayout = gd._fullLayout; + + // find the bounding boxes for each of these annotations' + // relative to their anchor points + // use the arrow and the text bg rectangle, + // as the whole anno may include hidden text in its bbox + Lib.filterVisible(fullLayout.annotations).forEach(function(ann) { + var xa = Axes.getFromId(gd, ann.xref); + var ya = Axes.getFromId(gd, ann.yref); + var xRefType = Axes.getRefType(ann.xref); + var yRefType = Axes.getRefType(ann.yref); + + ann._extremes = {}; + if(xRefType === 'range') calcAxisExpansion(ann, xa); + if(yRefType === 'range') calcAxisExpansion(ann, ya); + }); +} + +function calcAxisExpansion(ann, ax) { + var axId = ax._id; + var letter = axId.charAt(0); + var pos = ann[letter]; + var apos = ann['a' + letter]; + var ref = ann[letter + 'ref']; + var aref = ann['a' + letter + 'ref']; + var padplus = ann['_' + letter + 'padplus']; + var padminus = ann['_' + letter + 'padminus']; + var shift = {x: 1, y: -1}[letter] * ann[letter + 'shift']; + var headSize = 3 * ann.arrowsize * ann.arrowwidth || 0; + var headPlus = headSize + shift; + var headMinus = headSize - shift; + var startHeadSize = 3 * ann.startarrowsize * ann.arrowwidth || 0; + var startHeadPlus = startHeadSize + shift; + var startHeadMinus = startHeadSize - shift; + var extremes; + + if(aref === ref) { + // expand for the arrowhead (padded by arrowhead) + var extremeArrowHead = Axes.findExtremes(ax, [ax.r2c(pos)], { + ppadplus: headPlus, + ppadminus: headMinus + }); + // again for the textbox (padded by textbox) + var extremeText = Axes.findExtremes(ax, [ax.r2c(apos)], { + ppadplus: Math.max(padplus, startHeadPlus), + ppadminus: Math.max(padminus, startHeadMinus) + }); + extremes = { + min: [extremeArrowHead.min[0], extremeText.min[0]], + max: [extremeArrowHead.max[0], extremeText.max[0]] + }; + } else { + startHeadPlus = apos ? startHeadPlus + apos : startHeadPlus; + startHeadMinus = apos ? startHeadMinus - apos : startHeadMinus; + extremes = Axes.findExtremes(ax, [ax.r2c(pos)], { + ppadplus: Math.max(padplus, headPlus, startHeadPlus), + ppadminus: Math.max(padminus, headMinus, startHeadMinus) + }); + } + + ann._extremes[axId] = extremes; +} + +},{"../../lib":285,"../../plots/cartesian/axes":331,"./draw":144}],140:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); +var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor; + +module.exports = { + hasClickToShow: hasClickToShow, + onClick: onClick +}; + +/* + * hasClickToShow: does the given hoverData have ANY annotations which will + * turn ON if we click here? (used by hover events to set cursor) + * + * gd: graphDiv + * hoverData: a hoverData array, as included with the *plotly_hover* or + * *plotly_click* events in the `points` attribute + * + * returns: boolean + */ +function hasClickToShow(gd, hoverData) { + var sets = getToggleSets(gd, hoverData); + return sets.on.length > 0 || sets.explicitOff.length > 0; +} + +/* + * onClick: perform the toggling (via Plotly.update) implied by clicking + * at this hoverData + * + * gd: graphDiv + * hoverData: a hoverData array, as included with the *plotly_hover* or + * *plotly_click* events in the `points` attribute + * + * returns: Promise that the update is complete + */ +function onClick(gd, hoverData) { + var toggleSets = getToggleSets(gd, hoverData); + var onSet = toggleSets.on; + var offSet = toggleSets.off.concat(toggleSets.explicitOff); + var update = {}; + var annotationsOut = gd._fullLayout.annotations; + var i, editHelpers; + + if(!(onSet.length || offSet.length)) return; + + for(i = 0; i < onSet.length; i++) { + editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[onSet[i]]); + editHelpers.modifyItem('visible', true); + Lib.extendFlat(update, editHelpers.getUpdateObj()); + } + + for(i = 0; i < offSet.length; i++) { + editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[offSet[i]]); + editHelpers.modifyItem('visible', false); + Lib.extendFlat(update, editHelpers.getUpdateObj()); + } + + return Registry.call('update', gd, {}, update); +} + +/* + * getToggleSets: find the annotations which will turn on or off at this + * hoverData + * + * gd: graphDiv + * hoverData: a hoverData array, as included with the *plotly_hover* or + * *plotly_click* events in the `points` attribute + * + * returns: { + * on: Array (indices of annotations to turn on), + * off: Array (indices to turn off because you're not hovering on them), + * explicitOff: Array (indices to turn off because you *are* hovering on them) + * } + */ +function getToggleSets(gd, hoverData) { + var annotations = gd._fullLayout.annotations; + var onSet = []; + var offSet = []; + var explicitOffSet = []; + var hoverLen = (hoverData || []).length; + + var i, j, anni, showMode, pointj, xa, ya, toggleType; + + for(i = 0; i < annotations.length; i++) { + anni = annotations[i]; + showMode = anni.clicktoshow; + + if(showMode) { + for(j = 0; j < hoverLen; j++) { + pointj = hoverData[j]; + xa = pointj.xaxis; + ya = pointj.yaxis; + + if(xa._id === anni.xref && + ya._id === anni.yref && + xa.d2r(pointj.x) === clickData2r(anni._xclick, xa) && + ya.d2r(pointj.y) === clickData2r(anni._yclick, ya) + ) { + // match! toggle this annotation + // regardless of its clicktoshow mode + // but if it's onout mode, off is implicit + if(anni.visible) { + if(showMode === 'onout') toggleType = offSet; + else toggleType = explicitOffSet; + } else { + toggleType = onSet; + } + toggleType.push(i); + break; + } + } + + if(j === hoverLen) { + // no match - only turn this annotation OFF, and only if + // showmode is 'onout' + if(anni.visible && showMode === 'onout') offSet.push(i); + } + } + } + + return {on: onSet, off: offSet, explicitOff: explicitOffSet}; +} + +// to handle log axes until v3 +function clickData2r(d, ax) { + return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d); +} + +},{"../../lib":285,"../../plot_api/plot_template":320,"../../registry":373}],141:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../color'); + +// defaults common to 'annotations' and 'annotations3d' +module.exports = function handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce) { + coerce('opacity'); + var bgColor = coerce('bgcolor'); + + var borderColor = coerce('bordercolor'); + var borderOpacity = Color.opacity(borderColor); + + coerce('borderpad'); + + var borderWidth = coerce('borderwidth'); + var showArrow = coerce('showarrow'); + + coerce('text', showArrow ? ' ' : fullLayout._dfltTitle.annotation); + coerce('textangle'); + Lib.coerceFont(coerce, 'font', fullLayout.font); + + coerce('width'); + coerce('align'); + + var h = coerce('height'); + if(h) coerce('valign'); + + if(showArrow) { + var arrowside = coerce('arrowside'); + var arrowhead; + var arrowsize; + + if(arrowside.indexOf('end') !== -1) { + arrowhead = coerce('arrowhead'); + arrowsize = coerce('arrowsize'); + } + + if(arrowside.indexOf('start') !== -1) { + coerce('startarrowhead', arrowhead); + coerce('startarrowsize', arrowsize); + } + coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine); + coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2); + coerce('standoff'); + coerce('startstandoff'); + } + + var hoverText = coerce('hovertext'); + var globalHoverLabel = fullLayout.hoverlabel || {}; + + if(hoverText) { + var hoverBG = coerce('hoverlabel.bgcolor', globalHoverLabel.bgcolor || + (Color.opacity(bgColor) ? Color.rgb(bgColor) : Color.defaultLine) + ); + + var hoverBorder = coerce('hoverlabel.bordercolor', globalHoverLabel.bordercolor || + Color.contrast(hoverBG) + ); + + Lib.coerceFont(coerce, 'hoverlabel.font', { + family: globalHoverLabel.font.family, + size: globalHoverLabel.font.size, + color: globalHoverLabel.font.color || hoverBorder + }); + } + + coerce('captureevents', !!hoverText); +}; + +},{"../../lib":285,"../color":155}],142:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var toLogRange = _dereq_('../../lib/to_log_range'); + +/* + * convertCoords: when converting an axis between log and linear + * you need to alter any annotations on that axis to keep them + * pointing at the same data point. + * In v3.0 this will become obsolete + * + * gd: the plot div + * ax: the axis being changed + * newType: the type it's getting + * doExtra: function(attr, val) from inside relayout that sets the attribute. + * Use this to make the changes as it's aware if any other changes in the + * same relayout call should override this conversion. + */ +module.exports = function convertCoords(gd, ax, newType, doExtra) { + ax = ax || {}; + + var toLog = (newType === 'log') && (ax.type === 'linear'); + var fromLog = (newType === 'linear') && (ax.type === 'log'); + + if(!(toLog || fromLog)) return; + + var annotations = gd._fullLayout.annotations; + var axLetter = ax._id.charAt(0); + var ann; + var attrPrefix; + + function convert(attr) { + var currentVal = ann[attr]; + var newVal = null; + + if(toLog) newVal = toLogRange(currentVal, ax.range); + else newVal = Math.pow(10, currentVal); + + // if conversion failed, delete the value so it gets a default value + if(!isNumeric(newVal)) newVal = null; + + doExtra(attrPrefix + attr, newVal); + } + + for(var i = 0; i < annotations.length; i++) { + ann = annotations[i]; + attrPrefix = 'annotations[' + i + '].'; + + if(ann[axLetter + 'ref'] === ax._id) convert(axLetter); + if(ann['a' + axLetter + 'ref'] === ax._id) convert('a' + axLetter); + } +}; + +},{"../../lib/to_log_range":309,"fast-isnumeric":31}],143:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); + +var handleAnnotationCommonDefaults = _dereq_('./common_defaults'); +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + handleArrayContainerDefaults(layoutIn, layoutOut, { + name: 'annotations', + handleItemDefaults: handleAnnotationDefaults + }); +}; + +function handleAnnotationDefaults(annIn, annOut, fullLayout) { + function coerce(attr, dflt) { + return Lib.coerce(annIn, annOut, attributes, attr, dflt); + } + + var visible = coerce('visible'); + var clickToShow = coerce('clicktoshow'); + + if(!(visible || clickToShow)) return; + + handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce); + + var showArrow = annOut.showarrow; + + // positioning + var axLetters = ['x', 'y']; + var arrowPosDflt = [-10, -30]; + var gdMock = {_fullLayout: fullLayout}; + + for(var i = 0; i < 2; i++) { + var axLetter = axLetters[i]; + + // xref, yref + var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper'); + + if(axRef !== 'paper') { + var ax = Axes.getFromId(gdMock, axRef); + ax._annIndices.push(annOut._index); + } + + // x, y + Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5); + + if(showArrow) { + var arrowPosAttr = 'a' + axLetter; + // axref, ayref + var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel', + ['pixel', 'paper']); + + // for now the arrow can only be on the same axis or specified as pixels + // TODO: sometime it might be interesting to allow it to be on *any* axis + // but that would require updates to drawing & autorange code and maybe more + if(aaxRef !== 'pixel' && aaxRef !== axRef) { + aaxRef = annOut[arrowPosAttr] = 'pixel'; + } + + // ax, ay + var aDflt = (aaxRef === 'pixel') ? arrowPosDflt[i] : 0.4; + Axes.coercePosition(annOut, gdMock, coerce, aaxRef, arrowPosAttr, aDflt); + } + + // xanchor, yanchor + coerce(axLetter + 'anchor'); + + // xshift, yshift + coerce(axLetter + 'shift'); + } + + // if you have one coordinate you should have both + Lib.noneOrAll(annIn, annOut, ['x', 'y']); + + // if you have one part of arrow length you should have both + if(showArrow) { + Lib.noneOrAll(annIn, annOut, ['ax', 'ay']); + } + + if(clickToShow) { + var xClick = coerce('xclick'); + var yClick = coerce('yclick'); + + // put the actual click data to bind to into private attributes + // so we don't have to do this little bit of logic on every hover event + annOut._xclick = (xClick === undefined) ? + annOut.x : + Axes.cleanPosition(xClick, gdMock, annOut.xref); + annOut._yclick = (yClick === undefined) ? + annOut.y : + Axes.cleanPosition(yClick, gdMock, annOut.yref); + } +} + +},{"../../lib":285,"../../plots/array_container_defaults":326,"../../plots/cartesian/axes":331,"./attributes":138,"./common_defaults":141}],144:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var Axes = _dereq_('../../plots/cartesian/axes'); +var Color = _dereq_('../color'); +var Drawing = _dereq_('../drawing'); +var Fx = _dereq_('../fx'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var setCursor = _dereq_('../../lib/setcursor'); +var dragElement = _dereq_('../dragelement'); +var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor; + +var drawArrowHead = _dereq_('./draw_arrow_head'); + +// Annotations are stored in gd.layout.annotations, an array of objects +// index can point to one item in this array, +// or non-numeric to simply add a new one +// or -1 to modify all existing +// opt can be the full options object, or one key (to be set to value) +// or undefined to simply redraw +// if opt is blank, val can be 'add' or a full options object to add a new +// annotation at that point in the array, or 'remove' to delete this one + +module.exports = { + draw: draw, + drawOne: drawOne, + drawRaw: drawRaw +}; + +/* + * draw: draw all annotations without any new modifications + */ +function draw(gd) { + var fullLayout = gd._fullLayout; + + fullLayout._infolayer.selectAll('.annotation').remove(); + + for(var i = 0; i < fullLayout.annotations.length; i++) { + if(fullLayout.annotations[i].visible) { + drawOne(gd, i); + } + } + + return Plots.previousPromises(gd); +} + +/* + * drawOne: draw a single cartesian or paper-ref annotation, potentially with modifications + * + * index (int): the annotation to draw + */ +function drawOne(gd, index) { + var fullLayout = gd._fullLayout; + var options = fullLayout.annotations[index] || {}; + var xa = Axes.getFromId(gd, options.xref); + var ya = Axes.getFromId(gd, options.yref); + + if(xa) xa.setScale(); + if(ya) ya.setScale(); + + drawRaw(gd, options, index, false, xa, ya); +} + +// Convert pixels to the coordinates relevant for the axis referred to. For +// example, for paper it would convert to a value normalized by the dimension of +// the plot. +// axDomainRef: if true and axa defined, draws relative to axis domain, +// otherwise draws relative to data (if axa defined) or paper (if not). +function shiftPosition(axa, dAx, axLetter, gs, options) { + var optAx = options[axLetter]; + var axRef = options[axLetter + 'ref']; + var vertical = axLetter.indexOf('y') !== -1; + var axDomainRef = Axes.getRefType(axRef) === 'domain'; + var gsDim = vertical ? gs.h : gs.w; + if(axa) { + if(axDomainRef) { + // here optAx normalized to length of axis (e.g., normally in range + // 0 to 1). But dAx is in pixels. So we normalize dAx to length of + // axis before doing the math. + return optAx + (vertical ? -dAx : dAx) / axa._length; + } else { + return axa.p2r(axa.r2p(optAx) + dAx); + } + } else { + return optAx + (vertical ? -dAx : dAx) / gsDim; + } +} + +/** + * drawRaw: draw a single annotation, potentially with modifications + * + * @param {DOM element} gd + * @param {object} options : this annotation's fullLayout options + * @param {integer} index : index in 'annotations' container of the annotation to draw + * @param {string} subplotId : id of the annotation's subplot + * - use false for 2d (i.e. cartesian or paper-ref) annotations + * @param {object | undefined} xa : full x-axis object to compute subplot pos-to-px + * @param {object | undefined} ya : ... y-axis + */ +function drawRaw(gd, options, index, subplotId, xa, ya) { + var fullLayout = gd._fullLayout; + var gs = gd._fullLayout._size; + var edits = gd._context.edits; + + var className, containerStr; + + if(subplotId) { + className = 'annotation-' + subplotId; + containerStr = subplotId + '.annotations'; + } else { + className = 'annotation'; + containerStr = 'annotations'; + } + + var editHelpers = arrayEditor(gd.layout, containerStr, options); + var modifyBase = editHelpers.modifyBase; + var modifyItem = editHelpers.modifyItem; + var getUpdateObj = editHelpers.getUpdateObj; + + // remove the existing annotation if there is one + fullLayout._infolayer + .selectAll('.' + className + '[data-index="' + index + '"]') + .remove(); + + var annClipID = 'clip' + fullLayout._uid + '_ann' + index; + + // this annotation is gone - quit now after deleting it + // TODO: use d3 idioms instead of deleting and redrawing every time + if(!options._input || options.visible === false) { + d3.selectAll('#' + annClipID).remove(); + return; + } + + // calculated pixel positions + // x & y each will get text, head, and tail as appropriate + var annPosPx = {x: {}, y: {}}; + var textangle = +options.textangle || 0; + + // create the components + // made a single group to contain all, so opacity can work right + // with border/arrow together this could handle a whole bunch of + // cleanup at this point, but works for now + var annGroup = fullLayout._infolayer.append('g') + .classed(className, true) + .attr('data-index', String(index)) + .style('opacity', options.opacity); + + // another group for text+background so that they can rotate together + var annTextGroup = annGroup.append('g') + .classed('annotation-text-g', true); + + var editTextPosition = edits[options.showarrow ? 'annotationTail' : 'annotationPosition']; + var textEvents = options.captureevents || edits.annotationText || editTextPosition; + + function makeEventData(initialEvent) { + var eventData = { + index: index, + annotation: options._input, + fullAnnotation: options, + event: initialEvent + }; + if(subplotId) { + eventData.subplotId = subplotId; + } + return eventData; + } + + var annTextGroupInner = annTextGroup.append('g') + .style('pointer-events', textEvents ? 'all' : null) + .call(setCursor, 'pointer') + .on('click', function() { + gd._dragging = false; + gd.emit('plotly_clickannotation', makeEventData(d3.event)); + }); + + if(options.hovertext) { + annTextGroupInner + .on('mouseover', function() { + var hoverOptions = options.hoverlabel; + var hoverFont = hoverOptions.font; + var bBox = this.getBoundingClientRect(); + var bBoxRef = gd.getBoundingClientRect(); + + Fx.loneHover({ + x0: bBox.left - bBoxRef.left, + x1: bBox.right - bBoxRef.left, + y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top, + text: options.hovertext, + color: hoverOptions.bgcolor, + borderColor: hoverOptions.bordercolor, + fontFamily: hoverFont.family, + fontSize: hoverFont.size, + fontColor: hoverFont.color + }, { + container: fullLayout._hoverlayer.node(), + outerContainer: fullLayout._paper.node(), + gd: gd + }); + }) + .on('mouseout', function() { + Fx.loneUnhover(fullLayout._hoverlayer.node()); + }); + } + + var borderwidth = options.borderwidth; + var borderpad = options.borderpad; + var borderfull = borderwidth + borderpad; + + var annTextBG = annTextGroupInner.append('rect') + .attr('class', 'bg') + .style('stroke-width', borderwidth + 'px') + .call(Color.stroke, options.bordercolor) + .call(Color.fill, options.bgcolor); + + var isSizeConstrained = options.width || options.height; + + var annTextClip = fullLayout._topclips + .selectAll('#' + annClipID) + .data(isSizeConstrained ? [0] : []); + + annTextClip.enter().append('clipPath') + .classed('annclip', true) + .attr('id', annClipID) + .append('rect'); + annTextClip.exit().remove(); + + var font = options.font; + + var text = fullLayout._meta ? + Lib.templateString(options.text, fullLayout._meta) : + options.text; + + var annText = annTextGroupInner.append('text') + .classed('annotation-text', true) + .text(text); + + function textLayout(s) { + s.call(Drawing.font, font) + .attr({ + 'text-anchor': { + left: 'start', + right: 'end' + }[options.align] || 'middle' + }); + + svgTextUtils.convertToTspans(s, gd, drawGraphicalElements); + return s; + } + + function drawGraphicalElements() { + // if the text has *only* a link, make the whole box into a link + var anchor3 = annText.selectAll('a'); + if(anchor3.size() === 1 && anchor3.text() === annText.text()) { + var wholeLink = annTextGroupInner.insert('a', ':first-child').attr({ + 'xlink:xlink:href': anchor3.attr('xlink:href'), + 'xlink:xlink:show': anchor3.attr('xlink:show') + }) + .style({cursor: 'pointer'}); + + wholeLink.node().appendChild(annTextBG.node()); + } + + var mathjaxGroup = annTextGroupInner.select('.annotation-text-math-group'); + var hasMathjax = !mathjaxGroup.empty(); + var anntextBB = Drawing.bBox( + (hasMathjax ? mathjaxGroup : annText).node()); + var textWidth = anntextBB.width; + var textHeight = anntextBB.height; + var annWidth = options.width || textWidth; + var annHeight = options.height || textHeight; + var outerWidth = Math.round(annWidth + 2 * borderfull); + var outerHeight = Math.round(annHeight + 2 * borderfull); + + function shiftFraction(v, anchor) { + if(anchor === 'auto') { + if(v < 1 / 3) anchor = 'left'; + else if(v > 2 / 3) anchor = 'right'; + else anchor = 'center'; + } + return { + center: 0, + middle: 0, + left: 0.5, + bottom: -0.5, + right: -0.5, + top: 0.5 + }[anchor]; + } + + var annotationIsOffscreen = false; + var letters = ['x', 'y']; + + for(var i = 0; i < letters.length; i++) { + var axLetter = letters[i]; + var axRef = options[axLetter + 'ref'] || axLetter; + var tailRef = options['a' + axLetter + 'ref']; + var ax = {x: xa, y: ya}[axLetter]; + var dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180; + // note that these two can be either positive or negative + var annSizeFromWidth = outerWidth * Math.cos(dimAngle); + var annSizeFromHeight = outerHeight * Math.sin(dimAngle); + // but this one is the positive total size + var annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight); + var anchor = options[axLetter + 'anchor']; + var overallShift = options[axLetter + 'shift'] * (axLetter === 'x' ? 1 : -1); + var posPx = annPosPx[axLetter]; + var basePx; + var textPadShift; + var alignPosition; + var autoAlignFraction; + var textShift; + var axRefType = Axes.getRefType(axRef); + + /* + * calculate the *primary* pixel position + * which is the arrowhead if there is one, + * otherwise the text anchor point + */ + if(ax && (axRefType !== 'domain')) { + // check if annotation is off screen, to bypass DOM manipulations + var posFraction = ax.r2fraction(options[axLetter]); + if(posFraction < 0 || posFraction > 1) { + if(tailRef === axRef) { + posFraction = ax.r2fraction(options['a' + axLetter]); + if(posFraction < 0 || posFraction > 1) { + annotationIsOffscreen = true; + } + } else { + annotationIsOffscreen = true; + } + } + basePx = ax._offset + ax.r2p(options[axLetter]); + autoAlignFraction = 0.5; + } else { + var axRefTypeEqDomain = axRefType === 'domain'; + if(axLetter === 'x') { + alignPosition = options[axLetter]; + basePx = axRefTypeEqDomain ? + ax._offset + ax._length * alignPosition : + basePx = gs.l + gs.w * alignPosition; + } else { + alignPosition = 1 - options[axLetter]; + basePx = axRefTypeEqDomain ? + ax._offset + ax._length * alignPosition : + basePx = gs.t + gs.h * alignPosition; + } + autoAlignFraction = options.showarrow ? 0.5 : alignPosition; + } + + // now translate this into pixel positions of head, tail, and text + // as well as paddings for autorange + if(options.showarrow) { + posPx.head = basePx; + + var arrowLength = options['a' + axLetter]; + + // with an arrow, the text rotates around the anchor point + textShift = annSizeFromWidth * shiftFraction(0.5, options.xanchor) - + annSizeFromHeight * shiftFraction(0.5, options.yanchor); + + if(tailRef === axRef) { + // In the case tailRefType is 'domain' or 'paper', the arrow's + // position is set absolutely, which is consistent with how + // it behaves when its position is set in data ('range') + // coordinates. + var tailRefType = Axes.getRefType(tailRef); + if(tailRefType === 'domain') { + if(axLetter === 'y') { + arrowLength = 1 - arrowLength; + } + posPx.tail = ax._offset + ax._length * arrowLength; + } else if(tailRefType === 'paper') { + if(axLetter === 'y') { + arrowLength = 1 - arrowLength; + posPx.tail = gs.t + gs.h * arrowLength; + } else { + posPx.tail = gs.l + gs.w * arrowLength; + } + } else { + // assumed tailRef is range or paper referenced + posPx.tail = ax._offset + ax.r2p(arrowLength); + } + // tail is range- or domain-referenced: autorange pads the + // text in px from the tail + textPadShift = textShift; + } else { + posPx.tail = basePx + arrowLength; + // tail is specified in px from head, so autorange also pads vs head + textPadShift = textShift + arrowLength; + } + + posPx.text = posPx.tail + textShift; + + // constrain pixel/paper referenced so the draggers are at least + // partially visible + var maxPx = fullLayout[(axLetter === 'x') ? 'width' : 'height']; + if(axRef === 'paper') { + posPx.head = Lib.constrain(posPx.head, 1, maxPx - 1); + } + if(tailRef === 'pixel') { + var shiftPlus = -Math.max(posPx.tail - 3, posPx.text); + var shiftMinus = Math.min(posPx.tail + 3, posPx.text) - maxPx; + if(shiftPlus > 0) { + posPx.tail += shiftPlus; + posPx.text += shiftPlus; + } else if(shiftMinus > 0) { + posPx.tail -= shiftMinus; + posPx.text -= shiftMinus; + } + } + + posPx.tail += overallShift; + posPx.head += overallShift; + } else { + // with no arrow, the text rotates and *then* we put the anchor + // relative to the new bounding box + textShift = annSize * shiftFraction(autoAlignFraction, anchor); + textPadShift = textShift; + posPx.text = basePx + textShift; + } + + posPx.text += overallShift; + textShift += overallShift; + textPadShift += overallShift; + + // padplus/minus are used by autorange + options['_' + axLetter + 'padplus'] = (annSize / 2) + textPadShift; + options['_' + axLetter + 'padminus'] = (annSize / 2) - textPadShift; + + // size/shift are used during dragging + options['_' + axLetter + 'size'] = annSize; + options['_' + axLetter + 'shift'] = textShift; + } + + if(annotationIsOffscreen) { + annTextGroupInner.remove(); + return; + } + + var xShift = 0; + var yShift = 0; + + if(options.align !== 'left') { + xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1); + } + if(options.valign !== 'top') { + yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1); + } + + if(hasMathjax) { + mathjaxGroup.select('svg').attr({ + x: borderfull + xShift - 1, + y: borderfull + yShift + }) + .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd); + } else { + var texty = borderfull + yShift - anntextBB.top; + var textx = borderfull + xShift - anntextBB.left; + + annText.call(svgTextUtils.positionText, textx, texty) + .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd); + } + + annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull, + annWidth, annHeight); + + annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2, + outerWidth - borderwidth, outerHeight - borderwidth); + + annTextGroupInner.call(Drawing.setTranslate, + Math.round(annPosPx.x.text - outerWidth / 2), + Math.round(annPosPx.y.text - outerHeight / 2)); + + /* + * rotate text and background + * we already calculated the text center position *as rotated* + * because we needed that for autoranging anyway, so now whether + * we have an arrow or not, we rotate about the text center. + */ + annTextGroup.attr({transform: 'rotate(' + textangle + ',' + + annPosPx.x.text + ',' + annPosPx.y.text + ')'}); + + /* + * add the arrow + * uses options[arrowwidth,arrowcolor,arrowhead] for styling + * dx and dy are normally zero, but when you are dragging the textbox + * while the head stays put, dx and dy are the pixel offsets + */ + var drawArrow = function(dx, dy) { + annGroup + .selectAll('.annotation-arrow-g') + .remove(); + + var headX = annPosPx.x.head; + var headY = annPosPx.y.head; + var tailX = annPosPx.x.tail + dx; + var tailY = annPosPx.y.tail + dy; + var textX = annPosPx.x.text + dx; + var textY = annPosPx.y.text + dy; + + // find the edge of the text box, where we'll start the arrow: + // create transform matrix to rotate the text box corners + var transform = Lib.rotationXYMatrix(textangle, textX, textY); + var applyTransform = Lib.apply2DTransform(transform); + var applyTransform2 = Lib.apply2DTransform2(transform); + + // calculate and transform bounding box + var width = +annTextBG.attr('width'); + var height = +annTextBG.attr('height'); + var xLeft = textX - 0.5 * width; + var xRight = xLeft + width; + var yTop = textY - 0.5 * height; + var yBottom = yTop + height; + var edges = [ + [xLeft, yTop, xLeft, yBottom], + [xLeft, yBottom, xRight, yBottom], + [xRight, yBottom, xRight, yTop], + [xRight, yTop, xLeft, yTop] + ].map(applyTransform2); + + // Remove the line if it ends inside the box. Use ray + // casting for rotated boxes: see which edges intersect a + // line from the arrowhead to far away and reduce with xor + // to get the parity of the number of intersections. + if(edges.reduce(function(a, x) { + return a ^ + !!Lib.segmentsIntersect(headX, headY, headX + 1e6, headY + 1e6, + x[0], x[1], x[2], x[3]); + }, false)) { + // no line or arrow - so quit drawArrow now + return; + } + + edges.forEach(function(x) { + var p = Lib.segmentsIntersect(tailX, tailY, headX, headY, + x[0], x[1], x[2], x[3]); + if(p) { + tailX = p.x; + tailY = p.y; + } + }); + + var strokewidth = options.arrowwidth; + var arrowColor = options.arrowcolor; + var arrowSide = options.arrowside; + + var arrowGroup = annGroup.append('g') + .style({opacity: Color.opacity(arrowColor)}) + .classed('annotation-arrow-g', true); + + var arrow = arrowGroup.append('path') + .attr('d', 'M' + tailX + ',' + tailY + 'L' + headX + ',' + headY) + .style('stroke-width', strokewidth + 'px') + .call(Color.stroke, Color.rgb(arrowColor)); + + drawArrowHead(arrow, arrowSide, options); + + // the arrow dragger is a small square right at the head, then a line to the tail, + // all expanded by a stroke width of 6px plus the arrow line width + if(edits.annotationPosition && arrow.node().parentNode && !subplotId) { + var arrowDragHeadX = headX; + var arrowDragHeadY = headY; + if(options.standoff) { + var arrowLength = Math.sqrt(Math.pow(headX - tailX, 2) + Math.pow(headY - tailY, 2)); + arrowDragHeadX += options.standoff * (tailX - headX) / arrowLength; + arrowDragHeadY += options.standoff * (tailY - headY) / arrowLength; + } + var arrowDrag = arrowGroup.append('path') + .classed('annotation-arrow', true) + .classed('anndrag', true) + .classed('cursor-move', true) + .attr({ + d: 'M3,3H-3V-3H3ZM0,0L' + (tailX - arrowDragHeadX) + ',' + (tailY - arrowDragHeadY), + transform: strTranslate(arrowDragHeadX, arrowDragHeadY) + }) + .style('stroke-width', (strokewidth + 6) + 'px') + .call(Color.stroke, 'rgba(0,0,0,0)') + .call(Color.fill, 'rgba(0,0,0,0)'); + + var annx0, anny0; + + // dragger for the arrow & head: translates the whole thing + // (head/tail/text) all together + dragElement.init({ + element: arrowDrag.node(), + gd: gd, + prepFn: function() { + var pos = Drawing.getTranslate(annTextGroupInner); + + annx0 = pos.x; + anny0 = pos.y; + if(xa && xa.autorange) { + modifyBase(xa._name + '.autorange', true); + } + if(ya && ya.autorange) { + modifyBase(ya._name + '.autorange', true); + } + }, + moveFn: function(dx, dy) { + var annxy0 = applyTransform(annx0, anny0); + var xcenter = annxy0[0] + dx; + var ycenter = annxy0[1] + dy; + annTextGroupInner.call(Drawing.setTranslate, xcenter, ycenter); + + modifyItem('x', + shiftPosition(xa, dx, 'x', gs, options)); + modifyItem('y', + shiftPosition(ya, dy, 'y', gs, options)); + + // for these 2 calls to shiftPosition, it is assumed xa, ya are + // defined, so gsDim will not be used, but we put it in + // anyways for consistency + if(options.axref === options.xref) { + modifyItem('ax', shiftPosition(xa, dx, 'ax', gs, options)); + } + + if(options.ayref === options.yref) { + modifyItem('ay', shiftPosition(ya, dy, 'ay', gs, options)); + } + + arrowGroup.attr('transform', strTranslate(dx, dy)); + annTextGroup.attr({ + transform: 'rotate(' + textangle + ',' + + xcenter + ',' + ycenter + ')' + }); + }, + doneFn: function() { + Registry.call('_guiRelayout', gd, getUpdateObj()); + var notesBox = document.querySelector('.js-notes-box-panel'); + if(notesBox) notesBox.redraw(notesBox.selectedObj); + } + }); + } + }; + + if(options.showarrow) drawArrow(0, 0); + + // user dragging the annotation (text, not arrow) + if(editTextPosition) { + var baseTextTransform; + + // dragger for the textbox: if there's an arrow, just drag the + // textbox and tail, leave the head untouched + dragElement.init({ + element: annTextGroupInner.node(), + gd: gd, + prepFn: function() { + baseTextTransform = annTextGroup.attr('transform'); + }, + moveFn: function(dx, dy) { + var csr = 'pointer'; + if(options.showarrow) { + // for these 2 calls to shiftPosition, it is assumed xa, ya are + // defined, so gsDim will not be used, but we put it in + // anyways for consistency + if(options.axref === options.xref) { + modifyItem('ax', shiftPosition(xa, dx, 'ax', gs, options)); + } else { + modifyItem('ax', options.ax + dx); + } + + if(options.ayref === options.yref) { + modifyItem('ay', shiftPosition(ya, dy, 'ay', gs.w, options)); + } else { + modifyItem('ay', options.ay + dy); + } + + drawArrow(dx, dy); + } else if(!subplotId) { + var xUpdate, yUpdate; + if(xa) { + // shiftPosition will not execute code where xa was + // undefined, so we use to calculate xUpdate too + xUpdate = shiftPosition(xa, dx, 'x', gs, options); + } else { + var widthFraction = options._xsize / gs.w; + var xLeft = options.x + (options._xshift - options.xshift) / gs.w - widthFraction / 2; + + xUpdate = dragElement.align(xLeft + dx / gs.w, + widthFraction, 0, 1, options.xanchor); + } + + if(ya) { + // shiftPosition will not execute code where ya was + // undefined, so we use to calculate yUpdate too + yUpdate = shiftPosition(ya, dy, 'y', gs, options); + } else { + var heightFraction = options._ysize / gs.h; + var yBottom = options.y - (options._yshift + options.yshift) / gs.h - heightFraction / 2; + + yUpdate = dragElement.align(yBottom - dy / gs.h, + heightFraction, 0, 1, options.yanchor); + } + modifyItem('x', xUpdate); + modifyItem('y', yUpdate); + if(!xa || !ya) { + csr = dragElement.getCursor( + xa ? 0.5 : xUpdate, + ya ? 0.5 : yUpdate, + options.xanchor, options.yanchor + ); + } + } else return; + + annTextGroup.attr({ + transform: strTranslate(dx, dy) + baseTextTransform + }); + + setCursor(annTextGroupInner, csr); + }, + clickFn: function(_, initialEvent) { + if(options.captureevents) { + gd.emit('plotly_clickannotation', makeEventData(initialEvent)); + } + }, + doneFn: function() { + setCursor(annTextGroupInner); + Registry.call('_guiRelayout', gd, getUpdateObj()); + var notesBox = document.querySelector('.js-notes-box-panel'); + if(notesBox) notesBox.redraw(notesBox.selectedObj); + } + }); + } + } + + if(edits.annotationText) { + annText.call(svgTextUtils.makeEditable, {delegate: annTextGroupInner, gd: gd}) + .call(textLayout) + .on('edit', function(_text) { + options.text = _text; + + this.call(textLayout); + + modifyItem('text', _text); + + if(xa && xa.autorange) { + modifyBase(xa._name + '.autorange', true); + } + if(ya && ya.autorange) { + modifyBase(ya._name + '.autorange', true); + } + + Registry.call('_guiRelayout', gd, getUpdateObj()); + }); + } else annText.call(textLayout); +} + +},{"../../lib":285,"../../lib/setcursor":305,"../../lib/svg_text_utils":307,"../../plot_api/plot_template":320,"../../plots/cartesian/axes":331,"../../plots/plots":366,"../../registry":373,"../color":155,"../dragelement":174,"../drawing":177,"../fx":195,"./draw_arrow_head":145,"@plotly/d3":20}],145:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Color = _dereq_('../color'); + +var ARROWPATHS = _dereq_('./arrow_paths'); + +var Lib = _dereq_('../../lib'); +var strScale = Lib.strScale; +var strRotate = Lib.strRotate; +var strTranslate = Lib.strTranslate; + +/** + * Add arrowhead(s) to a path or line element + * + * @param {d3.selection} el3: a d3-selected line or path element + * + * @param {string} ends: 'none', 'start', 'end', or 'start+end' for which ends get arrowheads + * + * @param {object} options: style information. Must have all the following: + * @param {number} options.arrowhead: end head style - see ./arrow_paths + * @param {number} options.startarrowhead: start head style - see ./arrow_paths + * @param {number} options.arrowsize: relative size of the end head vs line width + * @param {number} options.startarrowsize: relative size of the start head vs line width + * @param {number} options.standoff: distance in px to move the end arrow point from its target + * @param {number} options.startstandoff: distance in px to move the start arrow point from its target + * @param {number} options.arrowwidth: width of the arrow line + * @param {string} options.arrowcolor: color of the arrow line, for the head to match + * Note that the opacity of this color is ignored, as it's assumed the container + * of both the line and head has opacity applied to it so there isn't greater opacity + * where they overlap. + */ +module.exports = function drawArrowHead(el3, ends, options) { + var el = el3.node(); + var headStyle = ARROWPATHS[options.arrowhead || 0]; + var startHeadStyle = ARROWPATHS[options.startarrowhead || 0]; + var scale = (options.arrowwidth || 1) * (options.arrowsize || 1); + var startScale = (options.arrowwidth || 1) * (options.startarrowsize || 1); + var doStart = ends.indexOf('start') >= 0; + var doEnd = ends.indexOf('end') >= 0; + var backOff = headStyle.backoff * scale + options.standoff; + var startBackOff = startHeadStyle.backoff * startScale + options.startstandoff; + + var start, end, startRot, endRot; + + if(el.nodeName === 'line') { + start = {x: +el3.attr('x1'), y: +el3.attr('y1')}; + end = {x: +el3.attr('x2'), y: +el3.attr('y2')}; + + var dx = start.x - end.x; + var dy = start.y - end.y; + + startRot = Math.atan2(dy, dx); + endRot = startRot + Math.PI; + if(backOff && startBackOff) { + if(backOff + startBackOff > Math.sqrt(dx * dx + dy * dy)) { + hideLine(); + return; + } + } + + if(backOff) { + if(backOff * backOff > dx * dx + dy * dy) { + hideLine(); + return; + } + var backOffX = backOff * Math.cos(startRot); + var backOffY = backOff * Math.sin(startRot); + + end.x += backOffX; + end.y += backOffY; + el3.attr({x2: end.x, y2: end.y}); + } + + if(startBackOff) { + if(startBackOff * startBackOff > dx * dx + dy * dy) { + hideLine(); + return; + } + var startBackOffX = startBackOff * Math.cos(startRot); + var startbackOffY = startBackOff * Math.sin(startRot); + + start.x -= startBackOffX; + start.y -= startbackOffY; + el3.attr({x1: start.x, y1: start.y}); + } + } else if(el.nodeName === 'path') { + var pathlen = el.getTotalLength(); + // using dash to hide the backOff region of the path. + // if we ever allow dash for the arrow we'll have to + // do better than this hack... maybe just manually + // combine the two + var dashArray = ''; + + if(pathlen < backOff + startBackOff) { + hideLine(); + return; + } + + + var start0 = el.getPointAtLength(0); + var dstart = el.getPointAtLength(0.1); + + startRot = Math.atan2(start0.y - dstart.y, start0.x - dstart.x); + start = el.getPointAtLength(Math.min(startBackOff, pathlen)); + + dashArray = '0px,' + startBackOff + 'px,'; + + var end0 = el.getPointAtLength(pathlen); + var dend = el.getPointAtLength(pathlen - 0.1); + + endRot = Math.atan2(end0.y - dend.y, end0.x - dend.x); + end = el.getPointAtLength(Math.max(0, pathlen - backOff)); + + var shortening = dashArray ? startBackOff + backOff : backOff; + dashArray += (pathlen - shortening) + 'px,' + pathlen + 'px'; + + el3.style('stroke-dasharray', dashArray); + } + + function hideLine() { el3.style('stroke-dasharray', '0px,100px'); } + + function drawhead(arrowHeadStyle, p, rot, arrowScale) { + if(!arrowHeadStyle.path) return; + if(arrowHeadStyle.noRotate) rot = 0; + + d3.select(el.parentNode).append('path') + .attr({ + 'class': el3.attr('class'), + d: arrowHeadStyle.path, + transform: + strTranslate(p.x, p.y) + + strRotate(rot * 180 / Math.PI) + + strScale(arrowScale) + }) + .style({ + fill: Color.rgb(options.arrowcolor), + 'stroke-width': 0 + }); + } + + if(doStart) drawhead(startHeadStyle, start, startRot, startScale); + if(doEnd) drawhead(headStyle, end, endRot, scale); +}; + +},{"../../lib":285,"../color":155,"./arrow_paths":137,"@plotly/d3":20}],146:[function(_dereq_,module,exports){ +'use strict'; + +var drawModule = _dereq_('./draw'); +var clickModule = _dereq_('./click'); + +module.exports = { + moduleType: 'component', + name: 'annotations', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + includeBasePlot: _dereq_('../../plots/cartesian/include_components')('annotations'), + + calcAutorange: _dereq_('./calc_autorange'), + draw: drawModule.draw, + drawOne: drawModule.drawOne, + drawRaw: drawModule.drawRaw, + + hasClickToShow: clickModule.hasClickToShow, + onClick: clickModule.onClick, + + convertCoords: _dereq_('./convert_coords') +}; + +},{"../../plots/cartesian/include_components":344,"./attributes":138,"./calc_autorange":139,"./click":140,"./convert_coords":142,"./defaults":143,"./draw":144}],147:[function(_dereq_,module,exports){ +'use strict'; + +var annAttrs = _dereq_('../annotations/attributes'); +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; + +module.exports = overrideAll(templatedArray('annotation', { + visible: annAttrs.visible, + x: { + valType: 'any', + }, + y: { + valType: 'any', + }, + z: { + valType: 'any', + }, + ax: { + valType: 'number', + }, + ay: { + valType: 'number', + }, + + xanchor: annAttrs.xanchor, + xshift: annAttrs.xshift, + yanchor: annAttrs.yanchor, + yshift: annAttrs.yshift, + + text: annAttrs.text, + textangle: annAttrs.textangle, + font: annAttrs.font, + width: annAttrs.width, + height: annAttrs.height, + opacity: annAttrs.opacity, + align: annAttrs.align, + valign: annAttrs.valign, + bgcolor: annAttrs.bgcolor, + bordercolor: annAttrs.bordercolor, + borderpad: annAttrs.borderpad, + borderwidth: annAttrs.borderwidth, + showarrow: annAttrs.showarrow, + arrowcolor: annAttrs.arrowcolor, + arrowhead: annAttrs.arrowhead, + startarrowhead: annAttrs.startarrowhead, + arrowside: annAttrs.arrowside, + arrowsize: annAttrs.arrowsize, + startarrowsize: annAttrs.startarrowsize, + arrowwidth: annAttrs.arrowwidth, + standoff: annAttrs.standoff, + startstandoff: annAttrs.startstandoff, + hovertext: annAttrs.hovertext, + hoverlabel: annAttrs.hoverlabel, + captureevents: annAttrs.captureevents, + + // maybes later? + // clicktoshow: annAttrs.clicktoshow, + // xclick: annAttrs.xclick, + // yclick: annAttrs.yclick, + + // not needed! + // axref: 'pixel' + // ayref: 'pixel' + // xref: 'x' + // yref: 'y + // zref: 'z' +}), 'calc', 'from-root'); + +},{"../../plot_api/edit_types":313,"../../plot_api/plot_template":320,"../annotations/attributes":138}],148:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function convert(scene) { + var fullSceneLayout = scene.fullSceneLayout; + var anns = fullSceneLayout.annotations; + + for(var i = 0; i < anns.length; i++) { + mockAnnAxes(anns[i], scene); + } + + scene.fullLayout._infolayer + .selectAll('.annotation-' + scene.id) + .remove(); +}; + +function mockAnnAxes(ann, scene) { + var fullSceneLayout = scene.fullSceneLayout; + var domain = fullSceneLayout.domain; + var size = scene.fullLayout._size; + + var base = { + // this gets fill in on render + pdata: null, + + // to get setConvert to not execute cleanly + type: 'linear', + + // don't try to update them on `editable: true` + autorange: false, + + // set infinite range so that annotation draw routine + // does not try to remove 'outside-range' annotations, + // this case is handled in the render loop + range: [-Infinity, Infinity] + }; + + ann._xa = {}; + Lib.extendFlat(ann._xa, base); + Axes.setConvert(ann._xa); + ann._xa._offset = size.l + domain.x[0] * size.w; + ann._xa.l2p = function() { + return 0.5 * (1 + ann._pdata[0] / ann._pdata[3]) * size.w * (domain.x[1] - domain.x[0]); + }; + + ann._ya = {}; + Lib.extendFlat(ann._ya, base); + Axes.setConvert(ann._ya); + ann._ya._offset = size.t + (1 - domain.y[1]) * size.h; + ann._ya.l2p = function() { + return 0.5 * (1 - ann._pdata[1] / ann._pdata[3]) * size.h * (domain.y[1] - domain.y[0]); + }; +} + +},{"../../lib":285,"../../plots/cartesian/axes":331}],149:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); +var handleAnnotationCommonDefaults = _dereq_('../annotations/common_defaults'); +var attributes = _dereq_('./attributes'); + +module.exports = function handleDefaults(sceneLayoutIn, sceneLayoutOut, opts) { + handleArrayContainerDefaults(sceneLayoutIn, sceneLayoutOut, { + name: 'annotations', + handleItemDefaults: handleAnnotationDefaults, + fullLayout: opts.fullLayout + }); +}; + +function handleAnnotationDefaults(annIn, annOut, sceneLayout, opts) { + function coerce(attr, dflt) { + return Lib.coerce(annIn, annOut, attributes, attr, dflt); + } + + function coercePosition(axLetter) { + var axName = axLetter + 'axis'; + + // mock in such way that getFromId grabs correct 3D axis + var gdMock = { _fullLayout: {} }; + gdMock._fullLayout[axName] = sceneLayout[axName]; + + return Axes.coercePosition(annOut, gdMock, coerce, axLetter, axLetter, 0.5); + } + + + var visible = coerce('visible'); + if(!visible) return; + + handleAnnotationCommonDefaults(annIn, annOut, opts.fullLayout, coerce); + + coercePosition('x'); + coercePosition('y'); + coercePosition('z'); + + // if you have one coordinate you should all three + Lib.noneOrAll(annIn, annOut, ['x', 'y', 'z']); + + // hard-set here for completeness + annOut.xref = 'x'; + annOut.yref = 'y'; + annOut.zref = 'z'; + + coerce('xanchor'); + coerce('yanchor'); + coerce('xshift'); + coerce('yshift'); + + if(annOut.showarrow) { + annOut.axref = 'pixel'; + annOut.ayref = 'pixel'; + + // TODO maybe default values should be bigger than the 2D case? + coerce('ax', -10); + coerce('ay', -30); + + // if you have one part of arrow length you should have both + Lib.noneOrAll(annIn, annOut, ['ax', 'ay']); + } +} + +},{"../../lib":285,"../../plots/array_container_defaults":326,"../../plots/cartesian/axes":331,"../annotations/common_defaults":141,"./attributes":147}],150:[function(_dereq_,module,exports){ +'use strict'; + +var drawRaw = _dereq_('../annotations/draw').drawRaw; +var project = _dereq_('../../plots/gl3d/project'); +var axLetters = ['x', 'y', 'z']; + +module.exports = function draw(scene) { + var fullSceneLayout = scene.fullSceneLayout; + var dataScale = scene.dataScale; + var anns = fullSceneLayout.annotations; + + for(var i = 0; i < anns.length; i++) { + var ann = anns[i]; + var annotationIsOffscreen = false; + + for(var j = 0; j < 3; j++) { + var axLetter = axLetters[j]; + var pos = ann[axLetter]; + var ax = fullSceneLayout[axLetter + 'axis']; + var posFraction = ax.r2fraction(pos); + + if(posFraction < 0 || posFraction > 1) { + annotationIsOffscreen = true; + break; + } + } + + if(annotationIsOffscreen) { + scene.fullLayout._infolayer + .select('.annotation-' + scene.id + '[data-index="' + i + '"]') + .remove(); + } else { + ann._pdata = project(scene.glplot.cameraParams, [ + fullSceneLayout.xaxis.r2l(ann.x) * dataScale[0], + fullSceneLayout.yaxis.r2l(ann.y) * dataScale[1], + fullSceneLayout.zaxis.r2l(ann.z) * dataScale[2] + ]); + + drawRaw(scene.graphDiv, ann, i, scene.id, ann._xa, ann._ya); + } + } +}; + +},{"../../plots/gl3d/project":363,"../annotations/draw":144}],151:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); + +module.exports = { + moduleType: 'component', + name: 'annotations3d', + + schema: { + subplots: { + scene: {annotations: _dereq_('./attributes')} + } + }, + + layoutAttributes: _dereq_('./attributes'), + handleDefaults: _dereq_('./defaults'), + includeBasePlot: includeGL3D, + + convert: _dereq_('./convert'), + draw: _dereq_('./draw') +}; + +function includeGL3D(layoutIn, layoutOut) { + var GL3D = Registry.subplotsRegistry.gl3d; + if(!GL3D) return; + + var attrRegex = GL3D.attrRegex; + + var keys = Object.keys(layoutIn); + for(var i = 0; i < keys.length; i++) { + var k = keys[i]; + if(attrRegex.test(k) && (layoutIn[k].annotations || []).length) { + Lib.pushUnique(layoutOut._basePlotModules, GL3D); + Lib.pushUnique(layoutOut._subplots.gl3d, k); + } + } +} + +},{"../../lib":285,"../../registry":373,"./attributes":147,"./convert":148,"./defaults":149,"./draw":150}],152:[function(_dereq_,module,exports){ +'use strict'; + +// a trimmed down version of: +// https://github.com/alexcjohnson/world-calendars/blob/master/dist/index.js + +module.exports = _dereq_('world-calendars/dist/main'); + +_dereq_('world-calendars/dist/plus'); + +_dereq_('world-calendars/dist/calendars/chinese'); +_dereq_('world-calendars/dist/calendars/coptic'); +_dereq_('world-calendars/dist/calendars/discworld'); +_dereq_('world-calendars/dist/calendars/ethiopian'); +_dereq_('world-calendars/dist/calendars/hebrew'); +_dereq_('world-calendars/dist/calendars/islamic'); +_dereq_('world-calendars/dist/calendars/julian'); +_dereq_('world-calendars/dist/calendars/mayan'); +_dereq_('world-calendars/dist/calendars/nanakshahi'); +_dereq_('world-calendars/dist/calendars/nepali'); +_dereq_('world-calendars/dist/calendars/persian'); +_dereq_('world-calendars/dist/calendars/taiwan'); +_dereq_('world-calendars/dist/calendars/thai'); +_dereq_('world-calendars/dist/calendars/ummalqura'); + +},{"world-calendars/dist/calendars/chinese":121,"world-calendars/dist/calendars/coptic":122,"world-calendars/dist/calendars/discworld":123,"world-calendars/dist/calendars/ethiopian":124,"world-calendars/dist/calendars/hebrew":125,"world-calendars/dist/calendars/islamic":126,"world-calendars/dist/calendars/julian":127,"world-calendars/dist/calendars/mayan":128,"world-calendars/dist/calendars/nanakshahi":129,"world-calendars/dist/calendars/nepali":130,"world-calendars/dist/calendars/persian":131,"world-calendars/dist/calendars/taiwan":132,"world-calendars/dist/calendars/thai":133,"world-calendars/dist/calendars/ummalqura":134,"world-calendars/dist/main":135,"world-calendars/dist/plus":136}],153:[function(_dereq_,module,exports){ +'use strict'; + +var calendars = _dereq_('./calendars'); + +var Lib = _dereq_('../../lib'); +var constants = _dereq_('../../constants/numerical'); + +var EPOCHJD = constants.EPOCHJD; +var ONEDAY = constants.ONEDAY; + +var attributes = { + valType: 'enumerated', + values: Object.keys(calendars.calendars), + editType: 'calc', + dflt: 'gregorian' +}; + +var handleDefaults = function(contIn, contOut, attr, dflt) { + var attrs = {}; + attrs[attr] = attributes; + + return Lib.coerce(contIn, contOut, attrs, attr, dflt); +}; + +var handleTraceDefaults = function(traceIn, traceOut, coords, layout) { + for(var i = 0; i < coords.length; i++) { + handleDefaults(traceIn, traceOut, coords[i] + 'calendar', layout.calendar); + } +}; + +// each calendar needs its own default canonical tick. I would love to use +// 2000-01-01 (or even 0000-01-01) for them all but they don't necessarily +// all support either of those dates. Instead I'll use the most significant +// number they *do* support, biased toward the present day. +var CANONICAL_TICK = { + chinese: '2000-01-01', + coptic: '2000-01-01', + discworld: '2000-01-01', + ethiopian: '2000-01-01', + hebrew: '5000-01-01', + islamic: '1000-01-01', + julian: '2000-01-01', + mayan: '5000-01-01', + nanakshahi: '1000-01-01', + nepali: '2000-01-01', + persian: '1000-01-01', + jalali: '1000-01-01', + taiwan: '1000-01-01', + thai: '2000-01-01', + ummalqura: '1400-01-01' +}; + +// Start on a Sunday - for week ticks +// Discworld and Mayan calendars don't have 7-day weeks but we're going to give them +// 7-day week ticks so start on our Sundays. +// If anyone really cares we can customize the auto tick spacings for these calendars. +var CANONICAL_SUNDAY = { + chinese: '2000-01-02', + coptic: '2000-01-03', + discworld: '2000-01-03', + ethiopian: '2000-01-05', + hebrew: '5000-01-01', + islamic: '1000-01-02', + julian: '2000-01-03', + mayan: '5000-01-01', + nanakshahi: '1000-01-05', + nepali: '2000-01-05', + persian: '1000-01-01', + jalali: '1000-01-01', + taiwan: '1000-01-04', + thai: '2000-01-04', + ummalqura: '1400-01-06' +}; + +var DFLTRANGE = { + chinese: ['2000-01-01', '2001-01-01'], + coptic: ['1700-01-01', '1701-01-01'], + discworld: ['1800-01-01', '1801-01-01'], + ethiopian: ['2000-01-01', '2001-01-01'], + hebrew: ['5700-01-01', '5701-01-01'], + islamic: ['1400-01-01', '1401-01-01'], + julian: ['2000-01-01', '2001-01-01'], + mayan: ['5200-01-01', '5201-01-01'], + nanakshahi: ['0500-01-01', '0501-01-01'], + nepali: ['2000-01-01', '2001-01-01'], + persian: ['1400-01-01', '1401-01-01'], + jalali: ['1400-01-01', '1401-01-01'], + taiwan: ['0100-01-01', '0101-01-01'], + thai: ['2500-01-01', '2501-01-01'], + ummalqura: ['1400-01-01', '1401-01-01'] +}; + +/* + * convert d3 templates to world-calendars templates, so our users only need + * to know d3's specifiers. Map space padding to no padding, and unknown fields + * to an ugly placeholder + */ +var UNKNOWN = '##'; +var d3ToWorldCalendars = { + 'd': {'0': 'dd', '-': 'd'}, // 2-digit or unpadded day of month + 'e': {'0': 'd', '-': 'd'}, // alternate, always unpadded day of month + 'a': {'0': 'D', '-': 'D'}, // short weekday name + 'A': {'0': 'DD', '-': 'DD'}, // full weekday name + 'j': {'0': 'oo', '-': 'o'}, // 3-digit or unpadded day of the year + 'W': {'0': 'ww', '-': 'w'}, // 2-digit or unpadded week of the year (Monday first) + 'm': {'0': 'mm', '-': 'm'}, // 2-digit or unpadded month number + 'b': {'0': 'M', '-': 'M'}, // short month name + 'B': {'0': 'MM', '-': 'MM'}, // full month name + 'y': {'0': 'yy', '-': 'yy'}, // 2-digit year (map unpadded to zero-padded) + 'Y': {'0': 'yyyy', '-': 'yyyy'}, // 4-digit year (map unpadded to zero-padded) + 'U': UNKNOWN, // Sunday-first week of the year + 'w': UNKNOWN, // day of the week [0(sunday),6] + // combined format, we replace the date part with the world-calendar version + // and the %X stays there for d3 to handle with time parts + 'c': {'0': 'D M d %X yyyy', '-': 'D M d %X yyyy'}, + 'x': {'0': 'mm/dd/yyyy', '-': 'mm/dd/yyyy'} +}; + +function worldCalFmt(fmt, x, calendar) { + var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD; + var cDate = getCal(calendar).fromJD(dateJD); + var i = 0; + var modifier, directive, directiveLen, directiveObj, replacementPart; + + while((i = fmt.indexOf('%', i)) !== -1) { + modifier = fmt.charAt(i + 1); + if(modifier === '0' || modifier === '-' || modifier === '_') { + directiveLen = 3; + directive = fmt.charAt(i + 2); + if(modifier === '_') modifier = '-'; + } else { + directive = modifier; + modifier = '0'; + directiveLen = 2; + } + directiveObj = d3ToWorldCalendars[directive]; + if(!directiveObj) { + i += directiveLen; + } else { + // code is recognized as a date part but world-calendars doesn't support it + if(directiveObj === UNKNOWN) replacementPart = UNKNOWN; + + // format the cDate according to the translated directive + else replacementPart = cDate.formatDate(directiveObj[modifier]); + + fmt = fmt.substr(0, i) + replacementPart + fmt.substr(i + directiveLen); + i += replacementPart.length; + } + } + return fmt; +} + +// cache world calendars, so we don't have to reinstantiate +// during each date-time conversion +var allCals = {}; +function getCal(calendar) { + var calendarObj = allCals[calendar]; + if(calendarObj) return calendarObj; + + calendarObj = allCals[calendar] = calendars.instance(calendar); + return calendarObj; +} + +function makeAttrs(description) { + return Lib.extendFlat({}, attributes, { description: description }); +} + +function makeTraceAttrsDescription(coord) { + return 'Sets the calendar system to use with `' + coord + '` date data.'; +} + +var xAttrs = { + xcalendar: makeAttrs(makeTraceAttrsDescription('x')) +}; + +var xyAttrs = Lib.extendFlat({}, xAttrs, { + ycalendar: makeAttrs(makeTraceAttrsDescription('y')) +}); + +var xyzAttrs = Lib.extendFlat({}, xyAttrs, { + zcalendar: makeAttrs(makeTraceAttrsDescription('z')) +}); + +var axisAttrs = makeAttrs([ + 'Sets the calendar system to use for `range` and `tick0`', + 'if this is a date axis. This does not set the calendar for', + 'interpreting data on this axis, that\'s specified in the trace', + 'or via the global `layout.calendar`' +].join(' ')); + +module.exports = { + moduleType: 'component', + name: 'calendars', + + schema: { + traces: { + scatter: xyAttrs, + bar: xyAttrs, + box: xyAttrs, + heatmap: xyAttrs, + contour: xyAttrs, + histogram: xyAttrs, + histogram2d: xyAttrs, + histogram2dcontour: xyAttrs, + scatter3d: xyzAttrs, + surface: xyzAttrs, + mesh3d: xyzAttrs, + scattergl: xyAttrs, + ohlc: xAttrs, + candlestick: xAttrs + }, + layout: { + calendar: makeAttrs([ + 'Sets the default calendar system to use for interpreting and', + 'displaying dates throughout the plot.' + ].join(' ')) + }, + subplots: { + xaxis: {calendar: axisAttrs}, + yaxis: {calendar: axisAttrs}, + scene: { + xaxis: {calendar: axisAttrs}, + // TODO: it's actually redundant to include yaxis and zaxis here + // because in the scene attributes these are the same object so merging + // into one merges into them all. However, I left them in for parity with + // cartesian, where yaxis is unused until we Plotschema.get() when we + // use its presence or absence to determine whether to delete attributes + // from yaxis if they only apply to x (rangeselector/rangeslider) + yaxis: {calendar: axisAttrs}, + zaxis: {calendar: axisAttrs} + }, + polar: { + radialaxis: {calendar: axisAttrs} + } + }, + transforms: { + filter: { + valuecalendar: makeAttrs([ + 'WARNING: All transforms are deprecated and may be removed from the API in next major version.', + 'Sets the calendar system to use for `value`, if it is a date.' + ].join(' ')), + targetcalendar: makeAttrs([ + 'WARNING: All transforms are deprecated and may be removed from the API in next major version.', + 'Sets the calendar system to use for `target`, if it is an', + 'array of dates. If `target` is a string (eg *x*) we use the', + 'corresponding trace attribute (eg `xcalendar`) if it exists,', + 'even if `targetcalendar` is provided.' + ].join(' ')) + } + } + }, + + layoutAttributes: attributes, + + handleDefaults: handleDefaults, + handleTraceDefaults: handleTraceDefaults, + + CANONICAL_SUNDAY: CANONICAL_SUNDAY, + CANONICAL_TICK: CANONICAL_TICK, + DFLTRANGE: DFLTRANGE, + + getCal: getCal, + worldCalFmt: worldCalFmt +}; + +},{"../../constants/numerical":265,"../../lib":285,"./calendars":152}],154:[function(_dereq_,module,exports){ +'use strict'; + + +// IMPORTANT - default colors should be in hex for compatibility +exports.defaults = [ + '#1f77b4', // muted blue + '#ff7f0e', // safety orange + '#2ca02c', // cooked asparagus green + '#d62728', // brick red + '#9467bd', // muted purple + '#8c564b', // chestnut brown + '#e377c2', // raspberry yogurt pink + '#7f7f7f', // middle gray + '#bcbd22', // curry yellow-green + '#17becf' // blue-teal +]; + +exports.defaultLine = '#444'; + +exports.lightLine = '#eee'; + +exports.background = '#fff'; + +exports.borderLine = '#BEC8D9'; + +// with axis.color and Color.interp we aren't using lightLine +// itself anymore, instead interpolating between axis.color +// and the background color using tinycolor.mix. lightFraction +// gives back exactly lightLine if the other colors are defaults. +exports.lightFraction = 100 * (0xe - 0x4) / (0xf - 0x4); + +},{}],155:[function(_dereq_,module,exports){ +'use strict'; + +var tinycolor = _dereq_('tinycolor2'); +var isNumeric = _dereq_('fast-isnumeric'); +var isTypedArray = _dereq_('../../lib/array').isTypedArray; + +var color = module.exports = {}; + +var colorAttrs = _dereq_('./attributes'); +color.defaults = colorAttrs.defaults; +var defaultLine = color.defaultLine = colorAttrs.defaultLine; +color.lightLine = colorAttrs.lightLine; +var background = color.background = colorAttrs.background; + +/* + * tinyRGB: turn a tinycolor into an rgb string, but + * unlike the built-in tinycolor.toRgbString this never includes alpha + */ +color.tinyRGB = function(tc) { + var c = tc.toRgb(); + return 'rgb(' + Math.round(c.r) + ', ' + + Math.round(c.g) + ', ' + Math.round(c.b) + ')'; +}; + +color.rgb = function(cstr) { return color.tinyRGB(tinycolor(cstr)); }; + +color.opacity = function(cstr) { return cstr ? tinycolor(cstr).getAlpha() : 0; }; + +color.addOpacity = function(cstr, op) { + var c = tinycolor(cstr).toRgb(); + return 'rgba(' + Math.round(c.r) + ', ' + + Math.round(c.g) + ', ' + Math.round(c.b) + ', ' + op + ')'; +}; + +// combine two colors into one apparent color +// if back has transparency or is missing, +// color.background is assumed behind it +color.combine = function(front, back) { + var fc = tinycolor(front).toRgb(); + if(fc.a === 1) return tinycolor(front).toRgbString(); + + var bc = tinycolor(back || background).toRgb(); + var bcflat = bc.a === 1 ? bc : { + r: 255 * (1 - bc.a) + bc.r * bc.a, + g: 255 * (1 - bc.a) + bc.g * bc.a, + b: 255 * (1 - bc.a) + bc.b * bc.a + }; + var fcflat = { + r: bcflat.r * (1 - fc.a) + fc.r * fc.a, + g: bcflat.g * (1 - fc.a) + fc.g * fc.a, + b: bcflat.b * (1 - fc.a) + fc.b * fc.a + }; + return tinycolor(fcflat).toRgbString(); +}; + +/* + * Create a color that contrasts with cstr. + * + * If cstr is a dark color, we lighten it; if it's light, we darken. + * + * If lightAmount / darkAmount are used, we adjust by these percentages, + * otherwise we go all the way to white or black. + */ +color.contrast = function(cstr, lightAmount, darkAmount) { + var tc = tinycolor(cstr); + + if(tc.getAlpha() !== 1) tc = tinycolor(color.combine(cstr, background)); + + var newColor = tc.isDark() ? + (lightAmount ? tc.lighten(lightAmount) : background) : + (darkAmount ? tc.darken(darkAmount) : defaultLine); + + return newColor.toString(); +}; + +color.stroke = function(s, c) { + var tc = tinycolor(c); + s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()}); +}; + +color.fill = function(s, c) { + var tc = tinycolor(c); + s.style({ + 'fill': color.tinyRGB(tc), + 'fill-opacity': tc.getAlpha() + }); +}; + +// search container for colors with the deprecated rgb(fractions) format +// and convert them to rgb(0-255 values) +color.clean = function(container) { + if(!container || typeof container !== 'object') return; + + var keys = Object.keys(container); + var i, j, key, val; + + for(i = 0; i < keys.length; i++) { + key = keys[i]; + val = container[key]; + + if(key.substr(key.length - 5) === 'color') { + // only sanitize keys that end in "color" or "colorscale" + + if(Array.isArray(val)) { + for(j = 0; j < val.length; j++) val[j] = cleanOne(val[j]); + } else container[key] = cleanOne(val); + } else if(key.substr(key.length - 10) === 'colorscale' && Array.isArray(val)) { + // colorscales have the format [[0, color1], [frac, color2], ... [1, colorN]] + + for(j = 0; j < val.length; j++) { + if(Array.isArray(val[j])) val[j][1] = cleanOne(val[j][1]); + } + } else if(Array.isArray(val)) { + // recurse into arrays of objects, and plain objects + + var el0 = val[0]; + if(!Array.isArray(el0) && el0 && typeof el0 === 'object') { + for(j = 0; j < val.length; j++) color.clean(val[j]); + } + } else if(val && typeof val === 'object' && !isTypedArray(val)) color.clean(val); + } +}; + +function cleanOne(val) { + if(isNumeric(val) || typeof val !== 'string') return val; + + var valTrim = val.trim(); + if(valTrim.substr(0, 3) !== 'rgb') return val; + + var match = valTrim.match(/^rgba?\s*\(([^()]*)\)$/); + if(!match) return val; + + var parts = match[1].trim().split(/\s*[\s,]\s*/); + var rgba = valTrim.charAt(3) === 'a' && parts.length === 4; + if(!rgba && parts.length !== 3) return val; + + for(var i = 0; i < parts.length; i++) { + if(!parts[i].length) return val; + parts[i] = Number(parts[i]); + + if(!(parts[i] >= 0)) { + // all parts must be non-negative numbers + + return val; + } + + if(i === 3) { + // alpha>1 gets clipped to 1 + + if(parts[i] > 1) parts[i] = 1; + } else if(parts[i] >= 1) { + // r, g, b must be < 1 (ie 1 itself is not allowed) + + return val; + } + } + + var rgbStr = Math.round(parts[0] * 255) + ', ' + + Math.round(parts[1] * 255) + ', ' + + Math.round(parts[2] * 255); + + if(rgba) return 'rgba(' + rgbStr + ', ' + parts[3] + ')'; + return 'rgb(' + rgbStr + ')'; +} + +},{"../../lib/array":271,"./attributes":154,"fast-isnumeric":31,"tinycolor2":119}],156:[function(_dereq_,module,exports){ +'use strict'; + +var axesAttrs = _dereq_('../../plots/cartesian/layout_attributes'); +var fontAttrs = _dereq_('../../plots/font_attributes'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; + + +module.exports = overrideAll({ +// TODO: only right is supported currently +// orient: { +// valType: 'enumerated', +// values: ['left', 'right', 'top', 'bottom'], +// dflt: 'right', +// +// }, + thicknessmode: { + valType: 'enumerated', + values: ['fraction', 'pixels'], + dflt: 'pixels', + }, + thickness: { + valType: 'number', + min: 0, + dflt: 30, + }, + lenmode: { + valType: 'enumerated', + values: ['fraction', 'pixels'], + dflt: 'fraction', + }, + len: { + valType: 'number', + min: 0, + dflt: 1, + }, + x: { + valType: 'number', + dflt: 1.02, + min: -2, + max: 3, + }, + xanchor: { + valType: 'enumerated', + values: ['left', 'center', 'right'], + dflt: 'left', + }, + xpad: { + valType: 'number', + min: 0, + dflt: 10, + }, + y: { + valType: 'number', + dflt: 0.5, + min: -2, + max: 3, + }, + yanchor: { + valType: 'enumerated', + values: ['top', 'middle', 'bottom'], + dflt: 'middle', + }, + ypad: { + valType: 'number', + min: 0, + dflt: 10, + }, + // a possible line around the bar itself + outlinecolor: axesAttrs.linecolor, + outlinewidth: axesAttrs.linewidth, + // Should outlinewidth have {dflt: 0} ? + // another possible line outside the padding and tick labels + bordercolor: axesAttrs.linecolor, + borderwidth: { + valType: 'number', + min: 0, + dflt: 0, + }, + bgcolor: { + valType: 'color', + dflt: 'rgba(0,0,0,0)', + }, + // tick and title properties named and function exactly as in axes + tickmode: axesAttrs.tickmode, + nticks: axesAttrs.nticks, + tick0: axesAttrs.tick0, + dtick: axesAttrs.dtick, + tickvals: axesAttrs.tickvals, + ticktext: axesAttrs.ticktext, + ticks: extendFlat({}, axesAttrs.ticks, {dflt: ''}), + ticklabeloverflow: extendFlat({}, axesAttrs.ticklabeloverflow, { + }), + ticklabelposition: { + valType: 'enumerated', + values: [ + 'outside', 'inside', + 'outside top', 'inside top', + 'outside bottom', 'inside bottom' + ], + dflt: 'outside', + }, + ticklen: axesAttrs.ticklen, + tickwidth: axesAttrs.tickwidth, + tickcolor: axesAttrs.tickcolor, + showticklabels: axesAttrs.showticklabels, + tickfont: fontAttrs({ + }), + tickangle: axesAttrs.tickangle, + tickformat: axesAttrs.tickformat, + tickformatstops: axesAttrs.tickformatstops, + tickprefix: axesAttrs.tickprefix, + showtickprefix: axesAttrs.showtickprefix, + ticksuffix: axesAttrs.ticksuffix, + showticksuffix: axesAttrs.showticksuffix, + separatethousands: axesAttrs.separatethousands, + exponentformat: axesAttrs.exponentformat, + minexponent: axesAttrs.minexponent, + showexponent: axesAttrs.showexponent, + title: { + text: { + valType: 'string', + }, + font: fontAttrs({ + }), + side: { + valType: 'enumerated', + values: ['right', 'top', 'bottom'], + dflt: 'top', + } + }, + + _deprecated: { + title: { + valType: 'string', + }, + titlefont: fontAttrs({ + }), + titleside: { + valType: 'enumerated', + values: ['right', 'top', 'bottom'], + dflt: 'top', + } + } +}, 'colorbars', 'from-root'); + +},{"../../lib/extend":279,"../../plot_api/edit_types":313,"../../plots/cartesian/layout_attributes":346,"../../plots/font_attributes":360}],157:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + cn: { + colorbar: 'colorbar', + cbbg: 'cbbg', + cbfill: 'cbfill', + cbfills: 'cbfills', + cbline: 'cbline', + cblines: 'cblines', + cbaxis: 'cbaxis', + cbtitleunshift: 'cbtitleunshift', + cbtitle: 'cbtitle', + cboutline: 'cboutline', + crisp: 'crisp', + jsPlaceholder: 'js-placeholder' + } +}; + +},{}],158:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Template = _dereq_('../../plot_api/plot_template'); + +var handleTickValueDefaults = _dereq_('../../plots/cartesian/tick_value_defaults'); +var handleTickMarkDefaults = _dereq_('../../plots/cartesian/tick_mark_defaults'); +var handleTickLabelDefaults = _dereq_('../../plots/cartesian/tick_label_defaults'); + +var attributes = _dereq_('./attributes'); + +module.exports = function colorbarDefaults(containerIn, containerOut, layout) { + var colorbarOut = Template.newContainer(containerOut, 'colorbar'); + var colorbarIn = containerIn.colorbar || {}; + + function coerce(attr, dflt) { + return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt); + } + + var thicknessmode = coerce('thicknessmode'); + coerce('thickness', (thicknessmode === 'fraction') ? + 30 / (layout.width - layout.margin.l - layout.margin.r) : + 30 + ); + + var lenmode = coerce('lenmode'); + coerce('len', (lenmode === 'fraction') ? + 1 : + layout.height - layout.margin.t - layout.margin.b + ); + + coerce('x'); + coerce('xanchor'); + coerce('xpad'); + coerce('y'); + coerce('yanchor'); + coerce('ypad'); + Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']); + + coerce('outlinecolor'); + coerce('outlinewidth'); + coerce('bordercolor'); + coerce('borderwidth'); + coerce('bgcolor'); + + var ticklabelposition = coerce('ticklabelposition'); + coerce('ticklabeloverflow', ticklabelposition.indexOf('inside') !== -1 ? 'hide past domain' : 'hide past div'); + + handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear'); + + var font = layout.font; + var opts = {outerTicks: false, font: font}; + if(ticklabelposition.indexOf('inside') !== -1) { + opts.bgColor = 'black'; // could we instead use the average of colors in the scale? + } + handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts); + handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts); + + coerce('title.text', layout._dfltTitle.colorbar); + + var tickFont = colorbarOut.tickfont; + var dfltTitleFont = Lib.extendFlat({}, tickFont, { + color: font.color, + size: Lib.bigFont(tickFont.size) + }); + Lib.coerceFont(coerce, 'title.font', dfltTitleFont); + coerce('title.side'); +}; + +},{"../../lib":285,"../../plot_api/plot_template":320,"../../plots/cartesian/tick_label_defaults":353,"../../plots/cartesian/tick_mark_defaults":354,"../../plots/cartesian/tick_value_defaults":355,"./attributes":156}],159:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var tinycolor = _dereq_('tinycolor2'); + +var Plots = _dereq_('../../plots/plots'); +var Registry = _dereq_('../../registry'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var dragElement = _dereq_('../dragelement'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var setCursor = _dereq_('../../lib/setcursor'); +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var Titles = _dereq_('../titles'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var flipScale = _dereq_('../colorscale/helpers').flipScale; + +var handleAxisDefaults = _dereq_('../../plots/cartesian/axis_defaults'); +var handleAxisPositionDefaults = _dereq_('../../plots/cartesian/position_defaults'); +var axisLayoutAttrs = _dereq_('../../plots/cartesian/layout_attributes'); + +var alignmentConstants = _dereq_('../../constants/alignment'); +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var FROM_TL = alignmentConstants.FROM_TL; +var FROM_BR = alignmentConstants.FROM_BR; + +var cn = _dereq_('./constants').cn; + +function draw(gd) { + var fullLayout = gd._fullLayout; + + var colorBars = fullLayout._infolayer + .selectAll('g.' + cn.colorbar) + .data(makeColorBarData(gd), function(opts) { return opts._id; }); + + colorBars.enter().append('g') + .attr('class', function(opts) { return opts._id; }) + .classed(cn.colorbar, true); + + colorBars.each(function(opts) { + var g = d3.select(this); + + Lib.ensureSingle(g, 'rect', cn.cbbg); + Lib.ensureSingle(g, 'g', cn.cbfills); + Lib.ensureSingle(g, 'g', cn.cblines); + Lib.ensureSingle(g, 'g', cn.cbaxis, function(s) { s.classed(cn.crisp, true); }); + Lib.ensureSingle(g, 'g', cn.cbtitleunshift, function(s) { s.append('g').classed(cn.cbtitle, true); }); + Lib.ensureSingle(g, 'rect', cn.cboutline); + + var done = drawColorBar(g, opts, gd); + if(done && done.then) (gd._promises || []).push(done); + + if(gd._context.edits.colorbarPosition) { + makeEditable(g, opts, gd); + } + }); + + colorBars.exit() + .each(function(opts) { Plots.autoMargin(gd, opts._id); }) + .remove(); + + colorBars.order(); +} + +function makeColorBarData(gd) { + var fullLayout = gd._fullLayout; + var calcdata = gd.calcdata; + var out = []; + + // single out item + var opts; + // colorbar attr parent container + var cont; + // trace attr container + var trace; + // colorbar options + var cbOpt; + + function initOpts(opts) { + return extendFlat(opts, { + // fillcolor can be a d3 scale, domain is z values, range is colors + // or leave it out for no fill, + // or set to a string constant for single-color fill + _fillcolor: null, + // line.color has the same options as fillcolor + _line: {color: null, width: null, dash: null}, + // levels of lines to draw. + // note that this DOES NOT determine the extent of the bar + // that's given by the domain of fillcolor + // (or line.color if no fillcolor domain) + _levels: {start: null, end: null, size: null}, + // separate fill levels (for example, heatmap coloring of a + // contour map) if this is omitted, fillcolors will be + // evaluated halfway between levels + _filllevels: null, + // for continuous colorscales: fill with a gradient instead of explicit levels + // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]] + _fillgradient: null, + // when using a gradient, we need the data range specified separately + _zrange: null + }); + } + + function calcOpts() { + if(typeof cbOpt.calc === 'function') { + cbOpt.calc(gd, trace, opts); + } else { + opts._fillgradient = cont.reversescale ? + flipScale(cont.colorscale) : + cont.colorscale; + opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]]; + } + } + + for(var i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + trace = cd[0].trace; + var moduleOpts = trace._module.colorbar; + + if(trace.visible === true && moduleOpts) { + var allowsMultiplotCbs = Array.isArray(moduleOpts); + var cbOpts = allowsMultiplotCbs ? moduleOpts : [moduleOpts]; + + for(var j = 0; j < cbOpts.length; j++) { + cbOpt = cbOpts[j]; + var contName = cbOpt.container; + cont = contName ? trace[contName] : trace; + + if(cont && cont.showscale) { + opts = initOpts(cont.colorbar); + opts._id = 'cb' + trace.uid + (allowsMultiplotCbs && contName ? '-' + contName : ''); + opts._traceIndex = trace.index; + opts._propPrefix = (contName ? contName + '.' : '') + 'colorbar.'; + opts._meta = trace._meta; + calcOpts(); + out.push(opts); + } + } + } + } + + for(var k in fullLayout._colorAxes) { + cont = fullLayout[k]; + + if(cont.showscale) { + var colorAxOpts = fullLayout._colorAxes[k]; + + opts = initOpts(cont.colorbar); + opts._id = 'cb' + k; + opts._propPrefix = k + '.colorbar.'; + opts._meta = fullLayout._meta; + + cbOpt = {min: 'cmin', max: 'cmax'}; + if(colorAxOpts[0] !== 'heatmap') { + trace = colorAxOpts[1]; + cbOpt.calc = trace._module.colorbar.calc; + } + + calcOpts(); + out.push(opts); + } + } + + return out; +} + +function drawColorBar(g, opts, gd) { + var fullLayout = gd._fullLayout; + var gs = fullLayout._size; + + var fillColor = opts._fillcolor; + var line = opts._line; + var title = opts.title; + var titleSide = title.side; + + var zrange = opts._zrange || + d3.extent((typeof fillColor === 'function' ? fillColor : line.color).domain()); + + var lineColormap = typeof line.color === 'function' ? + line.color : + function() { return line.color; }; + var fillColormap = typeof fillColor === 'function' ? + fillColor : + function() { return fillColor; }; + + var levelsIn = opts._levels; + var levelsOut = calcLevels(gd, opts, zrange); + var fillLevels = levelsOut.fill; + var lineLevels = levelsOut.line; + + // we calculate pixel sizes based on the specified graph size, + // not the actual (in case something pushed the margins around) + // which is a little odd but avoids an odd iterative effect + // when the colorbar itself is pushing the margins. + // but then the fractional size is calculated based on the + // actual graph size, so that the axes will size correctly. + var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? gs.w : 1)); + var thickFrac = thickPx / gs.w; + var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? gs.h : 1)); + var lenFrac = lenPx / gs.h; + var xpadFrac = opts.xpad / gs.w; + var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2; + var ypadFrac = opts.ypad / gs.h; + + // x positioning: do it initially just for left anchor, + // then fix at the end (since we don't know the width yet) + var xLeft = Math.round(opts.x * gs.w + opts.xpad); + // for dragging... this is getting a little muddled... + var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor] || 0); + + // y positioning we can do correctly from the start + var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5); + var yBottomPx = Math.round(gs.h * (1 - yBottomFrac)); + var yTopPx = yBottomPx - lenPx; + + // stash a few things for makeEditable + opts._lenFrac = lenFrac; + opts._thickFrac = thickFrac; + opts._xLeftFrac = xLeftFrac; + opts._yBottomFrac = yBottomFrac; + + // stash mocked axis for contour label formatting + var ax = opts._axis = mockColorBarAxis(gd, opts, zrange); + + // position can't go in through supplyDefaults + // because that restricts it to [0,1] + ax.position = opts.x + xpadFrac + thickFrac; + + if(['top', 'bottom'].indexOf(titleSide) !== -1) { + ax.title.side = titleSide; + ax.titlex = opts.x + xpadFrac; + ax.titley = yBottomFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac); + } + + if(line.color && opts.tickmode === 'auto') { + ax.tickmode = 'linear'; + ax.tick0 = levelsIn.start; + var dtick = levelsIn.size; + // expand if too many contours, so we don't get too many ticks + var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1; + var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick); + if(dtFactor > 1) { + var dtexp = Math.pow(10, Math.floor(Math.log(dtFactor) / Math.LN10)); + dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]); + // if the contours are at round multiples, reset tick0 + // so they're still at round multiples. Otherwise, + // keep the first label on the first contour level + if((Math.abs(levelsIn.start) / levelsIn.size + 1e-6) % 1 < 2e-6) { + ax.tick0 = 0; + } + } + ax.dtick = dtick; + } + + // set domain after init, because we may want to + // allow it outside [0,1] + ax.domain = [ + yBottomFrac + ypadFrac, + yBottomFrac + lenFrac - ypadFrac + ]; + + ax.setScale(); + + g.attr('transform', strTranslate(Math.round(gs.l), Math.round(gs.t))); + + var titleCont = g.select('.' + cn.cbtitleunshift) + .attr('transform', strTranslate(-Math.round(gs.l), -Math.round(gs.t))); + + var axLayer = g.select('.' + cn.cbaxis); + var titleEl; + var titleHeight = 0; + + function drawTitle(titleClass, titleOpts) { + var dfltTitleOpts = { + propContainer: ax, + propName: opts._propPrefix + 'title', + traceIndex: opts._traceIndex, + _meta: opts._meta, + placeholder: fullLayout._dfltTitle.colorbar, + containerGroup: g.select('.' + cn.cbtitle) + }; + + // this class-to-rotate thing with convertToTspans is + // getting hackier and hackier... delete groups with the + // wrong class (in case earlier the colorbar was drawn on + // a different side, I think?) + var otherClass = titleClass.charAt(0) === 'h' ? + titleClass.substr(1) : + 'h' + titleClass; + g.selectAll('.' + otherClass + ',.' + otherClass + '-math-group').remove(); + + Titles.draw(gd, titleClass, extendFlat(dfltTitleOpts, titleOpts || {})); + } + + function drawDummyTitle() { + if(['top', 'bottom'].indexOf(titleSide) !== -1) { + // draw the title so we know how much room it needs + // when we squish the axis. This one only applies to + // top or bottom titles, not right side. + var x = gs.l + (opts.x + xpadFrac) * gs.w; + var fontSize = ax.title.font.size; + var y; + + if(titleSide === 'top') { + y = (1 - (yBottomFrac + lenFrac - ypadFrac)) * gs.h + + gs.t + 3 + fontSize * 0.75; + } else { + y = (1 - (yBottomFrac + ypadFrac)) * gs.h + + gs.t - 3 - fontSize * 0.25; + } + drawTitle(ax._id + 'title', { + attributes: {x: x, y: y, 'text-anchor': 'start'} + }); + } + } + + function drawCbTitle() { + if(['top', 'bottom'].indexOf(titleSide) === -1) { + var fontSize = ax.title.font.size; + var y = ax._offset + ax._length / 2; + var x = gs.l + (ax.position || 0) * gs.w + ((ax.side === 'right') ? + 10 + fontSize * ((ax.showticklabels ? 1 : 0.5)) : + -10 - fontSize * ((ax.showticklabels ? 0.5 : 0))); + + // the 'h' + is a hack to get around the fact that + // convertToTspans rotates any 'y...' class by 90 degrees. + // TODO: find a better way to control this. + drawTitle('h' + ax._id + 'title', { + avoid: { + selection: d3.select(gd).selectAll('g.' + ax._id + 'tick'), + side: titleSide, + offsetLeft: gs.l, + offsetTop: 0, + maxShift: fullLayout.width + }, + attributes: {x: x, y: y, 'text-anchor': 'middle'}, + transform: {rotate: '-90', offset: 0} + }); + } + } + + function drawAxis() { + if(['top', 'bottom'].indexOf(titleSide) !== -1) { + // squish the axis top to make room for the title + var titleGroup = g.select('.' + cn.cbtitle); + var titleText = titleGroup.select('text'); + var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2]; + var mathJaxNode = titleGroup + .select('.h' + ax._id + 'title-math-group') + .node(); + var lineSize = 15.6; + if(titleText.node()) { + lineSize = parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING; + } + if(mathJaxNode) { + titleHeight = Drawing.bBox(mathJaxNode).height; + if(titleHeight > lineSize) { + // not entirely sure how mathjax is doing + // vertical alignment, but this seems to work. + titleTrans[1] -= (titleHeight - lineSize) / 2; + } + } else if(titleText.node() && !titleText.classed(cn.jsPlaceholder)) { + titleHeight = Drawing.bBox(titleText.node()).height; + } + if(titleHeight) { + // buffer btwn colorbar and title + // TODO: configurable + titleHeight += 5; + + if(titleSide === 'top') { + ax.domain[1] -= titleHeight / gs.h; + titleTrans[1] *= -1; + } else { + ax.domain[0] += titleHeight / gs.h; + var nlines = svgTextUtils.lineCount(titleText); + titleTrans[1] += (1 - nlines) * lineSize; + } + + titleGroup.attr('transform', strTranslate(titleTrans[0], titleTrans[1])); + ax.setScale(); + } + } + + g.selectAll('.' + cn.cbfills + ',.' + cn.cblines) + .attr('transform', strTranslate(0, Math.round(gs.h * (1 - ax.domain[1])))); + + axLayer.attr('transform', strTranslate(0, Math.round(-gs.t))); + + var fills = g.select('.' + cn.cbfills) + .selectAll('rect.' + cn.cbfill) + .attr('style', '') + .data(fillLevels); + fills.enter().append('rect') + .classed(cn.cbfill, true) + .style('stroke', 'none'); + fills.exit().remove(); + + var zBounds = zrange + .map(ax.c2p) + .map(Math.round) + .sort(function(a, b) { return a - b; }); + + fills.each(function(d, i) { + var z = [ + (i === 0) ? zrange[0] : (fillLevels[i] + fillLevels[i - 1]) / 2, + (i === fillLevels.length - 1) ? zrange[1] : (fillLevels[i] + fillLevels[i + 1]) / 2 + ] + .map(ax.c2p) + .map(Math.round); + + // offset the side adjoining the next rectangle so they + // overlap, to prevent antialiasing gaps + z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]); + + + // Colorbar cannot currently support opacities so we + // use an opaque fill even when alpha channels present + var fillEl = d3.select(this).attr({ + x: xLeft, + width: Math.max(thickPx, 2), + y: d3.min(z), + height: Math.max(d3.max(z) - d3.min(z), 2), + }); + + if(opts._fillgradient) { + Drawing.gradient(fillEl, gd, opts._id, 'vertical', opts._fillgradient, 'fill'); + } else { + // tinycolor can't handle exponents and + // at this scale, removing it makes no difference. + var colorString = fillColormap(d).replace('e-', ''); + fillEl.attr('fill', tinycolor(colorString).toHexString()); + } + }); + + var lines = g.select('.' + cn.cblines) + .selectAll('path.' + cn.cbline) + .data(line.color && line.width ? lineLevels : []); + lines.enter().append('path') + .classed(cn.cbline, true); + lines.exit().remove(); + lines.each(function(d) { + d3.select(this) + .attr('d', 'M' + xLeft + ',' + + (Math.round(ax.c2p(d)) + (line.width / 2) % 1) + 'h' + thickPx) + .call(Drawing.lineGroupStyle, line.width, lineColormap(d), line.dash); + }); + + // force full redraw of labels and ticks + axLayer.selectAll('g.' + ax._id + 'tick,path').remove(); + + var shift = xLeft + thickPx + + (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0); + + var vals = Axes.calcTicks(ax); + var tickSign = Axes.getTickSigns(ax)[2]; + + Axes.drawTicks(gd, ax, { + vals: ax.ticks === 'inside' ? Axes.clipEnds(ax, vals) : vals, + layer: axLayer, + path: Axes.makeTickPath(ax, shift, tickSign), + transFn: Axes.makeTransTickFn(ax) + }); + + return Axes.drawLabels(gd, ax, { + vals: vals, + layer: axLayer, + transFn: Axes.makeTransTickLabelFn(ax), + labelFns: Axes.makeLabelFns(ax, shift) + }); + } + + // wait for the axis & title to finish rendering before + // continuing positioning + // TODO: why are we redrawing multiple times now with this? + // I guess autoMargin doesn't like being post-promise? + function positionCB() { + var innerWidth = thickPx + opts.outlinewidth / 2; + if(ax.ticklabelposition.indexOf('inside') === -1) { + innerWidth += Drawing.bBox(axLayer.node()).width; + } + + titleEl = titleCont.select('text'); + + if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) { + var mathJaxNode = titleCont.select('.h' + ax._id + 'title-math-group').node(); + var titleWidth; + if(mathJaxNode && ['top', 'bottom'].indexOf(titleSide) !== -1) { + titleWidth = Drawing.bBox(mathJaxNode).width; + } else { + // note: the formula below works for all title sides, + // (except for top/bottom mathjax, above) + // but the weird gs.l is because the titleunshift + // transform gets removed by Drawing.bBox + titleWidth = Drawing.bBox(titleCont.node()).right - xLeft - gs.l; + } + innerWidth = Math.max(innerWidth, titleWidth); + } + + var outerwidth = 2 * opts.xpad + innerWidth + opts.borderwidth + opts.outlinewidth / 2; + var outerheight = yBottomPx - yTopPx; + + g.select('.' + cn.cbbg).attr({ + x: xLeft - opts.xpad - (opts.borderwidth + opts.outlinewidth) / 2, + y: yTopPx - yExtraPx, + width: Math.max(outerwidth, 2), + height: Math.max(outerheight + 2 * yExtraPx, 2) + }) + .call(Color.fill, opts.bgcolor) + .call(Color.stroke, opts.bordercolor) + .style('stroke-width', opts.borderwidth); + + g.selectAll('.' + cn.cboutline).attr({ + x: xLeft, + y: yTopPx + opts.ypad + (titleSide === 'top' ? titleHeight : 0), + width: Math.max(thickPx, 2), + height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2) + }) + .call(Color.stroke, opts.outlinecolor) + .style({ + fill: 'none', + 'stroke-width': opts.outlinewidth + }); + + // fix positioning for xanchor!='left' + var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) * outerwidth; + g.attr('transform', strTranslate(gs.l - xoffset, gs.t)); + + // auto margin adjustment + var marginOpts = {}; + var tFrac = FROM_TL[opts.yanchor]; + var bFrac = FROM_BR[opts.yanchor]; + if(opts.lenmode === 'pixels') { + marginOpts.y = opts.y; + marginOpts.t = outerheight * tFrac; + marginOpts.b = outerheight * bFrac; + } else { + marginOpts.t = marginOpts.b = 0; + marginOpts.yt = opts.y + opts.len * tFrac; + marginOpts.yb = opts.y - opts.len * bFrac; + } + + var lFrac = FROM_TL[opts.xanchor]; + var rFrac = FROM_BR[opts.xanchor]; + if(opts.thicknessmode === 'pixels') { + marginOpts.x = opts.x; + marginOpts.l = outerwidth * lFrac; + marginOpts.r = outerwidth * rFrac; + } else { + var extraThickness = outerwidth - thickPx; + marginOpts.l = extraThickness * lFrac; + marginOpts.r = extraThickness * rFrac; + marginOpts.xl = opts.x - opts.thickness * lFrac; + marginOpts.xr = opts.x + opts.thickness * rFrac; + } + + Plots.autoMargin(gd, opts._id, marginOpts); + } + + return Lib.syncOrAsync([ + Plots.previousPromises, + drawDummyTitle, + drawAxis, + drawCbTitle, + Plots.previousPromises, + positionCB + ], gd); +} + +function makeEditable(g, opts, gd) { + var fullLayout = gd._fullLayout; + var gs = fullLayout._size; + var t0, xf, yf; + + dragElement.init({ + element: g.node(), + gd: gd, + prepFn: function() { + t0 = g.attr('transform'); + setCursor(g); + }, + moveFn: function(dx, dy) { + g.attr('transform', t0 + strTranslate(dx, dy)); + + xf = dragElement.align(opts._xLeftFrac + (dx / gs.w), opts._thickFrac, + 0, 1, opts.xanchor); + yf = dragElement.align(opts._yBottomFrac - (dy / gs.h), opts._lenFrac, + 0, 1, opts.yanchor); + + var csr = dragElement.getCursor(xf, yf, opts.xanchor, opts.yanchor); + setCursor(g, csr); + }, + doneFn: function() { + setCursor(g); + + if(xf !== undefined && yf !== undefined) { + var update = {}; + update[opts._propPrefix + 'x'] = xf; + update[opts._propPrefix + 'y'] = yf; + if(opts._traceIndex !== undefined) { + Registry.call('_guiRestyle', gd, update, opts._traceIndex); + } else { + Registry.call('_guiRelayout', gd, update); + } + } + } + }); +} + +function calcLevels(gd, opts, zrange) { + var levelsIn = opts._levels; + var lineLevels = []; + var fillLevels = []; + var l; + var i; + + var l0 = levelsIn.end + levelsIn.size / 100; + var ls = levelsIn.size; + var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]); + var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]); + + for(i = 0; i < 1e5; i++) { + l = levelsIn.start + i * ls; + if(ls > 0 ? (l >= l0) : (l <= l0)) break; + if(l > zr0 && l < zr1) lineLevels.push(l); + } + + if(opts._fillgradient) { + fillLevels = [0]; + } else if(typeof opts._fillcolor === 'function') { + var fillLevelsIn = opts._filllevels; + + if(fillLevelsIn) { + l0 = fillLevelsIn.end + fillLevelsIn.size / 100; + ls = fillLevelsIn.size; + for(i = 0; i < 1e5; i++) { + l = fillLevelsIn.start + i * ls; + if(ls > 0 ? (l >= l0) : (l <= l0)) break; + if(l > zrange[0] && l < zrange[1]) fillLevels.push(l); + } + } else { + fillLevels = lineLevels.map(function(v) { + return v - levelsIn.size / 2; + }); + fillLevels.push(fillLevels[fillLevels.length - 1] + levelsIn.size); + } + } else if(opts._fillcolor && typeof opts._fillcolor === 'string') { + // doesn't matter what this value is, with a single value + // we'll make a single fill rect covering the whole bar + fillLevels = [0]; + } + + if(levelsIn.size < 0) { + lineLevels.reverse(); + fillLevels.reverse(); + } + + return {line: lineLevels, fill: fillLevels}; +} + +function mockColorBarAxis(gd, opts, zrange) { + var fullLayout = gd._fullLayout; + + var cbAxisIn = { + type: 'linear', + range: zrange, + tickmode: opts.tickmode, + nticks: opts.nticks, + tick0: opts.tick0, + dtick: opts.dtick, + tickvals: opts.tickvals, + ticktext: opts.ticktext, + ticks: opts.ticks, + ticklen: opts.ticklen, + tickwidth: opts.tickwidth, + tickcolor: opts.tickcolor, + showticklabels: opts.showticklabels, + ticklabelposition: opts.ticklabelposition, + ticklabeloverflow: opts.ticklabeloverflow, + tickfont: opts.tickfont, + tickangle: opts.tickangle, + tickformat: opts.tickformat, + exponentformat: opts.exponentformat, + minexponent: opts.minexponent, + separatethousands: opts.separatethousands, + showexponent: opts.showexponent, + showtickprefix: opts.showtickprefix, + tickprefix: opts.tickprefix, + showticksuffix: opts.showticksuffix, + ticksuffix: opts.ticksuffix, + title: opts.title, + showline: true, + anchor: 'free', + side: 'right', + position: 1 + }; + + var cbAxisOut = { + type: 'linear', + _id: 'y' + opts._id + }; + + var axisOptions = { + letter: 'y', + font: fullLayout.font, + noHover: true, + noTickson: true, + noTicklabelmode: true, + calendar: fullLayout.calendar // not really necessary (yet?) + }; + + function coerce(attr, dflt) { + return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt); + } + + handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout); + handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions); + + return cbAxisOut; +} + +module.exports = { + draw: draw +}; + +},{"../../constants/alignment":260,"../../lib":285,"../../lib/extend":279,"../../lib/setcursor":305,"../../lib/svg_text_utils":307,"../../plots/cartesian/axes":331,"../../plots/cartesian/axis_defaults":333,"../../plots/cartesian/layout_attributes":346,"../../plots/cartesian/position_defaults":349,"../../plots/plots":366,"../../registry":373,"../color":155,"../colorscale/helpers":166,"../dragelement":174,"../drawing":177,"../titles":253,"./constants":157,"@plotly/d3":20,"tinycolor2":119}],160:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + + +module.exports = function hasColorbar(container) { + return Lib.isPlainObject(container.colorbar); +}; + +},{"../../lib":285}],161:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + moduleType: 'component', + name: 'colorbar', + + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw').draw, + hasColorbar: _dereq_('./has_colorbar') +}; + +},{"./attributes":156,"./defaults":158,"./draw":159,"./has_colorbar":160}],162:[function(_dereq_,module,exports){ +'use strict'; + +var colorbarAttrs = _dereq_('../colorbar/attributes'); +var counterRegex = _dereq_('../../lib/regex').counter; + +var palettes = _dereq_('./scales.js').scales; +var paletteStr = Object.keys(palettes); + +function code(s) { + return '`' + s + '`'; +} + +/** + * Make colorscale attribute declarations for + * + * - colorscale, + * - (c|z)auto, (c|z)min, (c|z)max, + * - autocolorscale, reversescale, + * - showscale (optionally) + * - color (optionally) + * + * @param {string} context (dflt: '', i.e. from trace root): + * the container this is in ('', *marker*, *marker.line* etc) + * + * @param {object} opts: + * - cLetter {string} (dflt: 'c'): + * leading letter for 'min', 'max and 'auto' attribute (either 'z' or 'c') + * + * - colorAttr {string} (dflt: 'z' if `cLetter: 'z'`, 'color' if `cLetter: 'c'`): + * (for descriptions) sets the name of the color attribute that maps to the colorscale. + * + * N.B. if `colorAttr: 'color'`, we include the `color` declaration here. + * + * - onlyIfNumerical {string} (dflt: false' if `cLetter: 'z'`, true if `cLetter: 'c'`): + * (for descriptions) set to true if colorscale attribute only + * + * - colorscaleDflt {string}: + * overrides the colorscale dflt + * + * - autoColorDflt {boolean} (dflt true): + * normally autocolorscale.dflt is `true`, but pass `false` to override + * + * - noScale {boolean} (dflt: true if `context: 'marker.line'`, false otherwise): + * set to `false` to not include showscale attribute (e.g. for 'marker.line') + * + * - showScaleDflt {boolean} (dflt: true if `cLetter: 'z'`, false otherwise) + * + * - editTypeOverride {boolean} (dflt: ''): + * most of these attributes already require a recalc, but the ones that do not + * have editType *style* or *plot* unless you override (presumably with *calc*) + * + * - anim {boolean) (dflt: undefined): is 'color' animatable? + * + * @return {object} + */ +module.exports = function colorScaleAttrs(context, opts) { + context = context || ''; + opts = opts || {}; + + var cLetter = opts.cLetter || 'c'; + var onlyIfNumerical = ('onlyIfNumerical' in opts) ? opts.onlyIfNumerical : Boolean(context); + var noScale = ('noScale' in opts) ? opts.noScale : context === 'marker.line'; + var showScaleDflt = ('showScaleDflt' in opts) ? opts.showScaleDflt : cLetter === 'z'; + var colorscaleDflt = typeof opts.colorscaleDflt === 'string' ? palettes[opts.colorscaleDflt] : null; + var editTypeOverride = opts.editTypeOverride || ''; + var contextHead = context ? (context + '.') : ''; + + var colorAttr, colorAttrFull; + + if('colorAttr' in opts) { + colorAttr = opts.colorAttr; + colorAttrFull = opts.colorAttr; + } else { + colorAttr = {z: 'z', c: 'color'}[cLetter]; + colorAttrFull = 'in ' + code(contextHead + colorAttr); + } + + var effectDesc = onlyIfNumerical ? + ' Has an effect only if ' + colorAttrFull + 'is set to a numerical array.' : + ''; + + var auto = cLetter + 'auto'; + var min = cLetter + 'min'; + var max = cLetter + 'max'; + var mid = cLetter + 'mid'; + var autoFull = code(contextHead + auto); + var minFull = code(contextHead + min); + var maxFull = code(contextHead + max); + var minmaxFull = minFull + ' and ' + maxFull; + var autoImpliedEdits = {}; + autoImpliedEdits[min] = autoImpliedEdits[max] = undefined; + var minmaxImpliedEdits = {}; + minmaxImpliedEdits[auto] = false; + + var attrs = {}; + + if(colorAttr === 'color') { + attrs.color = { + valType: 'color', + arrayOk: true, + editType: editTypeOverride || 'style', + }; + + if(opts.anim) { + attrs.color.anim = true; + } + } + + attrs[auto] = { + valType: 'boolean', + dflt: true, + editType: 'calc', + impliedEdits: autoImpliedEdits, + }; + + attrs[min] = { + valType: 'number', + dflt: null, + editType: editTypeOverride || 'plot', + impliedEdits: minmaxImpliedEdits, + }; + + attrs[max] = { + valType: 'number', + dflt: null, + editType: editTypeOverride || 'plot', + impliedEdits: minmaxImpliedEdits, + }; + + attrs[mid] = { + valType: 'number', + dflt: null, + editType: 'calc', + impliedEdits: autoImpliedEdits, + }; + + attrs.colorscale = { + valType: 'colorscale', + editType: 'calc', + dflt: colorscaleDflt, + impliedEdits: {autocolorscale: false}, + }; + + attrs.autocolorscale = { + valType: 'boolean', + // gets overrode in 'heatmap' & 'surface' for backwards comp. + dflt: opts.autoColorDflt === false ? false : true, + editType: 'calc', + impliedEdits: {colorscale: undefined}, + }; + + attrs.reversescale = { + valType: 'boolean', + dflt: false, + editType: 'plot', + }; + + if(!noScale) { + attrs.showscale = { + valType: 'boolean', + dflt: showScaleDflt, + editType: 'calc', + }; + + attrs.colorbar = colorbarAttrs; + } + + if(!opts.noColorAxis) { + attrs.coloraxis = { + valType: 'subplotid', + regex: counterRegex('coloraxis'), + dflt: null, + editType: 'calc', + }; + } + + return attrs; +}; + +},{"../../lib/regex":301,"../colorbar/attributes":156,"./scales.js":170}],163:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var extractOpts = _dereq_('./helpers').extractOpts; + +module.exports = function calc(gd, trace, opts) { + var fullLayout = gd._fullLayout; + var vals = opts.vals; + var containerStr = opts.containerStr; + + var container = containerStr ? + Lib.nestedProperty(trace, containerStr).get() : + trace; + + var cOpts = extractOpts(container); + var auto = cOpts.auto !== false; + var min = cOpts.min; + var max = cOpts.max; + var mid = cOpts.mid; + + var minVal = function() { return Lib.aggNums(Math.min, null, vals); }; + var maxVal = function() { return Lib.aggNums(Math.max, null, vals); }; + + if(min === undefined) { + min = minVal(); + } else if(auto) { + if(container._colorAx && isNumeric(min)) { + min = Math.min(min, minVal()); + } else { + min = minVal(); + } + } + + if(max === undefined) { + max = maxVal(); + } else if(auto) { + if(container._colorAx && isNumeric(max)) { + max = Math.max(max, maxVal()); + } else { + max = maxVal(); + } + } + + if(auto && mid !== undefined) { + if(max - mid > mid - min) { + min = mid - (max - mid); + } else if(max - mid < mid - min) { + max = mid + (mid - min); + } + } + + if(min === max) { + min -= 0.5; + max += 0.5; + } + + cOpts._sync('min', min); + cOpts._sync('max', max); + + if(cOpts.autocolorscale) { + var scl; + if(min * max < 0) scl = fullLayout.colorscale.diverging; + else if(min >= 0) scl = fullLayout.colorscale.sequential; + else scl = fullLayout.colorscale.sequentialminus; + cOpts._sync('colorscale', scl); + } +}; + +},{"../../lib":285,"./helpers":166,"fast-isnumeric":31}],164:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var hasColorscale = _dereq_('./helpers').hasColorscale; +var extractOpts = _dereq_('./helpers').extractOpts; + +module.exports = function crossTraceDefaults(fullData, fullLayout) { + function replace(cont, k) { + var val = cont['_' + k]; + if(val !== undefined) { + cont[k] = val; + } + } + + function relinkColorAttrs(outerCont, cbOpt) { + var cont = cbOpt.container ? + Lib.nestedProperty(outerCont, cbOpt.container).get() : + outerCont; + + if(cont) { + if(cont.coloraxis) { + // stash ref to color axis + cont._colorAx = fullLayout[cont.coloraxis]; + } else { + var cOpts = extractOpts(cont); + var isAuto = cOpts.auto; + + if(isAuto || cOpts.min === undefined) { + replace(cont, cbOpt.min); + } + if(isAuto || cOpts.max === undefined) { + replace(cont, cbOpt.max); + } + if(cOpts.autocolorscale) { + replace(cont, 'colorscale'); + } + } + } + } + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + var cbOpts = trace._module.colorbar; + + if(cbOpts) { + if(Array.isArray(cbOpts)) { + for(var j = 0; j < cbOpts.length; j++) { + relinkColorAttrs(trace, cbOpts[j]); + } + } else { + relinkColorAttrs(trace, cbOpts); + } + } + + if(hasColorscale(trace, 'marker.line')) { + relinkColorAttrs(trace, { + container: 'marker.line', + min: 'cmin', + max: 'cmax' + }); + } + } + + for(var k in fullLayout._colorAxes) { + relinkColorAttrs(fullLayout[k], {min: 'cmin', max: 'cmax'}); + } +}; + +},{"../../lib":285,"./helpers":166}],165:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var hasColorbar = _dereq_('../colorbar/has_colorbar'); +var colorbarDefaults = _dereq_('../colorbar/defaults'); + +var isValidScale = _dereq_('./scales').isValid; +var traceIs = _dereq_('../../registry').traceIs; + +function npMaybe(parentCont, prefix) { + var containerStr = prefix.slice(0, prefix.length - 1); + return prefix ? + Lib.nestedProperty(parentCont, containerStr).get() || {} : + parentCont; +} + +/** + * Colorscale / colorbar default handler + * + * @param {object} parentContIn : user (input) parent container (e.g. trace or layout coloraxis object) + * @param {object} parentContOut : full parent container + * @param {object} layout : (full) layout object + * @param {fn} coerce : Lib.coerce wrapper + * @param {object} opts : + * - prefix {string} : attr string prefix to colorscale container from parent root + * - cLetter {string} : 'c or 'z' color letter + */ +module.exports = function colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts) { + var prefix = opts.prefix; + var cLetter = opts.cLetter; + var inTrace = '_module' in parentContOut; + var containerIn = npMaybe(parentContIn, prefix); + var containerOut = npMaybe(parentContOut, prefix); + var template = npMaybe(parentContOut._template || {}, prefix) || {}; + + // colorScaleDefaults wrapper called if-ever we need to reset the colorscale + // attributes for containers that were linked to invalid color axes + var thisFn = function() { + delete parentContIn.coloraxis; + delete parentContOut.coloraxis; + return colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts); + }; + + if(inTrace) { + var colorAxes = layout._colorAxes || {}; + var colorAx = coerce(prefix + 'coloraxis'); + + if(colorAx) { + var colorbarVisuals = ( + traceIs(parentContOut, 'contour') && + Lib.nestedProperty(parentContOut, 'contours.coloring').get() + ) || 'heatmap'; + + var stash = colorAxes[colorAx]; + + if(stash) { + stash[2].push(thisFn); + + if(stash[0] !== colorbarVisuals) { + stash[0] = false; + Lib.warn([ + 'Ignoring coloraxis:', colorAx, 'setting', + 'as it is linked to incompatible colorscales.' + ].join(' ')); + } + } else { + // stash: + // - colorbar visual 'type' + // - colorbar options to help in Colorbar.draw + // - list of colorScaleDefaults wrapper functions + colorAxes[colorAx] = [colorbarVisuals, parentContOut, [thisFn]]; + } + return; + } + } + + var minIn = containerIn[cLetter + 'min']; + var maxIn = containerIn[cLetter + 'max']; + var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn); + var auto = coerce(prefix + cLetter + 'auto', !validMinMax); + + if(auto) { + coerce(prefix + cLetter + 'mid'); + } else { + coerce(prefix + cLetter + 'min'); + coerce(prefix + cLetter + 'max'); + } + + // handles both the trace case (autocolorscale is false by default) and + // the marker and marker.line case (autocolorscale is true by default) + var sclIn = containerIn.colorscale; + var sclTemplate = template.colorscale; + var autoColorscaleDflt; + if(sclIn !== undefined) autoColorscaleDflt = !isValidScale(sclIn); + if(sclTemplate !== undefined) autoColorscaleDflt = !isValidScale(sclTemplate); + coerce(prefix + 'autocolorscale', autoColorscaleDflt); + + coerce(prefix + 'colorscale'); + coerce(prefix + 'reversescale'); + + if(prefix !== 'marker.line.') { + // handles both the trace case where the dflt is listed in attributes and + // the marker case where the dflt is determined by hasColorbar + var showScaleDflt; + if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn); + + var showScale = coerce(prefix + 'showscale', showScaleDflt); + if(showScale) { + if(prefix && template) containerOut._template = template; + colorbarDefaults(containerIn, containerOut, layout); + } + } +}; + +},{"../../lib":285,"../../registry":373,"../colorbar/defaults":158,"../colorbar/has_colorbar":160,"./scales":170,"fast-isnumeric":31}],166:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var tinycolor = _dereq_('tinycolor2'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../color'); + +var isValidScale = _dereq_('./scales').isValid; + +function hasColorscale(trace, containerStr, colorKey) { + var container = containerStr ? + Lib.nestedProperty(trace, containerStr).get() || {} : + trace; + var color = container[colorKey || 'color']; + + var isArrayWithOneNumber = false; + if(Lib.isArrayOrTypedArray(color)) { + for(var i = 0; i < color.length; i++) { + if(isNumeric(color[i])) { + isArrayWithOneNumber = true; + break; + } + } + } + + return ( + Lib.isPlainObject(container) && ( + isArrayWithOneNumber || + container.showscale === true || + (isNumeric(container.cmin) && isNumeric(container.cmax)) || + isValidScale(container.colorscale) || + Lib.isPlainObject(container.colorbar) + ) + ); +} + +var constantAttrs = ['showscale', 'autocolorscale', 'colorscale', 'reversescale', 'colorbar']; +var letterAttrs = ['min', 'max', 'mid', 'auto']; + +/** + * Extract 'c' / 'z', trace / color axis colorscale options + * + * Note that it would be nice to replace all z* with c* equivalents in v3 + * + * @param {object} cont : attribute container + * @return {object}: + * - min: cmin or zmin + * - max: cmax or zmax + * - mid: cmid or zmid + * - auto: cauto or zauto + * - *scale: *scale attrs + * - colorbar: colorbar + * - _sync: function syncing attr and underscore dual (useful when calc'ing min/max) + */ +function extractOpts(cont) { + var colorAx = cont._colorAx; + var cont2 = colorAx ? colorAx : cont; + var out = {}; + var cLetter; + var i, k; + + for(i = 0; i < constantAttrs.length; i++) { + k = constantAttrs[i]; + out[k] = cont2[k]; + } + + if(colorAx) { + cLetter = 'c'; + for(i = 0; i < letterAttrs.length; i++) { + k = letterAttrs[i]; + out[k] = cont2['c' + k]; + } + } else { + var k2; + for(i = 0; i < letterAttrs.length; i++) { + k = letterAttrs[i]; + k2 = 'c' + k; + if(k2 in cont2) { + out[k] = cont2[k2]; + continue; + } + k2 = 'z' + k; + if(k2 in cont2) { + out[k] = cont2[k2]; + } + } + cLetter = k2.charAt(0); + } + + out._sync = function(k, v) { + var k2 = letterAttrs.indexOf(k) !== -1 ? cLetter + k : k; + cont2[k2] = cont2['_' + k2] = v; + }; + + return out; +} + +/** + * Extract colorscale into numeric domain and color range. + * + * @param {object} cont colorscale container (e.g. trace, marker) + * - colorscale {array of arrays} + * - cmin/zmin {number} + * - cmax/zmax {number} + * - reversescale {boolean} + * + * @return {object} + * - domain {array} + * - range {array} + */ +function extractScale(cont) { + var cOpts = extractOpts(cont); + var cmin = cOpts.min; + var cmax = cOpts.max; + + var scl = cOpts.reversescale ? + flipScale(cOpts.colorscale) : + cOpts.colorscale; + + var N = scl.length; + var domain = new Array(N); + var range = new Array(N); + + for(var i = 0; i < N; i++) { + var si = scl[i]; + domain[i] = cmin + si[0] * (cmax - cmin); + range[i] = si[1]; + } + + return {domain: domain, range: range}; +} + +function flipScale(scl) { + var N = scl.length; + var sclNew = new Array(N); + + for(var i = N - 1, j = 0; i >= 0; i--, j++) { + var si = scl[i]; + sclNew[j] = [1 - si[0], si[1]]; + } + return sclNew; +} + +/** + * General colorscale function generator. + * + * @param {object} specs output of Colorscale.extractScale or precomputed domain, range. + * - domain {array} + * - range {array} + * + * @param {object} opts + * - noNumericCheck {boolean} if true, scale func bypasses numeric checks + * - returnArray {boolean} if true, scale func return 4-item array instead of color strings + * + * @return {function} + */ +function makeColorScaleFunc(specs, opts) { + opts = opts || {}; + + var domain = specs.domain; + var range = specs.range; + var N = range.length; + var _range = new Array(N); + + for(var i = 0; i < N; i++) { + var rgba = tinycolor(range[i]).toRgb(); + _range[i] = [rgba.r, rgba.g, rgba.b, rgba.a]; + } + + var _sclFunc = d3.scale.linear() + .domain(domain) + .range(_range) + .clamp(true); + + var noNumericCheck = opts.noNumericCheck; + var returnArray = opts.returnArray; + var sclFunc; + + if(noNumericCheck && returnArray) { + sclFunc = _sclFunc; + } else if(noNumericCheck) { + sclFunc = function(v) { + return colorArray2rbga(_sclFunc(v)); + }; + } else if(returnArray) { + sclFunc = function(v) { + if(isNumeric(v)) return _sclFunc(v); + else if(tinycolor(v).isValid()) return v; + else return Color.defaultLine; + }; + } else { + sclFunc = function(v) { + if(isNumeric(v)) return colorArray2rbga(_sclFunc(v)); + else if(tinycolor(v).isValid()) return v; + else return Color.defaultLine; + }; + } + + // colorbar draw looks into the d3 scale closure for domain and range + sclFunc.domain = _sclFunc.domain; + sclFunc.range = function() { return range; }; + + return sclFunc; +} + +function makeColorScaleFuncFromTrace(trace, opts) { + return makeColorScaleFunc(extractScale(trace), opts); +} + +function colorArray2rbga(colorArray) { + var colorObj = { + r: colorArray[0], + g: colorArray[1], + b: colorArray[2], + a: colorArray[3] + }; + + return tinycolor(colorObj).toRgbString(); +} + +module.exports = { + hasColorscale: hasColorscale, + extractOpts: extractOpts, + extractScale: extractScale, + flipScale: flipScale, + makeColorScaleFunc: makeColorScaleFunc, + makeColorScaleFuncFromTrace: makeColorScaleFuncFromTrace +}; + +},{"../../lib":285,"../color":155,"./scales":170,"@plotly/d3":20,"fast-isnumeric":31,"tinycolor2":119}],167:[function(_dereq_,module,exports){ +'use strict'; + +var scales = _dereq_('./scales'); +var helpers = _dereq_('./helpers'); + +module.exports = { + moduleType: 'component', + name: 'colorscale', + + attributes: _dereq_('./attributes'), + layoutAttributes: _dereq_('./layout_attributes'), + + supplyLayoutDefaults: _dereq_('./layout_defaults'), + handleDefaults: _dereq_('./defaults'), + crossTraceDefaults: _dereq_('./cross_trace_defaults'), + + calc: _dereq_('./calc'), + + // ./scales.js is required in lib/coerce.js ; + // it needs to be a separate module to avoid circular a dependency + scales: scales.scales, + defaultScale: scales.defaultScale, + getScale: scales.get, + isValidScale: scales.isValid, + + hasColorscale: helpers.hasColorscale, + extractOpts: helpers.extractOpts, + extractScale: helpers.extractScale, + flipScale: helpers.flipScale, + makeColorScaleFunc: helpers.makeColorScaleFunc, + makeColorScaleFuncFromTrace: helpers.makeColorScaleFuncFromTrace +}; + +},{"./attributes":162,"./calc":163,"./cross_trace_defaults":164,"./defaults":165,"./helpers":166,"./layout_attributes":168,"./layout_defaults":169,"./scales":170}],168:[function(_dereq_,module,exports){ +'use strict'; + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var colorScaleAttrs = _dereq_('./attributes'); +var scales = _dereq_('./scales').scales; + +var msg = 'Note that `autocolorscale` must be true for this attribute to work.'; + +module.exports = { + editType: 'calc', + + colorscale: { + editType: 'calc', + + sequential: { + valType: 'colorscale', + dflt: scales.Reds, + editType: 'calc', + }, + sequentialminus: { + valType: 'colorscale', + dflt: scales.Blues, + editType: 'calc', + }, + diverging: { + valType: 'colorscale', + dflt: scales.RdBu, + editType: 'calc', + } + }, + + coloraxis: extendFlat({ + // not really a 'subplot' attribute container, + // but this is the flag we use to denote attributes that + // support yaxis, yaxis2, yaxis3, ... counters + _isSubplotObj: true, + editType: 'calc', + }, colorScaleAttrs('', { + colorAttr: 'corresponding trace color array(s)', + noColorAxis: true, + showScaleDflt: true + })) +}; + +},{"../../lib/extend":279,"./attributes":162,"./scales":170}],169:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Template = _dereq_('../../plot_api/plot_template'); + +var colorScaleAttrs = _dereq_('./layout_attributes'); +var colorScaleDefaults = _dereq_('./defaults'); + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt); + } + + coerce('colorscale.sequential'); + coerce('colorscale.sequentialminus'); + coerce('colorscale.diverging'); + + var colorAxes = layoutOut._colorAxes; + var colorAxIn, colorAxOut; + + function coerceAx(attr, dflt) { + return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt); + } + + for(var k in colorAxes) { + var stash = colorAxes[k]; + + if(stash[0]) { + colorAxIn = layoutIn[k] || {}; + colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis'); + colorAxOut._name = k; + colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'}); + } else { + // re-coerce colorscale attributes w/o coloraxis + for(var i = 0; i < stash[2].length; i++) { + stash[2][i](); + } + delete layoutOut._colorAxes[k]; + } + } +}; + +},{"../../lib":285,"../../plot_api/plot_template":320,"./defaults":165,"./layout_attributes":168}],170:[function(_dereq_,module,exports){ +'use strict'; + +var tinycolor = _dereq_('tinycolor2'); + +var scales = { + 'Greys': [ + [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)'] + ], + + 'YlGnBu': [ + [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'], + [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'], + [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'], + [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'], + [1, 'rgb(255,255,217)'] + ], + + 'Greens': [ + [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'], + [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'], + [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'], + [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'], + [1, 'rgb(247,252,245)'] + ], + + 'YlOrRd': [ + [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'], + [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'], + [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'], + [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'], + [1, 'rgb(255,255,204)'] + ], + + 'Bluered': [ + [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)'] + ], + + // modified RdBu based on + // http://www.kennethmoreland.com/color-maps/ + 'RdBu': [ + [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'], + [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'], + [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)'] + ], + + // Scale for non-negative numeric values + 'Reds': [ + [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'], + [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)'] + ], + + // Scale for non-positive numeric values + 'Blues': [ + [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'], + [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'], + [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)'] + ], + + 'Picnic': [ + [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'], + [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'], + [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'], + [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'], + [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'], + [1, 'rgb(255,0,0)'] + ], + + 'Rainbow': [ + [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'], + [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'], + [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'], + [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'], + [1, 'rgb(255,0,0)'] + ], + + 'Portland': [ + [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'], + [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'], + [1, 'rgb(217,30,30)'] + ], + + 'Jet': [ + [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'], + [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'], + [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)'] + ], + + 'Hot': [ + [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'], + [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)'] + ], + + 'Blackbody': [ + [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'], + [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'], + [1, 'rgb(160,200,255)'] + ], + + 'Earth': [ + [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'], + [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'], + [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)'] + ], + + 'Electric': [ + [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'], + [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'], + [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)'] + ], + + 'Viridis': [ + [0, '#440154'], [0.06274509803921569, '#48186a'], + [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'], + [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'], + [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'], + [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'], + [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'], + [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'], + [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'], + [1, '#fde725'] + ], + + 'Cividis': [ + [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'], + [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'], + [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'], + [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'], + [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'], + [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'], + [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'], + [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'], + [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)'] + ] +}; + +var defaultScale = scales.RdBu; + +function getScale(scl, dflt) { + if(!dflt) dflt = defaultScale; + if(!scl) return dflt; + + function parseScale() { + try { + scl = scales[scl] || JSON.parse(scl); + } catch(e) { + scl = dflt; + } + } + + if(typeof scl === 'string') { + parseScale(); + // occasionally scl is double-JSON encoded... + if(typeof scl === 'string') parseScale(); + } + + if(!isValidScaleArray(scl)) return dflt; + return scl; +} + + +function isValidScaleArray(scl) { + var highestVal = 0; + + if(!Array.isArray(scl) || scl.length < 2) return false; + + if(!scl[0] || !scl[scl.length - 1]) return false; + + if(+scl[0][0] !== 0 || +scl[scl.length - 1][0] !== 1) return false; + + for(var i = 0; i < scl.length; i++) { + var si = scl[i]; + + if(si.length !== 2 || +si[0] < highestVal || !tinycolor(si[1]).isValid()) { + return false; + } + + highestVal = +si[0]; + } + + return true; +} + +function isValidScale(scl) { + if(scales[scl] !== undefined) return true; + else return isValidScaleArray(scl); +} + +module.exports = { + scales: scales, + defaultScale: defaultScale, + + get: getScale, + isValid: isValidScale +}; + +},{"tinycolor2":119}],171:[function(_dereq_,module,exports){ +'use strict'; + + +// for automatic alignment on dragging, <1/3 means left align, +// >2/3 means right, and between is center. Pick the right fraction +// based on where you are, and return the fraction corresponding to +// that position on the object +module.exports = function align(v, dv, v0, v1, anchor) { + var vmin = (v - v0) / (v1 - v0); + var vmax = vmin + dv / (v1 - v0); + var vc = (vmin + vmax) / 2; + + // explicitly specified anchor + if(anchor === 'left' || anchor === 'bottom') return vmin; + if(anchor === 'center' || anchor === 'middle') return vc; + if(anchor === 'right' || anchor === 'top') return vmax; + + // automatic based on position + if(vmin < (2 / 3) - vc) return vmin; + if(vmax > (4 / 3) - vc) return vmax; + return vc; +}; + +},{}],172:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + + +// set cursors pointing toward the closest corner/side, +// to indicate alignment +// x and y are 0-1, fractions of the plot area +var cursorset = [ + ['sw-resize', 's-resize', 'se-resize'], + ['w-resize', 'move', 'e-resize'], + ['nw-resize', 'n-resize', 'ne-resize'] +]; + +module.exports = function getCursor(x, y, xanchor, yanchor) { + if(xanchor === 'left') x = 0; + else if(xanchor === 'center') x = 1; + else if(xanchor === 'right') x = 2; + else x = Lib.constrain(Math.floor(x * 3), 0, 2); + + if(yanchor === 'bottom') y = 0; + else if(yanchor === 'middle') y = 1; + else if(yanchor === 'top') y = 2; + else y = Lib.constrain(Math.floor(y * 3), 0, 2); + + return cursorset[y][x]; +}; + +},{"../../lib":285}],173:[function(_dereq_,module,exports){ +'use strict'; + +exports.selectMode = function(dragmode) { + return ( + dragmode === 'lasso' || + dragmode === 'select' + ); +}; + +exports.drawMode = function(dragmode) { + return ( + dragmode === 'drawclosedpath' || + dragmode === 'drawopenpath' || + dragmode === 'drawline' || + dragmode === 'drawrect' || + dragmode === 'drawcircle' + ); +}; + +exports.openMode = function(dragmode) { + return ( + dragmode === 'drawline' || + dragmode === 'drawopenpath' + ); +}; + +exports.rectMode = function(dragmode) { + return ( + dragmode === 'select' || + dragmode === 'drawline' || + dragmode === 'drawrect' || + dragmode === 'drawcircle' + ); +}; + +exports.freeMode = function(dragmode) { + return ( + dragmode === 'lasso' || + dragmode === 'drawclosedpath' || + dragmode === 'drawopenpath' + ); +}; + +exports.selectingOrDrawing = function(dragmode) { + return ( + exports.freeMode(dragmode) || + exports.rectMode(dragmode) + ); +}; + +},{}],174:[function(_dereq_,module,exports){ +'use strict'; + +var mouseOffset = _dereq_('mouse-event-offset'); +var hasHover = _dereq_('has-hover'); +var supportsPassive = _dereq_('has-passive-events'); + +var removeElement = _dereq_('../../lib').removeElement; +var constants = _dereq_('../../plots/cartesian/constants'); + +var dragElement = module.exports = {}; + +dragElement.align = _dereq_('./align'); +dragElement.getCursor = _dereq_('./cursor'); + +var unhover = _dereq_('./unhover'); +dragElement.unhover = unhover.wrapped; +dragElement.unhoverRaw = unhover.raw; + +/** + * Abstracts click & drag interactions + * + * During the interaction, a "coverSlip" element - a transparent + * div covering the whole page - is created, which has two key effects: + * - Lets you drag beyond the boundaries of the plot itself without + * dropping (but if you drag all the way out of the browser window the + * interaction will end) + * - Freezes the cursor: whatever mouse cursor the drag element had when the + * interaction started gets copied to the coverSlip for use until mouseup + * + * If the user executes a drag bigger than MINDRAG, callbacks will fire as: + * prepFn, moveFn (1 or more times), doneFn + * If the user does not drag enough, prepFn and clickFn will fire. + * + * Note: If you cancel contextmenu, clickFn will fire even with a right click + * (unlike native events) so you'll get a `plotly_click` event. Cancel context eg: + * gd.addEventListener('contextmenu', function(e) { e.preventDefault(); }); + * TODO: we should probably turn this into a `config` parameter, so we can fix it + * such that if you *don't* cancel contextmenu, we can prevent partial drags, which + * put you in a weird state. + * + * If the user clicks multiple times quickly, clickFn will fire each time + * but numClicks will increase to help you recognize doubleclicks. + * + * @param {object} options with keys: + * element (required) the DOM element to drag + * prepFn (optional) function(event, startX, startY) + * executed on mousedown + * startX and startY are the clientX and clientY pixel position + * of the mousedown event + * moveFn (optional) function(dx, dy) + * executed on move, ONLY after we've exceeded MINDRAG + * (we keep executing moveFn if you move back to where you started) + * dx and dy are the net pixel offset of the drag, + * dragged is true/false, has the mouse moved enough to + * constitute a drag + * doneFn (optional) function(e) + * executed on mouseup, ONLY if we exceeded MINDRAG (so you can be + * sure that moveFn has been called at least once) + * numClicks is how many clicks we've registered within + * a doubleclick time + * e is the original mouseup event + * clickFn (optional) function(numClicks, e) + * executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn + * has not been called at all) + * numClicks is how many clicks we've registered within + * a doubleclick time + * e is the original mousedown event + * clampFn (optional, function(dx, dy) return [dx2, dy2]) + * Provide custom clamping function for small displacements. + * By default, clamping is done using `minDrag` to x and y displacements + * independently. + */ +dragElement.init = function init(options) { + var gd = options.gd; + var numClicks = 1; + var doubleClickDelay = gd._context.doubleClickDelay; + var element = options.element; + + var startX, + startY, + newMouseDownTime, + cursor, + dragCover, + initialEvent, + initialTarget, + rightClick; + + if(!gd._mouseDownTime) gd._mouseDownTime = 0; + + element.style.pointerEvents = 'all'; + + element.onmousedown = onStart; + + if(!supportsPassive) { + element.ontouchstart = onStart; + } else { + if(element._ontouchstart) { + element.removeEventListener('touchstart', element._ontouchstart); + } + element._ontouchstart = onStart; + element.addEventListener('touchstart', onStart, {passive: false}); + } + + function _clampFn(dx, dy, minDrag) { + if(Math.abs(dx) < minDrag) dx = 0; + if(Math.abs(dy) < minDrag) dy = 0; + return [dx, dy]; + } + + var clampFn = options.clampFn || _clampFn; + + function onStart(e) { + // make dragging and dragged into properties of gd + // so that others can look at and modify them + gd._dragged = false; + gd._dragging = true; + var offset = pointerOffset(e); + startX = offset[0]; + startY = offset[1]; + initialTarget = e.target; + initialEvent = e; + rightClick = e.buttons === 2 || e.ctrlKey; + + // fix Fx.hover for touch events + if(typeof e.clientX === 'undefined' && typeof e.clientY === 'undefined') { + e.clientX = startX; + e.clientY = startY; + } + + newMouseDownTime = (new Date()).getTime(); + if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) { + // in a click train + numClicks += 1; + } else { + // new click train + numClicks = 1; + gd._mouseDownTime = newMouseDownTime; + } + + if(options.prepFn) options.prepFn(e, startX, startY); + + if(hasHover && !rightClick) { + dragCover = coverSlip(); + dragCover.style.cursor = window.getComputedStyle(element).cursor; + } else if(!hasHover) { + // document acts as a dragcover for mobile, bc we can't create dragcover dynamically + dragCover = document; + cursor = window.getComputedStyle(document.documentElement).cursor; + document.documentElement.style.cursor = window.getComputedStyle(element).cursor; + } + + document.addEventListener('mouseup', onDone); + document.addEventListener('touchend', onDone); + + if(options.dragmode !== false) { + e.preventDefault(); + document.addEventListener('mousemove', onMove); + document.addEventListener('touchmove', onMove, {passive: false}); + } + + return; + } + + function onMove(e) { + e.preventDefault(); + + var offset = pointerOffset(e); + var minDrag = options.minDrag || constants.MINDRAG; + var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag); + var dx = dxdy[0]; + var dy = dxdy[1]; + + if(dx || dy) { + gd._dragged = true; + dragElement.unhover(gd, e); + } + + if(gd._dragged && options.moveFn && !rightClick) { + gd._dragdata = { + element: element, + dx: dx, + dy: dy + }; + options.moveFn(dx, dy); + } + + return; + } + + function onDone(e) { + delete gd._dragdata; + + if(options.dragmode !== false) { + e.preventDefault(); + document.removeEventListener('mousemove', onMove); + document.removeEventListener('touchmove', onMove); + } + + document.removeEventListener('mouseup', onDone); + document.removeEventListener('touchend', onDone); + + if(hasHover) { + removeElement(dragCover); + } else if(cursor) { + dragCover.documentElement.style.cursor = cursor; + cursor = null; + } + + if(!gd._dragging) { + gd._dragged = false; + return; + } + gd._dragging = false; + + // don't count as a dblClick unless the mouseUp is also within + // the dblclick delay + if((new Date()).getTime() - gd._mouseDownTime > doubleClickDelay) { + numClicks = Math.max(numClicks - 1, 1); + } + + if(gd._dragged) { + if(options.doneFn) options.doneFn(); + } else { + if(options.clickFn) options.clickFn(numClicks, initialEvent); + + // If we haven't dragged, this should be a click. But because of the + // coverSlip changing the element, the natural system might not generate one, + // so we need to make our own. But right clicks don't normally generate + // click events, only contextmenu events, which happen on mousedown. + if(!rightClick) { + var e2; + + try { + e2 = new MouseEvent('click', e); + } catch(err) { + var offset = pointerOffset(e); + e2 = document.createEvent('MouseEvents'); + e2.initMouseEvent('click', + e.bubbles, e.cancelable, + e.view, e.detail, + e.screenX, e.screenY, + offset[0], offset[1], + e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, + e.button, e.relatedTarget); + } + + initialTarget.dispatchEvent(e2); + } + } + + gd._dragging = false; + gd._dragged = false; + return; + } +}; + +function coverSlip() { + var cover = document.createElement('div'); + + cover.className = 'dragcover'; + var cStyle = cover.style; + cStyle.position = 'fixed'; + cStyle.left = 0; + cStyle.right = 0; + cStyle.top = 0; + cStyle.bottom = 0; + cStyle.zIndex = 999999999; + cStyle.background = 'none'; + + document.body.appendChild(cover); + + return cover; +} + +dragElement.coverSlip = coverSlip; + +function pointerOffset(e) { + return mouseOffset( + e.changedTouches ? e.changedTouches[0] : e, + document.body + ); +} + +},{"../../lib":285,"../../plots/cartesian/constants":338,"./align":171,"./cursor":172,"./unhover":175,"has-hover":62,"has-passive-events":63,"mouse-event-offset":69}],175:[function(_dereq_,module,exports){ +'use strict'; + +var Events = _dereq_('../../lib/events'); +var throttle = _dereq_('../../lib/throttle'); +var getGraphDiv = _dereq_('../../lib/dom').getGraphDiv; + +var hoverConstants = _dereq_('../fx/constants'); + +var unhover = module.exports = {}; + +unhover.wrapped = function(gd, evt, subplot) { + gd = getGraphDiv(gd); + + // Important, clear any queued hovers + if(gd._fullLayout) { + throttle.clear(gd._fullLayout._uid + hoverConstants.HOVERID); + } + + unhover.raw(gd, evt, subplot); +}; + + +// remove hover effects on mouse out, and emit unhover event +unhover.raw = function raw(gd, evt) { + var fullLayout = gd._fullLayout; + var oldhoverdata = gd._hoverdata; + + if(!evt) evt = {}; + if(evt.target && !gd._dragged && + Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) { + return; + } + + fullLayout._hoverlayer.selectAll('g').remove(); + fullLayout._hoverlayer.selectAll('line').remove(); + fullLayout._hoverlayer.selectAll('circle').remove(); + gd._hoverdata = undefined; + + if(evt.target && oldhoverdata) { + gd.emit('plotly_unhover', { + event: evt, + points: oldhoverdata + }); + } +}; + +},{"../../lib/dom":277,"../../lib/events":278,"../../lib/throttle":308,"../fx/constants":189}],176:[function(_dereq_,module,exports){ +'use strict'; + +exports.dash = { + valType: 'string', + // string type usually doesn't take values... this one should really be + // a special type or at least a special coercion function, from the GUI + // you only get these values but elsewhere the user can supply a list of + // dash lengths in px, and it will be honored + values: ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'], + dflt: 'solid', + editType: 'style', +}; + +exports.pattern = { + shape: { + valType: 'enumerated', + values: ['', '/', '\\', 'x', '-', '|', '+', '.'], + dflt: '', + arrayOk: true, + editType: 'style', + }, + fillmode: { + valType: 'enumerated', + values: ['replace', 'overlay'], + dflt: 'replace', + editType: 'style', + }, + bgcolor: { + valType: 'color', + arrayOk: true, + editType: 'style', + }, + fgcolor: { + valType: 'color', + arrayOk: true, + editType: 'style', + }, + fgopacity: { + valType: 'number', + editType: 'style', + min: 0, + max: 1, + }, + size: { + valType: 'number', + min: 0, + dflt: 8, + arrayOk: true, + editType: 'style', + }, + solidity: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.3, + arrayOk: true, + editType: 'style', + }, + editType: 'style', +}; + +},{}],177:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); + +var Registry = _dereq_('../../registry'); +var Color = _dereq_('../color'); +var Colorscale = _dereq_('../colorscale'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); + +var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces'); +var alignment = _dereq_('../../constants/alignment'); +var LINE_SPACING = alignment.LINE_SPACING; +var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM; + +var subTypes = _dereq_('../../traces/scatter/subtypes'); +var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func'); +var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue; + +var drawing = module.exports = {}; + +// ----------------------------------------------------- +// styling functions for plot elements +// ----------------------------------------------------- + +drawing.font = function(s, family, size, color) { + // also allow the form font(s, {family, size, color}) + if(Lib.isPlainObject(family)) { + color = family.color; + size = family.size; + family = family.family; + } + if(family) s.style('font-family', family); + if(size + 1) s.style('font-size', size + 'px'); + if(color) s.call(Color.fill, color); +}; + +/* + * Positioning helpers + * Note: do not use `setPosition` with nodes modified by + * `svgTextUtils.convertToTspans`. Use `svgTextUtils.positionText` + * instead, so that elements get updated to match. + */ +drawing.setPosition = function(s, x, y) { s.attr('x', x).attr('y', y); }; +drawing.setSize = function(s, w, h) { s.attr('width', w).attr('height', h); }; +drawing.setRect = function(s, x, y, w, h) { + s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h); +}; + +/** Translate node + * + * @param {object} d : calcdata point item + * @param {sel} sel : d3 selction of node to translate + * @param {object} xa : corresponding full xaxis object + * @param {object} ya : corresponding full yaxis object + * + * @return {boolean} : + * true if selection got translated + * false if selection could not get translated + */ +drawing.translatePoint = function(d, sel, xa, ya) { + var x = xa.c2p(d.x); + var y = ya.c2p(d.y); + + if(isNumeric(x) && isNumeric(y) && sel.node()) { + // for multiline text this works better + if(sel.node().nodeName === 'text') { + sel.attr('x', x).attr('y', y); + } else { + sel.attr('transform', strTranslate(x, y)); + } + } else { + return false; + } + + return true; +}; + +drawing.translatePoints = function(s, xa, ya) { + s.each(function(d) { + var sel = d3.select(this); + drawing.translatePoint(d, sel, xa, ya); + }); +}; + +drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) { + sel.attr( + 'display', + (xa.isPtWithinRange(d, xcalendar) && ya.isPtWithinRange(d, ycalendar)) ? null : 'none' + ); +}; + +drawing.hideOutsideRangePoints = function(traceGroups, subplot) { + if(!subplot._hasClipOnAxisFalse) return; + + var xa = subplot.xaxis; + var ya = subplot.yaxis; + + traceGroups.each(function(d) { + var trace = d[0].trace; + var xcalendar = trace.xcalendar; + var ycalendar = trace.ycalendar; + var selector = Registry.traceIs(trace, 'bar-like') ? '.bartext' : '.point,.textpoint'; + + traceGroups.selectAll(selector).each(function(d) { + drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar); + }); + }); +}; + +drawing.crispRound = function(gd, lineWidth, dflt) { + // for lines that disable antialiasing we want to + // make sure the width is an integer, and at least 1 if it's nonzero + + if(!lineWidth || !isNumeric(lineWidth)) return dflt || 0; + + // but not for static plots - these don't get antialiased anyway. + if(gd._context.staticPlot) return lineWidth; + + if(lineWidth < 1) return 1; + return Math.round(lineWidth); +}; + +drawing.singleLineStyle = function(d, s, lw, lc, ld) { + s.style('fill', 'none'); + var line = (((d || [])[0] || {}).trace || {}).line || {}; + var lw1 = lw || line.width || 0; + var dash = ld || line.dash || ''; + + Color.stroke(s, lc || line.color); + drawing.dashLine(s, dash, lw1); +}; + +drawing.lineGroupStyle = function(s, lw, lc, ld) { + s.style('fill', 'none') + .each(function(d) { + var line = (((d || [])[0] || {}).trace || {}).line || {}; + var lw1 = lw || line.width || 0; + var dash = ld || line.dash || ''; + + d3.select(this) + .call(Color.stroke, lc || line.color) + .call(drawing.dashLine, dash, lw1); + }); +}; + +drawing.dashLine = function(s, dash, lineWidth) { + lineWidth = +lineWidth || 0; + + dash = drawing.dashStyle(dash, lineWidth); + + s.style({ + 'stroke-dasharray': dash, + 'stroke-width': lineWidth + 'px' + }); +}; + +drawing.dashStyle = function(dash, lineWidth) { + lineWidth = +lineWidth || 1; + var dlw = Math.max(lineWidth, 3); + + if(dash === 'solid') dash = ''; + else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px'; + else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px'; + else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px'; + else if(dash === 'dashdot') { + dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px'; + } else if(dash === 'longdashdot') { + dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px'; + } + // otherwise user wrote the dasharray themselves - leave it be + + return dash; +}; + +// Same as fillGroupStyle, except in this case the selection may be a transition +drawing.singleFillStyle = function(sel) { + var node = d3.select(sel.node()); + var data = node.data(); + var fillcolor = (((data[0] || [])[0] || {}).trace || {}).fillcolor; + if(fillcolor) { + sel.call(Color.fill, fillcolor); + } +}; + +drawing.fillGroupStyle = function(s) { + s.style('stroke-width', 0) + .each(function(d) { + var shape = d3.select(this); + // N.B. 'd' won't be a calcdata item when + // fill !== 'none' on a segment-less and marker-less trace + if(d[0].trace) { + shape.call(Color.fill, d[0].trace.fillcolor); + } + }); +}; + +var SYMBOLDEFS = _dereq_('./symbol_defs'); + +drawing.symbolNames = []; +drawing.symbolFuncs = []; +drawing.symbolNeedLines = {}; +drawing.symbolNoDot = {}; +drawing.symbolNoFill = {}; +drawing.symbolList = []; + +Object.keys(SYMBOLDEFS).forEach(function(k) { + var symDef = SYMBOLDEFS[k]; + var n = symDef.n; + drawing.symbolList.push( + n, + String(n), + k, + + n + 100, + String(n + 100), + k + '-open' + ); + drawing.symbolNames[n] = k; + drawing.symbolFuncs[n] = symDef.f; + + if(symDef.needLine) { + drawing.symbolNeedLines[n] = true; + } + if(symDef.noDot) { + drawing.symbolNoDot[n] = true; + } else { + drawing.symbolList.push( + n + 200, + String(n + 200), + k + '-dot', + + n + 300, + String(n + 300), + k + '-open-dot' + ); + } + if(symDef.noFill) { + drawing.symbolNoFill[n] = true; + } +}); + +var MAXSYMBOL = drawing.symbolNames.length; +// add a dot in the middle of the symbol +var DOTPATH = 'M0,0.5L0.5,0L0,-0.5L-0.5,0Z'; + +drawing.symbolNumber = function(v) { + if(isNumeric(v)) { + v = +v; + } else if(typeof v === 'string') { + var vbase = 0; + if(v.indexOf('-open') > 0) { + vbase = 100; + v = v.replace('-open', ''); + } + if(v.indexOf('-dot') > 0) { + vbase += 200; + v = v.replace('-dot', ''); + } + v = drawing.symbolNames.indexOf(v); + if(v >= 0) { v += vbase; } + } + + return (v % 100 >= MAXSYMBOL || v >= 400) ? + 0 : Math.floor(Math.max(v, 0)); +}; + +function makePointPath(symbolNumber, r) { + var base = symbolNumber % 100; + return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : ''); +} + +var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0}; +var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0}; +var stopFormatter = d3.format('~.1f'); +var gradientInfo = { + radial: {node: 'radialGradient'}, + radialreversed: {node: 'radialGradient', reversed: true}, + horizontal: {node: 'linearGradient', attrs: HORZGRADIENT}, + horizontalreversed: {node: 'linearGradient', attrs: HORZGRADIENT, reversed: true}, + vertical: {node: 'linearGradient', attrs: VERTGRADIENT}, + verticalreversed: {node: 'linearGradient', attrs: VERTGRADIENT, reversed: true} +}; + +/** + * gradient: create and apply a gradient fill + * + * @param {object} sel: d3 selection to apply this gradient to + * You can use `selection.call(Drawing.gradient, ...)` + * @param {DOM element} gd: the graph div `sel` is part of + * @param {string} gradientID: a unique (within this plot) identifier + * for this gradient, so that we don't create unnecessary definitions + * @param {string} type: 'radial', 'horizontal', or 'vertical', optionally with + * 'reversed' at the end. Normally radial goes center to edge, + * horizontal goes right to left, and vertical goes bottom to top + * @param {array} colorscale: as in attribute values, [[fraction, color], ...] + * @param {string} prop: the property to apply to, 'fill' or 'stroke' + */ +drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) { + var len = colorscale.length; + var info = gradientInfo[type]; + var colorStops = new Array(len); + for(var i = 0; i < len; i++) { + if(info.reversed) { + colorStops[len - 1 - i] = [stopFormatter((1 - colorscale[i][0]) * 100), colorscale[i][1]]; + } else { + colorStops[i] = [stopFormatter(colorscale[i][0] * 100), colorscale[i][1]]; + } + } + + var fullLayout = gd._fullLayout; + var fullID = 'g' + fullLayout._uid + '-' + gradientID; + + var gradient = fullLayout._defs.select('.gradients') + .selectAll('#' + fullID) + .data([type + colorStops.join(';')], Lib.identity); + + gradient.exit().remove(); + + gradient.enter() + .append(info.node) + .each(function() { + var el = d3.select(this); + if(info.attrs) el.attr(info.attrs); + + el.attr('id', fullID); + + var stops = el.selectAll('stop') + .data(colorStops); + stops.exit().remove(); + stops.enter().append('stop'); + + stops.each(function(d) { + var tc = tinycolor(d[1]); + d3.select(this).attr({ + offset: d[0] + '%', + 'stop-color': Color.tinyRGB(tc), + 'stop-opacity': tc.getAlpha() + }); + }); + }); + + sel.style(prop, getFullUrl(fullID, gd)) + .style(prop + '-opacity', null); + + var className2query = function(s) { + return '.' + s.attr('class').replace(/\s/g, '.'); + }; + var k = className2query(d3.select(sel.node().parentNode)) + + '>' + className2query(sel); + fullLayout._gradientUrlQueryParts[k] = 1; +}; + +/** + * pattern: create and apply a pattern fill + * + * @param {object} sel: d3 selection to apply this pattern to + * You can use `selection.call(Drawing.pattern, ...)` + * @param {string} calledBy: option to know the caller component + * @param {DOM element} gd: the graph div `sel` is part of + * @param {string} patternID: a unique (within this plot) identifier + * for this pattern, so that we don't create unnecessary definitions + * @param {number} size: size of unit squares for repetition of this pattern + * @param {number} solidity: how solid lines of this pattern are + * @param {string} mcc: color when painted with colorscale + * @param {string} fillmode: fillmode for this pattern + * @param {string} bgcolor: background color for this pattern + * @param {string} fgcolor: foreground color for this pattern + * @param {number} fgopacity: foreground opacity for this pattern + */ +drawing.pattern = function(sel, calledBy, gd, patternID, shape, size, solidity, mcc, fillmode, bgcolor, fgcolor, fgopacity) { + var isLegend = calledBy === 'legend'; + + if(mcc) { + if(fillmode === 'overlay') { + bgcolor = mcc; + fgcolor = Color.contrast(bgcolor); + } else { + bgcolor = undefined; + fgcolor = mcc; + } + } + + var fullLayout = gd._fullLayout; + var fullID = 'p' + fullLayout._uid + '-' + patternID; + var width, height; + + // linear interpolation + var linearFn = function(x, x0, x1, y0, y1) { + return y0 + (y1 - y0) * (x - x0) / (x1 - x0); + }; + + var path, linewidth, radius; + var patternTag; + var patternAttrs = {}; + switch(shape) { + case '/': + width = size * Math.sqrt(2); + height = size * Math.sqrt(2); + path = 'M-' + (width / 4) + ',' + (height / 4) + 'l' + (width / 2) + ',-' + (height / 2) + + 'M0,' + height + 'L' + width + ',0' + + 'M' + (width / 4 * 3) + ',' + (height / 4 * 5) + 'l' + (width / 2) + ',-' + (height / 2); + linewidth = solidity * size; + patternTag = 'path'; + patternAttrs = { + 'd': path, + 'opacity': fgopacity, + 'stroke': fgcolor, + 'stroke-width': linewidth + 'px' + }; + break; + case '\\': + width = size * Math.sqrt(2); + height = size * Math.sqrt(2); + path = 'M' + (width / 4 * 3) + ',-' + (height / 4) + 'l' + (width / 2) + ',' + (height / 2) + + 'M0,0L' + width + ',' + height + + 'M-' + (width / 4) + ',' + (height / 4 * 3) + 'l' + (width / 2) + ',' + (height / 2); + linewidth = solidity * size; + patternTag = 'path'; + patternAttrs = { + 'd': path, + 'opacity': fgopacity, + 'stroke': fgcolor, + 'stroke-width': linewidth + 'px' + }; + break; + case 'x': + width = size * Math.sqrt(2); + height = size * Math.sqrt(2); + path = 'M-' + (width / 4) + ',' + (height / 4) + 'l' + (width / 2) + ',-' + (height / 2) + + 'M0,' + height + 'L' + width + ',0' + + 'M' + (width / 4 * 3) + ',' + (height / 4 * 5) + 'l' + (width / 2) + ',-' + (height / 2) + + 'M' + (width / 4 * 3) + ',-' + (height / 4) + 'l' + (width / 2) + ',' + (height / 2) + + 'M0,0L' + width + ',' + height + + 'M-' + (width / 4) + ',' + (height / 4 * 3) + 'l' + (width / 2) + ',' + (height / 2); + linewidth = size - size * Math.sqrt(1.0 - solidity); + patternTag = 'path'; + patternAttrs = { + 'd': path, + 'opacity': fgopacity, + 'stroke': fgcolor, + 'stroke-width': linewidth + 'px' + }; + break; + case '|': + width = size; + height = size; + patternTag = 'path'; + path = 'M' + (width / 2) + ',0L' + (width / 2) + ',' + height; + linewidth = solidity * size; + patternTag = 'path'; + patternAttrs = { + 'd': path, + 'opacity': fgopacity, + 'stroke': fgcolor, + 'stroke-width': linewidth + 'px' + }; + break; + case '-': + width = size; + height = size; + patternTag = 'path'; + path = 'M0,' + (height / 2) + 'L' + width + ',' + (height / 2); + linewidth = solidity * size; + patternTag = 'path'; + patternAttrs = { + 'd': path, + 'opacity': fgopacity, + 'stroke': fgcolor, + 'stroke-width': linewidth + 'px' + }; + break; + case '+': + width = size; + height = size; + patternTag = 'path'; + path = 'M' + (width / 2) + ',0L' + (width / 2) + ',' + height + + 'M0,' + (height / 2) + 'L' + width + ',' + (height / 2); + linewidth = size - size * Math.sqrt(1.0 - solidity); + patternTag = 'path'; + patternAttrs = { + 'd': path, + 'opacity': fgopacity, + 'stroke': fgcolor, + 'stroke-width': linewidth + 'px' + }; + break; + case '.': + width = size; + height = size; + if(solidity < Math.PI / 4) { + radius = Math.sqrt(solidity * size * size / Math.PI); + } else { + radius = linearFn(solidity, Math.PI / 4, 1.0, size / 2, size / Math.sqrt(2)); + } + patternTag = 'circle'; + patternAttrs = { + 'cx': width / 2, + 'cy': height / 2, + 'r': radius, + 'opacity': fgopacity, + 'fill': fgcolor + }; + break; + } + + var str = [ + shape || 'noSh', + bgcolor || 'noBg', + fgcolor || 'noFg', + size, + solidity + ].join(';'); + + var pattern = fullLayout._defs.select('.patterns') + .selectAll('#' + fullID) + .data([str], Lib.identity); + + pattern.exit().remove(); + + pattern.enter() + .append('pattern') + .each(function() { + var el = d3.select(this); + + el.attr({ + 'id': fullID, + 'width': width + 'px', + 'height': height + 'px', + 'patternUnits': 'userSpaceOnUse', + // for legends scale down patterns just a bit so that default size (i.e 8) nicely fit in small icons + 'patternTransform': isLegend ? 'scale(0.8)' : '' + }); + + if(bgcolor) { + var rects = el.selectAll('rect').data([0]); + rects.exit().remove(); + rects.enter() + .append('rect') + .attr({ + 'width': width + 'px', + 'height': height + 'px', + 'fill': bgcolor + }); + } + + var patterns = el.selectAll(patternTag).data([0]); + patterns.exit().remove(); + patterns.enter() + .append(patternTag) + .attr(patternAttrs); + }); + + sel.style('fill', getFullUrl(fullID, gd)) + .style('fill-opacity', null); + + sel.classed('pattern_filled', true); + var className2query = function(s) { + return '.' + s.attr('class').replace(/\s/g, '.'); + }; + var k = className2query(d3.select(sel.node().parentNode)) + '>.pattern_filled'; + fullLayout._patternUrlQueryParts[k] = 1; +}; + +/* + * Make the gradients container and clear out any previous gradients. + * We never collect all the gradients we need in one place, + * so we can't ever remove gradients that have stopped being useful, + * except all at once before a full redraw. + * The upside of this is arbitrary points can share gradient defs + */ +drawing.initGradients = function(gd) { + var fullLayout = gd._fullLayout; + + var gradientsGroup = Lib.ensureSingle(fullLayout._defs, 'g', 'gradients'); + gradientsGroup.selectAll('linearGradient,radialGradient').remove(); + + // initialize stash of query parts filled in Drawing.gradient, + // used to fix URL strings during image exports + fullLayout._gradientUrlQueryParts = {}; +}; + +drawing.initPatterns = function(gd) { + var fullLayout = gd._fullLayout; + + var patternsGroup = Lib.ensureSingle(fullLayout._defs, 'g', 'patterns'); + patternsGroup.selectAll('pattern').remove(); + + // initialize stash of query parts filled in Drawing.pattern, + // used to fix URL strings during image exports + fullLayout._patternUrlQueryParts = {}; +}; + +drawing.getPatternAttr = function(mp, i, dflt) { + if(mp && Lib.isArrayOrTypedArray(mp)) { + return i < mp.length ? mp[i] : dflt; + } + return mp; +}; + +drawing.pointStyle = function(s, trace, gd) { + if(!s.size()) return; + + var fns = drawing.makePointStyleFns(trace); + + s.each(function(d) { + drawing.singlePointStyle(d, d3.select(this), trace, fns, gd); + }); +}; + +drawing.singlePointStyle = function(d, sel, trace, fns, gd) { + var marker = trace.marker; + var markerLine = marker.line; + + sel.style('opacity', + fns.selectedOpacityFn ? fns.selectedOpacityFn(d) : + (d.mo === undefined ? marker.opacity : d.mo) + ); + + if(fns.ms2mrc) { + var r; + + // handle multi-trace graph edit case + if(d.ms === 'various' || marker.size === 'various') { + r = 3; + } else { + r = fns.ms2mrc(d.ms); + } + + // store the calculated size so hover can use it + d.mrc = r; + + if(fns.selectedSizeFn) { + r = d.mrc = fns.selectedSizeFn(d); + } + + // turn the symbol into a sanitized number + var x = drawing.symbolNumber(d.mx || marker.symbol) || 0; + + // save if this marker is open + // because that impacts how to handle colors + d.om = x % 200 >= 100; + + sel.attr('d', makePointPath(x, r)); + } + + var perPointGradient = false; + var fillColor, lineColor, lineWidth; + + // 'so' is suspected outliers, for box plots + if(d.so) { + lineWidth = markerLine.outlierwidth; + lineColor = markerLine.outliercolor; + fillColor = marker.outliercolor; + } else { + var markerLineWidth = (markerLine || {}).width; + + lineWidth = ( + d.mlw + 1 || + markerLineWidth + 1 || + // TODO: we need the latter for legends... can we get rid of it? + (d.trace ? (d.trace.marker.line || {}).width : 0) + 1 + ) - 1 || 0; + + if('mlc' in d) lineColor = d.mlcc = fns.lineScale(d.mlc); + // weird case: array wasn't long enough to apply to every point + else if(Lib.isArrayOrTypedArray(markerLine.color)) lineColor = Color.defaultLine; + else lineColor = markerLine.color; + + if(Lib.isArrayOrTypedArray(marker.color)) { + fillColor = Color.defaultLine; + perPointGradient = true; + } + + if('mc' in d) { + fillColor = d.mcc = fns.markerScale(d.mc); + } else { + fillColor = marker.color || 'rgba(0,0,0,0)'; + } + + if(fns.selectedColorFn) { + fillColor = fns.selectedColorFn(d); + } + } + + if(d.om) { + // open markers can't have zero linewidth, default to 1px, + // and use fill color as stroke color + sel.call(Color.stroke, fillColor) + .style({ + 'stroke-width': (lineWidth || 1) + 'px', + fill: 'none' + }); + } else { + sel.style('stroke-width', (d.isBlank ? 0 : lineWidth) + 'px'); + + var markerGradient = marker.gradient; + + var gradientType = d.mgt; + if(gradientType) perPointGradient = true; + else gradientType = markerGradient && markerGradient.type; + + // for legend - arrays will propagate through here, but we don't need + // to treat it as per-point. + if(Lib.isArrayOrTypedArray(gradientType)) { + gradientType = gradientType[0]; + if(!gradientInfo[gradientType]) gradientType = 0; + } + + var markerPattern = marker.pattern; + var patternShape = markerPattern && drawing.getPatternAttr(markerPattern.shape, d.i, ''); + + if(gradientType && gradientType !== 'none') { + var gradientColor = d.mgc; + if(gradientColor) perPointGradient = true; + else gradientColor = markerGradient.color; + + var gradientID = trace.uid; + if(perPointGradient) gradientID += '-' + d.i; + + drawing.gradient(sel, gd, gradientID, gradientType, + [[0, gradientColor], [1, fillColor]], 'fill'); + } else if(patternShape) { + var patternBGColor = drawing.getPatternAttr(markerPattern.bgcolor, d.i, null); + var patternFGColor = drawing.getPatternAttr(markerPattern.fgcolor, d.i, null); + var patternFGOpacity = markerPattern.fgopacity; + var patternSize = drawing.getPatternAttr(markerPattern.size, d.i, 8); + var patternSolidity = drawing.getPatternAttr(markerPattern.solidity, d.i, 0.3); + var perPointPattern = d.mcc || + Lib.isArrayOrTypedArray(markerPattern.shape) || + Lib.isArrayOrTypedArray(markerPattern.bgcolor) || + Lib.isArrayOrTypedArray(markerPattern.size) || + Lib.isArrayOrTypedArray(markerPattern.solidity); + + var patternID = trace.uid; + if(perPointPattern) patternID += '-' + d.i; + + drawing.pattern( + sel, 'point', gd, patternID, + patternShape, patternSize, patternSolidity, + d.mcc, markerPattern.fillmode, + patternBGColor, patternFGColor, patternFGOpacity + ); + } else { + Color.fill(sel, fillColor); + } + + if(lineWidth) { + Color.stroke(sel, lineColor); + } + } +}; + +drawing.makePointStyleFns = function(trace) { + var out = {}; + var marker = trace.marker; + + // allow array marker and marker line colors to be + // scaled by given max and min to colorscales + out.markerScale = drawing.tryColorscale(marker, ''); + out.lineScale = drawing.tryColorscale(marker, 'line'); + + if(Registry.traceIs(trace, 'symbols')) { + out.ms2mrc = subTypes.isBubble(trace) ? + makeBubbleSizeFn(trace) : + function() { return (marker.size || 6) / 2; }; + } + + if(trace.selectedpoints) { + Lib.extendFlat(out, drawing.makeSelectedPointStyleFns(trace)); + } + + return out; +}; + +drawing.makeSelectedPointStyleFns = function(trace) { + var out = {}; + + var selectedAttrs = trace.selected || {}; + var unselectedAttrs = trace.unselected || {}; + + var marker = trace.marker || {}; + var selectedMarker = selectedAttrs.marker || {}; + var unselectedMarker = unselectedAttrs.marker || {}; + + var mo = marker.opacity; + var smo = selectedMarker.opacity; + var usmo = unselectedMarker.opacity; + var smoIsDefined = smo !== undefined; + var usmoIsDefined = usmo !== undefined; + + if(Lib.isArrayOrTypedArray(mo) || smoIsDefined || usmoIsDefined) { + out.selectedOpacityFn = function(d) { + var base = d.mo === undefined ? marker.opacity : d.mo; + + if(d.selected) { + return smoIsDefined ? smo : base; + } else { + return usmoIsDefined ? usmo : DESELECTDIM * base; + } + }; + } + + var mc = marker.color; + var smc = selectedMarker.color; + var usmc = unselectedMarker.color; + + if(smc || usmc) { + out.selectedColorFn = function(d) { + var base = d.mcc || mc; + + if(d.selected) { + return smc || base; + } else { + return usmc || base; + } + }; + } + + var ms = marker.size; + var sms = selectedMarker.size; + var usms = unselectedMarker.size; + var smsIsDefined = sms !== undefined; + var usmsIsDefined = usms !== undefined; + + if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) { + out.selectedSizeFn = function(d) { + var base = d.mrc || ms / 2; + + if(d.selected) { + return smsIsDefined ? sms / 2 : base; + } else { + return usmsIsDefined ? usms / 2 : base; + } + }; + } + + return out; +}; + +drawing.makeSelectedTextStyleFns = function(trace) { + var out = {}; + + var selectedAttrs = trace.selected || {}; + var unselectedAttrs = trace.unselected || {}; + + var textFont = trace.textfont || {}; + var selectedTextFont = selectedAttrs.textfont || {}; + var unselectedTextFont = unselectedAttrs.textfont || {}; + + var tc = textFont.color; + var stc = selectedTextFont.color; + var utc = unselectedTextFont.color; + + out.selectedTextColorFn = function(d) { + var base = d.tc || tc; + + if(d.selected) { + return stc || base; + } else { + if(utc) return utc; + else return stc ? base : Color.addOpacity(base, DESELECTDIM); + } + }; + + return out; +}; + +drawing.selectedPointStyle = function(s, trace) { + if(!s.size() || !trace.selectedpoints) return; + + var fns = drawing.makeSelectedPointStyleFns(trace); + var marker = trace.marker || {}; + var seq = []; + + if(fns.selectedOpacityFn) { + seq.push(function(pt, d) { + pt.style('opacity', fns.selectedOpacityFn(d)); + }); + } + + if(fns.selectedColorFn) { + seq.push(function(pt, d) { + Color.fill(pt, fns.selectedColorFn(d)); + }); + } + + if(fns.selectedSizeFn) { + seq.push(function(pt, d) { + var mx = d.mx || marker.symbol || 0; + var mrc2 = fns.selectedSizeFn(d); + + pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2)); + + // save for Drawing.selectedTextStyle + d.mrc2 = mrc2; + }); + } + + if(seq.length) { + s.each(function(d) { + var pt = d3.select(this); + for(var i = 0; i < seq.length; i++) { + seq[i](pt, d); + } + }); + } +}; + +drawing.tryColorscale = function(marker, prefix) { + var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker; + + if(cont) { + var colorArray = cont.color; + if((cont.colorscale || cont._colorAx) && Lib.isArrayOrTypedArray(colorArray)) { + return Colorscale.makeColorScaleFuncFromTrace(cont); + } + } + return Lib.identity; +}; + +var TEXTOFFSETSIGN = { + start: 1, end: -1, middle: 0, bottom: 1, top: -1 +}; + +function textPointPosition(s, textPosition, fontSize, markerRadius) { + var group = d3.select(s.node().parentNode); + + var v = textPosition.indexOf('top') !== -1 ? + 'top' : + textPosition.indexOf('bottom') !== -1 ? 'bottom' : 'middle'; + var h = textPosition.indexOf('left') !== -1 ? + 'end' : + textPosition.indexOf('right') !== -1 ? 'start' : 'middle'; + + // if markers are shown, offset a little more than + // the nominal marker size + // ie 2/1.6 * nominal, bcs some markers are a bit bigger + var r = markerRadius ? markerRadius / 0.8 + 1 : 0; + + var numLines = (svgTextUtils.lineCount(s) - 1) * LINE_SPACING + 1; + var dx = TEXTOFFSETSIGN[h] * r; + var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r + + (TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2; + + // fix the overall text group position + s.attr('text-anchor', h); + group.attr('transform', strTranslate(dx, dy)); +} + +function extracTextFontSize(d, trace) { + var fontSize = d.ts || trace.textfont.size; + return (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0; +} + +// draw text at points +drawing.textPointStyle = function(s, trace, gd) { + if(!s.size()) return; + + var selectedTextColorFn; + if(trace.selectedpoints) { + var fns = drawing.makeSelectedTextStyleFns(trace); + selectedTextColorFn = fns.selectedTextColorFn; + } + + var texttemplate = trace.texttemplate; + var fullLayout = gd._fullLayout; + + s.each(function(d) { + var p = d3.select(this); + + var text = texttemplate ? + Lib.extractOption(d, trace, 'txt', 'texttemplate') : + Lib.extractOption(d, trace, 'tx', 'text'); + + if(!text && text !== 0) { + p.remove(); + return; + } + + if(texttemplate) { + var fn = trace._module.formatLabels; + var labels = fn ? fn(d, trace, fullLayout) : {}; + var pointValues = {}; + appendArrayPointValue(pointValues, trace, d.i); + var meta = trace._meta || {}; + text = Lib.texttemplateString(text, labels, fullLayout._d3locale, pointValues, d, meta); + } + + var pos = d.tp || trace.textposition; + var fontSize = extracTextFontSize(d, trace); + var fontColor = selectedTextColorFn ? + selectedTextColorFn(d) : + (d.tc || trace.textfont.color); + + p.call(drawing.font, + d.tf || trace.textfont.family, + fontSize, + fontColor) + .text(text) + .call(svgTextUtils.convertToTspans, gd) + .call(textPointPosition, pos, fontSize, d.mrc); + }); +}; + +drawing.selectedTextStyle = function(s, trace) { + if(!s.size() || !trace.selectedpoints) return; + + var fns = drawing.makeSelectedTextStyleFns(trace); + + s.each(function(d) { + var tx = d3.select(this); + var tc = fns.selectedTextColorFn(d); + var tp = d.tp || trace.textposition; + var fontSize = extracTextFontSize(d, trace); + + Color.fill(tx, tc); + textPointPosition(tx, tp, fontSize, d.mrc2 || d.mrc); + }); +}; + +// generalized Catmull-Rom splines, per +// http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf +var CatmullRomExp = 0.5; +drawing.smoothopen = function(pts, smoothness) { + if(pts.length < 3) { return 'M' + pts.join('L');} + var path = 'M' + pts[0]; + var tangents = []; + var i; + for(i = 1; i < pts.length - 1; i++) { + tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness)); + } + path += 'Q' + tangents[0][0] + ' ' + pts[1]; + for(i = 2; i < pts.length - 1; i++) { + path += 'C' + tangents[i - 2][1] + ' ' + tangents[i - 1][0] + ' ' + pts[i]; + } + path += 'Q' + tangents[pts.length - 3][1] + ' ' + pts[pts.length - 1]; + return path; +}; + +drawing.smoothclosed = function(pts, smoothness) { + if(pts.length < 3) { return 'M' + pts.join('L') + 'Z'; } + var path = 'M' + pts[0]; + var pLast = pts.length - 1; + var tangents = [makeTangent(pts[pLast], pts[0], pts[1], smoothness)]; + var i; + for(i = 1; i < pLast; i++) { + tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness)); + } + tangents.push( + makeTangent(pts[pLast - 1], pts[pLast], pts[0], smoothness) + ); + + for(i = 1; i <= pLast; i++) { + path += 'C' + tangents[i - 1][1] + ' ' + tangents[i][0] + ' ' + pts[i]; + } + path += 'C' + tangents[pLast][1] + ' ' + tangents[0][0] + ' ' + pts[0] + 'Z'; + return path; +}; + +function makeTangent(prevpt, thispt, nextpt, smoothness) { + var d1x = prevpt[0] - thispt[0]; + var d1y = prevpt[1] - thispt[1]; + var d2x = nextpt[0] - thispt[0]; + var d2y = nextpt[1] - thispt[1]; + var d1a = Math.pow(d1x * d1x + d1y * d1y, CatmullRomExp / 2); + var d2a = Math.pow(d2x * d2x + d2y * d2y, CatmullRomExp / 2); + var numx = (d2a * d2a * d1x - d1a * d1a * d2x) * smoothness; + var numy = (d2a * d2a * d1y - d1a * d1a * d2y) * smoothness; + var denom1 = 3 * d2a * (d1a + d2a); + var denom2 = 3 * d1a * (d1a + d2a); + return [ + [ + d3.round(thispt[0] + (denom1 && numx / denom1), 2), + d3.round(thispt[1] + (denom1 && numy / denom1), 2) + ], [ + d3.round(thispt[0] - (denom2 && numx / denom2), 2), + d3.round(thispt[1] - (denom2 && numy / denom2), 2) + ] + ]; +} + +// step paths - returns a generator function for paths +// with the given step shape +var STEPPATH = { + hv: function(p0, p1) { + return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2); + }, + vh: function(p0, p1) { + return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2); + }, + hvh: function(p0, p1) { + return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' + + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2); + }, + vhv: function(p0, p1) { + return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' + + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2); + } +}; +var STEPLINEAR = function(p0, p1) { + return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2); +}; +drawing.steps = function(shape) { + var onestep = STEPPATH[shape] || STEPLINEAR; + return function(pts) { + var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2); + for(var i = 1; i < pts.length; i++) { + path += onestep(pts[i - 1], pts[i]); + } + return path; + }; +}; + +// off-screen svg render testing element, shared by the whole page +// uses the id 'js-plotly-tester' and stores it in drawing.tester +drawing.makeTester = function() { + var tester = Lib.ensureSingleById(d3.select('body'), 'svg', 'js-plotly-tester', function(s) { + s.attr(xmlnsNamespaces.svgAttrs) + .style({ + position: 'absolute', + left: '-10000px', + top: '-10000px', + width: '9000px', + height: '9000px', + 'z-index': '1' + }); + }); + + // browsers differ on how they describe the bounding rect of + // the svg if its contents spill over... so make a 1x1px + // reference point we can measure off of. + var testref = Lib.ensureSingle(tester, 'path', 'js-reference-point', function(s) { + s.attr('d', 'M0,0H1V1H0Z') + .style({ + 'stroke-width': 0, + fill: 'black' + }); + }); + + drawing.tester = tester; + drawing.testref = testref; +}; + +/* + * use our offscreen tester to get a clientRect for an element, + * in a reference frame where it isn't translated (or transformed) and + * its anchor point is at (0,0) + * always returns a copy of the bbox, so the caller can modify it safely + * + * @param {SVGElement} node: the element to measure. If possible this should be + * a or MathJax element that's already passed through + * `convertToTspans` because in that case we can cache the results, but it's + * possible to pass in any svg element. + * + * @param {boolean} inTester: is this element already in `drawing.tester`? + * If you are measuring a dummy element, rather than one you really intend + * to use on the plot, making it in `drawing.tester` in the first place + * allows us to test faster because it cuts out cloning and appending it. + * + * @param {string} hash: for internal use only, if we already know the cache key + * for this element beforehand. + * + * @return {object}: a plain object containing the width, height, left, right, + * top, and bottom of `node` + */ +drawing.savedBBoxes = {}; +var savedBBoxesCount = 0; +var maxSavedBBoxes = 10000; + +drawing.bBox = function(node, inTester, hash) { + /* + * Cache elements we've already measured so we don't have to + * remeasure the same thing many times + * We have a few bBox callers though who pass a node larger than + * a or a MathJax , such as an axis group containing many labels. + * These will not generate a hash (unless we figure out an appropriate + * hash key for them) and thus we will not hash them. + */ + if(!hash) hash = nodeHash(node); + var out; + if(hash) { + out = drawing.savedBBoxes[hash]; + if(out) return Lib.extendFlat({}, out); + } else if(node.childNodes.length === 1) { + /* + * If we have only one child element, which is itself hashable, make + * a new hash from this element plus its x,y,transform + * These bounding boxes *include* x,y,transform - mostly for use by + * callers trying to avoid overlaps (ie titles) + */ + var innerNode = node.childNodes[0]; + + hash = nodeHash(innerNode); + if(hash) { + var x = +innerNode.getAttribute('x') || 0; + var y = +innerNode.getAttribute('y') || 0; + var transform = innerNode.getAttribute('transform'); + + if(!transform) { + // in this case, just varying x and y, don't bother caching + // the final bBox because the alteration is quick. + var innerBB = drawing.bBox(innerNode, false, hash); + if(x) { + innerBB.left += x; + innerBB.right += x; + } + if(y) { + innerBB.top += y; + innerBB.bottom += y; + } + return innerBB; + } + /* + * else we have a transform - rather than make a complicated + * (and error-prone and probably slow) transform parser/calculator, + * just continue on calculating the boundingClientRect of the group + * and use the new composite hash to cache it. + * That said, `innerNode.transform.baseVal` is an array of + * `SVGTransform` objects, that *do* seem to have a nice matrix + * multiplication interface that we could use to avoid making + * another getBoundingClientRect call... + */ + hash += '~' + x + '~' + y + '~' + transform; + + out = drawing.savedBBoxes[hash]; + if(out) return Lib.extendFlat({}, out); + } + } + var testNode, tester; + if(inTester) { + testNode = node; + } else { + tester = drawing.tester.node(); + + // copy the node to test into the tester + testNode = node.cloneNode(true); + tester.appendChild(testNode); + } + + // standardize its position (and newline tspans if any) + d3.select(testNode) + .attr('transform', null) + .call(svgTextUtils.positionText, 0, 0); + + var testRect = testNode.getBoundingClientRect(); + var refRect = drawing.testref + .node() + .getBoundingClientRect(); + + if(!inTester) tester.removeChild(testNode); + + var bb = { + height: testRect.height, + width: testRect.width, + left: testRect.left - refRect.left, + top: testRect.top - refRect.top, + right: testRect.right - refRect.left, + bottom: testRect.bottom - refRect.top + }; + + // make sure we don't have too many saved boxes, + // or a long session could overload on memory + // by saving boxes for long-gone elements + if(savedBBoxesCount >= maxSavedBBoxes) { + drawing.savedBBoxes = {}; + savedBBoxesCount = 0; + } + + // cache this bbox + if(hash) drawing.savedBBoxes[hash] = bb; + savedBBoxesCount++; + + return Lib.extendFlat({}, bb); +}; + +// capture everything about a node (at least in our usage) that +// impacts its bounding box, given that bBox clears x, y, and transform +function nodeHash(node) { + var inputText = node.getAttribute('data-unformatted'); + if(inputText === null) return; + return inputText + + node.getAttribute('data-math') + + node.getAttribute('text-anchor') + + node.getAttribute('style'); +} + +/** + * Set clipPath URL in a way that work for all situations. + * + * In details, graphs on pages with HTML tags need to prepend + * the clip path ids with the page's base url EXCEPT during toImage exports. + * + * @param {d3 selection} s : node to add clip-path attribute + * @param {string} localId : local clip-path (w/o base url) id + * @param {DOM element || object} gd + * - context._baseUrl {string} + * - context._exportedPlot {boolean} + */ +drawing.setClipUrl = function(s, localId, gd) { + s.attr('clip-path', getFullUrl(localId, gd)); +}; + +function getFullUrl(localId, gd) { + if(!localId) return null; + + var context = gd._context; + var baseUrl = context._exportedPlot ? '' : (context._baseUrl || ''); + return baseUrl ? + 'url(\'' + baseUrl + '#' + localId + '\')' : + 'url(#' + localId + ')'; +} + +drawing.getTranslate = function(element) { + // Note the separator [^\d] between x and y in this regex + // We generally use ',' but IE will convert it to ' ' + var re = /.*\btranslate\((-?\d*\.?\d*)[^-\d]*(-?\d*\.?\d*)[^\d].*/; + var getter = element.attr ? 'attr' : 'getAttribute'; + var transform = element[getter]('transform') || ''; + + var translate = transform.replace(re, function(match, p1, p2) { + return [p1, p2].join(' '); + }) + .split(' '); + + return { + x: +translate[0] || 0, + y: +translate[1] || 0 + }; +}; + +drawing.setTranslate = function(element, x, y) { + var re = /(\btranslate\(.*?\);?)/; + var getter = element.attr ? 'attr' : 'getAttribute'; + var setter = element.attr ? 'attr' : 'setAttribute'; + var transform = element[getter]('transform') || ''; + + x = x || 0; + y = y || 0; + + transform = transform.replace(re, '').trim(); + transform += strTranslate(x, y); + transform = transform.trim(); + + element[setter]('transform', transform); + + return transform; +}; + +drawing.getScale = function(element) { + var re = /.*\bscale\((\d*\.?\d*)[^\d]*(\d*\.?\d*)[^\d].*/; + var getter = element.attr ? 'attr' : 'getAttribute'; + var transform = element[getter]('transform') || ''; + + var translate = transform.replace(re, function(match, p1, p2) { + return [p1, p2].join(' '); + }) + .split(' '); + + return { + x: +translate[0] || 1, + y: +translate[1] || 1 + }; +}; + +drawing.setScale = function(element, x, y) { + var re = /(\bscale\(.*?\);?)/; + var getter = element.attr ? 'attr' : 'getAttribute'; + var setter = element.attr ? 'attr' : 'setAttribute'; + var transform = element[getter]('transform') || ''; + + x = x || 1; + y = y || 1; + + transform = transform.replace(re, '').trim(); + transform += 'scale(' + x + ',' + y + ')'; + transform = transform.trim(); + + element[setter]('transform', transform); + + return transform; +}; + +var SCALE_RE = /\s*sc.*/; + +drawing.setPointGroupScale = function(selection, xScale, yScale) { + xScale = xScale || 1; + yScale = yScale || 1; + + if(!selection) return; + + // The same scale transform for every point: + var scale = (xScale === 1 && yScale === 1) ? + '' : + 'scale(' + xScale + ',' + yScale + ')'; + + selection.each(function() { + var t = (this.getAttribute('transform') || '').replace(SCALE_RE, ''); + t += scale; + t = t.trim(); + this.setAttribute('transform', t); + }); +}; + +var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/; + +drawing.setTextPointsScale = function(selection, xScale, yScale) { + if(!selection) return; + + selection.each(function() { + var transforms; + var el = d3.select(this); + var text = el.select('text'); + + if(!text.node()) return; + + var x = parseFloat(text.attr('x') || 0); + var y = parseFloat(text.attr('y') || 0); + + var existingTransform = (el.attr('transform') || '').match(TEXT_POINT_LAST_TRANSLATION_RE); + + if(xScale === 1 && yScale === 1) { + transforms = []; + } else { + transforms = [ + strTranslate(x, y), + 'scale(' + xScale + ',' + yScale + ')', + strTranslate(-x, -y), + ]; + } + + if(existingTransform) { + transforms.push(existingTransform); + } + + el.attr('transform', transforms.join('')); + }); +}; + +},{"../../components/fx/helpers":191,"../../constants/alignment":260,"../../constants/interactions":264,"../../constants/xmlns_namespaces":266,"../../lib":285,"../../lib/svg_text_utils":307,"../../registry":373,"../../traces/scatter/make_bubble_size_func":511,"../../traces/scatter/subtypes":519,"../color":155,"../colorscale":167,"./symbol_defs":178,"@plotly/d3":20,"fast-isnumeric":31,"tinycolor2":119}],178:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +/** Marker symbol definitions + * users can specify markers either by number or name + * add 100 (or '-open') and you get an open marker + * open markers have no fill and use line color as the stroke color + * add 200 (or '-dot') and you get a dot in the middle + * add both and you get both + */ + +module.exports = { + circle: { + n: 0, + f: function(r) { + var rs = d3.round(r, 2); + return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + } + }, + square: { + n: 1, + f: function(r) { + var rs = d3.round(r, 2); + return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'; + } + }, + diamond: { + n: 2, + f: function(r) { + var rd = d3.round(r * 1.3, 2); + return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z'; + } + }, + cross: { + n: 3, + f: function(r) { + var rc = d3.round(r * 0.4, 2); + var rc2 = d3.round(r * 1.2, 2); + return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc + + 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 + + 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z'; + } + }, + x: { + n: 4, + f: function(r) { + var rx = d3.round(r * 0.8 / Math.sqrt(2), 2); + var ne = 'l' + rx + ',' + rx; + var se = 'l' + rx + ',-' + rx; + var sw = 'l-' + rx + ',-' + rx; + var nw = 'l-' + rx + ',' + rx; + return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z'; + } + }, + 'triangle-up': { + n: 5, + f: function(r) { + var rt = d3.round(r * 2 / Math.sqrt(3), 2); + var r2 = d3.round(r / 2, 2); + var rs = d3.round(r, 2); + return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z'; + } + }, + 'triangle-down': { + n: 6, + f: function(r) { + var rt = d3.round(r * 2 / Math.sqrt(3), 2); + var r2 = d3.round(r / 2, 2); + var rs = d3.round(r, 2); + return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z'; + } + }, + 'triangle-left': { + n: 7, + f: function(r) { + var rt = d3.round(r * 2 / Math.sqrt(3), 2); + var r2 = d3.round(r / 2, 2); + var rs = d3.round(r, 2); + return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z'; + } + }, + 'triangle-right': { + n: 8, + f: function(r) { + var rt = d3.round(r * 2 / Math.sqrt(3), 2); + var r2 = d3.round(r / 2, 2); + var rs = d3.round(r, 2); + return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z'; + } + }, + 'triangle-ne': { + n: 9, + f: function(r) { + var r1 = d3.round(r * 0.6, 2); + var r2 = d3.round(r * 1.2, 2); + return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z'; + } + }, + 'triangle-se': { + n: 10, + f: function(r) { + var r1 = d3.round(r * 0.6, 2); + var r2 = d3.round(r * 1.2, 2); + return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z'; + } + }, + 'triangle-sw': { + n: 11, + f: function(r) { + var r1 = d3.round(r * 0.6, 2); + var r2 = d3.round(r * 1.2, 2); + return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z'; + } + }, + 'triangle-nw': { + n: 12, + f: function(r) { + var r1 = d3.round(r * 0.6, 2); + var r2 = d3.round(r * 1.2, 2); + return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z'; + } + }, + pentagon: { + n: 13, + f: function(r) { + var x1 = d3.round(r * 0.951, 2); + var x2 = d3.round(r * 0.588, 2); + var y0 = d3.round(-r, 2); + var y1 = d3.round(r * -0.309, 2); + var y2 = d3.round(r * 0.809, 2); + return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 + + 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z'; + } + }, + hexagon: { + n: 14, + f: function(r) { + var y0 = d3.round(r, 2); + var y1 = d3.round(r / 2, 2); + var x = d3.round(r * Math.sqrt(3) / 2, 2); + return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 + + 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z'; + } + }, + hexagon2: { + n: 15, + f: function(r) { + var x0 = d3.round(r, 2); + var x1 = d3.round(r / 2, 2); + var y = d3.round(r * Math.sqrt(3) / 2, 2); + return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 + + ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z'; + } + }, + octagon: { + n: 16, + f: function(r) { + var a = d3.round(r * 0.924, 2); + var b = d3.round(r * 0.383, 2); + return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b + + 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z'; + } + }, + star: { + n: 17, + f: function(r) { + var rs = r * 1.4; + var x1 = d3.round(rs * 0.225, 2); + var x2 = d3.round(rs * 0.951, 2); + var x3 = d3.round(rs * 0.363, 2); + var x4 = d3.round(rs * 0.588, 2); + var y0 = d3.round(-rs, 2); + var y1 = d3.round(rs * -0.309, 2); + var y3 = d3.round(rs * 0.118, 2); + var y4 = d3.round(rs * 0.809, 2); + var y5 = d3.round(rs * 0.382, 2); + return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 + + 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 + + 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 + + 'L0,' + y0 + 'Z'; + } + }, + hexagram: { + n: 18, + f: function(r) { + var y = d3.round(r * 0.66, 2); + var x1 = d3.round(r * 0.38, 2); + var x2 = d3.round(r * 0.76, 2); + return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 + + 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 + + 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 + + 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z'; + } + }, + 'star-triangle-up': { + n: 19, + f: function(r) { + var x = d3.round(r * Math.sqrt(3) * 0.8, 2); + var y1 = d3.round(r * 0.8, 2); + var y2 = d3.round(r * 1.6, 2); + var rc = d3.round(r * 4, 2); + var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; + return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 + + aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z'; + } + }, + 'star-triangle-down': { + n: 20, + f: function(r) { + var x = d3.round(r * Math.sqrt(3) * 0.8, 2); + var y1 = d3.round(r * 0.8, 2); + var y2 = d3.round(r * 1.6, 2); + var rc = d3.round(r * 4, 2); + var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; + return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 + + aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z'; + } + }, + 'star-square': { + n: 21, + f: function(r) { + var rp = d3.round(r * 1.1, 2); + var rc = d3.round(r * 2, 2); + var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; + return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp + + aPart + rp + ',' + rp + aPart + rp + ',-' + rp + + aPart + '-' + rp + ',-' + rp + 'Z'; + } + }, + 'star-diamond': { + n: 22, + f: function(r) { + var rp = d3.round(r * 1.4, 2); + var rc = d3.round(r * 1.9, 2); + var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 '; + return 'M-' + rp + ',0' + aPart + '0,' + rp + + aPart + rp + ',0' + aPart + '0,-' + rp + + aPart + '-' + rp + ',0' + 'Z'; + } + }, + 'diamond-tall': { + n: 23, + f: function(r) { + var x = d3.round(r * 0.7, 2); + var y = d3.round(r * 1.4, 2); + return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z'; + } + }, + 'diamond-wide': { + n: 24, + f: function(r) { + var x = d3.round(r * 1.4, 2); + var y = d3.round(r * 0.7, 2); + return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z'; + } + }, + hourglass: { + n: 25, + f: function(r) { + var rs = d3.round(r, 2); + return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z'; + }, + noDot: true + }, + bowtie: { + n: 26, + f: function(r) { + var rs = d3.round(r, 2); + return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z'; + }, + noDot: true + }, + 'circle-cross': { + n: 27, + f: function(r) { + var rs = d3.round(r, 2); + return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + + 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + }, + needLine: true, + noDot: true + }, + 'circle-x': { + n: 28, + f: function(r) { + var rs = d3.round(r, 2); + var rc = d3.round(r / Math.sqrt(2), 2); + return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc + + 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc + + 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs + + 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z'; + }, + needLine: true, + noDot: true + }, + 'square-cross': { + n: 29, + f: function(r) { + var rs = d3.round(r, 2); + return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs + + 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'; + }, + needLine: true, + noDot: true + }, + 'square-x': { + n: 30, + f: function(r) { + var rs = d3.round(r, 2); + return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs + + 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs + + 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z'; + }, + needLine: true, + noDot: true + }, + 'diamond-cross': { + n: 31, + f: function(r) { + var rd = d3.round(r * 1.3, 2); + return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' + + 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd; + }, + needLine: true, + noDot: true + }, + 'diamond-x': { + n: 32, + f: function(r) { + var rd = d3.round(r * 1.3, 2); + var r2 = d3.round(r * 0.65, 2); + return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' + + 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 + + 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2; + }, + needLine: true, + noDot: true + }, + 'cross-thin': { + n: 33, + f: function(r) { + var rc = d3.round(r * 1.4, 2); + return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'x-thin': { + n: 34, + f: function(r) { + var rx = d3.round(r, 2); + return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx + + 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx; + }, + needLine: true, + noDot: true, + noFill: true + }, + asterisk: { + n: 35, + f: function(r) { + var rc = d3.round(r * 1.2, 2); + var rs = d3.round(r * 0.85, 2); + return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc + + 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs + + 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs; + }, + needLine: true, + noDot: true, + noFill: true + }, + hash: { + n: 36, + f: function(r) { + var r1 = d3.round(r / 2, 2); + var r2 = d3.round(r, 2); + return 'M' + r1 + ',' + r2 + 'V-' + r2 + + 'm-' + r2 + ',0V' + r2 + + 'M' + r2 + ',' + r1 + 'H-' + r2 + + 'm0,-' + r2 + 'H' + r2; + }, + needLine: true, + noFill: true + }, + 'y-up': { + n: 37, + f: function(r) { + var x = d3.round(r * 1.2, 2); + var y0 = d3.round(r * 1.6, 2); + var y1 = d3.round(r * 0.8, 2); + return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0'; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'y-down': { + n: 38, + f: function(r) { + var x = d3.round(r * 1.2, 2); + var y0 = d3.round(r * 1.6, 2); + var y1 = d3.round(r * 0.8, 2); + return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0'; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'y-left': { + n: 39, + f: function(r) { + var y = d3.round(r * 1.2, 2); + var x0 = d3.round(r * 1.6, 2); + var x1 = d3.round(r * 0.8, 2); + return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0'; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'y-right': { + n: 40, + f: function(r) { + var y = d3.round(r * 1.2, 2); + var x0 = d3.round(r * 1.6, 2); + var x1 = d3.round(r * 0.8, 2); + return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0'; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'line-ew': { + n: 41, + f: function(r) { + var rc = d3.round(r * 1.4, 2); + return 'M' + rc + ',0H-' + rc; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'line-ns': { + n: 42, + f: function(r) { + var rc = d3.round(r * 1.4, 2); + return 'M0,' + rc + 'V-' + rc; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'line-ne': { + n: 43, + f: function(r) { + var rx = d3.round(r, 2); + return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'line-nw': { + n: 44, + f: function(r) { + var rx = d3.round(r, 2); + return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx; + }, + needLine: true, + noDot: true, + noFill: true + }, + 'arrow-up': { + n: 45, + f: function(r) { + var rx = d3.round(r, 2); + var ry = d3.round(r * 2, 2); + return 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'; + }, + noDot: true + }, + 'arrow-down': { + n: 46, + f: function(r) { + var rx = d3.round(r, 2); + var ry = d3.round(r * 2, 2); + return 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'; + }, + noDot: true + }, + 'arrow-left': { + n: 47, + f: function(r) { + var rx = d3.round(r * 2, 2); + var ry = d3.round(r, 2); + return 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'; + }, + noDot: true + }, + 'arrow-right': { + n: 48, + f: function(r) { + var rx = d3.round(r * 2, 2); + var ry = d3.round(r, 2); + return 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'; + }, + noDot: true + }, + 'arrow-bar-up': { + n: 49, + f: function(r) { + var rx = d3.round(r, 2); + var ry = d3.round(r * 2, 2); + return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z'; + }, + needLine: true, + noDot: true + }, + 'arrow-bar-down': { + n: 50, + f: function(r) { + var rx = d3.round(r, 2); + var ry = d3.round(r * 2, 2); + return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z'; + }, + needLine: true, + noDot: true + }, + 'arrow-bar-left': { + n: 51, + f: function(r) { + var rx = d3.round(r * 2, 2); + var ry = d3.round(r, 2); + return 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z'; + }, + needLine: true, + noDot: true + }, + 'arrow-bar-right': { + n: 52, + f: function(r) { + var rx = d3.round(r * 2, 2); + var ry = d3.round(r, 2); + return 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z'; + }, + needLine: true, + noDot: true + } +}; + +},{"@plotly/d3":20}],179:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + visible: { + valType: 'boolean', + editType: 'calc', + }, + type: { + valType: 'enumerated', + values: ['percent', 'constant', 'sqrt', 'data'], + editType: 'calc', + }, + symmetric: { + valType: 'boolean', + editType: 'calc', + }, + array: { + valType: 'data_array', + editType: 'calc', + }, + arrayminus: { + valType: 'data_array', + editType: 'calc', + }, + value: { + valType: 'number', + min: 0, + dflt: 10, + editType: 'calc', + }, + valueminus: { + valType: 'number', + min: 0, + dflt: 10, + editType: 'calc', + }, + traceref: { + valType: 'integer', + min: 0, + dflt: 0, + editType: 'style' + }, + tracerefminus: { + valType: 'integer', + min: 0, + dflt: 0, + editType: 'style' + }, + copy_ystyle: { + valType: 'boolean', + editType: 'plot' + }, + copy_zstyle: { + valType: 'boolean', + editType: 'style' + }, + color: { + valType: 'color', + editType: 'style', + }, + thickness: { + valType: 'number', + min: 0, + dflt: 2, + editType: 'style', + }, + width: { + valType: 'number', + min: 0, + editType: 'plot', + }, + editType: 'calc', + + _deprecated: { + opacity: { + valType: 'number', + editType: 'style', + } + } +}; + +},{}],180:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Registry = _dereq_('../../registry'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); + +var makeComputeError = _dereq_('./compute_error'); + +module.exports = function calc(gd) { + var calcdata = gd.calcdata; + + for(var i = 0; i < calcdata.length; i++) { + var calcTrace = calcdata[i]; + var trace = calcTrace[0].trace; + + if(trace.visible === true && Registry.traceIs(trace, 'errorBarsOK')) { + var xa = Axes.getFromId(gd, trace.xaxis); + var ya = Axes.getFromId(gd, trace.yaxis); + calcOneAxis(calcTrace, trace, xa, 'x'); + calcOneAxis(calcTrace, trace, ya, 'y'); + } + } +}; + +function calcOneAxis(calcTrace, trace, axis, coord) { + var opts = trace['error_' + coord] || {}; + var isVisible = (opts.visible && ['linear', 'log'].indexOf(axis.type) !== -1); + var vals = []; + + if(!isVisible) return; + + var computeError = makeComputeError(opts); + + for(var i = 0; i < calcTrace.length; i++) { + var calcPt = calcTrace[i]; + + var iIn = calcPt.i; + + // for types that don't include `i` in each calcdata point + if(iIn === undefined) iIn = i; + + // for stacked area inserted points + // TODO: errorbars have been tested cursorily with stacked area, + // but not thoroughly. It's not even really clear what you want to do: + // Should it just be calculated based on that trace's size data? + // Should you add errors from below in quadrature? + // And what about normalization, where in principle the errors shrink + // again when you get up to the top end? + // One option would be to forbid errorbars with stacking until we + // decide how to handle these questions. + else if(iIn === null) continue; + + var calcCoord = calcPt[coord]; + + if(!isNumeric(axis.c2l(calcCoord))) continue; + + var errors = computeError(calcCoord, iIn); + if(isNumeric(errors[0]) && isNumeric(errors[1])) { + var shoe = calcPt[coord + 's'] = calcCoord - errors[0]; + var hat = calcPt[coord + 'h'] = calcCoord + errors[1]; + vals.push(shoe, hat); + } + } + + var axId = axis._id; + var baseExtremes = trace._extremes[axId]; + var extremes = Axes.findExtremes( + axis, + vals, + Lib.extendFlat({tozero: baseExtremes.opts.tozero}, {padded: true}) + ); + baseExtremes.min = baseExtremes.min.concat(extremes.min); + baseExtremes.max = baseExtremes.max.concat(extremes.max); +} + +},{"../../lib":285,"../../plots/cartesian/axes":331,"../../registry":373,"./compute_error":181,"fast-isnumeric":31}],181:[function(_dereq_,module,exports){ +'use strict'; + + +/** + * Error bar computing function generator + * + * N.B. The generated function does not clean the dataPt entries. Non-numeric + * entries result in undefined error magnitudes. + * + * @param {object} opts error bar attributes + * + * @return {function} : + * @param {numeric} dataPt data point from where to compute the error magnitude + * @param {number} index index of dataPt in its corresponding data array + * @return {array} + * - error[0] : error magnitude in the negative direction + * - error[1] : " " " " positive " + */ +module.exports = function makeComputeError(opts) { + var type = opts.type; + var symmetric = opts.symmetric; + + if(type === 'data') { + var array = opts.array || []; + + if(symmetric) { + return function computeError(dataPt, index) { + var val = +(array[index]); + return [val, val]; + }; + } else { + var arrayminus = opts.arrayminus || []; + return function computeError(dataPt, index) { + var val = +array[index]; + var valMinus = +arrayminus[index]; + // in case one is present and the other is missing, fill in 0 + // so we still see the present one. Mostly useful during manual + // data entry. + if(!isNaN(val) || !isNaN(valMinus)) { + return [valMinus || 0, val || 0]; + } + return [NaN, NaN]; + }; + } + } else { + var computeErrorValue = makeComputeErrorValue(type, opts.value); + var computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus); + + if(symmetric || opts.valueminus === undefined) { + return function computeError(dataPt) { + var val = computeErrorValue(dataPt); + return [val, val]; + }; + } else { + return function computeError(dataPt) { + return [ + computeErrorValueMinus(dataPt), + computeErrorValue(dataPt) + ]; + }; + } + } +}; + +/** + * Compute error bar magnitude (for all types except data) + * + * @param {string} type error bar type + * @param {numeric} value error bar value + * + * @return {function} : + * @param {numeric} dataPt + */ +function makeComputeErrorValue(type, value) { + if(type === 'percent') { + return function(dataPt) { + return Math.abs(dataPt * value / 100); + }; + } + if(type === 'constant') { + return function() { + return Math.abs(value); + }; + } + if(type === 'sqrt') { + return function(dataPt) { + return Math.sqrt(Math.abs(dataPt)); + }; + } +} + +},{}],182:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Template = _dereq_('../../plot_api/plot_template'); + +var attributes = _dereq_('./attributes'); + + +module.exports = function(traceIn, traceOut, defaultColor, opts) { + var objName = 'error_' + opts.axis; + var containerOut = Template.newContainer(traceOut, objName); + var containerIn = traceIn[objName] || {}; + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + var hasErrorBars = ( + containerIn.array !== undefined || + containerIn.value !== undefined || + containerIn.type === 'sqrt' + ); + + var visible = coerce('visible', hasErrorBars); + + if(visible === false) return; + + var type = coerce('type', 'array' in containerIn ? 'data' : 'percent'); + var symmetric = true; + + if(type !== 'sqrt') { + symmetric = coerce('symmetric', + !((type === 'data' ? 'arrayminus' : 'valueminus') in containerIn)); + } + + if(type === 'data') { + coerce('array'); + coerce('traceref'); + if(!symmetric) { + coerce('arrayminus'); + coerce('tracerefminus'); + } + } else if(type === 'percent' || type === 'constant') { + coerce('value'); + if(!symmetric) coerce('valueminus'); + } + + var copyAttr = 'copy_' + opts.inherit + 'style'; + if(opts.inherit) { + var inheritObj = traceOut['error_' + opts.inherit]; + if((inheritObj || {}).visible) { + coerce(copyAttr, !(containerIn.color || + isNumeric(containerIn.thickness) || + isNumeric(containerIn.width))); + } + } + if(!opts.inherit || !containerOut[copyAttr]) { + coerce('color', defaultColor); + coerce('thickness'); + coerce('width', Registry.traceIs(traceOut, 'gl3d') ? 0 : 4); + } +}; + +},{"../../lib":285,"../../plot_api/plot_template":320,"../../registry":373,"./attributes":179,"fast-isnumeric":31}],183:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; + +var attributes = _dereq_('./attributes'); + +var xyAttrs = { + error_x: Lib.extendFlat({}, attributes), + error_y: Lib.extendFlat({}, attributes) +}; +delete xyAttrs.error_x.copy_zstyle; +delete xyAttrs.error_y.copy_zstyle; +delete xyAttrs.error_y.copy_ystyle; + +var xyzAttrs = { + error_x: Lib.extendFlat({}, attributes), + error_y: Lib.extendFlat({}, attributes), + error_z: Lib.extendFlat({}, attributes) +}; +delete xyzAttrs.error_x.copy_ystyle; +delete xyzAttrs.error_y.copy_ystyle; +delete xyzAttrs.error_z.copy_ystyle; +delete xyzAttrs.error_z.copy_zstyle; + +module.exports = { + moduleType: 'component', + name: 'errorbars', + + schema: { + traces: { + scatter: xyAttrs, + bar: xyAttrs, + histogram: xyAttrs, + scatter3d: overrideAll(xyzAttrs, 'calc', 'nested'), + scattergl: overrideAll(xyAttrs, 'calc', 'nested') + } + }, + + supplyDefaults: _dereq_('./defaults'), + + calc: _dereq_('./calc'), + makeComputeError: _dereq_('./compute_error'), + + plot: _dereq_('./plot'), + style: _dereq_('./style'), + hoverInfo: hoverInfo +}; + +function hoverInfo(calcPoint, trace, hoverPoint) { + if((trace.error_y || {}).visible) { + hoverPoint.yerr = calcPoint.yh - calcPoint.y; + if(!trace.error_y.symmetric) hoverPoint.yerrneg = calcPoint.y - calcPoint.ys; + } + if((trace.error_x || {}).visible) { + hoverPoint.xerr = calcPoint.xh - calcPoint.x; + if(!trace.error_x.symmetric) hoverPoint.xerrneg = calcPoint.x - calcPoint.xs; + } +} + +},{"../../lib":285,"../../plot_api/edit_types":313,"./attributes":179,"./calc":180,"./compute_error":181,"./defaults":182,"./plot":184,"./style":185}],184:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Drawing = _dereq_('../drawing'); +var subTypes = _dereq_('../../traces/scatter/subtypes'); + +module.exports = function plot(gd, traces, plotinfo, transitionOpts) { + var isNew; + + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var hasAnimation = transitionOpts && transitionOpts.duration > 0; + + traces.each(function(d) { + var trace = d[0].trace; + // || {} is in case the trace (specifically scatterternary) + // doesn't support error bars at all, but does go through + // the scatter.plot mechanics, which calls ErrorBars.plot + // internally + var xObj = trace.error_x || {}; + var yObj = trace.error_y || {}; + + var keyFunc; + + if(trace.ids) { + keyFunc = function(d) {return d.id;}; + } + + var sparse = ( + subTypes.hasMarkers(trace) && + trace.marker.maxdisplayed > 0 + ); + + if(!yObj.visible && !xObj.visible) d = []; + + var errorbars = d3.select(this).selectAll('g.errorbar') + .data(d, keyFunc); + + errorbars.exit().remove(); + + if(!d.length) return; + + if(!xObj.visible) errorbars.selectAll('path.xerror').remove(); + if(!yObj.visible) errorbars.selectAll('path.yerror').remove(); + + errorbars.style('opacity', 1); + + var enter = errorbars.enter().append('g') + .classed('errorbar', true); + + if(hasAnimation) { + enter.style('opacity', 0).transition() + .duration(transitionOpts.duration) + .style('opacity', 1); + } + + Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd); + + errorbars.each(function(d) { + var errorbar = d3.select(this); + var coords = errorCoords(d, xa, ya); + + if(sparse && !d.vis) return; + + var path; + + var yerror = errorbar.select('path.yerror'); + if(yObj.visible && isNumeric(coords.x) && + isNumeric(coords.yh) && + isNumeric(coords.ys)) { + var yw = yObj.width; + + path = 'M' + (coords.x - yw) + ',' + + coords.yh + 'h' + (2 * yw) + // hat + 'm-' + yw + ',0V' + coords.ys; // bar + + + if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe + + isNew = !yerror.size(); + + if(isNew) { + yerror = errorbar.append('path') + .style('vector-effect', 'non-scaling-stroke') + .classed('yerror', true); + } else if(hasAnimation) { + yerror = yerror + .transition() + .duration(transitionOpts.duration) + .ease(transitionOpts.easing); + } + + yerror.attr('d', path); + } else yerror.remove(); + + var xerror = errorbar.select('path.xerror'); + if(xObj.visible && isNumeric(coords.y) && + isNumeric(coords.xh) && + isNumeric(coords.xs)) { + var xw = (xObj.copy_ystyle ? yObj : xObj).width; + + path = 'M' + coords.xh + ',' + + (coords.y - xw) + 'v' + (2 * xw) + // hat + 'm0,-' + xw + 'H' + coords.xs; // bar + + if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe + + isNew = !xerror.size(); + + if(isNew) { + xerror = errorbar.append('path') + .style('vector-effect', 'non-scaling-stroke') + .classed('xerror', true); + } else if(hasAnimation) { + xerror = xerror + .transition() + .duration(transitionOpts.duration) + .ease(transitionOpts.easing); + } + + xerror.attr('d', path); + } else xerror.remove(); + }); + }); +}; + +// compute the coordinates of the error-bar objects +function errorCoords(d, xa, ya) { + var out = { + x: xa.c2p(d.x), + y: ya.c2p(d.y) + }; + + // calculate the error bar size and hat and shoe locations + if(d.yh !== undefined) { + out.yh = ya.c2p(d.yh); + out.ys = ya.c2p(d.ys); + + // if the shoes go off-scale (ie log scale, error bars past zero) + // clip the bar and hide the shoes + if(!isNumeric(out.ys)) { + out.noYS = true; + out.ys = ya.c2p(d.ys, true); + } + } + + if(d.xh !== undefined) { + out.xh = xa.c2p(d.xh); + out.xs = xa.c2p(d.xs); + + if(!isNumeric(out.xs)) { + out.noXS = true; + out.xs = xa.c2p(d.xs, true); + } + } + + return out; +} + +},{"../../traces/scatter/subtypes":519,"../drawing":177,"@plotly/d3":20,"fast-isnumeric":31}],185:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Color = _dereq_('../color'); + + +module.exports = function style(traces) { + traces.each(function(d) { + var trace = d[0].trace; + var yObj = trace.error_y || {}; + var xObj = trace.error_x || {}; + + var s = d3.select(this); + + s.selectAll('path.yerror') + .style('stroke-width', yObj.thickness + 'px') + .call(Color.stroke, yObj.color); + + if(xObj.copy_ystyle) xObj = yObj; + + s.selectAll('path.xerror') + .style('stroke-width', xObj.thickness + 'px') + .call(Color.stroke, xObj.color); + }); +}; + +},{"../color":155,"@plotly/d3":20}],186:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('../../plots/font_attributes'); +var hoverLabelAttrs = _dereq_('./layout_attributes').hoverlabel; +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = { + hoverlabel: { + bgcolor: extendFlat({}, hoverLabelAttrs.bgcolor, { + arrayOk: true, + }), + bordercolor: extendFlat({}, hoverLabelAttrs.bordercolor, { + arrayOk: true, + }), + font: fontAttrs({ + arrayOk: true, + editType: 'none', + }), + align: extendFlat({}, hoverLabelAttrs.align, {arrayOk: true}), + namelength: extendFlat({}, hoverLabelAttrs.namelength, {arrayOk: true}), + editType: 'none' + } +}; + +},{"../../lib/extend":279,"../../plots/font_attributes":360,"./layout_attributes":196}],187:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); + +module.exports = function calc(gd) { + var calcdata = gd.calcdata; + var fullLayout = gd._fullLayout; + + function makeCoerceHoverInfo(trace) { + return function(val) { + return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout); + }; + } + + for(var i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var trace = cd[0].trace; + + // don't include hover calc fields for pie traces + // as calcdata items might be sorted by value and + // won't match the data array order. + if(Registry.traceIs(trace, 'pie-like')) continue; + + var fillFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.fillArray; + + fillFn(trace.hoverinfo, cd, 'hi', makeCoerceHoverInfo(trace)); + + if(trace.hovertemplate) fillFn(trace.hovertemplate, cd, 'ht'); + + if(!trace.hoverlabel) continue; + + fillFn(trace.hoverlabel.bgcolor, cd, 'hbg'); + fillFn(trace.hoverlabel.bordercolor, cd, 'hbc'); + fillFn(trace.hoverlabel.font.size, cd, 'hts'); + fillFn(trace.hoverlabel.font.color, cd, 'htc'); + fillFn(trace.hoverlabel.font.family, cd, 'htf'); + fillFn(trace.hoverlabel.namelength, cd, 'hnl'); + fillFn(trace.hoverlabel.align, cd, 'hta'); + } +}; + +function paste(traceAttr, cd, cdAttr, fn) { + fn = fn || Lib.identity; + + if(Array.isArray(traceAttr)) { + cd[0][cdAttr] = fn(traceAttr); + } +} + +},{"../../lib":285,"../../registry":373}],188:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var hover = _dereq_('./hover').hover; + +module.exports = function click(gd, evt, subplot) { + var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata); + + // fallback to fail-safe in case the plot type's hover method doesn't pass the subplot. + // Ternary, for example, didn't, but it was caught because tested. + if(subplot !== undefined) { + // The true flag at the end causes it to re-run the hover computation to figure out *which* + // point is being clicked. Without this, clicking is somewhat unreliable. + hover(gd, evt, subplot, true); + } + + function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); } + + if(gd._hoverdata && evt && evt.target) { + if(annotationsDone && annotationsDone.then) { + annotationsDone.then(emitClick); + } else emitClick(); + + // why do we get a double event without this??? + if(evt.stopImmediatePropagation) evt.stopImmediatePropagation(); + } +}; + +},{"../../registry":373,"./hover":192}],189:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + // hover labels for multiple horizontal bars get tilted by this angle + YANGLE: 60, + + // size and display constants for hover text + + // pixel size of hover arrows + HOVERARROWSIZE: 6, + // pixels padding around text + HOVERTEXTPAD: 3, + // hover font + HOVERFONTSIZE: 13, + HOVERFONT: 'Arial, sans-serif', + + // minimum time (msec) between hover calls + HOVERMINTIME: 50, + + // ID suffix (with fullLayout._uid) for hover events in the throttle cache + HOVERID: '-hover' +}; + +},{}],190:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var attributes = _dereq_('./attributes'); +var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults'); + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var opts = Lib.extendFlat({}, layout.hoverlabel); + if(traceOut.hovertemplate) opts.namelength = -1; + + handleHoverLabelDefaults(traceIn, traceOut, coerce, opts); +}; + +},{"../../lib":285,"./attributes":186,"./hoverlabel_defaults":193}],191:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +// look for either subplot or xaxis and yaxis attributes +// does not handle splom case +exports.getSubplot = function(trace) { + return trace.subplot || (trace.xaxis + trace.yaxis) || trace.geo; +}; + +// is trace in given list of subplots? +// does handle splom case +exports.isTraceInSubplots = function(trace, subplots) { + if(trace.type === 'splom') { + var xaxes = trace.xaxes || []; + var yaxes = trace.yaxes || []; + for(var i = 0; i < xaxes.length; i++) { + for(var j = 0; j < yaxes.length; j++) { + if(subplots.indexOf(xaxes[i] + yaxes[j]) !== -1) { + return true; + } + } + } + return false; + } + + return subplots.indexOf(exports.getSubplot(trace)) !== -1; +}; + +// convenience functions for mapping all relevant axes +exports.flat = function(subplots, v) { + var out = new Array(subplots.length); + for(var i = 0; i < subplots.length; i++) { + out[i] = v; + } + return out; +}; + +exports.p2c = function(axArray, v) { + var out = new Array(axArray.length); + for(var i = 0; i < axArray.length; i++) { + out[i] = axArray[i].p2c(v); + } + return out; +}; + +exports.getDistanceFunction = function(mode, dx, dy, dxy) { + if(mode === 'closest') return dxy || exports.quadrature(dx, dy); + return mode.charAt(0) === 'x' ? dx : dy; +}; + +exports.getClosest = function(cd, distfn, pointData) { + // do we already have a point number? (array mode only) + if(pointData.index !== false) { + if(pointData.index >= 0 && pointData.index < cd.length) { + pointData.distance = 0; + } else pointData.index = false; + } else { + // apply the distance function to each data point + // this is the longest loop... if this bogs down, we may need + // to create pre-sorted data (by x or y), not sure how to + // do this for 'closest' + for(var i = 0; i < cd.length; i++) { + var newDistance = distfn(cd[i]); + if(newDistance <= pointData.distance) { + pointData.index = i; + pointData.distance = newDistance; + } + } + } + return pointData; +}; + +/* + * pseudo-distance function for hover effects on areas: inside the region + * distance is finite (`passVal`), outside it's Infinity. + * + * @param {number} v0: signed difference between the current position and the left edge + * @param {number} v1: signed difference between the current position and the right edge + * @param {number} passVal: the value to return on success + */ +exports.inbox = function(v0, v1, passVal) { + return (v0 * v1 < 0 || v0 === 0) ? passVal : Infinity; +}; + +exports.quadrature = function(dx, dy) { + return function(di) { + var x = dx(di); + var y = dy(di); + return Math.sqrt(x * x + y * y); + }; +}; + +/** Fill event data point object for hover and selection. + * Invokes _module.eventData if present. + * + * N.B. note that point 'index' corresponds to input data array index + * whereas 'number' is its post-transform version. + * + * If the hovered/selected pt corresponds to an multiple input points + * (e.g. for histogram and transformed traces), 'pointNumbers` and 'pointIndices' + * are include in the event data. + * + * @param {object} pt + * @param {object} trace + * @param {object} cd + * @return {object} + */ +exports.makeEventData = function(pt, trace, cd) { + // hover uses 'index', select uses 'pointNumber' + var pointNumber = 'index' in pt ? pt.index : pt.pointNumber; + + var out = { + data: trace._input, + fullData: trace, + curveNumber: trace.index, + pointNumber: pointNumber + }; + + if(trace._indexToPoints) { + var pointIndices = trace._indexToPoints[pointNumber]; + + if(pointIndices.length === 1) { + out.pointIndex = pointIndices[0]; + } else { + out.pointIndices = pointIndices; + } + } else { + out.pointIndex = pointNumber; + } + + if(trace._module.eventData) { + out = trace._module.eventData(out, pt, trace, cd, pointNumber); + } else { + if('xVal' in pt) out.x = pt.xVal; + else if('x' in pt) out.x = pt.x; + + if('yVal' in pt) out.y = pt.yVal; + else if('y' in pt) out.y = pt.y; + + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal; + } + + exports.appendArrayPointValue(out, trace, pointNumber); + + return out; +}; + +/** Appends values inside array attributes corresponding to given point number + * + * @param {object} pointData : point data object (gets mutated here) + * @param {object} trace : full trace object + * @param {number|Array(number)} pointNumber : point number. May be a length-2 array + * [row, col] to dig into 2D arrays + */ +exports.appendArrayPointValue = function(pointData, trace, pointNumber) { + var arrayAttrs = trace._arrayAttrs; + + if(!arrayAttrs) { + return; + } + + for(var i = 0; i < arrayAttrs.length; i++) { + var astr = arrayAttrs[i]; + var key = getPointKey(astr); + + if(pointData[key] === undefined) { + var val = Lib.nestedProperty(trace, astr).get(); + var pointVal = getPointData(val, pointNumber); + + if(pointVal !== undefined) pointData[key] = pointVal; + } + } +}; + +/** + * Appends values inside array attributes corresponding to given point number array + * For use when pointData references a plot entity that arose (or potentially arose) + * from multiple points in the input data + * + * @param {object} pointData : point data object (gets mutated here) + * @param {object} trace : full trace object + * @param {Array(number)|Array(Array(number))} pointNumbers : Array of point numbers. + * Each entry in the array may itself be a length-2 array [row, col] to dig into 2D arrays + */ +exports.appendArrayMultiPointValues = function(pointData, trace, pointNumbers) { + var arrayAttrs = trace._arrayAttrs; + + if(!arrayAttrs) { + return; + } + + for(var i = 0; i < arrayAttrs.length; i++) { + var astr = arrayAttrs[i]; + var key = getPointKey(astr); + + if(pointData[key] === undefined) { + var val = Lib.nestedProperty(trace, astr).get(); + var keyVal = new Array(pointNumbers.length); + + for(var j = 0; j < pointNumbers.length; j++) { + keyVal[j] = getPointData(val, pointNumbers[j]); + } + pointData[key] = keyVal; + } + } +}; + +var pointKeyMap = { + ids: 'id', + locations: 'location', + labels: 'label', + values: 'value', + 'marker.colors': 'color', + parents: 'parent' +}; + +function getPointKey(astr) { + return pointKeyMap[astr] || astr; +} + +function getPointData(val, pointNumber) { + if(Array.isArray(pointNumber)) { + if(Array.isArray(val) && Array.isArray(val[pointNumber[0]])) { + return val[pointNumber[0]][pointNumber[1]]; + } + } else { + return val[pointNumber]; + } +} + +var xyHoverMode = { + x: true, + y: true +}; + +var unifiedHoverMode = { + 'x unified': true, + 'y unified': true +}; + +exports.isUnifiedHover = function(hovermode) { + if(typeof hovermode !== 'string') return false; + return !!unifiedHoverMode[hovermode]; +}; + +exports.isXYhover = function(hovermode) { + if(typeof hovermode !== 'string') return false; + return !!xyHoverMode[hovermode]; +}; + +},{"../../lib":285}],192:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); + +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var strRotate = Lib.strRotate; +var Events = _dereq_('../../lib/events'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var overrideCursor = _dereq_('../../lib/override_cursor'); +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var dragElement = _dereq_('../dragelement'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var Registry = _dereq_('../../registry'); + +var helpers = _dereq_('./helpers'); +var constants = _dereq_('./constants'); + +var legendSupplyDefaults = _dereq_('../legend/defaults'); +var legendDraw = _dereq_('../legend/draw'); + +// hover labels for multiple horizontal bars get tilted by some angle, +// then need to be offset differently if they overlap +var YANGLE = constants.YANGLE; +var YA_RADIANS = Math.PI * YANGLE / 180; + +// expansion of projected height +var YFACTOR = 1 / Math.sin(YA_RADIANS); + +// to make the appropriate post-rotation x offset, +// you need both x and y offsets +var YSHIFTX = Math.cos(YA_RADIANS); +var YSHIFTY = Math.sin(YA_RADIANS); + +// size and display constants for hover text +var HOVERARROWSIZE = constants.HOVERARROWSIZE; +var HOVERTEXTPAD = constants.HOVERTEXTPAD; + +var multipleHoverPoints = { + box: true, + ohlc: true, + violin: true, + candlestick: true +}; + +// fx.hover: highlight data on hover +// evt can be a mousemove event, or an object with data about what points +// to hover on +// {xpx,ypx[,hovermode]} - pixel locations from top left +// (with optional overriding hovermode) +// {xval,yval[,hovermode]} - data values +// [{curveNumber,(pointNumber|xval and/or yval)}] - +// array of specific points to highlight +// pointNumber is a single integer if gd.data[curveNumber] is 1D, +// or a two-element array if it's 2D +// xval and yval are data values, +// 1D data may specify either or both, +// 2D data must specify both +// subplot is an id string (default "xy") +// makes use of gl.hovermode, which can be: +// x (find the points with the closest x values, ie a column), +// closest (find the single closest point) +// internally there are two more that occasionally get used: +// y (pick out a row - only used for multiple horizontal bar charts) +// array (used when the user specifies an explicit +// array of points to hover on) +// +// We wrap the hovers in a timer, to limit their frequency. +// The actual rendering is done by private function _hover. +exports.hover = function hover(gd, evt, subplot, noHoverEvent) { + gd = Lib.getGraphDiv(gd); + + Lib.throttle( + gd._fullLayout._uid + constants.HOVERID, + constants.HOVERMINTIME, + function() { _hover(gd, evt, subplot, noHoverEvent); } + ); +}; + +/* + * Draw a single hover item or an array of hover item in a pre-existing svg container somewhere + * hoverItem should have keys: + * - x and y (or x0, x1, y0, and y1): + * the pixel position to mark, relative to opts.container + * - xLabel, yLabel, zLabel, text, and name: + * info to go in the label + * - color: + * the background color for the label. + * - idealAlign (optional): + * 'left' or 'right' for which side of the x/y box to try to put this on first + * - borderColor (optional): + * color for the border, defaults to strongest contrast with color + * - fontFamily (optional): + * string, the font for this label, defaults to constants.HOVERFONT + * - fontSize (optional): + * the label font size, defaults to constants.HOVERFONTSIZE + * - fontColor (optional): + * defaults to borderColor + * opts should have keys: + * - bgColor: + * the background color this is against, used if the trace is + * non-opaque, and for the name, which goes outside the box + * - container: + * a or element to add the hover label to + * - outerContainer: + * normally a parent of `container`, sets the bounding box to use to + * constrain the hover label and determine whether to show it on the left or right + * opts can have optional keys: + * - anchorIndex: + the index of the hover item used as an anchor for positioning. + The other hover items will be pushed up or down to prevent overlap. + */ +exports.loneHover = function loneHover(hoverItems, opts) { + var multiHover = true; + if(!Array.isArray(hoverItems)) { + multiHover = false; + hoverItems = [hoverItems]; + } + + var pointsData = hoverItems.map(function(hoverItem) { + return { + color: hoverItem.color || Color.defaultLine, + x0: hoverItem.x0 || hoverItem.x || 0, + x1: hoverItem.x1 || hoverItem.x || 0, + y0: hoverItem.y0 || hoverItem.y || 0, + y1: hoverItem.y1 || hoverItem.y || 0, + xLabel: hoverItem.xLabel, + yLabel: hoverItem.yLabel, + zLabel: hoverItem.zLabel, + text: hoverItem.text, + name: hoverItem.name, + idealAlign: hoverItem.idealAlign, + + // optional extra bits of styling + borderColor: hoverItem.borderColor, + fontFamily: hoverItem.fontFamily, + fontSize: hoverItem.fontSize, + fontColor: hoverItem.fontColor, + nameLength: hoverItem.nameLength, + textAlign: hoverItem.textAlign, + + // filler to make createHoverText happy + trace: hoverItem.trace || { + index: 0, + hoverinfo: '' + }, + xa: {_offset: 0}, + ya: {_offset: 0}, + index: 0, + + hovertemplate: hoverItem.hovertemplate || false, + eventData: hoverItem.eventData || false, + hovertemplateLabels: hoverItem.hovertemplateLabels || false, + }; + }); + + var container3 = d3.select(opts.container); + var outerContainer3 = opts.outerContainer ? d3.select(opts.outerContainer) : container3; + + var fullOpts = { + hovermode: 'closest', + rotateLabels: false, + bgColor: opts.bgColor || Color.background, + container: container3, + outerContainer: outerContainer3 + }; + + var hoverLabel = createHoverText(pointsData, fullOpts, opts.gd); + + // Fix vertical overlap + var tooltipSpacing = 5; + var lastBottomY = 0; + var anchor = 0; + hoverLabel + .sort(function(a, b) {return a.y0 - b.y0;}) + .each(function(d, i) { + var topY = d.y0 - d.by / 2; + + if((topY - tooltipSpacing) < lastBottomY) { + d.offset = (lastBottomY - topY) + tooltipSpacing; + } else { + d.offset = 0; + } + + lastBottomY = topY + d.by + d.offset; + + if(i === opts.anchorIndex || 0) anchor = d.offset; + }) + .each(function(d) { + d.offset -= anchor; + }); + + var scaleX = opts.gd._fullLayout._invScaleX; + var scaleY = opts.gd._fullLayout._invScaleY; + alignHoverText(hoverLabel, fullOpts.rotateLabels, scaleX, scaleY); + + return multiHover ? hoverLabel : hoverLabel.node(); +}; + +// The actual implementation is here: +function _hover(gd, evt, subplot, noHoverEvent) { + if(!subplot) subplot = 'xy'; + + // if the user passed in an array of subplots, + // use those instead of finding overlayed plots + var subplots = Array.isArray(subplot) ? subplot : [subplot]; + + var fullLayout = gd._fullLayout; + var plots = fullLayout._plots || []; + var plotinfo = plots[subplot]; + var hasCartesian = fullLayout._has('cartesian'); + + // list of all overlaid subplots to look at + if(plotinfo) { + var overlayedSubplots = plotinfo.overlays.map(function(pi) { + return pi.id; + }); + + subplots = subplots.concat(overlayedSubplots); + } + + var len = subplots.length; + var xaArray = new Array(len); + var yaArray = new Array(len); + var supportsCompare = false; + + for(var i = 0; i < len; i++) { + var spId = subplots[i]; + + if(plots[spId]) { + // 'cartesian' case + supportsCompare = true; + xaArray[i] = plots[spId].xaxis; + yaArray[i] = plots[spId].yaxis; + } else if(fullLayout[spId] && fullLayout[spId]._subplot) { + // other subplot types + var _subplot = fullLayout[spId]._subplot; + xaArray[i] = _subplot.xaxis; + yaArray[i] = _subplot.yaxis; + } else { + Lib.warn('Unrecognized subplot: ' + spId); + return; + } + } + + var hovermode = evt.hovermode || fullLayout.hovermode; + + if(hovermode && !supportsCompare) hovermode = 'closest'; + + if(['x', 'y', 'closest', 'x unified', 'y unified'].indexOf(hovermode) === -1 || !gd.calcdata || + gd.querySelector('.zoombox') || gd._dragging) { + return dragElement.unhoverRaw(gd, evt); + } + + var hoverdistance = fullLayout.hoverdistance; + if(hoverdistance === -1) hoverdistance = Infinity; + + var spikedistance = fullLayout.spikedistance; + if(spikedistance === -1) spikedistance = Infinity; + + // hoverData: the set of candidate points we've found to highlight + var hoverData = []; + + // searchData: the data to search in. Mostly this is just a copy of + // gd.calcdata, filtered to the subplot and overlays we're on + // but if a point array is supplied it will be a mapping + // of indicated curves + var searchData = []; + + // [x|y]valArray: the axis values of the hover event + // mapped onto each of the currently selected overlaid subplots + var xvalArray, yvalArray; + + var itemnum, curvenum, cd, trace, subplotId, subploti, _mode, + xval, yval, pointData, closedataPreviousLength; + + // spikePoints: the set of candidate points we've found to draw spikes to + var spikePoints = { + hLinePoint: null, + vLinePoint: null + }; + + // does subplot have one (or more) horizontal traces? + // This is used to determine whether we rotate the labels or not + var hasOneHorizontalTrace = false; + + // Figure out what we're hovering on: + // mouse location or user-supplied data + + if(Array.isArray(evt)) { + // user specified an array of points to highlight + hovermode = 'array'; + for(itemnum = 0; itemnum < evt.length; itemnum++) { + cd = gd.calcdata[evt[itemnum].curveNumber || 0]; + if(cd) { + trace = cd[0].trace; + if(cd[0].trace.hoverinfo !== 'skip') { + searchData.push(cd); + if(trace.orientation === 'h') { + hasOneHorizontalTrace = true; + } + } + } + } + } else { + for(curvenum = 0; curvenum < gd.calcdata.length; curvenum++) { + cd = gd.calcdata[curvenum]; + trace = cd[0].trace; + if(trace.hoverinfo !== 'skip' && helpers.isTraceInSubplots(trace, subplots)) { + searchData.push(cd); + if(trace.orientation === 'h') { + hasOneHorizontalTrace = true; + } + } + } + + // [x|y]px: the pixels (from top left) of the mouse location + // on the currently selected plot area + // add pointerX|Y property for drawing the spikes in spikesnap 'cursor' situation + var hasUserCalledHover = !evt.target; + var xpx, ypx; + + if(hasUserCalledHover) { + if('xpx' in evt) xpx = evt.xpx; + else xpx = xaArray[0]._length / 2; + + if('ypx' in evt) ypx = evt.ypx; + else ypx = yaArray[0]._length / 2; + } else { + // fire the beforehover event and quit if it returns false + // note that we're only calling this on real mouse events, so + // manual calls to fx.hover will always run. + if(Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) { + return; + } + + // Discover event target, traversing open shadow roots. + var target = evt.composedPath && evt.composedPath()[0]; + if(!target) { + // Fallback for browsers not supporting composedPath + target = evt.target; + } + var dbb = target.getBoundingClientRect(); + + xpx = evt.clientX - dbb.left; + ypx = evt.clientY - dbb.top; + + fullLayout._calcInverseTransform(gd); + var transformedCoords = Lib.apply3DTransform(fullLayout._invTransform)(xpx, ypx); + + xpx = transformedCoords[0]; + ypx = transformedCoords[1]; + + // in case hover was called from mouseout into hovertext, + // it's possible you're not actually over the plot anymore + if(xpx < 0 || xpx > xaArray[0]._length || ypx < 0 || ypx > yaArray[0]._length) { + return dragElement.unhoverRaw(gd, evt); + } + } + + evt.pointerX = xpx + xaArray[0]._offset; + evt.pointerY = ypx + yaArray[0]._offset; + + if('xval' in evt) xvalArray = helpers.flat(subplots, evt.xval); + else xvalArray = helpers.p2c(xaArray, xpx); + + if('yval' in evt) yvalArray = helpers.flat(subplots, evt.yval); + else yvalArray = helpers.p2c(yaArray, ypx); + + if(!isNumeric(xvalArray[0]) || !isNumeric(yvalArray[0])) { + Lib.warn('Fx.hover failed', evt, gd); + return dragElement.unhoverRaw(gd, evt); + } + } + + // the pixel distance to beat as a matching point + // in 'x' or 'y' mode this resets for each trace + var distance = Infinity; + + // find the closest point in each trace + // this is minimum dx and/or dy, depending on mode + // and the pixel position for the label (labelXpx, labelYpx) + function findHoverPoints(customXVal, customYVal) { + for(curvenum = 0; curvenum < searchData.length; curvenum++) { + cd = searchData[curvenum]; + + // filter out invisible or broken data + if(!cd || !cd[0] || !cd[0].trace) continue; + + trace = cd[0].trace; + + if(trace.visible !== true || trace._length === 0) continue; + + // Explicitly bail out for these two. I don't know how to otherwise prevent + // the rest of this function from running and failing + if(['carpet', 'contourcarpet'].indexOf(trace._module.name) !== -1) continue; + + if(trace.type === 'splom') { + // splom traces do not generate overlay subplots, + // it is safe to assume here splom traces correspond to the 0th subplot + subploti = 0; + subplotId = subplots[subploti]; + } else { + subplotId = helpers.getSubplot(trace); + subploti = subplots.indexOf(subplotId); + } + + // within one trace mode can sometimes be overridden + _mode = hovermode; + if(helpers.isUnifiedHover(_mode)) { + _mode = _mode.charAt(0); + } + + // container for new point, also used to pass info into module.hoverPoints + pointData = { + // trace properties + cd: cd, + trace: trace, + xa: xaArray[subploti], + ya: yaArray[subploti], + + // max distances for hover and spikes - for points that want to show but do not + // want to override other points, set distance/spikeDistance equal to max*Distance + // and it will not get filtered out but it will be guaranteed to have a greater + // distance than any point that calculated a real distance. + maxHoverDistance: hoverdistance, + maxSpikeDistance: spikedistance, + + // point properties - override all of these + index: false, // point index in trace - only used by plotly.js hoverdata consumers + distance: Math.min(distance, hoverdistance), // pixel distance or pseudo-distance + + // distance/pseudo-distance for spikes. This distance should always be calculated + // as if in "closest" mode, and should only be set if this point should + // generate a spike. + spikeDistance: Infinity, + + // in some cases the spikes have different positioning from the hover label + // they don't need x0/x1, just one position + xSpike: undefined, + ySpike: undefined, + + // where and how to display the hover label + color: Color.defaultLine, // trace color + name: trace.name, + x0: undefined, + x1: undefined, + y0: undefined, + y1: undefined, + xLabelVal: undefined, + yLabelVal: undefined, + zLabelVal: undefined, + text: undefined + }; + + // add ref to subplot object (non-cartesian case) + if(fullLayout[subplotId]) { + pointData.subplot = fullLayout[subplotId]._subplot; + } + // add ref to splom scene + if(fullLayout._splomScenes && fullLayout._splomScenes[trace.uid]) { + pointData.scene = fullLayout._splomScenes[trace.uid]; + } + + closedataPreviousLength = hoverData.length; + + // for a highlighting array, figure out what + // we're searching for with this element + if(_mode === 'array') { + var selection = evt[curvenum]; + if('pointNumber' in selection) { + pointData.index = selection.pointNumber; + _mode = 'closest'; + } else { + _mode = ''; + if('xval' in selection) { + xval = selection.xval; + _mode = 'x'; + } + if('yval' in selection) { + yval = selection.yval; + _mode = _mode ? 'closest' : 'y'; + } + } + } else if(customXVal !== undefined && customYVal !== undefined) { + xval = customXVal; + yval = customYVal; + } else { + xval = xvalArray[subploti]; + yval = yvalArray[subploti]; + } + + // Now if there is range to look in, find the points to hover. + if(hoverdistance !== 0) { + if(trace._module && trace._module.hoverPoints) { + var newPoints = trace._module.hoverPoints(pointData, xval, yval, _mode, { + finiteRange: true, + hoverLayer: fullLayout._hoverlayer + }); + + if(newPoints) { + var newPoint; + for(var newPointNum = 0; newPointNum < newPoints.length; newPointNum++) { + newPoint = newPoints[newPointNum]; + if(isNumeric(newPoint.x0) && isNumeric(newPoint.y0)) { + hoverData.push(cleanPoint(newPoint, hovermode)); + } + } + } + } else { + Lib.log('Unrecognized trace type in hover:', trace); + } + } + + // in closest mode, remove any existing (farther) points + // and don't look any farther than this latest point (or points, some + // traces like box & violin make multiple hover labels at once) + if(hovermode === 'closest' && hoverData.length > closedataPreviousLength) { + hoverData.splice(0, closedataPreviousLength); + distance = hoverData[0].distance; + } + + // Now if there is range to look in, find the points to draw the spikelines + // Do it only if there is no hoverData + if(hasCartesian && (spikedistance !== 0)) { + if(hoverData.length === 0) { + pointData.distance = spikedistance; + pointData.index = false; + var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', { + hoverLayer: fullLayout._hoverlayer + }); + if(closestPoints) { + closestPoints = closestPoints.filter(function(point) { + // some hover points, like scatter fills, do not allow spikes, + // so will generate a hover point but without a valid spikeDistance + return point.spikeDistance <= spikedistance; + }); + } + if(closestPoints && closestPoints.length) { + var tmpPoint; + var closestVPoints = closestPoints.filter(function(point) { + return point.xa.showspikes && point.xa.spikesnap !== 'hovered data'; + }); + if(closestVPoints.length) { + var closestVPt = closestVPoints[0]; + if(isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) { + tmpPoint = fillSpikePoint(closestVPt); + if(!spikePoints.vLinePoint || (spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance)) { + spikePoints.vLinePoint = tmpPoint; + } + } + } + + var closestHPoints = closestPoints.filter(function(point) { + return point.ya.showspikes && point.ya.spikesnap !== 'hovered data'; + }); + if(closestHPoints.length) { + var closestHPt = closestHPoints[0]; + if(isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) { + tmpPoint = fillSpikePoint(closestHPt); + if(!spikePoints.hLinePoint || (spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance)) { + spikePoints.hLinePoint = tmpPoint; + } + } + } + } + } + } + } + } + + findHoverPoints(); + + function selectClosestPoint(pointsData, spikedistance) { + var resultPoint = null; + var minDistance = Infinity; + var thisSpikeDistance; + for(var i = 0; i < pointsData.length; i++) { + thisSpikeDistance = pointsData[i].spikeDistance; + if(thisSpikeDistance <= minDistance && thisSpikeDistance <= spikedistance) { + resultPoint = pointsData[i]; + minDistance = thisSpikeDistance; + } + } + return resultPoint; + } + + function fillSpikePoint(point) { + if(!point) return null; + return { + xa: point.xa, + ya: point.ya, + x: point.xSpike !== undefined ? point.xSpike : (point.x0 + point.x1) / 2, + y: point.ySpike !== undefined ? point.ySpike : (point.y0 + point.y1) / 2, + distance: point.distance, + spikeDistance: point.spikeDistance, + curveNumber: point.trace.index, + color: point.color, + pointNumber: point.index + }; + } + + var spikelineOpts = { + fullLayout: fullLayout, + container: fullLayout._hoverlayer, + outerContainer: fullLayout._paperdiv, + event: evt + }; + var oldspikepoints = gd._spikepoints; + var newspikepoints = { + vLinePoint: spikePoints.vLinePoint, + hLinePoint: spikePoints.hLinePoint + }; + gd._spikepoints = newspikepoints; + + // Now if it is not restricted by spikedistance option, set the points to draw the spikelines + if(hasCartesian && (spikedistance !== 0)) { + if(hoverData.length !== 0) { + var tmpHPointData = hoverData.filter(function(point) { + return point.ya.showspikes; + }); + var tmpHPoint = selectClosestPoint(tmpHPointData, spikedistance); + spikePoints.hLinePoint = fillSpikePoint(tmpHPoint); + + var tmpVPointData = hoverData.filter(function(point) { + return point.xa.showspikes; + }); + var tmpVPoint = selectClosestPoint(tmpVPointData, spikedistance); + spikePoints.vLinePoint = fillSpikePoint(tmpVPoint); + } + } + + // if hoverData is empty check for the spikes to draw and quit if there are none + if(hoverData.length === 0) { + var result = dragElement.unhoverRaw(gd, evt); + if(hasCartesian && ((spikePoints.hLinePoint !== null) || (spikePoints.vLinePoint !== null))) { + if(spikesChanged(oldspikepoints)) { + createSpikelines(gd, spikePoints, spikelineOpts); + } + } + return result; + } + + if(hasCartesian) { + if(spikesChanged(oldspikepoints)) { + createSpikelines(gd, spikePoints, spikelineOpts); + } + } + + var sortHoverData = function() { + hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; }); + + // move period positioned points and box/bar-like traces to the end of the list + hoverData = orderRangePoints(hoverData, hovermode); + }; + sortHoverData(); + + if( + helpers.isXYhover(_mode) && + hoverData[0].length !== 0 && + hoverData[0].trace.type !== 'splom' // TODO: add support for splom + ) { + // pick winning point + var winningPoint = hoverData[0]; + // discard other points + if(multipleHoverPoints[winningPoint.trace.type]) { + hoverData = hoverData.filter(function(d) { + return d.trace.index === winningPoint.trace.index; + }); + } else { + hoverData = [winningPoint]; + } + var initLen = hoverData.length; + + var winX = getCoord('x', winningPoint, fullLayout); + var winY = getCoord('y', winningPoint, fullLayout); + + // in compare mode, select every point at position + findHoverPoints(winX, winY); + + var finalPoints = []; + var seen = {}; + var id = 0; + var insert = function(newHd) { + var key = multipleHoverPoints[newHd.trace.type] ? hoverDataKey(newHd) : newHd.trace.index; + if(!seen[key]) { + id++; + seen[key] = id; + finalPoints.push(newHd); + } else { + var oldId = seen[key] - 1; + var oldHd = finalPoints[oldId]; + if(oldId > 0 && + Math.abs(newHd.distance) < + Math.abs(oldHd.distance) + ) { + // replace with closest + finalPoints[oldId] = newHd; + } + } + }; + + var k; + // insert the winnig point(s) first + for(k = 0; k < initLen; k++) { + insert(hoverData[k]); + } + // override from the end + for(k = hoverData.length - 1; k > initLen - 1; k--) { + insert(hoverData[k]); + } + hoverData = finalPoints; + sortHoverData(); + } + + // lastly, emit custom hover/unhover events + var oldhoverdata = gd._hoverdata; + var newhoverdata = []; + + // pull out just the data that's useful to + // other people and send it to the event + for(itemnum = 0; itemnum < hoverData.length; itemnum++) { + var pt = hoverData[itemnum]; + var eventData = helpers.makeEventData(pt, pt.trace, pt.cd); + + if(pt.hovertemplate !== false) { + var ht = false; + if(pt.cd[pt.index] && pt.cd[pt.index].ht) { + ht = pt.cd[pt.index].ht; + } + pt.hovertemplate = ht || pt.trace.hovertemplate || false; + } + + pt.eventData = [eventData]; + newhoverdata.push(eventData); + } + + gd._hoverdata = newhoverdata; + + var rotateLabels = ( + (hovermode === 'y' && (searchData.length > 1 || hoverData.length > 1)) || + (hovermode === 'closest' && hasOneHorizontalTrace && hoverData.length > 1) + ); + + var bgColor = Color.combine( + fullLayout.plot_bgcolor || Color.background, + fullLayout.paper_bgcolor + ); + + var labelOpts = { + hovermode: hovermode, + rotateLabels: rotateLabels, + bgColor: bgColor, + container: fullLayout._hoverlayer, + outerContainer: fullLayout._paperdiv, + commonLabelOpts: fullLayout.hoverlabel, + hoverdistance: fullLayout.hoverdistance + }; + + var hoverLabels = createHoverText(hoverData, labelOpts, gd); + + if(!helpers.isUnifiedHover(hovermode)) { + hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout); + alignHoverText(hoverLabels, rotateLabels, fullLayout._invScaleX, fullLayout._invScaleY); + } // TODO: tagName hack is needed to appease geo.js's hack of using evt.target=true + // we should improve the "fx" API so other plots can use it without these hack. + if(evt.target && evt.target.tagName) { + var hasClickToShow = Registry.getComponentMethod('annotations', 'hasClickToShow')(gd, newhoverdata); + overrideCursor(d3.select(evt.target), hasClickToShow ? 'pointer' : ''); + } + + // don't emit events if called manually + if(!evt.target || noHoverEvent || !hoverChanged(gd, evt, oldhoverdata)) return; + + if(oldhoverdata) { + gd.emit('plotly_unhover', { + event: evt, + points: oldhoverdata + }); + } + + gd.emit('plotly_hover', { + event: evt, + points: gd._hoverdata, + xaxes: xaArray, + yaxes: yaArray, + xvals: xvalArray, + yvals: yvalArray + }); +} + +function hoverDataKey(d) { + return [d.trace.index, d.index, d.x0, d.y0, d.name, d.attr, d.xa, d.ya || ''].join(','); +} + +var EXTRA_STRING_REGEX = /([\s\S]*)<\/extra>/; + +function createHoverText(hoverData, opts, gd) { + var fullLayout = gd._fullLayout; + var hovermode = opts.hovermode; + var rotateLabels = opts.rotateLabels; + var bgColor = opts.bgColor; + var container = opts.container; + var outerContainer = opts.outerContainer; + var commonLabelOpts = opts.commonLabelOpts || {}; + + // opts.fontFamily/Size are used for the common label + // and as defaults for each hover label, though the individual labels + // can override this. + var fontFamily = opts.fontFamily || constants.HOVERFONT; + var fontSize = opts.fontSize || constants.HOVERFONTSIZE; + + var c0 = hoverData[0]; + var xa = c0.xa; + var ya = c0.ya; + var axLetter = hovermode.charAt(0); + var t0 = c0[axLetter + 'Label']; + var outerContainerBB = outerContainer.node().getBoundingClientRect(); + var outerTop = outerContainerBB.top; + var outerWidth = outerContainerBB.width; + var outerHeight = outerContainerBB.height; + + // show the common label, if any, on the axis + // never show a common label in array mode, + // even if sometimes there could be one + var showCommonLabel = ( + (t0 !== undefined) && + (c0.distance <= opts.hoverdistance) && + (hovermode === 'x' || hovermode === 'y') + ); + + // all hover traces hoverinfo must contain the hovermode + // to have common labels + if(showCommonLabel) { + var allHaveZ = true; + var i, traceHoverinfo; + for(i = 0; i < hoverData.length; i++) { + if(allHaveZ && hoverData[i].zLabel === undefined) allHaveZ = false; + + traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo; + if(traceHoverinfo) { + var parts = Array.isArray(traceHoverinfo) ? traceHoverinfo : traceHoverinfo.split('+'); + if(parts.indexOf('all') === -1 && + parts.indexOf(hovermode) === -1) { + showCommonLabel = false; + break; + } + } + } + + // xyz labels put all info in their main label, so have no need of a common label + if(allHaveZ) showCommonLabel = false; + } + + var commonLabel = container.selectAll('g.axistext') + .data(showCommonLabel ? [0] : []); + commonLabel.enter().append('g') + .classed('axistext', true); + commonLabel.exit().remove(); + + commonLabel.each(function() { + var label = d3.select(this); + var lpath = Lib.ensureSingle(label, 'path', '', function(s) { + s.style({'stroke-width': '1px'}); + }); + var ltext = Lib.ensureSingle(label, 'text', '', function(s) { + // prohibit tex interpretation until we can handle + // tex and regular text together + s.attr('data-notex', 1); + }); + + var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine; + var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor); + var contrastColor = Color.contrast(commonBgColor); + var commonLabelFont = { + family: commonLabelOpts.font.family || fontFamily, + size: commonLabelOpts.font.size || fontSize, + color: commonLabelOpts.font.color || contrastColor + }; + + lpath.style({ + fill: commonBgColor, + stroke: commonStroke + }); + + ltext.text(t0) + .call(Drawing.font, commonLabelFont) + .call(svgTextUtils.positionText, 0, 0) + .call(svgTextUtils.convertToTspans, gd); + + label.attr('transform', ''); + + var tbb = ltext.node().getBoundingClientRect(); + var lx, ly; + + if(hovermode === 'x') { + var topsign = xa.side === 'top' ? '-' : ''; + + ltext.attr('text-anchor', 'middle') + .call(svgTextUtils.positionText, 0, (xa.side === 'top' ? + (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) : + (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD))); + + lx = xa._offset + (c0.x0 + c0.x1) / 2; + ly = ya._offset + (xa.side === 'top' ? 0 : ya._length); + + var halfWidth = tbb.width / 2 + HOVERTEXTPAD; + + if(lx < halfWidth) { + lx = halfWidth; + + lpath.attr('d', 'M-' + (halfWidth - HOVERARROWSIZE) + ',0' + + 'L-' + (halfWidth - HOVERARROWSIZE * 2) + ',' + topsign + HOVERARROWSIZE + + 'H' + (HOVERTEXTPAD + tbb.width / 2) + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + halfWidth + + 'V' + topsign + HOVERARROWSIZE + + 'Z'); + } else if(lx > (fullLayout.width - halfWidth)) { + lx = fullLayout.width - halfWidth; + + lpath.attr('d', 'M' + (halfWidth - HOVERARROWSIZE) + ',0' + + 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + halfWidth + + 'V' + topsign + HOVERARROWSIZE + + 'H' + (halfWidth - HOVERARROWSIZE * 2) + 'Z'); + } else { + lpath.attr('d', 'M0,0' + + 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE + + 'H' + (HOVERTEXTPAD + tbb.width / 2) + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + (HOVERTEXTPAD + tbb.width / 2) + + 'V' + topsign + HOVERARROWSIZE + + 'H-' + HOVERARROWSIZE + 'Z'); + } + } else { + var anchor; + var sgn; + var leftsign; + if(ya.side === 'right') { + anchor = 'start'; + sgn = 1; + leftsign = ''; + lx = xa._offset + xa._length; + } else { + anchor = 'end'; + sgn = -1; + leftsign = '-'; + lx = xa._offset; + } + + ly = ya._offset + (c0.y0 + c0.y1) / 2; + + ltext.attr('text-anchor', anchor); + + lpath.attr('d', 'M0,0' + + 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE + + 'V' + (HOVERTEXTPAD + tbb.height / 2) + + 'h' + leftsign + (HOVERTEXTPAD * 2 + tbb.width) + + 'V-' + (HOVERTEXTPAD + tbb.height / 2) + + 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z'); + + var halfHeight = tbb.height / 2; + var lty = outerTop - tbb.top - halfHeight; + var clipId = 'clip' + fullLayout._uid + 'commonlabel' + ya._id; + var clipPath; + + if(lx < (tbb.width + 2 * HOVERTEXTPAD + HOVERARROWSIZE)) { + clipPath = 'M-' + (HOVERARROWSIZE + HOVERTEXTPAD) + '-' + halfHeight + + 'h-' + (tbb.width - HOVERTEXTPAD) + + 'V' + halfHeight + + 'h' + (tbb.width - HOVERTEXTPAD) + 'Z'; + + var ltx = tbb.width - lx + HOVERTEXTPAD; + svgTextUtils.positionText(ltext, ltx, lty); + + // shift each line (except the longest) so that start-of-line + // is always visible + if(anchor === 'end') { + ltext.selectAll('tspan').each(function() { + var s = d3.select(this); + var dummy = Drawing.tester.append('text') + .text(s.text()) + .call(Drawing.font, commonLabelFont); + var dummyBB = dummy.node().getBoundingClientRect(); + if(Math.round(dummyBB.width) < Math.round(tbb.width)) { + s.attr('x', ltx - dummyBB.width); + } + dummy.remove(); + }); + } + } else { + svgTextUtils.positionText(ltext, sgn * (HOVERTEXTPAD + HOVERARROWSIZE), lty); + clipPath = null; + } + + var textClip = fullLayout._topclips.selectAll('#' + clipId).data(clipPath ? [0] : []); + textClip.enter().append('clipPath').attr('id', clipId).append('path'); + textClip.exit().remove(); + textClip.select('path').attr('d', clipPath); + Drawing.setClipUrl(ltext, clipPath ? clipId : null, gd); + } + + label.attr('transform', strTranslate(lx, ly)); + }); + + // Show a single hover label + if(helpers.isUnifiedHover(hovermode)) { + // Delete leftover hover labels from other hovermodes + container.selectAll('g.hovertext').remove(); + + // Return early if nothing is hovered on + if(hoverData.length === 0) return; + + // mock legend + var mockLayoutIn = { + showlegend: true, + legend: { + title: {text: t0, font: fullLayout.hoverlabel.font}, + font: fullLayout.hoverlabel.font, + bgcolor: fullLayout.hoverlabel.bgcolor, + bordercolor: fullLayout.hoverlabel.bordercolor, + borderwidth: 1, + tracegroupgap: 7, + traceorder: fullLayout.legend ? fullLayout.legend.traceorder : undefined, + orientation: 'v' + } + }; + var mockLayoutOut = {}; + legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData); + var mockLegend = mockLayoutOut.legend; + + // prepare items for the legend + mockLegend.entries = []; + for(var j = 0; j < hoverData.length; j++) { + var texts = getHoverLabelText(hoverData[j], true, hovermode, fullLayout, t0); + var text = texts[0]; + var name = texts[1]; + var pt = hoverData[j]; + pt.name = name; + if(name !== '') { + pt.text = name + ' : ' + text; + } else { + pt.text = text; + } + + // pass through marker's calcdata to style legend items + var cd = pt.cd[pt.index]; + if(cd) { + if(cd.mc) pt.mc = cd.mc; + if(cd.mcc) pt.mc = cd.mcc; + if(cd.mlc) pt.mlc = cd.mlc; + if(cd.mlcc) pt.mlc = cd.mlcc; + if(cd.mlw) pt.mlw = cd.mlw; + if(cd.mrc) pt.mrc = cd.mrc; + if(cd.dir) pt.dir = cd.dir; + } + pt._distinct = true; + + mockLegend.entries.push([pt]); + } + mockLegend.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;}); + mockLegend.layer = container; + + // Draw unified hover label + mockLegend._inHover = true; + legendDraw(gd, mockLegend); + + // Position the hover + var winningPoint = hoverData[0]; + var ly = (winningPoint.y0 + winningPoint.y1) / 2; + var lx = (winningPoint.x0 + winningPoint.x1) / 2; + var legendContainer = container.select('g.legend'); + var tbb = legendContainer.node().getBoundingClientRect(); + lx += xa._offset; + ly += ya._offset - tbb.height / 2; + + // Change horizontal alignment to end up on screen + var txWidth = tbb.width + 2 * HOVERTEXTPAD; + var anchorStartOK = lx + txWidth <= outerWidth; + var anchorEndOK = lx - txWidth >= 0; + if(!anchorStartOK && anchorEndOK) { + lx -= txWidth; + } else { + lx += 2 * HOVERTEXTPAD; + } + + // Change vertical alignement to end up on screen + var txHeight = tbb.height + 2 * HOVERTEXTPAD; + var overflowTop = ly <= outerTop; + var overflowBottom = ly + txHeight >= outerHeight; + var canFit = txHeight <= outerHeight; + if(canFit) { + if(overflowTop) { + ly = ya._offset + 2 * HOVERTEXTPAD; + } else if(overflowBottom) { + ly = outerHeight - txHeight; + } + } + legendContainer.attr('transform', strTranslate(lx, ly)); + + return legendContainer; + } + + // show all the individual labels + + // first create the objects + var hoverLabels = container.selectAll('g.hovertext') + .data(hoverData, function(d) { + // N.B. when multiple items have the same result key-function value, + // only the first of those items in hoverData gets rendered + return hoverDataKey(d); + }); + hoverLabels.enter().append('g') + .classed('hovertext', true) + .each(function() { + var g = d3.select(this); + // trace name label (rect and text.name) + g.append('rect') + .call(Color.fill, Color.addOpacity(bgColor, 0.8)); + g.append('text').classed('name', true); + // trace data label (path and text.nums) + g.append('path') + .style('stroke-width', '1px'); + g.append('text').classed('nums', true) + .call(Drawing.font, fontFamily, fontSize); + }); + hoverLabels.exit().remove(); + + // then put the text in, position the pointer to the data, + // and figure out sizes + hoverLabels.each(function(d) { + var g = d3.select(this).attr('transform', ''); + + var dColor = d.color; + if(Array.isArray(dColor)) { + dColor = dColor[d.eventData[0].pointNumber]; + } + + // combine possible non-opaque trace color with bgColor + var color0 = d.bgcolor || dColor; + // color for 'nums' part of the label + var numsColor = Color.combine( + Color.opacity(color0) ? color0 : Color.defaultLine, + bgColor + ); + // color for 'name' part of the label + var nameColor = Color.combine( + Color.opacity(dColor) ? dColor : Color.defaultLine, + bgColor + ); + // find a contrasting color for border and text + var contrastColor = d.borderColor || Color.contrast(numsColor); + + var texts = getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g); + var text = texts[0]; + var name = texts[1]; + + // main label + var tx = g.select('text.nums') + .call(Drawing.font, + d.fontFamily || fontFamily, + d.fontSize || fontSize, + d.fontColor || contrastColor) + .text(text) + .attr('data-notex', 1) + .call(svgTextUtils.positionText, 0, 0) + .call(svgTextUtils.convertToTspans, gd); + + var tx2 = g.select('text.name'); + var tx2width = 0; + var tx2height = 0; + + // secondary label for non-empty 'name' + if(name && name !== text) { + tx2.call(Drawing.font, + d.fontFamily || fontFamily, + d.fontSize || fontSize, + nameColor) + .text(name) + .attr('data-notex', 1) + .call(svgTextUtils.positionText, 0, 0) + .call(svgTextUtils.convertToTspans, gd); + + var t2bb = tx2.node().getBoundingClientRect(); + tx2width = t2bb.width + 2 * HOVERTEXTPAD; + tx2height = t2bb.height + 2 * HOVERTEXTPAD; + } else { + tx2.remove(); + g.select('rect').remove(); + } + + g.select('path').style({ + fill: numsColor, + stroke: contrastColor + }); + + var tbb = tx.node().getBoundingClientRect(); + var htx = d.xa._offset + (d.x0 + d.x1) / 2; + var hty = d.ya._offset + (d.y0 + d.y1) / 2; + var dx = Math.abs(d.x1 - d.x0); + var dy = Math.abs(d.y1 - d.y0); + var txTotalWidth = tbb.width + HOVERARROWSIZE + HOVERTEXTPAD + tx2width; + var anchorStartOK, anchorEndOK; + + d.ty0 = outerTop - tbb.top; + d.bx = tbb.width + 2 * HOVERTEXTPAD; + d.by = Math.max(tbb.height + 2 * HOVERTEXTPAD, tx2height); + d.anchor = 'start'; + d.txwidth = tbb.width; + d.tx2width = tx2width; + d.offset = 0; + + if(rotateLabels) { + d.pos = htx; + anchorStartOK = hty + dy / 2 + txTotalWidth <= outerHeight; + anchorEndOK = hty - dy / 2 - txTotalWidth >= 0; + if((d.idealAlign === 'top' || !anchorStartOK) && anchorEndOK) { + hty -= dy / 2; + d.anchor = 'end'; + } else if(anchorStartOK) { + hty += dy / 2; + d.anchor = 'start'; + } else d.anchor = 'middle'; + } else { + d.pos = hty; + anchorStartOK = htx + dx / 2 + txTotalWidth <= outerWidth; + anchorEndOK = htx - dx / 2 - txTotalWidth >= 0; + + if((d.idealAlign === 'left' || !anchorStartOK) && anchorEndOK) { + htx -= dx / 2; + d.anchor = 'end'; + } else if(anchorStartOK) { + htx += dx / 2; + d.anchor = 'start'; + } else { + d.anchor = 'middle'; + + var txHalfWidth = txTotalWidth / 2; + var overflowR = htx + txHalfWidth - outerWidth; + var overflowL = htx - txHalfWidth; + if(overflowR > 0) htx -= overflowR; + if(overflowL < 0) htx += -overflowL; + } + } + + tx.attr('text-anchor', d.anchor); + if(tx2width) tx2.attr('text-anchor', d.anchor); + g.attr('transform', strTranslate(htx, hty) + + (rotateLabels ? strRotate(YANGLE) : '')); + }); + + return hoverLabels; +} + +function getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g) { + var name = ''; + var text = ''; + // to get custom 'name' labels pass cleanPoint + if(d.nameOverride !== undefined) d.name = d.nameOverride; + + if(d.name) { + if(d.trace._meta) { + d.name = Lib.templateString(d.name, d.trace._meta); + } + name = plainText(d.name, d.nameLength); + } + + var h0 = hovermode.charAt(0); + var h1 = h0 === 'x' ? 'y' : 'x'; + + if(d.zLabel !== undefined) { + if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '
'; + if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '
'; + if(d.trace.type !== 'choropleth' && d.trace.type !== 'choroplethmapbox') { + text += (text ? 'z: ' : '') + d.zLabel; + } + } else if(showCommonLabel && d[h0 + 'Label'] === t0) { + text = d[h1 + 'Label'] || ''; + } else if(d.xLabel === undefined) { + if(d.yLabel !== undefined && d.trace.type !== 'scattercarpet') { + text = d.yLabel; + } + } else if(d.yLabel === undefined) text = d.xLabel; + else text = '(' + d.xLabel + ', ' + d.yLabel + ')'; + + if((d.text || d.text === 0) && !Array.isArray(d.text)) { + text += (text ? '
' : '') + d.text; + } + + // used by other modules (initially just ternary) that + // manage their own hoverinfo independent of cleanPoint + // the rest of this will still apply, so such modules + // can still put things in (x|y|z)Label, text, and name + // and hoverinfo will still determine their visibility + if(d.extraText !== undefined) text += (text ? '
' : '') + d.extraText; + + // if 'text' is empty at this point, + // and hovertemplate is not defined, + // put 'name' in main label and don't show secondary label + if(g && text === '' && !d.hovertemplate) { + // if 'name' is also empty, remove entire label + if(name === '') g.remove(); + text = name; + } + + // hovertemplate + var hovertemplate = d.hovertemplate || false; + if(hovertemplate) { + var labels = d.hovertemplateLabels || d; + + if(d[h0 + 'Label'] !== t0) { + labels[h0 + 'other'] = labels[h0 + 'Val']; + labels[h0 + 'otherLabel'] = labels[h0 + 'Label']; + } + + text = Lib.hovertemplateString( + hovertemplate, + labels, + fullLayout._d3locale, + d.eventData[0] || {}, + d.trace._meta + ); + + text = text.replace(EXTRA_STRING_REGEX, function(match, extra) { + // assign name for secondary text label + name = plainText(extra, d.nameLength); + // remove from main text label + return ''; + }); + } + return [text, name]; +} + +// Make groups of touching points, and within each group +// move each point so that no labels overlap, but the average +// label position is the same as it was before moving. Incidentally, +// this is equivalent to saying all the labels are on equal linear +// springs about their initial position. Initially, each point is +// its own group, but as we find overlaps we will clump the points. +// +// Also, there are hard constraints at the edges of the graphs, +// that push all groups to the middle so they are visible. I don't +// know what happens if the group spans all the way from one edge to +// the other, though it hardly matters - there's just too much +// information then. +function hoverAvoidOverlaps(hoverLabels, axKey, fullLayout) { + var nummoves = 0; + var axSign = 1; + var nLabels = hoverLabels.size(); + + // make groups of touching points + var pointgroups = new Array(nLabels); + var k = 0; + + hoverLabels.each(function(d) { + var ax = d[axKey]; + var axIsX = ax._id.charAt(0) === 'x'; + var rng = ax.range; + + if(k === 0 && rng && ((rng[0] > rng[1]) !== axIsX)) { + axSign = -1; + } + pointgroups[k++] = [{ + datum: d, + traceIndex: d.trace.index, + dp: 0, + pos: d.pos, + posref: d.posref, + size: d.by * (axIsX ? YFACTOR : 1) / 2, + pmin: 0, + pmax: (axIsX ? fullLayout.width : fullLayout.height) + }]; + }); + + pointgroups.sort(function(a, b) { + return (a[0].posref - b[0].posref) || + // for equal positions, sort trace indices increasing or decreasing + // depending on whether the axis is reversed or not... so stacked + // traces will generally keep their order even if one trace adds + // nothing to the stack. + (axSign * (b[0].traceIndex - a[0].traceIndex)); + }); + + var donepositioning, topOverlap, bottomOverlap, i, j, pti, sumdp; + + function constrainGroup(grp) { + var minPt = grp[0]; + var maxPt = grp[grp.length - 1]; + + // overlap with the top - positive vals are overlaps + topOverlap = minPt.pmin - minPt.pos - minPt.dp + minPt.size; + + // overlap with the bottom - positive vals are overlaps + bottomOverlap = maxPt.pos + maxPt.dp + maxPt.size - minPt.pmax; + + // check for min overlap first, so that we always + // see the largest labels + // allow for .01px overlap, so we don't get an + // infinite loop from rounding errors + if(topOverlap > 0.01) { + for(j = grp.length - 1; j >= 0; j--) grp[j].dp += topOverlap; + donepositioning = false; + } + if(bottomOverlap < 0.01) return; + if(topOverlap < -0.01) { + // make sure we're not pushing back and forth + for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap; + donepositioning = false; + } + if(!donepositioning) return; + + // no room to fix positioning, delete off-screen points + + // first see how many points we need to delete + var deleteCount = 0; + for(i = 0; i < grp.length; i++) { + pti = grp[i]; + if(pti.pos + pti.dp + pti.size > minPt.pmax) deleteCount++; + } + + // start by deleting points whose data is off screen + for(i = grp.length - 1; i >= 0; i--) { + if(deleteCount <= 0) break; + pti = grp[i]; + + // pos has already been constrained to [pmin,pmax] + // so look for points close to that to delete + if(pti.pos > minPt.pmax - 1) { + pti.del = true; + deleteCount--; + } + } + for(i = 0; i < grp.length; i++) { + if(deleteCount <= 0) break; + pti = grp[i]; + + // pos has already been constrained to [pmin,pmax] + // so look for points close to that to delete + if(pti.pos < minPt.pmin + 1) { + pti.del = true; + deleteCount--; + + // shift the whole group minus into this new space + bottomOverlap = pti.size * 2; + for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap; + } + } + // then delete points that go off the bottom + for(i = grp.length - 1; i >= 0; i--) { + if(deleteCount <= 0) break; + pti = grp[i]; + if(pti.pos + pti.dp + pti.size > minPt.pmax) { + pti.del = true; + deleteCount--; + } + } + } + + // loop through groups, combining them if they overlap, + // until nothing moves + while(!donepositioning && nummoves <= nLabels) { + // to avoid infinite loops, don't move more times + // than there are traces + nummoves++; + + // assume nothing will move in this iteration, + // reverse this if it does + donepositioning = true; + i = 0; + while(i < pointgroups.length - 1) { + // the higher (g0) and lower (g1) point group + var g0 = pointgroups[i]; + var g1 = pointgroups[i + 1]; + + // the lowest point in the higher group (p0) + // the highest point in the lower group (p1) + var p0 = g0[g0.length - 1]; + var p1 = g1[0]; + topOverlap = p0.pos + p0.dp + p0.size - p1.pos - p1.dp + p1.size; + + // Only group points that lie on the same axes + if(topOverlap > 0.01 && (p0.pmin === p1.pmin) && (p0.pmax === p1.pmax)) { + // push the new point(s) added to this group out of the way + for(j = g1.length - 1; j >= 0; j--) g1[j].dp += topOverlap; + + // add them to the group + g0.push.apply(g0, g1); + pointgroups.splice(i + 1, 1); + + // adjust for minimum average movement + sumdp = 0; + for(j = g0.length - 1; j >= 0; j--) sumdp += g0[j].dp; + bottomOverlap = sumdp / g0.length; + for(j = g0.length - 1; j >= 0; j--) g0[j].dp -= bottomOverlap; + donepositioning = false; + } else i++; + } + + // check if we're going off the plot on either side and fix + pointgroups.forEach(constrainGroup); + } + + // now put these offsets into hoverData + for(i = pointgroups.length - 1; i >= 0; i--) { + var grp = pointgroups[i]; + for(j = grp.length - 1; j >= 0; j--) { + var pt = grp[j]; + var hoverPt = pt.datum; + hoverPt.offset = pt.dp; + hoverPt.del = pt.del; + } + } +} + +function alignHoverText(hoverLabels, rotateLabels, scaleX, scaleY) { + var pX = function(x) { return x * scaleX; }; + var pY = function(y) { return y * scaleY; }; + + // finally set the text positioning relative to the data and draw the + // box around it + hoverLabels.each(function(d) { + var g = d3.select(this); + if(d.del) return g.remove(); + + var tx = g.select('text.nums'); + var anchor = d.anchor; + var horzSign = anchor === 'end' ? -1 : 1; + var alignShift = {start: 1, end: -1, middle: 0}[anchor]; + var txx = alignShift * (HOVERARROWSIZE + HOVERTEXTPAD); + var tx2x = txx + alignShift * (d.txwidth + HOVERTEXTPAD); + var offsetX = 0; + var offsetY = d.offset; + + var isMiddle = anchor === 'middle'; + if(isMiddle) { + txx -= d.tx2width / 2; + tx2x += d.txwidth / 2 + HOVERTEXTPAD; + } + if(rotateLabels) { + offsetY *= -YSHIFTY; + offsetX = d.offset * YSHIFTX; + } + + g.select('path') + .attr('d', isMiddle ? + // middle aligned: rect centered on data + ('M-' + pX(d.bx / 2 + d.tx2width / 2) + ',' + pY(offsetY - d.by / 2) + + 'h' + pX(d.bx) + 'v' + pY(d.by) + 'h-' + pX(d.bx) + 'Z') : + // left or right aligned: side rect with arrow to data + ('M0,0L' + pX(horzSign * HOVERARROWSIZE + offsetX) + ',' + pY(HOVERARROWSIZE + offsetY) + + 'v' + pY(d.by / 2 - HOVERARROWSIZE) + + 'h' + pX(horzSign * d.bx) + + 'v-' + pY(d.by) + + 'H' + pX(horzSign * HOVERARROWSIZE + offsetX) + + 'V' + pY(offsetY - HOVERARROWSIZE) + + 'Z')); + + var posX = offsetX + txx; + var posY = offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD; + var textAlign = d.textAlign || 'auto'; + + if(textAlign !== 'auto') { + if(textAlign === 'left' && anchor !== 'start') { + tx.attr('text-anchor', 'start'); + posX = isMiddle ? + -d.bx / 2 - d.tx2width / 2 + HOVERTEXTPAD : + -d.bx - HOVERTEXTPAD; + } else if(textAlign === 'right' && anchor !== 'end') { + tx.attr('text-anchor', 'end'); + posX = isMiddle ? + d.bx / 2 - d.tx2width / 2 - HOVERTEXTPAD : + d.bx + HOVERTEXTPAD; + } + } + + tx.call(svgTextUtils.positionText, pX(posX), pY(posY)); + + if(d.tx2width) { + g.select('text.name') + .call(svgTextUtils.positionText, + pX(tx2x + alignShift * HOVERTEXTPAD + offsetX), + pY(offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD)); + g.select('rect') + .call(Drawing.setRect, + pX(tx2x + (alignShift - 1) * d.tx2width / 2 + offsetX), + pY(offsetY - d.by / 2 - 1), + pX(d.tx2width), pY(d.by + 2)); + } + }); +} + +function cleanPoint(d, hovermode) { + var index = d.index; + var trace = d.trace || {}; + var cd0 = d.cd[0]; + var cd = d.cd[index] || {}; + + function pass(v) { + return v || (isNumeric(v) && v === 0); + } + + var getVal = Array.isArray(index) ? + function(calcKey, traceKey) { + var v = Lib.castOption(cd0, index, calcKey); + return pass(v) ? v : Lib.extractOption({}, trace, '', traceKey); + } : + function(calcKey, traceKey) { + return Lib.extractOption(cd, trace, calcKey, traceKey); + }; + + function fill(key, calcKey, traceKey) { + var val = getVal(calcKey, traceKey); + if(pass(val)) d[key] = val; + } + + fill('hoverinfo', 'hi', 'hoverinfo'); + fill('bgcolor', 'hbg', 'hoverlabel.bgcolor'); + fill('borderColor', 'hbc', 'hoverlabel.bordercolor'); + fill('fontFamily', 'htf', 'hoverlabel.font.family'); + fill('fontSize', 'hts', 'hoverlabel.font.size'); + fill('fontColor', 'htc', 'hoverlabel.font.color'); + fill('nameLength', 'hnl', 'hoverlabel.namelength'); + fill('textAlign', 'hta', 'hoverlabel.align'); + + d.posref = (hovermode === 'y' || (hovermode === 'closest' && trace.orientation === 'h')) ? + (d.xa._offset + (d.x0 + d.x1) / 2) : + (d.ya._offset + (d.y0 + d.y1) / 2); + + // then constrain all the positions to be on the plot + d.x0 = Lib.constrain(d.x0, 0, d.xa._length); + d.x1 = Lib.constrain(d.x1, 0, d.xa._length); + d.y0 = Lib.constrain(d.y0, 0, d.ya._length); + d.y1 = Lib.constrain(d.y1, 0, d.ya._length); + + // and convert the x and y label values into formatted text + if(d.xLabelVal !== undefined) { + d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal, trace.xhoverformat); + d.xVal = d.xa.c2d(d.xLabelVal); + } + if(d.yLabelVal !== undefined) { + d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal, trace.yhoverformat); + d.yVal = d.ya.c2d(d.yLabelVal); + } + + // Traces like heatmaps generate the zLabel in their hoverPoints function + if(d.zLabelVal !== undefined && d.zLabel === undefined) { + d.zLabel = String(d.zLabelVal); + } + + // for box means and error bars, add the range to the label + if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) { + var xeText = Axes.tickText(d.xa, d.xa.c2l(d.xerr), 'hover').text; + if(d.xerrneg !== undefined) { + d.xLabel += ' +' + xeText + ' / -' + + Axes.tickText(d.xa, d.xa.c2l(d.xerrneg), 'hover').text; + } else d.xLabel += ' ± ' + xeText; + + // small distance penalty for error bars, so that if there are + // traces with errors and some without, the error bar label will + // hoist up to the point + if(hovermode === 'x') d.distance += 1; + } + if(!isNaN(d.yerr) && !(d.ya.type === 'log' && d.yerr <= 0)) { + var yeText = Axes.tickText(d.ya, d.ya.c2l(d.yerr), 'hover').text; + if(d.yerrneg !== undefined) { + d.yLabel += ' +' + yeText + ' / -' + + Axes.tickText(d.ya, d.ya.c2l(d.yerrneg), 'hover').text; + } else d.yLabel += ' ± ' + yeText; + + if(hovermode === 'y') d.distance += 1; + } + + var infomode = d.hoverinfo || d.trace.hoverinfo; + + if(infomode && infomode !== 'all') { + infomode = Array.isArray(infomode) ? infomode : infomode.split('+'); + if(infomode.indexOf('x') === -1) d.xLabel = undefined; + if(infomode.indexOf('y') === -1) d.yLabel = undefined; + if(infomode.indexOf('z') === -1) d.zLabel = undefined; + if(infomode.indexOf('text') === -1) d.text = undefined; + if(infomode.indexOf('name') === -1) d.name = undefined; + } + + return d; +} + +function createSpikelines(gd, closestPoints, opts) { + var container = opts.container; + var fullLayout = opts.fullLayout; + var gs = fullLayout._size; + var evt = opts.event; + var showY = !!closestPoints.hLinePoint; + var showX = !!closestPoints.vLinePoint; + + var xa, ya; + + // Remove old spikeline items + container.selectAll('.spikeline').remove(); + + if(!(showX || showY)) return; + + var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor); + + // Horizontal line (to y-axis) + if(showY) { + var hLinePoint = closestPoints.hLinePoint; + var hLinePointX, hLinePointY; + + xa = hLinePoint && hLinePoint.xa; + ya = hLinePoint && hLinePoint.ya; + var ySnap = ya.spikesnap; + + if(ySnap === 'cursor') { + hLinePointX = evt.pointerX; + hLinePointY = evt.pointerY; + } else { + hLinePointX = xa._offset + hLinePoint.x; + hLinePointY = ya._offset + hLinePoint.y; + } + var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ? + Color.contrast(contrastColor) : hLinePoint.color; + var yMode = ya.spikemode; + var yThickness = ya.spikethickness; + var yColor = ya.spikecolor || dfltHLineColor; + var xEdge = Axes.getPxPosition(gd, ya); + var xBase, xEndSpike; + + if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) { + if(yMode.indexOf('toaxis') !== -1) { + xBase = xEdge; + xEndSpike = hLinePointX; + } + if(yMode.indexOf('across') !== -1) { + var xAcross0 = ya._counterDomainMin; + var xAcross1 = ya._counterDomainMax; + if(ya.anchor === 'free') { + xAcross0 = Math.min(xAcross0, ya.position); + xAcross1 = Math.max(xAcross1, ya.position); + } + xBase = gs.l + xAcross0 * gs.w; + xEndSpike = gs.l + xAcross1 * gs.w; + } + + // Foreground horizontal line (to y-axis) + container.insert('line', ':first-child') + .attr({ + x1: xBase, + x2: xEndSpike, + y1: hLinePointY, + y2: hLinePointY, + 'stroke-width': yThickness, + stroke: yColor, + 'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness) + }) + .classed('spikeline', true) + .classed('crisp', true); + + // Background horizontal Line (to y-axis) + container.insert('line', ':first-child') + .attr({ + x1: xBase, + x2: xEndSpike, + y1: hLinePointY, + y2: hLinePointY, + 'stroke-width': yThickness + 2, + stroke: contrastColor + }) + .classed('spikeline', true) + .classed('crisp', true); + } + // Y axis marker + if(yMode.indexOf('marker') !== -1) { + container.insert('circle', ':first-child') + .attr({ + cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness), + cy: hLinePointY, + r: yThickness, + fill: yColor + }) + .classed('spikeline', true); + } + } + + if(showX) { + var vLinePoint = closestPoints.vLinePoint; + var vLinePointX, vLinePointY; + + xa = vLinePoint && vLinePoint.xa; + ya = vLinePoint && vLinePoint.ya; + var xSnap = xa.spikesnap; + + if(xSnap === 'cursor') { + vLinePointX = evt.pointerX; + vLinePointY = evt.pointerY; + } else { + vLinePointX = xa._offset + vLinePoint.x; + vLinePointY = ya._offset + vLinePoint.y; + } + var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ? + Color.contrast(contrastColor) : vLinePoint.color; + var xMode = xa.spikemode; + var xThickness = xa.spikethickness; + var xColor = xa.spikecolor || dfltVLineColor; + var yEdge = Axes.getPxPosition(gd, xa); + var yBase, yEndSpike; + + if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) { + if(xMode.indexOf('toaxis') !== -1) { + yBase = yEdge; + yEndSpike = vLinePointY; + } + if(xMode.indexOf('across') !== -1) { + var yAcross0 = xa._counterDomainMin; + var yAcross1 = xa._counterDomainMax; + if(xa.anchor === 'free') { + yAcross0 = Math.min(yAcross0, xa.position); + yAcross1 = Math.max(yAcross1, xa.position); + } + yBase = gs.t + (1 - yAcross1) * gs.h; + yEndSpike = gs.t + (1 - yAcross0) * gs.h; + } + + // Foreground vertical line (to x-axis) + container.insert('line', ':first-child') + .attr({ + x1: vLinePointX, + x2: vLinePointX, + y1: yBase, + y2: yEndSpike, + 'stroke-width': xThickness, + stroke: xColor, + 'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness) + }) + .classed('spikeline', true) + .classed('crisp', true); + + // Background vertical line (to x-axis) + container.insert('line', ':first-child') + .attr({ + x1: vLinePointX, + x2: vLinePointX, + y1: yBase, + y2: yEndSpike, + 'stroke-width': xThickness + 2, + stroke: contrastColor + }) + .classed('spikeline', true) + .classed('crisp', true); + } + + // X axis marker + if(xMode.indexOf('marker') !== -1) { + container.insert('circle', ':first-child') + .attr({ + cx: vLinePointX, + cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness), + r: xThickness, + fill: xColor + }) + .classed('spikeline', true); + } + } +} + +function hoverChanged(gd, evt, oldhoverdata) { + // don't emit any events if nothing changed + if(!oldhoverdata || oldhoverdata.length !== gd._hoverdata.length) return true; + + for(var i = oldhoverdata.length - 1; i >= 0; i--) { + var oldPt = oldhoverdata[i]; + var newPt = gd._hoverdata[i]; + + if(oldPt.curveNumber !== newPt.curveNumber || + String(oldPt.pointNumber) !== String(newPt.pointNumber) || + String(oldPt.pointNumbers) !== String(newPt.pointNumbers) + ) { + return true; + } + } + return false; +} + +function spikesChanged(gd, oldspikepoints) { + // don't relayout the plot because of new spikelines if spikelines points didn't change + if(!oldspikepoints) return true; + if(oldspikepoints.vLinePoint !== gd._spikepoints.vLinePoint || + oldspikepoints.hLinePoint !== gd._spikepoints.hLinePoint + ) return true; + return false; +} + +function plainText(s, len) { + return svgTextUtils.plainText(s || '', { + len: len, + allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em'] + }); +} + +function orderRangePoints(hoverData, hovermode) { + var axLetter = hovermode.charAt(0); + + var first = []; + var second = []; + var last = []; + + for(var i = 0; i < hoverData.length; i++) { + var d = hoverData[i]; + + if( + Registry.traceIs(d.trace, 'bar-like') || + Registry.traceIs(d.trace, 'box-violin') + ) { + last.push(d); + } else if(d.trace[axLetter + 'period']) { + second.push(d); + } else { + first.push(d); + } + } + + return first.concat(second).concat(last); +} + +function getCoord(axLetter, winningPoint, fullLayout) { + var ax = winningPoint[axLetter + 'a']; + var val = winningPoint[axLetter + 'Val']; + + if(ax.type === 'category') val = ax._categoriesMap[val]; + else if(ax.type === 'date') val = ax.d2c(val); + + var cd0 = winningPoint.cd[winningPoint.index]; + if(cd0 && cd0.t && cd0.t.posLetter === ax._id) { + if( + fullLayout.boxmode === 'group' || + fullLayout.violinmode === 'group' + ) { + val += cd0.t.dPos; + } + } + + return val; +} + +},{"../../lib":285,"../../lib/events":278,"../../lib/override_cursor":296,"../../lib/svg_text_utils":307,"../../plots/cartesian/axes":331,"../../registry":373,"../color":155,"../dragelement":174,"../drawing":177,"../legend/defaults":207,"../legend/draw":208,"./constants":189,"./helpers":191,"@plotly/d3":20,"fast-isnumeric":31,"tinycolor2":119}],193:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../color'); +var isUnifiedHover = _dereq_('./helpers').isUnifiedHover; + +module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) { + opts = opts || {}; + + function inheritFontAttr(attr) { + if(!opts.font[attr]) { + opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr]; + } + } + + // In unified hover, inherit from layout.legend if available or layout + if(contOut && isUnifiedHover(contOut.hovermode)) { + if(!opts.font) opts.font = {}; + inheritFontAttr('size'); + inheritFontAttr('family'); + inheritFontAttr('color'); + + if(contOut.legend) { + if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor); + if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor; + } else { + if(!opts.bgcolor) opts.bgcolor = contOut.paper_bgcolor; + } + } + + coerce('hoverlabel.bgcolor', opts.bgcolor); + coerce('hoverlabel.bordercolor', opts.bordercolor); + coerce('hoverlabel.namelength', opts.namelength); + Lib.coerceFont(coerce, 'hoverlabel.font', opts.font); + coerce('hoverlabel.align', opts.align); +}; + +},{"../../lib":285,"../color":155,"./helpers":191}],194:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var layoutAttributes = _dereq_('./layout_attributes'); + +module.exports = function handleHoverModeDefaults(layoutIn, layoutOut) { + function coerce(attr, dflt) { + // don't coerce if it is already coerced in other place e.g. in cartesian defaults + if(layoutOut[attr] !== undefined) return layoutOut[attr]; + + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + + coerce('clickmode'); + return coerce('hovermode'); +}; + +},{"../../lib":285,"./layout_attributes":196}],195:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Lib = _dereq_('../../lib'); +var dragElement = _dereq_('../dragelement'); +var helpers = _dereq_('./helpers'); +var layoutAttributes = _dereq_('./layout_attributes'); +var hoverModule = _dereq_('./hover'); + +module.exports = { + moduleType: 'component', + name: 'fx', + + constants: _dereq_('./constants'), + schema: { + layout: layoutAttributes + }, + + attributes: _dereq_('./attributes'), + layoutAttributes: layoutAttributes, + + supplyLayoutGlobalDefaults: _dereq_('./layout_global_defaults'), + supplyDefaults: _dereq_('./defaults'), + supplyLayoutDefaults: _dereq_('./layout_defaults'), + + calc: _dereq_('./calc'), + + getDistanceFunction: helpers.getDistanceFunction, + getClosest: helpers.getClosest, + inbox: helpers.inbox, + quadrature: helpers.quadrature, + appendArrayPointValue: helpers.appendArrayPointValue, + + castHoverOption: castHoverOption, + castHoverinfo: castHoverinfo, + + hover: hoverModule.hover, + unhover: dragElement.unhover, + + loneHover: hoverModule.loneHover, + loneUnhover: loneUnhover, + + click: _dereq_('./click') +}; + +function loneUnhover(containerOrSelection) { + // duck type whether the arg is a d3 selection because ie9 doesn't + // handle instanceof like modern browsers do. + var selection = Lib.isD3Selection(containerOrSelection) ? + containerOrSelection : + d3.select(containerOrSelection); + + selection.selectAll('g.hovertext').remove(); + selection.selectAll('.spikeline').remove(); +} + +// helpers for traces that use Fx.loneHover + +function castHoverOption(trace, ptNumber, attr) { + return Lib.castOption(trace, ptNumber, 'hoverlabel.' + attr); +} + +function castHoverinfo(trace, fullLayout, ptNumber) { + function _coerce(val) { + return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout); + } + + return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce); +} + +},{"../../lib":285,"../dragelement":174,"./attributes":186,"./calc":187,"./click":188,"./constants":189,"./defaults":190,"./helpers":191,"./hover":192,"./layout_attributes":196,"./layout_defaults":197,"./layout_global_defaults":198,"@plotly/d3":20}],196:[function(_dereq_,module,exports){ +'use strict'; + +var constants = _dereq_('./constants'); + +var fontAttrs = _dereq_('../../plots/font_attributes')({ + editType: 'none', +}); +fontAttrs.family.dflt = constants.HOVERFONT; +fontAttrs.size.dflt = constants.HOVERFONTSIZE; + +module.exports = { + clickmode: { + valType: 'flaglist', + flags: ['event', 'select'], + dflt: 'event', + editType: 'plot', + extras: ['none'], + }, + dragmode: { + valType: 'enumerated', + values: [ + 'zoom', + 'pan', + 'select', + 'lasso', + 'drawclosedpath', + 'drawopenpath', + 'drawline', + 'drawrect', + 'drawcircle', + 'orbit', + 'turntable', + false + ], + dflt: 'zoom', + editType: 'modebar', + }, + hovermode: { + valType: 'enumerated', + values: ['x', 'y', 'closest', false, 'x unified', 'y unified'], + dflt: 'closest', + editType: 'modebar', + }, + hoverdistance: { + valType: 'integer', + min: -1, + dflt: 20, + editType: 'none', + }, + spikedistance: { + valType: 'integer', + min: -1, + dflt: -1, + editType: 'none', + }, + hoverlabel: { + bgcolor: { + valType: 'color', + editType: 'none', + }, + bordercolor: { + valType: 'color', + editType: 'none', + }, + font: fontAttrs, + align: { + valType: 'enumerated', + values: ['left', 'right', 'auto'], + dflt: 'auto', + editType: 'none', + }, + namelength: { + valType: 'integer', + min: -1, + dflt: 15, + editType: 'none', + }, + editType: 'none' + }, + selectdirection: { + valType: 'enumerated', + values: ['h', 'v', 'd', 'any'], + dflt: 'any', + editType: 'none' + } +}; + +},{"../../plots/font_attributes":360,"./constants":189}],197:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var layoutAttributes = _dereq_('./layout_attributes'); +var handleHoverModeDefaults = _dereq_('./hovermode_defaults'); +var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults'); + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + + var hoverMode = handleHoverModeDefaults(layoutIn, layoutOut); + if(hoverMode) { + coerce('hoverdistance'); + coerce('spikedistance'); + } + + var dragMode = coerce('dragmode'); + if(dragMode === 'select') coerce('selectdirection'); + + // if only mapbox or geo subplots is present on graph, + // reset 'zoom' dragmode to 'pan' until 'zoom' is implemented, + // so that the correct modebar button is active + var hasMapbox = layoutOut._has('mapbox'); + var hasGeo = layoutOut._has('geo'); + var len = layoutOut._basePlotModules.length; + + if(layoutOut.dragmode === 'zoom' && ( + ((hasMapbox || hasGeo) && len === 1) || + (hasMapbox && hasGeo && len === 2) + )) { + layoutOut.dragmode = 'pan'; + } + + handleHoverLabelDefaults(layoutIn, layoutOut, coerce); +}; + +},{"../../lib":285,"./hoverlabel_defaults":193,"./hovermode_defaults":194,"./layout_attributes":196}],198:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults'); +var layoutAttributes = _dereq_('./layout_attributes'); + +module.exports = function supplyLayoutGlobalDefaults(layoutIn, layoutOut) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + + handleHoverLabelDefaults(layoutIn, layoutOut, coerce); +}; + +},{"../../lib":285,"./hoverlabel_defaults":193,"./layout_attributes":196}],199:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var counterRegex = _dereq_('../../lib/regex').counter; +var domainAttrs = _dereq_('../../plots/domain').attributes; +var cartesianIdRegex = _dereq_('../../plots/cartesian/constants').idRegex; +var Template = _dereq_('../../plot_api/plot_template'); + +var gridAttrs = { + rows: { + valType: 'integer', + min: 1, + editType: 'plot', + }, + roworder: { + valType: 'enumerated', + values: ['top to bottom', 'bottom to top'], + dflt: 'top to bottom', + editType: 'plot', + }, + columns: { + valType: 'integer', + min: 1, + editType: 'plot', + }, + subplots: { + valType: 'info_array', + freeLength: true, + dimensions: 2, + items: {valType: 'enumerated', values: [counterRegex('xy').toString(), ''], editType: 'plot'}, + editType: 'plot', + }, + xaxes: { + valType: 'info_array', + freeLength: true, + items: {valType: 'enumerated', values: [cartesianIdRegex.x.toString(), ''], editType: 'plot'}, + editType: 'plot', + }, + yaxes: { + valType: 'info_array', + freeLength: true, + items: {valType: 'enumerated', values: [cartesianIdRegex.y.toString(), ''], editType: 'plot'}, + editType: 'plot', + }, + pattern: { + valType: 'enumerated', + values: ['independent', 'coupled'], + dflt: 'coupled', + editType: 'plot', + }, + xgap: { + valType: 'number', + min: 0, + max: 1, + editType: 'plot', + }, + ygap: { + valType: 'number', + min: 0, + max: 1, + editType: 'plot', + }, + domain: domainAttrs({name: 'grid', editType: 'plot', noGridCell: true}, { + }), + xside: { + valType: 'enumerated', + values: ['bottom', 'bottom plot', 'top plot', 'top'], + dflt: 'bottom plot', + editType: 'plot', + }, + yside: { + valType: 'enumerated', + values: ['left', 'left plot', 'right plot', 'right'], + dflt: 'left plot', + editType: 'plot', + }, + editType: 'plot' +}; + +function getAxes(layout, grid, axLetter) { + var gridVal = grid[axLetter + 'axes']; + var splomVal = Object.keys((layout._splomAxes || {})[axLetter] || {}); + + if(Array.isArray(gridVal)) return gridVal; + if(splomVal.length) return splomVal; +} + +// the shape of the grid - this needs to be done BEFORE supplyDataDefaults +// so that non-subplot traces can place themselves in the grid +function sizeDefaults(layoutIn, layoutOut) { + var gridIn = layoutIn.grid || {}; + var xAxes = getAxes(layoutOut, gridIn, 'x'); + var yAxes = getAxes(layoutOut, gridIn, 'y'); + + if(!layoutIn.grid && !xAxes && !yAxes) return; + + var hasSubplotGrid = Array.isArray(gridIn.subplots) && Array.isArray(gridIn.subplots[0]); + var hasXaxes = Array.isArray(xAxes); + var hasYaxes = Array.isArray(yAxes); + var isSplomGenerated = ( + hasXaxes && xAxes !== gridIn.xaxes && + hasYaxes && yAxes !== gridIn.yaxes + ); + + var dfltRows, dfltColumns; + + if(hasSubplotGrid) { + dfltRows = gridIn.subplots.length; + dfltColumns = gridIn.subplots[0].length; + } else { + if(hasYaxes) dfltRows = yAxes.length; + if(hasXaxes) dfltColumns = xAxes.length; + } + + var gridOut = Template.newContainer(layoutOut, 'grid'); + + function coerce(attr, dflt) { + return Lib.coerce(gridIn, gridOut, gridAttrs, attr, dflt); + } + + var rows = coerce('rows', dfltRows); + var columns = coerce('columns', dfltColumns); + + if(!(rows * columns > 1)) { + delete layoutOut.grid; + return; + } + + if(!hasSubplotGrid && !hasXaxes && !hasYaxes) { + var useDefaultSubplots = coerce('pattern') === 'independent'; + if(useDefaultSubplots) hasSubplotGrid = true; + } + gridOut._hasSubplotGrid = hasSubplotGrid; + + var rowOrder = coerce('roworder'); + var reversed = rowOrder === 'top to bottom'; + + var dfltGapX = hasSubplotGrid ? 0.2 : 0.1; + var dfltGapY = hasSubplotGrid ? 0.3 : 0.1; + + var dfltSideX, dfltSideY; + if(isSplomGenerated && layoutOut._splomGridDflt) { + dfltSideX = layoutOut._splomGridDflt.xside; + dfltSideY = layoutOut._splomGridDflt.yside; + } + + gridOut._domains = { + x: fillGridPositions('x', coerce, dfltGapX, dfltSideX, columns), + y: fillGridPositions('y', coerce, dfltGapY, dfltSideY, rows, reversed) + }; +} + +// coerce x or y sizing attributes and return an array of domains for this direction +function fillGridPositions(axLetter, coerce, dfltGap, dfltSide, len, reversed) { + var dirGap = coerce(axLetter + 'gap', dfltGap); + var domain = coerce('domain.' + axLetter); + coerce(axLetter + 'side', dfltSide); + + var out = new Array(len); + var start = domain[0]; + var step = (domain[1] - start) / (len - dirGap); + var cellDomain = step * (1 - dirGap); + for(var i = 0; i < len; i++) { + var cellStart = start + step * i; + out[reversed ? (len - 1 - i) : i] = [cellStart, cellStart + cellDomain]; + } + return out; +} + +// the (cartesian) contents of the grid - this needs to happen AFTER supplyDataDefaults +// so that we know what cartesian subplots are available +function contentDefaults(layoutIn, layoutOut) { + var gridOut = layoutOut.grid; + // make sure we got to the end of handleGridSizing + if(!gridOut || !gridOut._domains) return; + + var gridIn = layoutIn.grid || {}; + var subplots = layoutOut._subplots; + var hasSubplotGrid = gridOut._hasSubplotGrid; + var rows = gridOut.rows; + var columns = gridOut.columns; + var useDefaultSubplots = gridOut.pattern === 'independent'; + + var i, j, xId, yId, subplotId, subplotsOut, yPos; + + var axisMap = gridOut._axisMap = {}; + + if(hasSubplotGrid) { + var subplotsIn = gridIn.subplots || []; + subplotsOut = gridOut.subplots = new Array(rows); + var index = 1; + + for(i = 0; i < rows; i++) { + var rowOut = subplotsOut[i] = new Array(columns); + var rowIn = subplotsIn[i] || []; + for(j = 0; j < columns; j++) { + if(useDefaultSubplots) { + subplotId = (index === 1) ? 'xy' : ('x' + index + 'y' + index); + index++; + } else subplotId = rowIn[j]; + + rowOut[j] = ''; + + if(subplots.cartesian.indexOf(subplotId) !== -1) { + yPos = subplotId.indexOf('y'); + xId = subplotId.slice(0, yPos); + yId = subplotId.slice(yPos); + if((axisMap[xId] !== undefined && axisMap[xId] !== j) || + (axisMap[yId] !== undefined && axisMap[yId] !== i) + ) { + continue; + } + + rowOut[j] = subplotId; + axisMap[xId] = j; + axisMap[yId] = i; + } + } + } + } else { + var xAxes = getAxes(layoutOut, gridIn, 'x'); + var yAxes = getAxes(layoutOut, gridIn, 'y'); + gridOut.xaxes = fillGridAxes(xAxes, subplots.xaxis, columns, axisMap, 'x'); + gridOut.yaxes = fillGridAxes(yAxes, subplots.yaxis, rows, axisMap, 'y'); + } + + var anchors = gridOut._anchors = {}; + var reversed = gridOut.roworder === 'top to bottom'; + + for(var axisId in axisMap) { + var axLetter = axisId.charAt(0); + var side = gridOut[axLetter + 'side']; + + var i0, inc, iFinal; + + if(side.length < 8) { + // grid edge - ie not "* plot" - make these as free axes + // since we're not guaranteed to have a subplot there at all + anchors[axisId] = 'free'; + } else if(axLetter === 'x') { + if((side.charAt(0) === 't') === reversed) { + i0 = 0; + inc = 1; + iFinal = rows; + } else { + i0 = rows - 1; + inc = -1; + iFinal = -1; + } + if(hasSubplotGrid) { + var column = axisMap[axisId]; + for(i = i0; i !== iFinal; i += inc) { + subplotId = subplotsOut[i][column]; + if(!subplotId) continue; + yPos = subplotId.indexOf('y'); + if(subplotId.slice(0, yPos) === axisId) { + anchors[axisId] = subplotId.slice(yPos); + break; + } + } + } else { + for(i = i0; i !== iFinal; i += inc) { + yId = gridOut.yaxes[i]; + if(subplots.cartesian.indexOf(axisId + yId) !== -1) { + anchors[axisId] = yId; + break; + } + } + } + } else { + if((side.charAt(0) === 'l')) { + i0 = 0; + inc = 1; + iFinal = columns; + } else { + i0 = columns - 1; + inc = -1; + iFinal = -1; + } + if(hasSubplotGrid) { + var row = axisMap[axisId]; + for(i = i0; i !== iFinal; i += inc) { + subplotId = subplotsOut[row][i]; + if(!subplotId) continue; + yPos = subplotId.indexOf('y'); + if(subplotId.slice(yPos) === axisId) { + anchors[axisId] = subplotId.slice(0, yPos); + break; + } + } + } else { + for(i = i0; i !== iFinal; i += inc) { + xId = gridOut.xaxes[i]; + if(subplots.cartesian.indexOf(xId + axisId) !== -1) { + anchors[axisId] = xId; + break; + } + } + } + } + } +} + +function fillGridAxes(axesIn, axesAllowed, len, axisMap, axLetter) { + var out = new Array(len); + var i; + + function fillOneAxis(i, axisId) { + if(axesAllowed.indexOf(axisId) !== -1 && axisMap[axisId] === undefined) { + out[i] = axisId; + axisMap[axisId] = i; + } else out[i] = ''; + } + + if(Array.isArray(axesIn)) { + for(i = 0; i < len; i++) { + fillOneAxis(i, axesIn[i]); + } + } else { + // default axis list is the first `len` axis ids + fillOneAxis(0, axLetter); + for(i = 1; i < len; i++) { + fillOneAxis(i, axLetter + (i + 1)); + } + } + + return out; +} + +module.exports = { + moduleType: 'component', + name: 'grid', + + schema: { + layout: {grid: gridAttrs} + }, + + layoutAttributes: gridAttrs, + sizeDefaults: sizeDefaults, + contentDefaults: contentDefaults +}; + +},{"../../lib":285,"../../lib/regex":301,"../../plot_api/plot_template":320,"../../plots/cartesian/constants":338,"../../plots/domain":359}],200:[function(_dereq_,module,exports){ +'use strict'; + +var cartesianConstants = _dereq_('../../plots/cartesian/constants'); +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; +var axisPlaceableObjs = _dereq_('../../constants/axis_placeable_objects'); + + +module.exports = templatedArray('image', { + visible: { + valType: 'boolean', + dflt: true, + editType: 'arraydraw', + }, + + source: { + valType: 'string', + editType: 'arraydraw', + }, + + layer: { + valType: 'enumerated', + values: ['below', 'above'], + dflt: 'above', + editType: 'arraydraw', + }, + + sizex: { + valType: 'number', + dflt: 0, + editType: 'arraydraw', + }, + + sizey: { + valType: 'number', + dflt: 0, + editType: 'arraydraw', + }, + + sizing: { + valType: 'enumerated', + values: ['fill', 'contain', 'stretch'], + dflt: 'contain', + editType: 'arraydraw', + }, + + opacity: { + valType: 'number', + min: 0, + max: 1, + dflt: 1, + editType: 'arraydraw', + }, + + x: { + valType: 'any', + dflt: 0, + editType: 'arraydraw', + }, + + y: { + valType: 'any', + dflt: 0, + editType: 'arraydraw', + }, + + xanchor: { + valType: 'enumerated', + values: ['left', 'center', 'right'], + dflt: 'left', + editType: 'arraydraw', + }, + + yanchor: { + valType: 'enumerated', + values: ['top', 'middle', 'bottom'], + dflt: 'top', + editType: 'arraydraw', + }, + + xref: { + valType: 'enumerated', + values: [ + 'paper', + cartesianConstants.idRegex.x.toString() + ], + dflt: 'paper', + editType: 'arraydraw', + }, + + yref: { + valType: 'enumerated', + values: [ + 'paper', + cartesianConstants.idRegex.y.toString() + ], + dflt: 'paper', + editType: 'arraydraw', + }, + editType: 'arraydraw' +}); + +},{"../../constants/axis_placeable_objects":261,"../../plot_api/plot_template":320,"../../plots/cartesian/constants":338}],201:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var toLogRange = _dereq_('../../lib/to_log_range'); + +/* + * convertCoords: when converting an axis between log and linear + * you need to alter any images on that axis to keep them + * pointing at the same data point. + * In v3.0 this will become obsolete (or perhaps size will still need conversion?) + * we convert size by declaring that the maximum extent *in data units* should be + * the same, assuming the image is anchored by its center (could remove that restriction + * if we think it's important) even though the actual left and right values will not be + * quite the same since the scale becomes nonlinear (and central anchor means the pixel + * center of the image, not the data units center) + * + * gd: the plot div + * ax: the axis being changed + * newType: the type it's getting + * doExtra: function(attr, val) from inside relayout that sets the attribute. + * Use this to make the changes as it's aware if any other changes in the + * same relayout call should override this conversion. + */ +module.exports = function convertCoords(gd, ax, newType, doExtra) { + ax = ax || {}; + + var toLog = (newType === 'log') && (ax.type === 'linear'); + var fromLog = (newType === 'linear') && (ax.type === 'log'); + + if(!(toLog || fromLog)) return; + + var images = gd._fullLayout.images; + var axLetter = ax._id.charAt(0); + var image; + var attrPrefix; + + for(var i = 0; i < images.length; i++) { + image = images[i]; + attrPrefix = 'images[' + i + '].'; + + if(image[axLetter + 'ref'] === ax._id) { + var currentPos = image[axLetter]; + var currentSize = image['size' + axLetter]; + var newPos = null; + var newSize = null; + + if(toLog) { + newPos = toLogRange(currentPos, ax.range); + + // this is the inverse of the conversion we do in fromLog below + // so that the conversion is reversible (notice the fromLog conversion + // is like sinh, and this one looks like arcsinh) + var dx = currentSize / Math.pow(10, newPos) / 2; + newSize = 2 * Math.log(dx + Math.sqrt(1 + dx * dx)) / Math.LN10; + } else { + newPos = Math.pow(10, currentPos); + newSize = newPos * (Math.pow(10, currentSize / 2) - Math.pow(10, -currentSize / 2)); + } + + // if conversion failed, delete the value so it can get a default later on + if(!isNumeric(newPos)) { + newPos = null; + newSize = null; + } else if(!isNumeric(newSize)) newSize = null; + + doExtra(attrPrefix + axLetter, newPos); + doExtra(attrPrefix + 'size' + axLetter, newSize); + } + } +}; + +},{"../../lib/to_log_range":309,"fast-isnumeric":31}],202:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); + +var attributes = _dereq_('./attributes'); +var name = 'images'; + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + var opts = { + name: name, + handleItemDefaults: imageDefaults + }; + + handleArrayContainerDefaults(layoutIn, layoutOut, opts); +}; + + +function imageDefaults(imageIn, imageOut, fullLayout) { + function coerce(attr, dflt) { + return Lib.coerce(imageIn, imageOut, attributes, attr, dflt); + } + + var source = coerce('source'); + var visible = coerce('visible', !!source); + + if(!visible) return imageOut; + + coerce('layer'); + coerce('xanchor'); + coerce('yanchor'); + coerce('sizex'); + coerce('sizey'); + coerce('sizing'); + coerce('opacity'); + + var gdMock = { _fullLayout: fullLayout }; + var axLetters = ['x', 'y']; + + for(var i = 0; i < 2; i++) { + // 'paper' is the fallback axref + var axLetter = axLetters[i]; + var axRef = Axes.coerceRef(imageIn, imageOut, gdMock, axLetter, 'paper', undefined); + + if(axRef !== 'paper') { + var ax = Axes.getFromId(gdMock, axRef); + ax._imgIndices.push(imageOut._index); + } + + Axes.coercePosition(imageOut, gdMock, coerce, axRef, axLetter, 0); + } + + return imageOut; +} + +},{"../../lib":285,"../../plots/array_container_defaults":326,"../../plots/cartesian/axes":331,"./attributes":200}],203:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Drawing = _dereq_('../drawing'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); +var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces'); + +module.exports = function draw(gd) { + var fullLayout = gd._fullLayout; + var imageDataAbove = []; + var imageDataSubplot = {}; + var imageDataBelow = []; + var subplot; + var i; + + // Sort into top, subplot, and bottom layers + for(i = 0; i < fullLayout.images.length; i++) { + var img = fullLayout.images[i]; + + if(img.visible) { + if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') { + subplot = axisIds.ref2id(img.xref) + axisIds.ref2id(img.yref); + + var plotinfo = fullLayout._plots[subplot]; + + if(!plotinfo) { + // Fall back to _imageLowerLayer in case the requested subplot doesn't exist. + // This can happen if you reference the image to an x / y axis combination + // that doesn't have any data on it (and layer is below) + imageDataBelow.push(img); + continue; + } + + if(plotinfo.mainplot) { + subplot = plotinfo.mainplot.id; + } + + if(!imageDataSubplot[subplot]) { + imageDataSubplot[subplot] = []; + } + imageDataSubplot[subplot].push(img); + } else if(img.layer === 'above') { + imageDataAbove.push(img); + } else { + imageDataBelow.push(img); + } + } + } + + + var anchors = { + x: { + left: { sizing: 'xMin', offset: 0 }, + center: { sizing: 'xMid', offset: -1 / 2 }, + right: { sizing: 'xMax', offset: -1 } + }, + y: { + top: { sizing: 'YMin', offset: 0 }, + middle: { sizing: 'YMid', offset: -1 / 2 }, + bottom: { sizing: 'YMax', offset: -1 } + } + }; + + + // Images must be converted to dataURL's for exporting. + function setImage(d) { + var thisImage = d3.select(this); + + if(this._imgSrc === d.source) { + return; + } + + thisImage.attr('xmlns', xmlnsNamespaces.svg); + + if(d.source && d.source.slice(0, 5) === 'data:') { + thisImage.attr('xlink:href', d.source); + this._imgSrc = d.source; + } else { + var imagePromise = new Promise(function(resolve) { + var img = new Image(); + this.img = img; + + // If not set, a `tainted canvas` error is thrown + img.setAttribute('crossOrigin', 'anonymous'); + img.onerror = errorHandler; + img.onload = function() { + var canvas = document.createElement('canvas'); + canvas.width = this.width; + canvas.height = this.height; + + var ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0); + + var dataURL = canvas.toDataURL('image/png'); + + thisImage.attr('xlink:href', dataURL); + + // resolve promise in onload handler instead of on 'load' to support IE11 + // see https://github.com/plotly/plotly.js/issues/1685 + // for more details + resolve(); + }; + + thisImage.on('error', errorHandler); + + img.src = d.source; + this._imgSrc = d.source; + + function errorHandler() { + thisImage.remove(); + resolve(); + } + }.bind(this)); + + gd._promises.push(imagePromise); + } + } + + function applyAttributes(d) { + var thisImage = d3.select(this); + + // Axes if specified + var xa = Axes.getFromId(gd, d.xref); + var ya = Axes.getFromId(gd, d.yref); + var xIsDomain = Axes.getRefType(d.xref) === 'domain'; + var yIsDomain = Axes.getRefType(d.yref) === 'domain'; + + var size = fullLayout._size; + var width, height; + if(xa !== undefined) { + width = ((typeof(d.xref) === 'string') && xIsDomain) ? + xa._length * d.sizex : + Math.abs(xa.l2p(d.sizex) - xa.l2p(0)); + } else { + width = d.sizex * size.w; + } + if(ya !== undefined) { + height = ((typeof(d.yref) === 'string') && yIsDomain) ? + ya._length * d.sizey : + Math.abs(ya.l2p(d.sizey) - ya.l2p(0)); + } else { + height = d.sizey * size.h; + } + + // Offsets for anchor positioning + var xOffset = width * anchors.x[d.xanchor].offset; + var yOffset = height * anchors.y[d.yanchor].offset; + + var sizing = anchors.x[d.xanchor].sizing + anchors.y[d.yanchor].sizing; + + // Final positions + var xPos, yPos; + if(xa !== undefined) { + xPos = ((typeof(d.xref) === 'string') && xIsDomain) ? + xa._length * d.x + xa._offset : + xa.r2p(d.x) + xa._offset; + } else { + xPos = d.x * size.w + size.l; + } + xPos += xOffset; + if(ya !== undefined) { + yPos = ((typeof(d.yref) === 'string') && yIsDomain) ? + // consistent with "paper" yref value, where positive values + // move up the page + ya._length * (1 - d.y) + ya._offset : + ya.r2p(d.y) + ya._offset; + } else { + yPos = size.h - d.y * size.h + size.t; + } + yPos += yOffset; + + // Construct the proper aspectRatio attribute + switch(d.sizing) { + case 'fill': + sizing += ' slice'; + break; + + case 'stretch': + sizing = 'none'; + break; + } + + thisImage.attr({ + x: xPos, + y: yPos, + width: width, + height: height, + preserveAspectRatio: sizing, + opacity: d.opacity + }); + + + // Set proper clipping on images + var xId = xa && (Axes.getRefType(d.xref) !== 'domain') ? xa._id : ''; + var yId = ya && (Axes.getRefType(d.yref) !== 'domain') ? ya._id : ''; + var clipAxes = xId + yId; + + Drawing.setClipUrl( + thisImage, + clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null, + gd + ); + } + + var imagesBelow = fullLayout._imageLowerLayer.selectAll('image') + .data(imageDataBelow); + var imagesAbove = fullLayout._imageUpperLayer.selectAll('image') + .data(imageDataAbove); + + imagesBelow.enter().append('image'); + imagesAbove.enter().append('image'); + + imagesBelow.exit().remove(); + imagesAbove.exit().remove(); + + imagesBelow.each(function(d) { + setImage.bind(this)(d); + applyAttributes.bind(this)(d); + }); + imagesAbove.each(function(d) { + setImage.bind(this)(d); + applyAttributes.bind(this)(d); + }); + + var allSubplots = Object.keys(fullLayout._plots); + for(i = 0; i < allSubplots.length; i++) { + subplot = allSubplots[i]; + var subplotObj = fullLayout._plots[subplot]; + + // filter out overlaid plots (which have their images on the main plot) + // and gl2d plots (which don't support below images, at least not yet) + if(!subplotObj.imagelayer) continue; + + var imagesOnSubplot = subplotObj.imagelayer.selectAll('image') + // even if there are no images on this subplot, we need to run + // enter and exit in case there were previously + .data(imageDataSubplot[subplot] || []); + + imagesOnSubplot.enter().append('image'); + imagesOnSubplot.exit().remove(); + + imagesOnSubplot.each(function(d) { + setImage.bind(this)(d); + applyAttributes.bind(this)(d); + }); + } +}; + +},{"../../constants/xmlns_namespaces":266,"../../plots/cartesian/axes":331,"../../plots/cartesian/axis_ids":335,"../drawing":177,"@plotly/d3":20}],204:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + moduleType: 'component', + name: 'images', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + includeBasePlot: _dereq_('../../plots/cartesian/include_components')('images'), + + draw: _dereq_('./draw'), + + convertCoords: _dereq_('./convert_coords') +}; + +},{"../../plots/cartesian/include_components":344,"./attributes":200,"./convert_coords":201,"./defaults":202,"./draw":203}],205:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('../../plots/font_attributes'); +var colorAttrs = _dereq_('../color/attributes'); + + +module.exports = { + bgcolor: { + valType: 'color', + editType: 'legend', + }, + bordercolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'legend', + }, + borderwidth: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'legend', + }, + font: fontAttrs({ + editType: 'legend', + }), + orientation: { + valType: 'enumerated', + values: ['v', 'h'], + dflt: 'v', + editType: 'legend', + }, + traceorder: { + valType: 'flaglist', + flags: ['reversed', 'grouped'], + extras: ['normal'], + editType: 'legend', + }, + tracegroupgap: { + valType: 'number', + min: 0, + dflt: 10, + editType: 'legend', + }, + itemsizing: { + valType: 'enumerated', + values: ['trace', 'constant'], + dflt: 'trace', + editType: 'legend', + }, + itemwidth: { + valType: 'number', + min: 30, + dflt: 30, + editType: 'legend', + }, + + itemclick: { + valType: 'enumerated', + values: ['toggle', 'toggleothers', false], + dflt: 'toggle', + editType: 'legend', + }, + itemdoubleclick: { + valType: 'enumerated', + values: ['toggle', 'toggleothers', false], + dflt: 'toggleothers', + editType: 'legend', + }, + + x: { + valType: 'number', + min: -2, + max: 3, + editType: 'legend', + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'left', + editType: 'legend', + }, + y: { + valType: 'number', + min: -2, + max: 3, + editType: 'legend', + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + editType: 'legend', + }, + uirevision: { + valType: 'any', + editType: 'none', + }, + valign: { + valType: 'enumerated', + values: ['top', 'middle', 'bottom'], + dflt: 'middle', + editType: 'legend', + }, + title: { + text: { + valType: 'string', + dflt: '', + editType: 'legend', + }, + font: fontAttrs({ + editType: 'legend', + }), + side: { + valType: 'enumerated', + values: ['top', 'left', 'top left'], + editType: 'legend', + }, + editType: 'legend', + }, + + editType: 'legend' +}; + +},{"../../plots/font_attributes":360,"../color/attributes":154}],206:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + scrollBarWidth: 6, + scrollBarMinHeight: 20, + scrollBarColor: '#808BA4', + scrollBarMargin: 4, + scrollBarEnterAttrs: {rx: 20, ry: 3, width: 0, height: 0}, + + // number of px between legend title and (left) side of legend (always in x direction and from inner border) + titlePad: 2, + // number of px between each legend item (x and/or y direction) + itemGap: 5 +}; + +},{}],207:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Template = _dereq_('../../plot_api/plot_template'); + +var attributes = _dereq_('./attributes'); +var basePlotLayoutAttributes = _dereq_('../../plots/layout_attributes'); +var helpers = _dereq_('./helpers'); + + +module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { + var containerIn = layoutIn.legend || {}; + + var legendTraceCount = 0; + var legendReallyHasATrace = false; + var defaultOrder = 'normal'; + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + + if(!trace.visible) continue; + + // Note that we explicitly count any trace that is either shown or + // *would* be shown by default, toward the two traces you need to + // ensure the legend is shown by default, because this can still help + // disambiguate. + if(trace.showlegend || ( + trace._dfltShowLegend && !( + trace._module && + trace._module.attributes && + trace._module.attributes.showlegend && + trace._module.attributes.showlegend.dflt === false + ) + )) { + legendTraceCount++; + if(trace.showlegend) { + legendReallyHasATrace = true; + // Always show the legend by default if there's a pie, + // or if there's only one trace but it's explicitly shown + if(Registry.traceIs(trace, 'pie-like') || + trace._input.showlegend === true + ) { + legendTraceCount++; + } + } + } + + if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') || + ['tonextx', 'tonexty'].indexOf(trace.fill) !== -1) { + defaultOrder = helpers.isGrouped({traceorder: defaultOrder}) ? + 'grouped+reversed' : 'reversed'; + } + + if(trace.legendgroup !== undefined && trace.legendgroup !== '') { + defaultOrder = helpers.isReversed({traceorder: defaultOrder}) ? + 'reversed+grouped' : 'grouped'; + } + } + + var showLegend = Lib.coerce(layoutIn, layoutOut, + basePlotLayoutAttributes, 'showlegend', + legendReallyHasATrace && legendTraceCount > 1); + + if(showLegend === false && !containerIn.uirevision) return; + + var containerOut = Template.newContainer(layoutOut, 'legend'); + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + coerce('uirevision', layoutOut.uirevision); + + if(showLegend === false) return; + + coerce('bgcolor', layoutOut.paper_bgcolor); + coerce('bordercolor'); + coerce('borderwidth'); + var itemFont = Lib.coerceFont(coerce, 'font', layoutOut.font); + + var orientation = coerce('orientation'); + var isHorizontal = orientation === 'h'; + var defaultX, defaultY, defaultYAnchor; + + if(isHorizontal) { + defaultX = 0; + + if(Registry.getComponentMethod('rangeslider', 'isVisible')(layoutIn.xaxis)) { + defaultY = 1.1; + defaultYAnchor = 'bottom'; + } else { + // maybe use y=1.1 / yanchor=bottom as above + // to avoid https://github.com/plotly/plotly.js/issues/1199 + // in v3 + defaultY = -0.1; + defaultYAnchor = 'top'; + } + } else { + defaultX = 1.02; + defaultY = 1; + defaultYAnchor = 'auto'; + } + + coerce('traceorder', defaultOrder); + if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap'); + + coerce('itemsizing'); + coerce('itemwidth'); + + coerce('itemclick'); + coerce('itemdoubleclick'); + + coerce('x', defaultX); + coerce('xanchor'); + coerce('y', defaultY); + coerce('yanchor', defaultYAnchor); + coerce('valign'); + Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); + + var titleText = coerce('title.text'); + if(titleText) { + coerce('title.side', isHorizontal ? 'left' : 'top'); + var dfltTitleFont = Lib.extendFlat({}, itemFont, { + size: Lib.bigFont(itemFont.size) + }); + + Lib.coerceFont(coerce, 'title.font', dfltTitleFont); + } +}; + +},{"../../lib":285,"../../plot_api/plot_template":320,"../../plots/layout_attributes":364,"../../registry":373,"./attributes":205,"./helpers":211}],208:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Lib = _dereq_('../../lib'); +var Plots = _dereq_('../../plots/plots'); +var Registry = _dereq_('../../registry'); +var Events = _dereq_('../../lib/events'); +var dragElement = _dereq_('../dragelement'); +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var handleClick = _dereq_('./handle_click'); + +var constants = _dereq_('./constants'); +var alignmentConstants = _dereq_('../../constants/alignment'); +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var FROM_TL = alignmentConstants.FROM_TL; +var FROM_BR = alignmentConstants.FROM_BR; + +var getLegendData = _dereq_('./get_legend_data'); +var style = _dereq_('./style'); +var helpers = _dereq_('./helpers'); + +var MAIN_TITLE = 1; + +module.exports = function draw(gd, opts) { + if(!opts) opts = gd._fullLayout.legend || {}; + return _draw(gd, opts); +}; + +function _draw(gd, legendObj) { + var fullLayout = gd._fullLayout; + var clipId = 'legend' + fullLayout._uid; + var layer; + + var inHover = legendObj._inHover; + if(inHover) { + layer = legendObj.layer; + clipId += '-hover'; + } else { + layer = fullLayout._infolayer; + } + + if(!layer) return; + + if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0; + + var legendData; + if(!inHover) { + if(!gd.calcdata) return; + legendData = fullLayout.showlegend && getLegendData(gd.calcdata, legendObj); + } else { + if(!legendObj.entries) return; + legendData = getLegendData(legendObj.entries, legendObj); + } + + var hiddenSlices = fullLayout.hiddenlabels || []; + + if(!inHover && (!fullLayout.showlegend || !legendData.length)) { + layer.selectAll('.legend').remove(); + fullLayout._topdefs.select('#' + clipId).remove(); + return Plots.autoMargin(gd, 'legend'); + } + + var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) { + if(!inHover) s.attr('pointer-events', 'all'); + }); + + var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) { + s.append('rect'); + }); + + var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) { + s.attr('shape-rendering', 'crispEdges'); + }); + bg.call(Color.stroke, legendObj.bordercolor) + .call(Color.fill, legendObj.bgcolor) + .style('stroke-width', legendObj.borderwidth + 'px'); + + var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox'); + + var title = legendObj.title; + legendObj._titleWidth = 0; + legendObj._titleHeight = 0; + if(title.text) { + var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext'); + titleEl.attr('text-anchor', 'start') + .call(Drawing.font, title.font) + .text(title.text); + + textLayout(titleEl, scrollBox, gd, legendObj, MAIN_TITLE); // handle mathjax or multi-line text and compute title height + } else { + scrollBox.selectAll('.legendtitletext').remove(); + } + + var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) { + s.attr(constants.scrollBarEnterAttrs) + .call(Color.fill, constants.scrollBarColor); + }); + + var groups = scrollBox.selectAll('g.groups').data(legendData); + groups.enter().append('g').attr('class', 'groups'); + groups.exit().remove(); + + var traces = groups.selectAll('g.traces').data(Lib.identity); + traces.enter().append('g').attr('class', 'traces'); + traces.exit().remove(); + + traces.style('opacity', function(d) { + var trace = d[0].trace; + if(Registry.traceIs(trace, 'pie-like')) { + return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1; + } else { + return trace.visible === 'legendonly' ? 0.5 : 1; + } + }) + .each(function() { d3.select(this).call(drawTexts, gd, legendObj); }) + .call(style, gd, legendObj) + .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); }); + + Lib.syncOrAsync([ + Plots.previousPromises, + function() { return computeLegendDimensions(gd, groups, traces, legendObj); }, + function() { + // IF expandMargin return a Promise (which is truthy), + // we're under a doAutoMargin redraw, so we don't have to + // draw the remaining pieces below + if(!inHover && expandMargin(gd)) return; + + var gs = fullLayout._size; + var bw = legendObj.borderwidth; + + var lx = gs.l + gs.w * legendObj.x - FROM_TL[getXanchor(legendObj)] * legendObj._width; + var ly = gs.t + gs.h * (1 - legendObj.y) - FROM_TL[getYanchor(legendObj)] * legendObj._effHeight; + + if(!inHover && fullLayout.margin.autoexpand) { + var lx0 = lx; + var ly0 = ly; + + lx = Lib.constrain(lx, 0, fullLayout.width - legendObj._width); + ly = Lib.constrain(ly, 0, fullLayout.height - legendObj._effHeight); + + if(lx !== lx0) { + Lib.log('Constrain legend.x to make legend fit inside graph'); + } + if(ly !== ly0) { + Lib.log('Constrain legend.y to make legend fit inside graph'); + } + } + + // Set size and position of all the elements that make up a legend: + // legend, background and border, scroll box and scroll bar as well as title + if(!inHover) Drawing.setTranslate(legend, lx, ly); + + // to be safe, remove previous listeners + scrollBar.on('.drag', null); + legend.on('wheel', null); + + if(inHover || legendObj._height <= legendObj._maxHeight || gd._context.staticPlot) { + // if scrollbar should not be shown. + var height = legendObj._effHeight; + + // if unified hover, let it be its full size + if(inHover) height = legendObj._height; + + bg.attr({ + width: legendObj._width - bw, + height: height - bw, + x: bw / 2, + y: bw / 2 + }); + + Drawing.setTranslate(scrollBox, 0, 0); + + clipPath.select('rect').attr({ + width: legendObj._width - 2 * bw, + height: height - 2 * bw, + x: bw, + y: bw + }); + + Drawing.setClipUrl(scrollBox, clipId, gd); + + Drawing.setRect(scrollBar, 0, 0, 0, 0); + delete legendObj._scrollY; + } else { + var scrollBarHeight = Math.max(constants.scrollBarMinHeight, + legendObj._effHeight * legendObj._effHeight / legendObj._height); + var scrollBarYMax = legendObj._effHeight - + scrollBarHeight - + 2 * constants.scrollBarMargin; + var scrollBoxYMax = legendObj._height - legendObj._effHeight; + var scrollRatio = scrollBarYMax / scrollBoxYMax; + + var scrollBoxY = Math.min(legendObj._scrollY || 0, scrollBoxYMax); + + // increase the background and clip-path width + // by the scrollbar width and margin + bg.attr({ + width: legendObj._width - + 2 * bw + + constants.scrollBarWidth + + constants.scrollBarMargin, + height: legendObj._effHeight - bw, + x: bw / 2, + y: bw / 2 + }); + + clipPath.select('rect').attr({ + width: legendObj._width - + 2 * bw + + constants.scrollBarWidth + + constants.scrollBarMargin, + height: legendObj._effHeight - 2 * bw, + x: bw, + y: bw + scrollBoxY + }); + + Drawing.setClipUrl(scrollBox, clipId, gd); + + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + + // scroll legend by mousewheel or touchpad swipe up/down + legend.on('wheel', function() { + scrollBoxY = Lib.constrain( + legendObj._scrollY + + ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax), + 0, scrollBoxYMax); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) { + d3.event.preventDefault(); + } + }); + + var eventY0, eventY1, scrollBoxY0; + + var getScrollBarDragY = function(scrollBoxY0, eventY0, eventY1) { + var y = ((eventY1 - eventY0) / scrollRatio) + scrollBoxY0; + return Lib.constrain(y, 0, scrollBoxYMax); + }; + + var getNaturalDragY = function(scrollBoxY0, eventY0, eventY1) { + var y = ((eventY0 - eventY1) / scrollRatio) + scrollBoxY0; + return Lib.constrain(y, 0, scrollBoxYMax); + }; + + // scroll legend by dragging scrollBAR + var scrollBarDrag = d3.behavior.drag() + .on('dragstart', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchstart') { + eventY0 = e.changedTouches[0].clientY; + } else { + eventY0 = e.clientY; + } + scrollBoxY0 = scrollBoxY; + }) + .on('drag', function() { + var e = d3.event.sourceEvent; + if(e.buttons === 2 || e.ctrlKey) return; + if(e.type === 'touchmove') { + eventY1 = e.changedTouches[0].clientY; + } else { + eventY1 = e.clientY; + } + scrollBoxY = getScrollBarDragY(scrollBoxY0, eventY0, eventY1); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + }); + scrollBar.call(scrollBarDrag); + + // scroll legend by touch-dragging scrollBOX + var scrollBoxTouchDrag = d3.behavior.drag() + .on('dragstart', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchstart') { + eventY0 = e.changedTouches[0].clientY; + scrollBoxY0 = scrollBoxY; + } + }) + .on('drag', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchmove') { + eventY1 = e.changedTouches[0].clientY; + scrollBoxY = getNaturalDragY(scrollBoxY0, eventY0, eventY1); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + } + }); + scrollBox.call(scrollBoxTouchDrag); + } + + function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) { + legendObj._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; + Drawing.setTranslate(scrollBox, 0, -scrollBoxY); + + Drawing.setRect( + scrollBar, + legendObj._width, + constants.scrollBarMargin + scrollBoxY * scrollRatio, + constants.scrollBarWidth, + scrollBarHeight + ); + clipPath.select('rect').attr('y', bw + scrollBoxY); + } + + if(gd._context.edits.legendPosition) { + var xf, yf, x0, y0; + + legend.classed('cursor-move', true); + + dragElement.init({ + element: legend.node(), + gd: gd, + prepFn: function() { + var transform = Drawing.getTranslate(legend); + x0 = transform.x; + y0 = transform.y; + }, + moveFn: function(dx, dy) { + var newX = x0 + dx; + var newY = y0 + dy; + + Drawing.setTranslate(legend, newX, newY); + + xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, legendObj.xanchor); + yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, legendObj.yanchor); + }, + doneFn: function() { + if(xf !== undefined && yf !== undefined) { + Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf}); + } + }, + clickFn: function(numClicks, e) { + var clickedTrace = layer.selectAll('g.traces').filter(function() { + var bbox = this.getBoundingClientRect(); + return ( + e.clientX >= bbox.left && e.clientX <= bbox.right && + e.clientY >= bbox.top && e.clientY <= bbox.bottom + ); + }); + if(clickedTrace.size() > 0) { + clickOrDoubleClick(gd, legend, clickedTrace, numClicks, e); + } + } + }); + } + }], gd); +} + +function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { + var trace = legendItem.data()[0][0].trace; + var evtData = { + event: evt, + node: legendItem.node(), + curveNumber: trace.index, + expandedIndex: trace._expandedIndex, + data: gd.data, + layout: gd.layout, + frames: gd._transitionData._frames, + config: gd._context, + fullData: gd._fullData, + fullLayout: gd._fullLayout + }; + + if(trace._group) { + evtData.group = trace._group; + } + if(Registry.traceIs(trace, 'pie-like')) { + evtData.label = legendItem.datum()[0].label; + } + + var clickVal = Events.triggerHandler(gd, 'plotly_legendclick', evtData); + if(clickVal === false) return; + + if(numClicks === 1) { + legend._clickTimeout = setTimeout(function() { + if(!gd._fullLayout) return; + handleClick(legendItem, gd, numClicks); + }, gd._context.doubleClickDelay); + } else if(numClicks === 2) { + if(legend._clickTimeout) clearTimeout(legend._clickTimeout); + gd._legendMouseDownTime = 0; + + var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData); + if(dblClickVal !== false) handleClick(legendItem, gd, numClicks); + } +} + +function drawTexts(g, gd, legendObj) { + var legendItem = g.data()[0][0]; + var trace = legendItem.trace; + var isPieLike = Registry.traceIs(trace, 'pie-like'); + var isEditable = !legendObj._inHover && gd._context.edits.legendText && !isPieLike; + var maxNameLength = legendObj._maxNameLength; + + var name, font; + if(legendItem.groupTitle) { + name = legendItem.groupTitle.text; + font = legendItem.groupTitle.font; + } else { + font = legendObj.font; + if(!legendObj.entries) { + name = isPieLike ? legendItem.label : trace.name; + if(trace._meta) { + name = Lib.templateString(name, trace._meta); + } + } else { + name = legendItem.text; + } + } + + var textEl = Lib.ensureSingle(g, 'text', 'legendtext'); + + textEl.attr('text-anchor', 'start') + .call(Drawing.font, font) + .text(isEditable ? ensureLength(name, maxNameLength) : name); + + var textGap = legendObj.itemwidth + constants.itemGap * 2; + svgTextUtils.positionText(textEl, textGap, 0); + + if(isEditable) { + textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) + .call(textLayout, g, gd, legendObj) + .on('edit', function(newName) { + this.text(ensureLength(newName, maxNameLength)) + .call(textLayout, g, gd, legendObj); + + var fullInput = legendItem.trace._fullInput || {}; + var update = {}; + + if(Registry.hasTransform(fullInput, 'groupby')) { + var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby'); + var index = groupbyIndices[groupbyIndices.length - 1]; + + var kcont = Lib.keyedContainer(fullInput, 'transforms[' + index + '].styles', 'target', 'value.name'); + + kcont.set(legendItem.trace._group, newName); + + update = kcont.constructUpdate(); + } else { + update.name = newName; + } + + return Registry.call('_guiRestyle', gd, update, trace.index); + }); + } else { + textLayout(textEl, g, gd, legendObj); + } +} + +/* + * Make sure we have a reasonably clickable region. + * If this string is missing or very short, pad it with spaces out to at least + * 4 characters, up to the max length of other labels, on the assumption that + * most characters are wider than spaces so a string of spaces will usually be + * no wider than the real labels. + */ +function ensureLength(str, maxLength) { + var targetLength = Math.max(4, maxLength); + if(str && str.trim().length >= targetLength / 2) return str; + str = str || ''; + for(var i = targetLength - str.length; i > 0; i--) str += ' '; + return str; +} + +function setupTraceToggle(g, gd) { + var doubleClickDelay = gd._context.doubleClickDelay; + var newMouseDownTime; + var numClicks = 1; + + var traceToggle = Lib.ensureSingle(g, 'rect', 'legendtoggle', function(s) { + if(!gd._context.staticPlot) { + s.style('cursor', 'pointer').attr('pointer-events', 'all'); + } + s.call(Color.fill, 'rgba(0,0,0,0)'); + }); + + if(gd._context.staticPlot) return; + + traceToggle.on('mousedown', function() { + newMouseDownTime = (new Date()).getTime(); + if(newMouseDownTime - gd._legendMouseDownTime < doubleClickDelay) { + // in a click train + numClicks += 1; + } else { + // new click train + numClicks = 1; + gd._legendMouseDownTime = newMouseDownTime; + } + }); + traceToggle.on('mouseup', function() { + if(gd._dragged || gd._editing) return; + var legend = gd._fullLayout.legend; + + if((new Date()).getTime() - gd._legendMouseDownTime > doubleClickDelay) { + numClicks = Math.max(numClicks - 1, 1); + } + + clickOrDoubleClick(gd, legend, g, numClicks, d3.event); + }); +} + +function textLayout(s, g, gd, legendObj, aTitle) { + if(legendObj._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover + svgTextUtils.convertToTspans(s, gd, function() { + computeTextDimensions(g, gd, legendObj, aTitle); + }); +} + +function computeTextDimensions(g, gd, legendObj, aTitle) { + var legendItem = g.data()[0][0]; + if(!legendObj._inHover && legendItem && !legendItem.trace.showlegend) { + g.remove(); + return; + } + + var mathjaxGroup = g.select('g[class*=math-group]'); + var mathjaxNode = mathjaxGroup.node(); + if(!legendObj) legendObj = gd._fullLayout.legend; + var bw = legendObj.borderwidth; + var font; + if(aTitle === MAIN_TITLE) { + font = legendObj.title.font; + } else if(legendItem.groupTitle) { + font = legendItem.groupTitle.font; + } else { + font = legendObj.font; + } + var lineHeight = font.size * LINE_SPACING; + var height, width; + + if(mathjaxNode) { + var mathjaxBB = Drawing.bBox(mathjaxNode); + + height = mathjaxBB.height; + width = mathjaxBB.width; + + if(aTitle === MAIN_TITLE) { + Drawing.setTranslate(mathjaxGroup, bw, bw + height * 0.75); + } else { // legend item + Drawing.setTranslate(mathjaxGroup, 0, height * 0.25); + } + } else { + var textEl = g.select(aTitle === MAIN_TITLE ? + '.legendtitletext' : '.legendtext' + ); + var textLines = svgTextUtils.lineCount(textEl); + var textNode = textEl.node(); + + height = lineHeight * textLines; + width = textNode ? Drawing.bBox(textNode).width : 0; + + // approximation to height offset to center the font + // to avoid getBoundingClientRect + if(aTitle === MAIN_TITLE) { + if(legendObj.title.side === 'left') { + // add extra space between legend title and itmes + width += constants.itemGap * 2; + } + + svgTextUtils.positionText(textEl, + bw + constants.titlePad, + bw + lineHeight + ); + } else { // legend item + var x = constants.itemGap * 2 + legendObj.itemwidth; + if(legendItem.groupTitle) { + x = constants.itemGap; + width -= legendObj.itemwidth; + } + + svgTextUtils.positionText(textEl, + x, + -lineHeight * ((textLines - 1) / 2 - 0.3) + ); + } + } + + if(aTitle === MAIN_TITLE) { + legendObj._titleWidth = width; + legendObj._titleHeight = height; + } else { // legend item + legendItem.lineHeight = lineHeight; + legendItem.height = Math.max(height, 16) + 3; + legendItem.width = width; + } +} + +function getTitleSize(legendObj) { + var w = 0; + var h = 0; + + var side = legendObj.title.side; + if(side) { + if(side.indexOf('left') !== -1) { + w = legendObj._titleWidth; + } + if(side.indexOf('top') !== -1) { + h = legendObj._titleHeight; + } + } + + return [w, h]; +} + +/* + * Computes in fullLayout.legend: + * + * - _height: legend height including items past scrollbox height + * - _maxHeight: maximum legend height before scrollbox is required + * - _effHeight: legend height w/ or w/o scrollbox + * + * - _width: legend width + * - _maxWidth (for orientation:h only): maximum width before starting new row + */ +function computeLegendDimensions(gd, groups, traces, legendObj) { + var fullLayout = gd._fullLayout; + if(!legendObj) legendObj = fullLayout.legend; + var gs = fullLayout._size; + + var isVertical = helpers.isVertical(legendObj); + var isGrouped = helpers.isGrouped(legendObj); + + var bw = legendObj.borderwidth; + var bw2 = 2 * bw; + var itemGap = constants.itemGap; + var textGap = legendObj.itemwidth + itemGap * 2; + var endPad = 2 * (bw + itemGap); + + var yanchor = getYanchor(legendObj); + var isBelowPlotArea = legendObj.y < 0 || (legendObj.y === 0 && yanchor === 'top'); + var isAbovePlotArea = legendObj.y > 1 || (legendObj.y === 1 && yanchor === 'bottom'); + + var traceGroupGap = legendObj.tracegroupgap; + + // - if below/above plot area, give it the maximum potential margin-push value + // - otherwise, extend the height of the plot area + legendObj._maxHeight = Math.max( + (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h, + 30 + ); + + var toggleRectWidth = 0; + legendObj._width = 0; + legendObj._height = 0; + var titleSize = getTitleSize(legendObj); + + if(isVertical) { + traces.each(function(d) { + var h = d[0].height; + Drawing.setTranslate(this, + bw + titleSize[0], + bw + titleSize[1] + legendObj._height + h / 2 + itemGap + ); + legendObj._height += h; + legendObj._width = Math.max(legendObj._width, d[0].width); + }); + + toggleRectWidth = textGap + legendObj._width; + legendObj._width += itemGap + textGap + bw2; + legendObj._height += endPad; + + if(isGrouped) { + groups.each(function(d, i) { + Drawing.setTranslate(this, 0, i * legendObj.tracegroupgap); + }); + legendObj._height += (legendObj._lgroupsLength - 1) * legendObj.tracegroupgap; + } + } else { + var xanchor = getXanchor(legendObj); + var isLeftOfPlotArea = legendObj.x < 0 || (legendObj.x === 0 && xanchor === 'right'); + var isRightOfPlotArea = legendObj.x > 1 || (legendObj.x === 1 && xanchor === 'left'); + var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea; + var hw = fullLayout.width / 2; + + // - if placed within x-margins, extend the width of the plot area + // - else if below/above plot area and anchored in the margin, extend to opposite margin, + // - otherwise give it the maximum potential margin-push value + legendObj._maxWidth = Math.max( + isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) : + isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) : + gs.w, + 2 * textGap); + var maxItemWidth = 0; + var combinedItemWidth = 0; + traces.each(function(d) { + var w = d[0].width + textGap; + maxItemWidth = Math.max(maxItemWidth, w); + combinedItemWidth += w; + }); + + toggleRectWidth = null; + var maxRowWidth = 0; + + if(isGrouped) { + var maxGroupHeightInRow = 0; + var groupOffsetX = 0; + var groupOffsetY = 0; + groups.each(function() { + var maxWidthInGroup = 0; + var offsetY = 0; + d3.select(this).selectAll('g.traces').each(function(d) { + var h = d[0].height; + Drawing.setTranslate(this, + titleSize[0], + titleSize[1] + bw + itemGap + h / 2 + offsetY + ); + offsetY += h; + maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width); + }); + maxGroupHeightInRow = Math.max(maxGroupHeightInRow, offsetY); + + var next = maxWidthInGroup + itemGap; + + if((next + bw + groupOffsetX) > legendObj._maxWidth) { + maxRowWidth = Math.max(maxRowWidth, groupOffsetX); + groupOffsetX = 0; + groupOffsetY += maxGroupHeightInRow + traceGroupGap; + maxGroupHeightInRow = offsetY; + } + + Drawing.setTranslate(this, groupOffsetX, groupOffsetY); + + groupOffsetX += next; + }); + + legendObj._width = Math.max(maxRowWidth, groupOffsetX) + bw; + legendObj._height = groupOffsetY + maxGroupHeightInRow + endPad; + } else { + var nTraces = traces.size(); + var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < legendObj._maxWidth; + + var maxItemHeightInRow = 0; + var offsetX = 0; + var offsetY = 0; + var rowWidth = 0; + traces.each(function(d) { + var h = d[0].height; + var w = textGap + d[0].width; + var next = (oneRowLegend ? w : maxItemWidth) + itemGap; + + if((next + bw + offsetX - itemGap) >= legendObj._maxWidth) { + maxRowWidth = Math.max(maxRowWidth, rowWidth); + offsetX = 0; + offsetY += maxItemHeightInRow; + legendObj._height += maxItemHeightInRow; + maxItemHeightInRow = 0; + } + + Drawing.setTranslate(this, + titleSize[0] + bw + offsetX, + titleSize[1] + bw + offsetY + h / 2 + itemGap + ); + + rowWidth = offsetX + w + itemGap; + offsetX += next; + maxItemHeightInRow = Math.max(maxItemHeightInRow, h); + }); + + if(oneRowLegend) { + legendObj._width = offsetX + bw2; + legendObj._height = maxItemHeightInRow + endPad; + } else { + legendObj._width = Math.max(maxRowWidth, rowWidth) + bw2; + legendObj._height += maxItemHeightInRow + endPad; + } + } + } + + legendObj._width = Math.ceil( + Math.max( + legendObj._width + titleSize[0], + legendObj._titleWidth + 2 * (bw + constants.titlePad) + ) + ); + + legendObj._height = Math.ceil( + Math.max( + legendObj._height + titleSize[1], + legendObj._titleHeight + 2 * (bw + constants.itemGap) + ) + ); + + legendObj._effHeight = Math.min(legendObj._height, legendObj._maxHeight); + + var edits = gd._context.edits; + var isEditable = edits.legendText || edits.legendPosition; + traces.each(function(d) { + var traceToggle = d3.select(this).select('.legendtoggle'); + var h = d[0].height; + var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width)); + if(!isVertical) w += itemGap / 2; + Drawing.setRect(traceToggle, 0, -h / 2, w, h); + }); +} + +function expandMargin(gd) { + var fullLayout = gd._fullLayout; + var legendObj = fullLayout.legend; + var xanchor = getXanchor(legendObj); + var yanchor = getYanchor(legendObj); + + return Plots.autoMargin(gd, 'legend', { + x: legendObj.x, + y: legendObj.y, + l: legendObj._width * (FROM_TL[xanchor]), + r: legendObj._width * (FROM_BR[xanchor]), + b: legendObj._effHeight * (FROM_BR[yanchor]), + t: legendObj._effHeight * (FROM_TL[yanchor]) + }); +} + +function getXanchor(legendObj) { + return Lib.isRightAnchor(legendObj) ? 'right' : + Lib.isCenterAnchor(legendObj) ? 'center' : + 'left'; +} + +function getYanchor(legendObj) { + return Lib.isBottomAnchor(legendObj) ? 'bottom' : + Lib.isMiddleAnchor(legendObj) ? 'middle' : + 'top'; +} + +},{"../../constants/alignment":260,"../../lib":285,"../../lib/events":278,"../../lib/svg_text_utils":307,"../../plots/plots":366,"../../registry":373,"../color":155,"../dragelement":174,"../drawing":177,"./constants":206,"./get_legend_data":209,"./handle_click":210,"./helpers":211,"./style":213,"@plotly/d3":20}],209:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var helpers = _dereq_('./helpers'); + +module.exports = function getLegendData(calcdata, opts) { + var grouped = helpers.isGrouped(opts); + var reversed = helpers.isReversed(opts); + + var lgroupToTraces = {}; + var lgroups = []; + var hasOneNonBlankGroup = false; + var slicesShown = {}; + var lgroupi = 0; + var maxNameLength = 0; + var i, j; + + function addOneItem(legendGroup, legendItem) { + // each '' legend group is treated as a separate group + if(legendGroup === '' || !helpers.isGrouped(opts)) { + // TODO: check this against fullData legendgroups? + var uniqueGroup = '~~i' + lgroupi; + lgroups.push(uniqueGroup); + lgroupToTraces[uniqueGroup] = [legendItem]; + lgroupi++; + } else if(lgroups.indexOf(legendGroup) === -1) { + lgroups.push(legendGroup); + hasOneNonBlankGroup = true; + lgroupToTraces[legendGroup] = [legendItem]; + } else { + lgroupToTraces[legendGroup].push(legendItem); + } + } + + // build an { legendgroup: [cd0, cd0], ... } object + for(i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var cd0 = cd[0]; + var trace = cd0.trace; + var lgroup = trace.legendgroup; + + if(!opts._inHover && (!trace.visible || !trace.showlegend)) continue; + + if(Registry.traceIs(trace, 'pie-like')) { + if(!slicesShown[lgroup]) slicesShown[lgroup] = {}; + + for(j = 0; j < cd.length; j++) { + var labelj = cd[j].label; + + if(!slicesShown[lgroup][labelj]) { + addOneItem(lgroup, { + label: labelj, + color: cd[j].color, + i: cd[j].i, + trace: trace, + pts: cd[j].pts + }); + + slicesShown[lgroup][labelj] = true; + maxNameLength = Math.max(maxNameLength, (labelj || '').length); + } + } + } else { + addOneItem(lgroup, cd0); + maxNameLength = Math.max(maxNameLength, (trace.name || '').length); + } + } + + // won't draw a legend in this case + if(!lgroups.length) return []; + + // collapse all groups into one if all groups are blank + var shouldCollapse = !hasOneNonBlankGroup || !grouped; + + var legendData = []; + for(i = 0; i < lgroups.length; i++) { + var t = lgroupToTraces[lgroups[i]]; + if(shouldCollapse) { + legendData.push(t[0]); + } else { + legendData.push(t); + } + } + if(shouldCollapse) legendData = [legendData]; + + for(i = 0; i < legendData.length; i++) { + // find minimum rank within group + var groupMinRank = Infinity; + for(j = 0; j < legendData[i].length; j++) { + var rank = legendData[i][j].trace.legendrank; + if(groupMinRank > rank) groupMinRank = rank; + } + + // record on first group element + legendData[i][0]._groupMinRank = groupMinRank; + legendData[i][0]._preGroupSort = i; + } + + var orderFn1 = function(a, b) { + return ( + (a[0]._groupMinRank - b[0]._groupMinRank) || + (a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90 + ); + }; + + var orderFn2 = function(a, b) { + return ( + (a.trace.legendrank - b.trace.legendrank) || + (a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90 + ); + }; + + // sort considering minimum group legendrank + legendData.forEach(function(a, k) { a[0]._preGroupSort = k; }); + legendData.sort(orderFn1); + for(i = 0; i < legendData.length; i++) { + // sort considering trace.legendrank and legend.traceorder + legendData[i].forEach(function(a, k) { a._preSort = k; }); + legendData[i].sort(orderFn2); + + var firstItem = legendData[i][0]; + + var groupTitle = null; + // get group title text + for(j = 0; j < legendData[i].length; j++) { + var gt = legendData[i][j].trace.legendgrouptitle; + if(gt && gt.text) { + groupTitle = gt; + break; + } + } + + // reverse order + if(reversed) legendData[i].reverse(); + + if(groupTitle) { + // set group title text + legendData[i].unshift({ + i: -1, + groupTitle: groupTitle, + trace: { + showlegend: firstItem.trace.showlegend + } + }); + } + + // rearrange lgroupToTraces into a d3-friendly array of arrays + for(j = 0; j < legendData[i].length; j++) { + legendData[i][j] = [ + legendData[i][j] + ]; + } + } + + // number of legend groups - needed in legend/draw.js + opts._lgroupsLength = legendData.length; + // maximum name/label length - needed in legend/draw.js + opts._maxNameLength = maxNameLength; + + return legendData; +}; + +},{"../../registry":373,"./helpers":211}],210:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); + +var SHOWISOLATETIP = true; + +module.exports = function handleClick(g, gd, numClicks) { + var fullLayout = gd._fullLayout; + + if(gd._dragged || gd._editing) return; + + var itemClick = fullLayout.legend.itemclick; + var itemDoubleClick = fullLayout.legend.itemdoubleclick; + + if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'toggleothers' && + SHOWISOLATETIP && gd.data && gd._context.showTips + ) { + Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long'); + SHOWISOLATETIP = false; + } else { + SHOWISOLATETIP = false; + } + + var mode; + if(numClicks === 1) mode = itemClick; + else if(numClicks === 2) mode = itemDoubleClick; + if(!mode) return; + + var hiddenSlices = fullLayout.hiddenlabels ? + fullLayout.hiddenlabels.slice() : + []; + + var legendItem = g.data()[0][0]; + if(legendItem.groupTitle) return; // no click on group legends for now + + var fullData = gd._fullData; + var fullTrace = legendItem.trace; + var legendgroup = fullTrace.legendgroup; + + var i, j, kcont, key, keys, val; + var attrUpdate = {}; + var attrIndices = []; + var carrs = []; + var carrIdx = []; + + function insertUpdate(traceIndex, key, value) { + var attrIndex = attrIndices.indexOf(traceIndex); + var valueArray = attrUpdate[key]; + if(!valueArray) { + valueArray = attrUpdate[key] = []; + } + + if(attrIndices.indexOf(traceIndex) === -1) { + attrIndices.push(traceIndex); + attrIndex = attrIndices.length - 1; + } + + valueArray[attrIndex] = value; + + return attrIndex; + } + + function setVisibility(fullTrace, visibility) { + var fullInput = fullTrace._fullInput; + if(Registry.hasTransform(fullInput, 'groupby')) { + var kcont = carrs[fullInput.index]; + if(!kcont) { + var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby'); + var lastGroupbyIndex = groupbyIndices[groupbyIndices.length - 1]; + kcont = Lib.keyedContainer(fullInput, 'transforms[' + lastGroupbyIndex + '].styles', 'target', 'value.visible'); + carrs[fullInput.index] = kcont; + } + + var curState = kcont.get(fullTrace._group); + + // If not specified, assume visible. This happens if there are other style + // properties set for a group but not the visibility. There are many similar + // ways to do this (e.g. why not just `curState = fullTrace.visible`??? The + // answer is: because it breaks other things like groupby trace names in + // subtle ways.) + if(curState === undefined) { + curState = true; + } + + if(curState !== false) { + // true -> legendonly. All others toggle to true: + kcont.set(fullTrace._group, visibility); + } + carrIdx[fullInput.index] = insertUpdate(fullInput.index, 'visible', fullInput.visible === false ? false : true); + } else { + // false -> false (not possible since will not be visible in legend) + // true -> legendonly + // legendonly -> true + var nextVisibility = fullInput.visible === false ? false : visibility; + + insertUpdate(fullInput.index, 'visible', nextVisibility); + } + } + + if(Registry.traceIs(fullTrace, 'pie-like')) { + var thisLabel = legendItem.label; + var thisLabelIndex = hiddenSlices.indexOf(thisLabel); + + if(mode === 'toggle') { + if(thisLabelIndex === -1) hiddenSlices.push(thisLabel); + else hiddenSlices.splice(thisLabelIndex, 1); + } else if(mode === 'toggleothers') { + hiddenSlices = []; + gd.calcdata[0].forEach(function(d) { + if(thisLabel !== d.label) { + hiddenSlices.push(d.label); + } + }); + if(gd._fullLayout.hiddenlabels && gd._fullLayout.hiddenlabels.length === hiddenSlices.length && thisLabelIndex === -1) { + hiddenSlices = []; + } + } + + Registry.call('_guiRelayout', gd, 'hiddenlabels', hiddenSlices); + } else { + var hasLegendgroup = legendgroup && legendgroup.length; + var traceIndicesInGroup = []; + var tracei; + if(hasLegendgroup) { + for(i = 0; i < fullData.length; i++) { + tracei = fullData[i]; + if(!tracei.visible) continue; + if(tracei.legendgroup === legendgroup) { + traceIndicesInGroup.push(i); + } + } + } + + if(mode === 'toggle') { + var nextVisibility; + + switch(fullTrace.visible) { + case true: + nextVisibility = 'legendonly'; + break; + case false: + nextVisibility = false; + break; + case 'legendonly': + nextVisibility = true; + break; + } + + if(hasLegendgroup) { + for(i = 0; i < fullData.length; i++) { + if(fullData[i].visible !== false && fullData[i].legendgroup === legendgroup) { + setVisibility(fullData[i], nextVisibility); + } + } + } else { + setVisibility(fullTrace, nextVisibility); + } + } else if(mode === 'toggleothers') { + // Compute the clicked index. expandedIndex does what we want for expanded traces + // but also culls hidden traces. That means we have some work to do. + var isClicked, isInGroup, notInLegend, otherState; + var isIsolated = true; + for(i = 0; i < fullData.length; i++) { + isClicked = fullData[i] === fullTrace; + notInLegend = fullData[i].showlegend !== true; + if(isClicked || notInLegend) continue; + + isInGroup = (hasLegendgroup && fullData[i].legendgroup === legendgroup); + + if(!isInGroup && fullData[i].visible === true && !Registry.traceIs(fullData[i], 'notLegendIsolatable')) { + isIsolated = false; + break; + } + } + + for(i = 0; i < fullData.length; i++) { + // False is sticky; we don't change it. + if(fullData[i].visible === false) continue; + + if(Registry.traceIs(fullData[i], 'notLegendIsolatable')) { + continue; + } + + switch(fullTrace.visible) { + case 'legendonly': + setVisibility(fullData[i], true); + break; + case true: + otherState = isIsolated ? true : 'legendonly'; + isClicked = fullData[i] === fullTrace; + // N.B. consider traces that have a set legendgroup as toggleable + notInLegend = (fullData[i].showlegend !== true && !fullData[i].legendgroup); + isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup); + setVisibility(fullData[i], (isInGroup || notInLegend) ? true : otherState); + break; + } + } + } + + for(i = 0; i < carrs.length; i++) { + kcont = carrs[i]; + if(!kcont) continue; + var update = kcont.constructUpdate(); + + var updateKeys = Object.keys(update); + for(j = 0; j < updateKeys.length; j++) { + key = updateKeys[j]; + val = attrUpdate[key] = attrUpdate[key] || []; + val[carrIdx[i]] = update[key]; + } + } + + // The length of the value arrays should be equal and any unspecified + // values should be explicitly undefined for them to get properly culled + // as updates and not accidentally reset to the default value. This fills + // out sparse arrays with the required number of undefined values: + keys = Object.keys(attrUpdate); + for(i = 0; i < keys.length; i++) { + key = keys[i]; + for(j = 0; j < attrIndices.length; j++) { + // Use hasOwnPropety to protect against falsey values: + if(!attrUpdate[key].hasOwnProperty(j)) { + attrUpdate[key][j] = undefined; + } + } + } + + Registry.call('_guiRestyle', gd, attrUpdate, attrIndices); + } +}; + +},{"../../lib":285,"../../registry":373}],211:[function(_dereq_,module,exports){ +'use strict'; + +exports.isGrouped = function isGrouped(legendLayout) { + return (legendLayout.traceorder || '').indexOf('grouped') !== -1; +}; + +exports.isVertical = function isVertical(legendLayout) { + return legendLayout.orientation !== 'h'; +}; + +exports.isReversed = function isReversed(legendLayout) { + return (legendLayout.traceorder || '').indexOf('reversed') !== -1; +}; + +},{}],212:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + moduleType: 'component', + name: 'legend', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw'), + style: _dereq_('./style') +}; + +},{"./attributes":205,"./defaults":207,"./draw":208,"./style":213}],213:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var extractOpts = _dereq_('../colorscale/helpers').extractOpts; + +var subTypes = _dereq_('../../traces/scatter/subtypes'); +var stylePie = _dereq_('../../traces/pie/style_one'); +var pieCastOption = _dereq_('../../traces/pie/helpers').castOption; + +var constants = _dereq_('./constants'); + +var CST_MARKER_SIZE = 12; +var CST_LINE_WIDTH = 5; +var CST_MARKER_LINE_WIDTH = 2; +var MAX_LINE_WIDTH = 10; +var MAX_MARKER_LINE_WIDTH = 5; + +module.exports = function style(s, gd, legend) { + var fullLayout = gd._fullLayout; + if(!legend) legend = fullLayout.legend; + var constantItemSizing = legend.itemsizing === 'constant'; + var itemWidth = legend.itemwidth; + var centerPos = (itemWidth + constants.itemGap * 2) / 2; + var centerTransform = strTranslate(centerPos, 0); + + var boundLineWidth = function(mlw, cont, max, cst) { + var v; + if(mlw + 1) { + v = mlw; + } else if(cont && cont.width > 0) { + v = cont.width; + } else { + return 0; + } + return constantItemSizing ? cst : Math.min(v, max); + }; + + s.each(function(d) { + var traceGroup = d3.select(this); + + var layers = Lib.ensureSingle(traceGroup, 'g', 'layers'); + layers.style('opacity', d[0].trace.opacity); + + var valign = legend.valign; + var lineHeight = d[0].lineHeight; + var height = d[0].height; + + if(valign === 'middle' || !lineHeight || !height) { + layers.attr('transform', null); + } else { + var factor = {top: 1, bottom: -1}[valign]; + var markerOffsetY = factor * (0.5 * (lineHeight - height + 3)); + layers.attr('transform', strTranslate(0, markerOffsetY)); + } + + var fill = layers + .selectAll('g.legendfill') + .data([d]); + fill.enter().append('g') + .classed('legendfill', true); + + var line = layers + .selectAll('g.legendlines') + .data([d]); + line.enter().append('g') + .classed('legendlines', true); + + var symbol = layers + .selectAll('g.legendsymbols') + .data([d]); + symbol.enter().append('g') + .classed('legendsymbols', true); + + symbol.selectAll('g.legendpoints') + .data([d]) + .enter().append('g') + .classed('legendpoints', true); + }) + .each(styleSpatial) + .each(styleWaterfalls) + .each(styleFunnels) + .each(styleBars) + .each(styleBoxes) + .each(styleFunnelareas) + .each(stylePies) + .each(styleLines) + .each(stylePoints) + .each(styleCandles) + .each(styleOHLC); + + function styleLines(d) { + var styleGuide = getStyleGuide(d); + var showFill = styleGuide.showFill; + var showLine = styleGuide.showLine; + var showGradientLine = styleGuide.showGradientLine; + var showGradientFill = styleGuide.showGradientFill; + var anyFill = styleGuide.anyFill; + var anyLine = styleGuide.anyLine; + + var d0 = d[0]; + var trace = d0.trace; + var dMod, tMod; + + var cOpts = extractOpts(trace); + var colorscale = cOpts.colorscale; + var reversescale = cOpts.reversescale; + + var fillGradient = function(s) { + if(s.size()) { + var gradientID = 'legendfill-' + trace.uid; + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale), + colorscale, 'fill'); + } + }; + + var lineGradient = function(s) { + if(s.size()) { + var gradientID = 'legendline-' + trace.uid; + Drawing.lineGroupStyle(s); + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale), + colorscale, 'stroke'); + } + }; + + // with fill and no markers or text, move the line and fill up a bit + // so it's more centered + + var pathStart = (subTypes.hasMarkers(trace) || !anyFill) ? 'M5,0' : + // with a line leave it slightly below center, to leave room for the + // line thickness and because the line is usually more prominent + anyLine ? 'M5,-2' : 'M5,-3'; + + var this3 = d3.select(this); + + var fill = this3.select('.legendfill').selectAll('path') + .data(showFill || showGradientFill ? [d] : []); + fill.enter().append('path').classed('js-fill', true); + fill.exit().remove(); + fill.attr('d', pathStart + 'h' + itemWidth + 'v6h-' + itemWidth + 'z') + .call(showFill ? Drawing.fillGroupStyle : fillGradient); + + if(showLine || showGradientLine) { + var lw = boundLineWidth(undefined, trace.line, MAX_LINE_WIDTH, CST_LINE_WIDTH); + tMod = Lib.minExtend(trace, {line: {width: lw}}); + dMod = [Lib.minExtend(d0, {trace: tMod})]; + } + + var line = this3.select('.legendlines').selectAll('path') + .data(showLine || showGradientLine ? [dMod] : []); + line.enter().append('path').classed('js-line', true); + line.exit().remove(); + + // this is ugly... but you can't apply a gradient to a perfectly + // horizontal or vertical line. Presumably because then + // the system doesn't know how to scale vertical variation, even + // though there *is* no vertical variation in this case. + // so add an invisibly small angle to the line + // This issue (and workaround) exist across (Mac) Chrome, FF, and Safari + line.attr('d', pathStart + (showGradientLine ? 'l' + itemWidth + ',0.0001' : 'h' + itemWidth)) + .call(showLine ? Drawing.lineGroupStyle : lineGradient); + } + + function stylePoints(d) { + var styleGuide = getStyleGuide(d); + var anyFill = styleGuide.anyFill; + var anyLine = styleGuide.anyLine; + var showLine = styleGuide.showLine; + var showMarker = styleGuide.showMarker; + + var d0 = d[0]; + var trace = d0.trace; + var showText = !showMarker && !anyLine && !anyFill && subTypes.hasText(trace); + var dMod, tMod; + + // 'scatter3d' don't use gd.calcdata, + // use d0.trace to infer arrayOk attributes + + function boundVal(attrIn, arrayToValFn, bounds, cst) { + var valIn = Lib.nestedProperty(trace, attrIn).get(); + var valToBound = (Lib.isArrayOrTypedArray(valIn) && arrayToValFn) ? + arrayToValFn(valIn) : + valIn; + + if(constantItemSizing && valToBound && cst !== undefined) { + valToBound = cst; + } + + if(bounds) { + if(valToBound < bounds[0]) return bounds[0]; + else if(valToBound > bounds[1]) return bounds[1]; + } + return valToBound; + } + + function pickFirst(array) { + if(d0._distinct && d0.index && array[d0.index]) return array[d0.index]; + return array[0]; + } + + // constrain text, markers, etc so they'll fit on the legend + if(showMarker || showText || showLine) { + var dEdit = {}; + var tEdit = {}; + + if(showMarker) { + dEdit.mc = boundVal('marker.color', pickFirst); + dEdit.mx = boundVal('marker.symbol', pickFirst); + dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]); + dEdit.mlc = boundVal('marker.line.color', pickFirst); + dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5], CST_MARKER_LINE_WIDTH); + tEdit.marker = { + sizeref: 1, + sizemin: 1, + sizemode: 'diameter' + }; + + var ms = boundVal('marker.size', Lib.mean, [2, 16], CST_MARKER_SIZE); + dEdit.ms = ms; + tEdit.marker.size = ms; + } + + if(showLine) { + tEdit.line = { + width: boundVal('line.width', pickFirst, [0, 10], CST_LINE_WIDTH) + }; + } + + if(showText) { + dEdit.tx = 'Aa'; + dEdit.tp = boundVal('textposition', pickFirst); + dEdit.ts = 10; + dEdit.tc = boundVal('textfont.color', pickFirst); + dEdit.tf = boundVal('textfont.family', pickFirst); + } + + dMod = [Lib.minExtend(d0, dEdit)]; + tMod = Lib.minExtend(trace, tEdit); + + // always show legend items in base state + tMod.selectedpoints = null; + + // never show texttemplate + tMod.texttemplate = null; + } + + var ptgroup = d3.select(this).select('g.legendpoints'); + + var pts = ptgroup.selectAll('path.scatterpts') + .data(showMarker ? dMod : []); + // make sure marker is on the bottom, in case it enters after text + pts.enter().insert('path', ':first-child') + .classed('scatterpts', true) + .attr('transform', centerTransform); + pts.exit().remove(); + pts.call(Drawing.pointStyle, tMod, gd); + + // 'mrc' is set in pointStyle and used in textPointStyle: + // constrain it here + if(showMarker) dMod[0].mrc = 3; + + var txt = ptgroup.selectAll('g.pointtext') + .data(showText ? dMod : []); + txt.enter() + .append('g').classed('pointtext', true) + .append('text').attr('transform', centerTransform); + txt.exit().remove(); + txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd); + } + + function styleWaterfalls(d) { + var trace = d[0].trace; + var isWaterfall = trace.type === 'waterfall'; + + if(d[0]._distinct && isWaterfall) { + var cont = d[0].trace[d[0].dir].marker; + d[0].mc = cont.color; + d[0].mlw = cont.line.width; + d[0].mlc = cont.line.color; + return styleBarLike(d, this, 'waterfall'); + } + + var ptsData = []; + if(trace.visible && isWaterfall) { + ptsData = d[0].hasTotals ? + [['increasing', 'M-6,-6V6H0Z'], ['totals', 'M6,6H0L-6,-6H-0Z'], ['decreasing', 'M6,6V-6H0Z']] : + [['increasing', 'M-6,-6V6H6Z'], ['decreasing', 'M6,6V-6H-6Z']]; + } + + var pts = d3.select(this).select('g.legendpoints') + .selectAll('path.legendwaterfall') + .data(ptsData); + pts.enter().append('path').classed('legendwaterfall', true) + .attr('transform', centerTransform) + .style('stroke-miterlimit', 1); + pts.exit().remove(); + + pts.each(function(dd) { + var pt = d3.select(this); + var cont = trace[dd[0]].marker; + var lw = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH); + + pt.attr('d', dd[1]) + .style('stroke-width', lw + 'px') + .call(Color.fill, cont.color); + + if(lw) { + pt.call(Color.stroke, cont.line.color); + } + }); + } + + function styleBars(d) { + styleBarLike(d, this); + } + + function styleFunnels(d) { + styleBarLike(d, this, 'funnel'); + } + + function styleBarLike(d, lThis, desiredType) { + var trace = d[0].trace; + var marker = trace.marker || {}; + var markerLine = marker.line || {}; + + var isVisible = (!desiredType) ? Registry.traceIs(trace, 'bar') : + (trace.visible && trace.type === desiredType); + + var barpath = d3.select(lThis).select('g.legendpoints') + .selectAll('path.legend' + desiredType) + .data(isVisible ? [d] : []); + barpath.enter().append('path').classed('legend' + desiredType, true) + .attr('d', 'M6,6H-6V-6H6Z') + .attr('transform', centerTransform); + barpath.exit().remove(); + + barpath.each(function(d) { + var p = d3.select(this); + var d0 = d[0]; + var w = boundLineWidth(d0.mlw, marker.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH); + + p.style('stroke-width', w + 'px'); + + var mcc = d0.mcc; + if(!legend._inHover && 'mc' in d0) { + // not in unified hover but + // for legend use the color in the middle of scale + var cOpts = extractOpts(marker); + var mid = cOpts.mid; + if(mid === undefined) mid = (cOpts.max + cOpts.min) / 2; + mcc = Drawing.tryColorscale(marker, '')(mid); + } + var fillColor = mcc || d0.mc || marker.color; + + var markerPattern = marker.pattern; + var patternShape = markerPattern && Drawing.getPatternAttr(markerPattern.shape, 0, ''); + + if(patternShape) { + var patternBGColor = Drawing.getPatternAttr(markerPattern.bgcolor, 0, null); + var patternFGColor = Drawing.getPatternAttr(markerPattern.fgcolor, 0, null); + var patternFGOpacity = markerPattern.fgopacity; + var patternSize = dimAttr(markerPattern.size, 8, 10); + var patternSolidity = dimAttr(markerPattern.solidity, 0.5, 1); + var patternID = 'legend-' + trace.uid; + p.call( + Drawing.pattern, 'legend', gd, patternID, + patternShape, patternSize, patternSolidity, + mcc, markerPattern.fillmode, + patternBGColor, patternFGColor, patternFGOpacity + ); + } else { + p.call(Color.fill, fillColor); + } + + if(w) Color.stroke(p, d0.mlc || markerLine.color); + }); + } + + function styleBoxes(d) { + var trace = d[0].trace; + + var pts = d3.select(this).select('g.legendpoints') + .selectAll('path.legendbox') + .data(trace.visible && Registry.traceIs(trace, 'box-violin') ? [d] : []); + pts.enter().append('path').classed('legendbox', true) + // if we want the median bar, prepend M6,0H-6 + .attr('d', 'M6,6H-6V-6H6Z') + .attr('transform', centerTransform); + pts.exit().remove(); + + pts.each(function() { + var p = d3.select(this); + + if((trace.boxpoints === 'all' || trace.points === 'all') && + Color.opacity(trace.fillcolor) === 0 && Color.opacity((trace.line || {}).color) === 0 + ) { + var tMod = Lib.minExtend(trace, { + marker: { + size: constantItemSizing ? CST_MARKER_SIZE : Lib.constrain(trace.marker.size, 2, 16), + sizeref: 1, + sizemin: 1, + sizemode: 'diameter' + } + }); + pts.call(Drawing.pointStyle, tMod, gd); + } else { + var w = boundLineWidth(undefined, trace.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH); + + p.style('stroke-width', w + 'px') + .call(Color.fill, trace.fillcolor); + + if(w) Color.stroke(p, trace.line.color); + } + }); + } + + function styleCandles(d) { + var trace = d[0].trace; + + var pts = d3.select(this).select('g.legendpoints') + .selectAll('path.legendcandle') + .data(trace.visible && trace.type === 'candlestick' ? [d, d] : []); + pts.enter().append('path').classed('legendcandle', true) + .attr('d', function(_, i) { + if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing + return 'M15,0H8M8,-6V6H-8Z'; // decreasing + }) + .attr('transform', centerTransform) + .style('stroke-miterlimit', 1); + pts.exit().remove(); + + pts.each(function(_, i) { + var p = d3.select(this); + var cont = trace[i ? 'increasing' : 'decreasing']; + var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH); + + p.style('stroke-width', w + 'px') + .call(Color.fill, cont.fillcolor); + + if(w) Color.stroke(p, cont.line.color); + }); + } + + function styleOHLC(d) { + var trace = d[0].trace; + + var pts = d3.select(this).select('g.legendpoints') + .selectAll('path.legendohlc') + .data(trace.visible && trace.type === 'ohlc' ? [d, d] : []); + pts.enter().append('path').classed('legendohlc', true) + .attr('d', function(_, i) { + if(i) return 'M-15,0H0M-8,-6V0'; // increasing + return 'M15,0H0M8,6V0'; // decreasing + }) + .attr('transform', centerTransform) + .style('stroke-miterlimit', 1); + pts.exit().remove(); + + pts.each(function(_, i) { + var p = d3.select(this); + var cont = trace[i ? 'increasing' : 'decreasing']; + var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH); + + p.style('fill', 'none') + .call(Drawing.dashLine, cont.line.dash, w); + + if(w) Color.stroke(p, cont.line.color); + }); + } + + function stylePies(d) { + stylePieLike(d, this, 'pie'); + } + + function styleFunnelareas(d) { + stylePieLike(d, this, 'funnelarea'); + } + + function stylePieLike(d, lThis, desiredType) { + var d0 = d[0]; + var trace = d0.trace; + + var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) : + (trace.visible && trace.type === desiredType); + + var pts = d3.select(lThis).select('g.legendpoints') + .selectAll('path.legend' + desiredType) + .data(isVisible ? [d] : []); + pts.enter().append('path').classed('legend' + desiredType, true) + .attr('d', 'M6,6H-6V-6H6Z') + .attr('transform', centerTransform); + pts.exit().remove(); + + if(pts.size()) { + var cont = (trace.marker || {}).line; + var lw = boundLineWidth(pieCastOption(cont.width, d0.pts), cont, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH); + + var tMod = Lib.minExtend(trace, {marker: {line: {width: lw}}}); + // since minExtend do not slice more than 3 items we need to patch line.color here + tMod.marker.line.color = cont.color; + + var d0Mod = Lib.minExtend(d0, {trace: tMod}); + + stylePie(pts, d0Mod, tMod); + } + } + + function styleSpatial(d) { // i.e. maninly traces having z and colorscale + var trace = d[0].trace; + + var useGradient; + var ptsData = []; + if(trace.visible) { + switch(trace.type) { + case 'histogram2d' : + case 'heatmap' : + ptsData = [ + ['M-15,-2V4H15V-2Z'] // similar to contour + ]; + useGradient = true; + break; + case 'choropleth' : + case 'choroplethmapbox' : + ptsData = [ + ['M-6,-6V6H6V-6Z'] + ]; + useGradient = true; + break; + case 'densitymapbox' : + ptsData = [ + ['M-6,0 a6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0'] + ]; + useGradient = 'radial'; + break; + case 'cone' : + ptsData = [ + ['M-6,2 A2,2 0 0,0 -6,6 V6L6,4Z'], + ['M-6,-6 A2,2 0 0,0 -6,-2 L6,-4Z'], + ['M-6,-2 A2,2 0 0,0 -6,2 L6,0Z'] + ]; + useGradient = false; + break; + case 'streamtube' : + ptsData = [ + ['M-6,2 A2,2 0 0,0 -6,6 H6 A2,2 0 0,1 6,2 Z'], + ['M-6,-6 A2,2 0 0,0 -6,-2 H6 A2,2 0 0,1 6,-6 Z'], + ['M-6,-2 A2,2 0 0,0 -6,2 H6 A2,2 0 0,1 6,-2 Z'] + ]; + useGradient = false; + break; + case 'surface' : + ptsData = [ + ['M-6,-6 A2,3 0 0,0 -6,0 H6 A2,3 0 0,1 6,-6 Z'], + ['M-6,1 A2,3 0 0,1 -6,6 H6 A2,3 0 0,0 6,0 Z'] + ]; + useGradient = true; + break; + case 'mesh3d' : + ptsData = [ + ['M-6,6H0L-6,-6Z'], + ['M6,6H0L6,-6Z'], + ['M-6,-6H6L0,6Z'] + ]; + useGradient = false; + break; + case 'volume' : + ptsData = [ + ['M-6,6H0L-6,-6Z'], + ['M6,6H0L6,-6Z'], + ['M-6,-6H6L0,6Z'] + ]; + useGradient = true; + break; + case 'isosurface': + ptsData = [ + ['M-6,6H0L-6,-6Z'], + ['M6,6H0L6,-6Z'], + ['M-6,-6 A12,24 0 0,0 6,-6 L0,6Z'] + ]; + useGradient = false; + break; + } + } + + var pts = d3.select(this).select('g.legendpoints') + .selectAll('path.legend3dandfriends') + .data(ptsData); + pts.enter().append('path').classed('legend3dandfriends', true) + .attr('transform', centerTransform) + .style('stroke-miterlimit', 1); + pts.exit().remove(); + + pts.each(function(dd, i) { + var pt = d3.select(this); + + var cOpts = extractOpts(trace); + var colorscale = cOpts.colorscale; + var reversescale = cOpts.reversescale; + var fillGradient = function(s) { + if(s.size()) { + var gradientID = 'legendfill-' + trace.uid; + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale, useGradient === 'radial'), + colorscale, 'fill'); + } + }; + + var fillColor; + if(!colorscale) { + var color = trace.vertexcolor || trace.facecolor || trace.color; + fillColor = Lib.isArrayOrTypedArray(color) ? (color[i] || color[0]) : color; + } else { + if(!useGradient) { + var len = colorscale.length; + fillColor = + i === 0 ? colorscale[reversescale ? len - 1 : 0][1] : // minimum + i === 1 ? colorscale[reversescale ? 0 : len - 1][1] : // maximum + colorscale[Math.floor((len - 1) / 2)][1]; // middle + } + } + + pt.attr('d', dd[0]); + if(fillColor) { + pt.call(Color.fill, fillColor); + } else { + pt.call(fillGradient); + } + }); + } +}; + +function getGradientDirection(reversescale, isRadial) { + var str = isRadial ? 'radial' : 'horizontal'; + return str + (reversescale ? '' : 'reversed'); +} + +function getStyleGuide(d) { + var trace = d[0].trace; + var contours = trace.contours; + var showLine = subTypes.hasLines(trace); + var showMarker = subTypes.hasMarkers(trace); + + var showFill = trace.visible && trace.fill && trace.fill !== 'none'; + var showGradientLine = false; + var showGradientFill = false; + + if(contours) { + var coloring = contours.coloring; + + if(coloring === 'lines') { + showGradientLine = true; + } else { + showLine = coloring === 'none' || coloring === 'heatmap' || contours.showlines; + } + + if(contours.type === 'constraint') { + showFill = contours._operation !== '='; + } else if(coloring === 'fill' || coloring === 'heatmap') { + showGradientFill = true; + } + } + + return { + showMarker: showMarker, + showLine: showLine, + showFill: showFill, + showGradientLine: showGradientLine, + showGradientFill: showGradientFill, + anyLine: showLine || showGradientLine, + anyFill: showFill || showGradientFill, + }; +} + +function dimAttr(v, dflt, max) { + if(v && Lib.isArrayOrTypedArray(v)) return dflt; + if(v > max) return max; + return v; +} + +},{"../../lib":285,"../../registry":373,"../../traces/pie/helpers":486,"../../traces/pie/style_one":492,"../../traces/scatter/subtypes":519,"../color":155,"../colorscale/helpers":166,"../drawing":177,"./constants":206,"@plotly/d3":20}],214:[function(_dereq_,module,exports){ +'use strict'; + +var constants = _dereq_('./constants'); + +module.exports = { + editType: 'modebar', + + orientation: { + valType: 'enumerated', + values: ['v', 'h'], + dflt: 'h', + editType: 'modebar', + }, + bgcolor: { + valType: 'color', + editType: 'modebar', + }, + color: { + valType: 'color', + editType: 'modebar', + }, + activecolor: { + valType: 'color', + editType: 'modebar', + }, + uirevision: { + valType: 'any', + editType: 'none', + }, + add: { + valType: 'string', + arrayOk: true, + dflt: '', + editType: 'modebar', + }, + remove: { + valType: 'string', + arrayOk: true, + dflt: '', + editType: 'modebar', + } +}; + +},{"./constants":216}],215:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); +var Icons = _dereq_('../../fonts/ploticon'); +var eraseActiveShape = _dereq_('../shapes/draw').eraseActiveShape; +var Lib = _dereq_('../../lib'); +var _ = Lib._; + +var modeBarButtons = module.exports = {}; + +/** + * ModeBar buttons configuration + * + * @param {string} name + * name / id of the buttons (for tracking) + * @param {string} title + * text that appears while hovering over the button, + * enter null, false or '' for no hover text + * @param {string} icon + * svg icon object associated with the button + * can be linked to Plotly.Icons to use the default plotly icons + * @param {string} [gravity] + * icon positioning + * @param {function} click + * click handler associated with the button, a function of + * 'gd' (the main graph object) and + * 'ev' (the event object) + * @param {string} [attr] + * attribute associated with button, + * use this with 'val' to keep track of the state + * @param {*} [val] + * initial 'attr' value, can be a function of gd + * @param {boolean} [toggle] + * is the button a toggle button? + */ +modeBarButtons.toImage = { + name: 'toImage', + title: function(gd) { + var opts = gd._context.toImageButtonOptions || {}; + var format = opts.format || 'png'; + return format === 'png' ? + _(gd, 'Download plot as a png') : // legacy text + _(gd, 'Download plot'); // generic non-PNG text + }, + icon: Icons.camera, + click: function(gd) { + var toImageButtonOptions = gd._context.toImageButtonOptions; + var opts = {format: toImageButtonOptions.format || 'png'}; + + Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long'); + + if(opts.format !== 'svg' && Lib.isIE()) { + Lib.notifier(_(gd, 'IE only supports svg. Changing format to svg.'), 'long'); + opts.format = 'svg'; + } + + ['filename', 'width', 'height', 'scale'].forEach(function(key) { + if(key in toImageButtonOptions) { + opts[key] = toImageButtonOptions[key]; + } + }); + + Registry.call('downloadImage', gd, opts) + .then(function(filename) { + Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long'); + }) + .catch(function() { + Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long'); + }); + } +}; + +modeBarButtons.sendDataToCloud = { + name: 'sendDataToCloud', + title: function(gd) { return _(gd, 'Edit in Chart Studio'); }, + icon: Icons.disk, + click: function(gd) { + Plots.sendDataToCloud(gd); + } +}; + +modeBarButtons.editInChartStudio = { + name: 'editInChartStudio', + title: function(gd) { return _(gd, 'Edit in Chart Studio'); }, + icon: Icons.pencil, + click: function(gd) { + Plots.sendDataToCloud(gd); + } +}; + +modeBarButtons.zoom2d = { + name: 'zoom2d', + _cat: 'zoom', + title: function(gd) { return _(gd, 'Zoom'); }, + attr: 'dragmode', + val: 'zoom', + icon: Icons.zoombox, + click: handleCartesian +}; + +modeBarButtons.pan2d = { + name: 'pan2d', + _cat: 'pan', + title: function(gd) { return _(gd, 'Pan'); }, + attr: 'dragmode', + val: 'pan', + icon: Icons.pan, + click: handleCartesian +}; + +modeBarButtons.select2d = { + name: 'select2d', + _cat: 'select', + title: function(gd) { return _(gd, 'Box Select'); }, + attr: 'dragmode', + val: 'select', + icon: Icons.selectbox, + click: handleCartesian +}; + +modeBarButtons.lasso2d = { + name: 'lasso2d', + _cat: 'lasso', + title: function(gd) { return _(gd, 'Lasso Select'); }, + attr: 'dragmode', + val: 'lasso', + icon: Icons.lasso, + click: handleCartesian +}; + +modeBarButtons.drawclosedpath = { + name: 'drawclosedpath', + title: function(gd) { return _(gd, 'Draw closed freeform'); }, + attr: 'dragmode', + val: 'drawclosedpath', + icon: Icons.drawclosedpath, + click: handleCartesian +}; + +modeBarButtons.drawopenpath = { + name: 'drawopenpath', + title: function(gd) { return _(gd, 'Draw open freeform'); }, + attr: 'dragmode', + val: 'drawopenpath', + icon: Icons.drawopenpath, + click: handleCartesian +}; + +modeBarButtons.drawline = { + name: 'drawline', + title: function(gd) { return _(gd, 'Draw line'); }, + attr: 'dragmode', + val: 'drawline', + icon: Icons.drawline, + click: handleCartesian +}; + +modeBarButtons.drawrect = { + name: 'drawrect', + title: function(gd) { return _(gd, 'Draw rectangle'); }, + attr: 'dragmode', + val: 'drawrect', + icon: Icons.drawrect, + click: handleCartesian +}; + +modeBarButtons.drawcircle = { + name: 'drawcircle', + title: function(gd) { return _(gd, 'Draw circle'); }, + attr: 'dragmode', + val: 'drawcircle', + icon: Icons.drawcircle, + click: handleCartesian +}; + +modeBarButtons.eraseshape = { + name: 'eraseshape', + title: function(gd) { return _(gd, 'Erase active shape'); }, + icon: Icons.eraseshape, + click: eraseActiveShape +}; + +modeBarButtons.zoomIn2d = { + name: 'zoomIn2d', + _cat: 'zoomin', + title: function(gd) { return _(gd, 'Zoom in'); }, + attr: 'zoom', + val: 'in', + icon: Icons.zoom_plus, + click: handleCartesian +}; + +modeBarButtons.zoomOut2d = { + name: 'zoomOut2d', + _cat: 'zoomout', + title: function(gd) { return _(gd, 'Zoom out'); }, + attr: 'zoom', + val: 'out', + icon: Icons.zoom_minus, + click: handleCartesian +}; + +modeBarButtons.autoScale2d = { + name: 'autoScale2d', + _cat: 'autoscale', + title: function(gd) { return _(gd, 'Autoscale'); }, + attr: 'zoom', + val: 'auto', + icon: Icons.autoscale, + click: handleCartesian +}; + +modeBarButtons.resetScale2d = { + name: 'resetScale2d', + _cat: 'resetscale', + title: function(gd) { return _(gd, 'Reset axes'); }, + attr: 'zoom', + val: 'reset', + icon: Icons.home, + click: handleCartesian +}; + +modeBarButtons.hoverClosestCartesian = { + name: 'hoverClosestCartesian', + _cat: 'hoverclosest', + title: function(gd) { return _(gd, 'Show closest data on hover'); }, + attr: 'hovermode', + val: 'closest', + icon: Icons.tooltip_basic, + gravity: 'ne', + click: handleCartesian +}; + +modeBarButtons.hoverCompareCartesian = { + name: 'hoverCompareCartesian', + _cat: 'hoverCompare', + title: function(gd) { return _(gd, 'Compare data on hover'); }, + attr: 'hovermode', + val: function(gd) { + return gd._fullLayout._isHoriz ? 'y' : 'x'; + }, + icon: Icons.tooltip_compare, + gravity: 'ne', + click: handleCartesian +}; + +function handleCartesian(gd, ev) { + var button = ev.currentTarget; + var astr = button.getAttribute('data-attr'); + var val = button.getAttribute('data-val') || true; + var fullLayout = gd._fullLayout; + var aobj = {}; + var axList = axisIds.list(gd, null, true); + var allSpikesEnabled = fullLayout._cartesianSpikesEnabled; + + var ax, i; + + if(astr === 'zoom') { + var mag = (val === 'in') ? 0.5 : 2; + var r0 = (1 + mag) / 2; + var r1 = (1 - mag) / 2; + var axName; + + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + + if(!ax.fixedrange) { + axName = ax._name; + if(val === 'auto') { + aobj[axName + '.autorange'] = true; + } else if(val === 'reset') { + if(ax._rangeInitial === undefined) { + aobj[axName + '.autorange'] = true; + } else { + var rangeInitial = ax._rangeInitial.slice(); + aobj[axName + '.range[0]'] = rangeInitial[0]; + aobj[axName + '.range[1]'] = rangeInitial[1]; + } + + // N.B. "reset" also resets showspikes + if(ax._showSpikeInitial !== undefined) { + aobj[axName + '.showspikes'] = ax._showSpikeInitial; + if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) { + allSpikesEnabled = 'off'; + } + } + } else { + var rangeNow = [ + ax.r2l(ax.range[0]), + ax.r2l(ax.range[1]), + ]; + + var rangeNew = [ + r0 * rangeNow[0] + r1 * rangeNow[1], + r0 * rangeNow[1] + r1 * rangeNow[0] + ]; + + aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]); + aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]); + } + } + } + } else { + // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y' + if(astr === 'hovermode' && (val === 'x' || val === 'y')) { + val = fullLayout._isHoriz ? 'y' : 'x'; + button.setAttribute('data-val', val); + } + + aobj[astr] = val; + } + + fullLayout._cartesianSpikesEnabled = allSpikesEnabled; + + Registry.call('_guiRelayout', gd, aobj); +} + +modeBarButtons.zoom3d = { + name: 'zoom3d', + _cat: 'zoom', + title: function(gd) { return _(gd, 'Zoom'); }, + attr: 'scene.dragmode', + val: 'zoom', + icon: Icons.zoombox, + click: handleDrag3d +}; + +modeBarButtons.pan3d = { + name: 'pan3d', + _cat: 'pan', + title: function(gd) { return _(gd, 'Pan'); }, + attr: 'scene.dragmode', + val: 'pan', + icon: Icons.pan, + click: handleDrag3d +}; + +modeBarButtons.orbitRotation = { + name: 'orbitRotation', + title: function(gd) { return _(gd, 'Orbital rotation'); }, + attr: 'scene.dragmode', + val: 'orbit', + icon: Icons['3d_rotate'], + click: handleDrag3d +}; + +modeBarButtons.tableRotation = { + name: 'tableRotation', + title: function(gd) { return _(gd, 'Turntable rotation'); }, + attr: 'scene.dragmode', + val: 'turntable', + icon: Icons['z-axis'], + click: handleDrag3d +}; + +function handleDrag3d(gd, ev) { + var button = ev.currentTarget; + var attr = button.getAttribute('data-attr'); + var val = button.getAttribute('data-val') || true; + var sceneIds = gd._fullLayout._subplots.gl3d || []; + var layoutUpdate = {}; + + var parts = attr.split('.'); + + for(var i = 0; i < sceneIds.length; i++) { + layoutUpdate[sceneIds[i] + '.' + parts[1]] = val; + } + + // for multi-type subplots + var val2d = (val === 'pan') ? val : 'zoom'; + layoutUpdate.dragmode = val2d; + + Registry.call('_guiRelayout', gd, layoutUpdate); +} + +modeBarButtons.resetCameraDefault3d = { + name: 'resetCameraDefault3d', + _cat: 'resetCameraDefault', + title: function(gd) { return _(gd, 'Reset camera to default'); }, + attr: 'resetDefault', + icon: Icons.home, + click: handleCamera3d +}; + +modeBarButtons.resetCameraLastSave3d = { + name: 'resetCameraLastSave3d', + _cat: 'resetCameraLastSave', + title: function(gd) { return _(gd, 'Reset camera to last save'); }, + attr: 'resetLastSave', + icon: Icons.movie, + click: handleCamera3d +}; + +function handleCamera3d(gd, ev) { + var button = ev.currentTarget; + var attr = button.getAttribute('data-attr'); + var resetLastSave = attr === 'resetLastSave'; + var resetDefault = attr === 'resetDefault'; + + var fullLayout = gd._fullLayout; + var sceneIds = fullLayout._subplots.gl3d || []; + var aobj = {}; + + for(var i = 0; i < sceneIds.length; i++) { + var sceneId = sceneIds[i]; + var camera = sceneId + '.camera'; + var aspectratio = sceneId + '.aspectratio'; + var aspectmode = sceneId + '.aspectmode'; + var scene = fullLayout[sceneId]._scene; + var didUpdate; + + if(resetLastSave) { + aobj[camera + '.up'] = scene.viewInitial.up; + aobj[camera + '.eye'] = scene.viewInitial.eye; + aobj[camera + '.center'] = scene.viewInitial.center; + didUpdate = true; + } else if(resetDefault) { + aobj[camera + '.up'] = null; + aobj[camera + '.eye'] = null; + aobj[camera + '.center'] = null; + didUpdate = true; + } + + if(didUpdate) { + aobj[aspectratio + '.x'] = scene.viewInitial.aspectratio.x; + aobj[aspectratio + '.y'] = scene.viewInitial.aspectratio.y; + aobj[aspectratio + '.z'] = scene.viewInitial.aspectratio.z; + aobj[aspectmode] = scene.viewInitial.aspectmode; + } + } + + Registry.call('_guiRelayout', gd, aobj); +} + +modeBarButtons.hoverClosest3d = { + name: 'hoverClosest3d', + _cat: 'hoverclosest', + title: function(gd) { return _(gd, 'Toggle show closest data on hover'); }, + attr: 'hovermode', + val: null, + toggle: true, + icon: Icons.tooltip_basic, + gravity: 'ne', + click: handleHover3d +}; + +function getNextHover3d(gd, ev) { + var button = ev.currentTarget; + var val = button._previousVal; + var fullLayout = gd._fullLayout; + var sceneIds = fullLayout._subplots.gl3d || []; + + var axes = ['xaxis', 'yaxis', 'zaxis']; + + // initialize 'current spike' object to be stored in the DOM + var currentSpikes = {}; + var layoutUpdate = {}; + + if(val) { + layoutUpdate = val; + button._previousVal = null; + } else { + for(var i = 0; i < sceneIds.length; i++) { + var sceneId = sceneIds[i]; + var sceneLayout = fullLayout[sceneId]; + + var hovermodeAStr = sceneId + '.hovermode'; + currentSpikes[hovermodeAStr] = sceneLayout.hovermode; + layoutUpdate[hovermodeAStr] = false; + + // copy all the current spike attrs + for(var j = 0; j < 3; j++) { + var axis = axes[j]; + var spikeAStr = sceneId + '.' + axis + '.showspikes'; + layoutUpdate[spikeAStr] = false; + currentSpikes[spikeAStr] = sceneLayout[axis].showspikes; + } + } + + button._previousVal = currentSpikes; + } + return layoutUpdate; +} + +function handleHover3d(gd, ev) { + var layoutUpdate = getNextHover3d(gd, ev); + Registry.call('_guiRelayout', gd, layoutUpdate); +} + +modeBarButtons.zoomInGeo = { + name: 'zoomInGeo', + _cat: 'zoomin', + title: function(gd) { return _(gd, 'Zoom in'); }, + attr: 'zoom', + val: 'in', + icon: Icons.zoom_plus, + click: handleGeo +}; + +modeBarButtons.zoomOutGeo = { + name: 'zoomOutGeo', + _cat: 'zoomout', + title: function(gd) { return _(gd, 'Zoom out'); }, + attr: 'zoom', + val: 'out', + icon: Icons.zoom_minus, + click: handleGeo +}; + +modeBarButtons.resetGeo = { + name: 'resetGeo', + _cat: 'reset', + title: function(gd) { return _(gd, 'Reset'); }, + attr: 'reset', + val: null, + icon: Icons.autoscale, + click: handleGeo +}; + +modeBarButtons.hoverClosestGeo = { + name: 'hoverClosestGeo', + _cat: 'hoverclosest', + title: function(gd) { return _(gd, 'Toggle show closest data on hover'); }, + attr: 'hovermode', + val: null, + toggle: true, + icon: Icons.tooltip_basic, + gravity: 'ne', + click: toggleHover +}; + +function handleGeo(gd, ev) { + var button = ev.currentTarget; + var attr = button.getAttribute('data-attr'); + var val = button.getAttribute('data-val') || true; + var fullLayout = gd._fullLayout; + var geoIds = fullLayout._subplots.geo || []; + + for(var i = 0; i < geoIds.length; i++) { + var id = geoIds[i]; + var geoLayout = fullLayout[id]; + + if(attr === 'zoom') { + var scale = geoLayout.projection.scale; + var newScale = (val === 'in') ? 2 * scale : 0.5 * scale; + + Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale); + } + } + + if(attr === 'reset') { + resetView(gd, 'geo'); + } +} + +modeBarButtons.hoverClosestGl2d = { + name: 'hoverClosestGl2d', + _cat: 'hoverclosest', + title: function(gd) { return _(gd, 'Toggle show closest data on hover'); }, + attr: 'hovermode', + val: null, + toggle: true, + icon: Icons.tooltip_basic, + gravity: 'ne', + click: toggleHover +}; + +modeBarButtons.hoverClosestPie = { + name: 'hoverClosestPie', + _cat: 'hoverclosest', + title: function(gd) { return _(gd, 'Toggle show closest data on hover'); }, + attr: 'hovermode', + val: 'closest', + icon: Icons.tooltip_basic, + gravity: 'ne', + click: toggleHover +}; + +function getNextHover(gd) { + var fullLayout = gd._fullLayout; + + if(fullLayout.hovermode) return false; + + if(fullLayout._has('cartesian')) { + return fullLayout._isHoriz ? 'y' : 'x'; + } + return 'closest'; +} + +function toggleHover(gd) { + var newHover = getNextHover(gd); + Registry.call('_guiRelayout', gd, 'hovermode', newHover); +} + +modeBarButtons.resetViewSankey = { + name: 'resetSankeyGroup', + title: function(gd) { return _(gd, 'Reset view'); }, + icon: Icons.home, + click: function(gd) { + var aObj = { + 'node.groups': [], + 'node.x': [], + 'node.y': [] + }; + for(var i = 0; i < gd._fullData.length; i++) { + var viewInitial = gd._fullData[i]._viewInitial; + aObj['node.groups'].push(viewInitial.node.groups.slice()); + aObj['node.x'].push(viewInitial.node.x.slice()); + aObj['node.y'].push(viewInitial.node.y.slice()); + } + Registry.call('restyle', gd, aObj); + } +}; + +// buttons when more then one plot types are present + +modeBarButtons.toggleHover = { + name: 'toggleHover', + title: function(gd) { return _(gd, 'Toggle show closest data on hover'); }, + attr: 'hovermode', + val: null, + toggle: true, + icon: Icons.tooltip_basic, + gravity: 'ne', + click: function(gd, ev) { + var layoutUpdate = getNextHover3d(gd, ev); + layoutUpdate.hovermode = getNextHover(gd); + + Registry.call('_guiRelayout', gd, layoutUpdate); + } +}; + +modeBarButtons.resetViews = { + name: 'resetViews', + title: function(gd) { return _(gd, 'Reset views'); }, + icon: Icons.home, + click: function(gd, ev) { + var button = ev.currentTarget; + + button.setAttribute('data-attr', 'zoom'); + button.setAttribute('data-val', 'reset'); + handleCartesian(gd, ev); + + button.setAttribute('data-attr', 'resetLastSave'); + handleCamera3d(gd, ev); + + resetView(gd, 'geo'); + resetView(gd, 'mapbox'); + } +}; + +modeBarButtons.toggleSpikelines = { + name: 'toggleSpikelines', + title: function(gd) { return _(gd, 'Toggle Spike Lines'); }, + icon: Icons.spikeline, + attr: '_cartesianSpikesEnabled', + val: 'on', + click: function(gd) { + var fullLayout = gd._fullLayout; + var allSpikesEnabled = fullLayout._cartesianSpikesEnabled; + + fullLayout._cartesianSpikesEnabled = allSpikesEnabled === 'on' ? 'off' : 'on'; + Registry.call('_guiRelayout', gd, setSpikelineVisibility(gd)); + } +}; + +function setSpikelineVisibility(gd) { + var fullLayout = gd._fullLayout; + var areSpikesOn = fullLayout._cartesianSpikesEnabled === 'on'; + var axList = axisIds.list(gd, null, true); + var aobj = {}; + + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + aobj[ax._name + '.showspikes'] = areSpikesOn ? true : ax._showSpikeInitial; + } + + return aobj; +} + +modeBarButtons.resetViewMapbox = { + name: 'resetViewMapbox', + _cat: 'resetView', + title: function(gd) { return _(gd, 'Reset view'); }, + attr: 'reset', + icon: Icons.home, + click: function(gd) { + resetView(gd, 'mapbox'); + } +}; + +modeBarButtons.zoomInMapbox = { + name: 'zoomInMapbox', + _cat: 'zoomin', + title: function(gd) { return _(gd, 'Zoom in'); }, + attr: 'zoom', + val: 'in', + icon: Icons.zoom_plus, + click: handleMapboxZoom +}; + +modeBarButtons.zoomOutMapbox = { + name: 'zoomOutMapbox', + _cat: 'zoomout', + title: function(gd) { return _(gd, 'Zoom out'); }, + attr: 'zoom', + val: 'out', + icon: Icons.zoom_minus, + click: handleMapboxZoom +}; + +function handleMapboxZoom(gd, ev) { + var button = ev.currentTarget; + var val = button.getAttribute('data-val'); + var fullLayout = gd._fullLayout; + var subplotIds = fullLayout._subplots.mapbox || []; + var scalar = 1.05; + var aObj = {}; + + for(var i = 0; i < subplotIds.length; i++) { + var id = subplotIds[i]; + var current = fullLayout[id].zoom; + var next = (val === 'in') ? scalar * current : current / scalar; + aObj[id + '.zoom'] = next; + } + + Registry.call('_guiRelayout', gd, aObj); +} + +function resetView(gd, subplotType) { + var fullLayout = gd._fullLayout; + var subplotIds = fullLayout._subplots[subplotType] || []; + var aObj = {}; + + for(var i = 0; i < subplotIds.length; i++) { + var id = subplotIds[i]; + var subplotObj = fullLayout[id]._subplot; + var viewInitial = subplotObj.viewInitial; + var viewKeys = Object.keys(viewInitial); + + for(var j = 0; j < viewKeys.length; j++) { + var key = viewKeys[j]; + aObj[id + '.' + key] = viewInitial[key]; + } + } + + Registry.call('_guiRelayout', gd, aObj); +} + +},{"../../fonts/ploticon":268,"../../lib":285,"../../plots/cartesian/axis_ids":335,"../../plots/plots":366,"../../registry":373,"../shapes/draw":239}],216:[function(_dereq_,module,exports){ +'use strict'; + +var modeBarButtons = _dereq_('./buttons'); +var buttonList = Object.keys(modeBarButtons); + +var DRAW_MODES = [ + 'drawline', + 'drawopenpath', + 'drawclosedpath', + 'drawcircle', + 'drawrect', + 'eraseshape' +]; + +var backButtons = [ + 'v1hovermode', + 'hoverclosest', + 'hovercompare', + 'togglehover', + 'togglespikelines' +].concat(DRAW_MODES); + +var foreButtons = []; +var addToForeButtons = function(b) { + if(backButtons.indexOf(b._cat || b.name) !== -1) return; + // for convenience add lowercase shotname e.g. zoomin as well fullname zoomInGeo + var name = b.name; + var _cat = (b._cat || b.name).toLowerCase(); + if(foreButtons.indexOf(name) === -1) foreButtons.push(name); + if(foreButtons.indexOf(_cat) === -1) foreButtons.push(_cat); +}; +buttonList.forEach(function(k) { + addToForeButtons(modeBarButtons[k]); +}); +foreButtons.sort(); + +module.exports = { + DRAW_MODES: DRAW_MODES, + backButtons: backButtons, + foreButtons: foreButtons +}; + +},{"./buttons":215}],217:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../color'); +var Template = _dereq_('../../plot_api/plot_template'); +var attributes = _dereq_('./attributes'); + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + var containerIn = layoutIn.modebar || {}; + var containerOut = Template.newContainer(layoutOut, 'modebar'); + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + coerce('orientation'); + coerce('bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5)); + var defaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor)); + coerce('color', Color.addOpacity(defaultColor, 0.3)); + coerce('activecolor', Color.addOpacity(defaultColor, 0.7)); + coerce('uirevision', layoutOut.uirevision); + coerce('add'); + coerce('remove'); +}; + +},{"../../lib":285,"../../plot_api/plot_template":320,"../color":155,"./attributes":214}],218:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + moduleType: 'component', + name: 'modebar', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + + manage: _dereq_('./manage') +}; + +},{"./attributes":214,"./defaults":217,"./manage":219}],219:[function(_dereq_,module,exports){ +'use strict'; + +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); +var scatterSubTypes = _dereq_('../../traces/scatter/subtypes'); +var Registry = _dereq_('../../registry'); +var isUnifiedHover = _dereq_('../fx/helpers').isUnifiedHover; + +var createModeBar = _dereq_('./modebar'); +var modeBarButtons = _dereq_('./buttons'); +var DRAW_MODES = _dereq_('./constants').DRAW_MODES; + +/** + * ModeBar wrapper around 'create' and 'update', + * chooses buttons to pass to ModeBar constructor based on + * plot type and plot config. + * + * @param {object} gd main plot object + * + */ +module.exports = function manageModeBar(gd) { + var fullLayout = gd._fullLayout; + var context = gd._context; + var modeBar = fullLayout._modeBar; + + if(!context.displayModeBar && !context.watermark) { + if(modeBar) { + modeBar.destroy(); + delete fullLayout._modeBar; + } + return; + } + + if(!Array.isArray(context.modeBarButtonsToRemove)) { + throw new Error([ + '*modeBarButtonsToRemove* configuration options', + 'must be an array.' + ].join(' ')); + } + + if(!Array.isArray(context.modeBarButtonsToAdd)) { + throw new Error([ + '*modeBarButtonsToAdd* configuration options', + 'must be an array.' + ].join(' ')); + } + + var customButtons = context.modeBarButtons; + var buttonGroups; + + if(Array.isArray(customButtons) && customButtons.length) { + buttonGroups = fillCustomButton(customButtons); + } else if(!context.displayModeBar && context.watermark) { + buttonGroups = []; + } else { + buttonGroups = getButtonGroups(gd); + } + + if(modeBar) modeBar.update(gd, buttonGroups); + else fullLayout._modeBar = createModeBar(gd, buttonGroups); +}; + +// logic behind which buttons are displayed by default +function getButtonGroups(gd) { + var fullLayout = gd._fullLayout; + var fullData = gd._fullData; + var context = gd._context; + + function match(name, B) { + if(typeof B === 'string') { + if(B.toLowerCase() === name.toLowerCase()) return true; + } else { + var v0 = B.name; + var v1 = (B._cat || B.name); + + if(v0 === name || v1 === name.toLowerCase()) return true; + } + return false; + } + + var layoutAdd = fullLayout.modebar.add; + if(typeof layoutAdd === 'string') layoutAdd = [layoutAdd]; + + var layoutRemove = fullLayout.modebar.remove; + if(typeof layoutRemove === 'string') layoutRemove = [layoutRemove]; + + var buttonsToAdd = context.modeBarButtonsToAdd.concat( + layoutAdd.filter(function(e) { + for(var i = 0; i < context.modeBarButtonsToRemove.length; i++) { + if(match(e, context.modeBarButtonsToRemove[i])) return false; + } + return true; + }) + ); + + var buttonsToRemove = context.modeBarButtonsToRemove.concat( + layoutRemove.filter(function(e) { + for(var i = 0; i < context.modeBarButtonsToAdd.length; i++) { + if(match(e, context.modeBarButtonsToAdd[i])) return false; + } + return true; + }) + ); + + var hasCartesian = fullLayout._has('cartesian'); + var hasGL3D = fullLayout._has('gl3d'); + var hasGeo = fullLayout._has('geo'); + var hasPie = fullLayout._has('pie'); + var hasFunnelarea = fullLayout._has('funnelarea'); + var hasGL2D = fullLayout._has('gl2d'); + var hasTernary = fullLayout._has('ternary'); + var hasMapbox = fullLayout._has('mapbox'); + var hasPolar = fullLayout._has('polar'); + var hasSankey = fullLayout._has('sankey'); + var allAxesFixed = areAllAxesFixed(fullLayout); + var hasUnifiedHoverLabel = isUnifiedHover(fullLayout.hovermode); + + var groups = []; + + function addGroup(newGroup) { + if(!newGroup.length) return; + + var out = []; + + for(var i = 0; i < newGroup.length; i++) { + var name = newGroup[i]; + var B = modeBarButtons[name]; + var v0 = B.name.toLowerCase(); + var v1 = (B._cat || B.name).toLowerCase(); + var found = false; + for(var q = 0; q < buttonsToRemove.length; q++) { + var t = buttonsToRemove[q].toLowerCase(); + if(t === v0 || t === v1) { + found = true; + break; + } + } + if(found) continue; + out.push(modeBarButtons[name]); + } + + groups.push(out); + } + + // buttons common to all plot types + var commonGroup = ['toImage']; + if(context.showEditInChartStudio) commonGroup.push('editInChartStudio'); + else if(context.showSendToCloud) commonGroup.push('sendDataToCloud'); + addGroup(commonGroup); + + var zoomGroup = []; + var hoverGroup = []; + var resetGroup = []; + var dragModeGroup = []; + + if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) { + // graphs with more than one plot types get 'union buttons' + // which reset the view or toggle hover labels across all subplots. + hoverGroup = ['toggleHover']; + resetGroup = ['resetViews']; + } else if(hasGeo) { + zoomGroup = ['zoomInGeo', 'zoomOutGeo']; + hoverGroup = ['hoverClosestGeo']; + resetGroup = ['resetGeo']; + } else if(hasGL3D) { + hoverGroup = ['hoverClosest3d']; + resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d']; + } else if(hasMapbox) { + zoomGroup = ['zoomInMapbox', 'zoomOutMapbox']; + hoverGroup = ['toggleHover']; + resetGroup = ['resetViewMapbox']; + } else if(hasGL2D) { + hoverGroup = ['hoverClosestGl2d']; + } else if(hasPie) { + hoverGroup = ['hoverClosestPie']; + } else if(hasSankey) { + hoverGroup = ['hoverClosestCartesian', 'hoverCompareCartesian']; + resetGroup = ['resetViewSankey']; + } else { // hasPolar, hasTernary + // always show at least one hover icon. + hoverGroup = ['toggleHover']; + } + // if we have cartesian, allow switching between closest and compare + // regardless of what other types are on the plot, since they'll all + // just treat any truthy hovermode as 'closest' + if(hasCartesian) { + hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian']; + } + if(hasNoHover(fullData) || hasUnifiedHoverLabel) { + hoverGroup = []; + } + + if((hasCartesian || hasGL2D) && !allAxesFixed) { + zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d']; + if(resetGroup[0] !== 'resetViews') resetGroup = ['resetScale2d']; + } + + if(hasGL3D) { + dragModeGroup = ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation']; + } else if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) { + dragModeGroup = ['zoom2d', 'pan2d']; + } else if(hasMapbox || hasGeo) { + dragModeGroup = ['pan2d']; + } else if(hasPolar) { + dragModeGroup = ['zoom2d']; + } + if(isSelectable(fullData)) { + dragModeGroup.push('select2d', 'lasso2d'); + } + + var enabledHoverGroup = []; + var enableHover = function(a) { + // return if already added + if(enabledHoverGroup.indexOf(a) !== -1) return; + // should be in hoverGroup + if(hoverGroup.indexOf(a) !== -1) { + enabledHoverGroup.push(a); + } + }; + if(Array.isArray(buttonsToAdd)) { + var newList = []; + for(var i = 0; i < buttonsToAdd.length; i++) { + var b = buttonsToAdd[i]; + if(typeof b === 'string') { + b = b.toLowerCase(); + + if(DRAW_MODES.indexOf(b) !== -1) { + // accept pre-defined drag modes i.e. shape drawing features as string + if( + fullLayout._has('mapbox') || // draw shapes in paper coordinate (could be improved in future to support data coordinate, when there is no pitch) + fullLayout._has('cartesian') // draw shapes in data coordinate + ) { + dragModeGroup.push(b); + } + } else if(b === 'togglespikelines') { + enableHover('toggleSpikelines'); + } else if(b === 'togglehover') { + enableHover('toggleHover'); + } else if(b === 'hovercompare') { + enableHover('hoverCompareCartesian'); + } else if(b === 'hoverclosest') { + enableHover('hoverClosestCartesian'); + enableHover('hoverClosestGeo'); + enableHover('hoverClosest3d'); + enableHover('hoverClosestGl2d'); + enableHover('hoverClosestPie'); + } else if(b === 'v1hovermode') { + enableHover('toggleHover'); + enableHover('hoverClosestCartesian'); + enableHover('hoverCompareCartesian'); + enableHover('hoverClosestGeo'); + enableHover('hoverClosest3d'); + enableHover('hoverClosestGl2d'); + enableHover('hoverClosestPie'); + } + } else newList.push(b); + } + buttonsToAdd = newList; + } + + addGroup(dragModeGroup); + addGroup(zoomGroup.concat(resetGroup)); + addGroup(enabledHoverGroup); + + return appendButtonsToGroups(groups, buttonsToAdd); +} + +function areAllAxesFixed(fullLayout) { + var axList = axisIds.list({_fullLayout: fullLayout}, null, true); + + for(var i = 0; i < axList.length; i++) { + if(!axList[i].fixedrange) { + return false; + } + } + + return true; +} + +// look for traces that support selection +// to be updated as we add more selectPoints handlers +function isSelectable(fullData) { + var selectable = false; + + for(var i = 0; i < fullData.length; i++) { + if(selectable) break; + + var trace = fullData[i]; + + if(!trace._module || !trace._module.selectPoints) continue; + + if(Registry.traceIs(trace, 'scatter-like')) { + if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) { + selectable = true; + } + } else if(Registry.traceIs(trace, 'box-violin')) { + if(trace.boxpoints === 'all' || trace.points === 'all') { + selectable = true; + } + } else { + // assume that in general if the trace module has selectPoints, + // then it's selectable. Scatter is an exception to this because it must + // have markers or text, not just be a scatter type. + + selectable = true; + } + } + + return selectable; +} + +// check whether all trace are 'noHover' +function hasNoHover(fullData) { + for(var i = 0; i < fullData.length; i++) { + if(!Registry.traceIs(fullData[i], 'noHover')) return false; + } + return true; +} + +function appendButtonsToGroups(groups, buttons) { + if(buttons.length) { + if(Array.isArray(buttons[0])) { + for(var i = 0; i < buttons.length; i++) { + groups.push(buttons[i]); + } + } else groups.push(buttons); + } + + return groups; +} + +// fill in custom buttons referring to default mode bar buttons +function fillCustomButton(customButtons) { + for(var i = 0; i < customButtons.length; i++) { + var buttonGroup = customButtons[i]; + + for(var j = 0; j < buttonGroup.length; j++) { + var button = buttonGroup[j]; + + if(typeof button === 'string') { + if(modeBarButtons[button] !== undefined) { + customButtons[i][j] = modeBarButtons[button]; + } else { + throw new Error([ + '*modeBarButtons* configuration options', + 'invalid button name' + ].join(' ')); + } + } + } + } + + return customButtons; +} + +},{"../../plots/cartesian/axis_ids":335,"../../registry":373,"../../traces/scatter/subtypes":519,"../fx/helpers":191,"./buttons":215,"./constants":216,"./modebar":220}],220:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var Icons = _dereq_('../../fonts/ploticon'); +var Parser = new DOMParser(); + +/** + * UI controller for interactive plots + * @Class + * @Param {object} opts + * @Param {object} opts.buttons nested arrays of grouped buttons config objects + * @Param {object} opts.container container div to append modeBar + * @Param {object} opts.graphInfo primary plot object containing data and layout + */ +function ModeBar(opts) { + this.container = opts.container; + this.element = document.createElement('div'); + + this.update(opts.graphInfo, opts.buttons); + + this.container.appendChild(this.element); +} + +var proto = ModeBar.prototype; + +/** + * Update modeBar (buttons and logo) + * + * @param {object} graphInfo primary plot object containing data and layout + * @param {array of arrays} buttons nested arrays of grouped buttons to initialize + * + */ +proto.update = function(graphInfo, buttons) { + this.graphInfo = graphInfo; + + var context = this.graphInfo._context; + var fullLayout = this.graphInfo._fullLayout; + var modeBarId = 'modebar-' + fullLayout._uid; + + this.element.setAttribute('id', modeBarId); + this._uid = modeBarId; + + this.element.className = 'modebar'; + if(context.displayModeBar === 'hover') this.element.className += ' modebar--hover ease-bg'; + + if(fullLayout.modebar.orientation === 'v') { + this.element.className += ' vertical'; + buttons = buttons.reverse(); + } + + var style = fullLayout.modebar; + var bgSelector = context.displayModeBar === 'hover' ? '.js-plotly-plot .plotly:hover ' : ''; + + Lib.deleteRelatedStyleRule(modeBarId); + Lib.addRelatedStyleRule(modeBarId, bgSelector + '#' + modeBarId + ' .modebar-group', 'background-color: ' + style.bgcolor); + Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn .icon path', 'fill: ' + style.color); + Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn:hover .icon path', 'fill: ' + style.activecolor); + Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn.active .icon path', 'fill: ' + style.activecolor); + + // if buttons or logo have changed, redraw modebar interior + var needsNewButtons = !this.hasButtons(buttons); + var needsNewLogo = (this.hasLogo !== context.displaylogo); + var needsNewLocale = (this.locale !== context.locale); + + this.locale = context.locale; + + if(needsNewButtons || needsNewLogo || needsNewLocale) { + this.removeAllButtons(); + + this.updateButtons(buttons); + + if(context.watermark || context.displaylogo) { + var logoGroup = this.getLogo(); + if(context.watermark) { + logoGroup.className = logoGroup.className + ' watermark'; + } + + if(fullLayout.modebar.orientation === 'v') { + this.element.insertBefore(logoGroup, this.element.childNodes[0]); + } else { + this.element.appendChild(logoGroup); + } + + this.hasLogo = true; + } + } + + this.updateActiveButton(); +}; + +proto.updateButtons = function(buttons) { + var _this = this; + + this.buttons = buttons; + this.buttonElements = []; + this.buttonsNames = []; + + this.buttons.forEach(function(buttonGroup) { + var group = _this.createGroup(); + + buttonGroup.forEach(function(buttonConfig) { + var buttonName = buttonConfig.name; + if(!buttonName) { + throw new Error('must provide button \'name\' in button config'); + } + if(_this.buttonsNames.indexOf(buttonName) !== -1) { + throw new Error('button name \'' + buttonName + '\' is taken'); + } + _this.buttonsNames.push(buttonName); + + var button = _this.createButton(buttonConfig); + _this.buttonElements.push(button); + group.appendChild(button); + }); + + _this.element.appendChild(group); + }); +}; + +/** + * Empty div for containing a group of buttons + * @Return {HTMLelement} + */ +proto.createGroup = function() { + var group = document.createElement('div'); + group.className = 'modebar-group'; + return group; +}; + +/** + * Create a new button div and set constant and configurable attributes + * @Param {object} config (see ./buttons.js for more info) + * @Return {HTMLelement} + */ +proto.createButton = function(config) { + var _this = this; + var button = document.createElement('a'); + + button.setAttribute('rel', 'tooltip'); + button.className = 'modebar-btn'; + + var title = config.title; + if(title === undefined) title = config.name; + // for localization: allow title to be a callable that takes gd as arg + else if(typeof title === 'function') title = title(this.graphInfo); + + if(title || title === 0) button.setAttribute('data-title', title); + + if(config.attr !== undefined) button.setAttribute('data-attr', config.attr); + + var val = config.val; + if(val !== undefined) { + if(typeof val === 'function') val = val(this.graphInfo); + button.setAttribute('data-val', val); + } + + var click = config.click; + if(typeof click !== 'function') { + throw new Error('must provide button \'click\' function in button config'); + } else { + button.addEventListener('click', function(ev) { + config.click(_this.graphInfo, ev); + + // only needed for 'hoverClosestGeo' which does not call relayout + _this.updateActiveButton(ev.currentTarget); + }); + } + + button.setAttribute('data-toggle', config.toggle || false); + if(config.toggle) d3.select(button).classed('active', true); + + var icon = config.icon; + if(typeof icon === 'function') { + button.appendChild(icon()); + } else { + button.appendChild(this.createIcon(icon || Icons.question)); + } + button.setAttribute('data-gravity', config.gravity || 'n'); + + return button; +}; + +/** + * Add an icon to a button + * @Param {object} thisIcon + * @Param {number} thisIcon.width + * @Param {string} thisIcon.path + * @Param {string} thisIcon.color + * @Return {HTMLelement} + */ +proto.createIcon = function(thisIcon) { + var iconHeight = isNumeric(thisIcon.height) ? + Number(thisIcon.height) : + thisIcon.ascent - thisIcon.descent; + var svgNS = 'http://www.w3.org/2000/svg'; + var icon; + + if(thisIcon.path) { + icon = document.createElementNS(svgNS, 'svg'); + icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' ')); + icon.setAttribute('class', 'icon'); + + var path = document.createElementNS(svgNS, 'path'); + path.setAttribute('d', thisIcon.path); + + if(thisIcon.transform) { + path.setAttribute('transform', thisIcon.transform); + } else if(thisIcon.ascent !== undefined) { + // Legacy icon transform calculation + path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')'); + } + + icon.appendChild(path); + } + + if(thisIcon.svg) { + var svgDoc = Parser.parseFromString(thisIcon.svg, 'application/xml'); + icon = svgDoc.childNodes[0]; + } + + icon.setAttribute('height', '1em'); + icon.setAttribute('width', '1em'); + + return icon; +}; + +/** + * Updates active button with attribute specified in layout + * @Param {object} graphInfo plot object containing data and layout + * @Return {HTMLelement} + */ +proto.updateActiveButton = function(buttonClicked) { + var fullLayout = this.graphInfo._fullLayout; + var dataAttrClicked = (buttonClicked !== undefined) ? + buttonClicked.getAttribute('data-attr') : + null; + + this.buttonElements.forEach(function(button) { + var thisval = button.getAttribute('data-val') || true; + var dataAttr = button.getAttribute('data-attr'); + var isToggleButton = (button.getAttribute('data-toggle') === 'true'); + var button3 = d3.select(button); + + // Use 'data-toggle' and 'buttonClicked' to toggle buttons + // that have no one-to-one equivalent in fullLayout + if(isToggleButton) { + if(dataAttr === dataAttrClicked) { + button3.classed('active', !button3.classed('active')); + } + } else { + var val = (dataAttr === null) ? + dataAttr : + Lib.nestedProperty(fullLayout, dataAttr).get(); + + button3.classed('active', val === thisval); + } + }); +}; + +/** + * Check if modeBar is configured as button configuration argument + * + * @Param {object} buttons 2d array of grouped button config objects + * @Return {boolean} + */ +proto.hasButtons = function(buttons) { + var currentButtons = this.buttons; + + if(!currentButtons) return false; + + if(buttons.length !== currentButtons.length) return false; + + for(var i = 0; i < buttons.length; ++i) { + if(buttons[i].length !== currentButtons[i].length) return false; + for(var j = 0; j < buttons[i].length; j++) { + if(buttons[i][j].name !== currentButtons[i][j].name) return false; + } + } + + return true; +}; + +/** + * @return {HTMLDivElement} The logo image wrapped in a group + */ +proto.getLogo = function() { + var group = this.createGroup(); + var a = document.createElement('a'); + + a.href = 'https://plotly.com/'; + a.target = '_blank'; + a.setAttribute('data-title', Lib._(this.graphInfo, 'Produced with Plotly')); + a.className = 'modebar-btn plotlyjsicon modebar-btn--logo'; + + a.appendChild(this.createIcon(Icons.newplotlylogo)); + + group.appendChild(a); + return group; +}; + +proto.removeAllButtons = function() { + while(this.element.firstChild) { + this.element.removeChild(this.element.firstChild); + } + + this.hasLogo = false; +}; + +proto.destroy = function() { + Lib.removeElement(this.container.querySelector('.modebar')); + Lib.deleteRelatedStyleRule(this._uid); +}; + +function createModeBar(gd, buttons) { + var fullLayout = gd._fullLayout; + + var modeBar = new ModeBar({ + graphInfo: gd, + container: fullLayout._modebardiv.node(), + buttons: buttons + }); + + if(fullLayout._privateplot) { + d3.select(modeBar.element).append('span') + .classed('badge-private float--left', true) + .text('PRIVATE'); + } + + return modeBar; +} + +module.exports = createModeBar; + +},{"../../fonts/ploticon":268,"../../lib":285,"@plotly/d3":20,"fast-isnumeric":31}],221:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('../../plots/font_attributes'); +var colorAttrs = _dereq_('../color/attributes'); +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; + +var buttonAttrs = templatedArray('button', { + visible: { + valType: 'boolean', + dflt: true, + editType: 'plot', + }, + step: { + valType: 'enumerated', + values: ['month', 'year', 'day', 'hour', 'minute', 'second', 'all'], + dflt: 'month', + editType: 'plot', + }, + stepmode: { + valType: 'enumerated', + values: ['backward', 'todate'], + dflt: 'backward', + editType: 'plot', + }, + count: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'plot', + }, + label: { + valType: 'string', + editType: 'plot', + }, + editType: 'plot', +}); + +module.exports = { + visible: { + valType: 'boolean', + editType: 'plot', + }, + + buttons: buttonAttrs, + + x: { + valType: 'number', + min: -2, + max: 3, + editType: 'plot', + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'left', + editType: 'plot', + }, + y: { + valType: 'number', + min: -2, + max: 3, + editType: 'plot', + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + dflt: 'bottom', + editType: 'plot', + }, + + font: fontAttrs({ + editType: 'plot', + }), + + bgcolor: { + valType: 'color', + dflt: colorAttrs.lightLine, + editType: 'plot', + }, + activecolor: { + valType: 'color', + editType: 'plot', + }, + bordercolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'plot', + }, + borderwidth: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'plot', + }, + editType: 'plot' +}; + +},{"../../plot_api/plot_template":320,"../../plots/font_attributes":360,"../color/attributes":154}],222:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + + // 'y' position pad above counter axis domain + yPad: 0.02, + + // minimum button width (regardless of text size) + minButtonWidth: 30, + + // buttons rect radii + rx: 3, + ry: 3, + + // light fraction used to compute the 'activecolor' default + lightAmount: 25, + darkAmount: 10 +}; + +},{}],223:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../color'); +var Template = _dereq_('../../plot_api/plot_template'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); + +var attributes = _dereq_('./attributes'); +var constants = _dereq_('./constants'); + + +module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes, calendar) { + var selectorIn = containerIn.rangeselector || {}; + var selectorOut = Template.newContainer(containerOut, 'rangeselector'); + + function coerce(attr, dflt) { + return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt); + } + + var buttons = handleArrayContainerDefaults(selectorIn, selectorOut, { + name: 'buttons', + handleItemDefaults: buttonDefaults, + calendar: calendar + }); + + var visible = coerce('visible', buttons.length > 0); + if(visible) { + var posDflt = getPosDflt(containerOut, layout, counterAxes); + coerce('x', posDflt[0]); + coerce('y', posDflt[1]); + Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); + + coerce('xanchor'); + coerce('yanchor'); + + Lib.coerceFont(coerce, 'font', layout.font); + + var bgColor = coerce('bgcolor'); + coerce('activecolor', Color.contrast(bgColor, constants.lightAmount, constants.darkAmount)); + coerce('bordercolor'); + coerce('borderwidth'); + } +}; + +function buttonDefaults(buttonIn, buttonOut, selectorOut, opts) { + var calendar = opts.calendar; + + function coerce(attr, dflt) { + return Lib.coerce(buttonIn, buttonOut, attributes.buttons, attr, dflt); + } + + var visible = coerce('visible'); + + if(visible) { + var step = coerce('step'); + if(step !== 'all') { + if(calendar && calendar !== 'gregorian' && (step === 'month' || step === 'year')) { + buttonOut.stepmode = 'backward'; + } else { + coerce('stepmode'); + } + + coerce('count'); + } + + coerce('label'); + } +} + +function getPosDflt(containerOut, layout, counterAxes) { + var anchoredList = counterAxes.filter(function(ax) { + return layout[ax].anchor === containerOut._id; + }); + + var posY = 0; + for(var i = 0; i < anchoredList.length; i++) { + var domain = layout[anchoredList[i]].domain; + if(domain) posY = Math.max(domain[1], posY); + } + + return [containerOut.domain[0], posY + constants.yPad]; +} + +},{"../../lib":285,"../../plot_api/plot_template":320,"../../plots/array_container_defaults":326,"../color":155,"./attributes":221,"./constants":222}],224:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); +var Color = _dereq_('../color'); +var Drawing = _dereq_('../drawing'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); + +var alignmentConstants = _dereq_('../../constants/alignment'); +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var FROM_TL = alignmentConstants.FROM_TL; +var FROM_BR = alignmentConstants.FROM_BR; + +var constants = _dereq_('./constants'); +var getUpdateObject = _dereq_('./get_update_object'); + +module.exports = function draw(gd) { + var fullLayout = gd._fullLayout; + + var selectors = fullLayout._infolayer.selectAll('.rangeselector') + .data(makeSelectorData(gd), selectorKeyFunc); + + selectors.enter().append('g') + .classed('rangeselector', true); + + selectors.exit().remove(); + + selectors.style({ + cursor: 'pointer', + 'pointer-events': 'all' + }); + + selectors.each(function(d) { + var selector = d3.select(this); + var axisLayout = d; + var selectorLayout = axisLayout.rangeselector; + + var buttons = selector.selectAll('g.button') + .data(Lib.filterVisible(selectorLayout.buttons)); + + buttons.enter().append('g') + .classed('button', true); + + buttons.exit().remove(); + + buttons.each(function(d) { + var button = d3.select(this); + var update = getUpdateObject(axisLayout, d); + + d._isActive = isActive(axisLayout, d, update); + + button.call(drawButtonRect, selectorLayout, d); + button.call(drawButtonText, selectorLayout, d, gd); + + button.on('click', function() { + if(gd._dragged) return; + + Registry.call('_guiRelayout', gd, update); + }); + + button.on('mouseover', function() { + d._isHovered = true; + button.call(drawButtonRect, selectorLayout, d); + }); + + button.on('mouseout', function() { + d._isHovered = false; + button.call(drawButtonRect, selectorLayout, d); + }); + }); + + reposition(gd, buttons, selectorLayout, axisLayout._name, selector); + }); +}; + +function makeSelectorData(gd) { + var axes = axisIds.list(gd, 'x', true); + var data = []; + + for(var i = 0; i < axes.length; i++) { + var axis = axes[i]; + + if(axis.rangeselector && axis.rangeselector.visible) { + data.push(axis); + } + } + + return data; +} + +function selectorKeyFunc(d) { + return d._id; +} + +function isActive(axisLayout, opts, update) { + if(opts.step === 'all') { + return axisLayout.autorange === true; + } else { + var keys = Object.keys(update); + + return ( + axisLayout.range[0] === update[keys[0]] && + axisLayout.range[1] === update[keys[1]] + ); + } +} + +function drawButtonRect(button, selectorLayout, d) { + var rect = Lib.ensureSingle(button, 'rect', 'selector-rect', function(s) { + s.attr('shape-rendering', 'crispEdges'); + }); + + rect.attr({ + 'rx': constants.rx, + 'ry': constants.ry + }); + + rect.call(Color.stroke, selectorLayout.bordercolor) + .call(Color.fill, getFillColor(selectorLayout, d)) + .style('stroke-width', selectorLayout.borderwidth + 'px'); +} + +function getFillColor(selectorLayout, d) { + return (d._isActive || d._isHovered) ? + selectorLayout.activecolor : + selectorLayout.bgcolor; +} + +function drawButtonText(button, selectorLayout, d, gd) { + function textLayout(s) { + svgTextUtils.convertToTspans(s, gd); + } + + var text = Lib.ensureSingle(button, 'text', 'selector-text', function(s) { + s.attr('text-anchor', 'middle'); + }); + + text.call(Drawing.font, selectorLayout.font) + .text(getLabel(d, gd._fullLayout._meta)) + .call(textLayout); +} + +function getLabel(opts, _meta) { + if(opts.label) { + return _meta ? + Lib.templateString(opts.label, _meta) : + opts.label; + } + + if(opts.step === 'all') return 'all'; + + return opts.count + opts.step.charAt(0); +} + +function reposition(gd, buttons, opts, axName, selector) { + var width = 0; + var height = 0; + + var borderWidth = opts.borderwidth; + + buttons.each(function() { + var button = d3.select(this); + var text = button.select('.selector-text'); + + var tHeight = opts.font.size * LINE_SPACING; + var hEff = Math.max(tHeight * svgTextUtils.lineCount(text), 16) + 3; + + height = Math.max(height, hEff); + }); + + buttons.each(function() { + var button = d3.select(this); + var rect = button.select('.selector-rect'); + var text = button.select('.selector-text'); + + var tWidth = text.node() && Drawing.bBox(text.node()).width; + var tHeight = opts.font.size * LINE_SPACING; + var tLines = svgTextUtils.lineCount(text); + + var wEff = Math.max(tWidth + 10, constants.minButtonWidth); + + // TODO add MathJax support + + // TODO add buttongap attribute + + button.attr('transform', strTranslate(borderWidth + width, borderWidth)); + + rect.attr({ + x: 0, + y: 0, + width: wEff, + height: height + }); + + svgTextUtils.positionText(text, wEff / 2, + height / 2 - ((tLines - 1) * tHeight / 2) + 3); + + width += wEff + 5; + }); + + var graphSize = gd._fullLayout._size; + var lx = graphSize.l + graphSize.w * opts.x; + var ly = graphSize.t + graphSize.h * (1 - opts.y); + + var xanchor = 'left'; + if(Lib.isRightAnchor(opts)) { + lx -= width; + xanchor = 'right'; + } + if(Lib.isCenterAnchor(opts)) { + lx -= width / 2; + xanchor = 'center'; + } + + var yanchor = 'top'; + if(Lib.isBottomAnchor(opts)) { + ly -= height; + yanchor = 'bottom'; + } + if(Lib.isMiddleAnchor(opts)) { + ly -= height / 2; + yanchor = 'middle'; + } + + width = Math.ceil(width); + height = Math.ceil(height); + lx = Math.round(lx); + ly = Math.round(ly); + + Plots.autoMargin(gd, axName + '-range-selector', { + x: opts.x, + y: opts.y, + l: width * FROM_TL[xanchor], + r: width * FROM_BR[xanchor], + b: height * FROM_BR[yanchor], + t: height * FROM_TL[yanchor] + }); + + selector.attr('transform', strTranslate(lx, ly)); +} + +},{"../../constants/alignment":260,"../../lib":285,"../../lib/svg_text_utils":307,"../../plots/cartesian/axis_ids":335,"../../plots/plots":366,"../../registry":373,"../color":155,"../drawing":177,"./constants":222,"./get_update_object":225,"@plotly/d3":20}],225:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +module.exports = function getUpdateObject(axisLayout, buttonLayout) { + var axName = axisLayout._name; + var update = {}; + + if(buttonLayout.step === 'all') { + update[axName + '.autorange'] = true; + } else { + var xrange = getXRange(axisLayout, buttonLayout); + + update[axName + '.range[0]'] = xrange[0]; + update[axName + '.range[1]'] = xrange[1]; + } + + return update; +}; + +function getXRange(axisLayout, buttonLayout) { + var currentRange = axisLayout.range; + var base = new Date(axisLayout.r2l(currentRange[1])); + var step = buttonLayout.step; + var count = buttonLayout.count; + var range0; + + switch(buttonLayout.stepmode) { + case 'backward': + range0 = axisLayout.l2r(+d3.time[step].utc.offset(base, -count)); + break; + + case 'todate': + var base2 = d3.time[step].utc.offset(base, -count); + + range0 = axisLayout.l2r(+d3.time[step].utc.ceil(base2)); + break; + } + + var range1 = currentRange[1]; + + return [range0, range1]; +} + +},{"@plotly/d3":20}],226:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + moduleType: 'component', + name: 'rangeselector', + + schema: { + subplots: { + xaxis: {rangeselector: _dereq_('./attributes')} + } + }, + + layoutAttributes: _dereq_('./attributes'), + handleDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw') +}; + +},{"./attributes":221,"./defaults":223,"./draw":224}],227:[function(_dereq_,module,exports){ +'use strict'; + +var colorAttributes = _dereq_('../color/attributes'); + +module.exports = { + bgcolor: { + valType: 'color', + dflt: colorAttributes.background, + editType: 'plot', + }, + bordercolor: { + valType: 'color', + dflt: colorAttributes.defaultLine, + editType: 'plot', + }, + borderwidth: { + valType: 'integer', + dflt: 0, + min: 0, + editType: 'plot', + }, + autorange: { + valType: 'boolean', + dflt: true, + editType: 'calc', + impliedEdits: {'range[0]': undefined, 'range[1]': undefined}, + }, + range: { + valType: 'info_array', + items: [ + {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}}, + {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}} + ], + editType: 'calc', + impliedEdits: {'autorange': false}, + }, + thickness: { + valType: 'number', + dflt: 0.15, + min: 0, + max: 1, + editType: 'plot', + }, + visible: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + editType: 'calc' +}; + +},{"../color/attributes":154}],228:[function(_dereq_,module,exports){ +'use strict'; + +var listAxes = _dereq_('../../plots/cartesian/axis_ids').list; +var getAutoRange = _dereq_('../../plots/cartesian/autorange').getAutoRange; +var constants = _dereq_('./constants'); + +module.exports = function calcAutorange(gd) { + var axes = listAxes(gd, 'x', true); + + // Compute new slider range using axis autorange if necessary. + // + // Copy back range to input range slider container to skip + // this step in subsequent draw calls. + + for(var i = 0; i < axes.length; i++) { + var ax = axes[i]; + var opts = ax[constants.name]; + + if(opts && opts.visible && opts.autorange) { + opts._input.autorange = true; + opts._input.range = opts.range = getAutoRange(gd, ax); + } + } +}; + +},{"../../plots/cartesian/autorange":330,"../../plots/cartesian/axis_ids":335,"./constants":229}],229:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + + // attribute container name + name: 'rangeslider', + + // class names + + containerClassName: 'rangeslider-container', + bgClassName: 'rangeslider-bg', + rangePlotClassName: 'rangeslider-rangeplot', + + maskMinClassName: 'rangeslider-mask-min', + maskMaxClassName: 'rangeslider-mask-max', + slideBoxClassName: 'rangeslider-slidebox', + + grabberMinClassName: 'rangeslider-grabber-min', + grabAreaMinClassName: 'rangeslider-grabarea-min', + handleMinClassName: 'rangeslider-handle-min', + + grabberMaxClassName: 'rangeslider-grabber-max', + grabAreaMaxClassName: 'rangeslider-grabarea-max', + handleMaxClassName: 'rangeslider-handle-max', + + maskMinOppAxisClassName: 'rangeslider-mask-min-opp-axis', + maskMaxOppAxisClassName: 'rangeslider-mask-max-opp-axis', + + // style constants + + maskColor: 'rgba(0,0,0,0.4)', + maskOppAxisColor: 'rgba(0,0,0,0.2)', + + slideBoxFill: 'transparent', + slideBoxCursor: 'ew-resize', + + grabAreaFill: 'transparent', + grabAreaCursor: 'col-resize', + grabAreaWidth: 10, + + handleWidth: 4, + handleRadius: 1, + handleStrokeWidth: 1, + + extraPad: 15 +}; + +},{}],230:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Template = _dereq_('../../plot_api/plot_template'); +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); + +var attributes = _dereq_('./attributes'); +var oppAxisAttrs = _dereq_('./oppaxis_attributes'); + +module.exports = function handleDefaults(layoutIn, layoutOut, axName) { + var axIn = layoutIn[axName]; + var axOut = layoutOut[axName]; + + if(!(axIn.rangeslider || layoutOut._requestRangeslider[axOut._id])) return; + + // not super proud of this (maybe store _ in axis object instead + if(!Lib.isPlainObject(axIn.rangeslider)) { + axIn.rangeslider = {}; + } + + var containerIn = axIn.rangeslider; + var containerOut = Template.newContainer(axOut, 'rangeslider'); + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + var rangeContainerIn, rangeContainerOut; + function coerceRange(attr, dflt) { + return Lib.coerce(rangeContainerIn, rangeContainerOut, oppAxisAttrs, attr, dflt); + } + + var visible = coerce('visible'); + if(!visible) return; + + coerce('bgcolor', layoutOut.plot_bgcolor); + coerce('bordercolor'); + coerce('borderwidth'); + coerce('thickness'); + + coerce('autorange', !axOut.isValidRange(containerIn.range)); + coerce('range'); + + var subplots = layoutOut._subplots; + if(subplots) { + var yIds = subplots.cartesian + .filter(function(subplotId) { + return subplotId.substr(0, subplotId.indexOf('y')) === axisIds.name2id(axName); + }) + .map(function(subplotId) { + return subplotId.substr(subplotId.indexOf('y'), subplotId.length); + }); + var yNames = Lib.simpleMap(yIds, axisIds.id2name); + for(var i = 0; i < yNames.length; i++) { + var yName = yNames[i]; + + rangeContainerIn = containerIn[yName] || {}; + rangeContainerOut = Template.newContainer(containerOut, yName, 'yaxis'); + + var yAxOut = layoutOut[yName]; + + var rangemodeDflt; + if(rangeContainerIn.range && yAxOut.isValidRange(rangeContainerIn.range)) { + rangemodeDflt = 'fixed'; + } + + var rangeMode = coerceRange('rangemode', rangemodeDflt); + if(rangeMode !== 'match') { + coerceRange('range', yAxOut.range.slice()); + } + } + } + + // to map back range slider (auto) range + containerOut._input = containerIn; +}; + +},{"../../lib":285,"../../plot_api/plot_template":320,"../../plots/cartesian/axis_ids":335,"./attributes":227,"./oppaxis_attributes":234}],231:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); + +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var Titles = _dereq_('../titles'); + +var Cartesian = _dereq_('../../plots/cartesian'); +var axisIDs = _dereq_('../../plots/cartesian/axis_ids'); + +var dragElement = _dereq_('../dragelement'); +var setCursor = _dereq_('../../lib/setcursor'); + +var constants = _dereq_('./constants'); + +module.exports = function(gd) { + var fullLayout = gd._fullLayout; + var rangeSliderData = fullLayout._rangeSliderData; + for(var i = 0; i < rangeSliderData.length; i++) { + var opts = rangeSliderData[i][constants.name]; + // fullLayout._uid may not exist when we call makeData + opts._clipId = opts._id + '-' + fullLayout._uid; + } + + /* + * + * + * < .... range plot /> + * + * + * + * + * + * + * + * + * + * + * ... + */ + + function keyFunction(axisOpts) { + return axisOpts._name; + } + + var rangeSliders = fullLayout._infolayer + .selectAll('g.' + constants.containerClassName) + .data(rangeSliderData, keyFunction); + + // remove exiting sliders and their corresponding clip paths + rangeSliders.exit().each(function(axisOpts) { + var opts = axisOpts[constants.name]; + fullLayout._topdefs.select('#' + opts._clipId).remove(); + }).remove(); + + // return early if no range slider is visible + if(rangeSliderData.length === 0) return; + + rangeSliders.enter().append('g') + .classed(constants.containerClassName, true) + .attr('pointer-events', 'all'); + + // for all present range sliders + rangeSliders.each(function(axisOpts) { + var rangeSlider = d3.select(this); + var opts = axisOpts[constants.name]; + var oppAxisOpts = fullLayout[axisIDs.id2name(axisOpts.anchor)]; + var oppAxisRangeOpts = opts[axisIDs.id2name(axisOpts.anchor)]; + + // update range + // Expand slider range to the axis range + if(opts.range) { + var rng = Lib.simpleMap(opts.range, axisOpts.r2l); + var axRng = Lib.simpleMap(axisOpts.range, axisOpts.r2l); + var newRng; + + if(axRng[0] < axRng[1]) { + newRng = [ + Math.min(rng[0], axRng[0]), + Math.max(rng[1], axRng[1]) + ]; + } else { + newRng = [ + Math.max(rng[0], axRng[0]), + Math.min(rng[1], axRng[1]) + ]; + } + + opts.range = opts._input.range = Lib.simpleMap(newRng, axisOpts.l2r); + } + + axisOpts.cleanRange('rangeslider.range'); + + // update range slider dimensions + + var gs = fullLayout._size; + var domain = axisOpts.domain; + + opts._width = gs.w * (domain[1] - domain[0]); + + var x = Math.round(gs.l + (gs.w * domain[0])); + + var y = Math.round( + gs.t + gs.h * (1 - axisOpts._counterDomainMin) + + (axisOpts.side === 'bottom' ? axisOpts._depth : 0) + + opts._offsetShift + constants.extraPad + ); + + rangeSlider.attr('transform', strTranslate(x, y)); + + // update data <--> pixel coordinate conversion methods + + opts._rl = Lib.simpleMap(opts.range, axisOpts.r2l); + var rl0 = opts._rl[0]; + var rl1 = opts._rl[1]; + var drl = rl1 - rl0; + + opts.p2d = function(v) { + return (v / opts._width) * drl + rl0; + }; + + opts.d2p = function(v) { + return (v - rl0) / drl * opts._width; + }; + + if(axisOpts.rangebreaks) { + var rsBreaks = axisOpts.locateBreaks(rl0, rl1); + + if(rsBreaks.length) { + var j, brk; + + var lBreaks = 0; + for(j = 0; j < rsBreaks.length; j++) { + brk = rsBreaks[j]; + lBreaks += (brk.max - brk.min); + } + + // TODO fix for reversed-range axes !!! + + // compute slope and piecewise offsets + var m2 = opts._width / (rl1 - rl0 - lBreaks); + var _B = [-m2 * rl0]; + for(j = 0; j < rsBreaks.length; j++) { + brk = rsBreaks[j]; + _B.push(_B[_B.length - 1] - m2 * (brk.max - brk.min)); + } + + opts.d2p = function(v) { + var b = _B[0]; + for(var j = 0; j < rsBreaks.length; j++) { + var brk = rsBreaks[j]; + if(v >= brk.max) b = _B[j + 1]; + else if(v < brk.min) break; + } + return b + m2 * v; + }; + + // fill pixel (i.e. 'p') min/max here, + // to not have to loop through the _rangebreaks twice during `p2d` + for(j = 0; j < rsBreaks.length; j++) { + brk = rsBreaks[j]; + brk.pmin = opts.d2p(brk.min); + brk.pmax = opts.d2p(brk.max); + } + + opts.p2d = function(v) { + var b = _B[0]; + for(var j = 0; j < rsBreaks.length; j++) { + var brk = rsBreaks[j]; + if(v >= brk.pmax) b = _B[j + 1]; + else if(v < brk.pmin) break; + } + return (v - b) / m2; + }; + } + } + + if(oppAxisRangeOpts.rangemode !== 'match') { + var range0OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[0]); + var range1OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[1]); + var distOppAxis = range1OppAxis - range0OppAxis; + + opts.d2pOppAxis = function(v) { + return (v - range0OppAxis) / distOppAxis * opts._height; + }; + } + + // update inner nodes + + rangeSlider + .call(drawBg, gd, axisOpts, opts) + .call(addClipPath, gd, axisOpts, opts) + .call(drawRangePlot, gd, axisOpts, opts) + .call(drawMasks, gd, axisOpts, opts, oppAxisRangeOpts) + .call(drawSlideBox, gd, axisOpts, opts) + .call(drawGrabbers, gd, axisOpts, opts); + + // setup drag element + setupDragElement(rangeSlider, gd, axisOpts, opts); + + // update current range + setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts); + + // title goes next to range slider instead of tick labels, so + // just take it over and draw it from here + if(axisOpts.side === 'bottom') { + Titles.draw(gd, axisOpts._id + 'title', { + propContainer: axisOpts, + propName: axisOpts._name + '.title', + placeholder: fullLayout._dfltTitle.x, + attributes: { + x: axisOpts._offset + axisOpts._length / 2, + y: y + opts._height + opts._offsetShift + 10 + 1.5 * axisOpts.title.font.size, + 'text-anchor': 'middle' + } + }); + } + }); +}; + +function setupDragElement(rangeSlider, gd, axisOpts, opts) { + if(gd._context.staticPlot) return; + + var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node(); + var grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node(); + var grabAreaMax = rangeSlider.select('rect.' + constants.grabAreaMaxClassName).node(); + + function mouseDownHandler() { + var event = d3.event; + var target = event.target; + var startX = event.clientX || event.touches[0].clientX; + var offsetX = startX - rangeSlider.node().getBoundingClientRect().left; + var minVal = opts.d2p(axisOpts._rl[0]); + var maxVal = opts.d2p(axisOpts._rl[1]); + + var dragCover = dragElement.coverSlip(); + + this.addEventListener('touchmove', mouseMove); + this.addEventListener('touchend', mouseUp); + dragCover.addEventListener('mousemove', mouseMove); + dragCover.addEventListener('mouseup', mouseUp); + + function mouseMove(e) { + var clientX = e.clientX || e.touches[0].clientX; + var delta = +clientX - startX; + var pixelMin, pixelMax, cursor; + + switch(target) { + case slideBox: + cursor = 'ew-resize'; + pixelMin = minVal + delta; + pixelMax = maxVal + delta; + break; + + case grabAreaMin: + cursor = 'col-resize'; + pixelMin = minVal + delta; + pixelMax = maxVal; + break; + + case grabAreaMax: + cursor = 'col-resize'; + pixelMin = minVal; + pixelMax = maxVal + delta; + break; + + default: + cursor = 'ew-resize'; + pixelMin = offsetX; + pixelMax = offsetX + delta; + break; + } + + if(pixelMax < pixelMin) { + var tmp = pixelMax; + pixelMax = pixelMin; + pixelMin = tmp; + } + + opts._pixelMin = pixelMin; + opts._pixelMax = pixelMax; + + setCursor(d3.select(dragCover), cursor); + setDataRange(rangeSlider, gd, axisOpts, opts); + } + + function mouseUp() { + dragCover.removeEventListener('mousemove', mouseMove); + dragCover.removeEventListener('mouseup', mouseUp); + this.removeEventListener('touchmove', mouseMove); + this.removeEventListener('touchend', mouseUp); + Lib.removeElement(dragCover); + } + } + + rangeSlider.on('mousedown', mouseDownHandler); + rangeSlider.on('touchstart', mouseDownHandler); +} + +function setDataRange(rangeSlider, gd, axisOpts, opts) { + function clamp(v) { + return axisOpts.l2r(Lib.constrain(v, opts._rl[0], opts._rl[1])); + } + + var dataMin = clamp(opts.p2d(opts._pixelMin)); + var dataMax = clamp(opts.p2d(opts._pixelMax)); + + window.requestAnimationFrame(function() { + Registry.call('_guiRelayout', gd, axisOpts._name + '.range', [dataMin, dataMax]); + }); +} + +function setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts) { + var hw2 = constants.handleWidth / 2; + + function clamp(v) { + return Lib.constrain(v, 0, opts._width); + } + + function clampOppAxis(v) { + return Lib.constrain(v, 0, opts._height); + } + + function clampHandle(v) { + return Lib.constrain(v, -hw2, opts._width + hw2); + } + + var pixelMin = clamp(opts.d2p(axisOpts._rl[0])); + var pixelMax = clamp(opts.d2p(axisOpts._rl[1])); + + rangeSlider.select('rect.' + constants.slideBoxClassName) + .attr('x', pixelMin) + .attr('width', pixelMax - pixelMin); + + rangeSlider.select('rect.' + constants.maskMinClassName) + .attr('width', pixelMin); + + rangeSlider.select('rect.' + constants.maskMaxClassName) + .attr('x', pixelMax) + .attr('width', opts._width - pixelMax); + + if(oppAxisRangeOpts.rangemode !== 'match') { + var pixelMinOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[1])); + var pixelMaxOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[0])); + + rangeSlider.select('rect.' + constants.maskMinOppAxisClassName) + .attr('x', pixelMin) + .attr('height', pixelMinOppAxis) + .attr('width', pixelMax - pixelMin); + + rangeSlider.select('rect.' + constants.maskMaxOppAxisClassName) + .attr('x', pixelMin) + .attr('y', pixelMaxOppAxis) + .attr('height', opts._height - pixelMaxOppAxis) + .attr('width', pixelMax - pixelMin); + + rangeSlider.select('rect.' + constants.slideBoxClassName) + .attr('y', pixelMinOppAxis) + .attr('height', pixelMaxOppAxis - pixelMinOppAxis); + } + + // add offset for crispier corners + // https://github.com/plotly/plotly.js/pull/1409 + var offset = 0.5; + + var xMin = Math.round(clampHandle(pixelMin - hw2)) - offset; + var xMax = Math.round(clampHandle(pixelMax - hw2)) + offset; + + rangeSlider.select('g.' + constants.grabberMinClassName) + .attr('transform', strTranslate(xMin, offset)); + + rangeSlider.select('g.' + constants.grabberMaxClassName) + .attr('transform', strTranslate(xMax, offset)); +} + +function drawBg(rangeSlider, gd, axisOpts, opts) { + var bg = Lib.ensureSingle(rangeSlider, 'rect', constants.bgClassName, function(s) { + s.attr({ + x: 0, + y: 0, + 'shape-rendering': 'crispEdges' + }); + }); + + var borderCorrect = (opts.borderwidth % 2) === 0 ? + opts.borderwidth : + opts.borderwidth - 1; + + var offsetShift = -opts._offsetShift; + var lw = Drawing.crispRound(gd, opts.borderwidth); + + bg.attr({ + width: opts._width + borderCorrect, + height: opts._height + borderCorrect, + transform: strTranslate(offsetShift, offsetShift), + fill: opts.bgcolor, + stroke: opts.bordercolor, + 'stroke-width': lw + }); +} + +function addClipPath(rangeSlider, gd, axisOpts, opts) { + var fullLayout = gd._fullLayout; + + var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', opts._clipId, function(s) { + s.append('rect').attr({ x: 0, y: 0 }); + }); + + clipPath.select('rect').attr({ + width: opts._width, + height: opts._height + }); +} + +function drawRangePlot(rangeSlider, gd, axisOpts, opts) { + var calcData = gd.calcdata; + + var rangePlots = rangeSlider.selectAll('g.' + constants.rangePlotClassName) + .data(axisOpts._subplotsWith, Lib.identity); + + rangePlots.enter().append('g') + .attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; }) + .call(Drawing.setClipUrl, opts._clipId, gd); + + rangePlots.order(); + + rangePlots.exit().remove(); + + var mainplotinfo; + + rangePlots.each(function(id, i) { + var plotgroup = d3.select(this); + var isMainPlot = (i === 0); + + var oppAxisOpts = axisIDs.getFromId(gd, id, 'y'); + var oppAxisName = oppAxisOpts._name; + var oppAxisRangeOpts = opts[oppAxisName]; + + var mockFigure = { + data: [], + layout: { + xaxis: { + type: axisOpts.type, + domain: [0, 1], + range: opts.range.slice(), + calendar: axisOpts.calendar + }, + width: opts._width, + height: opts._height, + margin: { t: 0, b: 0, l: 0, r: 0 } + }, + _context: gd._context + }; + + if(axisOpts.rangebreaks) { + mockFigure.layout.xaxis.rangebreaks = axisOpts.rangebreaks; + } + + mockFigure.layout[oppAxisName] = { + type: oppAxisOpts.type, + domain: [0, 1], + range: oppAxisRangeOpts.rangemode !== 'match' ? oppAxisRangeOpts.range.slice() : oppAxisOpts.range.slice(), + calendar: oppAxisOpts.calendar + }; + + if(oppAxisOpts.rangebreaks) { + mockFigure.layout[oppAxisName].rangebreaks = oppAxisOpts.rangebreaks; + } + + Plots.supplyDefaults(mockFigure); + + var xa = mockFigure._fullLayout.xaxis; + var ya = mockFigure._fullLayout[oppAxisName]; + + xa.clearCalc(); + xa.setScale(); + ya.clearCalc(); + ya.setScale(); + + var plotinfo = { + id: id, + plotgroup: plotgroup, + xaxis: xa, + yaxis: ya, + isRangePlot: true + }; + + if(isMainPlot) mainplotinfo = plotinfo; + else { + plotinfo.mainplot = 'xy'; + plotinfo.mainplotinfo = mainplotinfo; + } + + Cartesian.rangePlot(gd, plotinfo, filterRangePlotCalcData(calcData, id)); + }); +} + +function filterRangePlotCalcData(calcData, subplotId) { + var out = []; + + for(var i = 0; i < calcData.length; i++) { + var calcTrace = calcData[i]; + var trace = calcTrace[0].trace; + + if(trace.xaxis + trace.yaxis === subplotId) { + out.push(calcTrace); + } + } + + return out; +} + +function drawMasks(rangeSlider, gd, axisOpts, opts, oppAxisRangeOpts) { + var maskMin = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinClassName, function(s) { + s.attr({ + x: 0, + y: 0, + 'shape-rendering': 'crispEdges' + }); + }); + + maskMin + .attr('height', opts._height) + .call(Color.fill, constants.maskColor); + + var maskMax = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxClassName, function(s) { + s.attr({ + y: 0, + 'shape-rendering': 'crispEdges' + }); + }); + + maskMax + .attr('height', opts._height) + .call(Color.fill, constants.maskColor); + + // masks used for oppAxis zoom + if(oppAxisRangeOpts.rangemode !== 'match') { + var maskMinOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinOppAxisClassName, function(s) { + s.attr({ + y: 0, + 'shape-rendering': 'crispEdges' + }); + }); + + maskMinOppAxis + .attr('width', opts._width) + .call(Color.fill, constants.maskOppAxisColor); + + var maskMaxOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxOppAxisClassName, function(s) { + s.attr({ + y: 0, + 'shape-rendering': 'crispEdges' + }); + }); + + maskMaxOppAxis + .attr('width', opts._width) + .style('border-top', constants.maskOppBorder) + .call(Color.fill, constants.maskOppAxisColor); + } +} + +function drawSlideBox(rangeSlider, gd, axisOpts, opts) { + if(gd._context.staticPlot) return; + + var slideBox = Lib.ensureSingle(rangeSlider, 'rect', constants.slideBoxClassName, function(s) { + s.attr({ + y: 0, + cursor: constants.slideBoxCursor, + 'shape-rendering': 'crispEdges' + }); + }); + + slideBox.attr({ + height: opts._height, + fill: constants.slideBoxFill + }); +} + +function drawGrabbers(rangeSlider, gd, axisOpts, opts) { + // + var grabberMin = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMinClassName); + var grabberMax = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMaxClassName); + + // + var handleFixAttrs = { + x: 0, + width: constants.handleWidth, + rx: constants.handleRadius, + fill: Color.background, + stroke: Color.defaultLine, + 'stroke-width': constants.handleStrokeWidth, + 'shape-rendering': 'crispEdges' + }; + var handleDynamicAttrs = { + y: Math.round(opts._height / 4), + height: Math.round(opts._height / 2), + }; + var handleMin = Lib.ensureSingle(grabberMin, 'rect', constants.handleMinClassName, function(s) { + s.attr(handleFixAttrs); + }); + handleMin.attr(handleDynamicAttrs); + + var handleMax = Lib.ensureSingle(grabberMax, 'rect', constants.handleMaxClassName, function(s) { + s.attr(handleFixAttrs); + }); + handleMax.attr(handleDynamicAttrs); + + // + var grabAreaFixAttrs = { + width: constants.grabAreaWidth, + x: 0, + y: 0, + fill: constants.grabAreaFill, + cursor: !gd._context.staticPlot ? constants.grabAreaCursor : undefined, + }; + + var grabAreaMin = Lib.ensureSingle(grabberMin, 'rect', constants.grabAreaMinClassName, function(s) { + s.attr(grabAreaFixAttrs); + }); + grabAreaMin.attr('height', opts._height); + + var grabAreaMax = Lib.ensureSingle(grabberMax, 'rect', constants.grabAreaMaxClassName, function(s) { + s.attr(grabAreaFixAttrs); + }); + grabAreaMax.attr('height', opts._height); +} + +},{"../../lib":285,"../../lib/setcursor":305,"../../plots/cartesian":345,"../../plots/cartesian/axis_ids":335,"../../plots/plots":366,"../../registry":373,"../color":155,"../dragelement":174,"../drawing":177,"../titles":253,"./constants":229,"@plotly/d3":20}],232:[function(_dereq_,module,exports){ +'use strict'; + +var axisIDs = _dereq_('../../plots/cartesian/axis_ids'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var constants = _dereq_('./constants'); +var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING; +var name = constants.name; + +function isVisible(ax) { + var rangeSlider = ax && ax[name]; + return rangeSlider && rangeSlider.visible; +} +exports.isVisible = isVisible; + +exports.makeData = function(fullLayout) { + var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true); + var margin = fullLayout.margin; + var rangeSliderData = []; + + if(!fullLayout._has('gl2d')) { + for(var i = 0; i < axes.length; i++) { + var ax = axes[i]; + + if(isVisible(ax)) { + rangeSliderData.push(ax); + + var opts = ax[name]; + opts._id = name + ax._id; + opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness; + opts._offsetShift = Math.floor(opts.borderwidth / 2); + } + } + } + + fullLayout._rangeSliderData = rangeSliderData; +}; + +exports.autoMarginOpts = function(gd, ax) { + var fullLayout = gd._fullLayout; + var opts = ax[name]; + var axLetter = ax._id.charAt(0); + + var bottomDepth = 0; + var titleHeight = 0; + if(ax.side === 'bottom') { + bottomDepth = ax._depth; + if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { + // as in rangeslider/draw.js + titleHeight = 1.5 * ax.title.font.size + 10 + opts._offsetShift; + // multi-line extra bump + var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length; + titleHeight += extraLines * ax.title.font.size * LINE_SPACING; + } + } + + return { + x: 0, + y: ax._counterDomainMin, + l: 0, + r: 0, + t: 0, + b: opts._height + bottomDepth + Math.max(fullLayout.margin.b, titleHeight), + pad: constants.extraPad + opts._offsetShift * 2 + }; +}; + +},{"../../constants/alignment":260,"../../lib/svg_text_utils":307,"../../plots/cartesian/axis_ids":335,"./constants":229}],233:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var attrs = _dereq_('./attributes'); +var oppAxisAttrs = _dereq_('./oppaxis_attributes'); +var helpers = _dereq_('./helpers'); + +module.exports = { + moduleType: 'component', + name: 'rangeslider', + + schema: { + subplots: { + xaxis: { + rangeslider: Lib.extendFlat({}, attrs, { + yaxis: oppAxisAttrs + }) + } + } + }, + + layoutAttributes: _dereq_('./attributes'), + handleDefaults: _dereq_('./defaults'), + calcAutorange: _dereq_('./calc_autorange'), + draw: _dereq_('./draw'), + isVisible: helpers.isVisible, + makeData: helpers.makeData, + autoMarginOpts: helpers.autoMarginOpts +}; + +},{"../../lib":285,"./attributes":227,"./calc_autorange":228,"./defaults":230,"./draw":231,"./helpers":232,"./oppaxis_attributes":234}],234:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + // not really a 'subplot' attribute container, + // but this is the flag we use to denote attributes that + // support yaxis, yaxis2, yaxis3, ... counters + _isSubplotObj: true, + + rangemode: { + valType: 'enumerated', + values: ['auto', 'fixed', 'match'], + dflt: 'match', + editType: 'calc', + }, + range: { + valType: 'info_array', + items: [ + {valType: 'any', editType: 'plot'}, + {valType: 'any', editType: 'plot'} + ], + editType: 'plot', + }, + editType: 'calc' +}; + +},{}],235:[function(_dereq_,module,exports){ +'use strict'; + +var annAttrs = _dereq_('../annotations/attributes'); +var scatterLineAttrs = _dereq_('../../traces/scatter/attributes').line; +var dash = _dereq_('../drawing/attributes').dash; +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; +var axisPlaceableObjs = _dereq_('../../constants/axis_placeable_objects'); + +module.exports = templatedArray('shape', { + visible: { + valType: 'boolean', + dflt: true, + editType: 'calc+arraydraw', + }, + + type: { + valType: 'enumerated', + values: ['circle', 'rect', 'path', 'line'], + editType: 'calc+arraydraw', + }, + + layer: { + valType: 'enumerated', + values: ['below', 'above'], + dflt: 'above', + editType: 'arraydraw', + }, + + xref: extendFlat({}, annAttrs.xref, { + }), + xsizemode: { + valType: 'enumerated', + values: ['scaled', 'pixel'], + dflt: 'scaled', + editType: 'calc+arraydraw', + }, + xanchor: { + valType: 'any', + editType: 'calc+arraydraw', + }, + x0: { + valType: 'any', + editType: 'calc+arraydraw', + }, + x1: { + valType: 'any', + editType: 'calc+arraydraw', + }, + + yref: extendFlat({}, annAttrs.yref, { + }), + ysizemode: { + valType: 'enumerated', + values: ['scaled', 'pixel'], + dflt: 'scaled', + editType: 'calc+arraydraw', + }, + yanchor: { + valType: 'any', + editType: 'calc+arraydraw', + }, + y0: { + valType: 'any', + editType: 'calc+arraydraw', + }, + y1: { + valType: 'any', + editType: 'calc+arraydraw', + }, + + path: { + valType: 'string', + editType: 'calc+arraydraw', + }, + + opacity: { + valType: 'number', + min: 0, + max: 1, + dflt: 1, + editType: 'arraydraw', + }, + line: { + color: extendFlat({}, scatterLineAttrs.color, {editType: 'arraydraw'}), + width: extendFlat({}, scatterLineAttrs.width, {editType: 'calc+arraydraw'}), + dash: extendFlat({}, dash, {editType: 'arraydraw'}), + editType: 'calc+arraydraw' + }, + fillcolor: { + valType: 'color', + dflt: 'rgba(0,0,0,0)', + editType: 'arraydraw', + }, + fillrule: { + valType: 'enumerated', + values: ['evenodd', 'nonzero'], + dflt: 'evenodd', + editType: 'arraydraw', + }, + editable: { + valType: 'boolean', + dflt: false, + editType: 'calc+arraydraw', + }, + + editType: 'arraydraw' +}); + +},{"../../constants/axis_placeable_objects":261,"../../lib/extend":279,"../../plot_api/plot_template":320,"../../traces/scatter/attributes":494,"../annotations/attributes":138,"../drawing/attributes":176}],236:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +var constants = _dereq_('./constants'); +var helpers = _dereq_('./helpers'); + + +module.exports = function calcAutorange(gd) { + var fullLayout = gd._fullLayout; + var shapeList = Lib.filterVisible(fullLayout.shapes); + + if(!shapeList.length || !gd._fullData.length) return; + + for(var i = 0; i < shapeList.length; i++) { + var shape = shapeList[i]; + shape._extremes = {}; + + var ax; var bounds; + var xRefType = Axes.getRefType(shape.xref); + var yRefType = Axes.getRefType(shape.yref); + + // paper and axis domain referenced shapes don't affect autorange + if(shape.xref !== 'paper' && xRefType !== 'domain') { + var vx0 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x0; + var vx1 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x1; + ax = Axes.getFromId(gd, shape.xref); + + bounds = shapeBounds(ax, vx0, vx1, shape.path, constants.paramIsX); + if(bounds) { + shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape)); + } + } + + if(shape.yref !== 'paper' && yRefType !== 'domain') { + var vy0 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y0; + var vy1 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y1; + ax = Axes.getFromId(gd, shape.yref); + + bounds = shapeBounds(ax, vy0, vy1, shape.path, constants.paramIsY); + if(bounds) { + shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape)); + } + } + } +}; + +function calcXPaddingOptions(shape) { + return calcPaddingOptions(shape.line.width, shape.xsizemode, shape.x0, shape.x1, shape.path, false); +} + +function calcYPaddingOptions(shape) { + return calcPaddingOptions(shape.line.width, shape.ysizemode, shape.y0, shape.y1, shape.path, true); +} + +function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) { + var ppad = lineWidth / 2; + var axisDirectionReverted = isYAxis; + + if(sizeMode === 'pixel') { + var coords = path ? + helpers.extractPathCoords(path, isYAxis ? constants.paramIsY : constants.paramIsX) : + [v0, v1]; + var maxValue = Lib.aggNums(Math.max, null, coords); + var minValue = Lib.aggNums(Math.min, null, coords); + var beforePad = minValue < 0 ? Math.abs(minValue) + ppad : ppad; + var afterPad = maxValue > 0 ? maxValue + ppad : ppad; + + return { + ppad: ppad, + ppadplus: axisDirectionReverted ? beforePad : afterPad, + ppadminus: axisDirectionReverted ? afterPad : beforePad + }; + } else { + return {ppad: ppad}; + } +} + +function shapeBounds(ax, v0, v1, path, paramsToUse) { + var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c; + + if(v0 !== undefined) return [convertVal(v0), convertVal(v1)]; + if(!path) return; + + var min = Infinity; + var max = -Infinity; + var segments = path.match(constants.segmentRE); + var i; + var segment; + var drawnParam; + var params; + var val; + + if(ax.type === 'date') convertVal = helpers.decodeDate(convertVal); + + for(i = 0; i < segments.length; i++) { + segment = segments[i]; + drawnParam = paramsToUse[segment.charAt(0)].drawn; + if(drawnParam === undefined) continue; + + params = segments[i].substr(1).match(constants.paramRE); + if(!params || params.length < drawnParam) continue; + + val = convertVal(params[drawnParam]); + if(val < min) min = val; + if(val > max) max = val; + } + if(max >= min) return [min, max]; +} + +},{"../../lib":285,"../../plots/cartesian/axes":331,"./constants":237,"./helpers":246}],237:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + segmentRE: /[MLHVQCTSZ][^MLHVQCTSZ]*/g, + paramRE: /[^\s,]+/g, + + // which numbers in each path segment are x (or y) values + // drawn is which param is a drawn point, as opposed to a + // control point (which doesn't count toward autorange. + // TODO: this means curved paths could extend beyond the + // autorange bounds. This is a bit tricky to get right + // unless we revert to bounding boxes, but perhaps there's + // a calculation we could do...) + paramIsX: { + M: {0: true, drawn: 0}, + L: {0: true, drawn: 0}, + H: {0: true, drawn: 0}, + V: {}, + Q: {0: true, 2: true, drawn: 2}, + C: {0: true, 2: true, 4: true, drawn: 4}, + T: {0: true, drawn: 0}, + S: {0: true, 2: true, drawn: 2}, + // A: {0: true, 5: true}, + Z: {} + }, + + paramIsY: { + M: {1: true, drawn: 1}, + L: {1: true, drawn: 1}, + H: {}, + V: {0: true, drawn: 0}, + Q: {1: true, 3: true, drawn: 3}, + C: {1: true, 3: true, 5: true, drawn: 5}, + T: {1: true, drawn: 1}, + S: {1: true, 3: true, drawn: 5}, + // A: {1: true, 6: true}, + Z: {} + }, + + numParams: { + M: 2, + L: 2, + H: 1, + V: 1, + Q: 4, + C: 6, + T: 2, + S: 4, + // A: 7, + Z: 0 + } +}; + +},{}],238:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); + +var attributes = _dereq_('./attributes'); +var helpers = _dereq_('./helpers'); + + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + handleArrayContainerDefaults(layoutIn, layoutOut, { + name: 'shapes', + handleItemDefaults: handleShapeDefaults + }); +}; + +function handleShapeDefaults(shapeIn, shapeOut, fullLayout) { + function coerce(attr, dflt) { + return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt); + } + + var visible = coerce('visible'); + if(!visible) return; + + var path = coerce('path'); + var dfltType = path ? 'path' : 'rect'; + var shapeType = coerce('type', dfltType); + if(shapeOut.type !== 'path') delete shapeOut.path; + + coerce('editable'); + coerce('layer'); + coerce('opacity'); + coerce('fillcolor'); + coerce('fillrule'); + var lineWidth = coerce('line.width'); + if(lineWidth) { + coerce('line.color'); + coerce('line.dash'); + } + + var xSizeMode = coerce('xsizemode'); + var ySizeMode = coerce('ysizemode'); + + // positioning + var axLetters = ['x', 'y']; + for(var i = 0; i < 2; i++) { + var axLetter = axLetters[i]; + var attrAnchor = axLetter + 'anchor'; + var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode; + var gdMock = {_fullLayout: fullLayout}; + var ax; + var pos2r; + var r2pos; + + // xref, yref + var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined, + 'paper'); + var axRefType = Axes.getRefType(axRef); + + if(axRefType === 'range') { + ax = Axes.getFromId(gdMock, axRef); + ax._shapeIndices.push(shapeOut._index); + r2pos = helpers.rangeToShapePosition(ax); + pos2r = helpers.shapePositionToRange(ax); + } else { + pos2r = r2pos = Lib.identity; + } + + // Coerce x0, x1, y0, y1 + if(shapeType !== 'path') { + var dflt0 = 0.25; + var dflt1 = 0.75; + + // hack until V3.0 when log has regular range behavior - make it look like other + // ranges to send to coerce, then put it back after + // this is all to give reasonable default position behavior on log axes, which is + // a pretty unimportant edge case so we could just ignore this. + var attr0 = axLetter + '0'; + var attr1 = axLetter + '1'; + var in0 = shapeIn[attr0]; + var in1 = shapeIn[attr1]; + shapeIn[attr0] = pos2r(shapeIn[attr0], true); + shapeIn[attr1] = pos2r(shapeIn[attr1], true); + + if(sizeMode === 'pixel') { + coerce(attr0, 0); + coerce(attr1, 10); + } else { + Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0); + Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1); + } + + // hack part 2 + shapeOut[attr0] = r2pos(shapeOut[attr0]); + shapeOut[attr1] = r2pos(shapeOut[attr1]); + shapeIn[attr0] = in0; + shapeIn[attr1] = in1; + } + + // Coerce xanchor and yanchor + if(sizeMode === 'pixel') { + // Hack for log axis described above + var inAnchor = shapeIn[attrAnchor]; + shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true); + + Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25); + + // Hack part 2 + shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]); + shapeIn[attrAnchor] = inAnchor; + } + } + + if(shapeType === 'path') { + coerce('path'); + } else { + Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']); + } +} + +},{"../../lib":285,"../../plots/array_container_defaults":326,"../../plots/cartesian/axes":331,"./attributes":235,"./helpers":246}],239:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +var readPaths = _dereq_('./draw_newshape/helpers').readPaths; +var displayOutlines = _dereq_('./draw_newshape/display_outlines'); + +var clearOutlineControllers = _dereq_('../../plots/cartesian/handle_outline').clearOutlineControllers; + +var Color = _dereq_('../color'); +var Drawing = _dereq_('../drawing'); +var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor; + +var dragElement = _dereq_('../dragelement'); +var setCursor = _dereq_('../../lib/setcursor'); + +var constants = _dereq_('./constants'); +var helpers = _dereq_('./helpers'); + + +// Shapes are stored in gd.layout.shapes, an array of objects +// index can point to one item in this array, +// or non-numeric to simply add a new one +// or -1 to modify all existing +// opt can be the full options object, or one key (to be set to value) +// or undefined to simply redraw +// if opt is blank, val can be 'add' or a full options object to add a new +// annotation at that point in the array, or 'remove' to delete this one + +module.exports = { + draw: draw, + drawOne: drawOne, + eraseActiveShape: eraseActiveShape +}; + +function draw(gd) { + var fullLayout = gd._fullLayout; + + // Remove previous shapes before drawing new in shapes in fullLayout.shapes + fullLayout._shapeUpperLayer.selectAll('path').remove(); + fullLayout._shapeLowerLayer.selectAll('path').remove(); + + for(var k in fullLayout._plots) { + var shapelayer = fullLayout._plots[k].shapelayer; + if(shapelayer) shapelayer.selectAll('path').remove(); + } + + for(var i = 0; i < fullLayout.shapes.length; i++) { + if(fullLayout.shapes[i].visible) { + drawOne(gd, i); + } + } + + // may need to resurrect this if we put text (LaTeX) in shapes + // return Plots.previousPromises(gd); +} + +function shouldSkipEdits(gd) { + return !!gd._fullLayout._drawing; +} + +function couldHaveActiveShape(gd) { + // for now keep config.editable: true as it was before shape-drawing PR + return !gd._context.edits.shapePosition; +} + +function drawOne(gd, index) { + // remove the existing shape if there is one. + // because indices can change, we need to look in all shape layers + gd._fullLayout._paperdiv + .selectAll('.shapelayer [data-index="' + index + '"]') + .remove(); + + var o = helpers.makeOptionsAndPlotinfo(gd, index); + var options = o.options; + var plotinfo = o.plotinfo; + + // this shape is gone - quit now after deleting it + // TODO: use d3 idioms instead of deleting and redrawing every time + if(!options._input || options.visible === false) return; + + if(options.layer !== 'below') { + drawShape(gd._fullLayout._shapeUpperLayer); + } else if(options.xref === 'paper' || options.yref === 'paper') { + drawShape(gd._fullLayout._shapeLowerLayer); + } else { + if(plotinfo._hadPlotinfo) { + var mainPlot = plotinfo.mainplotinfo || plotinfo; + drawShape(mainPlot.shapelayer); + } else { + // Fall back to _shapeLowerLayer in case the requested subplot doesn't exist. + // This can happen if you reference the shape to an x / y axis combination + // that doesn't have any data on it (and layer is below) + drawShape(gd._fullLayout._shapeLowerLayer); + } + } + + function drawShape(shapeLayer) { + var d = getPathString(gd, options); + var attrs = { + 'data-index': index, + 'fill-rule': options.fillrule, + d: d + }; + + var opacity = options.opacity; + var fillColor = options.fillcolor; + var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)'; + var lineWidth = options.line.width; + var lineDash = options.line.dash; + if(!lineWidth && options.editable === true) { + // ensure invisible border to activate the shape + lineWidth = 5; + lineDash = 'solid'; + } + + var isOpen = d[d.length - 1] !== 'Z'; + + var isActiveShape = couldHaveActiveShape(gd) && + options.editable && gd._fullLayout._activeShapeIndex === index; + + if(isActiveShape) { + fillColor = isOpen ? 'rgba(0,0,0,0)' : + gd._fullLayout.activeshape.fillcolor; + + opacity = gd._fullLayout.activeshape.opacity; + } + + var path = shapeLayer.append('path') + .attr(attrs) + .style('opacity', opacity) + .call(Color.stroke, lineColor) + .call(Color.fill, fillColor) + .call(Drawing.dashLine, lineDash, lineWidth); + + setClipPath(path, gd, options); + + var editHelpers; + if(isActiveShape || gd._context.edits.shapePosition) editHelpers = arrayEditor(gd.layout, 'shapes', options); + + if(isActiveShape) { + path.style({ + 'cursor': 'move', + }); + + var dragOptions = { + element: path.node(), + plotinfo: plotinfo, + gd: gd, + editHelpers: editHelpers, + isActiveShape: true // i.e. to enable controllers + }; + + var polygons = readPaths(d, gd); + // display polygons on the screen + displayOutlines(polygons, path, dragOptions); + } else { + if(gd._context.edits.shapePosition) { + setupDragElement(gd, path, options, index, shapeLayer, editHelpers); + } else if(options.editable === true) { + path.style('pointer-events', + (isOpen || Color.opacity(fillColor) * opacity <= 0.5) ? 'stroke' : 'all' + ); + } + } + + path.node().addEventListener('click', function() { return activateShape(gd, path); }); + } +} + +function setClipPath(shapePath, gd, shapeOptions) { + // note that for layer="below" the clipAxes can be different from the + // subplot we're drawing this in. This could cause problems if the shape + // spans two subplots. See https://github.com/plotly/plotly.js/issues/1452 + // + // if axis is 'paper' or an axis with " domain" appended, then there is no + // clip axis + var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '').replace(/[xyz][1-9]* *domain/g, ''); + + Drawing.setClipUrl( + shapePath, + clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null, + gd + ); +} + +function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHelpers) { + var MINWIDTH = 10; + var MINHEIGHT = 10; + + var xPixelSized = shapeOptions.xsizemode === 'pixel'; + var yPixelSized = shapeOptions.ysizemode === 'pixel'; + var isLine = shapeOptions.type === 'line'; + var isPath = shapeOptions.type === 'path'; + + var modifyItem = editHelpers.modifyItem; + + var x0, y0, x1, y1, xAnchor, yAnchor; + var n0, s0, w0, e0, optN, optS, optW, optE; + var pathIn; + + // setup conversion functions + var xa = Axes.getFromId(gd, shapeOptions.xref); + var xRefType = Axes.getRefType(shapeOptions.xref); + var ya = Axes.getFromId(gd, shapeOptions.yref); + var yRefType = Axes.getRefType(shapeOptions.yref); + var x2p = helpers.getDataToPixel(gd, xa, false, xRefType); + var y2p = helpers.getDataToPixel(gd, ya, true, yRefType); + var p2x = helpers.getPixelToData(gd, xa, false, xRefType); + var p2y = helpers.getPixelToData(gd, ya, true, yRefType); + + var sensoryElement = obtainSensoryElement(); + var dragOptions = { + element: sensoryElement.node(), + gd: gd, + prepFn: startDrag, + doneFn: endDrag, + clickFn: abortDrag + }; + var dragMode; + + dragElement.init(dragOptions); + + sensoryElement.node().onmousemove = updateDragMode; + + function obtainSensoryElement() { + return isLine ? createLineDragHandles() : shapePath; + } + + function createLineDragHandles() { + var minSensoryWidth = 10; + var sensoryWidth = Math.max(shapeOptions.line.width, minSensoryWidth); + + // Helper shapes group + // Note that by setting the `data-index` attr, it is ensured that + // the helper group is purged in this modules `draw` function + var g = shapeLayer.append('g') + .attr('data-index', index); + + // Helper path for moving + g.append('path') + .attr('d', shapePath.attr('d')) + .style({ + 'cursor': 'move', + 'stroke-width': sensoryWidth, + 'stroke-opacity': '0' // ensure not visible + }); + + // Helper circles for resizing + var circleStyle = { + 'fill-opacity': '0' // ensure not visible + }; + var circleRadius = Math.max(sensoryWidth / 2, minSensoryWidth); + + g.append('circle') + .attr({ + 'data-line-point': 'start-point', + 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0), + 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0), + 'r': circleRadius + }) + .style(circleStyle) + .classed('cursor-grab', true); + + g.append('circle') + .attr({ + 'data-line-point': 'end-point', + 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1), + 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1), + 'r': circleRadius + }) + .style(circleStyle) + .classed('cursor-grab', true); + + return g; + } + + function updateDragMode(evt) { + if(shouldSkipEdits(gd)) { + dragMode = null; + return; + } + + if(isLine) { + if(evt.target.tagName === 'path') { + dragMode = 'move'; + } else { + dragMode = evt.target.attributes['data-line-point'].value === 'start-point' ? + 'resize-over-start-point' : 'resize-over-end-point'; + } + } else { + // element might not be on screen at time of setup, + // so obtain bounding box here + var dragBBox = dragOptions.element.getBoundingClientRect(); + + // choose 'move' or 'resize' + // based on initial position of cursor within the drag element + var w = dragBBox.right - dragBBox.left; + var h = dragBBox.bottom - dragBBox.top; + var x = evt.clientX - dragBBox.left; + var y = evt.clientY - dragBBox.top; + var cursor = (!isPath && w > MINWIDTH && h > MINHEIGHT && !evt.shiftKey) ? + dragElement.getCursor(x / w, 1 - y / h) : + 'move'; + + setCursor(shapePath, cursor); + + // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w' + dragMode = cursor.split('-')[0]; + } + } + + function startDrag(evt) { + if(shouldSkipEdits(gd)) return; + + // setup update strings and initial values + if(xPixelSized) { + xAnchor = x2p(shapeOptions.xanchor); + } + if(yPixelSized) { + yAnchor = y2p(shapeOptions.yanchor); + } + + if(shapeOptions.type === 'path') { + pathIn = shapeOptions.path; + } else { + x0 = xPixelSized ? shapeOptions.x0 : x2p(shapeOptions.x0); + y0 = yPixelSized ? shapeOptions.y0 : y2p(shapeOptions.y0); + x1 = xPixelSized ? shapeOptions.x1 : x2p(shapeOptions.x1); + y1 = yPixelSized ? shapeOptions.y1 : y2p(shapeOptions.y1); + } + + if(x0 < x1) { + w0 = x0; + optW = 'x0'; + e0 = x1; + optE = 'x1'; + } else { + w0 = x1; + optW = 'x1'; + e0 = x0; + optE = 'x0'; + } + + // For fixed size shapes take opposing direction of y-axis into account. + // Hint: For data sized shapes this is done by the y2p function. + if((!yPixelSized && y0 < y1) || (yPixelSized && y0 > y1)) { + n0 = y0; + optN = 'y0'; + s0 = y1; + optS = 'y1'; + } else { + n0 = y1; + optN = 'y1'; + s0 = y0; + optS = 'y0'; + } + + // setup dragMode and the corresponding handler + updateDragMode(evt); + renderVisualCues(shapeLayer, shapeOptions); + deactivateClipPathTemporarily(shapePath, shapeOptions, gd); + dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape; + dragOptions.altKey = evt.altKey; + } + + function endDrag() { + if(shouldSkipEdits(gd)) return; + + setCursor(shapePath); + removeVisualCues(shapeLayer); + + // Don't rely on clipPath being activated during re-layout + setClipPath(shapePath, gd, shapeOptions); + Registry.call('_guiRelayout', gd, editHelpers.getUpdateObj()); + } + + function abortDrag() { + if(shouldSkipEdits(gd)) return; + + removeVisualCues(shapeLayer); + } + + function moveShape(dx, dy) { + if(shapeOptions.type === 'path') { + var noOp = function(coord) { return coord; }; + var moveX = noOp; + var moveY = noOp; + + if(xPixelSized) { + modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx)); + } else { + moveX = function moveX(x) { return p2x(x2p(x) + dx); }; + if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX); + } + + if(yPixelSized) { + modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy)); + } else { + moveY = function moveY(y) { return p2y(y2p(y) + dy); }; + if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY); + } + + modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY)); + } else { + if(xPixelSized) { + modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx)); + } else { + modifyItem('x0', shapeOptions.x0 = p2x(x0 + dx)); + modifyItem('x1', shapeOptions.x1 = p2x(x1 + dx)); + } + + if(yPixelSized) { + modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy)); + } else { + modifyItem('y0', shapeOptions.y0 = p2y(y0 + dy)); + modifyItem('y1', shapeOptions.y1 = p2y(y1 + dy)); + } + } + + shapePath.attr('d', getPathString(gd, shapeOptions)); + renderVisualCues(shapeLayer, shapeOptions); + } + + function resizeShape(dx, dy) { + if(isPath) { + // TODO: implement path resize, don't forget to update dragMode code + var noOp = function(coord) { return coord; }; + var moveX = noOp; + var moveY = noOp; + + if(xPixelSized) { + modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx)); + } else { + moveX = function moveX(x) { return p2x(x2p(x) + dx); }; + if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX); + } + + if(yPixelSized) { + modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy)); + } else { + moveY = function moveY(y) { return p2y(y2p(y) + dy); }; + if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY); + } + + modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY)); + } else if(isLine) { + if(dragMode === 'resize-over-start-point') { + var newX0 = x0 + dx; + var newY0 = yPixelSized ? y0 - dy : y0 + dy; + modifyItem('x0', shapeOptions.x0 = xPixelSized ? newX0 : p2x(newX0)); + modifyItem('y0', shapeOptions.y0 = yPixelSized ? newY0 : p2y(newY0)); + } else if(dragMode === 'resize-over-end-point') { + var newX1 = x1 + dx; + var newY1 = yPixelSized ? y1 - dy : y1 + dy; + modifyItem('x1', shapeOptions.x1 = xPixelSized ? newX1 : p2x(newX1)); + modifyItem('y1', shapeOptions.y1 = yPixelSized ? newY1 : p2y(newY1)); + } + } else { + var has = function(str) { return dragMode.indexOf(str) !== -1; }; + var hasN = has('n'); + var hasS = has('s'); + var hasW = has('w'); + var hasE = has('e'); + + var newN = hasN ? n0 + dy : n0; + var newS = hasS ? s0 + dy : s0; + var newW = hasW ? w0 + dx : w0; + var newE = hasE ? e0 + dx : e0; + + if(yPixelSized) { + // Do things in opposing direction for y-axis. + // Hint: for data-sized shapes the reversal of axis direction is done in p2y. + if(hasN) newN = n0 - dy; + if(hasS) newS = s0 - dy; + } + + // Update shape eventually. Again, be aware of the + // opposing direction of the y-axis of fixed size shapes. + if( + (!yPixelSized && newS - newN > MINHEIGHT) || + (yPixelSized && newN - newS > MINHEIGHT) + ) { + modifyItem(optN, shapeOptions[optN] = yPixelSized ? newN : p2y(newN)); + modifyItem(optS, shapeOptions[optS] = yPixelSized ? newS : p2y(newS)); + } + if(newE - newW > MINWIDTH) { + modifyItem(optW, shapeOptions[optW] = xPixelSized ? newW : p2x(newW)); + modifyItem(optE, shapeOptions[optE] = xPixelSized ? newE : p2x(newE)); + } + } + + shapePath.attr('d', getPathString(gd, shapeOptions)); + renderVisualCues(shapeLayer, shapeOptions); + } + + function renderVisualCues(shapeLayer, shapeOptions) { + if(xPixelSized || yPixelSized) { + renderAnchor(); + } + + function renderAnchor() { + var isNotPath = shapeOptions.type !== 'path'; + + // d3 join with dummy data to satisfy d3 data-binding + var visualCues = shapeLayer.selectAll('.visual-cue').data([0]); + + // Enter + var strokeWidth = 1; + visualCues.enter() + .append('path') + .attr({ + 'fill': '#fff', + 'fill-rule': 'evenodd', + 'stroke': '#000', + 'stroke-width': strokeWidth + }) + .classed('visual-cue', true); + + // Update + var posX = x2p( + xPixelSized ? + shapeOptions.xanchor : + Lib.midRange( + isNotPath ? + [shapeOptions.x0, shapeOptions.x1] : + helpers.extractPathCoords(shapeOptions.path, constants.paramIsX)) + ); + var posY = y2p( + yPixelSized ? + shapeOptions.yanchor : + Lib.midRange( + isNotPath ? + [shapeOptions.y0, shapeOptions.y1] : + helpers.extractPathCoords(shapeOptions.path, constants.paramIsY)) + ); + + posX = helpers.roundPositionForSharpStrokeRendering(posX, strokeWidth); + posY = helpers.roundPositionForSharpStrokeRendering(posY, strokeWidth); + + if(xPixelSized && yPixelSized) { + var crossPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 1 - strokeWidth) + + 'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z'; + visualCues.attr('d', crossPath); + } else if(xPixelSized) { + var vBarPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 9 - strokeWidth) + + 'v18 h2 v-18 Z'; + visualCues.attr('d', vBarPath); + } else { + var hBarPath = 'M' + (posX - 9 - strokeWidth) + ',' + (posY - 1 - strokeWidth) + + 'h18 v2 h-18 Z'; + visualCues.attr('d', hBarPath); + } + } + } + + function removeVisualCues(shapeLayer) { + shapeLayer.selectAll('.visual-cue').remove(); + } + + function deactivateClipPathTemporarily(shapePath, shapeOptions, gd) { + var xref = shapeOptions.xref; + var yref = shapeOptions.yref; + var xa = Axes.getFromId(gd, xref); + var ya = Axes.getFromId(gd, yref); + + var clipAxes = ''; + if(xref !== 'paper' && !xa.autorange) clipAxes += xref; + if(yref !== 'paper' && !ya.autorange) clipAxes += yref; + + Drawing.setClipUrl( + shapePath, + clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null, + gd + ); + } +} + +function getPathString(gd, options) { + var type = options.type; + var xRefType = Axes.getRefType(options.xref); + var yRefType = Axes.getRefType(options.yref); + var xa = Axes.getFromId(gd, options.xref); + var ya = Axes.getFromId(gd, options.yref); + var gs = gd._fullLayout._size; + var x2r, x2p, y2r, y2p; + var x0, x1, y0, y1; + + if(xa) { + if(xRefType === 'domain') { + x2p = function(v) { return xa._offset + xa._length * v; }; + } else { + x2r = helpers.shapePositionToRange(xa); + x2p = function(v) { return xa._offset + xa.r2p(x2r(v, true)); }; + } + } else { + x2p = function(v) { return gs.l + gs.w * v; }; + } + + if(ya) { + if(yRefType === 'domain') { + y2p = function(v) { return ya._offset + ya._length * (1 - v); }; + } else { + y2r = helpers.shapePositionToRange(ya); + y2p = function(v) { return ya._offset + ya.r2p(y2r(v, true)); }; + } + } else { + y2p = function(v) { return gs.t + gs.h * (1 - v); }; + } + + if(type === 'path') { + if(xa && xa.type === 'date') x2p = helpers.decodeDate(x2p); + if(ya && ya.type === 'date') y2p = helpers.decodeDate(y2p); + return convertPath(options, x2p, y2p); + } + + if(options.xsizemode === 'pixel') { + var xAnchorPos = x2p(options.xanchor); + x0 = xAnchorPos + options.x0; + x1 = xAnchorPos + options.x1; + } else { + x0 = x2p(options.x0); + x1 = x2p(options.x1); + } + + if(options.ysizemode === 'pixel') { + var yAnchorPos = y2p(options.yanchor); + y0 = yAnchorPos - options.y0; + y1 = yAnchorPos - options.y1; + } else { + y0 = y2p(options.y0); + y1 = y2p(options.y1); + } + + if(type === 'line') return 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + y1; + if(type === 'rect') return 'M' + x0 + ',' + y0 + 'H' + x1 + 'V' + y1 + 'H' + x0 + 'Z'; + + // circle + var cx = (x0 + x1) / 2; + var cy = (y0 + y1) / 2; + var rx = Math.abs(cx - x0); + var ry = Math.abs(cy - y0); + var rArc = 'A' + rx + ',' + ry; + var rightPt = (cx + rx) + ',' + cy; + var topPt = cx + ',' + (cy - ry); + return 'M' + rightPt + rArc + ' 0 1,1 ' + topPt + + rArc + ' 0 0,1 ' + rightPt + 'Z'; +} + + +function convertPath(options, x2p, y2p) { + var pathIn = options.path; + var xSizemode = options.xsizemode; + var ySizemode = options.ysizemode; + var xAnchor = options.xanchor; + var yAnchor = options.yanchor; + + return pathIn.replace(constants.segmentRE, function(segment) { + var paramNumber = 0; + var segmentType = segment.charAt(0); + var xParams = constants.paramIsX[segmentType]; + var yParams = constants.paramIsY[segmentType]; + var nParams = constants.numParams[segmentType]; + + var paramString = segment.substr(1).replace(constants.paramRE, function(param) { + if(xParams[paramNumber]) { + if(xSizemode === 'pixel') param = x2p(xAnchor) + Number(param); + else param = x2p(param); + } else if(yParams[paramNumber]) { + if(ySizemode === 'pixel') param = y2p(yAnchor) - Number(param); + else param = y2p(param); + } + paramNumber++; + + if(paramNumber > nParams) param = 'X'; + return param; + }); + + if(paramNumber > nParams) { + paramString = paramString.replace(/[\s,]*X.*/, ''); + Lib.log('Ignoring extra params in segment ' + segment); + } + + return segmentType + paramString; + }); +} + +function movePath(pathIn, moveX, moveY) { + return pathIn.replace(constants.segmentRE, function(segment) { + var paramNumber = 0; + var segmentType = segment.charAt(0); + var xParams = constants.paramIsX[segmentType]; + var yParams = constants.paramIsY[segmentType]; + var nParams = constants.numParams[segmentType]; + + var paramString = segment.substr(1).replace(constants.paramRE, function(param) { + if(paramNumber >= nParams) return param; + + if(xParams[paramNumber]) param = moveX(param); + else if(yParams[paramNumber]) param = moveY(param); + + paramNumber++; + + return param; + }); + + return segmentType + paramString; + }); +} + +function activateShape(gd, path) { + if(!couldHaveActiveShape(gd)) return; + + var element = path.node(); + var id = +element.getAttribute('data-index'); + if(id >= 0) { + // deactivate if already active + if(id === gd._fullLayout._activeShapeIndex) { + deactivateShape(gd); + return; + } + + gd._fullLayout._activeShapeIndex = id; + gd._fullLayout._deactivateShape = deactivateShape; + draw(gd); + } +} + +function deactivateShape(gd) { + if(!couldHaveActiveShape(gd)) return; + + var id = gd._fullLayout._activeShapeIndex; + if(id >= 0) { + clearOutlineControllers(gd); + delete gd._fullLayout._activeShapeIndex; + draw(gd); + } +} + +function eraseActiveShape(gd) { + if(!couldHaveActiveShape(gd)) return; + + clearOutlineControllers(gd); + + var id = gd._fullLayout._activeShapeIndex; + var shapes = (gd.layout || {}).shapes || []; + if(id < shapes.length) { + var newShapes = []; + for(var q = 0; q < shapes.length; q++) { + if(q !== id) { + newShapes.push(shapes[q]); + } + } + + delete gd._fullLayout._activeShapeIndex; + + Registry.call('_guiRelayout', gd, { + shapes: newShapes + }); + } +} + +},{"../../lib":285,"../../lib/setcursor":305,"../../plot_api/plot_template":320,"../../plots/cartesian/axes":331,"../../plots/cartesian/handle_outline":342,"../../registry":373,"../color":155,"../dragelement":174,"../drawing":177,"./constants":237,"./draw_newshape/display_outlines":243,"./draw_newshape/helpers":244,"./helpers":246}],240:[function(_dereq_,module,exports){ +'use strict'; + +var dash = _dereq_('../../drawing/attributes').dash; +var extendFlat = _dereq_('../../../lib/extend').extendFlat; + +module.exports = { + newshape: { + line: { + color: { + valType: 'color', + editType: 'none', + }, + width: { + valType: 'number', + min: 0, + dflt: 4, + editType: 'none', + }, + dash: extendFlat({}, dash, { + dflt: 'solid', + editType: 'none' + }), + editType: 'none' + }, + fillcolor: { + valType: 'color', + dflt: 'rgba(0,0,0,0)', + editType: 'none', + }, + fillrule: { + valType: 'enumerated', + values: ['evenodd', 'nonzero'], + dflt: 'evenodd', + editType: 'none', + }, + opacity: { + valType: 'number', + min: 0, + max: 1, + dflt: 1, + editType: 'none', + }, + layer: { + valType: 'enumerated', + values: ['below', 'above'], + dflt: 'above', + editType: 'none', + }, + drawdirection: { + valType: 'enumerated', + values: ['ortho', 'horizontal', 'vertical', 'diagonal'], + dflt: 'diagonal', + editType: 'none', + }, + + editType: 'none' + }, + + activeshape: { + fillcolor: { + valType: 'color', + dflt: 'rgb(255,0,255)', + editType: 'none', + }, + opacity: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.5, + editType: 'none', + }, + editType: 'none' + } +}; + +},{"../../../lib/extend":279,"../../drawing/attributes":176}],241:[function(_dereq_,module,exports){ +'use strict'; + +var CIRCLE_SIDES = 32; // should be divisible by 4 + +module.exports = { + CIRCLE_SIDES: CIRCLE_SIDES, + i000: 0, + i090: CIRCLE_SIDES / 4, + i180: CIRCLE_SIDES / 2, + i270: CIRCLE_SIDES / 4 * 3, + cos45: Math.cos(Math.PI / 4), + sin45: Math.sin(Math.PI / 4), + SQRT2: Math.sqrt(2) +}; + +},{}],242:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../color'); + + +module.exports = function supplyDrawNewShapeDefaults(layoutIn, layoutOut, coerce) { + coerce('newshape.drawdirection'); + coerce('newshape.layer'); + coerce('newshape.fillcolor'); + coerce('newshape.fillrule'); + coerce('newshape.opacity'); + var newshapeLineWidth = coerce('newshape.line.width'); + if(newshapeLineWidth) { + var bgcolor = (layoutIn || {}).plot_bgcolor || '#FFF'; + coerce('newshape.line.color', Color.contrast(bgcolor)); + coerce('newshape.line.dash'); + } + + coerce('activeshape.fillcolor'); + coerce('activeshape.opacity'); +}; + +},{"../../color":155}],243:[function(_dereq_,module,exports){ +'use strict'; + +var dragElement = _dereq_('../../dragelement'); +var dragHelpers = _dereq_('../../dragelement/helpers'); +var drawMode = dragHelpers.drawMode; + +var Registry = _dereq_('../../../registry'); + +var constants = _dereq_('./constants'); +var i000 = constants.i000; +var i090 = constants.i090; +var i180 = constants.i180; +var i270 = constants.i270; + +var handleOutline = _dereq_('../../../plots/cartesian/handle_outline'); +var clearOutlineControllers = handleOutline.clearOutlineControllers; + +var helpers = _dereq_('./helpers'); +var pointsShapeRectangle = helpers.pointsShapeRectangle; +var pointsShapeEllipse = helpers.pointsShapeEllipse; +var writePaths = helpers.writePaths; +var newShapes = _dereq_('./newshapes'); + +module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) { + if(!nCalls) nCalls = 0; + + var gd = dragOptions.gd; + + function redraw() { + // recursive call + displayOutlines(polygons, outlines, dragOptions, nCalls++); + + if(pointsShapeEllipse(polygons[0])) { + update({redrawing: true}); + } + } + + function update(opts) { + dragOptions.isActiveShape = false; // i.e. to disable controllers + + var updateObject = newShapes(outlines, dragOptions); + if(Object.keys(updateObject).length) { + Registry.call((opts || {}).redrawing ? 'relayout' : '_guiRelayout', gd, updateObject); + } + } + + + var isActiveShape = dragOptions.isActiveShape; + var fullLayout = gd._fullLayout; + var zoomLayer = fullLayout._zoomlayer; + + var dragmode = dragOptions.dragmode; + var isDrawMode = drawMode(dragmode); + + if(isDrawMode) gd._fullLayout._drawing = true; + else if(gd._fullLayout._activeShapeIndex >= 0) clearOutlineControllers(gd); + + // make outline + outlines.attr('d', writePaths(polygons)); + + // add controllers + var vertexDragOptions; + var shapeDragOptions; + var indexI; // cell index + var indexJ; // vertex or cell-controller index + var copyPolygons; + + if(isActiveShape && !nCalls) { + copyPolygons = recordPositions([], polygons); + + var g = zoomLayer.append('g').attr('class', 'outline-controllers'); + addVertexControllers(g); + addShapeControllers(); + } + + function startDragVertex(evt) { + indexI = +evt.srcElement.getAttribute('data-i'); + indexJ = +evt.srcElement.getAttribute('data-j'); + + vertexDragOptions[indexI][indexJ].moveFn = moveVertexController; + } + + function moveVertexController(dx, dy) { + if(!polygons.length) return; + + var x0 = copyPolygons[indexI][indexJ][1]; + var y0 = copyPolygons[indexI][indexJ][2]; + + var cell = polygons[indexI]; + var len = cell.length; + if(pointsShapeRectangle(cell)) { + for(var q = 0; q < len; q++) { + if(q === indexJ) continue; + + // move other corners of rectangle + var pos = cell[q]; + + if(pos[1] === cell[indexJ][1]) { + pos[1] = x0 + dx; + } + + if(pos[2] === cell[indexJ][2]) { + pos[2] = y0 + dy; + } + } + // move the corner + cell[indexJ][1] = x0 + dx; + cell[indexJ][2] = y0 + dy; + + if(!pointsShapeRectangle(cell)) { + // reject result to rectangles with ensure areas + for(var j = 0; j < len; j++) { + for(var k = 0; k < cell[j].length; k++) { + cell[j][k] = copyPolygons[indexI][j][k]; + } + } + } + } else { // other polylines + cell[indexJ][1] = x0 + dx; + cell[indexJ][2] = y0 + dy; + } + + redraw(); + } + + function endDragVertexController() { + update(); + } + + function removeVertex() { + if(!polygons.length) return; + if(!polygons[indexI]) return; + if(!polygons[indexI].length) return; + + var newPolygon = []; + for(var j = 0; j < polygons[indexI].length; j++) { + if(j !== indexJ) { + newPolygon.push( + polygons[indexI][j] + ); + } + } + + if(newPolygon.length > 1 && !( + newPolygon.length === 2 && newPolygon[1][0] === 'Z') + ) { + if(indexJ === 0) { + newPolygon[0][0] = 'M'; + } + + polygons[indexI] = newPolygon; + + redraw(); + update(); + } + } + + function clickVertexController(numClicks, evt) { + if(numClicks === 2) { + indexI = +evt.srcElement.getAttribute('data-i'); + indexJ = +evt.srcElement.getAttribute('data-j'); + + var cell = polygons[indexI]; + if( + !pointsShapeRectangle(cell) && + !pointsShapeEllipse(cell) + ) { + removeVertex(); + } + } + } + + function addVertexControllers(g) { + vertexDragOptions = []; + + for(var i = 0; i < polygons.length; i++) { + var cell = polygons[i]; + + var onRect = pointsShapeRectangle(cell); + var onEllipse = !onRect && pointsShapeEllipse(cell); + + vertexDragOptions[i] = []; + for(var j = 0; j < cell.length; j++) { + if(cell[j][0] === 'Z') continue; + + if(onEllipse && + j !== i000 && + j !== i090 && + j !== i180 && + j !== i270 + ) { + continue; + } + + var x = cell[j][1]; + var y = cell[j][2]; + + var vertex = g.append('circle') + .classed('cursor-grab', true) + .attr('data-i', i) + .attr('data-j', j) + .attr('cx', x) + .attr('cy', y) + .attr('r', 4) + .style({ + 'mix-blend-mode': 'luminosity', + fill: 'black', + stroke: 'white', + 'stroke-width': 1 + }); + + vertexDragOptions[i][j] = { + element: vertex.node(), + gd: gd, + prepFn: startDragVertex, + doneFn: endDragVertexController, + clickFn: clickVertexController + }; + + dragElement.init(vertexDragOptions[i][j]); + } + } + } + + function moveShape(dx, dy) { + if(!polygons.length) return; + + for(var i = 0; i < polygons.length; i++) { + for(var j = 0; j < polygons[i].length; j++) { + for(var k = 0; k + 2 < polygons[i][j].length; k += 2) { + polygons[i][j][k + 1] = copyPolygons[i][j][k + 1] + dx; + polygons[i][j][k + 2] = copyPolygons[i][j][k + 2] + dy; + } + } + } + } + + function moveShapeController(dx, dy) { + moveShape(dx, dy); + + redraw(); + } + + function startDragShapeController(evt) { + indexI = +evt.srcElement.getAttribute('data-i'); + if(!indexI) indexI = 0; // ensure non-existing move button get zero index + + shapeDragOptions[indexI].moveFn = moveShapeController; + } + + function endDragShapeController() { + update(); + } + + function addShapeControllers() { + shapeDragOptions = []; + + if(!polygons.length) return; + + var i = 0; + shapeDragOptions[i] = { + element: outlines[0][0], + gd: gd, + prepFn: startDragShapeController, + doneFn: endDragShapeController + }; + + dragElement.init(shapeDragOptions[i]); + } +}; + +function recordPositions(polygonsOut, polygonsIn) { + for(var i = 0; i < polygonsIn.length; i++) { + var cell = polygonsIn[i]; + polygonsOut[i] = []; + for(var j = 0; j < cell.length; j++) { + polygonsOut[i][j] = []; + for(var k = 0; k < cell[j].length; k++) { + polygonsOut[i][j][k] = cell[j][k]; + } + } + } + return polygonsOut; +} + +},{"../../../plots/cartesian/handle_outline":342,"../../../registry":373,"../../dragelement":174,"../../dragelement/helpers":173,"./constants":241,"./helpers":244,"./newshapes":245}],244:[function(_dereq_,module,exports){ +'use strict'; + +var parseSvgPath = _dereq_('parse-svg-path'); + +var constants = _dereq_('./constants'); +var CIRCLE_SIDES = constants.CIRCLE_SIDES; +var SQRT2 = constants.SQRT2; + +var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers'); +var p2r = cartesianHelpers.p2r; +var r2p = cartesianHelpers.r2p; + +var iC = [0, 3, 4, 5, 6, 1, 2]; +var iQS = [0, 3, 4, 1, 2]; + +exports.writePaths = function(polygons) { + var nI = polygons.length; + if(!nI) return 'M0,0Z'; + + var str = ''; + for(var i = 0; i < nI; i++) { + var nJ = polygons[i].length; + for(var j = 0; j < nJ; j++) { + var w = polygons[i][j][0]; + if(w === 'Z') { + str += 'Z'; + } else { + var nK = polygons[i][j].length; + for(var k = 0; k < nK; k++) { + var realK = k; + if(w === 'Q' || w === 'S') { + realK = iQS[k]; + } else if(w === 'C') { + realK = iC[k]; + } + + str += polygons[i][j][realK]; + if(k > 0 && k < nK - 1) { + str += ','; + } + } + } + } + } + + return str; +}; + +exports.readPaths = function(str, gd, plotinfo, isActiveShape) { + var cmd = parseSvgPath(str); + + var polys = []; + var n = -1; + var newPoly = function() { + n++; + polys[n] = []; + }; + + var k; + var x = 0; + var y = 0; + var initX; + var initY; + var recStart = function() { + initX = x; + initY = y; + }; + + recStart(); + for(var i = 0; i < cmd.length; i++) { + var newPos = []; + + var x1, x2, y1, y2; // i.e. extra params for curves + + var c = cmd[i][0]; + var w = c; + switch(c) { + case 'M': + newPoly(); + x = +cmd[i][1]; + y = +cmd[i][2]; + newPos.push([w, x, y]); + + recStart(); + break; + + case 'Q': + case 'S': + x1 = +cmd[i][1]; + y1 = +cmd[i][2]; + x = +cmd[i][3]; + y = +cmd[i][4]; + newPos.push([w, x, y, x1, y1]); // -> iQS order + break; + + case 'C': + x1 = +cmd[i][1]; + y1 = +cmd[i][2]; + x2 = +cmd[i][3]; + y2 = +cmd[i][4]; + x = +cmd[i][5]; + y = +cmd[i][6]; + newPos.push([w, x, y, x1, y1, x2, y2]); // -> iC order + break; + + case 'T': + case 'L': + x = +cmd[i][1]; + y = +cmd[i][2]; + newPos.push([w, x, y]); + break; + + case 'H': + w = 'L'; // convert to line (for now) + x = +cmd[i][1]; + newPos.push([w, x, y]); + break; + + case 'V': + w = 'L'; // convert to line (for now) + y = +cmd[i][1]; + newPos.push([w, x, y]); + break; + + case 'A': + w = 'L'; // convert to line to handle circle + var rx = +cmd[i][1]; + var ry = +cmd[i][2]; + if(!+cmd[i][4]) { + rx = -rx; + ry = -ry; + } + + var cenX = x - rx; + var cenY = y; + for(k = 1; k <= CIRCLE_SIDES / 2; k++) { + var t = 2 * Math.PI * k / CIRCLE_SIDES; + newPos.push([ + w, + cenX + rx * Math.cos(t), + cenY + ry * Math.sin(t) + ]); + } + break; + + case 'Z': + if(x !== initX || y !== initY) { + x = initX; + y = initY; + newPos.push([w, x, y]); + } + break; + } + + var domain = (plotinfo || {}).domain; + var size = gd._fullLayout._size; + var xPixelSized = plotinfo && plotinfo.xsizemode === 'pixel'; + var yPixelSized = plotinfo && plotinfo.ysizemode === 'pixel'; + var noOffset = isActiveShape === false; + + for(var j = 0; j < newPos.length; j++) { + for(k = 0; k + 2 < 7; k += 2) { + var _x = newPos[j][k + 1]; + var _y = newPos[j][k + 2]; + + if(_x === undefined || _y === undefined) continue; + // keep track of end point for Z + x = _x; + y = _y; + + if(plotinfo) { + if(plotinfo.xaxis && plotinfo.xaxis.p2r) { + if(noOffset) _x -= plotinfo.xaxis._offset; + if(xPixelSized) { + _x = r2p(plotinfo.xaxis, plotinfo.xanchor) + _x; + } else { + _x = p2r(plotinfo.xaxis, _x); + } + } else { + if(noOffset) _x -= size.l; + if(domain) _x = domain.x[0] + _x / size.w; + else _x = _x / size.w; + } + + if(plotinfo.yaxis && plotinfo.yaxis.p2r) { + if(noOffset) _y -= plotinfo.yaxis._offset; + if(yPixelSized) { + _y = r2p(plotinfo.yaxis, plotinfo.yanchor) - _y; + } else { + _y = p2r(plotinfo.yaxis, _y); + } + } else { + if(noOffset) _y -= size.t; + if(domain) _y = domain.y[1] - _y / size.h; + else _y = 1 - _y / size.h; + } + } + + newPos[j][k + 1] = _x; + newPos[j][k + 2] = _y; + } + polys[n].push( + newPos[j].slice() + ); + } + } + + return polys; +}; + +function almostEq(a, b) { + return Math.abs(a - b) <= 1e-6; +} + +function dist(a, b) { + var dx = b[1] - a[1]; + var dy = b[2] - a[2]; + return Math.sqrt( + dx * dx + + dy * dy + ); +} + +exports.pointsShapeRectangle = function(cell) { + var len = cell.length; + if(len !== 5) return false; + + for(var j = 1; j < 3; j++) { + var e01 = cell[0][j] - cell[1][j]; + var e32 = cell[3][j] - cell[2][j]; + + if(!almostEq(e01, e32)) return false; + + var e03 = cell[0][j] - cell[3][j]; + var e12 = cell[1][j] - cell[2][j]; + if(!almostEq(e03, e12)) return false; + } + + // N.B. rotated rectangles are not valid rects since rotation is not supported in shapes for now. + if( + !almostEq(cell[0][1], cell[1][1]) && + !almostEq(cell[0][1], cell[3][1]) + ) return false; + + // reject cases with zero area + return !!( + dist(cell[0], cell[1]) * + dist(cell[0], cell[3]) + ); +}; + +exports.pointsShapeEllipse = function(cell) { + var len = cell.length; + if(len !== CIRCLE_SIDES + 1) return false; + + // opposite diagonals should be the same + len = CIRCLE_SIDES; + for(var i = 0; i < len; i++) { + var k = (len * 2 - i) % len; + + var k2 = (len / 2 + k) % len; + var i2 = (len / 2 + i) % len; + + if(!almostEq( + dist(cell[i], cell[i2]), + dist(cell[k], cell[k2]) + )) return false; + } + return true; +}; + +exports.handleEllipse = function(isEllipse, start, end) { + if(!isEllipse) return [start, end]; // i.e. case of line + + var pos = exports.ellipseOver({ + x0: start[0], + y0: start[1], + x1: end[0], + y1: end[1] + }); + + var cx = (pos.x1 + pos.x0) / 2; + var cy = (pos.y1 + pos.y0) / 2; + var rx = (pos.x1 - pos.x0) / 2; + var ry = (pos.y1 - pos.y0) / 2; + + // make a circle when one dimension is zero + if(!rx) rx = ry = ry / SQRT2; + if(!ry) ry = rx = rx / SQRT2; + + var cell = []; + for(var i = 0; i < CIRCLE_SIDES; i++) { + var t = i * 2 * Math.PI / CIRCLE_SIDES; + cell.push([ + cx + rx * Math.cos(t), + cy + ry * Math.sin(t), + ]); + } + return cell; +}; + +exports.ellipseOver = function(pos) { + var x0 = pos.x0; + var y0 = pos.y0; + var x1 = pos.x1; + var y1 = pos.y1; + + var dx = x1 - x0; + var dy = y1 - y0; + + x0 -= dx; + y0 -= dy; + + var cx = (x0 + x1) / 2; + var cy = (y0 + y1) / 2; + + var scale = SQRT2; + dx *= scale; + dy *= scale; + + return { + x0: cx - dx, + y0: cy - dy, + x1: cx + dx, + y1: cy + dy + }; +}; + +},{"../../../plots/cartesian/helpers":343,"./constants":241,"parse-svg-path":72}],245:[function(_dereq_,module,exports){ +'use strict'; + +var dragHelpers = _dereq_('../../dragelement/helpers'); +var drawMode = dragHelpers.drawMode; +var openMode = dragHelpers.openMode; + +var constants = _dereq_('./constants'); +var i000 = constants.i000; +var i090 = constants.i090; +var i180 = constants.i180; +var i270 = constants.i270; +var cos45 = constants.cos45; +var sin45 = constants.sin45; + +var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers'); +var p2r = cartesianHelpers.p2r; +var r2p = cartesianHelpers.r2p; + +var handleOutline = _dereq_('../../../plots/cartesian/handle_outline'); +var clearSelect = handleOutline.clearSelect; + +var helpers = _dereq_('./helpers'); +var readPaths = helpers.readPaths; +var writePaths = helpers.writePaths; +var ellipseOver = helpers.ellipseOver; + + +module.exports = function newShapes(outlines, dragOptions) { + if(!outlines.length) return; + var e = outlines[0][0]; // pick first + if(!e) return; + var d = e.getAttribute('d'); + + var gd = dragOptions.gd; + var drwStyle = gd._fullLayout.newshape; + + var plotinfo = dragOptions.plotinfo; + var xaxis = plotinfo.xaxis; + var yaxis = plotinfo.yaxis; + var xPaper = !!plotinfo.domain || !plotinfo.xaxis; + var yPaper = !!plotinfo.domain || !plotinfo.yaxis; + + var isActiveShape = dragOptions.isActiveShape; + var dragmode = dragOptions.dragmode; + + var shapes = (gd.layout || {}).shapes || []; + + if(!drawMode(dragmode) && isActiveShape !== undefined) { + var id = gd._fullLayout._activeShapeIndex; + if(id < shapes.length) { + switch(gd._fullLayout.shapes[id].type) { + case 'rect': + dragmode = 'drawrect'; + break; + case 'circle': + dragmode = 'drawcircle'; + break; + case 'line': + dragmode = 'drawline'; + break; + case 'path': + var path = shapes[id].path || ''; + if(path[path.length - 1] === 'Z') { + dragmode = 'drawclosedpath'; + } else { + dragmode = 'drawopenpath'; + } + break; + } + } + } + + var isOpenMode = openMode(dragmode); + + var polygons = readPaths(d, gd, plotinfo, isActiveShape); + + var newShape = { + editable: true, + + xref: xPaper ? 'paper' : xaxis._id, + yref: yPaper ? 'paper' : yaxis._id, + + layer: drwStyle.layer, + opacity: drwStyle.opacity, + line: { + color: drwStyle.line.color, + width: drwStyle.line.width, + dash: drwStyle.line.dash + } + }; + + if(!isOpenMode) { + newShape.fillcolor = drwStyle.fillcolor; + newShape.fillrule = drwStyle.fillrule; + } + + var cell; + // line, rect and circle can be in one cell + // only define cell if there is single cell + if(polygons.length === 1) cell = polygons[0]; + + if( + cell && + dragmode === 'drawrect' + ) { + newShape.type = 'rect'; + newShape.x0 = cell[0][1]; + newShape.y0 = cell[0][2]; + newShape.x1 = cell[2][1]; + newShape.y1 = cell[2][2]; + } else if( + cell && + dragmode === 'drawline' + ) { + newShape.type = 'line'; + newShape.x0 = cell[0][1]; + newShape.y0 = cell[0][2]; + newShape.x1 = cell[1][1]; + newShape.y1 = cell[1][2]; + } else if( + cell && + dragmode === 'drawcircle' + ) { + newShape.type = 'circle'; // an ellipse! + + var xA = cell[i000][1]; + var xB = cell[i090][1]; + var xC = cell[i180][1]; + var xD = cell[i270][1]; + + var yA = cell[i000][2]; + var yB = cell[i090][2]; + var yC = cell[i180][2]; + var yD = cell[i270][2]; + + var xDateOrLog = plotinfo.xaxis && ( + plotinfo.xaxis.type === 'date' || + plotinfo.xaxis.type === 'log' + ); + + var yDateOrLog = plotinfo.yaxis && ( + plotinfo.yaxis.type === 'date' || + plotinfo.yaxis.type === 'log' + ); + + if(xDateOrLog) { + xA = r2p(plotinfo.xaxis, xA); + xB = r2p(plotinfo.xaxis, xB); + xC = r2p(plotinfo.xaxis, xC); + xD = r2p(plotinfo.xaxis, xD); + } + + if(yDateOrLog) { + yA = r2p(plotinfo.yaxis, yA); + yB = r2p(plotinfo.yaxis, yB); + yC = r2p(plotinfo.yaxis, yC); + yD = r2p(plotinfo.yaxis, yD); + } + + var x0 = (xB + xD) / 2; + var y0 = (yA + yC) / 2; + var rx = (xD - xB + xC - xA) / 2; + var ry = (yD - yB + yC - yA) / 2; + var pos = ellipseOver({ + x0: x0, + y0: y0, + x1: x0 + rx * cos45, + y1: y0 + ry * sin45 + }); + + if(xDateOrLog) { + pos.x0 = p2r(plotinfo.xaxis, pos.x0); + pos.x1 = p2r(plotinfo.xaxis, pos.x1); + } + + if(yDateOrLog) { + pos.y0 = p2r(plotinfo.yaxis, pos.y0); + pos.y1 = p2r(plotinfo.yaxis, pos.y1); + } + + newShape.x0 = pos.x0; + newShape.y0 = pos.y0; + newShape.x1 = pos.x1; + newShape.y1 = pos.y1; + } else { + newShape.type = 'path'; + if(xaxis && yaxis) fixDatesForPaths(polygons, xaxis, yaxis); + newShape.path = writePaths(polygons); + cell = null; + } + + clearSelect(gd); + + var editHelpers = dragOptions.editHelpers; + var modifyItem = (editHelpers || {}).modifyItem; + + var allShapes = []; + for(var q = 0; q < shapes.length; q++) { + var beforeEdit = gd._fullLayout.shapes[q]; + allShapes[q] = beforeEdit._input; + + if( + isActiveShape !== undefined && + q === gd._fullLayout._activeShapeIndex + ) { + var afterEdit = newShape; + + switch(beforeEdit.type) { + case 'line': + case 'rect': + case 'circle': + modifyItem('x0', afterEdit.x0); + modifyItem('x1', afterEdit.x1); + modifyItem('y0', afterEdit.y0); + modifyItem('y1', afterEdit.y1); + break; + + case 'path': + modifyItem('path', afterEdit.path); + break; + } + } + } + + if(isActiveShape === undefined) { + allShapes.push(newShape); // add new shape + return allShapes; + } + + return editHelpers ? editHelpers.getUpdateObj() : {}; +}; + +function fixDatesForPaths(polygons, xaxis, yaxis) { + var xIsDate = xaxis.type === 'date'; + var yIsDate = yaxis.type === 'date'; + if(!xIsDate && !yIsDate) return polygons; + + for(var i = 0; i < polygons.length; i++) { + for(var j = 0; j < polygons[i].length; j++) { + for(var k = 0; k + 2 < polygons[i][j].length; k += 2) { + if(xIsDate) polygons[i][j][k + 1] = polygons[i][j][k + 1].replace(' ', '_'); + if(yIsDate) polygons[i][j][k + 2] = polygons[i][j][k + 2].replace(' ', '_'); + } + } + } + + return polygons; +} + +},{"../../../plots/cartesian/handle_outline":342,"../../../plots/cartesian/helpers":343,"../../dragelement/helpers":173,"./constants":241,"./helpers":244}],246:[function(_dereq_,module,exports){ +'use strict'; + +var constants = _dereq_('./constants'); + +var Lib = _dereq_('../../lib'); + +// special position conversion functions... category axis positions can't be +// specified by their data values, because they don't make a continuous mapping. +// so these have to be specified in terms of the category serial numbers, +// but can take fractional values. Other axis types we specify position based on +// the actual data values. +// TODO: in V3.0 (when log axis ranges are in data units) range and shape position +// will be identical, so rangeToShapePosition and shapePositionToRange can be +// removed entirely. + +exports.rangeToShapePosition = function(ax) { + return (ax.type === 'log') ? ax.r2d : function(v) { return v; }; +}; + +exports.shapePositionToRange = function(ax) { + return (ax.type === 'log') ? ax.d2r : function(v) { return v; }; +}; + +exports.decodeDate = function(convertToPx) { + return function(v) { + if(v.replace) v = v.replace('_', ' '); + return convertToPx(v); + }; +}; + +exports.encodeDate = function(convertToDate) { + return function(v) { return convertToDate(v).replace(' ', '_'); }; +}; + +exports.extractPathCoords = function(path, paramsToUse) { + var extractedCoordinates = []; + + var segments = path.match(constants.segmentRE); + segments.forEach(function(segment) { + var relevantParamIdx = paramsToUse[segment.charAt(0)].drawn; + if(relevantParamIdx === undefined) return; + + var params = segment.substr(1).match(constants.paramRE); + if(!params || params.length < relevantParamIdx) return; + + extractedCoordinates.push(Lib.cleanNumber(params[relevantParamIdx])); + }); + + return extractedCoordinates; +}; + +exports.getDataToPixel = function(gd, axis, isVertical, refType) { + var gs = gd._fullLayout._size; + var dataToPixel; + + if(axis) { + if(refType === 'domain') { + dataToPixel = function(v) { + return axis._length * (isVertical ? (1 - v) : v) + axis._offset; + }; + } else { + var d2r = exports.shapePositionToRange(axis); + + dataToPixel = function(v) { + return axis._offset + axis.r2p(d2r(v, true)); + }; + + if(axis.type === 'date') dataToPixel = exports.decodeDate(dataToPixel); + } + } else if(isVertical) { + dataToPixel = function(v) { return gs.t + gs.h * (1 - v); }; + } else { + dataToPixel = function(v) { return gs.l + gs.w * v; }; + } + + return dataToPixel; +}; + +exports.getPixelToData = function(gd, axis, isVertical, opt) { + var gs = gd._fullLayout._size; + var pixelToData; + + if(axis) { + if(opt === 'domain') { + pixelToData = function(p) { + var q = (p - axis._offset) / axis._length; + return isVertical ? 1 - q : q; + }; + } else { + var r2d = exports.rangeToShapePosition(axis); + pixelToData = function(p) { return r2d(axis.p2r(p - axis._offset)); }; + } + } else if(isVertical) { + pixelToData = function(p) { return 1 - (p - gs.t) / gs.h; }; + } else { + pixelToData = function(p) { return (p - gs.l) / gs.w; }; + } + + return pixelToData; +}; + +/** + * Based on the given stroke width, rounds the passed + * position value to represent either a full or half pixel. + * + * In case of an odd stroke width (e.g. 1), this measure ensures + * that a stroke positioned at the returned position isn't rendered + * blurry due to anti-aliasing. + * + * In case of an even stroke width (e.g. 2), this measure ensures + * that the position value is transformed to a full pixel value + * so that anti-aliasing doesn't take effect either. + * + * @param {number} pos The raw position value to be transformed + * @param {number} strokeWidth The stroke width + * @returns {number} either an integer or a .5 decimal number + */ +exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) { + var strokeWidthIsOdd = Math.round(strokeWidth % 2) === 1; + var posValAsInt = Math.round(pos); + + return strokeWidthIsOdd ? posValAsInt + 0.5 : posValAsInt; +}; + +exports.makeOptionsAndPlotinfo = function(gd, index) { + var options = gd._fullLayout.shapes[index] || {}; + + var plotinfo = gd._fullLayout._plots[options.xref + options.yref]; + var hasPlotinfo = !!plotinfo; + if(hasPlotinfo) { + plotinfo._hadPlotinfo = true; + } else { + plotinfo = {}; + if(options.xref && options.xref !== 'paper') plotinfo.xaxis = gd._fullLayout[options.xref + 'axis']; + if(options.yref && options.yref !== 'paper') plotinfo.yaxis = gd._fullLayout[options.yref + 'axis']; + } + + plotinfo.xsizemode = options.xsizemode; + plotinfo.ysizemode = options.ysizemode; + plotinfo.xanchor = options.xanchor; + plotinfo.yanchor = options.yanchor; + + return { + options: options, + plotinfo: plotinfo + }; +}; + +},{"../../lib":285,"./constants":237}],247:[function(_dereq_,module,exports){ +'use strict'; + +var drawModule = _dereq_('./draw'); + +module.exports = { + moduleType: 'component', + name: 'shapes', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + supplyDrawNewShapeDefaults: _dereq_('./draw_newshape/defaults'), + includeBasePlot: _dereq_('../../plots/cartesian/include_components')('shapes'), + + calcAutorange: _dereq_('./calc_autorange'), + draw: drawModule.draw, + drawOne: drawModule.drawOne +}; + +},{"../../plots/cartesian/include_components":344,"./attributes":235,"./calc_autorange":236,"./defaults":238,"./draw":239,"./draw_newshape/defaults":242}],248:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('../../plots/font_attributes'); +var padAttrs = _dereq_('../../plots/pad_attributes'); +var extendDeepAll = _dereq_('../../lib/extend').extendDeepAll; +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; +var animationAttrs = _dereq_('../../plots/animation_attributes'); +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; +var constants = _dereq_('./constants'); + +var stepsAttrs = templatedArray('step', { + visible: { + valType: 'boolean', + dflt: true, + }, + method: { + valType: 'enumerated', + values: ['restyle', 'relayout', 'animate', 'update', 'skip'], + dflt: 'restyle', + }, + args: { + valType: 'info_array', + freeLength: true, + items: [ + { valType: 'any' }, + { valType: 'any' }, + { valType: 'any' } + ], + }, + label: { + valType: 'string', + }, + value: { + valType: 'string', + }, + execute: { + valType: 'boolean', + dflt: true, + } +}); + +module.exports = overrideAll(templatedArray('slider', { + visible: { + valType: 'boolean', + dflt: true, + }, + + active: { + valType: 'number', + min: 0, + dflt: 0, + }, + + steps: stepsAttrs, + + lenmode: { + valType: 'enumerated', + values: ['fraction', 'pixels'], + dflt: 'fraction', + }, + len: { + valType: 'number', + min: 0, + dflt: 1, + }, + x: { + valType: 'number', + min: -2, + max: 3, + dflt: 0, + }, + pad: extendDeepAll(padAttrs({editType: 'arraydraw'}), { + }, {t: {dflt: 20}}), + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'left', + }, + y: { + valType: 'number', + min: -2, + max: 3, + dflt: 0, + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + dflt: 'top', + }, + + transition: { + duration: { + valType: 'number', + min: 0, + dflt: 150, + }, + easing: { + valType: 'enumerated', + values: animationAttrs.transition.easing.values, + dflt: 'cubic-in-out', + } + }, + + currentvalue: { + visible: { + valType: 'boolean', + dflt: true, + }, + + xanchor: { + valType: 'enumerated', + values: ['left', 'center', 'right'], + dflt: 'left', + }, + + offset: { + valType: 'number', + dflt: 10, + }, + + prefix: { + valType: 'string', + }, + + suffix: { + valType: 'string', + }, + + font: fontAttrs({ + }) + }, + + font: fontAttrs({ + }), + + activebgcolor: { + valType: 'color', + dflt: constants.gripBgActiveColor, + }, + bgcolor: { + valType: 'color', + dflt: constants.railBgColor, + }, + bordercolor: { + valType: 'color', + dflt: constants.railBorderColor, + }, + borderwidth: { + valType: 'number', + min: 0, + dflt: constants.railBorderWidth, + }, + ticklen: { + valType: 'number', + min: 0, + dflt: constants.tickLength, + }, + tickcolor: { + valType: 'color', + dflt: constants.tickColor, + }, + tickwidth: { + valType: 'number', + min: 0, + dflt: 1, + }, + minorticklen: { + valType: 'number', + min: 0, + dflt: constants.minorTickLength, + } +}), 'arraydraw', 'from-root'); + +},{"../../lib/extend":279,"../../plot_api/edit_types":313,"../../plot_api/plot_template":320,"../../plots/animation_attributes":325,"../../plots/font_attributes":360,"../../plots/pad_attributes":365,"./constants":249}],249:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + + // layout attribute name + name: 'sliders', + + // class names + containerClassName: 'slider-container', + groupClassName: 'slider-group', + inputAreaClass: 'slider-input-area', + railRectClass: 'slider-rail-rect', + railTouchRectClass: 'slider-rail-touch-rect', + gripRectClass: 'slider-grip-rect', + tickRectClass: 'slider-tick-rect', + inputProxyClass: 'slider-input-proxy', + labelsClass: 'slider-labels', + labelGroupClass: 'slider-label-group', + labelClass: 'slider-label', + currentValueClass: 'slider-current-value', + + railHeight: 5, + + // DOM attribute name in button group keeping track + // of active update menu + menuIndexAttrName: 'slider-active-index', + + // id root pass to Plots.autoMargin + autoMarginIdRoot: 'slider-', + + // min item width / height + minWidth: 30, + minHeight: 30, + + // padding around item text + textPadX: 40, + + // arrow offset off right edge + arrowOffsetX: 4, + + railRadius: 2, + railWidth: 5, + railBorder: 4, + railBorderWidth: 1, + railBorderColor: '#bec8d9', + railBgColor: '#f8fafc', + + // The distance of the rail from the edge of the touchable area + // Slightly less than the step inset because of the curved edges + // of the rail + railInset: 8, + + // The distance from the extremal tick marks to the edge of the + // touchable area. This is basically the same as the grip radius, + // but for other styles it wouldn't really need to be. + stepInset: 10, + + gripRadius: 10, + gripWidth: 20, + gripHeight: 20, + gripBorder: 20, + gripBorderWidth: 1, + gripBorderColor: '#bec8d9', + gripBgColor: '#f6f8fa', + gripBgActiveColor: '#dbdde0', + + labelPadding: 8, + labelOffset: 0, + + tickWidth: 1, + tickColor: '#333', + tickOffset: 25, + tickLength: 7, + + minorTickOffset: 25, + minorTickColor: '#333', + minorTickLength: 4, + + // Extra space below the current value label: + currentValuePadding: 8, + currentValueInset: 0, +}; + +},{}],250:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); + +var attributes = _dereq_('./attributes'); +var constants = _dereq_('./constants'); + +var name = constants.name; +var stepAttrs = attributes.steps; + + +module.exports = function slidersDefaults(layoutIn, layoutOut) { + handleArrayContainerDefaults(layoutIn, layoutOut, { + name: name, + handleItemDefaults: sliderDefaults + }); +}; + +function sliderDefaults(sliderIn, sliderOut, layoutOut) { + function coerce(attr, dflt) { + return Lib.coerce(sliderIn, sliderOut, attributes, attr, dflt); + } + + var steps = handleArrayContainerDefaults(sliderIn, sliderOut, { + name: 'steps', + handleItemDefaults: stepDefaults + }); + + var stepCount = 0; + for(var i = 0; i < steps.length; i++) { + if(steps[i].visible) stepCount++; + } + + var visible; + // If it has fewer than two options, it's not really a slider + if(stepCount < 2) visible = sliderOut.visible = false; + else visible = coerce('visible'); + if(!visible) return; + + sliderOut._stepCount = stepCount; + var visSteps = sliderOut._visibleSteps = Lib.filterVisible(steps); + + var active = coerce('active'); + if(!(steps[active] || {}).visible) sliderOut.active = visSteps[0]._index; + + coerce('x'); + coerce('y'); + Lib.noneOrAll(sliderIn, sliderOut, ['x', 'y']); + + coerce('xanchor'); + coerce('yanchor'); + + coerce('len'); + coerce('lenmode'); + + coerce('pad.t'); + coerce('pad.r'); + coerce('pad.b'); + coerce('pad.l'); + + Lib.coerceFont(coerce, 'font', layoutOut.font); + + var currentValueIsVisible = coerce('currentvalue.visible'); + + if(currentValueIsVisible) { + coerce('currentvalue.xanchor'); + coerce('currentvalue.prefix'); + coerce('currentvalue.suffix'); + coerce('currentvalue.offset'); + + Lib.coerceFont(coerce, 'currentvalue.font', sliderOut.font); + } + + coerce('transition.duration'); + coerce('transition.easing'); + + coerce('bgcolor'); + coerce('activebgcolor'); + coerce('bordercolor'); + coerce('borderwidth'); + coerce('ticklen'); + coerce('tickwidth'); + coerce('tickcolor'); + coerce('minorticklen'); +} + +function stepDefaults(valueIn, valueOut) { + function coerce(attr, dflt) { + return Lib.coerce(valueIn, valueOut, stepAttrs, attr, dflt); + } + + var visible; + if(valueIn.method !== 'skip' && !Array.isArray(valueIn.args)) { + visible = valueOut.visible = false; + } else visible = coerce('visible'); + + if(visible) { + coerce('method'); + coerce('args'); + var label = coerce('label', 'step-' + valueOut._index); + coerce('value', label); + coerce('execute'); + } +} + +},{"../../lib":285,"../../plots/array_container_defaults":326,"./attributes":248,"./constants":249}],251:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Plots = _dereq_('../../plots/plots'); +var Color = _dereq_('../color'); +var Drawing = _dereq_('../drawing'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor; + +var constants = _dereq_('./constants'); +var alignmentConstants = _dereq_('../../constants/alignment'); +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var FROM_TL = alignmentConstants.FROM_TL; +var FROM_BR = alignmentConstants.FROM_BR; + +module.exports = function draw(gd) { + var fullLayout = gd._fullLayout; + var sliderData = makeSliderData(fullLayout, gd); + + // draw a container for *all* sliders: + var sliders = fullLayout._infolayer + .selectAll('g.' + constants.containerClassName) + .data(sliderData.length > 0 ? [0] : []); + + sliders.enter().append('g') + .classed(constants.containerClassName, true) + .style('cursor', 'ew-resize'); + + function clearSlider(sliderOpts) { + if(sliderOpts._commandObserver) { + sliderOpts._commandObserver.remove(); + delete sliderOpts._commandObserver; + } + + // Most components don't need to explicitly remove autoMargin, because + // marginPushers does this - but slider updates don't go through + // a full replot so we need to explicitly remove it. + Plots.autoMargin(gd, autoMarginId(sliderOpts)); + } + + sliders.exit().each(function() { + d3.select(this).selectAll('g.' + constants.groupClassName) + .each(clearSlider); + }) + .remove(); + + // Return early if no menus visible: + if(sliderData.length === 0) return; + + var sliderGroups = sliders.selectAll('g.' + constants.groupClassName) + .data(sliderData, keyFunction); + + sliderGroups.enter().append('g') + .classed(constants.groupClassName, true); + + sliderGroups.exit() + .each(clearSlider) + .remove(); + + // Find the dimensions of the sliders: + for(var i = 0; i < sliderData.length; i++) { + var sliderOpts = sliderData[i]; + findDimensions(gd, sliderOpts); + } + + sliderGroups.each(function(sliderOpts) { + var gSlider = d3.select(this); + + computeLabelSteps(sliderOpts); + + Plots.manageCommandObserver(gd, sliderOpts, sliderOpts._visibleSteps, function(data) { + // NB: Same as below. This is *not* always the same as sliderOpts since + // if a new set of steps comes in, the reference in this callback would + // be invalid. We need to refetch it from the slider group, which is + // the join data that creates this slider. So if this slider still exists, + // the group should be valid, *to the best of my knowledge.* If not, + // we'd have to look it up by d3 data join index/key. + var opts = gSlider.data()[0]; + + if(opts.active === data.index) return; + if(opts._dragging) return; + + setActive(gd, gSlider, opts, data.index, false, true); + }); + + drawSlider(gd, d3.select(this), sliderOpts); + }); +}; + +function autoMarginId(sliderOpts) { + return constants.autoMarginIdRoot + sliderOpts._index; +} + +// This really only just filters by visibility: +function makeSliderData(fullLayout, gd) { + var contOpts = fullLayout[constants.name]; + var sliderData = []; + + for(var i = 0; i < contOpts.length; i++) { + var item = contOpts[i]; + if(!item.visible) continue; + item._gd = gd; + sliderData.push(item); + } + + return sliderData; +} + +// This is set in the defaults step: +function keyFunction(opts) { + return opts._index; +} + +// Compute the dimensions (mutates sliderOpts): +function findDimensions(gd, sliderOpts) { + var sliderLabels = Drawing.tester.selectAll('g.' + constants.labelGroupClass) + .data(sliderOpts._visibleSteps); + + sliderLabels.enter().append('g') + .classed(constants.labelGroupClass, true); + + // loop over fake buttons to find width / height + var maxLabelWidth = 0; + var labelHeight = 0; + sliderLabels.each(function(stepOpts) { + var labelGroup = d3.select(this); + + var text = drawLabel(labelGroup, {step: stepOpts}, sliderOpts); + + var textNode = text.node(); + if(textNode) { + var bBox = Drawing.bBox(textNode); + labelHeight = Math.max(labelHeight, bBox.height); + maxLabelWidth = Math.max(maxLabelWidth, bBox.width); + } + }); + + sliderLabels.remove(); + + var dims = sliderOpts._dims = {}; + + dims.inputAreaWidth = Math.max( + constants.railWidth, + constants.gripHeight + ); + + // calculate some overall dimensions - some of these are needed for + // calculating the currentValue dimensions + var graphSize = gd._fullLayout._size; + dims.lx = graphSize.l + graphSize.w * sliderOpts.x; + dims.ly = graphSize.t + graphSize.h * (1 - sliderOpts.y); + + if(sliderOpts.lenmode === 'fraction') { + // fraction: + dims.outerLength = Math.round(graphSize.w * sliderOpts.len); + } else { + // pixels: + dims.outerLength = sliderOpts.len; + } + + // The length of the rail, *excluding* padding on either end: + dims.inputAreaStart = 0; + dims.inputAreaLength = Math.round(dims.outerLength - sliderOpts.pad.l - sliderOpts.pad.r); + + var textableInputLength = dims.inputAreaLength - 2 * constants.stepInset; + var availableSpacePerLabel = textableInputLength / (sliderOpts._stepCount - 1); + var computedSpacePerLabel = maxLabelWidth + constants.labelPadding; + dims.labelStride = Math.max(1, Math.ceil(computedSpacePerLabel / availableSpacePerLabel)); + dims.labelHeight = labelHeight; + + // loop over all possible values for currentValue to find the + // area we need for it + dims.currentValueMaxWidth = 0; + dims.currentValueHeight = 0; + dims.currentValueTotalHeight = 0; + dims.currentValueMaxLines = 1; + + if(sliderOpts.currentvalue.visible) { + // Get the dimensions of the current value label: + var dummyGroup = Drawing.tester.append('g'); + + sliderLabels.each(function(stepOpts) { + var curValPrefix = drawCurrentValue(dummyGroup, sliderOpts, stepOpts.label); + var curValSize = (curValPrefix.node() && Drawing.bBox(curValPrefix.node())) || {width: 0, height: 0}; + var lines = svgTextUtils.lineCount(curValPrefix); + dims.currentValueMaxWidth = Math.max(dims.currentValueMaxWidth, Math.ceil(curValSize.width)); + dims.currentValueHeight = Math.max(dims.currentValueHeight, Math.ceil(curValSize.height)); + dims.currentValueMaxLines = Math.max(dims.currentValueMaxLines, lines); + }); + + dims.currentValueTotalHeight = dims.currentValueHeight + sliderOpts.currentvalue.offset; + + dummyGroup.remove(); + } + + dims.height = dims.currentValueTotalHeight + constants.tickOffset + sliderOpts.ticklen + constants.labelOffset + dims.labelHeight + sliderOpts.pad.t + sliderOpts.pad.b; + + var xanchor = 'left'; + if(Lib.isRightAnchor(sliderOpts)) { + dims.lx -= dims.outerLength; + xanchor = 'right'; + } + if(Lib.isCenterAnchor(sliderOpts)) { + dims.lx -= dims.outerLength / 2; + xanchor = 'center'; + } + + var yanchor = 'top'; + if(Lib.isBottomAnchor(sliderOpts)) { + dims.ly -= dims.height; + yanchor = 'bottom'; + } + if(Lib.isMiddleAnchor(sliderOpts)) { + dims.ly -= dims.height / 2; + yanchor = 'middle'; + } + + dims.outerLength = Math.ceil(dims.outerLength); + dims.height = Math.ceil(dims.height); + dims.lx = Math.round(dims.lx); + dims.ly = Math.round(dims.ly); + + var marginOpts = { + y: sliderOpts.y, + b: dims.height * FROM_BR[yanchor], + t: dims.height * FROM_TL[yanchor] + }; + + if(sliderOpts.lenmode === 'fraction') { + marginOpts.l = 0; + marginOpts.xl = sliderOpts.x - sliderOpts.len * FROM_TL[xanchor]; + marginOpts.r = 0; + marginOpts.xr = sliderOpts.x + sliderOpts.len * FROM_BR[xanchor]; + } else { + marginOpts.x = sliderOpts.x; + marginOpts.l = dims.outerLength * FROM_TL[xanchor]; + marginOpts.r = dims.outerLength * FROM_BR[xanchor]; + } + + Plots.autoMargin(gd, autoMarginId(sliderOpts), marginOpts); +} + +function drawSlider(gd, sliderGroup, sliderOpts) { + // This is related to the other long notes in this file regarding what happens + // when slider steps disappear. This particular fix handles what happens when + // the *current* slider step is removed. The drawing functions will error out + // when they fail to find it, so the fix for now is that it will just draw the + // slider in the first position but will not execute the command. + if(!((sliderOpts.steps[sliderOpts.active] || {}).visible)) { + sliderOpts.active = sliderOpts._visibleSteps[0]._index; + } + + // These are carefully ordered for proper z-ordering: + sliderGroup + .call(drawCurrentValue, sliderOpts) + .call(drawRail, sliderOpts) + .call(drawLabelGroup, sliderOpts) + .call(drawTicks, sliderOpts) + .call(drawTouchRect, gd, sliderOpts) + .call(drawGrip, gd, sliderOpts); + + var dims = sliderOpts._dims; + + // Position the rectangle: + Drawing.setTranslate(sliderGroup, dims.lx + sliderOpts.pad.l, dims.ly + sliderOpts.pad.t); + + sliderGroup.call(setGripPosition, sliderOpts, false); + sliderGroup.call(drawCurrentValue, sliderOpts); +} + +function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) { + if(!sliderOpts.currentvalue.visible) return; + + var dims = sliderOpts._dims; + var x0, textAnchor; + + switch(sliderOpts.currentvalue.xanchor) { + case 'right': + // This is anchored left and adjusted by the width of the longest label + // so that the prefix doesn't move. The goal of this is to emphasize + // what's actually changing and make the update less distracting. + x0 = dims.inputAreaLength - constants.currentValueInset - dims.currentValueMaxWidth; + textAnchor = 'left'; + break; + case 'center': + x0 = dims.inputAreaLength * 0.5; + textAnchor = 'middle'; + break; + default: + x0 = constants.currentValueInset; + textAnchor = 'left'; + } + + var text = Lib.ensureSingle(sliderGroup, 'text', constants.labelClass, function(s) { + s.attr({ + 'text-anchor': textAnchor, + 'data-notex': 1 + }); + }); + + var str = sliderOpts.currentvalue.prefix ? sliderOpts.currentvalue.prefix : ''; + + if(typeof valueOverride === 'string') { + str += valueOverride; + } else { + var curVal = sliderOpts.steps[sliderOpts.active].label; + var _meta = sliderOpts._gd._fullLayout._meta; + if(_meta) curVal = Lib.templateString(curVal, _meta); + str += curVal; + } + + if(sliderOpts.currentvalue.suffix) { + str += sliderOpts.currentvalue.suffix; + } + + text.call(Drawing.font, sliderOpts.currentvalue.font) + .text(str) + .call(svgTextUtils.convertToTspans, sliderOpts._gd); + + var lines = svgTextUtils.lineCount(text); + + var y0 = (dims.currentValueMaxLines + 1 - lines) * + sliderOpts.currentvalue.font.size * LINE_SPACING; + + svgTextUtils.positionText(text, x0, y0); + + return text; +} + +function drawGrip(sliderGroup, gd, sliderOpts) { + var grip = Lib.ensureSingle(sliderGroup, 'rect', constants.gripRectClass, function(s) { + s.call(attachGripEvents, gd, sliderGroup, sliderOpts) + .style('pointer-events', 'all'); + }); + + grip.attr({ + width: constants.gripWidth, + height: constants.gripHeight, + rx: constants.gripRadius, + ry: constants.gripRadius, + }) + .call(Color.stroke, sliderOpts.bordercolor) + .call(Color.fill, sliderOpts.bgcolor) + .style('stroke-width', sliderOpts.borderwidth + 'px'); +} + +function drawLabel(item, data, sliderOpts) { + var text = Lib.ensureSingle(item, 'text', constants.labelClass, function(s) { + s.attr({ + 'text-anchor': 'middle', + 'data-notex': 1 + }); + }); + + var tx = data.step.label; + var _meta = sliderOpts._gd._fullLayout._meta; + if(_meta) tx = Lib.templateString(tx, _meta); + + text.call(Drawing.font, sliderOpts.font) + .text(tx) + .call(svgTextUtils.convertToTspans, sliderOpts._gd); + + return text; +} + +function drawLabelGroup(sliderGroup, sliderOpts) { + var labels = Lib.ensureSingle(sliderGroup, 'g', constants.labelsClass); + var dims = sliderOpts._dims; + + var labelItems = labels.selectAll('g.' + constants.labelGroupClass) + .data(dims.labelSteps); + + labelItems.enter().append('g') + .classed(constants.labelGroupClass, true); + + labelItems.exit().remove(); + + labelItems.each(function(d) { + var item = d3.select(this); + + item.call(drawLabel, d, sliderOpts); + + Drawing.setTranslate(item, + normalizedValueToPosition(sliderOpts, d.fraction), + constants.tickOffset + + sliderOpts.ticklen + + // position is the baseline of the top line of text only, even + // if the label spans multiple lines + sliderOpts.font.size * LINE_SPACING + + constants.labelOffset + + dims.currentValueTotalHeight + ); + }); +} + +function handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, doTransition) { + var quantizedPosition = Math.round(normalizedPosition * (sliderOpts._stepCount - 1)); + var quantizedIndex = sliderOpts._visibleSteps[quantizedPosition]._index; + + if(quantizedIndex !== sliderOpts.active) { + setActive(gd, sliderGroup, sliderOpts, quantizedIndex, true, doTransition); + } +} + +function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition) { + var previousActive = sliderOpts.active; + sliderOpts.active = index; + + // due to templating, it's possible this slider doesn't even exist yet + arrayEditor(gd.layout, constants.name, sliderOpts) + .applyUpdate('active', index); + + var step = sliderOpts.steps[sliderOpts.active]; + + sliderGroup.call(setGripPosition, sliderOpts, doTransition); + sliderGroup.call(drawCurrentValue, sliderOpts); + + gd.emit('plotly_sliderchange', { + slider: sliderOpts, + step: sliderOpts.steps[sliderOpts.active], + interaction: doCallback, + previousActive: previousActive + }); + + if(step && step.method && doCallback) { + if(sliderGroup._nextMethod) { + // If we've already queued up an update, just overwrite it with the most recent: + sliderGroup._nextMethod.step = step; + sliderGroup._nextMethod.doCallback = doCallback; + sliderGroup._nextMethod.doTransition = doTransition; + } else { + sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition}; + sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() { + var _step = sliderGroup._nextMethod.step; + if(!_step.method) return; + + if(_step.execute) { + Plots.executeAPICommand(gd, _step.method, _step.args); + } + + sliderGroup._nextMethod = null; + sliderGroup._nextMethodRaf = null; + }); + } + } +} + +function attachGripEvents(item, gd, sliderGroup) { + var node = sliderGroup.node(); + var $gd = d3.select(gd); + + // NB: This is *not* the same as sliderOpts itself! These callbacks + // are in a closure so this array won't actually be correct if the + // steps have changed since this was initialized. The sliderGroup, + // however, has not changed since that *is* the slider, so it must + // be present to receive mouse events. + function getSliderOpts() { + return sliderGroup.data()[0]; + } + + item.on('mousedown', function() { + var sliderOpts = getSliderOpts(); + gd.emit('plotly_sliderstart', {slider: sliderOpts}); + + var grip = sliderGroup.select('.' + constants.gripRectClass); + + d3.event.stopPropagation(); + d3.event.preventDefault(); + grip.call(Color.fill, sliderOpts.activebgcolor); + + var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]); + handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true); + sliderOpts._dragging = true; + + $gd.on('mousemove', function() { + var sliderOpts = getSliderOpts(); + var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]); + handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false); + }); + + $gd.on('mouseup', function() { + var sliderOpts = getSliderOpts(); + sliderOpts._dragging = false; + grip.call(Color.fill, sliderOpts.bgcolor); + $gd.on('mouseup', null); + $gd.on('mousemove', null); + + gd.emit('plotly_sliderend', { + slider: sliderOpts, + step: sliderOpts.steps[sliderOpts.active] + }); + }); + }); +} + +function drawTicks(sliderGroup, sliderOpts) { + var tick = sliderGroup.selectAll('rect.' + constants.tickRectClass) + .data(sliderOpts._visibleSteps); + var dims = sliderOpts._dims; + + tick.enter().append('rect') + .classed(constants.tickRectClass, true); + + tick.exit().remove(); + + tick.attr({ + width: sliderOpts.tickwidth + 'px', + 'shape-rendering': 'crispEdges' + }); + + tick.each(function(d, i) { + var isMajor = i % dims.labelStride === 0; + var item = d3.select(this); + + item + .attr({height: isMajor ? sliderOpts.ticklen : sliderOpts.minorticklen}) + .call(Color.fill, isMajor ? sliderOpts.tickcolor : sliderOpts.tickcolor); + + Drawing.setTranslate(item, + normalizedValueToPosition(sliderOpts, i / (sliderOpts._stepCount - 1)) - 0.5 * sliderOpts.tickwidth, + (isMajor ? constants.tickOffset : constants.minorTickOffset) + dims.currentValueTotalHeight + ); + }); +} + +function computeLabelSteps(sliderOpts) { + var dims = sliderOpts._dims; + dims.labelSteps = []; + var nsteps = sliderOpts._stepCount; + + for(var i = 0; i < nsteps; i += dims.labelStride) { + dims.labelSteps.push({ + fraction: i / (nsteps - 1), + step: sliderOpts._visibleSteps[i] + }); + } +} + +function setGripPosition(sliderGroup, sliderOpts, doTransition) { + var grip = sliderGroup.select('rect.' + constants.gripRectClass); + + var quantizedIndex = 0; + for(var i = 0; i < sliderOpts._stepCount; i++) { + if(sliderOpts._visibleSteps[i]._index === sliderOpts.active) { + quantizedIndex = i; + break; + } + } + + var x = normalizedValueToPosition(sliderOpts, quantizedIndex / (sliderOpts._stepCount - 1)); + + // If this is true, then *this component* is already invoking its own command + // and has triggered its own animation. + if(sliderOpts._invokingCommand) return; + + var el = grip; + if(doTransition && sliderOpts.transition.duration > 0) { + el = el.transition() + .duration(sliderOpts.transition.duration) + .ease(sliderOpts.transition.easing); + } + + // Drawing.setTranslate doesn't work here because of the transition duck-typing. + // It's also not necessary because there are no other transitions to preserve. + el.attr('transform', strTranslate(x - constants.gripWidth * 0.5, sliderOpts._dims.currentValueTotalHeight)); +} + +// Convert a number from [0-1] to a pixel position relative to the slider group container: +function normalizedValueToPosition(sliderOpts, normalizedPosition) { + var dims = sliderOpts._dims; + return dims.inputAreaStart + constants.stepInset + + (dims.inputAreaLength - 2 * constants.stepInset) * Math.min(1, Math.max(0, normalizedPosition)); +} + +// Convert a position relative to the slider group to a nubmer in [0, 1] +function positionToNormalizedValue(sliderOpts, position) { + var dims = sliderOpts._dims; + return Math.min(1, Math.max(0, (position - constants.stepInset - dims.inputAreaStart) / (dims.inputAreaLength - 2 * constants.stepInset - 2 * dims.inputAreaStart))); +} + +function drawTouchRect(sliderGroup, gd, sliderOpts) { + var dims = sliderOpts._dims; + var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railTouchRectClass, function(s) { + s.call(attachGripEvents, gd, sliderGroup, sliderOpts) + .style('pointer-events', 'all'); + }); + + rect.attr({ + width: dims.inputAreaLength, + height: Math.max(dims.inputAreaWidth, constants.tickOffset + sliderOpts.ticklen + dims.labelHeight) + }) + .call(Color.fill, sliderOpts.bgcolor) + .attr('opacity', 0); + + Drawing.setTranslate(rect, 0, dims.currentValueTotalHeight); +} + +function drawRail(sliderGroup, sliderOpts) { + var dims = sliderOpts._dims; + var computedLength = dims.inputAreaLength - constants.railInset * 2; + var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railRectClass); + + rect.attr({ + width: computedLength, + height: constants.railWidth, + rx: constants.railRadius, + ry: constants.railRadius, + 'shape-rendering': 'crispEdges' + }) + .call(Color.stroke, sliderOpts.bordercolor) + .call(Color.fill, sliderOpts.bgcolor) + .style('stroke-width', sliderOpts.borderwidth + 'px'); + + Drawing.setTranslate(rect, + constants.railInset, + (dims.inputAreaWidth - constants.railWidth) * 0.5 + dims.currentValueTotalHeight + ); +} + +},{"../../constants/alignment":260,"../../lib":285,"../../lib/svg_text_utils":307,"../../plot_api/plot_template":320,"../../plots/plots":366,"../color":155,"../drawing":177,"./constants":249,"@plotly/d3":20}],252:[function(_dereq_,module,exports){ +'use strict'; + +var constants = _dereq_('./constants'); + +module.exports = { + moduleType: 'component', + name: constants.name, + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw') +}; + +},{"./attributes":248,"./constants":249,"./defaults":250,"./draw":251}],253:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Plots = _dereq_('../../plots/plots'); +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var interactConstants = _dereq_('../../constants/interactions'); + +var OPPOSITE_SIDE = _dereq_('../../constants/alignment').OPPOSITE_SIDE; +var numStripRE = / [XY][0-9]* /; + +/** + * Titles - (re)draw titles on the axes and plot: + * @param {DOM element} gd - the graphDiv + * @param {string} titleClass - the css class of this title + * @param {object} options - how and what to draw + * propContainer - the layout object containing `title` and `titlefont` + * attributes that apply to this title + * propName - the full name of the title property (for Plotly.relayout) + * [traceIndex] - include only if this property applies to one trace + * (such as a colorbar title) - then editing pipes to Plotly.restyle + * instead of Plotly.relayout + * placeholder - placeholder text for an empty editable title + * [avoid] {object} - include if this title should move to avoid other elements + * selection - d3 selection of elements to avoid + * side - which direction to move if there is a conflict + * [offsetLeft] - if these elements are subject to a translation + * wrt the title element + * [offsetTop] + * attributes {object} - position and alignment attributes + * x - pixels + * y - pixels + * text-anchor - start|middle|end + * transform {object} - how to transform the title after positioning + * rotate - degrees + * offset - shift up/down in the rotated frame (unused?) + * containerGroup - if an svg element already exists to hold this + * title, include here. Otherwise it will go in fullLayout._infolayer + * _meta {object (optional} - meta key-value to for title with + * Lib.templateString, default to fullLayout._meta, if not provided + * + * @return {selection} d3 selection of title container group + */ +function draw(gd, titleClass, options) { + var cont = options.propContainer; + var prop = options.propName; + var placeholder = options.placeholder; + var traceIndex = options.traceIndex; + var avoid = options.avoid || {}; + var attributes = options.attributes; + var transform = options.transform; + var group = options.containerGroup; + + var fullLayout = gd._fullLayout; + + var opacity = 1; + var isplaceholder = false; + var title = cont.title; + var txt = (title && title.text ? title.text : '').trim(); + + var font = title && title.font ? title.font : {}; + var fontFamily = font.family; + var fontSize = font.size; + var fontColor = font.color; + + // only make this title editable if we positively identify its property + // as one that has editing enabled. + var editAttr; + if(prop === 'title.text') editAttr = 'titleText'; + else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText'; + else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText'; + var editable = gd._context.edits[editAttr]; + + if(txt === '') opacity = 0; + // look for placeholder text while stripping out numbers from eg X2, Y3 + // this is just for backward compatibility with the old version that had + // "Click to enter X2 title" and may have gotten saved in some old plots, + // we don't want this to show up when these are displayed. + else if(txt.replace(numStripRE, ' % ') === placeholder.replace(numStripRE, ' % ')) { + opacity = 0.2; + isplaceholder = true; + if(!editable) txt = ''; + } + + if(options._meta) { + txt = Lib.templateString(txt, options._meta); + } else if(fullLayout._meta) { + txt = Lib.templateString(txt, fullLayout._meta); + } + + var elShouldExist = txt || editable; + + if(!group) { + group = Lib.ensureSingle(fullLayout._infolayer, 'g', 'g-' + titleClass); + } + + var el = group.selectAll('text') + .data(elShouldExist ? [0] : []); + el.enter().append('text'); + el.text(txt) + // this is hacky, but convertToTspans uses the class + // to determine whether to rotate mathJax... + // so we need to clear out any old class and put the + // correct one (only relevant for colorbars, at least + // for now) - ie don't use .classed + .attr('class', titleClass); + el.exit().remove(); + + if(!elShouldExist) return group; + + function titleLayout(titleEl) { + Lib.syncOrAsync([drawTitle, scootTitle], titleEl); + } + + function drawTitle(titleEl) { + var transformVal; + + if(transform) { + transformVal = ''; + if(transform.rotate) { + transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')'; + } + if(transform.offset) { + transformVal += strTranslate(0, transform.offset); + } + } else { + transformVal = null; + } + + titleEl.attr('transform', transformVal); + + titleEl.style({ + 'font-family': fontFamily, + 'font-size': d3.round(fontSize, 2) + 'px', + fill: Color.rgb(fontColor), + opacity: opacity * Color.opacity(fontColor), + 'font-weight': Plots.fontWeight + }) + .attr(attributes) + .call(svgTextUtils.convertToTspans, gd); + + return Plots.previousPromises(gd); + } + + function scootTitle(titleElIn) { + var titleGroup = d3.select(titleElIn.node().parentNode); + + if(avoid && avoid.selection && avoid.side && txt) { + titleGroup.attr('transform', null); + + // move toward avoid.side (= left, right, top, bottom) if needed + // can include pad (pixels, default 2) + var backside = OPPOSITE_SIDE[avoid.side]; + var shiftSign = (avoid.side === 'left' || avoid.side === 'top') ? -1 : 1; + var pad = isNumeric(avoid.pad) ? avoid.pad : 2; + + var titlebb = Drawing.bBox(titleGroup.node()); + var paperbb = { + left: 0, + top: 0, + right: fullLayout.width, + bottom: fullLayout.height + }; + + var maxshift = avoid.maxShift || + shiftSign * (paperbb[avoid.side] - titlebb[avoid.side]); + var shift = 0; + + // Prevent the title going off the paper + if(maxshift < 0) { + shift = maxshift; + } else { + // so we don't have to offset each avoided element, + // give the title the opposite offset + var offsetLeft = avoid.offsetLeft || 0; + var offsetTop = avoid.offsetTop || 0; + titlebb.left -= offsetLeft; + titlebb.right -= offsetLeft; + titlebb.top -= offsetTop; + titlebb.bottom -= offsetTop; + + // iterate over a set of elements (avoid.selection) + // to avoid collisions with + avoid.selection.each(function() { + var avoidbb = Drawing.bBox(this); + + if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) { + shift = Math.max(shift, shiftSign * ( + avoidbb[avoid.side] - titlebb[backside]) + pad); + } + }); + shift = Math.min(maxshift, shift); + } + + if(shift > 0 || maxshift < 0) { + var shiftTemplate = { + left: [-shift, 0], + right: [shift, 0], + top: [0, -shift], + bottom: [0, shift] + }[avoid.side]; + titleGroup.attr('transform', strTranslate(shiftTemplate[0], shiftTemplate[1])); + } + } + } + + el.call(titleLayout); + + function setPlaceholder() { + opacity = 0; + isplaceholder = true; + el.text(placeholder) + .on('mouseover.opacity', function() { + d3.select(this).transition() + .duration(interactConstants.SHOW_PLACEHOLDER).style('opacity', 1); + }) + .on('mouseout.opacity', function() { + d3.select(this).transition() + .duration(interactConstants.HIDE_PLACEHOLDER).style('opacity', 0); + }); + } + + if(editable) { + if(!txt) setPlaceholder(); + else el.on('.opacity', null); + + el.call(svgTextUtils.makeEditable, {gd: gd}) + .on('edit', function(text) { + if(traceIndex !== undefined) { + Registry.call('_guiRestyle', gd, prop, text, traceIndex); + } else { + Registry.call('_guiRelayout', gd, prop, text); + } + }) + .on('cancel', function() { + this.text(this.attr('data-unformatted')) + .call(titleLayout); + }) + .on('input', function(d) { + this.text(d || ' ') + .call(svgTextUtils.positionText, attributes.x, attributes.y); + }); + } + el.classed('js-placeholder', isplaceholder); + + return group; +} + +module.exports = { + draw: draw +}; + +},{"../../constants/alignment":260,"../../constants/interactions":264,"../../lib":285,"../../lib/svg_text_utils":307,"../../plots/plots":366,"../../registry":373,"../color":155,"../drawing":177,"@plotly/d3":20,"fast-isnumeric":31}],254:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('../../plots/font_attributes'); +var colorAttrs = _dereq_('../color/attributes'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; +var padAttrs = _dereq_('../../plots/pad_attributes'); +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; + +var buttonsAttrs = templatedArray('button', { + visible: { + valType: 'boolean', + }, + method: { + valType: 'enumerated', + values: ['restyle', 'relayout', 'animate', 'update', 'skip'], + dflt: 'restyle', + }, + args: { + valType: 'info_array', + freeLength: true, + items: [ + {valType: 'any'}, + {valType: 'any'}, + {valType: 'any'} + ], + }, + args2: { + valType: 'info_array', + freeLength: true, + items: [ + {valType: 'any'}, + {valType: 'any'}, + {valType: 'any'} + ], + }, + label: { + valType: 'string', + dflt: '', + }, + execute: { + valType: 'boolean', + dflt: true, + } +}); + +module.exports = overrideAll(templatedArray('updatemenu', { + _arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/], + + visible: { + valType: 'boolean', + }, + + type: { + valType: 'enumerated', + values: ['dropdown', 'buttons'], + dflt: 'dropdown', + }, + + direction: { + valType: 'enumerated', + values: ['left', 'right', 'up', 'down'], + dflt: 'down', + }, + + active: { + valType: 'integer', + min: -1, + dflt: 0, + }, + + showactive: { + valType: 'boolean', + dflt: true, + }, + + buttons: buttonsAttrs, + + x: { + valType: 'number', + min: -2, + max: 3, + dflt: -0.05, + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'right', + }, + y: { + valType: 'number', + min: -2, + max: 3, + dflt: 1, + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + dflt: 'top', + }, + + pad: extendFlat(padAttrs({editType: 'arraydraw'}), { + }), + + font: fontAttrs({ + }), + + bgcolor: { + valType: 'color', + }, + bordercolor: { + valType: 'color', + dflt: colorAttrs.borderLine, + }, + borderwidth: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'arraydraw', + } +}), 'arraydraw', 'from-root'); + +},{"../../lib/extend":279,"../../plot_api/edit_types":313,"../../plot_api/plot_template":320,"../../plots/font_attributes":360,"../../plots/pad_attributes":365,"../color/attributes":154}],255:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + + // layout attribute name + name: 'updatemenus', + + // class names + containerClassName: 'updatemenu-container', + headerGroupClassName: 'updatemenu-header-group', + headerClassName: 'updatemenu-header', + headerArrowClassName: 'updatemenu-header-arrow', + dropdownButtonGroupClassName: 'updatemenu-dropdown-button-group', + dropdownButtonClassName: 'updatemenu-dropdown-button', + buttonClassName: 'updatemenu-button', + itemRectClassName: 'updatemenu-item-rect', + itemTextClassName: 'updatemenu-item-text', + + // DOM attribute name in button group keeping track + // of active update menu + menuIndexAttrName: 'updatemenu-active-index', + + // id root pass to Plots.autoMargin + autoMarginIdRoot: 'updatemenu-', + + // options when 'active: -1' + blankHeaderOpts: { label: ' ' }, + + // min item width / height + minWidth: 30, + minHeight: 30, + + // padding around item text + textPadX: 24, + arrowPadX: 16, + + // item rect radii + rx: 2, + ry: 2, + + // item text x offset off left edge + textOffsetX: 12, + + // item text y offset (w.r.t. middle) + textOffsetY: 3, + + // arrow offset off right edge + arrowOffsetX: 4, + + // gap between header and buttons + gapButtonHeader: 5, + + // gap between between buttons + gapButton: 2, + + // color given to active buttons + activeColor: '#F4FAFF', + + // color given to hovered buttons + hoverColor: '#F4FAFF', + + // symbol for menu open arrow + arrowSymbol: { + left: '◄', + right: '►', + up: '▲', + down: '▼' + } +}; + +},{}],256:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults'); + +var attributes = _dereq_('./attributes'); +var constants = _dereq_('./constants'); + +var name = constants.name; +var buttonAttrs = attributes.buttons; + + +module.exports = function updateMenusDefaults(layoutIn, layoutOut) { + var opts = { + name: name, + handleItemDefaults: menuDefaults + }; + + handleArrayContainerDefaults(layoutIn, layoutOut, opts); +}; + +function menuDefaults(menuIn, menuOut, layoutOut) { + function coerce(attr, dflt) { + return Lib.coerce(menuIn, menuOut, attributes, attr, dflt); + } + + var buttons = handleArrayContainerDefaults(menuIn, menuOut, { + name: 'buttons', + handleItemDefaults: buttonDefaults + }); + + var visible = coerce('visible', buttons.length > 0); + if(!visible) return; + + coerce('active'); + coerce('direction'); + coerce('type'); + coerce('showactive'); + + coerce('x'); + coerce('y'); + Lib.noneOrAll(menuIn, menuOut, ['x', 'y']); + + coerce('xanchor'); + coerce('yanchor'); + + coerce('pad.t'); + coerce('pad.r'); + coerce('pad.b'); + coerce('pad.l'); + + Lib.coerceFont(coerce, 'font', layoutOut.font); + + coerce('bgcolor', layoutOut.paper_bgcolor); + coerce('bordercolor'); + coerce('borderwidth'); +} + +function buttonDefaults(buttonIn, buttonOut) { + function coerce(attr, dflt) { + return Lib.coerce(buttonIn, buttonOut, buttonAttrs, attr, dflt); + } + + var visible = coerce('visible', + (buttonIn.method === 'skip' || Array.isArray(buttonIn.args))); + if(visible) { + coerce('method'); + coerce('args'); + coerce('args2'); + coerce('label'); + coerce('execute'); + } +} + +},{"../../lib":285,"../../plots/array_container_defaults":326,"./attributes":254,"./constants":255}],257:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Plots = _dereq_('../../plots/plots'); +var Color = _dereq_('../color'); +var Drawing = _dereq_('../drawing'); +var Lib = _dereq_('../../lib'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor; + +var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING; + +var constants = _dereq_('./constants'); +var ScrollBox = _dereq_('./scrollbox'); + +module.exports = function draw(gd) { + var fullLayout = gd._fullLayout; + var menuData = Lib.filterVisible(fullLayout[constants.name]); + + /* Update menu data is bound to the header-group. + * The items in the header group are always present. + * + * Upon clicking on a header its corresponding button + * data is bound to the button-group. + * + * We draw all headers in one group before all buttons + * so that the buttons *always* appear above the headers. + * + * Note that only one set of buttons are visible at once. + * + * + * + * + * + * + * + * + * + * ... + * + * + * + * + * ... + */ + + function clearAutoMargin(menuOpts) { + Plots.autoMargin(gd, autoMarginId(menuOpts)); + } + + // draw update menu container + var menus = fullLayout._menulayer + .selectAll('g.' + constants.containerClassName) + .data(menuData.length > 0 ? [0] : []); + + menus.enter().append('g') + .classed(constants.containerClassName, true) + .style('cursor', 'pointer'); + + menus.exit().each(function() { + // Most components don't need to explicitly remove autoMargin, because + // marginPushers does this - but updatemenu updates don't go through + // a full replot so we need to explicitly remove it. + // This is for removing *all* updatemenus, removing individuals is + // handled below, in headerGroups.exit + d3.select(this).selectAll('g.' + constants.headerGroupClassName) + .each(clearAutoMargin); + }).remove(); + + // return early if no update menus are visible + if(menuData.length === 0) return; + + // join header group + var headerGroups = menus.selectAll('g.' + constants.headerGroupClassName) + .data(menuData, keyFunction); + + headerGroups.enter().append('g') + .classed(constants.headerGroupClassName, true); + + // draw dropdown button container + var gButton = Lib.ensureSingle(menus, 'g', constants.dropdownButtonGroupClassName, function(s) { + s.style('pointer-events', 'all'); + }); + + // find dimensions before plotting anything (this mutates menuOpts) + for(var i = 0; i < menuData.length; i++) { + var menuOpts = menuData[i]; + findDimensions(gd, menuOpts); + } + + // setup scrollbox + var scrollBoxId = 'updatemenus' + fullLayout._uid; + var scrollBox = new ScrollBox(gd, gButton, scrollBoxId); + + // remove exiting header, remove dropped buttons and reset margins + if(headerGroups.enter().size()) { + // make sure gButton is on top of all headers + gButton.node().parentNode.appendChild(gButton.node()); + gButton.call(removeAllButtons); + } + + headerGroups.exit().each(function(menuOpts) { + gButton.call(removeAllButtons); + clearAutoMargin(menuOpts); + }).remove(); + + // draw headers! + headerGroups.each(function(menuOpts) { + var gHeader = d3.select(this); + + var _gButton = menuOpts.type === 'dropdown' ? gButton : null; + + Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) { + setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, scrollBox, data.index, true); + }); + + if(menuOpts.type === 'dropdown') { + drawHeader(gd, gHeader, gButton, scrollBox, menuOpts); + + // if this menu is active, update the dropdown container + if(isActive(gButton, menuOpts)) { + drawButtons(gd, gHeader, gButton, scrollBox, menuOpts); + } + } else { + drawButtons(gd, gHeader, null, null, menuOpts); + } + }); +}; + +// Note that '_index' is set at the default step, +// it corresponds to the menu index in the user layout update menu container. +// Because a menu can be set invisible, +// this is a more 'consistent' field than the index in the menuData. +function keyFunction(menuOpts) { + return menuOpts._index; +} + +function isFolded(gButton) { + return +gButton.attr(constants.menuIndexAttrName) === -1; +} + +function isActive(gButton, menuOpts) { + return +gButton.attr(constants.menuIndexAttrName) === menuOpts._index; +} + +function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex, isSilentUpdate) { + // update 'active' attribute in menuOpts + menuOpts.active = buttonIndex; + + // due to templating, it's possible this slider doesn't even exist yet + arrayEditor(gd.layout, constants.name, menuOpts) + .applyUpdate('active', buttonIndex); + + if(menuOpts.type === 'buttons') { + drawButtons(gd, gHeader, null, null, menuOpts); + } else if(menuOpts.type === 'dropdown') { + // fold up buttons and redraw header + gButton.attr(constants.menuIndexAttrName, '-1'); + + drawHeader(gd, gHeader, gButton, scrollBox, menuOpts); + + if(!isSilentUpdate) { + drawButtons(gd, gHeader, gButton, scrollBox, menuOpts); + } + } +} + +function drawHeader(gd, gHeader, gButton, scrollBox, menuOpts) { + var header = Lib.ensureSingle(gHeader, 'g', constants.headerClassName, function(s) { + s.style('pointer-events', 'all'); + }); + + var dims = menuOpts._dims; + var active = menuOpts.active; + var headerOpts = menuOpts.buttons[active] || constants.blankHeaderOpts; + var posOpts = { y: menuOpts.pad.t, yPad: 0, x: menuOpts.pad.l, xPad: 0, index: 0 }; + var positionOverrides = { + width: dims.headerWidth, + height: dims.headerHeight + }; + + header + .call(drawItem, menuOpts, headerOpts, gd) + .call(setItemPosition, menuOpts, posOpts, positionOverrides); + + // draw drop arrow at the right edge + var arrow = Lib.ensureSingle(gHeader, 'text', constants.headerArrowClassName, function(s) { + s.attr('text-anchor', 'end') + .call(Drawing.font, menuOpts.font) + .text(constants.arrowSymbol[menuOpts.direction]); + }); + + arrow.attr({ + x: dims.headerWidth - constants.arrowOffsetX + menuOpts.pad.l, + y: dims.headerHeight / 2 + constants.textOffsetY + menuOpts.pad.t + }); + + header.on('click', function() { + gButton.call(removeAllButtons, + String(isActive(gButton, menuOpts) ? -1 : menuOpts._index) + ); + + drawButtons(gd, gHeader, gButton, scrollBox, menuOpts); + }); + + header.on('mouseover', function() { + header.call(styleOnMouseOver); + }); + + header.on('mouseout', function() { + header.call(styleOnMouseOut, menuOpts); + }); + + // translate header group + Drawing.setTranslate(gHeader, dims.lx, dims.ly); +} + +function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) { + // If this is a set of buttons, set pointer events = all since we play + // some minor games with which container is which in order to simplify + // the drawing of *either* buttons or menus + if(!gButton) { + gButton = gHeader; + gButton.attr('pointer-events', 'all'); + } + + var buttonData = (!isFolded(gButton) || menuOpts.type === 'buttons') ? + menuOpts.buttons : + []; + + var klass = menuOpts.type === 'dropdown' ? constants.dropdownButtonClassName : constants.buttonClassName; + + var buttons = gButton.selectAll('g.' + klass) + .data(Lib.filterVisible(buttonData)); + + var enter = buttons.enter().append('g') + .classed(klass, true); + + var exit = buttons.exit(); + + if(menuOpts.type === 'dropdown') { + enter.attr('opacity', '0') + .transition() + .attr('opacity', '1'); + + exit.transition() + .attr('opacity', '0') + .remove(); + } else { + exit.remove(); + } + + var x0 = 0; + var y0 = 0; + var dims = menuOpts._dims; + + var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1; + + if(menuOpts.type === 'dropdown') { + if(isVertical) { + y0 = dims.headerHeight + constants.gapButtonHeader; + } else { + x0 = dims.headerWidth + constants.gapButtonHeader; + } + } + + if(menuOpts.type === 'dropdown' && menuOpts.direction === 'up') { + y0 = -constants.gapButtonHeader + constants.gapButton - dims.openHeight; + } + + if(menuOpts.type === 'dropdown' && menuOpts.direction === 'left') { + x0 = -constants.gapButtonHeader + constants.gapButton - dims.openWidth; + } + + var posOpts = { + x: dims.lx + x0 + menuOpts.pad.l, + y: dims.ly + y0 + menuOpts.pad.t, + yPad: constants.gapButton, + xPad: constants.gapButton, + index: 0, + }; + + var scrollBoxPosition = { + l: posOpts.x + menuOpts.borderwidth, + t: posOpts.y + menuOpts.borderwidth + }; + + buttons.each(function(buttonOpts, buttonIndex) { + var button = d3.select(this); + + button + .call(drawItem, menuOpts, buttonOpts, gd) + .call(setItemPosition, menuOpts, posOpts); + + button.on('click', function() { + // skip `dragend` events + if(d3.event.defaultPrevented) return; + + if(buttonOpts.execute) { + if(buttonOpts.args2 && menuOpts.active === buttonIndex) { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2); + } else { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args); + } + } + + gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active}); + }); + + button.on('mouseover', function() { + button.call(styleOnMouseOver); + }); + + button.on('mouseout', function() { + button.call(styleOnMouseOut, menuOpts); + buttons.call(styleButtons, menuOpts); + }); + }); + + buttons.call(styleButtons, menuOpts); + + if(isVertical) { + scrollBoxPosition.w = Math.max(dims.openWidth, dims.headerWidth); + scrollBoxPosition.h = posOpts.y - scrollBoxPosition.t; + } else { + scrollBoxPosition.w = posOpts.x - scrollBoxPosition.l; + scrollBoxPosition.h = Math.max(dims.openHeight, dims.headerHeight); + } + + scrollBoxPosition.direction = menuOpts.direction; + + if(scrollBox) { + if(buttons.size()) { + drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, scrollBoxPosition); + } else { + hideScrollBox(scrollBox); + } + } +} + +function drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, position) { + // enable the scrollbox + var direction = menuOpts.direction; + var isVertical = (direction === 'up' || direction === 'down'); + var dims = menuOpts._dims; + + var active = menuOpts.active; + var translateX, translateY; + var i; + if(isVertical) { + translateY = 0; + for(i = 0; i < active; i++) { + translateY += dims.heights[i] + constants.gapButton; + } + } else { + translateX = 0; + for(i = 0; i < active; i++) { + translateX += dims.widths[i] + constants.gapButton; + } + } + + scrollBox.enable(position, translateX, translateY); + + if(scrollBox.hbar) { + scrollBox.hbar + .attr('opacity', '0') + .transition() + .attr('opacity', '1'); + } + + if(scrollBox.vbar) { + scrollBox.vbar + .attr('opacity', '0') + .transition() + .attr('opacity', '1'); + } +} + +function hideScrollBox(scrollBox) { + var hasHBar = !!scrollBox.hbar; + var hasVBar = !!scrollBox.vbar; + + if(hasHBar) { + scrollBox.hbar + .transition() + .attr('opacity', '0') + .each('end', function() { + hasHBar = false; + if(!hasVBar) scrollBox.disable(); + }); + } + + if(hasVBar) { + scrollBox.vbar + .transition() + .attr('opacity', '0') + .each('end', function() { + hasVBar = false; + if(!hasHBar) scrollBox.disable(); + }); + } +} + +function drawItem(item, menuOpts, itemOpts, gd) { + item.call(drawItemRect, menuOpts) + .call(drawItemText, menuOpts, itemOpts, gd); +} + +function drawItemRect(item, menuOpts) { + var rect = Lib.ensureSingle(item, 'rect', constants.itemRectClassName, function(s) { + s.attr({ + rx: constants.rx, + ry: constants.ry, + 'shape-rendering': 'crispEdges' + }); + }); + + rect.call(Color.stroke, menuOpts.bordercolor) + .call(Color.fill, menuOpts.bgcolor) + .style('stroke-width', menuOpts.borderwidth + 'px'); +} + +function drawItemText(item, menuOpts, itemOpts, gd) { + var text = Lib.ensureSingle(item, 'text', constants.itemTextClassName, function(s) { + s.attr({ + 'text-anchor': 'start', + 'data-notex': 1 + }); + }); + + var tx = itemOpts.label; + var _meta = gd._fullLayout._meta; + if(_meta) tx = Lib.templateString(tx, _meta); + + text.call(Drawing.font, menuOpts.font) + .text(tx) + .call(svgTextUtils.convertToTspans, gd); +} + +function styleButtons(buttons, menuOpts) { + var active = menuOpts.active; + + buttons.each(function(buttonOpts, i) { + var button = d3.select(this); + + if(i === active && menuOpts.showactive) { + button.select('rect.' + constants.itemRectClassName) + .call(Color.fill, constants.activeColor); + } + }); +} + +function styleOnMouseOver(item) { + item.select('rect.' + constants.itemRectClassName) + .call(Color.fill, constants.hoverColor); +} + +function styleOnMouseOut(item, menuOpts) { + item.select('rect.' + constants.itemRectClassName) + .call(Color.fill, menuOpts.bgcolor); +} + +// find item dimensions (this mutates menuOpts) +function findDimensions(gd, menuOpts) { + var dims = menuOpts._dims = { + width1: 0, + height1: 0, + heights: [], + widths: [], + totalWidth: 0, + totalHeight: 0, + openWidth: 0, + openHeight: 0, + lx: 0, + ly: 0 + }; + + var fakeButtons = Drawing.tester.selectAll('g.' + constants.dropdownButtonClassName) + .data(Lib.filterVisible(menuOpts.buttons)); + + fakeButtons.enter().append('g') + .classed(constants.dropdownButtonClassName, true); + + var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1; + + // loop over fake buttons to find width / height + fakeButtons.each(function(buttonOpts, i) { + var button = d3.select(this); + + button.call(drawItem, menuOpts, buttonOpts, gd); + + var text = button.select('.' + constants.itemTextClassName); + + // width is given by max width of all buttons + var tWidth = text.node() && Drawing.bBox(text.node()).width; + var wEff = Math.max(tWidth + constants.textPadX, constants.minWidth); + + // height is determined by item text + var tHeight = menuOpts.font.size * LINE_SPACING; + var tLines = svgTextUtils.lineCount(text); + var hEff = Math.max(tHeight * tLines, constants.minHeight) + constants.textOffsetY; + + hEff = Math.ceil(hEff); + wEff = Math.ceil(wEff); + + // Store per-item sizes since a row of horizontal buttons, for example, + // don't all need to be the same width: + dims.widths[i] = wEff; + dims.heights[i] = hEff; + + // Height and width of individual element: + dims.height1 = Math.max(dims.height1, hEff); + dims.width1 = Math.max(dims.width1, wEff); + + if(isVertical) { + dims.totalWidth = Math.max(dims.totalWidth, wEff); + dims.openWidth = dims.totalWidth; + dims.totalHeight += hEff + constants.gapButton; + dims.openHeight += hEff + constants.gapButton; + } else { + dims.totalWidth += wEff + constants.gapButton; + dims.openWidth += wEff + constants.gapButton; + dims.totalHeight = Math.max(dims.totalHeight, hEff); + dims.openHeight = dims.totalHeight; + } + }); + + if(isVertical) { + dims.totalHeight -= constants.gapButton; + } else { + dims.totalWidth -= constants.gapButton; + } + + + dims.headerWidth = dims.width1 + constants.arrowPadX; + dims.headerHeight = dims.height1; + + if(menuOpts.type === 'dropdown') { + if(isVertical) { + dims.width1 += constants.arrowPadX; + dims.totalHeight = dims.height1; + } else { + dims.totalWidth = dims.width1; + } + dims.totalWidth += constants.arrowPadX; + } + + fakeButtons.remove(); + + var paddedWidth = dims.totalWidth + menuOpts.pad.l + menuOpts.pad.r; + var paddedHeight = dims.totalHeight + menuOpts.pad.t + menuOpts.pad.b; + + var graphSize = gd._fullLayout._size; + dims.lx = graphSize.l + graphSize.w * menuOpts.x; + dims.ly = graphSize.t + graphSize.h * (1 - menuOpts.y); + + var xanchor = 'left'; + if(Lib.isRightAnchor(menuOpts)) { + dims.lx -= paddedWidth; + xanchor = 'right'; + } + if(Lib.isCenterAnchor(menuOpts)) { + dims.lx -= paddedWidth / 2; + xanchor = 'center'; + } + + var yanchor = 'top'; + if(Lib.isBottomAnchor(menuOpts)) { + dims.ly -= paddedHeight; + yanchor = 'bottom'; + } + if(Lib.isMiddleAnchor(menuOpts)) { + dims.ly -= paddedHeight / 2; + yanchor = 'middle'; + } + + dims.totalWidth = Math.ceil(dims.totalWidth); + dims.totalHeight = Math.ceil(dims.totalHeight); + dims.lx = Math.round(dims.lx); + dims.ly = Math.round(dims.ly); + + Plots.autoMargin(gd, autoMarginId(menuOpts), { + x: menuOpts.x, + y: menuOpts.y, + l: paddedWidth * ({right: 1, center: 0.5}[xanchor] || 0), + r: paddedWidth * ({left: 1, center: 0.5}[xanchor] || 0), + b: paddedHeight * ({top: 1, middle: 0.5}[yanchor] || 0), + t: paddedHeight * ({bottom: 1, middle: 0.5}[yanchor] || 0) + }); +} + +function autoMarginId(menuOpts) { + return constants.autoMarginIdRoot + menuOpts._index; +} + +// set item positions (mutates posOpts) +function setItemPosition(item, menuOpts, posOpts, overrideOpts) { + overrideOpts = overrideOpts || {}; + var rect = item.select('.' + constants.itemRectClassName); + var text = item.select('.' + constants.itemTextClassName); + var borderWidth = menuOpts.borderwidth; + var index = posOpts.index; + var dims = menuOpts._dims; + + Drawing.setTranslate(item, borderWidth + posOpts.x, borderWidth + posOpts.y); + + var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1; + var finalHeight = overrideOpts.height || (isVertical ? dims.heights[index] : dims.height1); + + rect.attr({ + x: 0, + y: 0, + width: overrideOpts.width || (isVertical ? dims.width1 : dims.widths[index]), + height: finalHeight + }); + + var tHeight = menuOpts.font.size * LINE_SPACING; + var tLines = svgTextUtils.lineCount(text); + var spanOffset = ((tLines - 1) * tHeight / 2); + + svgTextUtils.positionText(text, constants.textOffsetX, + finalHeight / 2 - spanOffset + constants.textOffsetY); + + if(isVertical) { + posOpts.y += dims.heights[index] + posOpts.yPad; + } else { + posOpts.x += dims.widths[index] + posOpts.xPad; + } + + posOpts.index++; +} + +function removeAllButtons(gButton, newMenuIndexAttr) { + gButton + .attr(constants.menuIndexAttrName, newMenuIndexAttr || '-1') + .selectAll('g.' + constants.dropdownButtonClassName).remove(); +} + +},{"../../constants/alignment":260,"../../lib":285,"../../lib/svg_text_utils":307,"../../plot_api/plot_template":320,"../../plots/plots":366,"../color":155,"../drawing":177,"./constants":255,"./scrollbox":259,"@plotly/d3":20}],258:[function(_dereq_,module,exports){ +arguments[4][252][0].apply(exports,arguments) +},{"./attributes":254,"./constants":255,"./defaults":256,"./draw":257,"dup":252}],259:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = ScrollBox; + +var d3 = _dereq_('@plotly/d3'); + +var Color = _dereq_('../color'); +var Drawing = _dereq_('../drawing'); + +var Lib = _dereq_('../../lib'); + +/** + * Helper class to setup a scroll box + * + * @class + * @param gd Plotly's graph div + * @param container Container to be scroll-boxed (as a D3 selection) + * @param {string} id Id for the clip path to implement the scroll box + */ +function ScrollBox(gd, container, id) { + this.gd = gd; + this.container = container; + this.id = id; + + // See ScrollBox.prototype.enable for further definition + this.position = null; // scrollbox position + this.translateX = null; // scrollbox horizontal translation + this.translateY = null; // scrollbox vertical translation + this.hbar = null; // horizontal scrollbar D3 selection + this.vbar = null; // vertical scrollbar D3 selection + + // element to capture pointer events + this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]); + + this.bg.exit() + .on('.drag', null) + .on('wheel', null) + .remove(); + + this.bg.enter().append('rect') + .classed('scrollbox-bg', true) + .style('pointer-events', 'all') + .attr({ + opacity: 0, + x: 0, + y: 0, + width: 0, + height: 0 + }); +} + +// scroll bar dimensions +ScrollBox.barWidth = 2; +ScrollBox.barLength = 20; +ScrollBox.barRadius = 2; +ScrollBox.barPad = 1; +ScrollBox.barColor = '#808BA4'; + +/** + * If needed, setup a clip path and scrollbars + * + * @method + * @param {Object} position + * @param {number} position.l Left side position (in pixels) + * @param {number} position.t Top side (in pixels) + * @param {number} position.w Width (in pixels) + * @param {number} position.h Height (in pixels) + * @param {string} [position.direction='down'] + * Either 'down', 'left', 'right' or 'up' + * @param {number} [translateX=0] Horizontal offset (in pixels) + * @param {number} [translateY=0] Vertical offset (in pixels) + */ +ScrollBox.prototype.enable = function enable(position, translateX, translateY) { + var fullLayout = this.gd._fullLayout; + var fullWidth = fullLayout.width; + var fullHeight = fullLayout.height; + + // compute position of scrollbox + this.position = position; + + var l = this.position.l; + var w = this.position.w; + var t = this.position.t; + var h = this.position.h; + var direction = this.position.direction; + var isDown = (direction === 'down'); + var isLeft = (direction === 'left'); + var isRight = (direction === 'right'); + var isUp = (direction === 'up'); + var boxW = w; + var boxH = h; + var boxL, boxR; + var boxT, boxB; + + if(!isDown && !isLeft && !isRight && !isUp) { + this.position.direction = 'down'; + isDown = true; + } + + var isVertical = isDown || isUp; + if(isVertical) { + boxL = l; + boxR = boxL + boxW; + + if(isDown) { + // anchor to top side + boxT = t; + boxB = Math.min(boxT + boxH, fullHeight); + boxH = boxB - boxT; + } else { + // anchor to bottom side + boxB = t + boxH; + boxT = Math.max(boxB - boxH, 0); + boxH = boxB - boxT; + } + } else { + boxT = t; + boxB = boxT + boxH; + + if(isLeft) { + // anchor to right side + boxR = l + boxW; + boxL = Math.max(boxR - boxW, 0); + boxW = boxR - boxL; + } else { + // anchor to left side + boxL = l; + boxR = Math.min(boxL + boxW, fullWidth); + boxW = boxR - boxL; + } + } + + this._box = { + l: boxL, + t: boxT, + w: boxW, + h: boxH + }; + + // compute position of horizontal scroll bar + var needsHorizontalScrollBar = (w > boxW); + var hbarW = ScrollBox.barLength + 2 * ScrollBox.barPad; + var hbarH = ScrollBox.barWidth + 2 * ScrollBox.barPad; + // draw horizontal scrollbar on the bottom side + var hbarL = l; + var hbarT = t + h; + + if(hbarT + hbarH > fullHeight) hbarT = fullHeight - hbarH; + + var hbar = this.container.selectAll('rect.scrollbar-horizontal').data( + (needsHorizontalScrollBar) ? [0] : []); + + hbar.exit() + .on('.drag', null) + .remove(); + + hbar.enter().append('rect') + .classed('scrollbar-horizontal', true) + .call(Color.fill, ScrollBox.barColor); + + if(needsHorizontalScrollBar) { + this.hbar = hbar.attr({ + 'rx': ScrollBox.barRadius, + 'ry': ScrollBox.barRadius, + 'x': hbarL, + 'y': hbarT, + 'width': hbarW, + 'height': hbarH + }); + + // hbar center moves between hbarXMin and hbarXMin + hbarTranslateMax + this._hbarXMin = hbarL + hbarW / 2; + this._hbarTranslateMax = boxW - hbarW; + } else { + delete this.hbar; + delete this._hbarXMin; + delete this._hbarTranslateMax; + } + + // compute position of vertical scroll bar + var needsVerticalScrollBar = (h > boxH); + var vbarW = ScrollBox.barWidth + 2 * ScrollBox.barPad; + var vbarH = ScrollBox.barLength + 2 * ScrollBox.barPad; + // draw vertical scrollbar on the right side + var vbarL = l + w; + var vbarT = t; + + if(vbarL + vbarW > fullWidth) vbarL = fullWidth - vbarW; + + var vbar = this.container.selectAll('rect.scrollbar-vertical').data( + (needsVerticalScrollBar) ? [0] : []); + + vbar.exit() + .on('.drag', null) + .remove(); + + vbar.enter().append('rect') + .classed('scrollbar-vertical', true) + .call(Color.fill, ScrollBox.barColor); + + if(needsVerticalScrollBar) { + this.vbar = vbar.attr({ + 'rx': ScrollBox.barRadius, + 'ry': ScrollBox.barRadius, + 'x': vbarL, + 'y': vbarT, + 'width': vbarW, + 'height': vbarH + }); + + // vbar center moves between vbarYMin and vbarYMin + vbarTranslateMax + this._vbarYMin = vbarT + vbarH / 2; + this._vbarTranslateMax = boxH - vbarH; + } else { + delete this.vbar; + delete this._vbarYMin; + delete this._vbarTranslateMax; + } + + // setup a clip path (if scroll bars are needed) + var clipId = this.id; + var clipL = boxL - 0.5; + var clipR = (needsVerticalScrollBar) ? boxR + vbarW + 0.5 : boxR + 0.5; + var clipT = boxT - 0.5; + var clipB = (needsHorizontalScrollBar) ? boxB + hbarH + 0.5 : boxB + 0.5; + + var clipPath = fullLayout._topdefs.selectAll('#' + clipId) + .data((needsHorizontalScrollBar || needsVerticalScrollBar) ? [0] : []); + + clipPath.exit().remove(); + + clipPath.enter() + .append('clipPath').attr('id', clipId) + .append('rect'); + + if(needsHorizontalScrollBar || needsVerticalScrollBar) { + this._clipRect = clipPath.select('rect').attr({ + x: Math.floor(clipL), + y: Math.floor(clipT), + width: Math.ceil(clipR) - Math.floor(clipL), + height: Math.ceil(clipB) - Math.floor(clipT) + }); + + this.container.call(Drawing.setClipUrl, clipId, this.gd); + + this.bg.attr({ + x: l, + y: t, + width: w, + height: h + }); + } else { + this.bg.attr({ + width: 0, + height: 0 + }); + this.container + .on('wheel', null) + .on('.drag', null) + .call(Drawing.setClipUrl, null); + delete this._clipRect; + } + + // set up drag listeners (if scroll bars are needed) + if(needsHorizontalScrollBar || needsVerticalScrollBar) { + var onBoxDrag = d3.behavior.drag() + .on('dragstart', function() { + d3.event.sourceEvent.preventDefault(); + }) + .on('drag', this._onBoxDrag.bind(this)); + + this.container + .on('wheel', null) + .on('wheel', this._onBoxWheel.bind(this)) + .on('.drag', null) + .call(onBoxDrag); + + var onBarDrag = d3.behavior.drag() + .on('dragstart', function() { + d3.event.sourceEvent.preventDefault(); + d3.event.sourceEvent.stopPropagation(); + }) + .on('drag', this._onBarDrag.bind(this)); + + if(needsHorizontalScrollBar) { + this.hbar + .on('.drag', null) + .call(onBarDrag); + } + + if(needsVerticalScrollBar) { + this.vbar + .on('.drag', null) + .call(onBarDrag); + } + } + + // set scrollbox translation + this.setTranslate(translateX, translateY); +}; + +/** + * If present, remove clip-path and scrollbars + * + * @method + */ +ScrollBox.prototype.disable = function disable() { + if(this.hbar || this.vbar) { + this.bg.attr({ + width: 0, + height: 0 + }); + this.container + .on('wheel', null) + .on('.drag', null) + .call(Drawing.setClipUrl, null); + delete this._clipRect; + } + + if(this.hbar) { + this.hbar.on('.drag', null); + this.hbar.remove(); + delete this.hbar; + delete this._hbarXMin; + delete this._hbarTranslateMax; + } + + if(this.vbar) { + this.vbar.on('.drag', null); + this.vbar.remove(); + delete this.vbar; + delete this._vbarYMin; + delete this._vbarTranslateMax; + } +}; + +/** + * Handles scroll box drag events + * + * @method + */ +ScrollBox.prototype._onBoxDrag = function _onBoxDrag() { + var translateX = this.translateX; + var translateY = this.translateY; + + if(this.hbar) { + translateX -= d3.event.dx; + } + + if(this.vbar) { + translateY -= d3.event.dy; + } + + this.setTranslate(translateX, translateY); +}; + +/** + * Handles scroll box wheel events + * + * @method + */ +ScrollBox.prototype._onBoxWheel = function _onBoxWheel() { + var translateX = this.translateX; + var translateY = this.translateY; + + if(this.hbar) { + translateX += d3.event.deltaY; + } + + if(this.vbar) { + translateY += d3.event.deltaY; + } + + this.setTranslate(translateX, translateY); +}; + +/** + * Handles scroll bar drag events + * + * @method + */ +ScrollBox.prototype._onBarDrag = function _onBarDrag() { + var translateX = this.translateX; + var translateY = this.translateY; + + if(this.hbar) { + var xMin = translateX + this._hbarXMin; + var xMax = xMin + this._hbarTranslateMax; + var x = Lib.constrain(d3.event.x, xMin, xMax); + var xf = (x - xMin) / (xMax - xMin); + + var translateXMax = this.position.w - this._box.w; + + translateX = xf * translateXMax; + } + + if(this.vbar) { + var yMin = translateY + this._vbarYMin; + var yMax = yMin + this._vbarTranslateMax; + var y = Lib.constrain(d3.event.y, yMin, yMax); + var yf = (y - yMin) / (yMax - yMin); + + var translateYMax = this.position.h - this._box.h; + + translateY = yf * translateYMax; + } + + this.setTranslate(translateX, translateY); +}; + +/** + * Set clip path and scroll bar translate transform + * + * @method + * @param {number} [translateX=0] Horizontal offset (in pixels) + * @param {number} [translateY=0] Vertical offset (in pixels) + */ +ScrollBox.prototype.setTranslate = function setTranslate(translateX, translateY) { + // store translateX and translateY (needed by mouse event handlers) + var translateXMax = this.position.w - this._box.w; + var translateYMax = this.position.h - this._box.h; + + translateX = Lib.constrain(translateX || 0, 0, translateXMax); + translateY = Lib.constrain(translateY || 0, 0, translateYMax); + + this.translateX = translateX; + this.translateY = translateY; + + this.container.call(Drawing.setTranslate, + this._box.l - this.position.l - translateX, + this._box.t - this.position.t - translateY); + + if(this._clipRect) { + this._clipRect.attr({ + x: Math.floor(this.position.l + translateX - 0.5), + y: Math.floor(this.position.t + translateY - 0.5) + }); + } + + if(this.hbar) { + var xf = translateX / translateXMax; + + this.hbar.call(Drawing.setTranslate, + translateX + xf * this._hbarTranslateMax, + translateY); + } + + if(this.vbar) { + var yf = translateY / translateYMax; + + this.vbar.call(Drawing.setTranslate, + translateX, + translateY + yf * this._vbarTranslateMax); + } +}; + +},{"../../lib":285,"../color":155,"../drawing":177,"@plotly/d3":20}],260:[function(_dereq_,module,exports){ +'use strict'; + +// fraction of some size to get to a named position +module.exports = { + // from bottom left: this is the origin of our paper-reference + // positioning system + FROM_BL: { + left: 0, + center: 0.5, + right: 1, + bottom: 0, + middle: 0.5, + top: 1 + }, + // from top left: this is the screen pixel positioning origin + FROM_TL: { + left: 0, + center: 0.5, + right: 1, + bottom: 1, + middle: 0.5, + top: 0 + }, + // from bottom right: sometimes you just need the opposite of ^^ + FROM_BR: { + left: 1, + center: 0.5, + right: 0, + bottom: 0, + middle: 0.5, + top: 1 + }, + // multiple of fontSize to get the vertical offset between lines + LINE_SPACING: 1.3, + + // multiple of fontSize to shift from the baseline + // to the cap (captical letter) line + // (to use when we don't calculate this shift from Drawing.bBox) + // This is an approximation since in reality cap height can differ + // from font to font. However, according to Wikipedia + // an "average" font might have a cap height of 70% of the em + // https://en.wikipedia.org/wiki/Em_(typography)#History + CAP_SHIFT: 0.70, + + // half the cap height (distance between baseline and cap line) + // of an "average" font (for more info see above). + MID_SHIFT: 0.35, + + OPPOSITE_SIDE: { + left: 'right', + right: 'left', + top: 'bottom', + bottom: 'top' + } +}; + +},{}],261:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + axisRefDescription: function(axisname, lower, upper) { + return [ + 'If set to a', axisname, 'axis id (e.g. *' + axisname + '* or', + '*' + axisname + '2*), the `' + axisname + '` position refers to a', + axisname, 'coordinate. If set to *paper*, the `' + axisname + '`', + 'position refers to the distance from the', lower, 'of the plotting', + 'area in normalized coordinates where *0* (*1*) corresponds to the', + lower, '(' + upper + '). If set to a', axisname, 'axis ID followed by', + '*domain* (separated by a space), the position behaves like for', + '*paper*, but refers to the distance in fractions of the domain', + 'length from the', lower, 'of the domain of that axis: e.g.,', + '*' + axisname + '2 domain* refers to the domain of the second', + axisname, ' axis and a', axisname, 'position of 0.5 refers to the', + 'point between the', lower, 'and the', upper, 'of the domain of the', + 'second', axisname, 'axis.', + ].join(' '); + } +}; + +},{}],262:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + FORMAT_LINK: 'https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_format', + DATE_FORMAT_LINK: 'https://github.com/d3/d3-time-format#locale_format' +}; + +},{}],263:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + COMPARISON_OPS: ['=', '!=', '<', '>=', '>', '<='], + COMPARISON_OPS2: ['=', '<', '>=', '>', '<='], + INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['], + SET_OPS: ['{}', '}{'], + CONSTRAINT_REDUCTION: { + // for contour constraints, open/closed endpoints are equivalent + '=': '=', + + '<': '<', + '<=': '<', + + '>': '>', + '>=': '>', + + '[]': '[]', + '()': '[]', + '[)': '[]', + '(]': '[]', + + '][': '][', + ')(': '][', + '](': '][', + ')[': '][' + } +}; + +},{}],264:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + /** + * Timing information for interactive elements + */ + SHOW_PLACEHOLDER: 100, + HIDE_PLACEHOLDER: 1000, + + // opacity dimming fraction for points that are not in selection + DESELECTDIM: 0.2 +}; + +},{}],265:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + /** + * Standardize all missing data in calcdata to use undefined + * never null or NaN. + * That way we can use !==undefined, or !== BADNUM, + * to test for real data + */ + BADNUM: undefined, + + /* + * Limit certain operations to well below floating point max value + * to avoid glitches: Make sure that even when you multiply it by the + * number of pixels on a giant screen it still works + */ + FP_SAFE: Number.MAX_VALUE * 1e-4, + + /* + * conversion of date units to milliseconds + * year and month constants are marked "AVG" + * to remind us that not all years and months + * have the same length + */ + ONEMAXYEAR: 31622400000, // 366 * ONEDAY + ONEAVGYEAR: 31557600000, // 365.25 days + ONEMINYEAR: 31536000000, // 365 * ONEDAY + ONEMAXQUARTER: 7948800000, // 92 * ONEDAY + ONEAVGQUARTER: 7889400000, // 1/4 of ONEAVGYEAR + ONEMINQUARTER: 7689600000, // 89 * ONEDAY + ONEMAXMONTH: 2678400000, // 31 * ONEDAY + ONEAVGMONTH: 2629800000, // 1/12 of ONEAVGYEAR + ONEMINMONTH: 2419200000, // 28 * ONEDAY + ONEWEEK: 604800000, // 7 * ONEDAY + ONEDAY: 86400000, // 24 * ONEHOUR + ONEHOUR: 3600000, + ONEMIN: 60000, + ONESEC: 1000, + + /* + * For fast conversion btwn world calendars and epoch ms, the Julian Day Number + * of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD() + */ + EPOCHJD: 2440587.5, + + /* + * Are two values nearly equal? Compare to 1PPM + */ + ALMOST_EQUAL: 1 - 1e-6, + + /* + * If we're asked to clip a non-positive log value, how far off-screen + * do we put it? + */ + LOG_CLIP: 10, + + /* + * not a number, but for displaying numbers: the "minus sign" symbol is + * wider than the regular ascii dash "-" + */ + MINUS_SIGN: '\u2212' +}; + +},{}],266:[function(_dereq_,module,exports){ +'use strict'; + + +exports.xmlns = 'http://www.w3.org/2000/xmlns/'; +exports.svg = 'http://www.w3.org/2000/svg'; +exports.xlink = 'http://www.w3.org/1999/xlink'; + +// the 'old' d3 quirk got fix in v3.5.7 +// https://github.com/mbostock/d3/commit/a6f66e9dd37f764403fc7c1f26be09ab4af24fed +exports.svgAttrs = { + xmlns: exports.svg, + 'xmlns:xlink': exports.xlink +}; + +},{}],267:[function(_dereq_,module,exports){ +'use strict'; + +exports.version = _dereq_('./version').version; + +// inject promise polyfill +_dereq_('native-promise-only'); + +// inject plot css +_dereq_('../build/plotcss'); + +// include registry module and expose register method +var Registry = _dereq_('./registry'); +var register = exports.register = Registry.register; + +// expose plot api methods +var plotApi = _dereq_('./plot_api'); +var methodNames = Object.keys(plotApi); +for(var i = 0; i < methodNames.length; i++) { + var name = methodNames[i]; + // _ -> private API methods, but still registered for internal use + if(name.charAt(0) !== '_') exports[name] = plotApi[name]; + register({ + moduleType: 'apiMethod', + name: name, + fn: plotApi[name] + }); +} + +// scatter is the only trace included by default +register(_dereq_('./traces/scatter')); + +// register all registrable components modules +register([ + _dereq_('./components/legend'), + _dereq_('./components/fx'), // fx needs to come after legend + _dereq_('./components/annotations'), + _dereq_('./components/annotations3d'), + _dereq_('./components/shapes'), + _dereq_('./components/images'), + _dereq_('./components/updatemenus'), + _dereq_('./components/sliders'), + _dereq_('./components/rangeslider'), + _dereq_('./components/rangeselector'), + _dereq_('./components/grid'), + _dereq_('./components/errorbars'), + _dereq_('./components/colorscale'), + _dereq_('./components/colorbar'), + _dereq_('./components/modebar') +]); + +// locales en and en-US are required for default behavior +register([ + _dereq_('./locale-en'), + _dereq_('./locale-en-us') +]); + +// locales that are present in the window should be loaded +if(window.PlotlyLocales && Array.isArray(window.PlotlyLocales)) { + register(window.PlotlyLocales); + delete window.PlotlyLocales; +} + +// plot icons +exports.Icons = _dereq_('./fonts/ploticon'); + +// unofficial 'beta' plot methods, use at your own risk +var Fx = _dereq_('./components/fx'); +var Plots = _dereq_('./plots/plots'); + +exports.Plots = { + resize: Plots.resize, + graphJson: Plots.graphJson, + sendDataToCloud: Plots.sendDataToCloud +}; +exports.Fx = { + hover: Fx.hover, + unhover: Fx.unhover, + loneHover: Fx.loneHover, + loneUnhover: Fx.loneUnhover +}; +exports.Snapshot = _dereq_('./snapshot'); +exports.PlotSchema = _dereq_('./plot_api/plot_schema'); + +},{"../build/plotcss":1,"./components/annotations":146,"./components/annotations3d":151,"./components/colorbar":161,"./components/colorscale":167,"./components/errorbars":183,"./components/fx":195,"./components/grid":199,"./components/images":204,"./components/legend":212,"./components/modebar":218,"./components/rangeselector":226,"./components/rangeslider":233,"./components/shapes":247,"./components/sliders":252,"./components/updatemenus":258,"./fonts/ploticon":268,"./locale-en":311,"./locale-en-us":310,"./plot_api":315,"./plot_api/plot_schema":319,"./plots/plots":366,"./registry":373,"./snapshot":378,"./traces/scatter":506,"./version":546,"native-promise-only":70}],268:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + 'undo': { + 'width': 857.1, + 'height': 1000, + 'path': 'm857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'home': { + 'width': 928.6, + 'height': 1000, + 'path': 'm786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'camera-retro': { + 'width': 1000, + 'height': 1000, + 'path': 'm518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'zoombox': { + 'width': 1000, + 'height': 1000, + 'path': 'm1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'pan': { + 'width': 1000, + 'height': 1000, + 'path': 'm1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'zoom_plus': { + 'width': 875, + 'height': 1000, + 'path': 'm1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'zoom_minus': { + 'width': 875, + 'height': 1000, + 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'autoscale': { + 'width': 1000, + 'height': 1000, + 'path': 'm250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'tooltip_basic': { + 'width': 1500, + 'height': 1000, + 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'tooltip_compare': { + 'width': 1125, + 'height': 1000, + 'path': 'm187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'plotlylogo': { + 'width': 1542, + 'height': 1000, + 'path': 'm0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'z-axis': { + 'width': 1000, + 'height': 1000, + 'path': 'm833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + '3d_rotate': { + 'width': 1000, + 'height': 1000, + 'path': 'm922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'camera': { + 'width': 1000, + 'height': 1000, + 'path': 'm500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'movie': { + 'width': 1000, + 'height': 1000, + 'path': 'm938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'question': { + 'width': 857.1, + 'height': 1000, + 'path': 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'disk': { + 'width': 857.1, + 'height': 1000, + 'path': 'm214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'drawopenpath': { + 'width': 70, + 'height': 70, + 'path': 'M33.21,85.65a7.31,7.31,0,0,1-2.59-.48c-8.16-3.11-9.27-19.8-9.88-41.3-.1-3.58-.19-6.68-.35-9-.15-2.1-.67-3.48-1.43-3.79-2.13-.88-7.91,2.32-12,5.86L3,32.38c1.87-1.64,11.55-9.66,18.27-6.9,2.13.87,4.75,3.14,5.17,9,.17,2.43.26,5.59.36,9.25a224.17,224.17,0,0,0,1.5,23.4c1.54,10.76,4,12.22,4.48,12.4.84.32,2.79-.46,5.76-3.59L43,80.07C41.53,81.57,37.68,85.64,33.21,85.65ZM74.81,69a11.34,11.34,0,0,0,6.09-6.72L87.26,44.5,74.72,32,56.9,38.35c-2.37.86-5.57,3.42-6.61,6L38.65,72.14l8.42,8.43ZM55,46.27a7.91,7.91,0,0,1,3.64-3.17l14.8-5.3,8,8L76.11,60.6l-.06.19a6.37,6.37,0,0,1-3,3.43L48.25,74.59,44.62,71Zm16.57,7.82A6.9,6.9,0,1,0,64.64,61,6.91,6.91,0,0,0,71.54,54.09Zm-4.05,0a2.85,2.85,0,1,1-2.85-2.85A2.86,2.86,0,0,1,67.49,54.09Zm-4.13,5.22L60.5,56.45,44.26,72.7l2.86,2.86ZM97.83,35.67,84.14,22l-8.57,8.57L89.26,44.24Zm-13.69-8,8,8-2.85,2.85-8-8Z', + 'transform': 'matrix(1 0 0 1 -15 -15)' + }, + 'drawclosedpath': { + 'width': 90, + 'height': 90, + 'path': 'M88.41,21.12a26.56,26.56,0,0,0-36.18,0l-2.07,2-2.07-2a26.57,26.57,0,0,0-36.18,0,23.74,23.74,0,0,0,0,34.8L48,90.12a3.22,3.22,0,0,0,4.42,0l36-34.21a23.73,23.73,0,0,0,0-34.79ZM84,51.24,50.16,83.35,16.35,51.25a17.28,17.28,0,0,1,0-25.47,20,20,0,0,1,27.3,0l4.29,4.07a3.23,3.23,0,0,0,4.44,0l4.29-4.07a20,20,0,0,1,27.3,0,17.27,17.27,0,0,1,0,25.46ZM66.76,47.68h-33v6.91h33ZM53.35,35H46.44V68h6.91Z', + 'transform': 'matrix(1 0 0 1 -5 -5)' + }, + 'lasso': { + 'width': 1031, + 'height': 1000, + 'path': 'm1018 538c-36 207-290 336-568 286-277-48-473-256-436-463 10-57 36-108 76-151-13-66 11-137 68-183 34-28 75-41 114-42l-55-70 0 0c-2-1-3-2-4-3-10-14-8-34 5-45 14-11 34-8 45 4 1 1 2 3 2 5l0 0 113 140c16 11 31 24 45 40 4 3 6 7 8 11 48-3 100 0 151 9 278 48 473 255 436 462z m-624-379c-80 14-149 48-197 96 42 42 109 47 156 9 33-26 47-66 41-105z m-187-74c-19 16-33 37-39 60 50-32 109-55 174-68-42-25-95-24-135 8z m360 75c-34-7-69-9-102-8 8 62-16 128-68 170-73 59-175 54-244-5-9 20-16 40-20 61-28 159 121 317 333 354s407-60 434-217c28-159-121-318-333-355z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'selectbox': { + 'width': 1000, + 'height': 1000, + 'path': 'm0 850l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-285l0-143 143 0 0 143-143 0z m857 0l0-143 143 0 0 143-143 0z m-857-286l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z m285 0l0-143 143 0 0 143-143 0z m286 0l0-143 143 0 0 143-143 0z', + 'transform': 'matrix(1 0 0 -1 0 850)' + }, + 'drawline': { + 'width': 70, + 'height': 70, + 'path': 'M60.64,62.3a11.29,11.29,0,0,0,6.09-6.72l6.35-17.72L60.54,25.31l-17.82,6.4c-2.36.86-5.57,3.41-6.6,6L24.48,65.5l8.42,8.42ZM40.79,39.63a7.89,7.89,0,0,1,3.65-3.17l14.79-5.31,8,8L61.94,54l-.06.19a6.44,6.44,0,0,1-3,3.43L34.07,68l-3.62-3.63Zm16.57,7.81a6.9,6.9,0,1,0-6.89,6.9A6.9,6.9,0,0,0,57.36,47.44Zm-4,0a2.86,2.86,0,1,1-2.85-2.85A2.86,2.86,0,0,1,53.32,47.44Zm-4.13,5.22L46.33,49.8,30.08,66.05l2.86,2.86ZM83.65,29,70,15.34,61.4,23.9,75.09,37.59ZM70,21.06l8,8-2.84,2.85-8-8ZM87,80.49H10.67V87H87Z', + 'transform': 'matrix(1 0 0 1 -15 -15)' + }, + 'drawrect': { + 'width': 80, + 'height': 80, + 'path': 'M78,22V79H21V22H78m9-9H12V88H87V13ZM68,46.22H31V54H68ZM53,32H45.22V69H53Z', + 'transform': 'matrix(1 0 0 1 -10 -10)' + }, + 'drawcircle': { + 'width': 80, + 'height': 80, + 'path': 'M50,84.72C26.84,84.72,8,69.28,8,50.3S26.84,15.87,50,15.87,92,31.31,92,50.3,73.16,84.72,50,84.72Zm0-60.59c-18.6,0-33.74,11.74-33.74,26.17S31.4,76.46,50,76.46,83.74,64.72,83.74,50.3,68.6,24.13,50,24.13Zm17.15,22h-34v7.11h34Zm-13.8-13H46.24v34h7.11Z', + 'transform': 'matrix(1 0 0 1 -10 -10)' + }, + 'eraseshape': { + 'width': 80, + 'height': 80, + 'path': 'M82.77,78H31.85L6,49.57,31.85,21.14H82.77a8.72,8.72,0,0,1,8.65,8.77V69.24A8.72,8.72,0,0,1,82.77,78ZM35.46,69.84H82.77a.57.57,0,0,0,.49-.6V29.91a.57.57,0,0,0-.49-.61H35.46L17,49.57Zm32.68-34.7-24,24,5,5,24-24Zm-19,.53-5,5,24,24,5-5Z', + 'transform': 'matrix(1 0 0 1 -10 -10)' + }, + 'spikeline': { + 'width': 1000, + 'height': 1000, + 'path': 'M512 409c0-57-46-104-103-104-57 0-104 47-104 104 0 57 47 103 104 103 57 0 103-46 103-103z m-327-39l92 0 0 92-92 0z m-185 0l92 0 0 92-92 0z m370-186l92 0 0 93-92 0z m0-184l92 0 0 92-92 0z', + 'transform': 'matrix(1.5 0 0 -1.5 0 850)' + }, + 'pencil': { + 'width': 1792, + 'height': 1792, + 'path': 'M491 1536l91-91-235-235-91 91v107h128v128h107zm523-928q0-22-22-22-10 0-17 7l-542 542q-7 7-7 17 0 22 22 22 10 0 17-7l542-542q7-7 7-17zm-54-192l416 416-832 832h-416v-416zm683 96q0 53-37 90l-166 166-416-416 166-165q36-38 90-38 53 0 91 38l235 234q37 39 37 91z', + 'transform': 'matrix(1 0 0 1 0 1)' + }, + 'newplotlylogo': { + 'name': 'newplotlylogo', + 'svg': 'plotly-logomark' + } +}; + +},{}],269:[function(_dereq_,module,exports){ +'use strict'; + + +/** + * Determine the position anchor property of x/y xanchor/yanchor components. + * + * - values < 1/3 align the low side at that fraction, + * - values [1/3, 2/3] align the center at that fraction, + * - values > 2/3 align the right at that fraction. + */ + + +exports.isLeftAnchor = function isLeftAnchor(opts) { + return ( + opts.xanchor === 'left' || + (opts.xanchor === 'auto' && opts.x <= 1 / 3) + ); +}; + +exports.isCenterAnchor = function isCenterAnchor(opts) { + return ( + opts.xanchor === 'center' || + (opts.xanchor === 'auto' && opts.x > 1 / 3 && opts.x < 2 / 3) + ); +}; + +exports.isRightAnchor = function isRightAnchor(opts) { + return ( + opts.xanchor === 'right' || + (opts.xanchor === 'auto' && opts.x >= 2 / 3) + ); +}; + +exports.isTopAnchor = function isTopAnchor(opts) { + return ( + opts.yanchor === 'top' || + (opts.yanchor === 'auto' && opts.y >= 2 / 3) + ); +}; + +exports.isMiddleAnchor = function isMiddleAnchor(opts) { + return ( + opts.yanchor === 'middle' || + (opts.yanchor === 'auto' && opts.y > 1 / 3 && opts.y < 2 / 3) + ); +}; + +exports.isBottomAnchor = function isBottomAnchor(opts) { + return ( + opts.yanchor === 'bottom' || + (opts.yanchor === 'auto' && opts.y <= 1 / 3) + ); +}; + +},{}],270:[function(_dereq_,module,exports){ +'use strict'; + +var modModule = _dereq_('./mod'); +var mod = modModule.mod; +var modHalf = modModule.modHalf; + +var PI = Math.PI; +var twoPI = 2 * PI; + +function deg2rad(deg) { return deg / 180 * PI; } + +function rad2deg(rad) { return rad / PI * 180; } + +/** + * is sector a full circle? + * ... this comes up a lot in SVG path-drawing routines + * + * N.B. we consider all sectors that span more that 2pi 'full' circles + * + * @param {2-item array} aBnds : angular bounds in *radians* + * @return {boolean} + */ +function isFullCircle(aBnds) { + return Math.abs(aBnds[1] - aBnds[0]) > twoPI - 1e-14; +} + +/** + * angular delta between angle 'a' and 'b' + * solution taken from: https://stackoverflow.com/a/2007279 + * + * @param {number} a : first angle in *radians* + * @param {number} b : second angle in *radians* + * @return {number} angular delta in *radians* + */ +function angleDelta(a, b) { + return modHalf(b - a, twoPI); +} + +/** + * angular distance between angle 'a' and 'b' + * + * @param {number} a : first angle in *radians* + * @param {number} b : second angle in *radians* + * @return {number} angular distance in *radians* + */ +function angleDist(a, b) { + return Math.abs(angleDelta(a, b)); +} + +/** + * is angle inside sector? + * + * @param {number} a : angle to test in *radians* + * @param {2-item array} aBnds : sector's angular bounds in *radians* + * @param {boolean} + */ +function isAngleInsideSector(a, aBnds) { + if(isFullCircle(aBnds)) return true; + + var s0, s1; + + if(aBnds[0] < aBnds[1]) { + s0 = aBnds[0]; + s1 = aBnds[1]; + } else { + s0 = aBnds[1]; + s1 = aBnds[0]; + } + + s0 = mod(s0, twoPI); + s1 = mod(s1, twoPI); + if(s0 > s1) s1 += twoPI; + + var a0 = mod(a, twoPI); + var a1 = a0 + twoPI; + + return (a0 >= s0 && a0 <= s1) || (a1 >= s0 && a1 <= s1); +} + +/** + * is pt (r,a) inside sector? + * + * @param {number} r : pt's radial coordinate + * @param {number} a : pt's angular coordinate in *radians* + * @param {2-item array} rBnds : sector's radial bounds + * @param {2-item array} aBnds : sector's angular bounds in *radians* + * @return {boolean} + */ +function isPtInsideSector(r, a, rBnds, aBnds) { + if(!isAngleInsideSector(a, aBnds)) return false; + + var r0, r1; + + if(rBnds[0] < rBnds[1]) { + r0 = rBnds[0]; + r1 = rBnds[1]; + } else { + r0 = rBnds[1]; + r1 = rBnds[0]; + } + + return r >= r0 && r <= r1; +} + +// common to pathArc, pathSector and pathAnnulus +function _path(r0, r1, a0, a1, cx, cy, isClosed) { + cx = cx || 0; + cy = cy || 0; + + var isCircle = isFullCircle([a0, a1]); + var aStart, aMid, aEnd; + var rStart, rEnd; + + if(isCircle) { + aStart = 0; + aMid = PI; + aEnd = twoPI; + } else { + if(a0 < a1) { + aStart = a0; + aEnd = a1; + } else { + aStart = a1; + aEnd = a0; + } + } + + if(r0 < r1) { + rStart = r0; + rEnd = r1; + } else { + rStart = r1; + rEnd = r0; + } + + // N.B. svg coordinates here, where y increases downward + function pt(r, a) { + return [r * Math.cos(a) + cx, cy - r * Math.sin(a)]; + } + + var largeArc = Math.abs(aEnd - aStart) <= PI ? 0 : 1; + function arc(r, a, cw) { + return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, a); + } + + var p; + + if(isCircle) { + if(rStart === null) { + p = 'M' + pt(rEnd, aStart) + + arc(rEnd, aMid, 0) + + arc(rEnd, aEnd, 0) + 'Z'; + } else { + p = 'M' + pt(rStart, aStart) + + arc(rStart, aMid, 0) + + arc(rStart, aEnd, 0) + 'Z' + + 'M' + pt(rEnd, aStart) + + arc(rEnd, aMid, 1) + + arc(rEnd, aEnd, 1) + 'Z'; + } + } else { + if(rStart === null) { + p = 'M' + pt(rEnd, aStart) + arc(rEnd, aEnd, 0); + if(isClosed) p += 'L0,0Z'; + } else { + p = 'M' + pt(rStart, aStart) + + 'L' + pt(rEnd, aStart) + + arc(rEnd, aEnd, 0) + + 'L' + pt(rStart, aEnd) + + arc(rStart, aStart, 1) + 'Z'; + } + } + + return p; +} + +/** + * path an arc + * + * @param {number} r : radius + * @param {number} a0 : first angular coordinate in *radians* + * @param {number} a1 : second angular coordinate in *radians* + * @param {number (optional)} cx : x coordinate of center + * @param {number (optional)} cy : y coordinate of center + * @return {string} svg path + */ +function pathArc(r, a0, a1, cx, cy) { + return _path(null, r, a0, a1, cx, cy, 0); +} + +/** + * path a sector + * + * @param {number} r : radius + * @param {number} a0 : first angular coordinate in *radians* + * @param {number} a1 : second angular coordinate in *radians* + * @param {number (optional)} cx : x coordinate of center + * @param {number (optional)} cy : y coordinate of center + * @return {string} svg path + */ +function pathSector(r, a0, a1, cx, cy) { + return _path(null, r, a0, a1, cx, cy, 1); +} + +/** + * path an annulus + * + * @param {number} r0 : first radial coordinate + * @param {number} r1 : second radial coordinate + * @param {number} a0 : first angular coordinate in *radians* + * @param {number} a1 : second angular coordinate in *radians* + * @param {number (optional)} cx : x coordinate of center + * @param {number (optional)} cy : y coordinate of center + * @return {string} svg path + */ +function pathAnnulus(r0, r1, a0, a1, cx, cy) { + return _path(r0, r1, a0, a1, cx, cy, 1); +} + +module.exports = { + deg2rad: deg2rad, + rad2deg: rad2deg, + angleDelta: angleDelta, + angleDist: angleDist, + isFullCircle: isFullCircle, + isAngleInsideSector: isAngleInsideSector, + isPtInsideSector: isPtInsideSector, + pathArc: pathArc, + pathSector: pathSector, + pathAnnulus: pathAnnulus +}; + +},{"./mod":292}],271:[function(_dereq_,module,exports){ +'use strict'; + +var isArray = Array.isArray; + +var ab = ArrayBuffer; +var dv = DataView; + +function isTypedArray(a) { + return ab.isView(a) && !(a instanceof dv); +} +exports.isTypedArray = isTypedArray; + +function isArrayOrTypedArray(a) { + return isArray(a) || isTypedArray(a); +} +exports.isArrayOrTypedArray = isArrayOrTypedArray; + +/* + * Test whether an input object is 1D. + * + * Assumes we already know the object is an array. + * + * Looks only at the first element, if the dimensionality is + * not consistent we won't figure that out here. + */ +function isArray1D(a) { + return !isArrayOrTypedArray(a[0]); +} +exports.isArray1D = isArray1D; + +/* + * Ensures an array has the right amount of storage space. If it doesn't + * exist, it creates an array. If it does exist, it returns it if too + * short or truncates it in-place. + * + * The goal is to just reuse memory to avoid a bit of excessive garbage + * collection. + */ +exports.ensureArray = function(out, n) { + // TODO: typed array support here? This is only used in + // traces/carpet/compute_control_points + if(!isArray(out)) out = []; + + // If too long, truncate. (If too short, it will grow + // automatically so we don't care about that case) + out.length = n; + + return out; +}; + +/* + * TypedArray-compatible concatenation of n arrays + * if all arrays are the same type it will preserve that type, + * otherwise it falls back on Array. + * Also tries to avoid copying, in case one array has zero length + * But never mutates an existing array + */ +exports.concat = function() { + var args = []; + var allArray = true; + var totalLen = 0; + + var _constructor, arg0, i, argi, posi, leni, out, j; + + for(i = 0; i < arguments.length; i++) { + argi = arguments[i]; + leni = argi.length; + if(leni) { + if(arg0) args.push(argi); + else { + arg0 = argi; + posi = leni; + } + + if(isArray(argi)) { + _constructor = false; + } else { + allArray = false; + if(!totalLen) { + _constructor = argi.constructor; + } else if(_constructor !== argi.constructor) { + // TODO: in principle we could upgrade here, + // ie keep typed array but convert all to Float64Array? + _constructor = false; + } + } + + totalLen += leni; + } + } + + if(!totalLen) return []; + if(!args.length) return arg0; + + if(allArray) return arg0.concat.apply(arg0, args); + if(_constructor) { + // matching typed arrays + out = new _constructor(totalLen); + out.set(arg0); + for(i = 0; i < args.length; i++) { + argi = args[i]; + out.set(argi, posi); + posi += argi.length; + } + return out; + } + + // mismatched types or Array + typed + out = new Array(totalLen); + for(j = 0; j < arg0.length; j++) out[j] = arg0[j]; + for(i = 0; i < args.length; i++) { + argi = args[i]; + for(j = 0; j < argi.length; j++) out[posi + j] = argi[j]; + posi += j; + } + return out; +}; + +exports.maxRowLength = function(z) { + return _rowLength(z, Math.max, 0); +}; + +exports.minRowLength = function(z) { + return _rowLength(z, Math.min, Infinity); +}; + +function _rowLength(z, fn, len0) { + if(isArrayOrTypedArray(z)) { + if(isArrayOrTypedArray(z[0])) { + var len = len0; + for(var i = 0; i < z.length; i++) { + len = fn(len, z[i].length); + } + return len; + } else { + return z.length; + } + } + return 0; +} + +},{}],272:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var BADNUM = _dereq_('../constants/numerical').BADNUM; + +// precompile for speed +var JUNK = /^['"%,$#\s']+|[, ]|['"%,$#\s']+$/g; + +/** + * cleanNumber: remove common leading and trailing cruft + * Always returns either a number or BADNUM. + */ +module.exports = function cleanNumber(v) { + if(typeof v === 'string') { + v = v.replace(JUNK, ''); + } + + if(isNumeric(v)) return Number(v); + + return BADNUM; +}; + +},{"../constants/numerical":265,"fast-isnumeric":31}],273:[function(_dereq_,module,exports){ +'use strict'; + +/** + * Clear gl frame (if any). This is a common pattern as + * we usually set `preserveDrawingBuffer: true` during + * gl context creation (e.g. via `reglUtils.prepare`). + * + * @param {DOM node or object} gd : graph div object + */ +module.exports = function clearGlCanvases(gd) { + var fullLayout = gd._fullLayout; + + if(fullLayout._glcanvas && fullLayout._glcanvas.size()) { + fullLayout._glcanvas.each(function(d) { + if(d.regl) d.regl.clear({color: true, depth: true}); + }); + } +}; + +},{}],274:[function(_dereq_,module,exports){ +'use strict'; + +/** + * Clear responsive handlers (if any). + * + * @param {DOM node or object} gd : graph div object + */ +module.exports = function clearResponsive(gd) { + if(gd._responsiveChartHandler) { + window.removeEventListener('resize', gd._responsiveChartHandler); + delete gd._responsiveChartHandler; + } +}; + +},{}],275:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); + +var baseTraceAttrs = _dereq_('../plots/attributes'); +var colorscales = _dereq_('../components/colorscale/scales'); +var Color = _dereq_('../components/color'); +var DESELECTDIM = _dereq_('../constants/interactions').DESELECTDIM; + +var nestedProperty = _dereq_('./nested_property'); +var counterRegex = _dereq_('./regex').counter; +var modHalf = _dereq_('./mod').modHalf; +var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray; + +exports.valObjectMeta = { + data_array: { + // You can use *dflt=[] to force said array to exist though. + coerceFunction: function(v, propOut, dflt) { + // TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also + if(isArrayOrTypedArray(v)) propOut.set(v); + else if(dflt !== undefined) propOut.set(dflt); + } + }, + enumerated: { + coerceFunction: function(v, propOut, dflt, opts) { + if(opts.coerceNumber) v = +v; + if(opts.values.indexOf(v) === -1) propOut.set(dflt); + else propOut.set(v); + }, + validateFunction: function(v, opts) { + if(opts.coerceNumber) v = +v; + + var values = opts.values; + for(var i = 0; i < values.length; i++) { + var k = String(values[i]); + + if((k.charAt(0) === '/' && k.charAt(k.length - 1) === '/')) { + var regex = new RegExp(k.substr(1, k.length - 2)); + if(regex.test(v)) return true; + } else if(v === values[i]) return true; + } + return false; + } + }, + 'boolean': { + coerceFunction: function(v, propOut, dflt) { + if(v === true || v === false) propOut.set(v); + else propOut.set(dflt); + } + }, + number: { + coerceFunction: function(v, propOut, dflt, opts) { + if(!isNumeric(v) || + (opts.min !== undefined && v < opts.min) || + (opts.max !== undefined && v > opts.max)) { + propOut.set(dflt); + } else propOut.set(+v); + } + }, + integer: { + coerceFunction: function(v, propOut, dflt, opts) { + if(v % 1 || !isNumeric(v) || + (opts.min !== undefined && v < opts.min) || + (opts.max !== undefined && v > opts.max)) { + propOut.set(dflt); + } else propOut.set(+v); + } + }, + string: { + // TODO 'values shouldn't be in there (edge case: 'dash' in Scatter) + coerceFunction: function(v, propOut, dflt, opts) { + if(typeof v !== 'string') { + var okToCoerce = (typeof v === 'number'); + + if(opts.strict === true || !okToCoerce) propOut.set(dflt); + else propOut.set(String(v)); + } else if(opts.noBlank && !v) propOut.set(dflt); + else propOut.set(v); + } + }, + color: { + coerceFunction: function(v, propOut, dflt) { + if(tinycolor(v).isValid()) propOut.set(v); + else propOut.set(dflt); + } + }, + colorlist: { + coerceFunction: function(v, propOut, dflt) { + function isColor(color) { + return tinycolor(color).isValid(); + } + if(!Array.isArray(v) || !v.length) propOut.set(dflt); + else if(v.every(isColor)) propOut.set(v); + else propOut.set(dflt); + } + }, + colorscale: { + coerceFunction: function(v, propOut, dflt) { + propOut.set(colorscales.get(v, dflt)); + } + }, + angle: { + coerceFunction: function(v, propOut, dflt) { + if(v === 'auto') propOut.set('auto'); + else if(!isNumeric(v)) propOut.set(dflt); + else propOut.set(modHalf(+v, 360)); + } + }, + subplotid: { + coerceFunction: function(v, propOut, dflt, opts) { + var regex = opts.regex || counterRegex(dflt); + if(typeof v === 'string' && regex.test(v)) { + propOut.set(v); + return; + } + propOut.set(dflt); + }, + validateFunction: function(v, opts) { + var dflt = opts.dflt; + + if(v === dflt) return true; + if(typeof v !== 'string') return false; + if(counterRegex(dflt).test(v)) return true; + + return false; + } + }, + flaglist: { + coerceFunction: function(v, propOut, dflt, opts) { + if(typeof v !== 'string') { + propOut.set(dflt); + return; + } + if((opts.extras || []).indexOf(v) !== -1) { + propOut.set(v); + return; + } + var vParts = v.split('+'); + var i = 0; + while(i < vParts.length) { + var vi = vParts[i]; + if(opts.flags.indexOf(vi) === -1 || vParts.indexOf(vi) < i) { + vParts.splice(i, 1); + } else i++; + } + if(!vParts.length) propOut.set(dflt); + else propOut.set(vParts.join('+')); + } + }, + any: { + coerceFunction: function(v, propOut, dflt) { + if(v === undefined) propOut.set(dflt); + else propOut.set(v); + } + }, + info_array: { + // set `dimensions=2` for a 2D array or '1-2' for either + // `items` may be a single object instead of an array, in which case + // `freeLength` must be true. + // if `dimensions='1-2'` and items is a 1D array, then the value can + // either be a matching 1D array or an array of such matching 1D arrays + coerceFunction: function(v, propOut, dflt, opts) { + // simplified coerce function just for array items + function coercePart(v, opts, dflt) { + var out; + var propPart = {set: function(v) { out = v; }}; + + if(dflt === undefined) dflt = opts.dflt; + + exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts); + + return out; + } + + var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && Array.isArray(v[0])); + + if(!Array.isArray(v)) { + propOut.set(dflt); + return; + } + + var items = opts.items; + var vOut = []; + var arrayItems = Array.isArray(items); + var arrayItems2D = arrayItems && twoD && Array.isArray(items[0]); + var innerItemsOnly = twoD && arrayItems && !arrayItems2D; + var len = (arrayItems && !innerItemsOnly) ? items.length : v.length; + + var i, j, row, item, len2, vNew; + + dflt = Array.isArray(dflt) ? dflt : []; + + if(twoD) { + for(i = 0; i < len; i++) { + vOut[i] = []; + row = Array.isArray(v[i]) ? v[i] : []; + if(innerItemsOnly) len2 = items.length; + else if(arrayItems) len2 = items[i].length; + else len2 = row.length; + + for(j = 0; j < len2; j++) { + if(innerItemsOnly) item = items[j]; + else if(arrayItems) item = items[i][j]; + else item = items; + + vNew = coercePart(row[j], item, (dflt[i] || [])[j]); + if(vNew !== undefined) vOut[i][j] = vNew; + } + } + } else { + for(i = 0; i < len; i++) { + vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]); + if(vNew !== undefined) vOut[i] = vNew; + } + } + + propOut.set(vOut); + }, + validateFunction: function(v, opts) { + if(!Array.isArray(v)) return false; + + var items = opts.items; + var arrayItems = Array.isArray(items); + var twoD = opts.dimensions === 2; + + // when free length is off, input and declared lengths must match + if(!opts.freeLength && v.length !== items.length) return false; + + // valid when all input items are valid + for(var i = 0; i < v.length; i++) { + if(twoD) { + if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) { + return false; + } + for(var j = 0; j < v[i].length; j++) { + if(!validate(v[i][j], arrayItems ? items[i][j] : items)) { + return false; + } + } + } else if(!validate(v[i], arrayItems ? items[i] : items)) return false; + } + + return true; + } + } +}; + +/** + * Ensures that container[attribute] has a valid value. + * + * attributes[attribute] is an object with possible keys: + * - valType: data_array, enumerated, boolean, ... as in valObjectMeta + * - values: (enumerated only) array of allowed vals + * - min, max: (number, integer only) inclusive bounds on allowed vals + * either or both may be omitted + * - dflt: if attribute is invalid or missing, use this default + * if dflt is provided as an argument to lib.coerce it takes precedence + * as a convenience, returns the value it finally set + */ +exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) { + var opts = nestedProperty(attributes, attribute).get(); + var propIn = nestedProperty(containerIn, attribute); + var propOut = nestedProperty(containerOut, attribute); + var v = propIn.get(); + + var template = containerOut._template; + if(v === undefined && template) { + v = nestedProperty(template, attribute).get(); + // already used the template value, so short-circuit the second check + template = 0; + } + + if(dflt === undefined) dflt = opts.dflt; + + /** + * arrayOk: value MAY be an array, then we do no value checking + * at this point, because it can be more complicated than the + * individual form (eg. some array vals can be numbers, even if the + * single values must be color strings) + */ + if(opts.arrayOk && isArrayOrTypedArray(v)) { + propOut.set(v); + return v; + } + + var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction; + coerceFunction(v, propOut, dflt, opts); + + var out = propOut.get(); + // in case v was provided but invalid, try the template again so it still + // overrides the regular default + if(template && out === dflt && !validate(v, opts)) { + v = nestedProperty(template, attribute).get(); + coerceFunction(v, propOut, dflt, opts); + out = propOut.get(); + } + return out; +}; + +/** + * Variation on coerce + * + * Uses coerce to get attribute value if user input is valid, + * returns attribute default if user input it not valid or + * returns false if there is no user input. + */ +exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) { + var propIn = nestedProperty(containerIn, attribute); + var propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt); + var valIn = propIn.get(); + + return (valIn !== undefined && valIn !== null) ? propOut : false; +}; + +/* + * Shortcut to coerce the three font attributes + * + * 'coerce' is a lib.coerce wrapper with implied first three arguments + */ +exports.coerceFont = function(coerce, attr, dfltObj) { + var out = {}; + + dfltObj = dfltObj || {}; + + out.family = coerce(attr + '.family', dfltObj.family); + out.size = coerce(attr + '.size', dfltObj.size); + out.color = coerce(attr + '.color', dfltObj.color); + + return out; +}; + +/* + * Shortcut to coerce the pattern attributes + */ +exports.coercePattern = function(coerce, attr, markerColor, hasMarkerColorscale) { + var shape = coerce(attr + '.shape'); + if(shape) { + coerce(attr + '.solidity'); + coerce(attr + '.size'); + var fillmode = coerce(attr + '.fillmode'); + var isOverlay = fillmode === 'overlay'; + + if(!hasMarkerColorscale) { + var bgcolor = coerce(attr + '.bgcolor', isOverlay ? + markerColor : + undefined + ); + + coerce(attr + '.fgcolor', isOverlay ? + Color.contrast(bgcolor) : + markerColor + ); + } + + coerce(attr + '.fgopacity', isOverlay ? + 0.5 : + 1 + ); + } +}; + +/** Coerce shortcut for 'hoverinfo' + * handling 1-vs-multi-trace dflt logic + * + * @param {object} traceIn : user trace object + * @param {object} traceOut : full trace object (requires _module ref) + * @param {object} layoutOut : full layout object (require _dataLength ref) + * @return {any} : the coerced value + */ +exports.coerceHoverinfo = function(traceIn, traceOut, layoutOut) { + var moduleAttrs = traceOut._module.attributes; + var attrs = moduleAttrs.hoverinfo ? moduleAttrs : baseTraceAttrs; + + var valObj = attrs.hoverinfo; + var dflt; + + if(layoutOut._dataLength === 1) { + var flags = valObj.dflt === 'all' ? + valObj.flags.slice() : + valObj.dflt.split('+'); + + flags.splice(flags.indexOf('name'), 1); + dflt = flags.join('+'); + } + + return exports.coerce(traceIn, traceOut, attrs, 'hoverinfo', dflt); +}; + +/** Coerce shortcut for [un]selected.marker.opacity, + * which has special default logic, to ensure that it corresponds to the + * default selection behavior while allowing to be overtaken by any other + * [un]selected attribute. + * + * N.B. This must be called *after* coercing all the other [un]selected attrs, + * to give the intended result. + * + * @param {object} traceOut : fullData item + * @param {function} coerce : lib.coerce wrapper with implied first three arguments + */ +exports.coerceSelectionMarkerOpacity = function(traceOut, coerce) { + if(!traceOut.marker) return; + + var mo = traceOut.marker.opacity; + // you can still have a `marker` container with no markers if there's text + if(mo === undefined) return; + + var smoDflt; + var usmoDflt; + + // Don't give [un]selected.marker.opacity a default value if + // marker.opacity is an array: handle this during style step. + // + // Only give [un]selected.marker.opacity a default value if you don't + // set any other [un]selected attributes. + if(!isArrayOrTypedArray(mo) && !traceOut.selected && !traceOut.unselected) { + smoDflt = mo; + usmoDflt = DESELECTDIM * mo; + } + + coerce('selected.marker.opacity', smoDflt); + coerce('unselected.marker.opacity', usmoDflt); +}; + +function validate(value, opts) { + var valObjectDef = exports.valObjectMeta[opts.valType]; + + if(opts.arrayOk && isArrayOrTypedArray(value)) return true; + + if(valObjectDef.validateFunction) { + return valObjectDef.validateFunction(value, opts); + } + + var failed = {}; + var out = failed; + var propMock = { set: function(v) { out = v; } }; + + // 'failed' just something mutable that won't be === anything else + + valObjectDef.coerceFunction(value, propMock, failed, opts); + return out !== failed; +} +exports.validate = validate; + +},{"../components/color":155,"../components/colorscale/scales":170,"../constants/interactions":264,"../plots/attributes":327,"./array":271,"./mod":292,"./nested_property":293,"./regex":301,"fast-isnumeric":31,"tinycolor2":119}],276:[function(_dereq_,module,exports){ +'use strict'; + +var timeFormat = _dereq_('d3-time-format').timeFormat; +var isNumeric = _dereq_('fast-isnumeric'); + +var Loggers = _dereq_('./loggers'); +var mod = _dereq_('./mod').mod; + +var constants = _dereq_('../constants/numerical'); +var BADNUM = constants.BADNUM; +var ONEDAY = constants.ONEDAY; +var ONEHOUR = constants.ONEHOUR; +var ONEMIN = constants.ONEMIN; +var ONESEC = constants.ONESEC; +var EPOCHJD = constants.EPOCHJD; + +var Registry = _dereq_('../registry'); + +var utcFormat = _dereq_('d3-time-format').utcFormat; + +var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d(:?\d\d)?)?)?)?)?)?\s*$/m; +// special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months +var DATETIME_REGEXP_CN = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\di?)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d(:?\d\d)?)?)?)?)?)?\s*$/m; + +// for 2-digit years, the first year we map them onto +var YFIRST = new Date().getFullYear() - 70; + +function isWorldCalendar(calendar) { + return ( + calendar && + Registry.componentsRegistry.calendars && + typeof calendar === 'string' && calendar !== 'gregorian' + ); +} + +/* + * dateTick0: get the canonical tick for this calendar + * + * integer weekdays : Saturday: 0, Sunday: 1, Monday: 2, etc. + */ +exports.dateTick0 = function(calendar, dayOfWeek) { + var tick0 = _dateTick0(calendar, !!dayOfWeek); + if(dayOfWeek < 2) return tick0; + + var v = exports.dateTime2ms(tick0, calendar); + v += ONEDAY * (dayOfWeek - 1); // shift Sunday to Monday, etc. + return exports.ms2DateTime(v, 0, calendar); +}; + +/* + * _dateTick0: get the canonical tick for this calendar + * + * bool sunday is for week ticks, shift it to a Sunday. + */ +function _dateTick0(calendar, sunday) { + if(isWorldCalendar(calendar)) { + return sunday ? + Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] : + Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar]; + } else { + return sunday ? '2000-01-02' : '2000-01-01'; + } +} + +/* + * dfltRange: for each calendar, give a valid default range + */ +exports.dfltRange = function(calendar) { + if(isWorldCalendar(calendar)) { + return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar]; + } else { + return ['2000-01-01', '2001-01-01']; + } +}; + +// is an object a javascript date? +exports.isJSDate = function(v) { + return typeof v === 'object' && v !== null && typeof v.getTime === 'function'; +}; + +// The absolute limits of our date-time system +// This is a little weird: we use MIN_MS and MAX_MS in dateTime2ms +// but we use dateTime2ms to calculate them (after defining it!) +var MIN_MS, MAX_MS; + +/** + * dateTime2ms - turn a date object or string s into milliseconds + * (relative to 1970-01-01, per javascript standard) + * optional calendar (string) to use a non-gregorian calendar + * + * Returns BADNUM if it doesn't find a date + * + * strings should have the form: + * + * -?YYYY-mm-ddHH:MM:SS.sss? + * + * : space (our normal standard) or T or t (ISO-8601) + * : Z, z, [+\-]HH:?MM or [+\-]HH and we THROW IT AWAY + * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6 + * and 4.2.5.1 Difference between local time and UTC of day (ISO-8601) + * but we allow it even with a space as the separator + * + * May truncate after any full field, and sss can be any length + * even >3 digits, though javascript dates truncate to milliseconds, + * we keep as much as javascript numeric precision can hold, but we only + * report back up to 100 microsecond precision, because most dates support + * this precision (close to 1970 support more, very far away support less) + * + * Expanded to support negative years to -9999 but you must always + * give 4 digits, except for 2-digit positive years which we assume are + * near the present time. + * Note that we follow ISO 8601:2004: there *is* a year 0, which + * is 1BC/BCE, and -1===2BC etc. + * + * World calendars: not all of these *have* agreed extensions to this full range, + * if you have another calendar system but want a date range outside its validity, + * you can use a gregorian date string prefixed with 'G' or 'g'. + * + * Where to cut off 2-digit years between 1900s and 2000s? + * from https://docs.microsoft.com/en-us/office/troubleshoot/excel/two-digit-year-numbers#the-2029-rule: + * 1930-2029 (the most retro of all...) + * but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')): + * 1950-2049 + * by Java, from http://stackoverflow.com/questions/2024273/: + * now-80 - now+19 + * or FileMaker Pro, from + * https://fmhelp.filemaker.com/help/18/fmp/en/index.html#page/FMP_Help/dates-with-two-digit-years.html: + * now-70 - now+29 + * but python strptime etc, via + * http://docs.python.org/py3k/library/time.html: + * 1969-2068 (super forward-looking, but static, not sliding!) + * + * lets go with now-70 to now+29, and if anyone runs into this problem + * they can learn the hard way not to use 2-digit years, as no choice we + * make now will cover all possibilities. mostly this will all be taken + * care of in initial parsing, should only be an issue for hand-entered data + * currently (2016) this range is: + * 1946-2045 + */ +exports.dateTime2ms = function(s, calendar) { + // first check if s is a date object + if(exports.isJSDate(s)) { + // Convert to the UTC milliseconds that give the same + // hours as this date has in the local timezone + var tzOffset = s.getTimezoneOffset() * ONEMIN; + var offsetTweak = (s.getUTCMinutes() - s.getMinutes()) * ONEMIN + + (s.getUTCSeconds() - s.getSeconds()) * ONESEC + + (s.getUTCMilliseconds() - s.getMilliseconds()); + + if(offsetTweak) { + var comb = 3 * ONEMIN; + tzOffset = tzOffset - comb / 2 + mod(offsetTweak - tzOffset + comb / 2, comb); + } + s = Number(s) - tzOffset; + if(s >= MIN_MS && s <= MAX_MS) return s; + return BADNUM; + } + // otherwise only accept strings and numbers + if(typeof s !== 'string' && typeof s !== 'number') return BADNUM; + + s = String(s); + + var isWorld = isWorldCalendar(calendar); + + // to handle out-of-range dates in international calendars, accept + // 'G' as a prefix to force the built-in gregorian calendar. + var s0 = s.charAt(0); + if(isWorld && (s0 === 'G' || s0 === 'g')) { + s = s.substr(1); + calendar = ''; + } + + var isChinese = isWorld && calendar.substr(0, 7) === 'chinese'; + + var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP); + if(!match) return BADNUM; + var y = match[1]; + var m = match[3] || '1'; + var d = Number(match[5] || 1); + var H = Number(match[7] || 0); + var M = Number(match[9] || 0); + var S = Number(match[11] || 0); + + if(isWorld) { + // disallow 2-digit years for world calendars + if(y.length === 2) return BADNUM; + y = Number(y); + + var cDate; + try { + var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar); + if(isChinese) { + var isIntercalary = m.charAt(m.length - 1) === 'i'; + m = parseInt(m, 10); + cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d); + } else { + cDate = calInstance.newDate(y, Number(m), d); + } + } catch(e) { return BADNUM; } // Invalid ... date + + if(!cDate) return BADNUM; + + return ((cDate.toJD() - EPOCHJD) * ONEDAY) + + (H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC); + } + + if(y.length === 2) { + y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST; + } else y = Number(y); + + // new Date uses months from 0; subtract 1 here just so we + // don't have to do it again during the validity test below + m -= 1; + + // javascript takes new Date(0..99,m,d) to mean 1900-1999, so + // to support years 0-99 we need to use setFullYear explicitly + // Note that 2000 is a leap year. + var date = new Date(Date.UTC(2000, m, d, H, M)); + date.setUTCFullYear(y); + + if(date.getUTCMonth() !== m) return BADNUM; + if(date.getUTCDate() !== d) return BADNUM; + + return date.getTime() + S * ONESEC; +}; + +MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999'); +MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999'); + +// is string s a date? (see above) +exports.isDateTime = function(s, calendar) { + return (exports.dateTime2ms(s, calendar) !== BADNUM); +}; + +// pad a number with zeroes, to given # of digits before the decimal point +function lpad(val, digits) { + return String(val + Math.pow(10, digits)).substr(1); +} + +/** + * Turn ms into string of the form YYYY-mm-dd HH:MM:SS.ssss + * Crop any trailing zeros in time, except never stop right after hours + * (we could choose to crop '-01' from date too but for now we always + * show the whole date) + * Optional range r is the data range that applies, also in ms. + * If rng is big, the later parts of time will be omitted + */ +var NINETYDAYS = 90 * ONEDAY; +var THREEHOURS = 3 * ONEHOUR; +var FIVEMIN = 5 * ONEMIN; +exports.ms2DateTime = function(ms, r, calendar) { + if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM; + + if(!r) r = 0; + + var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10); + var msRounded = Math.round(ms - msecTenths / 10); + var dateStr, h, m, s, msec10, d; + + if(isWorldCalendar(calendar)) { + var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD; + var timeMs = Math.floor(mod(ms, ONEDAY)); + try { + dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar) + .fromJD(dateJD).formatDate('yyyy-mm-dd'); + } catch(e) { + // invalid date in this calendar - fall back to Gyyyy-mm-dd + dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded)); + } + + // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does + // other things for a few calendars, so we can't trust it. Just pad + // it manually (after the '-' if there is one) + if(dateStr.charAt(0) === '-') { + while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1); + } else { + while(dateStr.length < 10) dateStr = '0' + dateStr; + } + + // TODO: if this is faster, we could use this block for extracting + // the time components of regular gregorian too + h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0; + m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0; + s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0; + msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0; + } else { + d = new Date(msRounded); + + dateStr = utcFormat('%Y-%m-%d')(d); + + // <90 days: add hours and minutes - never *only* add hours + h = (r < NINETYDAYS) ? d.getUTCHours() : 0; + m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0; + // <3 hours: add seconds + s = (r < THREEHOURS) ? d.getUTCSeconds() : 0; + // <5 minutes: add ms (plus one extra digit, this is msec*10) + msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0; + } + + return includeTime(dateStr, h, m, s, msec10); +}; + +// For converting old-style milliseconds to date strings, +// we use the local timezone rather than UTC like we use +// everywhere else, both for backward compatibility and +// because that's how people mostly use javasript date objects. +// Clip one extra day off our date range though so we can't get +// thrown beyond the range by the timezone shift. +exports.ms2DateTimeLocal = function(ms) { + if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM; + + var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10); + var d = new Date(Math.round(ms - msecTenths / 10)); + var dateStr = timeFormat('%Y-%m-%d')(d); + var h = d.getHours(); + var m = d.getMinutes(); + var s = d.getSeconds(); + var msec10 = d.getUTCMilliseconds() * 10 + msecTenths; + + return includeTime(dateStr, h, m, s, msec10); +}; + +function includeTime(dateStr, h, m, s, msec10) { + // include each part that has nonzero data in or after it + if(h || m || s || msec10) { + dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2); + if(s || msec10) { + dateStr += ':' + lpad(s, 2); + if(msec10) { + var digits = 4; + while(msec10 % 10 === 0) { + digits -= 1; + msec10 /= 10; + } + dateStr += '.' + lpad(msec10, digits); + } + } + } + return dateStr; +} + +// normalize date format to date string, in case it starts as +// a Date object or milliseconds +// optional dflt is the return value if cleaning fails +exports.cleanDate = function(v, dflt, calendar) { + // let us use cleanDate to provide a missing default without an error + if(v === BADNUM) return dflt; + if(exports.isJSDate(v) || (typeof v === 'number' && isFinite(v))) { + // do not allow milliseconds (old) or jsdate objects (inherently + // described as gregorian dates) with world calendars + if(isWorldCalendar(calendar)) { + Loggers.error('JS Dates and milliseconds are incompatible with world calendars', v); + return dflt; + } + + // NOTE: if someone puts in a year as a number rather than a string, + // this will mistakenly convert it thinking it's milliseconds from 1970 + // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds + v = exports.ms2DateTimeLocal(+v); + if(!v && dflt !== undefined) return dflt; + } else if(!exports.isDateTime(v, calendar)) { + Loggers.error('unrecognized date', v); + return dflt; + } + return v; +}; + +/* + * Date formatting for ticks and hovertext + */ + +/* + * modDateFormat: Support world calendars, and add two items to + * d3's vocabulary: + * %{n}f where n is the max number of digits of fractional seconds + * %h formats: half of the year as a decimal number [1,2] + */ +var fracMatch = /%\d?f/g; +var halfYearMatch = /%h/g; +var quarterToHalfYear = { + '1': '1', + '2': '1', + '3': '2', + '4': '2', +}; +function modDateFormat(fmt, x, formatter, calendar) { + fmt = fmt.replace(fracMatch, function(match) { + var digits = Math.min(+(match.charAt(1)) || 6, 6); + var fracSecs = ((x / 1000 % 1) + 2) + .toFixed(digits) + .substr(2).replace(/0+$/, '') || '0'; + return fracSecs; + }); + + var d = new Date(Math.floor(x + 0.05)); + + fmt = fmt.replace(halfYearMatch, function() { + return quarterToHalfYear[formatter('%q')(d)]; + }); + + if(isWorldCalendar(calendar)) { + try { + fmt = Registry.getComponentMethod('calendars', 'worldCalFmt')(fmt, x, calendar); + } catch(e) { + return 'Invalid'; + } + } + return formatter(fmt)(d); +} + +/* + * formatTime: create a time string from: + * x: milliseconds + * tr: tickround ('M', 'S', or # digits) + * only supports UTC times (where every day is 24 hours and 0 is at midnight) + */ +var MAXSECONDS = [59, 59.9, 59.99, 59.999, 59.9999]; +function formatTime(x, tr) { + var timePart = mod(x + 0.05, ONEDAY); + + var timeStr = lpad(Math.floor(timePart / ONEHOUR), 2) + ':' + + lpad(mod(Math.floor(timePart / ONEMIN), 60), 2); + + if(tr !== 'M') { + if(!isNumeric(tr)) tr = 0; // should only be 'S' + + /* + * this is a weird one - and shouldn't come up unless people + * monkey with tick0 in weird ways, but we need to do something! + * IN PARTICULAR we had better not display garbage (see below) + * for numbers we always round to the nearest increment of the + * precision we're showing, and this seems like the right way to + * handle seconds and milliseconds, as they have a decimal point + * and people will interpret that to mean rounding like numbers. + * but for larger increments we floor the value: it's always + * 2013 until the ball drops on the new year. We could argue about + * which field it is where we start rounding (should 12:08:59 + * round to 12:09 if we're stopping at minutes?) but for now I'll + * say we round seconds but floor everything else. BUT that means + * we need to never round up to 60 seconds, ie 23:59:60 + */ + var sec = Math.min(mod(x / ONESEC, 60), MAXSECONDS[tr]); + + var secStr = (100 + sec).toFixed(tr).substr(1); + if(tr > 0) { + secStr = secStr.replace(/0+$/, '').replace(/[\.]$/, ''); + } + + timeStr += ':' + secStr; + } + return timeStr; +} + +/* + * formatDate: turn a date into tick or hover label text. + * + * x: milliseconds, the value to convert + * fmt: optional, an explicit format string (d3 format, even for world calendars) + * tr: tickround ('y', 'm', 'd', 'M', 'S', or # digits) + * used if no explicit fmt is provided + * formatter: locale-aware d3 date formatter for standard gregorian calendars + * should be the result of exports.getD3DateFormat(gd) + * calendar: optional string, the world calendar system to use + * + * returns the date/time as a string, potentially with the leading portion + * on a separate line (after '\n') + * Note that this means if you provide an explicit format which includes '\n' + * the axis may choose to strip things after it when they don't change from + * one tick to the next (as it does with automatic formatting) + */ +exports.formatDate = function(x, fmt, tr, formatter, calendar, extraFormat) { + calendar = isWorldCalendar(calendar) && calendar; + + if(!fmt) { + if(tr === 'y') fmt = extraFormat.year; + else if(tr === 'm') fmt = extraFormat.month; + else if(tr === 'd') { + fmt = extraFormat.dayMonth + '\n' + extraFormat.year; + } else { + return formatTime(x, tr) + '\n' + modDateFormat(extraFormat.dayMonthYear, x, formatter, calendar); + } + } + + return modDateFormat(fmt, x, formatter, calendar); +}; + +/* + * incrementMonth: make a new milliseconds value from the given one, + * having changed the month + * + * special case for world calendars: multiples of 12 are treated as years, + * even for calendar systems that don't have (always or ever) 12 months/year + * TODO: perhaps we need a different code for year increments to support this? + * + * ms (number): the initial millisecond value + * dMonth (int): the (signed) number of months to shift + * calendar (string): the calendar system to use + * + * changing month does not (and CANNOT) always preserve day, since + * months have different lengths. The worst example of this is: + * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3 + * + * But we want to be able to iterate over the last day of each month, + * regardless of what its number is. + * So shift 3 days forward, THEN set the new month, then unshift: + * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ... + * + * Note that odd behavior still exists if you start from the 26th-28th: + * 1/28 -> 2/28 -> 3/31 + * but at least you can't shift any dates into the wrong month, + * and ticks on these days incrementing by month would be very unusual + */ +var THREEDAYS = 3 * ONEDAY; +exports.incrementMonth = function(ms, dMonth, calendar) { + calendar = isWorldCalendar(calendar) && calendar; + + // pull time out and operate on pure dates, then add time back at the end + // this gives maximum precision - not that we *normally* care if we're + // incrementing by month, but better to be safe! + var timeMs = mod(ms, ONEDAY); + ms = Math.round(ms - timeMs); + + if(calendar) { + try { + var dateJD = Math.round(ms / ONEDAY) + EPOCHJD; + var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar); + var cDate = calInstance.fromJD(dateJD); + + if(dMonth % 12) calInstance.add(cDate, dMonth, 'm'); + else calInstance.add(cDate, dMonth / 12, 'y'); + + return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs; + } catch(e) { + Loggers.error('invalid ms ' + ms + ' in calendar ' + calendar); + // then keep going in gregorian even though the result will be 'Invalid' + } + } + + var y = new Date(ms + THREEDAYS); + return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS; +}; + +/* + * findExactDates: what fraction of data is exact days, months, or years? + * + * data: array of millisecond values + * calendar (string) the calendar to test against + */ +exports.findExactDates = function(data, calendar) { + var exactYears = 0; + var exactMonths = 0; + var exactDays = 0; + var blankCount = 0; + var d; + var di; + + var calInstance = ( + isWorldCalendar(calendar) && + Registry.getComponentMethod('calendars', 'getCal')(calendar) + ); + + for(var i = 0; i < data.length; i++) { + di = data[i]; + + // not date data at all + if(!isNumeric(di)) { + blankCount ++; + continue; + } + + // not an exact date + if(di % ONEDAY) continue; + + if(calInstance) { + try { + d = calInstance.fromJD(di / ONEDAY + EPOCHJD); + if(d.day() === 1) { + if(d.month() === 1) exactYears++; + else exactMonths++; + } else exactDays++; + } catch(e) { + // invalid date in this calendar - ignore it here. + } + } else { + d = new Date(di); + if(d.getUTCDate() === 1) { + if(d.getUTCMonth() === 0) exactYears++; + else exactMonths++; + } else exactDays++; + } + } + exactMonths += exactYears; + exactDays += exactMonths; + + var dataCount = data.length - blankCount; + + return { + exactYears: exactYears / dataCount, + exactMonths: exactMonths / dataCount, + exactDays: exactDays / dataCount + }; +}; + +},{"../constants/numerical":265,"../registry":373,"./loggers":289,"./mod":292,"d3-time-format":29,"fast-isnumeric":31}],277:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var loggers = _dereq_('./loggers'); +var matrix = _dereq_('./matrix'); +var mat4X4 = _dereq_('gl-mat4'); + +/** + * Allow referencing a graph DOM element either directly + * or by its id string + * + * @param {HTMLDivElement|string} gd: a graph element or its id + * + * @returns {HTMLDivElement} the DOM element of the graph + */ +function getGraphDiv(gd) { + var gdElement; + + if(typeof gd === 'string') { + gdElement = document.getElementById(gd); + + if(gdElement === null) { + throw new Error('No DOM element with id \'' + gd + '\' exists on the page.'); + } + + return gdElement; + } else if(gd === null || gd === undefined) { + throw new Error('DOM element provided is null or undefined'); + } + + // otherwise assume that gd is a DOM element + return gd; +} + +function isPlotDiv(el) { + var el3 = d3.select(el); + return el3.node() instanceof HTMLElement && + el3.size() && + el3.classed('js-plotly-plot'); +} + +function removeElement(el) { + var elParent = el && el.parentNode; + if(elParent) elParent.removeChild(el); +} + +/** + * for dynamically adding style rules + * makes one stylesheet that contains all rules added + * by all calls to this function + */ +function addStyleRule(selector, styleString) { + addRelatedStyleRule('global', selector, styleString); +} + +/** + * for dynamically adding style rules + * to a stylesheet uniquely identified by a uid + */ +function addRelatedStyleRule(uid, selector, styleString) { + var id = 'plotly.js-style-' + uid; + var style = document.getElementById(id); + if(!style) { + style = document.createElement('style'); + style.setAttribute('id', id); + // WebKit hack :( + style.appendChild(document.createTextNode('')); + document.head.appendChild(style); + } + var styleSheet = style.sheet; + + if(styleSheet.insertRule) { + styleSheet.insertRule(selector + '{' + styleString + '}', 0); + } else if(styleSheet.addRule) { + styleSheet.addRule(selector, styleString, 0); + } else loggers.warn('addStyleRule failed'); +} + +/** + * to remove from the page a stylesheet identified by a given uid + */ +function deleteRelatedStyleRule(uid) { + var id = 'plotly.js-style-' + uid; + var style = document.getElementById(id); + if(style) removeElement(style); +} + +function getFullTransformMatrix(element) { + var allElements = getElementAndAncestors(element); + // the identity matrix + var out = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; + allElements.forEach(function(e) { + var t = getElementTransformMatrix(e); + if(t) { + var m = matrix.convertCssMatrix(t); + out = mat4X4.multiply(out, out, m); + } + }); + return out; +} + +/** + * extracts and parses the 2d css style transform matrix from some element + */ +function getElementTransformMatrix(element) { + var style = window.getComputedStyle(element, null); + var transform = ( + style.getPropertyValue('-webkit-transform') || + style.getPropertyValue('-moz-transform') || + style.getPropertyValue('-ms-transform') || + style.getPropertyValue('-o-transform') || + style.getPropertyValue('transform') + ); + + if(transform === 'none') return null; + // the transform is a string in the form of matrix(a, b, ...) or matrix3d(...) + return transform + .replace('matrix', '') + .replace('3d', '') + .slice(1, -1) + .split(',') + .map(function(n) { return +n; }); +} +/** + * retrieve all DOM elements that are ancestors of the specified one (including itself) + */ +function getElementAndAncestors(element) { + var allElements = []; + while(isTransformableElement(element)) { + allElements.push(element); + element = element.parentNode; + } + return allElements; +} + +function isTransformableElement(element) { + return element && (element instanceof Element || element instanceof HTMLElement); +} + +function equalDomRects(a, b) { + return ( + a && b && + a.x === b.x && + a.y === b.y && + a.top === b.top && + a.left === b.left && + a.right === b.right && + a.bottom === b.bottom + ); +} + +module.exports = { + getGraphDiv: getGraphDiv, + isPlotDiv: isPlotDiv, + removeElement: removeElement, + addStyleRule: addStyleRule, + addRelatedStyleRule: addRelatedStyleRule, + deleteRelatedStyleRule: deleteRelatedStyleRule, + getFullTransformMatrix: getFullTransformMatrix, + getElementTransformMatrix: getElementTransformMatrix, + getElementAndAncestors: getElementAndAncestors, + equalDomRects: equalDomRects +}; + +},{"./loggers":289,"./matrix":291,"@plotly/d3":20,"gl-mat4":47}],278:[function(_dereq_,module,exports){ +'use strict'; + +/* global jQuery:false */ + +var EventEmitter = _dereq_('events').EventEmitter; + +var Events = { + + init: function(plotObj) { + /* + * If we have already instantiated an emitter for this plot + * return early. + */ + if(plotObj._ev instanceof EventEmitter) return plotObj; + + var ev = new EventEmitter(); + var internalEv = new EventEmitter(); + + /* + * Assign to plot._ev while we still live in a land + * where plot is a DOM element with stuff attached to it. + * In the future we can make plot the event emitter itself. + */ + plotObj._ev = ev; + + /* + * Create a second event handler that will manage events *internally*. + * This allows parts of plotly to respond to thing like relayout without + * having to use the user-facing event handler. They cannot peacefully + * coexist on the same handler because a user invoking + * plotObj.removeAllListeners() would detach internal events, breaking + * plotly. + */ + plotObj._internalEv = internalEv; + + /* + * Assign bound methods from the ev to the plot object. These methods + * will reference the 'this' of plot._ev even though they are methods + * of plot. This will keep the event machinery away from the plot object + * which currently is often a DOM element but presents an API that will + * continue to function when plot becomes an emitter. Not all EventEmitter + * methods have been bound to `plot` as some do not currently add value to + * the Plotly event API. + */ + plotObj.on = ev.on.bind(ev); + plotObj.once = ev.once.bind(ev); + plotObj.removeListener = ev.removeListener.bind(ev); + plotObj.removeAllListeners = ev.removeAllListeners.bind(ev); + + /* + * Create functions for managing internal events. These are *only* triggered + * by the mirroring of external events via the emit function. + */ + plotObj._internalOn = internalEv.on.bind(internalEv); + plotObj._internalOnce = internalEv.once.bind(internalEv); + plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv); + plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv); + + /* + * We must wrap emit to continue to support JQuery events. The idea + * is to check to see if the user is using JQuery events, if they are + * we emit JQuery events to trigger user handlers as well as the EventEmitter + * events. + */ + plotObj.emit = function(event, data) { + if(typeof jQuery !== 'undefined') { + jQuery(plotObj).trigger(event, data); + } + + ev.emit(event, data); + internalEv.emit(event, data); + }; + + return plotObj; + }, + + /* + * This function behaves like jQuery's triggerHandler. It calls + * all handlers for a particular event and returns the return value + * of the LAST handler. This function also triggers jQuery's + * triggerHandler for backwards compatibility. + */ + triggerHandler: function(plotObj, event, data) { + var jQueryHandlerValue; + var nodeEventHandlerValue; + + /* + * If jQuery exists run all its handlers for this event and + * collect the return value of the LAST handler function + */ + if(typeof jQuery !== 'undefined') { + jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data); + } + + /* + * Now run all the node style event handlers + */ + var ev = plotObj._ev; + if(!ev) return jQueryHandlerValue; + + var handlers = ev._events[event]; + if(!handlers) return jQueryHandlerValue; + + // making sure 'this' is the EventEmitter instance + function apply(handler) { + // The 'once' case, we can't just call handler() as we need + // the return value here. So, + // - remove handler + // - call listener and grab return value! + // - stash 'fired' key to not call handler twice + if(handler.listener) { + ev.removeListener(event, handler.listener); + if(!handler.fired) { + handler.fired = true; + return handler.listener.apply(ev, [data]); + } + } else { + return handler.apply(ev, [data]); + } + } + + // handlers can be function or an array of functions + handlers = Array.isArray(handlers) ? handlers : [handlers]; + + var i; + for(i = 0; i < handlers.length - 1; i++) { + apply(handlers[i]); + } + // now call the final handler and collect its value + nodeEventHandlerValue = apply(handlers[i]); + + /* + * Return either the jQuery handler value if it exists or the + * nodeEventHandler value. jQuery event value supersedes nodejs + * events for backwards compatibility reasons. + */ + return jQueryHandlerValue !== undefined ? + jQueryHandlerValue : + nodeEventHandlerValue; + }, + + purge: function(plotObj) { + delete plotObj._ev; + delete plotObj.on; + delete plotObj.once; + delete plotObj.removeListener; + delete plotObj.removeAllListeners; + delete plotObj.emit; + + delete plotObj._ev; + delete plotObj._internalEv; + delete plotObj._internalOn; + delete plotObj._internalOnce; + delete plotObj._removeInternalListener; + delete plotObj._removeAllInternalListeners; + + return plotObj; + } + +}; + +module.exports = Events; + +},{"events":27}],279:[function(_dereq_,module,exports){ +'use strict'; + +var isPlainObject = _dereq_('./is_plain_object.js'); +var isArray = Array.isArray; + +function primitivesLoopSplice(source, target) { + var i, value; + for(i = 0; i < source.length; i++) { + value = source[i]; + if(value !== null && typeof(value) === 'object') { + return false; + } + if(value !== void(0)) { + target[i] = value; + } + } + return true; +} + +exports.extendFlat = function() { + return _extend(arguments, false, false, false); +}; + +exports.extendDeep = function() { + return _extend(arguments, true, false, false); +}; + +exports.extendDeepAll = function() { + return _extend(arguments, true, true, false); +}; + +exports.extendDeepNoArrays = function() { + return _extend(arguments, true, false, true); +}; + +/* + * Inspired by https://github.com/justmoon/node-extend/blob/master/index.js + * All credit to the jQuery authors for perfecting this amazing utility. + * + * API difference with jQuery version: + * - No optional boolean (true -> deep extend) first argument, + * use `extendFlat` for first-level only extend and + * use `extendDeep` for a deep extend. + * + * Other differences with jQuery version: + * - Uses a modern (and faster) isPlainObject routine. + * - Expected to work with object {} and array [] arguments only. + * - Does not check for circular structure. + * FYI: jQuery only does a check across one level. + * Warning: this might result in infinite loops. + * + */ +function _extend(inputs, isDeep, keepAllKeys, noArrayCopies) { + var target = inputs[0]; + var length = inputs.length; + + var input, key, src, copy, copyIsArray, clone, allPrimitives; + + // TODO does this do the right thing for typed arrays? + + if(length === 2 && isArray(target) && isArray(inputs[1]) && target.length === 0) { + allPrimitives = primitivesLoopSplice(inputs[1], target); + + if(allPrimitives) { + return target; + } else { + target.splice(0, target.length); // reset target and continue to next block + } + } + + for(var i = 1; i < length; i++) { + input = inputs[i]; + + for(key in input) { + src = target[key]; + copy = input[key]; + + if(noArrayCopies && isArray(copy)) { + // Stop early and just transfer the array if array copies are disallowed: + + target[key] = copy; + } else if(isDeep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { + // recurse if we're merging plain objects or arrays + + if(copyIsArray) { + copyIsArray = false; + clone = src && isArray(src) ? src : []; + } else { + clone = src && isPlainObject(src) ? src : {}; + } + + // never move original objects, clone them + target[key] = _extend([clone, copy], isDeep, keepAllKeys, noArrayCopies); + } else if(typeof copy !== 'undefined' || keepAllKeys) { + // don't bring in undefined values, except for extendDeepAll + + target[key] = copy; + } + } + } + + return target; +} + +},{"./is_plain_object.js":286}],280:[function(_dereq_,module,exports){ +'use strict'; + + +/** + * Return news array containing only the unique items + * found in input array. + * + * IMPORTANT: Note that items are considered unique + * if `String({})` is unique. For example; + * + * Lib.filterUnique([ { a: 1 }, { b: 2 } ]) + * + * returns [{ a: 1 }] + * + * and + * + * Lib.filterUnique([ '1', 1 ]) + * + * returns ['1'] + * + * + * @param {array} array base array + * @return {array} new filtered array + */ +module.exports = function filterUnique(array) { + var seen = {}; + var out = []; + var j = 0; + + for(var i = 0; i < array.length; i++) { + var item = array[i]; + + if(seen[item] !== 1) { + seen[item] = 1; + out[j++] = item; + } + } + + return out; +}; + +},{}],281:[function(_dereq_,module,exports){ +'use strict'; + +/** Filter out object items with visible !== true + * insider array container. + * + * @param {array of objects} container + * @return {array of objects} of length <= container + * + */ +module.exports = function filterVisible(container) { + var filterFn = isCalcData(container) ? calcDataFilter : baseFilter; + var out = []; + + for(var i = 0; i < container.length; i++) { + var item = container[i]; + if(filterFn(item)) out.push(item); + } + + return out; +}; + +function baseFilter(item) { + return item.visible === true; +} + +function calcDataFilter(item) { + var trace = item[0].trace; + return trace.visible === true && trace._length !== 0; +} + +function isCalcData(cont) { + return ( + Array.isArray(cont) && + Array.isArray(cont[0]) && + cont[0][0] && + cont[0][0].trace + ); +} + +},{}],282:[function(_dereq_,module,exports){ +'use strict'; + +var mod = _dereq_('./mod').mod; + +/* + * look for intersection of two line segments + * (1->2 and 3->4) - returns array [x,y] if they do, null if not + */ +exports.segmentsIntersect = segmentsIntersect; +function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) { + var a = x2 - x1; + var b = x3 - x1; + var c = x4 - x3; + var d = y2 - y1; + var e = y3 - y1; + var f = y4 - y3; + var det = a * f - c * d; + // parallel lines? intersection is undefined + // ignore the case where they are colinear + if(det === 0) return null; + var t = (b * f - c * e) / det; + var u = (b * d - a * e) / det; + // segments do not intersect? + if(u < 0 || u > 1 || t < 0 || t > 1) return null; + + return {x: x1 + a * t, y: y1 + d * t}; +} + +/* + * find the minimum distance between two line segments (1->2 and 3->4) + */ +exports.segmentDistance = function segmentDistance(x1, y1, x2, y2, x3, y3, x4, y4) { + if(segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return 0; + + // the two segments and their lengths squared + var x12 = x2 - x1; + var y12 = y2 - y1; + var x34 = x4 - x3; + var y34 = y4 - y3; + var ll12 = x12 * x12 + y12 * y12; + var ll34 = x34 * x34 + y34 * y34; + + // calculate distance squared, then take the sqrt at the very end + var dist2 = Math.min( + perpDistance2(x12, y12, ll12, x3 - x1, y3 - y1), + perpDistance2(x12, y12, ll12, x4 - x1, y4 - y1), + perpDistance2(x34, y34, ll34, x1 - x3, y1 - y3), + perpDistance2(x34, y34, ll34, x2 - x3, y2 - y3) + ); + + return Math.sqrt(dist2); +}; + +/* + * distance squared from segment ab to point c + * [xab, yab] is the vector b-a + * [xac, yac] is the vector c-a + * llab is the length squared of (b-a), just to simplify calculation + */ +function perpDistance2(xab, yab, llab, xac, yac) { + var fcAB = (xac * xab + yac * yab); + if(fcAB < 0) { + // point c is closer to point a + return xac * xac + yac * yac; + } else if(fcAB > llab) { + // point c is closer to point b + var xbc = xac - xab; + var ybc = yac - yab; + return xbc * xbc + ybc * ybc; + } else { + // perpendicular distance is the shortest + var crossProduct = xac * yab - yac * xab; + return crossProduct * crossProduct / llab; + } +} + +// a very short-term cache for getTextLocation, just because +// we're often looping over the same locations multiple times +// invalidated as soon as we look at a different path +var locationCache, workingPath, workingTextWidth; + +// turn a path and position along it into x, y, and angle for the given text +exports.getTextLocation = function getTextLocation(path, totalPathLen, positionOnPath, textWidth) { + if(path !== workingPath || textWidth !== workingTextWidth) { + locationCache = {}; + workingPath = path; + workingTextWidth = textWidth; + } + if(locationCache[positionOnPath]) { + return locationCache[positionOnPath]; + } + + // for the angle, use points on the path separated by the text width + // even though due to curvature, the text will cover a bit more than that + var p0 = path.getPointAtLength(mod(positionOnPath - textWidth / 2, totalPathLen)); + var p1 = path.getPointAtLength(mod(positionOnPath + textWidth / 2, totalPathLen)); + // note: atan handles 1/0 nicely + var theta = Math.atan((p1.y - p0.y) / (p1.x - p0.x)); + // center the text at 2/3 of the center position plus 1/3 the p0/p1 midpoint + // that's the average position of this segment, assuming it's roughly quadratic + var pCenter = path.getPointAtLength(mod(positionOnPath, totalPathLen)); + var x = (pCenter.x * 4 + p0.x + p1.x) / 6; + var y = (pCenter.y * 4 + p0.y + p1.y) / 6; + + var out = {x: x, y: y, theta: theta}; + locationCache[positionOnPath] = out; + return out; +}; + +exports.clearLocationCache = function() { + workingPath = null; +}; + +/* + * Find the segment of `path` that's within the visible area + * given by `bounds` {left, right, top, bottom}, to within a + * precision of `buffer` px + * + * returns: undefined if nothing is visible, else object: + * { + * min: position where the path first enters bounds, or 0 if it + * starts within bounds + * max: position where the path last exits bounds, or the path length + * if it finishes within bounds + * len: max - min, ie the length of visible path + * total: the total path length - just included so the caller doesn't + * need to call path.getTotalLength() again + * isClosed: true iff the start and end points of the path are both visible + * and are at the same point + * } + * + * Works by starting from either end and repeatedly finding the distance from + * that point to the plot area, and if it's outside the plot, moving along the + * path by that distance (because the plot must be at least that far away on + * the path). Note that if a path enters, exits, and re-enters the plot, we + * will not capture this behavior. + */ +exports.getVisibleSegment = function getVisibleSegment(path, bounds, buffer) { + var left = bounds.left; + var right = bounds.right; + var top = bounds.top; + var bottom = bounds.bottom; + + var pMin = 0; + var pTotal = path.getTotalLength(); + var pMax = pTotal; + + var pt0, ptTotal; + + function getDistToPlot(len) { + var pt = path.getPointAtLength(len); + + // hold on to the start and end points for `closed` + if(len === 0) pt0 = pt; + else if(len === pTotal) ptTotal = pt; + + var dx = (pt.x < left) ? left - pt.x : (pt.x > right ? pt.x - right : 0); + var dy = (pt.y < top) ? top - pt.y : (pt.y > bottom ? pt.y - bottom : 0); + return Math.sqrt(dx * dx + dy * dy); + } + + var distToPlot = getDistToPlot(pMin); + while(distToPlot) { + pMin += distToPlot + buffer; + if(pMin > pMax) return; + distToPlot = getDistToPlot(pMin); + } + + distToPlot = getDistToPlot(pMax); + while(distToPlot) { + pMax -= distToPlot + buffer; + if(pMin > pMax) return; + distToPlot = getDistToPlot(pMax); + } + + return { + min: pMin, + max: pMax, + len: pMax - pMin, + total: pTotal, + isClosed: pMin === 0 && pMax === pTotal && + Math.abs(pt0.x - ptTotal.x) < 0.1 && + Math.abs(pt0.y - ptTotal.y) < 0.1 + }; +}; + +/** + * Find point on SVG path corresponding to a given constraint coordinate + * + * @param {SVGPathElement} path + * @param {Number} val : constraint coordinate value + * @param {String} coord : 'x' or 'y' the constraint coordinate + * @param {Object} opts : + * - {Number} pathLength : supply total path length before hand + * - {Number} tolerance + * - {Number} iterationLimit + * @return {SVGPoint} + */ +exports.findPointOnPath = function findPointOnPath(path, val, coord, opts) { + opts = opts || {}; + + var pathLength = opts.pathLength || path.getTotalLength(); + var tolerance = opts.tolerance || 1e-3; + var iterationLimit = opts.iterationLimit || 30; + + // if path starts at a val greater than the path tail (like on vertical violins), + // we must flip the sign of the computed diff. + var mul = path.getPointAtLength(0)[coord] > path.getPointAtLength(pathLength)[coord] ? -1 : 1; + + var i = 0; + var b0 = 0; + var b1 = pathLength; + var mid; + var pt; + var diff; + + while(i < iterationLimit) { + mid = (b0 + b1) / 2; + pt = path.getPointAtLength(mid); + diff = pt[coord] - val; + + if(Math.abs(diff) < tolerance) { + return pt; + } else { + if(mul * diff > 0) { + b1 = mid; + } else { + b0 = mid; + } + i++; + } + } + return pt; +}; + +},{"./mod":292}],283:[function(_dereq_,module,exports){ +'use strict'; + +// Simple helper functions +// none of these need any external deps + +module.exports = function identity(d) { return d; }; + +},{}],284:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function incrementNumeric(x, delta) { + if(!delta) return x; + + // Note 1: + // 0.3 != 0.1 + 0.2 == 0.30000000000000004 + // but 0.3 == (10 * 0.1 + 10 * 0.2) / 10 + // Attempt to use integer steps to increment + var scale = 1 / Math.abs(delta); + var newX = (scale > 1) ? ( + scale * x + + scale * delta + ) / scale : x + delta; + + // Note 2: + // now we may also consider rounding to cover few more edge cases + // e.g. 0.3 * 3 = 0.8999999999999999 + var lenX1 = String(newX).length; + if(lenX1 > 16) { + var lenDt = String(delta).length; + var lenX0 = String(x).length; + + if(lenX1 >= lenX0 + lenDt) { // likely a rounding error! + var s = parseFloat(newX).toPrecision(12); + if(s.indexOf('e+') === -1) newX = +s; + } + } + + return newX; +}; + +},{}],285:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var utcFormat = _dereq_('d3-time-format').utcFormat; +var isNumeric = _dereq_('fast-isnumeric'); + +var numConstants = _dereq_('../constants/numerical'); +var MAX_SAFE = numConstants.FP_SAFE; +var MIN_SAFE = -MAX_SAFE; +var BADNUM = numConstants.BADNUM; + +var lib = module.exports = {}; + +lib.nestedProperty = _dereq_('./nested_property'); +lib.keyedContainer = _dereq_('./keyed_container'); +lib.relativeAttr = _dereq_('./relative_attr'); +lib.isPlainObject = _dereq_('./is_plain_object'); +lib.toLogRange = _dereq_('./to_log_range'); +lib.relinkPrivateKeys = _dereq_('./relink_private'); + +var arrayModule = _dereq_('./array'); +lib.isTypedArray = arrayModule.isTypedArray; +lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray; +lib.isArray1D = arrayModule.isArray1D; +lib.ensureArray = arrayModule.ensureArray; +lib.concat = arrayModule.concat; +lib.maxRowLength = arrayModule.maxRowLength; +lib.minRowLength = arrayModule.minRowLength; + +var modModule = _dereq_('./mod'); +lib.mod = modModule.mod; +lib.modHalf = modModule.modHalf; + +var coerceModule = _dereq_('./coerce'); +lib.valObjectMeta = coerceModule.valObjectMeta; +lib.coerce = coerceModule.coerce; +lib.coerce2 = coerceModule.coerce2; +lib.coerceFont = coerceModule.coerceFont; +lib.coercePattern = coerceModule.coercePattern; +lib.coerceHoverinfo = coerceModule.coerceHoverinfo; +lib.coerceSelectionMarkerOpacity = coerceModule.coerceSelectionMarkerOpacity; +lib.validate = coerceModule.validate; + +var datesModule = _dereq_('./dates'); +lib.dateTime2ms = datesModule.dateTime2ms; +lib.isDateTime = datesModule.isDateTime; +lib.ms2DateTime = datesModule.ms2DateTime; +lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal; +lib.cleanDate = datesModule.cleanDate; +lib.isJSDate = datesModule.isJSDate; +lib.formatDate = datesModule.formatDate; +lib.incrementMonth = datesModule.incrementMonth; +lib.dateTick0 = datesModule.dateTick0; +lib.dfltRange = datesModule.dfltRange; +lib.findExactDates = datesModule.findExactDates; +lib.MIN_MS = datesModule.MIN_MS; +lib.MAX_MS = datesModule.MAX_MS; + +var searchModule = _dereq_('./search'); +lib.findBin = searchModule.findBin; +lib.sorterAsc = searchModule.sorterAsc; +lib.sorterDes = searchModule.sorterDes; +lib.distinctVals = searchModule.distinctVals; +lib.roundUp = searchModule.roundUp; +lib.sort = searchModule.sort; +lib.findIndexOfMin = searchModule.findIndexOfMin; + +var statsModule = _dereq_('./stats'); +lib.aggNums = statsModule.aggNums; +lib.len = statsModule.len; +lib.mean = statsModule.mean; +lib.median = statsModule.median; +lib.midRange = statsModule.midRange; +lib.variance = statsModule.variance; +lib.stdev = statsModule.stdev; +lib.interp = statsModule.interp; + +var matrixModule = _dereq_('./matrix'); +lib.init2dArray = matrixModule.init2dArray; +lib.transposeRagged = matrixModule.transposeRagged; +lib.dot = matrixModule.dot; +lib.translationMatrix = matrixModule.translationMatrix; +lib.rotationMatrix = matrixModule.rotationMatrix; +lib.rotationXYMatrix = matrixModule.rotationXYMatrix; +lib.apply3DTransform = matrixModule.apply3DTransform; +lib.apply2DTransform = matrixModule.apply2DTransform; +lib.apply2DTransform2 = matrixModule.apply2DTransform2; +lib.convertCssMatrix = matrixModule.convertCssMatrix; +lib.inverseTransformMatrix = matrixModule.inverseTransformMatrix; + +var anglesModule = _dereq_('./angles'); +lib.deg2rad = anglesModule.deg2rad; +lib.rad2deg = anglesModule.rad2deg; +lib.angleDelta = anglesModule.angleDelta; +lib.angleDist = anglesModule.angleDist; +lib.isFullCircle = anglesModule.isFullCircle; +lib.isAngleInsideSector = anglesModule.isAngleInsideSector; +lib.isPtInsideSector = anglesModule.isPtInsideSector; +lib.pathArc = anglesModule.pathArc; +lib.pathSector = anglesModule.pathSector; +lib.pathAnnulus = anglesModule.pathAnnulus; + +var anchorUtils = _dereq_('./anchor_utils'); +lib.isLeftAnchor = anchorUtils.isLeftAnchor; +lib.isCenterAnchor = anchorUtils.isCenterAnchor; +lib.isRightAnchor = anchorUtils.isRightAnchor; +lib.isTopAnchor = anchorUtils.isTopAnchor; +lib.isMiddleAnchor = anchorUtils.isMiddleAnchor; +lib.isBottomAnchor = anchorUtils.isBottomAnchor; + +var geom2dModule = _dereq_('./geometry2d'); +lib.segmentsIntersect = geom2dModule.segmentsIntersect; +lib.segmentDistance = geom2dModule.segmentDistance; +lib.getTextLocation = geom2dModule.getTextLocation; +lib.clearLocationCache = geom2dModule.clearLocationCache; +lib.getVisibleSegment = geom2dModule.getVisibleSegment; +lib.findPointOnPath = geom2dModule.findPointOnPath; + +var extendModule = _dereq_('./extend'); +lib.extendFlat = extendModule.extendFlat; +lib.extendDeep = extendModule.extendDeep; +lib.extendDeepAll = extendModule.extendDeepAll; +lib.extendDeepNoArrays = extendModule.extendDeepNoArrays; + +var loggersModule = _dereq_('./loggers'); +lib.log = loggersModule.log; +lib.warn = loggersModule.warn; +lib.error = loggersModule.error; + +var regexModule = _dereq_('./regex'); +lib.counterRegex = regexModule.counter; + +var throttleModule = _dereq_('./throttle'); +lib.throttle = throttleModule.throttle; +lib.throttleDone = throttleModule.done; +lib.clearThrottle = throttleModule.clear; + +var domModule = _dereq_('./dom'); +lib.getGraphDiv = domModule.getGraphDiv; +lib.isPlotDiv = domModule.isPlotDiv; +lib.removeElement = domModule.removeElement; +lib.addStyleRule = domModule.addStyleRule; +lib.addRelatedStyleRule = domModule.addRelatedStyleRule; +lib.deleteRelatedStyleRule = domModule.deleteRelatedStyleRule; +lib.getFullTransformMatrix = domModule.getFullTransformMatrix; +lib.getElementTransformMatrix = domModule.getElementTransformMatrix; +lib.getElementAndAncestors = domModule.getElementAndAncestors; +lib.equalDomRects = domModule.equalDomRects; + +lib.clearResponsive = _dereq_('./clear_responsive'); +lib.preserveDrawingBuffer = _dereq_('./preserve_drawing_buffer'); + +lib.makeTraceGroups = _dereq_('./make_trace_groups'); + +lib._ = _dereq_('./localize'); + +lib.notifier = _dereq_('./notifier'); + +lib.filterUnique = _dereq_('./filter_unique'); +lib.filterVisible = _dereq_('./filter_visible'); +lib.pushUnique = _dereq_('./push_unique'); + +lib.increment = _dereq_('./increment'); + +lib.cleanNumber = _dereq_('./clean_number'); + +lib.ensureNumber = function ensureNumber(v) { + if(!isNumeric(v)) return BADNUM; + v = Number(v); + return (v > MAX_SAFE || v < MIN_SAFE) ? BADNUM : v; +}; + +/** + * Is v a valid array index? Accepts numeric strings as well as numbers. + * + * @param {any} v: the value to test + * @param {Optional[integer]} len: the array length we are indexing + * + * @return {bool}: v is a valid array index + */ +lib.isIndex = function(v, len) { + if(len !== undefined && v >= len) return false; + return isNumeric(v) && (v >= 0) && (v % 1 === 0); +}; + +lib.noop = _dereq_('./noop'); +lib.identity = _dereq_('./identity'); + +/** + * create an array of length 'cnt' filled with 'v' at all indices + * + * @param {any} v + * @param {number} cnt + * @return {array} + */ +lib.repeat = function(v, cnt) { + var out = new Array(cnt); + for(var i = 0; i < cnt; i++) { + out[i] = v; + } + return out; +}; + +/** + * swap x and y of the same attribute in container cont + * specify attr with a ? in place of x/y + * you can also swap other things than x/y by providing part1 and part2 + */ +lib.swapAttrs = function(cont, attrList, part1, part2) { + if(!part1) part1 = 'x'; + if(!part2) part2 = 'y'; + for(var i = 0; i < attrList.length; i++) { + var attr = attrList[i]; + var xp = lib.nestedProperty(cont, attr.replace('?', part1)); + var yp = lib.nestedProperty(cont, attr.replace('?', part2)); + var temp = xp.get(); + xp.set(yp.get()); + yp.set(temp); + } +}; + +/** + * SVG painter's algo worked around with reinsertion + */ +lib.raiseToTop = function raiseToTop(elem) { + elem.parentNode.appendChild(elem); +}; + +/** + * cancel a possibly pending transition; returned selection may be used by caller + */ +lib.cancelTransition = function(selection) { + return selection.transition().duration(0); +}; + +// constrain - restrict a number v to be between v0 and v1 +lib.constrain = function(v, v0, v1) { + if(v0 > v1) return Math.max(v1, Math.min(v0, v)); + return Math.max(v0, Math.min(v1, v)); +}; + +/** + * do two bounding boxes from getBoundingClientRect, + * ie {left,right,top,bottom,width,height}, overlap? + * takes optional padding pixels + */ +lib.bBoxIntersect = function(a, b, pad) { + pad = pad || 0; + return (a.left <= b.right + pad && + b.left <= a.right + pad && + a.top <= b.bottom + pad && + b.top <= a.bottom + pad); +}; + +/* + * simpleMap: alternative to Array.map that only + * passes on the element and up to 2 extra args you + * provide (but not the array index or the whole array) + * + * array: the array to map it to + * func: the function to apply + * x1, x2: optional extra args + */ +lib.simpleMap = function(array, func, x1, x2, opts) { + var len = array.length; + var out = new Array(len); + for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2, opts); + return out; +}; + +/** + * Random string generator + * + * @param {object} existing + * pass in strings to avoid as keys with truthy values + * @param {int} bits + * bits of information in the output string, default 24 + * @param {int} base + * base of string representation, default 16. Should be a power of 2. + */ +lib.randstr = function randstr(existing, bits, base, _recursion) { + if(!base) base = 16; + if(bits === undefined) bits = 24; + if(bits <= 0) return '0'; + + var digits = Math.log(Math.pow(2, bits)) / Math.log(base); + var res = ''; + var i, b, x; + + for(i = 2; digits === Infinity; i *= 2) { + digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i; + } + + var rem = digits - Math.floor(digits); + + for(i = 0; i < Math.floor(digits); i++) { + x = Math.floor(Math.random() * base).toString(base); + res = x + res; + } + + if(rem) { + b = Math.pow(base, rem); + x = Math.floor(Math.random() * b).toString(base); + res = x + res; + } + + var parsed = parseInt(res, base); + if((existing && existing[res]) || + (parsed !== Infinity && parsed >= Math.pow(2, bits))) { + if(_recursion > 10) { + lib.warn('randstr failed uniqueness'); + return res; + } + return randstr(existing, bits, base, (_recursion || 0) + 1); + } else return res; +}; + +lib.OptionControl = function(opt, optname) { + /* + * An environment to contain all option setters and + * getters that collectively modify opts. + * + * You can call up opts from any function in new object + * as this.optname || this.opt + * + * See FitOpts for example of usage + */ + if(!opt) opt = {}; + if(!optname) optname = 'opt'; + + var self = {}; + self.optionList = []; + + self._newoption = function(optObj) { + optObj[optname] = opt; + self[optObj.name] = optObj; + self.optionList.push(optObj); + }; + + self['_' + optname] = opt; + return self; +}; + +/** + * lib.smooth: smooth arrayIn by convolving with + * a hann window with given full width at half max + * bounce the ends in, so the output has the same length as the input + */ +lib.smooth = function(arrayIn, FWHM) { + FWHM = Math.round(FWHM) || 0; // only makes sense for integers + if(FWHM < 2) return arrayIn; + + var alen = arrayIn.length; + var alen2 = 2 * alen; + var wlen = 2 * FWHM - 1; + var w = new Array(wlen); + var arrayOut = new Array(alen); + var i; + var j; + var k; + var v; + + // first make the window array + for(i = 0; i < wlen; i++) { + w[i] = (1 - Math.cos(Math.PI * (i + 1) / FWHM)) / (2 * FWHM); + } + + // now do the convolution + for(i = 0; i < alen; i++) { + v = 0; + for(j = 0; j < wlen; j++) { + k = i + j + 1 - FWHM; + + // multibounce + if(k < -alen) k -= alen2 * Math.round(k / alen2); + else if(k >= alen2) k -= alen2 * Math.floor(k / alen2); + + // single bounce + if(k < 0) k = - 1 - k; + else if(k >= alen) k = alen2 - 1 - k; + + v += arrayIn[k] * w[j]; + } + arrayOut[i] = v; + } + + return arrayOut; +}; + +/** + * syncOrAsync: run a sequence of functions synchronously + * as long as its returns are not promises (ie have no .then) + * includes one argument arg to send to all functions... + * this is mainly just to prevent us having to make wrapper functions + * when the only purpose of the wrapper is to reference gd + * and a final step to be executed at the end + * TODO: if there's an error and everything is sync, + * this doesn't happen yet because we want to make sure + * that it gets reported + */ +lib.syncOrAsync = function(sequence, arg, finalStep) { + var ret, fni; + + function continueAsync() { + return lib.syncOrAsync(sequence, arg, finalStep); + } + + while(sequence.length) { + fni = sequence.splice(0, 1)[0]; + ret = fni(arg); + + if(ret && ret.then) { + return ret.then(continueAsync) + .then(undefined, lib.promiseError); + } + } + + return finalStep && finalStep(arg); +}; + + +/** + * Helper to strip trailing slash, from + * http://stackoverflow.com/questions/6680825/return-string-without-trailing-slash + */ +lib.stripTrailingSlash = function(str) { + if(str.substr(-1) === '/') return str.substr(0, str.length - 1); + return str; +}; + +lib.noneOrAll = function(containerIn, containerOut, attrList) { + /** + * some attributes come together, so if you have one of them + * in the input, you should copy the default values of the others + * to the input as well. + */ + if(!containerIn) return; + + var hasAny = false; + var hasAll = true; + var i; + var val; + + for(i = 0; i < attrList.length; i++) { + val = containerIn[attrList[i]]; + if(val !== undefined && val !== null) hasAny = true; + else hasAll = false; + } + + if(hasAny && !hasAll) { + for(i = 0; i < attrList.length; i++) { + containerIn[attrList[i]] = containerOut[attrList[i]]; + } + } +}; + +/** merges calcdata field (given by cdAttr) with traceAttr values + * + * N.B. Loop over minimum of cd.length and traceAttr.length + * i.e. it does not try to fill in beyond traceAttr.length-1 + * + * @param {array} traceAttr : trace attribute + * @param {object} cd : calcdata trace + * @param {string} cdAttr : calcdata key + */ +lib.mergeArray = function(traceAttr, cd, cdAttr, fn) { + var hasFn = typeof fn === 'function'; + if(lib.isArrayOrTypedArray(traceAttr)) { + var imax = Math.min(traceAttr.length, cd.length); + for(var i = 0; i < imax; i++) { + var v = traceAttr[i]; + cd[i][cdAttr] = hasFn ? fn(v) : v; + } + } +}; + +// cast numbers to positive numbers, returns 0 if not greater than 0 +lib.mergeArrayCastPositive = function(traceAttr, cd, cdAttr) { + return lib.mergeArray(traceAttr, cd, cdAttr, function(v) { + var w = +v; + return !isFinite(w) ? 0 : w > 0 ? w : 0; + }); +}; + +/** fills calcdata field (given by cdAttr) with traceAttr values + * or function of traceAttr values (e.g. some fallback) + * + * N.B. Loops over all cd items. + * + * @param {array} traceAttr : trace attribute + * @param {object} cd : calcdata trace + * @param {string} cdAttr : calcdata key + * @param {function} [fn] : optional function to apply to each array item + */ +lib.fillArray = function(traceAttr, cd, cdAttr, fn) { + fn = fn || lib.identity; + + if(lib.isArrayOrTypedArray(traceAttr)) { + for(var i = 0; i < cd.length; i++) { + cd[i][cdAttr] = fn(traceAttr[i]); + } + } +}; + +/** Handler for trace-wide vs per-point options + * + * @param {object} trace : (full) trace object + * @param {number} ptNumber : index of the point in question + * @param {string} astr : attribute string + * @param {function} [fn] : optional function to apply to each array item + * + * @return {any} + */ +lib.castOption = function(trace, ptNumber, astr, fn) { + fn = fn || lib.identity; + + var val = lib.nestedProperty(trace, astr).get(); + + if(lib.isArrayOrTypedArray(val)) { + if(Array.isArray(ptNumber) && lib.isArrayOrTypedArray(val[ptNumber[0]])) { + return fn(val[ptNumber[0]][ptNumber[1]]); + } else { + return fn(val[ptNumber]); + } + } else { + return val; + } +}; + +/** Extract option from calcdata item, correctly falling back to + * trace value if not found. + * + * @param {object} calcPt : calcdata[i][j] item + * @param {object} trace : (full) trace object + * @param {string} calcKey : calcdata key + * @param {string} traceKey : aka trace attribute string + * @return {any} + */ +lib.extractOption = function(calcPt, trace, calcKey, traceKey) { + if(calcKey in calcPt) return calcPt[calcKey]; + + // fallback to trace value, + // must check if value isn't itself an array + // which means the trace attribute has a corresponding + // calcdata key, but its value is falsy + var traceVal = lib.nestedProperty(trace, traceKey).get(); + if(!Array.isArray(traceVal)) return traceVal; +}; + +function makePtIndex2PtNumber(indexToPoints) { + var ptIndex2ptNumber = {}; + for(var k in indexToPoints) { + var pts = indexToPoints[k]; + for(var j = 0; j < pts.length; j++) { + ptIndex2ptNumber[pts[j]] = +k; + } + } + return ptIndex2ptNumber; +} + +/** Tag selected calcdata items + * + * N.B. note that point 'index' corresponds to input data array index + * whereas 'number' is its post-transform version. + * + * @param {array} calcTrace + * @param {object} trace + * - selectedpoints {array} + * - _indexToPoints {object} + * @param {ptNumber2cdIndex} ptNumber2cdIndex (optional) + * optional map object for trace types that do not have 1-to-1 point number to + * calcdata item index correspondence (e.g. histogram) + */ +lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) { + var selectedpoints = trace.selectedpoints; + var indexToPoints = trace._indexToPoints; + var ptIndex2ptNumber; + + // make pt index-to-number map object, which takes care of transformed traces + if(indexToPoints) { + ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints); + } + + function isCdIndexValid(v) { + return v !== undefined && v < calcTrace.length; + } + + for(var i = 0; i < selectedpoints.length; i++) { + var ptIndex = selectedpoints[i]; + + if(lib.isIndex(ptIndex) || + (lib.isArrayOrTypedArray(ptIndex) && lib.isIndex(ptIndex[0]) && lib.isIndex(ptIndex[1])) + ) { + var ptNumber = ptIndex2ptNumber ? ptIndex2ptNumber[ptIndex] : ptIndex; + var cdIndex = ptNumber2cdIndex ? ptNumber2cdIndex[ptNumber] : ptNumber; + + if(isCdIndexValid(cdIndex)) { + calcTrace[cdIndex].selected = 1; + } + } + } +}; + +lib.selIndices2selPoints = function(trace) { + var selectedpoints = trace.selectedpoints; + var indexToPoints = trace._indexToPoints; + + if(indexToPoints) { + var ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints); + var out = []; + + for(var i = 0; i < selectedpoints.length; i++) { + var ptIndex = selectedpoints[i]; + if(lib.isIndex(ptIndex)) { + var ptNumber = ptIndex2ptNumber[ptIndex]; + if(lib.isIndex(ptNumber)) { + out.push(ptNumber); + } + } + } + + return out; + } else { + return selectedpoints; + } +}; + +/** Returns target as set by 'target' transform attribute + * + * @param {object} trace : full trace object + * @param {object} transformOpts : transform option object + * - target (string} : + * either an attribute string referencing an array in the trace object, or + * a set array. + * + * @return {array or false} : the target array (NOT a copy!!) or false if invalid + */ +lib.getTargetArray = function(trace, transformOpts) { + var target = transformOpts.target; + + if(typeof target === 'string' && target) { + var array = lib.nestedProperty(trace, target).get(); + return Array.isArray(array) ? array : false; + } else if(Array.isArray(target)) { + return target; + } + + return false; +}; + +/** + * modified version of jQuery's extend to strip out private objs and functions, + * and cut arrays down to first or 1 elements + * because extend-like algorithms are hella slow + * obj2 is assumed to already be clean of these things (including no arrays) + */ +lib.minExtend = function(obj1, obj2) { + var objOut = {}; + if(typeof obj2 !== 'object') obj2 = {}; + var arrayLen = 3; + var keys = Object.keys(obj1); + var i, k, v; + + for(i = 0; i < keys.length; i++) { + k = keys[i]; + v = obj1[k]; + if(k.charAt(0) === '_' || typeof v === 'function') continue; + else if(k === 'module') objOut[k] = v; + else if(Array.isArray(v)) { + if(k === 'colorscale') { + objOut[k] = v.slice(); + } else { + objOut[k] = v.slice(0, arrayLen); + } + } else if(lib.isTypedArray(v)) { + objOut[k] = v.subarray(0, arrayLen); + } else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]); + else objOut[k] = v; + } + + keys = Object.keys(obj2); + for(i = 0; i < keys.length; i++) { + k = keys[i]; + v = obj2[k]; + if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') { + objOut[k] = v; + } + } + + return objOut; +}; + +lib.titleCase = function(s) { + return s.charAt(0).toUpperCase() + s.substr(1); +}; + +lib.containsAny = function(s, fragments) { + for(var i = 0; i < fragments.length; i++) { + if(s.indexOf(fragments[i]) !== -1) return true; + } + return false; +}; + +lib.isIE = function() { + return typeof window.navigator.msSaveBlob !== 'undefined'; +}; + +var IS_SAFARI_REGEX = /Version\/[\d\.]+.*Safari/; +lib.isSafari = function() { + return IS_SAFARI_REGEX.test(window.navigator.userAgent); +}; + +var IS_IOS_REGEX = /iPad|iPhone|iPod/; +lib.isIOS = function() { + return IS_IOS_REGEX.test(window.navigator.userAgent); +}; + +var FIREFOX_VERSION_REGEX = /Firefox\/(\d+)\.\d+/; +lib.getFirefoxVersion = function() { + var match = FIREFOX_VERSION_REGEX.exec(window.navigator.userAgent); + if(match && match.length === 2) { + var versionInt = parseInt(match[1]); + if(!isNaN(versionInt)) { + return versionInt; + } + } + return null; +}; + +lib.isD3Selection = function(obj) { + return obj instanceof d3.selection; +}; + +/** + * Append element to DOM only if not present. + * + * @param {d3 selection} parent : parent selection of the element in question + * @param {string} nodeType : node type of element to append + * @param {string} className (optional) : class name of element in question + * @param {fn} enterFn (optional) : optional fn applied to entering elements only + * @return {d3 selection} selection of new layer + * + * Previously, we were using the following pattern: + * + * ``` + * var sel = parent.selectAll('.' + className) + * .data([0]); + * + * sel.enter().append(nodeType) + * .classed(className, true); + * + * return sel; + * ``` + * + * in numerous places in our codebase to achieve the same behavior. + * + * The logic below performs much better, mostly as we are using + * `.select` instead `.selectAll` that is `querySelector` instead of + * `querySelectorAll`. + * + */ +lib.ensureSingle = function(parent, nodeType, className, enterFn) { + var sel = parent.select(nodeType + (className ? '.' + className : '')); + if(sel.size()) return sel; + + var layer = parent.append(nodeType); + if(className) layer.classed(className, true); + if(enterFn) layer.call(enterFn); + + return layer; +}; + +/** + * Same as Lib.ensureSingle, but using id as selector. + * This version is mostly used for clipPath nodes. + * + * @param {d3 selection} parent : parent selection of the element in question + * @param {string} nodeType : node type of element to append + * @param {string} id : id of element in question + * @param {fn} enterFn (optional) : optional fn applied to entering elements only + * @return {d3 selection} selection of new layer + */ +lib.ensureSingleById = function(parent, nodeType, id, enterFn) { + var sel = parent.select(nodeType + '#' + id); + if(sel.size()) return sel; + + var layer = parent.append(nodeType).attr('id', id); + if(enterFn) layer.call(enterFn); + + return layer; +}; + +/** + * Converts a string path to an object. + * + * When given a string containing an array element, it will create a `null` + * filled array of the given size. + * + * @example + * lib.objectFromPath('nested.test[2].path', 'value'); + * // returns { nested: { test: [null, null, { path: 'value' }]} + * + * @param {string} path to nested value + * @param {*} any value to be set + * + * @return {Object} the constructed object with a full nested path + */ +lib.objectFromPath = function(path, value) { + var keys = path.split('.'); + var tmpObj; + var obj = tmpObj = {}; + + for(var i = 0; i < keys.length; i++) { + var key = keys[i]; + var el = null; + + var parts = keys[i].match(/(.*)\[([0-9]+)\]/); + + if(parts) { + key = parts[1]; + el = parts[2]; + + tmpObj = tmpObj[key] = []; + + if(i === keys.length - 1) { + tmpObj[el] = value; + } else { + tmpObj[el] = {}; + } + + tmpObj = tmpObj[el]; + } else { + if(i === keys.length - 1) { + tmpObj[key] = value; + } else { + tmpObj[key] = {}; + } + + tmpObj = tmpObj[key]; + } + } + + return obj; +}; + +/** + * Iterate through an object in-place, converting dotted properties to objects. + * + * Examples: + * + * lib.expandObjectPaths({'nested.test.path': 'value'}); + * => { nested: { test: {path: 'value'}}} + * + * It also handles array notation, e.g.: + * + * lib.expandObjectPaths({'foo[1].bar': 'value'}); + * => { foo: [null, {bar: value}] } + * + * It handles merges the results when two properties are specified in parallel: + * + * lib.expandObjectPaths({'foo[1].bar': 10, 'foo[0].bar': 20}); + * => { foo: [{bar: 10}, {bar: 20}] } + * + * It does NOT, however, merge multiple multiply-nested arrays:: + * + * lib.expandObjectPaths({'marker[1].range[1]': 5, 'marker[1].range[0]': 4}) + * => { marker: [null, {range: 4}] } + */ + +// Store this to avoid recompiling regex on *every* prop since this may happen many +// many times for animations. Could maybe be inside the function. Not sure about +// scoping vs. recompilation tradeoff, but at least it's not just inlining it into +// the inner loop. +var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/; +var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/; + +lib.expandObjectPaths = function(data) { + var match, key, prop, datum, idx, dest, trailingPath; + if(typeof data === 'object' && !Array.isArray(data)) { + for(key in data) { + if(data.hasOwnProperty(key)) { + if((match = key.match(dottedPropertyRegex))) { + datum = data[key]; + prop = match[1]; + + delete data[key]; + + data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths(datum))[prop]); + } else if((match = key.match(indexedPropertyRegex))) { + datum = data[key]; + + prop = match[1]; + idx = parseInt(match[2]); + + delete data[key]; + + data[prop] = data[prop] || []; + + if(match[3] === '.') { + // This is the case where theere are subsequent properties into which + // we must recurse, e.g. transforms[0].value + trailingPath = match[4]; + dest = data[prop][idx] = data[prop][idx] || {}; + + // NB: Extend deep no arrays prevents this from working on multiple + // nested properties in the same object, e.g. + // + // { + // foo[0].bar[1].range + // foo[0].bar[0].range + // } + // + // In this case, the extendDeepNoArrays will overwrite one array with + // the other, so that both properties *will not* be present in the + // result. Fixing this would require a more intelligent tracking + // of changes and merging than extendDeepNoArrays currently accomplishes. + lib.extendDeepNoArrays(dest, lib.objectFromPath(trailingPath, lib.expandObjectPaths(datum))); + } else { + // This is the case where this property is the end of the line, + // e.g. xaxis.range[0] + data[prop][idx] = lib.expandObjectPaths(datum); + } + } else { + data[key] = lib.expandObjectPaths(data[key]); + } + } + } + } + + return data; +}; + +/** + * Converts value to string separated by the provided separators. + * + * @example + * lib.numSeparate(2016, '.,'); + * // returns '2016' + * + * @example + * lib.numSeparate(3000, '.,', true); + * // returns '3,000' + * + * @example + * lib.numSeparate(1234.56, '|,') + * // returns '1,234|56' + * + * @param {string|number} value the value to be converted + * @param {string} separators string of decimal, then thousands separators + * @param {boolean} separatethousands boolean, 4-digit integers are separated if true + * + * @return {string} the value that has been separated + */ +lib.numSeparate = function(value, separators, separatethousands) { + if(!separatethousands) separatethousands = false; + + if(typeof separators !== 'string' || separators.length === 0) { + throw new Error('Separator string required for formatting!'); + } + + if(typeof value === 'number') { + value = String(value); + } + + var thousandsRe = /(\d+)(\d{3})/; + var decimalSep = separators.charAt(0); + var thouSep = separators.charAt(1); + + var x = value.split('.'); + var x1 = x[0]; + var x2 = x.length > 1 ? decimalSep + x[1] : ''; + + // Years are ignored for thousands separators + if(thouSep && (x.length > 1 || x1.length > 4 || separatethousands)) { + while(thousandsRe.test(x1)) { + x1 = x1.replace(thousandsRe, '$1' + thouSep + '$2'); + } + } + + return x1 + x2; +}; + +lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)([:|\|][^}]*)?}/g; +var SIMPLE_PROPERTY_REGEX = /^\w*$/; + +/** + * Substitute values from an object into a string + * + * Examples: + * Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf' + * Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf' + * + * @param {string} input string containing %{...} template strings + * @param {obj} data object containing substitution values + * + * @return {string} templated string + */ +lib.templateString = function(string, obj) { + // Not all that useful, but cache nestedProperty instantiation + // just in case it speeds things up *slightly*: + var getterCache = {}; + + return string.replace(lib.TEMPLATE_STRING_REGEX, function(dummy, key) { + var v; + if(SIMPLE_PROPERTY_REGEX.test(key)) { + v = obj[key]; + } else { + getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get; + v = getterCache[key](); + } + return lib.isValidTextValue(v) ? v : ''; + }); +}; + +var hovertemplateWarnings = { + max: 10, + count: 0, + name: 'hovertemplate' +}; +lib.hovertemplateString = function() { + return templateFormatString.apply(hovertemplateWarnings, arguments); +}; + +var texttemplateWarnings = { + max: 10, + count: 0, + name: 'texttemplate' +}; +lib.texttemplateString = function() { + return templateFormatString.apply(texttemplateWarnings, arguments); +}; + +var TEMPLATE_STRING_FORMAT_SEPARATOR = /^[:|\|]/; +/** + * Substitute values from an object into a string and optionally formats them using d3-format, + * or fallback to associated labels. + * + * Examples: + * Lib.hovertemplateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf' + * Lib.hovertemplateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf' + * Lib.hovertemplateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00' + * + * @param {string} input string containing %{...:...} template strings + * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'} + * @param {obj} d3 locale + * @param {obj} data objects containing substitution values + * + * @return {string} templated string + */ +function templateFormatString(string, labels, d3locale) { + var opts = this; + var args = arguments; + if(!labels) labels = {}; + // Not all that useful, but cache nestedProperty instantiation + // just in case it speeds things up *slightly*: + var getterCache = {}; + + return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, rawKey, format) { + var isOther = + rawKey === 'xother' || + rawKey === 'yother'; + + var isSpaceOther = + rawKey === '_xother' || + rawKey === '_yother'; + + var isSpaceOtherSpace = + rawKey === '_xother_' || + rawKey === '_yother_'; + + var isOtherSpace = + rawKey === 'xother_' || + rawKey === 'yother_'; + + var hasOther = isOther || isSpaceOther || isOtherSpace || isSpaceOtherSpace; + + var key = rawKey; + if(isSpaceOther || isSpaceOtherSpace) key = key.substring(1); + if(isOtherSpace || isSpaceOtherSpace) key = key.substring(0, key.length - 1); + + var value; + if(hasOther) { + value = labels[key]; + if(value === undefined) return ''; + } else { + var obj, i; + for(i = 3; i < args.length; i++) { + obj = args[i]; + if(!obj) continue; + if(obj.hasOwnProperty(key)) { + value = obj[key]; + break; + } + + if(!SIMPLE_PROPERTY_REGEX.test(key)) { + value = lib.nestedProperty(obj, key).get(); + value = getterCache[key] || lib.nestedProperty(obj, key).get(); + if(value) getterCache[key] = value; + } + if(value !== undefined) break; + } + } + + if(value === undefined && opts) { + if(opts.count < opts.max) { + lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!'); + value = match; + } + + if(opts.count === opts.max) { + lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed'); + } + opts.count++; + + return match; + } + + if(format) { + var fmt; + if(format[0] === ':') { + fmt = d3locale ? d3locale.numberFormat : d3.format; + value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value); + } + + if(format[0] === '|') { + fmt = d3locale ? d3locale.timeFormat : utcFormat; + var ms = lib.dateTime2ms(value); + value = lib.formatDate(ms, format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''), false, fmt); + } + } else { + var keyLabel = key + 'Label'; + if(labels.hasOwnProperty(keyLabel)) value = labels[keyLabel]; + } + + if(hasOther) { + value = '(' + value + ')'; + if(isSpaceOther || isSpaceOtherSpace) value = ' ' + value; + if(isOtherSpace || isSpaceOtherSpace) value = value + ' '; + } + + return value; + }); +} + +/* + * alphanumeric string sort, tailored for subplot IDs like scene2, scene10, x10y13 etc + */ +var char0 = 48; +var char9 = 57; +lib.subplotSort = function(a, b) { + var l = Math.min(a.length, b.length) + 1; + var numA = 0; + var numB = 0; + for(var i = 0; i < l; i++) { + var charA = a.charCodeAt(i) || 0; + var charB = b.charCodeAt(i) || 0; + var isNumA = charA >= char0 && charA <= char9; + var isNumB = charB >= char0 && charB <= char9; + + if(isNumA) numA = 10 * numA + charA - char0; + if(isNumB) numB = 10 * numB + charB - char0; + + if(!isNumA || !isNumB) { + if(numA !== numB) return numA - numB; + if(charA !== charB) return charA - charB; + } + } + return numB - numA; +}; + +// repeatable pseudorandom generator +var randSeed = 2000000000; + +lib.seedPseudoRandom = function() { + randSeed = 2000000000; +}; + +lib.pseudoRandom = function() { + var lastVal = randSeed; + randSeed = (69069 * randSeed + 1) % 4294967296; + // don't let consecutive vals be too close together + // gets away from really trying to be random, in favor of better local uniformity + if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom(); + return randSeed / 4294967296; +}; + + +/** Fill hover 'pointData' container with 'correct' hover text value + * + * - If trace hoverinfo contains a 'text' flag and hovertext is not set, + * the text elements will be seen in the hover labels. + * + * - If trace hoverinfo contains a 'text' flag and hovertext is set, + * hovertext takes precedence over text + * i.e. the hoverinfo elements will be seen in the hover labels + * + * @param {object} calcPt + * @param {object} trace + * @param {object || array} contOut (mutated here) + */ +lib.fillText = function(calcPt, trace, contOut) { + var fill = Array.isArray(contOut) ? + function(v) { contOut.push(v); } : + function(v) { contOut.text = v; }; + + var htx = lib.extractOption(calcPt, trace, 'htx', 'hovertext'); + if(lib.isValidTextValue(htx)) return fill(htx); + + var tx = lib.extractOption(calcPt, trace, 'tx', 'text'); + if(lib.isValidTextValue(tx)) return fill(tx); +}; + +// accept all truthy values and 0 (which gets cast to '0' in the hover labels) +lib.isValidTextValue = function(v) { + return v || v === 0; +}; + +/** + * @param {number} ratio + * @param {number} n (number of decimal places) + */ +lib.formatPercent = function(ratio, n) { + n = n || 0; + var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%'; + for(var i = 0; i < n; i++) { + if(str.indexOf('.') !== -1) { + str = str.replace('0%', '%'); + str = str.replace('.%', '%'); + } + } + return str; +}; + +lib.isHidden = function(gd) { + var display = window.getComputedStyle(gd).display; + return !display || display === 'none'; +}; + +lib.strTranslate = function(x, y) { + return (x || y) ? 'translate(' + x + ',' + y + ')' : ''; +}; + +lib.strRotate = function(a) { + return a ? 'rotate(' + a + ')' : ''; +}; + +lib.strScale = function(s) { + return s !== 1 ? 'scale(' + s + ')' : ''; +}; + +/** Return transform text for bar bar-like rectangles and pie-like slices + * @param {object} transform + * - targetX: desired position on the x-axis + * - targetY: desired position on the y-axis + * - textX: text middle position on the x-axis + * - textY: text middle position on the y-axis + * - anchorX: (optional) text anchor position on the x-axis (computed from textX), zero for middle anchor + * - anchorY: (optional) text anchor position on the y-axis (computed from textY), zero for middle anchor + * - scale: (optional) scale applied after translate + * - rotate: (optional) rotation applied after scale + * - noCenter: when defined no extra arguments needed in rotation + */ +lib.getTextTransform = function(transform) { + var noCenter = transform.noCenter; + var textX = transform.textX; + var textY = transform.textY; + var targetX = transform.targetX; + var targetY = transform.targetY; + var anchorX = transform.anchorX || 0; + var anchorY = transform.anchorY || 0; + var rotate = transform.rotate; + var scale = transform.scale; + if(!scale) scale = 0; + else if(scale > 1) scale = 1; + + return ( + lib.strTranslate( + targetX - scale * (textX + anchorX), + targetY - scale * (textY + anchorY) + ) + + lib.strScale(scale) + + (rotate ? + 'rotate(' + rotate + + (noCenter ? '' : ' ' + textX + ' ' + textY) + + ')' : '' + ) + ); +}; + +lib.ensureUniformFontSize = function(gd, baseFont) { + var out = lib.extendFlat({}, baseFont); + out.size = Math.max( + baseFont.size, + gd._fullLayout.uniformtext.minsize || 0 + ); + return out; +}; + +/** + * provide a human-readable list e.g. "A, B, C and D" with an ending separator + * + * @param {array} arr : the array to join + * @param {string} mainSeparator : main separator + * @param {string} lastSeparator : last separator + * + * @return {string} : joined list + */ +lib.join2 = function(arr, mainSeparator, lastSeparator) { + var len = arr.length; + if(len > 1) { + return arr.slice(0, -1).join(mainSeparator) + lastSeparator + arr[len - 1]; + } + return arr.join(mainSeparator); +}; + +lib.bigFont = function(size) { + return Math.round(1.2 * size); +}; + +var firefoxVersion = lib.getFirefoxVersion(); +// see https://bugzilla.mozilla.org/show_bug.cgi?id=1684973 +var isProblematicFirefox = firefoxVersion !== null && firefoxVersion < 86; + +/** + * Return the mouse position from the last event registered by D3. + * @returns An array with two numbers, representing the x and y coordinates of the mouse pointer + * at the event relative to the targeted node. + */ +lib.getPositionFromD3Event = function() { + if(isProblematicFirefox) { + // layerX and layerY are non-standard, so we only fallback to them when we have to: + return [ + d3.event.layerX, + d3.event.layerY + ]; + } else { + return [ + d3.event.offsetX, + d3.event.offsetY + ]; + } +}; + +},{"../constants/numerical":265,"./anchor_utils":269,"./angles":270,"./array":271,"./clean_number":272,"./clear_responsive":274,"./coerce":275,"./dates":276,"./dom":277,"./extend":279,"./filter_unique":280,"./filter_visible":281,"./geometry2d":282,"./identity":283,"./increment":284,"./is_plain_object":286,"./keyed_container":287,"./localize":288,"./loggers":289,"./make_trace_groups":290,"./matrix":291,"./mod":292,"./nested_property":293,"./noop":294,"./notifier":295,"./preserve_drawing_buffer":298,"./push_unique":299,"./regex":301,"./relative_attr":302,"./relink_private":303,"./search":304,"./stats":306,"./throttle":308,"./to_log_range":309,"@plotly/d3":20,"d3-time-format":29,"fast-isnumeric":31}],286:[function(_dereq_,module,exports){ +'use strict'; + +// more info: http://stackoverflow.com/questions/18531624/isplainobject-thing +module.exports = function isPlainObject(obj) { + // We need to be a little less strict in the `imagetest` container because + // of how async image requests are handled. + // + // N.B. isPlainObject(new Constructor()) will return true in `imagetest` + if(window && window.process && window.process.versions) { + return Object.prototype.toString.call(obj) === '[object Object]'; + } + + return ( + Object.prototype.toString.call(obj) === '[object Object]' && + Object.getPrototypeOf(obj).hasOwnProperty('hasOwnProperty') + ); +}; + +},{}],287:[function(_dereq_,module,exports){ +'use strict'; + +var nestedProperty = _dereq_('./nested_property'); + +var SIMPLE_PROPERTY_REGEX = /^\w*$/; + +// bitmask for deciding what's updated. Sometimes the name needs to be updated, +// sometimes the value needs to be updated, and sometimes both do. This is just +// a simple way to track what's updated such that it's a simple OR operation to +// assimilate new updates. +// +// The only exception is the UNSET bit that tracks when we need to explicitly +// unset and remove the property. This concrn arises because of the special +// way in which nestedProperty handles null/undefined. When you specify `null`, +// it prunes any unused items in the tree. I ran into some issues with it getting +// null vs undefined confused, so UNSET is just a bit that forces the property +// update to send `null`, removing the property explicitly rather than setting +// it to undefined. +var NONE = 0; +var NAME = 1; +var VALUE = 2; +var BOTH = 3; +var UNSET = 4; + +module.exports = function keyedContainer(baseObj, path, keyName, valueName) { + keyName = keyName || 'name'; + valueName = valueName || 'value'; + var i, arr, baseProp; + var changeTypes = {}; + + if(path && path.length) { + baseProp = nestedProperty(baseObj, path); + arr = baseProp.get(); + } else { + arr = baseObj; + } + + path = path || ''; + + // Construct an index: + var indexLookup = {}; + if(arr) { + for(i = 0; i < arr.length; i++) { + indexLookup[arr[i][keyName]] = i; + } + } + + var isSimpleValueProp = SIMPLE_PROPERTY_REGEX.test(valueName); + + var obj = { + set: function(name, value) { + var changeType = value === null ? UNSET : NONE; + + // create the base array if necessary + if(!arr) { + if(!baseProp || changeType === UNSET) return; + + arr = []; + baseProp.set(arr); + } + + var idx = indexLookup[name]; + if(idx === undefined) { + if(changeType === UNSET) return; + + changeType = changeType | BOTH; + idx = arr.length; + indexLookup[name] = idx; + } else if(value !== (isSimpleValueProp ? arr[idx][valueName] : nestedProperty(arr[idx], valueName).get())) { + changeType = changeType | VALUE; + } + + var newValue = arr[idx] = arr[idx] || {}; + newValue[keyName] = name; + + if(isSimpleValueProp) { + newValue[valueName] = value; + } else { + nestedProperty(newValue, valueName).set(value); + } + + // If it's not an unset, force that bit to be unset. This is all related to the fact + // that undefined and null are a bit specially implemented in nestedProperties. + if(value !== null) { + changeType = changeType & ~UNSET; + } + + changeTypes[idx] = changeTypes[idx] | changeType; + + return obj; + }, + get: function(name) { + if(!arr) return; + + var idx = indexLookup[name]; + + if(idx === undefined) { + return undefined; + } else if(isSimpleValueProp) { + return arr[idx][valueName]; + } else { + return nestedProperty(arr[idx], valueName).get(); + } + }, + rename: function(name, newName) { + var idx = indexLookup[name]; + + if(idx === undefined) return obj; + changeTypes[idx] = changeTypes[idx] | NAME; + + indexLookup[newName] = idx; + delete indexLookup[name]; + + arr[idx][keyName] = newName; + + return obj; + }, + remove: function(name) { + var idx = indexLookup[name]; + + if(idx === undefined) return obj; + + var object = arr[idx]; + if(Object.keys(object).length > 2) { + // This object contains more than just the key/value, so unset + // the value without modifying the entry otherwise: + changeTypes[idx] = changeTypes[idx] | VALUE; + return obj.set(name, null); + } + + if(isSimpleValueProp) { + for(i = idx; i < arr.length; i++) { + changeTypes[i] = changeTypes[i] | BOTH; + } + for(i = idx; i < arr.length; i++) { + indexLookup[arr[i][keyName]]--; + } + arr.splice(idx, 1); + delete(indexLookup[name]); + } else { + // Perform this update *strictly* so we can check whether the result's + // been pruned. If so, it's a removal. If not, it's a value unset only. + nestedProperty(object, valueName).set(null); + + // Now check if the top level nested property has any keys left. If so, + // the object still has values so we only want to unset the key. If not, + // the entire object can be removed since there's no other data. + // var topLevelKeys = Object.keys(object[valueName.split('.')[0]] || []); + + changeTypes[idx] = changeTypes[idx] | VALUE | UNSET; + } + + return obj; + }, + constructUpdate: function() { + var astr, idx; + var update = {}; + var changed = Object.keys(changeTypes); + for(var i = 0; i < changed.length; i++) { + idx = changed[i]; + astr = path + '[' + idx + ']'; + if(arr[idx]) { + if(changeTypes[idx] & NAME) { + update[astr + '.' + keyName] = arr[idx][keyName]; + } + if(changeTypes[idx] & VALUE) { + if(isSimpleValueProp) { + update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : arr[idx][valueName]; + } else { + update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : nestedProperty(arr[idx], valueName).get(); + } + } + } else { + update[astr] = null; + } + } + + return update; + } + }; + + return obj; +}; + +},{"./nested_property":293}],288:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); + +/** + * localize: translate a string for the current locale + * + * @param {object} gd: the graphDiv for context + * gd._context.locale determines the language (& optional region/country) + * the dictionary for each locale may either be supplied in + * gd._context.locales or globally via Plotly.register + * @param {string} s: the string to translate + */ +module.exports = function localize(gd, s) { + var locale = gd._context.locale; + + /* + * Priority of lookup: + * contextDicts[locale], + * registeredDicts[locale], + * contextDicts[baseLocale], (if baseLocale is distinct) + * registeredDicts[baseLocale] + * Return the first translation we find. + * This way if you have a regionalization you are allowed to specify + * only what's different from the base locale, everything else will + * fall back on the base. + */ + for(var i = 0; i < 2; i++) { + var locales = gd._context.locales; + for(var j = 0; j < 2; j++) { + var dict = (locales[locale] || {}).dictionary; + if(dict) { + var out = dict[s]; + if(out) return out; + } + locales = Registry.localeRegistry; + } + + var baseLocale = locale.split('-')[0]; + if(baseLocale === locale) break; + locale = baseLocale; + } + + return s; +}; + +},{"../registry":373}],289:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint-disable no-console */ + +var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig; + +var notifier = _dereq_('./notifier'); + +var loggers = module.exports = {}; + +/** + * ------------------------------------------ + * debugging tools + * ------------------------------------------ + */ + +loggers.log = function() { + var i; + + if(dfltConfig.logging > 1) { + var messages = ['LOG:']; + for(i = 0; i < arguments.length; i++) { + messages.push(arguments[i]); + } + console.trace.apply(console, messages); + } + + if(dfltConfig.notifyOnLogging > 1) { + var lines = []; + for(i = 0; i < arguments.length; i++) { + lines.push(arguments[i]); + } + notifier(lines.join('
'), 'long'); + } +}; + +loggers.warn = function() { + var i; + + if(dfltConfig.logging > 0) { + var messages = ['WARN:']; + for(i = 0; i < arguments.length; i++) { + messages.push(arguments[i]); + } + console.trace.apply(console, messages); + } + + if(dfltConfig.notifyOnLogging > 0) { + var lines = []; + for(i = 0; i < arguments.length; i++) { + lines.push(arguments[i]); + } + notifier(lines.join('
'), 'stick'); + } +}; + +loggers.error = function() { + var i; + + if(dfltConfig.logging > 0) { + var messages = ['ERROR:']; + for(i = 0; i < arguments.length; i++) { + messages.push(arguments[i]); + } + console.error.apply(console, messages); + } + + if(dfltConfig.notifyOnLogging > 0) { + var lines = []; + for(i = 0; i < arguments.length; i++) { + lines.push(arguments[i]); + } + notifier(lines.join('
'), 'stick'); + } +}; + +},{"../plot_api/plot_config":318,"./notifier":295}],290:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +/** + * General helper to manage trace groups based on calcdata + * + * @param {d3.selection} traceLayer: a selection containing a single group + * to draw these traces into + * @param {array} cdModule: array of calcdata items for this + * module and subplot combination. Assumes the calcdata item for each + * trace is an array with the fullData trace attached to the first item. + * @param {string} cls: the class attribute to give each trace group + * so you can give multiple classes separated by spaces + */ +module.exports = function makeTraceGroups(traceLayer, cdModule, cls) { + var traces = traceLayer.selectAll('g.' + cls.replace(/\s/g, '.')) + .data(cdModule, function(cd) { return cd[0].trace.uid; }); + + traces.exit().remove(); + + traces.enter().append('g') + .attr('class', cls); + + traces.order(); + + // stash ref node to trace group in calcdata, + // useful for (fast) styleOnSelect + var k = traceLayer.classed('rangeplot') ? 'nodeRangePlot3' : 'node3'; + traces.each(function(cd) { cd[0][k] = d3.select(this); }); + + return traces; +}; + +},{"@plotly/d3":20}],291:[function(_dereq_,module,exports){ +'use strict'; + +var mat4X4 = _dereq_('gl-mat4'); + +exports.init2dArray = function(rowLength, colLength) { + var array = new Array(rowLength); + for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength); + return array; +}; + +/** + * transpose a (possibly ragged) 2d array z. inspired by + * http://stackoverflow.com/questions/17428587/ + * transposing-a-2d-array-in-javascript + */ +exports.transposeRagged = function(z) { + var maxlen = 0; + var zlen = z.length; + var i, j; + // Maximum row length: + for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length); + + var t = new Array(maxlen); + for(i = 0; i < maxlen; i++) { + t[i] = new Array(zlen); + for(j = 0; j < zlen; j++) t[i][j] = z[j][i]; + } + + return t; +}; + +// our own dot function so that we don't need to include numeric +exports.dot = function(x, y) { + if(!(x.length && y.length) || x.length !== y.length) return null; + + var len = x.length; + var out; + var i; + + if(x[0].length) { + // mat-vec or mat-mat + out = new Array(len); + for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y); + } else if(y[0].length) { + // vec-mat + var yTranspose = exports.transposeRagged(y); + out = new Array(yTranspose.length); + for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]); + } else { + // vec-vec + out = 0; + for(i = 0; i < len; i++) out += x[i] * y[i]; + } + + return out; +}; + +// translate by (x,y) +exports.translationMatrix = function(x, y) { + return [[1, 0, x], [0, 1, y], [0, 0, 1]]; +}; + +// rotate by alpha around (0,0) +exports.rotationMatrix = function(alpha) { + var a = alpha * Math.PI / 180; + return [[Math.cos(a), -Math.sin(a), 0], + [Math.sin(a), Math.cos(a), 0], + [0, 0, 1]]; +}; + +// rotate by alpha around (x,y) +exports.rotationXYMatrix = function(a, x, y) { + return exports.dot( + exports.dot(exports.translationMatrix(x, y), + exports.rotationMatrix(a)), + exports.translationMatrix(-x, -y)); +}; + +// applies a 3D transformation matrix to either x, y and z params +// Note: z is optional +exports.apply3DTransform = function(transform) { + return function() { + var args = arguments; + var xyz = arguments.length === 1 ? args[0] : [args[0], args[1], args[2] || 0]; + return exports.dot(transform, [xyz[0], xyz[1], xyz[2], 1]).slice(0, 3); + }; +}; + +// applies a 2D transformation matrix to either x and y params or an [x,y] array +exports.apply2DTransform = function(transform) { + return function() { + var args = arguments; + if(args.length === 3) { + args = args[0]; + } // from map + var xy = arguments.length === 1 ? args[0] : [args[0], args[1]]; + return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2); + }; +}; + +// applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment) +exports.apply2DTransform2 = function(transform) { + var at = exports.apply2DTransform(transform); + return function(xys) { + return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4))); + }; +}; + +exports.convertCssMatrix = function(m) { + if(m) { + var len = m.length; + if(len === 16) return m; + if(len === 6) { + // converts a 2x3 css transform matrix to a 4x4 matrix see https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix + return [ + m[0], m[1], 0, 0, + m[2], m[3], 0, 0, + 0, 0, 1, 0, + m[4], m[5], 0, 1 + ]; + } + } + return [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; +}; + +// find the inverse for a 4x4 affine transform matrix +exports.inverseTransformMatrix = function(m) { + var out = []; + mat4X4.invert(out, m); + return [ + [out[0], out[1], out[2], out[3]], + [out[4], out[5], out[6], out[7]], + [out[8], out[9], out[10], out[11]], + [out[12], out[13], out[14], out[15]] + ]; +}; + +},{"gl-mat4":47}],292:[function(_dereq_,module,exports){ +'use strict'; + +/** + * sanitized modulus function that always returns in the range [0, d) + * rather than (-d, 0] if v is negative + */ +function mod(v, d) { + var out = v % d; + return out < 0 ? out + d : out; +} + +/** + * sanitized modulus function that always returns in the range [-d/2, d/2] + * rather than (-d, 0] if v is negative + */ +function modHalf(v, d) { + return Math.abs(v) > (d / 2) ? + v - Math.round(v / d) * d : + v; +} + +module.exports = { + mod: mod, + modHalf: modHalf +}; + +},{}],293:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray; + +/** + * convert a string s (such as 'xaxis.range[0]') + * representing a property of nested object into set and get methods + * also return the string and object so we don't have to keep track of them + * allows [-1] for an array index, to set a property inside all elements + * of an array + * eg if obj = {arr: [{a: 1}, {a: 2}]} + * you can do p = nestedProperty(obj, 'arr[-1].a') + * but you cannot set the array itself this way, to do that + * just set the whole array. + * eg if obj = {arr: [1, 2, 3]} + * you can't do nestedProperty(obj, 'arr[-1]').set(5) + * but you can do nestedProperty(obj, 'arr').set([5, 5, 5]) + */ +module.exports = function nestedProperty(container, propStr) { + if(isNumeric(propStr)) propStr = String(propStr); + else if(typeof propStr !== 'string' || + propStr.substr(propStr.length - 4) === '[-1]') { + throw 'bad property string'; + } + + var j = 0; + var propParts = propStr.split('.'); + var indexed; + var indices; + var i; + + // check for parts of the nesting hierarchy that are numbers (ie array elements) + while(j < propParts.length) { + // look for non-bracket chars, then any number of [##] blocks + indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/); + if(indexed) { + if(indexed[1]) propParts[j] = indexed[1]; + // allow propStr to start with bracketed array indices + else if(j === 0) propParts.splice(0, 1); + else throw 'bad property string'; + + indices = indexed[2] + .substr(1, indexed[2].length - 2) + .split(']['); + + for(i = 0; i < indices.length; i++) { + j++; + propParts.splice(j, 0, Number(indices[i])); + } + } + j++; + } + + if(typeof container !== 'object') { + return badContainer(container, propStr, propParts); + } + + return { + set: npSet(container, propParts, propStr), + get: npGet(container, propParts), + astr: propStr, + parts: propParts, + obj: container + }; +}; + +function npGet(cont, parts) { + return function() { + var curCont = cont; + var curPart; + var allSame; + var out; + var i; + var j; + + for(i = 0; i < parts.length - 1; i++) { + curPart = parts[i]; + if(curPart === -1) { + allSame = true; + out = []; + for(j = 0; j < curCont.length; j++) { + out[j] = npGet(curCont[j], parts.slice(i + 1))(); + if(out[j] !== out[0]) allSame = false; + } + return allSame ? out[0] : out; + } + if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) { + return undefined; + } + curCont = curCont[curPart]; + if(typeof curCont !== 'object' || curCont === null) { + return undefined; + } + } + + // only hit this if parts.length === 1 + if(typeof curCont !== 'object' || curCont === null) return undefined; + + out = curCont[parts[i]]; + if(out === null) return undefined; + return out; + }; +} + +/* + * Can this value be deleted? We can delete `undefined`, and `null` except INSIDE an + * *args* array. + * + * Previously we also deleted some `{}` and `[]`, in order to try and make set/unset + * a net noop; but this causes far more complication than it's worth, and still had + * lots of exceptions. See https://github.com/plotly/plotly.js/issues/1410 + * + * *args* arrays get passed directly to API methods and we should respect null if + * the user put it there, but otherwise null is deleted as we use it as code + * in restyle/relayout/update for "delete this value" whereas undefined means + * "ignore this edit" + */ +var ARGS_PATTERN = /(^|\.)args\[/; +function isDeletable(val, propStr) { + return (val === undefined) || (val === null && !propStr.match(ARGS_PATTERN)); +} + +function npSet(cont, parts, propStr) { + return function(val) { + var curCont = cont; + var propPart = ''; + var containerLevels = [[cont, propPart]]; + var toDelete = isDeletable(val, propStr); + var curPart; + var i; + + for(i = 0; i < parts.length - 1; i++) { + curPart = parts[i]; + + if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) { + throw 'array index but container is not an array'; + } + + // handle special -1 array index + if(curPart === -1) { + toDelete = !setArrayAll(curCont, parts.slice(i + 1), val, propStr); + if(toDelete) break; + else return; + } + + if(!checkNewContainer(curCont, curPart, parts[i + 1], toDelete)) { + break; + } + + curCont = curCont[curPart]; + + if(typeof curCont !== 'object' || curCont === null) { + throw 'container is not an object'; + } + + propPart = joinPropStr(propPart, curPart); + + containerLevels.push([curCont, propPart]); + } + + if(toDelete) { + if(i === parts.length - 1) { + delete curCont[parts[i]]; + + // The one bit of pruning we still do: drop `undefined` from the end of arrays. + // In case someone has already unset previous items, continue until we hit a + // non-undefined value. + if(Array.isArray(curCont) && +parts[i] === curCont.length - 1) { + while(curCont.length && curCont[curCont.length - 1] === undefined) { + curCont.pop(); + } + } + } + } else curCont[parts[i]] = val; + }; +} + +function joinPropStr(propStr, newPart) { + var toAdd = newPart; + if(isNumeric(newPart)) toAdd = '[' + newPart + ']'; + else if(propStr) toAdd = '.' + newPart; + + return propStr + toAdd; +} + +// handle special -1 array index +function setArrayAll(containerArray, innerParts, val, propStr) { + var arrayVal = isArrayOrTypedArray(val); + var allSet = true; + var thisVal = val; + var thisPropStr = propStr.replace('-1', 0); + var deleteThis = arrayVal ? false : isDeletable(val, thisPropStr); + var firstPart = innerParts[0]; + var i; + + for(i = 0; i < containerArray.length; i++) { + thisPropStr = propStr.replace('-1', i); + if(arrayVal) { + thisVal = val[i % val.length]; + deleteThis = isDeletable(thisVal, thisPropStr); + } + if(deleteThis) allSet = false; + if(!checkNewContainer(containerArray, i, firstPart, deleteThis)) { + continue; + } + npSet(containerArray[i], innerParts, propStr.replace('-1', i))(thisVal); + } + return allSet; +} + +/** + * make new sub-container as needed. + * returns false if there's no container and none is needed + * because we're only deleting an attribute + */ +function checkNewContainer(container, part, nextPart, toDelete) { + if(container[part] === undefined) { + if(toDelete) return false; + + if(typeof nextPart === 'number') container[part] = []; + else container[part] = {}; + } + return true; +} + +function badContainer(container, propStr, propParts) { + return { + set: function() { throw 'bad container'; }, + get: function() {}, + astr: propStr, + parts: propParts, + obj: container + }; +} + +},{"./array":271,"fast-isnumeric":31}],294:[function(_dereq_,module,exports){ +'use strict'; + +// Simple helper functions +// none of these need any external deps + +module.exports = function noop() {}; + +},{}],295:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var NOTEDATA = []; + +/** + * notifier + * @param {String} text The person's user name + * @param {Number} [delay=1000] The delay time in milliseconds + * or 'long' which provides 2000 ms delay time. + * @return {undefined} this function does not return a value + */ +module.exports = function(text, displayLength) { + if(NOTEDATA.indexOf(text) !== -1) return; + + NOTEDATA.push(text); + + var ts = 1000; + if(isNumeric(displayLength)) ts = displayLength; + else if(displayLength === 'long') ts = 3000; + + var notifierContainer = d3.select('body') + .selectAll('.plotly-notifier') + .data([0]); + notifierContainer.enter() + .append('div') + .classed('plotly-notifier', true); + + var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA); + + function killNote(transition) { + transition + .duration(700) + .style('opacity', 0) + .each('end', function(thisText) { + var thisIndex = NOTEDATA.indexOf(thisText); + if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1); + d3.select(this).remove(); + }); + } + + notes.enter().append('div') + .classed('notifier-note', true) + .style('opacity', 0) + .each(function(thisText) { + var note = d3.select(this); + + note.append('button') + .classed('notifier-close', true) + .html('×') + .on('click', function() { + note.transition().call(killNote); + }); + + var p = note.append('p'); + var lines = thisText.split(//g); + for(var i = 0; i < lines.length; i++) { + if(i) p.append('br'); + p.append('span').text(lines[i]); + } + + if(displayLength === 'stick') { + note.transition() + .duration(350) + .style('opacity', 1); + } else { + note.transition() + .duration(700) + .style('opacity', 1) + .transition() + .delay(ts) + .call(killNote); + } + }); +}; + +},{"@plotly/d3":20,"fast-isnumeric":31}],296:[function(_dereq_,module,exports){ +'use strict'; + +var setCursor = _dereq_('./setcursor'); + +var STASHATTR = 'data-savedcursor'; +var NO_CURSOR = '!!'; + +/* + * works with our CSS cursor classes (see css/_cursor.scss) + * to override a previous cursor set on d3 single-element selections, + * by moving the name of the original cursor to the data-savedcursor attr. + * omit cursor to revert to the previously set value. + */ +module.exports = function overrideCursor(el3, csr) { + var savedCursor = el3.attr(STASHATTR); + if(csr) { + if(!savedCursor) { + var classes = (el3.attr('class') || '').split(' '); + for(var i = 0; i < classes.length; i++) { + var cls = classes[i]; + if(cls.indexOf('cursor-') === 0) { + el3.attr(STASHATTR, cls.substr(7)) + .classed(cls, false); + } + } + if(!el3.attr(STASHATTR)) { + el3.attr(STASHATTR, NO_CURSOR); + } + } + setCursor(el3, csr); + } else if(savedCursor) { + el3.attr(STASHATTR, null); + + if(savedCursor === NO_CURSOR) setCursor(el3); + else setCursor(el3, savedCursor); + } +}; + +},{"./setcursor":305}],297:[function(_dereq_,module,exports){ +'use strict'; + +var dot = _dereq_('./matrix').dot; +var BADNUM = _dereq_('../constants/numerical').BADNUM; + +var polygon = module.exports = {}; + +/** + * Turn an array of [x, y] pairs into a polygon object + * that can test if points are inside it + * + * @param ptsIn Array of [x, y] pairs + * + * @returns polygon Object {xmin, xmax, ymin, ymax, pts, contains} + * (x|y)(min|max) are the bounding rect of the polygon + * pts is the original array, with the first pair repeated at the end + * contains is a function: (pt, omitFirstEdge) + * pt is the [x, y] pair to test + * omitFirstEdge truthy means points exactly on the first edge don't + * count. This is for use adding one polygon to another so we + * don't double-count the edge where they meet. + * returns boolean: is pt inside the polygon (including on its edges) + */ +polygon.tester = function tester(ptsIn) { + var pts = ptsIn.slice(); + var xmin = pts[0][0]; + var xmax = xmin; + var ymin = pts[0][1]; + var ymax = ymin; + var i; + + pts.push(pts[0]); + for(i = 1; i < pts.length; i++) { + xmin = Math.min(xmin, pts[i][0]); + xmax = Math.max(xmax, pts[i][0]); + ymin = Math.min(ymin, pts[i][1]); + ymax = Math.max(ymax, pts[i][1]); + } + + // do we have a rectangle? Handle this here, so we can use the same + // tester for the rectangular case without sacrificing speed + + var isRect = false; + var rectFirstEdgeTest; + + if(pts.length === 5) { + if(pts[0][0] === pts[1][0]) { // vert, horz, vert, horz + if(pts[2][0] === pts[3][0] && + pts[0][1] === pts[3][1] && + pts[1][1] === pts[2][1]) { + isRect = true; + rectFirstEdgeTest = function(pt) { return pt[0] === pts[0][0]; }; + } + } else if(pts[0][1] === pts[1][1]) { // horz, vert, horz, vert + if(pts[2][1] === pts[3][1] && + pts[0][0] === pts[3][0] && + pts[1][0] === pts[2][0]) { + isRect = true; + rectFirstEdgeTest = function(pt) { return pt[1] === pts[0][1]; }; + } + } + } + + function rectContains(pt, omitFirstEdge) { + var x = pt[0]; + var y = pt[1]; + + if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) { + // pt is outside the bounding box of polygon + return false; + } + if(omitFirstEdge && rectFirstEdgeTest(pt)) return false; + + return true; + } + + function contains(pt, omitFirstEdge) { + var x = pt[0]; + var y = pt[1]; + + if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) { + // pt is outside the bounding box of polygon + return false; + } + + var imax = pts.length; + var x1 = pts[0][0]; + var y1 = pts[0][1]; + var crossings = 0; + var i; + var x0; + var y0; + var xmini; + var ycross; + + for(i = 1; i < imax; i++) { + // find all crossings of a vertical line upward from pt with + // polygon segments + // crossings exactly at xmax don't count, unless the point is + // exactly on the segment, then it counts as inside. + x0 = x1; + y0 = y1; + x1 = pts[i][0]; + y1 = pts[i][1]; + xmini = Math.min(x0, x1); + + if(x < xmini || x > Math.max(x0, x1) || y > Math.max(y0, y1)) { + // outside the bounding box of this segment, it's only a crossing + // if it's below the box. + + continue; + } else if(y < Math.min(y0, y1)) { + // don't count the left-most point of the segment as a crossing + // because we don't want to double-count adjacent crossings + // UNLESS the polygon turns past vertical at exactly this x + // Note that this is repeated below, but we can't factor it out + // because + if(x !== xmini) crossings++; + } else { + // inside the bounding box, check the actual line intercept + + // vertical segment - we know already that the point is exactly + // on the segment, so mark the crossing as exactly at the point. + if(x1 === x0) ycross = y; + // any other angle + else ycross = y0 + (x - x0) * (y1 - y0) / (x1 - x0); + + // exactly on the edge: counts as inside the polygon, unless it's the + // first edge and we're omitting it. + if(y === ycross) { + if(i === 1 && omitFirstEdge) return false; + return true; + } + + if(y <= ycross && x !== xmini) crossings++; + } + } + + // if we've gotten this far, odd crossings means inside, even is outside + return crossings % 2 === 1; + } + + // detect if poly is degenerate + var degenerate = true; + var lastPt = pts[0]; + for(i = 1; i < pts.length; i++) { + if(lastPt[0] !== pts[i][0] || lastPt[1] !== pts[i][1]) { + degenerate = false; + break; + } + } + + return { + xmin: xmin, + xmax: xmax, + ymin: ymin, + ymax: ymax, + pts: pts, + contains: isRect ? rectContains : contains, + isRect: isRect, + degenerate: degenerate + }; +}; + +/** + * Test if a segment of a points array is bent or straight + * + * @param pts Array of [x, y] pairs + * @param start the index of the proposed start of the straight section + * @param end the index of the proposed end point + * @param tolerance the max distance off the line connecting start and end + * before the line counts as bent + * @returns boolean: true means this segment is bent, false means straight + */ +polygon.isSegmentBent = function isSegmentBent(pts, start, end, tolerance) { + var startPt = pts[start]; + var segment = [pts[end][0] - startPt[0], pts[end][1] - startPt[1]]; + var segmentSquared = dot(segment, segment); + var segmentLen = Math.sqrt(segmentSquared); + var unitPerp = [-segment[1] / segmentLen, segment[0] / segmentLen]; + var i; + var part; + var partParallel; + + for(i = start + 1; i < end; i++) { + part = [pts[i][0] - startPt[0], pts[i][1] - startPt[1]]; + partParallel = dot(part, segment); + + if(partParallel < 0 || partParallel > segmentSquared || + Math.abs(dot(part, unitPerp)) > tolerance) return true; + } + return false; +}; + +/** + * Make a filtering polygon, to minimize the number of segments + * + * @param pts Array of [x, y] pairs (must start with at least 1 pair) + * @param tolerance the maximum deviation from straight allowed for + * removing points to simplify the polygon + * + * @returns Object {addPt, raw, filtered} + * addPt is a function(pt: [x, y] pair) to add a raw point and + * continue filtering + * raw is all the input points + * filtered is the resulting filtered Array of [x, y] pairs + */ +polygon.filter = function filter(pts, tolerance) { + var ptsFiltered = [pts[0]]; + var doneRawIndex = 0; + var doneFilteredIndex = 0; + + function addPt(pt) { + pts.push(pt); + var prevFilterLen = ptsFiltered.length; + var iLast = doneRawIndex; + ptsFiltered.splice(doneFilteredIndex + 1); + + for(var i = iLast + 1; i < pts.length; i++) { + if(i === pts.length - 1 || polygon.isSegmentBent(pts, iLast, i + 1, tolerance)) { + ptsFiltered.push(pts[i]); + if(ptsFiltered.length < prevFilterLen - 2) { + doneRawIndex = i; + doneFilteredIndex = ptsFiltered.length - 1; + } + iLast = i; + } + } + } + + if(pts.length > 1) { + var lastPt = pts.pop(); + addPt(lastPt); + } + + return { + addPt: addPt, + raw: pts, + filtered: ptsFiltered + }; +}; + +},{"../constants/numerical":265,"./matrix":291}],298:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var isMobileOrTablet = _dereq_('is-mobile'); + +module.exports = function preserveDrawingBuffer(opts) { + var ua; + + if(opts && opts.hasOwnProperty('userAgent')) { + ua = opts.userAgent; + } else { + ua = getUserAgent(); + } + + if(typeof ua !== 'string') return true; + + var enable = isMobileOrTablet({ + ua: { headers: {'user-agent': ua }}, + tablet: true, + featureDetect: false + }); + + if(!enable) { + var allParts = ua.split(' '); + for(var i = 1; i < allParts.length; i++) { + var part = allParts[i]; + if(part.indexOf('Safari') !== -1) { + // find Safari version + for(var k = i - 1; k > -1; k--) { + var prevPart = allParts[k]; + if(prevPart.substr(0, 8) === 'Version/') { + var v = prevPart.substr(8).split('.')[0]; + if(isNumeric(v)) v = +v; + if(v >= 13) return true; + } + } + } + } + } + + return enable; +}; + +function getUserAgent() { + // similar to https://github.com/juliangruber/is-mobile/blob/91ca39ccdd4cfc5edfb5391e2515b923a730fbea/index.js#L14-L17 + var ua; + if(typeof navigator !== 'undefined') { + ua = navigator.userAgent; + } + + if( + ua && + ua.headers && + typeof ua.headers['user-agent'] === 'string' + ) { + ua = ua.headers['user-agent']; + } + + return ua; +} + +},{"fast-isnumeric":31,"is-mobile":67}],299:[function(_dereq_,module,exports){ +'use strict'; + +/** + * Push array with unique items + * + * Ignores falsy items, except 0 so we can use it to construct arrays of indices. + * + * @param {array} array + * array to be filled + * @param {any} item + * item to be or not to be inserted + * @return {array} + * ref to array (now possibly containing one more item) + * + */ +module.exports = function pushUnique(array, item) { + if(item instanceof RegExp) { + var itemStr = item.toString(); + for(var i = 0; i < array.length; i++) { + if(array[i] instanceof RegExp && array[i].toString() === itemStr) { + return array; + } + } + array.push(item); + } else if((item || item === 0) && array.indexOf(item) === -1) array.push(item); + + return array; +}; + +},{}],300:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig; + +/** + * Copy arg array *without* removing `undefined` values from objects. + * + * @param gd + * @param args + * @returns {Array} + */ +function copyArgArray(gd, args) { + var copy = []; + var arg; + + for(var i = 0; i < args.length; i++) { + arg = args[i]; + + if(arg === gd) copy[i] = arg; + else if(typeof arg === 'object') { + copy[i] = Array.isArray(arg) ? + Lib.extendDeep([], arg) : + Lib.extendDeepAll({}, arg); + } else copy[i] = arg; + } + + return copy; +} + + +// ----------------------------------------------------- +// Undo/Redo queue for plots +// ----------------------------------------------------- + + +var queue = {}; + +// TODO: disable/enable undo and redo buttons appropriately + +/** + * Add an item to the undoQueue for a graphDiv + * + * @param gd + * @param undoFunc Function undo this operation + * @param undoArgs Args to supply undoFunc with + * @param redoFunc Function to redo this operation + * @param redoArgs Args to supply redoFunc with + */ +queue.add = function(gd, undoFunc, undoArgs, redoFunc, redoArgs) { + var queueObj, + queueIndex; + + // make sure we have the queue and our position in it + gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false}; + queueIndex = gd.undoQueue.index; + + // if we're already playing an undo or redo, or if this is an auto operation + // (like pane resize... any others?) then we don't save this to the undo queue + if(gd.autoplay) { + if(!gd.undoQueue.inSequence) gd.autoplay = false; + return; + } + + // if we're not in a sequence or are just starting, we need a new queue item + if(!gd.undoQueue.sequence || gd.undoQueue.beginSequence) { + queueObj = {undo: {calls: [], args: []}, redo: {calls: [], args: []}}; + gd.undoQueue.queue.splice(queueIndex, gd.undoQueue.queue.length - queueIndex, queueObj); + gd.undoQueue.index += 1; + } else { + queueObj = gd.undoQueue.queue[queueIndex - 1]; + } + gd.undoQueue.beginSequence = false; + + // we unshift to handle calls for undo in a forward for loop later + if(queueObj) { + queueObj.undo.calls.unshift(undoFunc); + queueObj.undo.args.unshift(undoArgs); + queueObj.redo.calls.push(redoFunc); + queueObj.redo.args.push(redoArgs); + } + + if(gd.undoQueue.queue.length > dfltConfig.queueLength) { + gd.undoQueue.queue.shift(); + gd.undoQueue.index--; + } +}; + +/** + * Begin a sequence of undoQueue changes + * + * @param gd + */ +queue.startSequence = function(gd) { + gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false}; + gd.undoQueue.sequence = true; + gd.undoQueue.beginSequence = true; +}; + +/** + * Stop a sequence of undoQueue changes + * + * Call this *after* you're sure your undo chain has ended + * + * @param gd + */ +queue.stopSequence = function(gd) { + gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false}; + gd.undoQueue.sequence = false; + gd.undoQueue.beginSequence = false; +}; + +/** + * Move one step back in the undo queue, and undo the object there. + * + * @param gd + */ +queue.undo = function undo(gd) { + var queueObj, i; + + if(gd.undoQueue === undefined || + isNaN(gd.undoQueue.index) || + gd.undoQueue.index <= 0) { + return; + } + + // index is pointing to next *forward* queueObj, point to the one we're undoing + gd.undoQueue.index--; + + // get the queueObj for instructions on how to undo + queueObj = gd.undoQueue.queue[gd.undoQueue.index]; + + // this sequence keeps things from adding to the queue during undo/redo + gd.undoQueue.inSequence = true; + for(i = 0; i < queueObj.undo.calls.length; i++) { + queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]); + } + gd.undoQueue.inSequence = false; + gd.autoplay = false; +}; + +/** + * Redo the current object in the undo, then move forward in the queue. + * + * @param gd + */ +queue.redo = function redo(gd) { + var queueObj, i; + + if(gd.undoQueue === undefined || + isNaN(gd.undoQueue.index) || + gd.undoQueue.index >= gd.undoQueue.queue.length) { + return; + } + + // get the queueObj for instructions on how to undo + queueObj = gd.undoQueue.queue[gd.undoQueue.index]; + + // this sequence keeps things from adding to the queue during undo/redo + gd.undoQueue.inSequence = true; + for(i = 0; i < queueObj.redo.calls.length; i++) { + queue.plotDo(gd, queueObj.redo.calls[i], queueObj.redo.args[i]); + } + gd.undoQueue.inSequence = false; + gd.autoplay = false; + + // index is pointing to the thing we just redid, move it + gd.undoQueue.index++; +}; + +/** + * Called by undo/redo to make the actual changes. + * + * Not meant to be called publically, but included for mocking out in tests. + * + * @param gd + * @param func + * @param args + */ +queue.plotDo = function(gd, func, args) { + gd.autoplay = true; + + // this *won't* copy gd and it preserves `undefined` properties! + args = copyArgArray(gd, args); + + // call the supplied function + func.apply(null, args); +}; + +module.exports = queue; + +},{"../lib":285,"../plot_api/plot_config":318}],301:[function(_dereq_,module,exports){ +'use strict'; + +/* + * make a regex for matching counter ids/names ie xaxis, xaxis2, xaxis10... + * + * @param {string} head: the head of the pattern, eg 'x' matches 'x', 'x2', 'x10' etc. + * 'xy' is a special case for cartesian subplots: it matches 'x2y3' etc + * @param {Optional(string)} tail: a fixed piece after the id + * eg counterRegex('scene', '.annotations') for scene2.annotations etc. + * @param {boolean} openEnded: if true, the string may continue past the match. + * @param {boolean} matchBeginning: if false, the string may start before the match. + */ +exports.counter = function(head, tail, openEnded, matchBeginning) { + var fullTail = (tail || '') + (openEnded ? '' : '$'); + var startWithPrefix = matchBeginning === false ? '' : '^'; + if(head === 'xy') { + return new RegExp(startWithPrefix + 'x([2-9]|[1-9][0-9]+)?y([2-9]|[1-9][0-9]+)?' + fullTail); + } + return new RegExp(startWithPrefix + head + '([2-9]|[1-9][0-9]+)?' + fullTail); +}; + +},{}],302:[function(_dereq_,module,exports){ +'use strict'; + +// ASCEND: chop off the last nesting level - either [] or . - to ascend +// the attribute tree. the remaining attrString is in match[1] +var ASCEND = /^(.*)(\.[^\.\[\]]+|\[\d\])$/; + +// SIMPLEATTR: is this an un-nested attribute? (no dots or brackets) +var SIMPLEATTR = /^[^\.\[\]]+$/; + +/* + * calculate a relative attribute string, similar to a relative path + * + * @param {string} baseAttr: + * an attribute string, such as 'annotations[3].x'. The "current location" + * is the attribute string minus the last component ('annotations[3]') + * @param {string} relativeAttr: + * a route to the desired attribute string, using '^' to ascend + * + * @return {string} attrString: + * for example: + * relativeAttr('annotations[3].x', 'y') = 'annotations[3].y' + * relativeAttr('annotations[3].x', '^[2].z') = 'annotations[2].z' + * relativeAttr('annotations[3].x', '^^margin') = 'margin' + * relativeAttr('annotations[3].x', '^^margin.r') = 'margin.r' + */ +module.exports = function(baseAttr, relativeAttr) { + while(relativeAttr) { + var match = baseAttr.match(ASCEND); + + if(match) baseAttr = match[1]; + else if(baseAttr.match(SIMPLEATTR)) baseAttr = ''; + else throw new Error('bad relativeAttr call:' + [baseAttr, relativeAttr]); + + if(relativeAttr.charAt(0) === '^') relativeAttr = relativeAttr.slice(1); + else break; + } + + if(baseAttr && relativeAttr.charAt(0) !== '[') { + return baseAttr + '.' + relativeAttr; + } + return baseAttr + relativeAttr; +}; + +},{}],303:[function(_dereq_,module,exports){ +'use strict'; + +var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray; +var isPlainObject = _dereq_('./is_plain_object'); + +/** + * Relink private _keys and keys with a function value from one container + * to the new container. + * Relink means copying if object is pass-by-value and adding a reference + * if object is pass-by-ref. + * This prevents deepCopying massive structures like a webgl context. + */ +module.exports = function relinkPrivateKeys(toContainer, fromContainer) { + for(var k in fromContainer) { + var fromVal = fromContainer[k]; + var toVal = toContainer[k]; + + if(toVal === fromVal) continue; + + if(k.charAt(0) === '_' || typeof fromVal === 'function') { + // if it already exists at this point, it's something + // that we recreate each time around, so ignore it + if(k in toContainer) continue; + + toContainer[k] = fromVal; + } else if(isArrayOrTypedArray(fromVal) && isArrayOrTypedArray(toVal) && isPlainObject(fromVal[0])) { + // filter out data_array items that can contain user objects + // most of the time the toVal === fromVal check will catch these early + // but if the user makes new ones we also don't want to recurse in. + if(k === 'customdata' || k === 'ids') continue; + + // recurse into arrays containers + var minLen = Math.min(fromVal.length, toVal.length); + for(var j = 0; j < minLen; j++) { + if((toVal[j] !== fromVal[j]) && isPlainObject(fromVal[j]) && isPlainObject(toVal[j])) { + relinkPrivateKeys(toVal[j], fromVal[j]); + } + } + } else if(isPlainObject(fromVal) && isPlainObject(toVal)) { + // recurse into objects, but only if they still exist + relinkPrivateKeys(toVal, fromVal); + + if(!Object.keys(toVal).length) delete toContainer[k]; + } + } +}; + +},{"./array":271,"./is_plain_object":286}],304:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var loggers = _dereq_('./loggers'); +var identity = _dereq_('./identity'); +var BADNUM = _dereq_('../constants/numerical').BADNUM; + +// don't trust floating point equality - fraction of bin size to call +// "on the line" and ensure that they go the right way specified by +// linelow +var roundingError = 1e-9; + + +/** + * findBin - find the bin for val - note that it can return outside the + * bin range any pos. or neg. integer for linear bins, or -1 or + * bins.length-1 for explicit. + * bins is either an object {start,size,end} or an array length #bins+1 + * bins can be either increasing or decreasing but must be monotonic + * for linear bins, we can just calculate. For listed bins, run a binary + * search linelow (truthy) says the bin boundary should be attributed to + * the lower bin rather than the default upper bin + */ +exports.findBin = function(val, bins, linelow) { + if(isNumeric(bins.start)) { + return linelow ? + Math.ceil((val - bins.start) / bins.size - roundingError) - 1 : + Math.floor((val - bins.start) / bins.size + roundingError); + } else { + var n1 = 0; + var n2 = bins.length; + var c = 0; + var binSize = (n2 > 1) ? (bins[n2 - 1] - bins[0]) / (n2 - 1) : 1; + var n, test; + if(binSize >= 0) { + test = linelow ? lessThan : lessOrEqual; + } else { + test = linelow ? greaterOrEqual : greaterThan; + } + val += binSize * roundingError * (linelow ? -1 : 1) * (binSize >= 0 ? 1 : -1); + // c is just to avoid infinite loops if there's an error + while(n1 < n2 && c++ < 100) { + n = Math.floor((n1 + n2) / 2); + if(test(bins[n], val)) n1 = n + 1; + else n2 = n; + } + if(c > 90) loggers.log('Long binary search...'); + return n1 - 1; + } +}; + +function lessThan(a, b) { return a < b; } +function lessOrEqual(a, b) { return a <= b; } +function greaterThan(a, b) { return a > b; } +function greaterOrEqual(a, b) { return a >= b; } + +exports.sorterAsc = function(a, b) { return a - b; }; +exports.sorterDes = function(a, b) { return b - a; }; + +/** + * find distinct values in an array, lumping together ones that appear to + * just be off by a rounding error + * return the distinct values and the minimum difference between any two + */ +exports.distinctVals = function(valsIn) { + var vals = valsIn.slice(); // otherwise we sort the original array... + vals.sort(exports.sorterAsc); // undefined listed in the end - also works on IE11 + + var last; + for(last = vals.length - 1; last > -1; last--) { + if(vals[last] !== BADNUM) break; + } + + var minDiff = (vals[last] - vals[0]) || 1; + var errDiff = minDiff / (last || 1) / 10000; + var newVals = []; + var preV; + for(var i = 0; i <= last; i++) { + var v = vals[i]; + + // make sure values aren't just off by a rounding error + var diff = v - preV; + + if(preV === undefined) { + newVals.push(v); + preV = v; + } else if(diff > errDiff) { + minDiff = Math.min(minDiff, diff); + + newVals.push(v); + preV = v; + } + } + + return {vals: newVals, minDiff: minDiff}; +}; + +/** + * return the smallest element from (sorted) array arrayIn that's bigger than val, + * or (reverse) the largest element smaller than val + * used to find the best tick given the minimum (non-rounded) tick + * particularly useful for date/time where things are not powers of 10 + * binary search is probably overkill here... + */ +exports.roundUp = function(val, arrayIn, reverse) { + var low = 0; + var high = arrayIn.length - 1; + var mid; + var c = 0; + var dlow = reverse ? 0 : 1; + var dhigh = reverse ? 1 : 0; + var rounded = reverse ? Math.ceil : Math.floor; + // c is just to avoid infinite loops if there's an error + while(low < high && c++ < 100) { + mid = rounded((low + high) / 2); + if(arrayIn[mid] <= val) low = mid + dlow; + else high = mid - dhigh; + } + return arrayIn[low]; +}; + +/** + * Tweak to Array.sort(sortFn) that improves performance for pre-sorted arrays + * + * Note that newer browsers (such as Chrome v70+) are starting to pick up + * on pre-sorted arrays which may render the following optimization unnecessary + * in the future. + * + * Motivation: sometimes we need to sort arrays but the input is likely to + * already be sorted. Browsers don't seem to pick up on pre-sorted arrays, + * and in fact Chrome is actually *slower* sorting pre-sorted arrays than purely + * random arrays. FF is at least faster if the array is pre-sorted, but still + * not as fast as it could be. + * Here's how this plays out sorting a length-1e6 array: + * + * Calls to Sort FN | Chrome bare | FF bare | Chrome tweak | FF tweak + * | v68.0 Mac | v61.0 Mac| | + * ------------------+---------------+-----------+----------------+------------ + * ordered | 30.4e6 | 10.1e6 | 1e6 | 1e6 + * reversed | 29.4e6 | 9.9e6 | 1e6 + reverse | 1e6 + reverse + * random | ~21e6 | ~18.7e6 | ~21e6 | ~18.7e6 + * + * So this is a substantial win for pre-sorted (ordered or exactly reversed) + * arrays. Including this wrapper on an unsorted array adds a penalty that will + * in general be only a few calls to the sort function. The only case this + * penalty will be significant is if the array is mostly sorted but there are + * a few unsorted items near the end, but the penalty is still at most N calls + * out of (for N=1e6) ~20N total calls + * + * @param {Array} array: the array, to be sorted in place + * @param {function} sortFn: As in Array.sort, function(a, b) that puts + * item a before item b if the return is negative, a after b if positive, + * and no change if zero. + * @return {Array}: the original array, sorted in place. + */ +exports.sort = function(array, sortFn) { + var notOrdered = 0; + var notReversed = 0; + for(var i = 1; i < array.length; i++) { + var pairOrder = sortFn(array[i], array[i - 1]); + if(pairOrder < 0) notOrdered = 1; + else if(pairOrder > 0) notReversed = 1; + if(notOrdered && notReversed) return array.sort(sortFn); + } + return notReversed ? array : array.reverse(); +}; + +/** + * find index in array 'arr' that minimizes 'fn' + * + * @param {array} arr : array where to search + * @param {fn (optional)} fn : function to minimize, + * if not given, fn is the identity function + * @return {integer} + */ +exports.findIndexOfMin = function(arr, fn) { + fn = fn || identity; + + var min = Infinity; + var ind; + + for(var i = 0; i < arr.length; i++) { + var v = fn(arr[i]); + if(v < min) { + min = v; + ind = i; + } + } + return ind; +}; + +},{"../constants/numerical":265,"./identity":283,"./loggers":289,"fast-isnumeric":31}],305:[function(_dereq_,module,exports){ +'use strict'; + +// works with our CSS cursor classes (see css/_cursor.scss) +// to apply cursors to d3 single-element selections. +// omit cursor to revert to the default. +module.exports = function setCursor(el3, csr) { + (el3.attr('class') || '').split(' ').forEach(function(cls) { + if(cls.indexOf('cursor-') === 0) el3.classed(cls, false); + }); + + if(csr) el3.classed('cursor-' + csr, true); +}; + +},{}],306:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray; + +/** + * aggNums() returns the result of an aggregate function applied to an array of + * values, where non-numerical values have been tossed out. + * + * @param {function} f - aggregation function (e.g., Math.min) + * @param {Number} v - initial value (continuing from previous calls) + * if there's no continuing value, use null for selector-type + * functions (max,min), or 0 for summations + * @param {Array} a - array to aggregate (may be nested, we will recurse, + * but all elements must have the same dimension) + * @param {Number} len - maximum length of a to aggregate + * @return {Number} - result of f applied to a starting from v + */ +exports.aggNums = function(f, v, a, len) { + var i, + b; + if(!len || len > a.length) len = a.length; + if(!isNumeric(v)) v = false; + if(isArrayOrTypedArray(a[0])) { + b = new Array(len); + for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]); + a = b; + } + + for(i = 0; i < len; i++) { + if(!isNumeric(v)) v = a[i]; + else if(isNumeric(a[i])) v = f(+v, +a[i]); + } + return v; +}; + +/** + * mean & std dev functions using aggNums, so it handles non-numerics nicely + * even need to use aggNums instead of .length, to toss out non-numerics + */ +exports.len = function(data) { + return exports.aggNums(function(a) { return a + 1; }, 0, data); +}; + +exports.mean = function(data, len) { + if(!len) len = exports.len(data); + return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len; +}; + +exports.midRange = function(numArr) { + if(numArr === undefined || numArr.length === 0) return undefined; + return (exports.aggNums(Math.max, null, numArr) + exports.aggNums(Math.min, null, numArr)) / 2; +}; + +exports.variance = function(data, len, mean) { + if(!len) len = exports.len(data); + if(!isNumeric(mean)) mean = exports.mean(data, len); + + return exports.aggNums(function(a, b) { + return a + Math.pow(b - mean, 2); + }, 0, data) / len; +}; + +exports.stdev = function(data, len, mean) { + return Math.sqrt(exports.variance(data, len, mean)); +}; + +/** + * median of a finite set of numbers + * reference page: https://en.wikipedia.org/wiki/Median#Finite_set_of_numbers +**/ +exports.median = function(data) { + var b = data.slice().sort(); + return exports.interp(b, 0.5); +}; + +/** + * interp() computes a percentile (quantile) for a given distribution. + * We interpolate the distribution (to compute quantiles, we follow method #10 here: + * http://www.amstat.org/publications/jse/v14n3/langford.html). + * Typically the index or rank (n * arr.length) may be non-integer. + * For reference: ends are clipped to the extreme values in the array; + * For box plots: index you get is half a point too high (see + * http://en.wikipedia.org/wiki/Percentile#Nearest_rank) but note that this definition + * indexes from 1 rather than 0, so we subtract 1/2 (instead of add). + * + * @param {Array} arr - This array contains the values that make up the distribution. + * @param {Number} n - Between 0 and 1, n = p/100 is such that we compute the p^th percentile. + * For example, the 50th percentile (or median) corresponds to n = 0.5 + * @return {Number} - percentile + */ +exports.interp = function(arr, n) { + if(!isNumeric(n)) throw 'n should be a finite number'; + n = n * arr.length - 0.5; + if(n < 0) return arr[0]; + if(n > arr.length - 1) return arr[arr.length - 1]; + var frac = n % 1; + return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)]; +}; + +},{"./array":271,"fast-isnumeric":31}],307:[function(_dereq_,module,exports){ +'use strict'; + +/* global MathJax:false */ + +var d3 = _dereq_('@plotly/d3'); + +var Lib = _dereq_('../lib'); +var strTranslate = Lib.strTranslate; +var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces'); +var LINE_SPACING = _dereq_('../constants/alignment').LINE_SPACING; + +// text converter + +function getSize(_selection, _dimension) { + return _selection.node().getBoundingClientRect()[_dimension]; +} + +var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/; + +exports.convertToTspans = function(_context, gd, _callback) { + var str = _context.text(); + + // Until we get tex integrated more fully (so it can be used along with non-tex) + // allow some elements to prohibit it by attaching 'data-notex' to the original + var tex = (!_context.attr('data-notex')) && + (typeof MathJax !== 'undefined') && + str.match(FIND_TEX); + + var parent = d3.select(_context.node().parentNode); + if(parent.empty()) return; + var svgClass = (_context.attr('class')) ? _context.attr('class').split(' ')[0] : 'text'; + svgClass += '-math'; + parent.selectAll('svg.' + svgClass).remove(); + parent.selectAll('g.' + svgClass + '-group').remove(); + _context.style('display', null) + .attr({ + // some callers use data-unformatted *from the element* in 'cancel' + // so we need it here even if we're going to turn it into math + // these two (plus style and text-anchor attributes) form the key we're + // going to use for Drawing.bBox + 'data-unformatted': str, + 'data-math': 'N' + }); + + function showText() { + if(!parent.empty()) { + svgClass = _context.attr('class') + '-math'; + parent.select('svg.' + svgClass).remove(); + } + _context.text('') + .style('white-space', 'pre'); + + var hasLink = buildSVGText(_context.node(), str); + + if(hasLink) { + // at least in Chrome, pointer-events does not seem + // to be honored in children of elements + // so if we have an anchor, we have to make the + // whole element respond + _context.style('pointer-events', 'all'); + } + + exports.positionText(_context); + + if(_callback) _callback.call(_context); + } + + if(tex) { + ((gd && gd._promises) || []).push(new Promise(function(resolve) { + _context.style('display', 'none'); + var fontSize = parseInt(_context.node().style.fontSize, 10); + var config = {fontSize: fontSize}; + + texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) { + parent.selectAll('svg.' + svgClass).remove(); + parent.selectAll('g.' + svgClass + '-group').remove(); + + var newSvg = _svgEl && _svgEl.select('svg'); + if(!newSvg || !newSvg.node()) { + showText(); + resolve(); + return; + } + + var mathjaxGroup = parent.append('g') + .classed(svgClass + '-group', true) + .attr({ + 'pointer-events': 'none', + 'data-unformatted': str, + 'data-math': 'Y' + }); + + mathjaxGroup.node().appendChild(newSvg.node()); + + // stitch the glyph defs + if(_glyphDefs && _glyphDefs.node()) { + newSvg.node().insertBefore(_glyphDefs.node().cloneNode(true), + newSvg.node().firstChild); + } + + newSvg.attr({ + 'class': svgClass, + height: _svgBBox.height, + preserveAspectRatio: 'xMinYMin meet' + }) + .style({overflow: 'visible', 'pointer-events': 'none'}); + + var fill = _context.node().style.fill || 'black'; + var g = newSvg.select('g'); + g.attr({fill: fill, stroke: fill}); + + var newSvgW = getSize(g, 'width'); + var newSvgH = getSize(g, 'height'); + var newX = +_context.attr('x') - newSvgW * + {start: 0, middle: 0.5, end: 1}[_context.attr('text-anchor') || 'start']; + // font baseline is about 1/4 fontSize below centerline + var textHeight = fontSize || getSize(_context, 'height'); + var dy = -textHeight / 4; + + if(svgClass[0] === 'y') { + mathjaxGroup.attr({ + transform: 'rotate(' + [-90, +_context.attr('x'), +_context.attr('y')] + + ')' + strTranslate(-newSvgW / 2, dy - newSvgH / 2) + }); + newSvg.attr({x: +_context.attr('x'), y: +_context.attr('y')}); + } else if(svgClass[0] === 'l') { + newSvg.attr({x: _context.attr('x'), y: dy - (newSvgH / 2)}); + } else if(svgClass[0] === 'a' && svgClass.indexOf('atitle') !== 0) { + newSvg.attr({x: 0, y: dy}); + } else { + newSvg.attr({x: newX, y: (+_context.attr('y') + dy - newSvgH / 2)}); + } + + if(_callback) _callback.call(_context, mathjaxGroup); + resolve(mathjaxGroup); + }); + })); + } else showText(); + + return _context; +}; + + +// MathJax + +var LT_MATCH = /(<|<|<)/g; +var GT_MATCH = /(>|>|>)/g; + +function cleanEscapesForTex(s) { + return s.replace(LT_MATCH, '\\lt ') + .replace(GT_MATCH, '\\gt '); +} + +function texToSVG(_texString, _config, _callback) { + var originalRenderer, + originalConfig, + originalProcessSectionDelay, + tmpDiv; + + MathJax.Hub.Queue( + function() { + originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config); + + originalProcessSectionDelay = MathJax.Hub.processSectionDelay; + if(MathJax.Hub.processSectionDelay !== undefined) { + // MathJax 2.5+ + MathJax.Hub.processSectionDelay = 0; + } + + return MathJax.Hub.Config({ + messageStyle: 'none', + tex2jax: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + }, + displayAlign: 'left', + }); + }, + function() { + // Get original renderer + originalRenderer = MathJax.Hub.config.menuSettings.renderer; + if(originalRenderer !== 'SVG') { + return MathJax.Hub.setRenderer('SVG'); + } + }, + function() { + var randomID = 'math-output-' + Lib.randstr({}, 64); + tmpDiv = d3.select('body').append('div') + .attr({id: randomID}) + .style({visibility: 'hidden', position: 'absolute'}) + .style({'font-size': _config.fontSize + 'px'}) + .text(cleanEscapesForTex(_texString)); + + return MathJax.Hub.Typeset(tmpDiv.node()); + }, + function() { + var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs'); + + if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) { + Lib.log('There was an error in the tex syntax.', _texString); + _callback(); + } else { + var svgBBox = tmpDiv.select('svg').node().getBoundingClientRect(); + _callback(tmpDiv.select('.MathJax_SVG'), glyphDefs, svgBBox); + } + + tmpDiv.remove(); + + if(originalRenderer !== 'SVG') { + return MathJax.Hub.setRenderer(originalRenderer); + } + }, + function() { + if(originalProcessSectionDelay !== undefined) { + MathJax.Hub.processSectionDelay = originalProcessSectionDelay; + } + return MathJax.Hub.Config(originalConfig); + }); +} + +var TAG_STYLES = { + // would like to use baseline-shift for sub/sup but FF doesn't support it + // so we need to use dy along with the uber hacky shift-back-to + // baseline below + sup: 'font-size:70%', + sub: 'font-size:70%', + b: 'font-weight:bold', + i: 'font-style:italic', + a: 'cursor:pointer', + span: '', + em: 'font-style:italic;font-weight:bold' +}; + +// baseline shifts for sub and sup +var SHIFT_DY = { + sub: '0.3em', + sup: '-0.6em' +}; +// reset baseline by adding a tspan (empty except for a zero-width space) +// with dy of -70% * SHIFT_DY (because font-size=70%) +var RESET_DY = { + sub: '-0.21em', + sup: '0.42em' +}; +var ZERO_WIDTH_SPACE = '\u200b'; + +/* + * Whitelist of protocols in user-supplied urls. Mostly we want to avoid javascript + * and related attack vectors. The empty items are there for IE, that in various + * versions treats relative paths as having different flavors of no protocol, while + * other browsers have these explicitly inherit the protocol of the page they're in. + */ +var PROTOCOLS = ['http:', 'https:', 'mailto:', '', undefined, ':']; + +var NEWLINES = exports.NEWLINES = /(\r\n?|\n)/g; + +var SPLIT_TAGS = /(<[^<>]*>)/; + +var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i; + +var BR_TAG = //i; +exports.BR_TAG_ALL = //gi; + +/* + * style and href: pull them out of either single or double quotes. Also + * - target: (_blank|_self|_parent|_top|framename) + * note that you can't use target to get a popup but if you use popup, + * a `framename` will be passed along as the name of the popup window. + * per the spec, cannot contain whitespace. + * for backward compatibility we default to '_blank' + * - popup: a custom one for us to enable popup (new window) links. String + * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550' + * note that at least in Chrome, you need to give at least one property + * in this string or the page will open in a new tab anyway. We follow this + * convention and will not make a popup if this string is empty. + * per the spec, cannot contain whitespace. + * + * Because we hack in other attributes with style (sub & sup), drop any trailing + * semicolon in user-supplied styles so we can consistently append the tag-dependent style + * + * These are for tag attributes; Chrome anyway will convert entities in + * attribute values, but not in attribute names + * you can test this by for example: + * > p = document.createElement('p') + * > p.innerHTML = 'Hi' + * > p.innerHTML + * <- 'Hi' + */ +var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i; +var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i; +var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i; +var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i; + +// dedicated matcher for these quoted regexes, that can return their results +// in two different places +function getQuotedMatch(_str, re) { + if(!_str) return null; + var match = _str.match(re); + var result = match && (match[3] || match[4]); + return result && convertEntities(result); +} + +var COLORMATCH = /(^|;)\s*color:/; + +/** + * Strip string of tags + * + * @param {string} _str : input string + * @param {object} opts : + * - len {number} max length of output string + * - allowedTags {array} list of pseudo-html tags to NOT strip + * @return {string} + */ +exports.plainText = function(_str, opts) { + opts = opts || {}; + + var len = (opts.len !== undefined && opts.len !== -1) ? opts.len : Infinity; + var allowedTags = opts.allowedTags !== undefined ? opts.allowedTags : ['br']; + + var ellipsis = '...'; + var eLen = ellipsis.length; + + var oldParts = _str.split(SPLIT_TAGS); + var newParts = []; + var prevTag = ''; + var l = 0; + + for(var i = 0; i < oldParts.length; i++) { + var p = oldParts[i]; + var match = p.match(ONE_TAG); + var tagType = match && match[2].toLowerCase(); + + if(tagType) { + // N.B. tags do not count towards string length + if(allowedTags.indexOf(tagType) !== -1) { + newParts.push(p); + prevTag = tagType; + } + } else { + var pLen = p.length; + + if((l + pLen) < len) { + newParts.push(p); + l += pLen; + } else if(l < len) { + var pLen2 = len - l; + + if(prevTag && (prevTag !== 'br' || pLen2 <= eLen || pLen <= eLen)) { + newParts.pop(); + } + + if(len > eLen) { + newParts.push(p.substr(0, pLen2 - eLen) + ellipsis); + } else { + newParts.push(p.substr(0, pLen2)); + } + break; + } + + prevTag = ''; + } + } + + return newParts.join(''); +}; + +/* + * N.B. HTML entities are listed without the leading '&' and trailing ';' + * https://www.freeformatter.com/html-entities.html + * + * FWIW if we wanted to support the full set, it has 2261 entries: + * https://www.w3.org/TR/html5/entities.json + * though I notice that some of these are duplicates and/or are missing ";" + * eg: "&", "&", "&", and "&" all map to "&" + * We no longer need to include numeric entities here, these are now handled + * by String.fromCodePoint/fromCharCode + * + * Anyway the only ones that are really important to allow are the HTML special + * chars <, >, and &, because these ones can trigger special processing if not + * replaced by the corresponding entity. + */ +var entityToUnicode = { + mu: 'μ', + amp: '&', + lt: '<', + gt: '>', + nbsp: ' ', + times: '×', + plusmn: '±', + deg: '°' +}; + +// NOTE: in general entities can contain uppercase too (so [a-zA-Z]) but all the +// ones we support use only lowercase. If we ever change that, update the regex. +var ENTITY_MATCH = /&(#\d+|#x[\da-fA-F]+|[a-z]+);/g; +function convertEntities(_str) { + return _str.replace(ENTITY_MATCH, function(fullMatch, innerMatch) { + var outChar; + if(innerMatch.charAt(0) === '#') { + // cannot use String.fromCodePoint in IE + outChar = fromCodePoint( + innerMatch.charAt(1) === 'x' ? + parseInt(innerMatch.substr(2), 16) : + parseInt(innerMatch.substr(1), 10) + ); + } else outChar = entityToUnicode[innerMatch]; + + // as in regular HTML, if we didn't decode the entity just + // leave the raw text in place. + return outChar || fullMatch; + }); +} +exports.convertEntities = convertEntities; + +function fromCodePoint(code) { + // Don't allow overflow. In Chrome this turns into � but I feel like it's + // more useful to just not convert it at all. + if(code > 0x10FFFF) return; + var stringFromCodePoint = String.fromCodePoint; + if(stringFromCodePoint) return stringFromCodePoint(code); + + // IE doesn't have String.fromCodePoint + // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint + var stringFromCharCode = String.fromCharCode; + if(code <= 0xFFFF) return stringFromCharCode(code); + return stringFromCharCode( + (code >> 10) + 0xD7C0, + (code % 0x400) + 0xDC00 + ); +} + +/* + * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these + * to containerNode + * + * @param {svg text element} containerNode: the node to insert this text into + * @param {string} str: the pseudo-html string to convert to svg + * + * @returns {bool}: does the result contain any links? We need to handle the text element + * somewhat differently if it does, so just keep track of this when it happens. + */ +function buildSVGText(containerNode, str) { + /* + * Normalize behavior between IE and others wrt newlines and whitespace:pre + * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746 + * Chrome and FF display \n, \r, or \r\n as a space in this mode. + * I feel like at some point we turned these into
but currently we don't so + * I'm just going to cement what we do now in Chrome and FF + */ + str = str.replace(NEWLINES, ' '); + + var hasLink = false; + + // as we're building the text, keep track of what elements we're nested inside + // nodeStack will be an array of {node, type, style, href, target, popup} + // where only type: 'a' gets the last 3 and node is only added when it's created + var nodeStack = []; + var currentNode; + var currentLine = -1; + + function newLine() { + currentLine++; + + var lineNode = document.createElementNS(xmlnsNamespaces.svg, 'tspan'); + d3.select(lineNode).attr({ + class: 'line', + dy: (currentLine * LINE_SPACING) + 'em' + }); + containerNode.appendChild(lineNode); + + currentNode = lineNode; + + var oldNodeStack = nodeStack; + nodeStack = [{node: lineNode}]; + + if(oldNodeStack.length > 1) { + for(var i = 1; i < oldNodeStack.length; i++) { + enterNode(oldNodeStack[i]); + } + } + } + + function enterNode(nodeSpec) { + var type = nodeSpec.type; + var nodeAttrs = {}; + var nodeType; + + if(type === 'a') { + nodeType = 'a'; + var target = nodeSpec.target; + var href = nodeSpec.href; + var popup = nodeSpec.popup; + if(href) { + nodeAttrs = { + 'xlink:xlink:show': (target === '_blank' || target.charAt(0) !== '_') ? 'new' : 'replace', + target: target, + 'xlink:xlink:href': href + }; + if(popup) { + // security: href and target are not inserted as code but + // as attributes. popup is, but limited to /[A-Za-z0-9_=,]/ + nodeAttrs.onclick = 'window.open(this.href.baseVal,this.target.baseVal,"' + + popup + '");return false;'; + } + } + } else nodeType = 'tspan'; + + if(nodeSpec.style) nodeAttrs.style = nodeSpec.style; + + var newNode = document.createElementNS(xmlnsNamespaces.svg, nodeType); + + if(type === 'sup' || type === 'sub') { + addTextNode(currentNode, ZERO_WIDTH_SPACE); + currentNode.appendChild(newNode); + + var resetter = document.createElementNS(xmlnsNamespaces.svg, 'tspan'); + addTextNode(resetter, ZERO_WIDTH_SPACE); + d3.select(resetter).attr('dy', RESET_DY[type]); + nodeAttrs.dy = SHIFT_DY[type]; + + currentNode.appendChild(newNode); + currentNode.appendChild(resetter); + } else { + currentNode.appendChild(newNode); + } + + d3.select(newNode).attr(nodeAttrs); + + currentNode = nodeSpec.node = newNode; + nodeStack.push(nodeSpec); + } + + function addTextNode(node, text) { + node.appendChild(document.createTextNode(text)); + } + + function exitNode(type) { + // A bare closing tag can't close the root node. If we encounter this it + // means there's an extra closing tag that can just be ignored: + if(nodeStack.length === 1) { + Lib.log('Ignoring unexpected end tag .', str); + return; + } + + var innerNode = nodeStack.pop(); + + if(type !== innerNode.type) { + Lib.log('Start tag <' + innerNode.type + '> doesnt match end tag <' + + type + '>. Pretending it did match.', str); + } + currentNode = nodeStack[nodeStack.length - 1].node; + } + + var hasLines = BR_TAG.test(str); + + if(hasLines) newLine(); + else { + currentNode = containerNode; + nodeStack = [{node: containerNode}]; + } + + var parts = str.split(SPLIT_TAGS); + for(var i = 0; i < parts.length; i++) { + var parti = parts[i]; + var match = parti.match(ONE_TAG); + var tagType = match && match[2].toLowerCase(); + var tagStyle = TAG_STYLES[tagType]; + + if(tagType === 'br') { + newLine(); + } else if(tagStyle === undefined) { + addTextNode(currentNode, convertEntities(parti)); + } else { + // tag - open or close + if(match[1]) { + exitNode(tagType); + } else { + var extra = match[4]; + + var nodeSpec = {type: tagType}; + + // now add style, from both the tag name and any extra css + // Most of the svg css that users will care about is just like html, + // but font color is different (uses fill). Let our users ignore this. + var css = getQuotedMatch(extra, STYLEMATCH); + if(css) { + css = css.replace(COLORMATCH, '$1 fill:'); + if(tagStyle) css += ';' + tagStyle; + } else if(tagStyle) css = tagStyle; + + if(css) nodeSpec.style = css; + + if(tagType === 'a') { + hasLink = true; + + var href = getQuotedMatch(extra, HREFMATCH); + + if(href) { + // check safe protocols + var dummyAnchor = document.createElement('a'); + dummyAnchor.href = href; + if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) { + // Decode href to allow both already encoded and not encoded + // URIs. Without decoding prior encoding, an already encoded + // URI would be encoded twice producing a semantically different URI. + nodeSpec.href = encodeURI(decodeURI(href)); + nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank'; + nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH); + } + } + } + + enterNode(nodeSpec); + } + } + } + + return hasLink; +} + +/* + * sanitizeHTML: port of buildSVGText aimed at providing a clean subset of HTML + * @param {string} str: the html string to clean + * @returns {string}: a cleaned and normalized version of the input, + * supporting only a small subset of html + */ +exports.sanitizeHTML = function sanitizeHTML(str) { + str = str.replace(NEWLINES, ' '); + + var rootNode = document.createElement('p'); + var currentNode = rootNode; + var nodeStack = []; + + var parts = str.split(SPLIT_TAGS); + for(var i = 0; i < parts.length; i++) { + var parti = parts[i]; + var match = parti.match(ONE_TAG); + var tagType = match && match[2].toLowerCase(); + + if(tagType in TAG_STYLES) { + if(match[1]) { + if(nodeStack.length) { + currentNode = nodeStack.pop(); + } + } else { + var extra = match[4]; + + var css = getQuotedMatch(extra, STYLEMATCH); + var nodeAttrs = css ? {style: css} : {}; + + if(tagType === 'a') { + var href = getQuotedMatch(extra, HREFMATCH); + + if(href) { + var dummyAnchor = document.createElement('a'); + dummyAnchor.href = href; + if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) { + nodeAttrs.href = encodeURI(decodeURI(href)); + var target = getQuotedMatch(extra, TARGETMATCH); + if(target) { + nodeAttrs.target = target; + } + } + } + } + + var newNode = document.createElement(tagType); + currentNode.appendChild(newNode); + d3.select(newNode).attr(nodeAttrs); + + currentNode = newNode; + nodeStack.push(newNode); + } + } else { + currentNode.appendChild( + document.createTextNode(convertEntities(parti)) + ); + } + } + var key = 'innerHTML'; // i.e. to avoid pass test-syntax + return rootNode[key]; +}; + +exports.lineCount = function lineCount(s) { + return s.selectAll('tspan.line').size() || 1; +}; + +exports.positionText = function positionText(s, x, y) { + return s.each(function() { + var text = d3.select(this); + + function setOrGet(attr, val) { + if(val === undefined) { + val = text.attr(attr); + if(val === null) { + text.attr(attr, 0); + val = 0; + } + } else text.attr(attr, val); + return val; + } + + var thisX = setOrGet('x', x); + var thisY = setOrGet('y', y); + + if(this.nodeName === 'text') { + text.selectAll('tspan.line').attr({x: thisX, y: thisY}); + } + }); +}; + +function alignHTMLWith(_base, container, options) { + var alignH = options.horizontalAlign; + var alignV = options.verticalAlign || 'top'; + var bRect = _base.node().getBoundingClientRect(); + var cRect = container.node().getBoundingClientRect(); + var thisRect; + var getTop; + var getLeft; + + if(alignV === 'bottom') { + getTop = function() { return bRect.bottom - thisRect.height; }; + } else if(alignV === 'middle') { + getTop = function() { return bRect.top + (bRect.height - thisRect.height) / 2; }; + } else { // default: top + getTop = function() { return bRect.top; }; + } + + if(alignH === 'right') { + getLeft = function() { return bRect.right - thisRect.width; }; + } else if(alignH === 'center') { + getLeft = function() { return bRect.left + (bRect.width - thisRect.width) / 2; }; + } else { // default: left + getLeft = function() { return bRect.left; }; + } + + return function() { + thisRect = this.node().getBoundingClientRect(); + + var x0 = getLeft() - cRect.left; + var y0 = getTop() - cRect.top; + var gd = options.gd || {}; + if(options.gd) { + gd._fullLayout._calcInverseTransform(gd); + var transformedCoords = Lib.apply3DTransform(gd._fullLayout._invTransform)(x0, y0); + x0 = transformedCoords[0]; + y0 = transformedCoords[1]; + } + + this.style({ + top: y0 + 'px', + left: x0 + 'px', + 'z-index': 1000 + }); + return this; + }; +} + +var onePx = '1px '; + +exports.makeTextShadow = function(color) { + var x = onePx; + var y = onePx; + var b = onePx; + return x + y + b + color + ', ' + + '-' + x + '-' + y + b + color + ', ' + + x + '-' + y + b + color + ', ' + + '-' + x + y + b + color; +}; + +/* + * Editable title + * @param {d3.selection} context: the element being edited. Normally text, + * but if it isn't, you should provide the styling options + * @param {object} options: + * @param {div} options.gd: graphDiv + * @param {d3.selection} options.delegate: item to bind events to if not this + * @param {boolean} options.immediate: start editing now (true) or on click (false, default) + * @param {string} options.fill: font color if not as shown + * @param {string} options.background: background color if not as shown + * @param {string} options.text: initial text, if not as shown + * @param {string} options.horizontalAlign: alignment of the edit box wrt. the bound element + * @param {string} options.verticalAlign: alignment of the edit box wrt. the bound element + */ + +exports.makeEditable = function(context, options) { + var gd = options.gd; + var _delegate = options.delegate; + var dispatch = d3.dispatch('edit', 'input', 'cancel'); + var handlerElement = _delegate || context; + + context.style({'pointer-events': _delegate ? 'none' : 'all'}); + + if(context.size() !== 1) throw new Error('boo'); + + function handleClick() { + appendEditable(); + context.style({opacity: 0}); + // also hide any mathjax svg + var svgClass = handlerElement.attr('class'); + var mathjaxClass; + if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group'; + else mathjaxClass = '[class*=-math-group]'; + if(mathjaxClass) { + d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0}); + } + } + + function selectElementContents(_el) { + var el = _el.node(); + var range = document.createRange(); + range.selectNodeContents(el); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + el.focus(); + } + + function appendEditable() { + var plotDiv = d3.select(gd); + var container = plotDiv.select('.svg-container'); + var div = container.append('div'); + var cStyle = context.node().style; + var fontSize = parseFloat(cStyle.fontSize || 12); + + var initialText = options.text; + if(initialText === undefined) initialText = context.attr('data-unformatted'); + + div.classed('plugin-editable editable', true) + .style({ + position: 'absolute', + 'font-family': cStyle.fontFamily || 'Arial', + 'font-size': fontSize, + color: options.fill || cStyle.fill || 'black', + opacity: 1, + 'background-color': options.background || 'transparent', + outline: '#ffffff33 1px solid', + margin: [-fontSize / 8 + 1, 0, 0, -1].join('px ') + 'px', + padding: '0', + 'box-sizing': 'border-box' + }) + .attr({contenteditable: true}) + .text(initialText) + .call(alignHTMLWith(context, container, options)) + .on('blur', function() { + gd._editing = false; + context.text(this.textContent) + .style({opacity: 1}); + var svgClass = d3.select(this).attr('class'); + var mathjaxClass; + if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group'; + else mathjaxClass = '[class*=-math-group]'; + if(mathjaxClass) { + d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0}); + } + var text = this.textContent; + d3.select(this).transition().duration(0).remove(); + d3.select(document).on('mouseup', null); + dispatch.edit.call(context, text); + }) + .on('focus', function() { + var editDiv = this; + gd._editing = true; + d3.select(document).on('mouseup', function() { + if(d3.event.target === editDiv) return false; + if(document.activeElement === div.node()) div.node().blur(); + }); + }) + .on('keyup', function() { + if(d3.event.which === 27) { + gd._editing = false; + context.style({opacity: 1}); + d3.select(this) + .style({opacity: 0}) + .on('blur', function() { return false; }) + .transition().remove(); + dispatch.cancel.call(context, this.textContent); + } else { + dispatch.input.call(context, this.textContent); + d3.select(this).call(alignHTMLWith(context, container, options)); + } + }) + .on('keydown', function() { + if(d3.event.which === 13) this.blur(); + }) + .call(selectElementContents); + } + + if(options.immediate) handleClick(); + else handlerElement.on('click', handleClick); + + return d3.rebind(context, dispatch, 'on'); +}; + +},{"../constants/alignment":260,"../constants/xmlns_namespaces":266,"../lib":285,"@plotly/d3":20}],308:[function(_dereq_,module,exports){ +'use strict'; + +var timerCache = {}; + +/** + * Throttle a callback. `callback` executes synchronously only if + * more than `minInterval` milliseconds have already elapsed since the latest + * call (if any). Otherwise we wait until `minInterval` is over and execute the + * last callback received while waiting. + * So the first and last events in a train are always executed (eventually) + * but some of the events in the middle can be dropped. + * + * @param {string} id: an identifier to mark events to throttle together + * @param {number} minInterval: minimum time, in milliseconds, between + * invocations of `callback` + * @param {function} callback: the function to throttle. `callback` itself + * should be a purely synchronous function. + */ +exports.throttle = function throttle(id, minInterval, callback) { + var cache = timerCache[id]; + var now = Date.now(); + + if(!cache) { + /* + * Throw out old items before making a new one, to prevent the cache + * getting overgrown, for example from old plots that have been replaced. + * 1 minute age is arbitrary. + */ + for(var idi in timerCache) { + if(timerCache[idi].ts < now - 60000) { + delete timerCache[idi]; + } + } + cache = timerCache[id] = {ts: 0, timer: null}; + } + + _clearTimeout(cache); + + function exec() { + callback(); + cache.ts = Date.now(); + if(cache.onDone) { + cache.onDone(); + cache.onDone = null; + } + } + + if(now > cache.ts + minInterval) { + exec(); + return; + } + + cache.timer = setTimeout(function() { + exec(); + cache.timer = null; + }, minInterval); +}; + +exports.done = function(id) { + var cache = timerCache[id]; + if(!cache || !cache.timer) return Promise.resolve(); + + return new Promise(function(resolve) { + var previousOnDone = cache.onDone; + cache.onDone = function onDone() { + if(previousOnDone) previousOnDone(); + resolve(); + cache.onDone = null; + }; + }); +}; + +/** + * Clear the throttle cache for one or all timers + * @param {optional string} id: + * if provided, clear just this timer + * if omitted, clear all timers (mainly useful for testing) + */ +exports.clear = function(id) { + if(id) { + _clearTimeout(timerCache[id]); + delete timerCache[id]; + } else { + for(var idi in timerCache) exports.clear(idi); + } +}; + +function _clearTimeout(cache) { + if(cache && cache.timer !== null) { + clearTimeout(cache.timer); + cache.timer = null; + } +} + +},{}],309:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +/** + * convert a linear value into a logged value, folding negative numbers into + * the given range + */ +module.exports = function toLogRange(val, range) { + if(val > 0) return Math.log(val) / Math.LN10; + + // move a negative value reference to a log axis - just put the + // result at the lowest range value on the plot (or if the range also went negative, + // one millionth of the top of the range) + var newVal = Math.log(Math.min(range[0], range[1])) / Math.LN10; + if(!isNumeric(newVal)) newVal = Math.log(Math.max(range[0], range[1])) / Math.LN10 - 6; + return newVal; +}; + +},{"fast-isnumeric":31}],310:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + moduleType: 'locale', + name: 'en-US', + dictionary: { + 'Click to enter Colorscale title': 'Click to enter Colorscale title' + }, + format: { + date: '%m/%d/%Y' + } +}; + +},{}],311:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + moduleType: 'locale', + name: 'en', + dictionary: { + 'Click to enter Colorscale title': 'Click to enter Colourscale title' + }, + format: { + 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' + ], + periods: ['AM', 'PM'], + dateTime: '%a %b %e %X %Y', + date: '%d/%m/%Y', + time: '%H:%M:%S', + decimal: '.', + thousands: ',', + grouping: [3], + currency: ['$', ''], + year: '%Y', + month: '%b %Y', + dayMonth: '%b %-d', + dayMonthYear: '%b %-d, %Y' + } +}; + +},{}],312:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); + +/* + * containerArrayMatch: does this attribute string point into a + * layout container array? + * + * @param {String} astr: an attribute string, like *annotations[2].text* + * + * @returns {Object | false} Returns false if `astr` doesn't match a container + * array. If it does, returns: + * {array: {String}, index: {Number}, property: {String}} + * ie the attribute string for the array, the index within the array (or '' + * if the whole array) and the property within that (or '' if the whole array + * or the whole object) + */ +module.exports = function containerArrayMatch(astr) { + var rootContainers = Registry.layoutArrayContainers; + var regexpContainers = Registry.layoutArrayRegexes; + var rootPart = astr.split('[')[0]; + var arrayStr; + var match; + + // look for regexp matches first, because they may be nested inside root matches + // eg updatemenus[i].buttons is nested inside updatemenus + for(var i = 0; i < regexpContainers.length; i++) { + match = astr.match(regexpContainers[i]); + if(match && match.index === 0) { + arrayStr = match[0]; + break; + } + } + + // now look for root matches + if(!arrayStr) arrayStr = rootContainers[rootContainers.indexOf(rootPart)]; + + if(!arrayStr) return false; + + var tail = astr.substr(arrayStr.length); + if(!tail) return {array: arrayStr, index: '', property: ''}; + + match = tail.match(/^\[(0|[1-9][0-9]*)\](\.(.+))?$/); + if(!match) return false; + + return {array: arrayStr, index: Number(match[1]), property: match[3] || ''}; +}; + +},{"../registry":373}],313:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var extendFlat = Lib.extendFlat; +var isPlainObject = Lib.isPlainObject; + +var traceOpts = { + valType: 'flaglist', + extras: ['none'], + flags: ['calc', 'clearAxisTypes', 'plot', 'style', 'markerSize', 'colorbars'], +}; + +var layoutOpts = { + valType: 'flaglist', + extras: ['none'], + flags: [ + 'calc', 'plot', 'legend', 'ticks', 'axrange', + 'layoutstyle', 'modebar', 'camera', 'arraydraw', 'colorbars' + ], +}; + +// flags for inside restyle/relayout include a few extras +// that shouldn't be used in attributes, to deal with certain +// combinations and conditionals efficiently +var traceEditTypeFlags = traceOpts.flags.slice() + .concat(['fullReplot']); + +var layoutEditTypeFlags = layoutOpts.flags.slice() + .concat('layoutReplot'); + +module.exports = { + traces: traceOpts, + layout: layoutOpts, + /* + * default (all false) edit flags for restyle (traces) + * creates a new object each call, so the caller can mutate freely + */ + traceFlags: function() { return falseObj(traceEditTypeFlags); }, + + /* + * default (all false) edit flags for relayout + * creates a new object each call, so the caller can mutate freely + */ + layoutFlags: function() { return falseObj(layoutEditTypeFlags); }, + + /* + * update `flags` with the `editType` values found in `attr` + */ + update: function(flags, attr) { + var editType = attr.editType; + if(editType && editType !== 'none') { + var editTypeParts = editType.split('+'); + for(var i = 0; i < editTypeParts.length; i++) { + flags[editTypeParts[i]] = true; + } + } + }, + + overrideAll: overrideAll +}; + +function falseObj(keys) { + var out = {}; + for(var i = 0; i < keys.length; i++) out[keys[i]] = false; + return out; +} + +/** + * For attributes that are largely copied from elsewhere into a plot type that doesn't + * support partial redraws - overrides the editType field of all attributes in the object + * + * @param {object} attrs: the attributes to override. Will not be mutated. + * @param {string} editTypeOverride: the new editType to use + * @param {'nested'|'from-root'} overrideContainers: + * - 'nested' will override editType for nested containers but not the root. + * - 'from-root' will also override editType of the root container. + * Containers below the absolute top level (trace or layout root) DO need an + * editType even if they are not `valObject`s themselves (eg `scatter.marker`) + * to handle the case where you edit the whole container. + * + * @return {object} a new attributes object with `editType` modified as directed + */ +function overrideAll(attrs, editTypeOverride, overrideContainers) { + var out = extendFlat({}, attrs); + for(var key in out) { + var attr = out[key]; + if(isPlainObject(attr)) { + out[key] = overrideOne(attr, editTypeOverride, overrideContainers, key); + } + } + if(overrideContainers === 'from-root') out.editType = editTypeOverride; + + return out; +} + +function overrideOne(attr, editTypeOverride, overrideContainers, key) { + if(attr.valType) { + var out = extendFlat({}, attr); + out.editType = editTypeOverride; + + if(Array.isArray(attr.items)) { + out.items = new Array(attr.items.length); + for(var i = 0; i < attr.items.length; i++) { + out.items[i] = overrideOne(attr.items[i], editTypeOverride, 'from-root'); + } + } + return out; + } else { + // don't provide an editType for the _deprecated container + return overrideAll(attr, editTypeOverride, + (key.charAt(0) === '_') ? 'nested' : 'from-root'); + } +} + +},{"../lib":285}],314:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var m4FromQuat = _dereq_('gl-mat4/fromQuat'); + +var Registry = _dereq_('../registry'); +var Lib = _dereq_('../lib'); +var Plots = _dereq_('../plots/plots'); +var AxisIds = _dereq_('../plots/cartesian/axis_ids'); +var Color = _dereq_('../components/color'); + +var cleanId = AxisIds.cleanId; +var getFromTrace = AxisIds.getFromTrace; +var traceIs = Registry.traceIs; + +// clear the promise queue if one of them got rejected +exports.clearPromiseQueue = function(gd) { + if(Array.isArray(gd._promises) && gd._promises.length > 0) { + Lib.log('Clearing previous rejected promises from queue.'); + } + + gd._promises = []; +}; + +// make a few changes to the layout right away +// before it gets used for anything +// backward compatibility and cleanup of nonstandard options +exports.cleanLayout = function(layout) { + var i, j; + + if(!layout) layout = {}; + + // cannot have (x|y)axis1, numbering goes axis, axis2, axis3... + if(layout.xaxis1) { + if(!layout.xaxis) layout.xaxis = layout.xaxis1; + delete layout.xaxis1; + } + if(layout.yaxis1) { + if(!layout.yaxis) layout.yaxis = layout.yaxis1; + delete layout.yaxis1; + } + if(layout.scene1) { + if(!layout.scene) layout.scene = layout.scene1; + delete layout.scene1; + } + + var axisAttrRegex = (Plots.subplotsRegistry.cartesian || {}).attrRegex; + var polarAttrRegex = (Plots.subplotsRegistry.polar || {}).attrRegex; + var ternaryAttrRegex = (Plots.subplotsRegistry.ternary || {}).attrRegex; + var sceneAttrRegex = (Plots.subplotsRegistry.gl3d || {}).attrRegex; + + var keys = Object.keys(layout); + for(i = 0; i < keys.length; i++) { + var key = keys[i]; + + if(axisAttrRegex && axisAttrRegex.test(key)) { + // modifications to cartesian axes + + var ax = layout[key]; + if(ax.anchor && ax.anchor !== 'free') { + ax.anchor = cleanId(ax.anchor); + } + if(ax.overlaying) ax.overlaying = cleanId(ax.overlaying); + + // old method of axis type - isdate and islog (before category existed) + if(!ax.type) { + if(ax.isdate) ax.type = 'date'; + else if(ax.islog) ax.type = 'log'; + else if(ax.isdate === false && ax.islog === false) ax.type = 'linear'; + } + if(ax.autorange === 'withzero' || ax.autorange === 'tozero') { + ax.autorange = true; + ax.rangemode = 'tozero'; + } + delete ax.islog; + delete ax.isdate; + delete ax.categories; // replaced by _categories + + // prune empty domain arrays made before the new nestedProperty + if(emptyContainer(ax, 'domain')) delete ax.domain; + + // autotick -> tickmode + if(ax.autotick !== undefined) { + if(ax.tickmode === undefined) { + ax.tickmode = ax.autotick ? 'auto' : 'linear'; + } + delete ax.autotick; + } + + cleanTitle(ax); + } else if(polarAttrRegex && polarAttrRegex.test(key)) { + // modifications for polar + + var polar = layout[key]; + cleanTitle(polar.radialaxis); + } else if(ternaryAttrRegex && ternaryAttrRegex.test(key)) { + // modifications for ternary + + var ternary = layout[key]; + cleanTitle(ternary.aaxis); + cleanTitle(ternary.baxis); + cleanTitle(ternary.caxis); + } else if(sceneAttrRegex && sceneAttrRegex.test(key)) { + // modifications for 3D scenes + + var scene = layout[key]; + + // clean old Camera coords + var cameraposition = scene.cameraposition; + + if(Array.isArray(cameraposition) && cameraposition[0].length === 4) { + var rotation = cameraposition[0]; + var center = cameraposition[1]; + var radius = cameraposition[2]; + var mat = m4FromQuat([], rotation); + var eye = []; + + for(j = 0; j < 3; ++j) { + eye[j] = center[j] + radius * mat[2 + 4 * j]; + } + + scene.camera = { + eye: {x: eye[0], y: eye[1], z: eye[2]}, + center: {x: center[0], y: center[1], z: center[2]}, + up: {x: 0, y: 0, z: 1} // we just ignore calculating camera z up in this case + }; + + delete scene.cameraposition; + } + + // clean axis titles + cleanTitle(scene.xaxis); + cleanTitle(scene.yaxis); + cleanTitle(scene.zaxis); + } + } + + var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0; + for(i = 0; i < annotationsLen; i++) { + var ann = layout.annotations[i]; + + if(!Lib.isPlainObject(ann)) continue; + + if(ann.ref) { + if(ann.ref === 'paper') { + ann.xref = 'paper'; + ann.yref = 'paper'; + } else if(ann.ref === 'data') { + ann.xref = 'x'; + ann.yref = 'y'; + } + delete ann.ref; + } + + cleanAxRef(ann, 'xref'); + cleanAxRef(ann, 'yref'); + } + + var shapesLen = Array.isArray(layout.shapes) ? layout.shapes.length : 0; + for(i = 0; i < shapesLen; i++) { + var shape = layout.shapes[i]; + + if(!Lib.isPlainObject(shape)) continue; + + cleanAxRef(shape, 'xref'); + cleanAxRef(shape, 'yref'); + } + + var imagesLen = Array.isArray(layout.images) ? layout.images.length : 0; + for(i = 0; i < imagesLen; i++) { + var image = layout.images[i]; + + if(!Lib.isPlainObject(image)) continue; + + cleanAxRef(image, 'xref'); + cleanAxRef(image, 'yref'); + } + + var legend = layout.legend; + if(legend) { + // check for old-style legend positioning (x or y is +/- 100) + if(legend.x > 3) { + legend.x = 1.02; + legend.xanchor = 'left'; + } else if(legend.x < -2) { + legend.x = -0.02; + legend.xanchor = 'right'; + } + + if(legend.y > 3) { + legend.y = 1.02; + legend.yanchor = 'bottom'; + } else if(legend.y < -2) { + legend.y = -0.02; + legend.yanchor = 'top'; + } + } + + // clean plot title + cleanTitle(layout); + + /* + * Moved from rotate -> orbit for dragmode + */ + if(layout.dragmode === 'rotate') layout.dragmode = 'orbit'; + + // sanitize rgb(fractions) and rgba(fractions) that old tinycolor + // supported, but new tinycolor does not because they're not valid css + Color.clean(layout); + + // clean the layout container in layout.template + if(layout.template && layout.template.layout) { + exports.cleanLayout(layout.template.layout); + } + + return layout; +}; + +function cleanAxRef(container, attr) { + var valIn = container[attr]; + var axLetter = attr.charAt(0); + if(valIn && valIn !== 'paper') { + container[attr] = cleanId(valIn, axLetter, true); + } +} + +/** + * Cleans up old title attribute structure (flat) in favor of the new one (nested). + * + * @param {Object} titleContainer - an object potentially including deprecated title attributes + */ +function cleanTitle(titleContainer) { + if(titleContainer) { + // title -> title.text + // (although title used to be a string attribute, + // numbers are accepted as well) + if(typeof titleContainer.title === 'string' || typeof titleContainer.title === 'number') { + titleContainer.title = { + text: titleContainer.title + }; + } + + rewireAttr('titlefont', 'font'); + rewireAttr('titleposition', 'position'); + rewireAttr('titleside', 'side'); + rewireAttr('titleoffset', 'offset'); + } + + function rewireAttr(oldAttrName, newAttrName) { + var oldAttrSet = titleContainer[oldAttrName]; + var newAttrSet = titleContainer.title && titleContainer.title[newAttrName]; + + if(oldAttrSet && !newAttrSet) { + // Ensure title object exists + if(!titleContainer.title) { + titleContainer.title = {}; + } + + titleContainer.title[newAttrName] = titleContainer[oldAttrName]; + delete titleContainer[oldAttrName]; + } + } +} + +/* + * cleanData: Make a few changes to the data for backward compatibility + * before it gets used for anything. Modifies the data traces users provide. + * + * Important: if you're going to add something here that modifies a data array, + * update it in place so the new array === the old one. + */ +exports.cleanData = function(data) { + for(var tracei = 0; tracei < data.length; tracei++) { + var trace = data[tracei]; + var i; + + // use xbins to bin data in x, and ybins to bin data in y + if(trace.type === 'histogramy' && 'xbins' in trace && !('ybins' in trace)) { + trace.ybins = trace.xbins; + delete trace.xbins; + } + + // error_y.opacity is obsolete - merge into color + if(trace.error_y && 'opacity' in trace.error_y) { + var dc = Color.defaults; + var yeColor = trace.error_y.color || (traceIs(trace, 'bar') ? + Color.defaultLine : + dc[tracei % dc.length]); + trace.error_y.color = Color.addOpacity( + Color.rgb(yeColor), + Color.opacity(yeColor) * trace.error_y.opacity); + delete trace.error_y.opacity; + } + + // convert bardir to orientation, and put the data into + // the axes it's eventually going to be used with + if('bardir' in trace) { + if(trace.bardir === 'h' && (traceIs(trace, 'bar') || + trace.type.substr(0, 9) === 'histogram')) { + trace.orientation = 'h'; + exports.swapXYData(trace); + } + delete trace.bardir; + } + + // now we have only one 1D histogram type, and whether + // it uses x or y data depends on trace.orientation + if(trace.type === 'histogramy') exports.swapXYData(trace); + if(trace.type === 'histogramx' || trace.type === 'histogramy') { + trace.type = 'histogram'; + } + + // scl->scale, reversescl->reversescale + if('scl' in trace && !('colorscale' in trace)) { + trace.colorscale = trace.scl; + delete trace.scl; + } + if('reversescl' in trace && !('reversescale' in trace)) { + trace.reversescale = trace.reversescl; + delete trace.reversescl; + } + + // axis ids x1 -> x, y1-> y + if(trace.xaxis) trace.xaxis = cleanId(trace.xaxis, 'x'); + if(trace.yaxis) trace.yaxis = cleanId(trace.yaxis, 'y'); + + // scene ids scene1 -> scene + if(traceIs(trace, 'gl3d') && trace.scene) { + trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene); + } + + if(!traceIs(trace, 'pie-like') && !traceIs(trace, 'bar-like')) { + if(Array.isArray(trace.textposition)) { + for(i = 0; i < trace.textposition.length; i++) { + trace.textposition[i] = cleanTextPosition(trace.textposition[i]); + } + } else if(trace.textposition) { + trace.textposition = cleanTextPosition(trace.textposition); + } + } + + // fix typo in colorscale definition + var _module = Registry.getModule(trace); + if(_module && _module.colorbar) { + var containerName = _module.colorbar.container; + var container = containerName ? trace[containerName] : trace; + if(container && container.colorscale) { + if(container.colorscale === 'YIGnBu') container.colorscale = 'YlGnBu'; + if(container.colorscale === 'YIOrRd') container.colorscale = 'YlOrRd'; + } + } + + // fix typo in surface 'highlight*' definitions + if(trace.type === 'surface' && Lib.isPlainObject(trace.contours)) { + var dims = ['x', 'y', 'z']; + + for(i = 0; i < dims.length; i++) { + var opts = trace.contours[dims[i]]; + + if(!Lib.isPlainObject(opts)) continue; + + if(opts.highlightColor) { + opts.highlightcolor = opts.highlightColor; + delete opts.highlightColor; + } + + if(opts.highlightWidth) { + opts.highlightwidth = opts.highlightWidth; + delete opts.highlightWidth; + } + } + } + + // fixes from converting finance from transforms to real trace types + if(trace.type === 'candlestick' || trace.type === 'ohlc') { + var increasingShowlegend = (trace.increasing || {}).showlegend !== false; + var decreasingShowlegend = (trace.decreasing || {}).showlegend !== false; + var increasingName = cleanFinanceDir(trace.increasing); + var decreasingName = cleanFinanceDir(trace.decreasing); + + // now figure out something smart to do with the separate direction + // names we removed + if((increasingName !== false) && (decreasingName !== false)) { + // both sub-names existed: base name previously had no effect + // so ignore it and try to find a shared part of the sub-names + + var newName = commonPrefix( + increasingName, decreasingName, + increasingShowlegend, decreasingShowlegend + ); + // if no common part, leave whatever name was (or wasn't) there + if(newName) trace.name = newName; + } else if((increasingName || decreasingName) && !trace.name) { + // one sub-name existed but not the base name - just use the sub-name + trace.name = increasingName || decreasingName; + } + } + + // transforms backward compatibility fixes + if(Array.isArray(trace.transforms)) { + var transforms = trace.transforms; + + for(i = 0; i < transforms.length; i++) { + var transform = transforms[i]; + + if(!Lib.isPlainObject(transform)) continue; + + switch(transform.type) { + case 'filter': + if(transform.filtersrc) { + transform.target = transform.filtersrc; + delete transform.filtersrc; + } + + if(transform.calendar) { + if(!transform.valuecalendar) { + transform.valuecalendar = transform.calendar; + } + delete transform.calendar; + } + break; + + case 'groupby': + // Name has changed from `style` to `styles`, so use `style` but prefer `styles`: + transform.styles = transform.styles || transform.style; + + if(transform.styles && !Array.isArray(transform.styles)) { + var prevStyles = transform.styles; + var styleKeys = Object.keys(prevStyles); + + transform.styles = []; + for(var j = 0; j < styleKeys.length; j++) { + transform.styles.push({ + target: styleKeys[j], + value: prevStyles[styleKeys[j]] + }); + } + } + break; + } + } + } + + // prune empty containers made before the new nestedProperty + if(emptyContainer(trace, 'line')) delete trace.line; + if('marker' in trace) { + if(emptyContainer(trace.marker, 'line')) delete trace.marker.line; + if(emptyContainer(trace, 'marker')) delete trace.marker; + } + + // sanitize rgb(fractions) and rgba(fractions) that old tinycolor + // supported, but new tinycolor does not because they're not valid css + Color.clean(trace); + + // remove obsolete autobin(x|y) attributes, but only if true + // if false, this needs to happen in Histogram.calc because it + // can be a one-time autobin so we need to know the results before + // we can push them back into the trace. + if(trace.autobinx) { + delete trace.autobinx; + delete trace.xbins; + } + if(trace.autobiny) { + delete trace.autobiny; + delete trace.ybins; + } + + cleanTitle(trace); + if(trace.colorbar) cleanTitle(trace.colorbar); + if(trace.marker && trace.marker.colorbar) cleanTitle(trace.marker.colorbar); + if(trace.line && trace.line.colorbar) cleanTitle(trace.line.colorbar); + if(trace.aaxis) cleanTitle(trace.aaxis); + if(trace.baxis) cleanTitle(trace.baxis); + } +}; + +function cleanFinanceDir(dirContainer) { + if(!Lib.isPlainObject(dirContainer)) return false; + + var dirName = dirContainer.name; + + delete dirContainer.name; + delete dirContainer.showlegend; + + return (typeof dirName === 'string' || typeof dirName === 'number') && String(dirName); +} + +function commonPrefix(name1, name2, show1, show2) { + // if only one is shown in the legend, use that + if(show1 && !show2) return name1; + if(show2 && !show1) return name2; + + // if both or neither are in the legend, check if one is blank (or whitespace) + // and use the other one + // note that hover labels can still use the name even if the legend doesn't + if(!name1.trim()) return name2; + if(!name2.trim()) return name1; + + var minLen = Math.min(name1.length, name2.length); + var i; + for(i = 0; i < minLen; i++) { + if(name1.charAt(i) !== name2.charAt(i)) break; + } + + var out = name1.substr(0, i); + return out.trim(); +} + +// textposition - support partial attributes (ie just 'top') +// and incorrect use of middle / center etc. +function cleanTextPosition(textposition) { + var posY = 'middle'; + var posX = 'center'; + + if(typeof textposition === 'string') { + if(textposition.indexOf('top') !== -1) posY = 'top'; + else if(textposition.indexOf('bottom') !== -1) posY = 'bottom'; + + if(textposition.indexOf('left') !== -1) posX = 'left'; + else if(textposition.indexOf('right') !== -1) posX = 'right'; + } + + return posY + ' ' + posX; +} + +function emptyContainer(outer, innerStr) { + return (innerStr in outer) && + (typeof outer[innerStr] === 'object') && + (Object.keys(outer[innerStr]).length === 0); +} + + +// swap all the data and data attributes associated with x and y +exports.swapXYData = function(trace) { + var i; + Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins', 'nbins?', 'autobin?', '?src', 'error_?']); + if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) { + if(trace.transpose) delete trace.transpose; + else trace.transpose = true; + } + if(trace.error_x && trace.error_y) { + var errorY = trace.error_y; + var copyYstyle = ('copy_ystyle' in errorY) ? + errorY.copy_ystyle : + !(errorY.color || errorY.thickness || errorY.width); + Lib.swapAttrs(trace, ['error_?.copy_ystyle']); + if(copyYstyle) { + Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']); + } + } + if(typeof trace.hoverinfo === 'string') { + var hoverInfoParts = trace.hoverinfo.split('+'); + for(i = 0; i < hoverInfoParts.length; i++) { + if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y'; + else if(hoverInfoParts[i] === 'y') hoverInfoParts[i] = 'x'; + } + trace.hoverinfo = hoverInfoParts.join('+'); + } +}; + +// coerce traceIndices input to array of trace indices +exports.coerceTraceIndices = function(gd, traceIndices) { + if(isNumeric(traceIndices)) { + return [traceIndices]; + } else if(!Array.isArray(traceIndices) || !traceIndices.length) { + return gd.data.map(function(_, i) { return i; }); + } else if(Array.isArray(traceIndices)) { + var traceIndicesOut = []; + for(var i = 0; i < traceIndices.length; i++) { + if(Lib.isIndex(traceIndices[i], gd.data.length)) { + traceIndicesOut.push(traceIndices[i]); + } else { + Lib.warn('trace index (', traceIndices[i], ') is not a number or is out of bounds'); + } + } + return traceIndicesOut; + } + + return traceIndices; +}; + +/** + * Manages logic around array container item creation / deletion / update + * that nested property alone can't handle. + * + * @param {Object} np + * nested property of update attribute string about trace or layout object + * @param {*} newVal + * update value passed to restyle / relayout / update + * @param {Object} undoit + * undo hash (N.B. undoit may be mutated here). + * + */ +exports.manageArrayContainers = function(np, newVal, undoit) { + var obj = np.obj; + var parts = np.parts; + var pLength = parts.length; + var pLast = parts[pLength - 1]; + + var pLastIsNumber = isNumeric(pLast); + + if(pLastIsNumber && newVal === null) { + // delete item + + // Clear item in array container when new value is null + var contPath = parts.slice(0, pLength - 1).join('.'); + var cont = Lib.nestedProperty(obj, contPath).get(); + cont.splice(pLast, 1); + + // Note that nested property clears null / undefined at end of + // array container, but not within them. + } else if(pLastIsNumber && np.get() === undefined) { + // create item + + // When adding a new item, make sure undo command will remove it + if(np.get() === undefined) undoit[np.astr] = null; + + np.set(newVal); + } else { + // update item + + // If the last part of attribute string isn't a number, + // np.set is all we need. + np.set(newVal); + } +}; + +/* + * Match the part to strip off to turn an attribute into its parent + * really it should be either '.some_characters' or '[number]' + * but we're a little more permissive here and match either + * '.not_brackets_or_dot' or '[not_brackets_or_dot]' + */ +var ATTR_TAIL_RE = /(\.[^\[\]\.]+|\[[^\[\]\.]+\])$/; + +function getParent(attr) { + var tail = attr.search(ATTR_TAIL_RE); + if(tail > 0) return attr.substr(0, tail); +} + +/* + * hasParent: does an attribute object contain a parent of the given attribute? + * for example, given 'images[2].x' do we also have 'images' or 'images[2]'? + * + * @param {Object} aobj + * update object, whose keys are attribute strings and values are their new settings + * @param {string} attr + * the attribute string to test against + * @returns {Boolean} + * is a parent of attr present in aobj? + */ +exports.hasParent = function(aobj, attr) { + var attrParent = getParent(attr); + while(attrParent) { + if(attrParent in aobj) return true; + attrParent = getParent(attrParent); + } + return false; +}; + +/** + * Empty out types for all axes containing these traces so we auto-set them again + * + * @param {object} gd + * @param {[integer]} traces: trace indices to search for axes to clear the types of + * @param {object} layoutUpdate: any update being done concurrently to the layout, + * which may supercede clearing the axis types + */ +var axLetters = ['x', 'y', 'z']; +exports.clearAxisTypes = function(gd, traces, layoutUpdate) { + for(var i = 0; i < traces.length; i++) { + var trace = gd._fullData[i]; + for(var j = 0; j < 3; j++) { + var ax = getFromTrace(gd, trace, axLetters[j]); + + // do not clear log type - that's never an auto result so must have been intentional + if(ax && ax.type !== 'log') { + var axAttr = ax._name; + var sceneName = ax._id.substr(1); + if(sceneName.substr(0, 5) === 'scene') { + if(layoutUpdate[sceneName] !== undefined) continue; + axAttr = sceneName + '.' + axAttr; + } + var typeAttr = axAttr + '.type'; + + if(layoutUpdate[axAttr] === undefined && layoutUpdate[typeAttr] === undefined) { + Lib.nestedProperty(gd.layout, typeAttr).set(null); + } + } + } + } +}; + +},{"../components/color":155,"../lib":285,"../plots/cartesian/axis_ids":335,"../plots/plots":366,"../registry":373,"fast-isnumeric":31,"gl-mat4/fromQuat":37}],315:[function(_dereq_,module,exports){ +'use strict'; + +var main = _dereq_('./plot_api'); + +exports._doPlot = main._doPlot; +exports.newPlot = main.newPlot; +exports.restyle = main.restyle; +exports.relayout = main.relayout; +exports.redraw = main.redraw; +exports.update = main.update; +exports._guiRestyle = main._guiRestyle; +exports._guiRelayout = main._guiRelayout; +exports._guiUpdate = main._guiUpdate; +exports._storeDirectGUIEdit = main._storeDirectGUIEdit; +exports.react = main.react; +exports.extendTraces = main.extendTraces; +exports.prependTraces = main.prependTraces; +exports.addTraces = main.addTraces; +exports.deleteTraces = main.deleteTraces; +exports.moveTraces = main.moveTraces; +exports.purge = main.purge; +exports.addFrames = main.addFrames; +exports.deleteFrames = main.deleteFrames; +exports.animate = main.animate; +exports.setPlotConfig = main.setPlotConfig; + +exports.toImage = _dereq_('./to_image'); +exports.validate = _dereq_('./validate'); +exports.downloadImage = _dereq_('../snapshot/download'); + +var templateApi = _dereq_('./template_api'); +exports.makeTemplate = templateApi.makeTemplate; +exports.validateTemplate = templateApi.validateTemplate; + +},{"../snapshot/download":375,"./plot_api":317,"./template_api":322,"./to_image":323,"./validate":324}],316:[function(_dereq_,module,exports){ +'use strict'; + +var isPlainObject = _dereq_('../lib/is_plain_object'); +var noop = _dereq_('../lib/noop'); +var Loggers = _dereq_('../lib/loggers'); +var sorterAsc = _dereq_('../lib/search').sorterAsc; +var Registry = _dereq_('../registry'); + + +exports.containerArrayMatch = _dereq_('./container_array_match'); + +var isAddVal = exports.isAddVal = function isAddVal(val) { + return val === 'add' || isPlainObject(val); +}; + +var isRemoveVal = exports.isRemoveVal = function isRemoveVal(val) { + return val === null || val === 'remove'; +}; + +/* + * applyContainerArrayChanges: for managing arrays of layout components in relayout + * handles them all with a consistent interface. + * + * Here are the supported actions -> relayout calls -> edits we get here + * (as prepared in _relayout): + * + * add an empty obj -> {'annotations[2]': 'add'} -> {2: {'': 'add'}} + * add a specific obj -> {'annotations[2]': {attrs}} -> {2: {'': {attrs}}} + * delete an obj -> {'annotations[2]': 'remove'} -> {2: {'': 'remove'}} + * -> {'annotations[2]': null} -> {2: {'': null}} + * delete the whole array -> {'annotations': 'remove'} -> {'': {'': 'remove'}} + * -> {'annotations': null} -> {'': {'': null}} + * edit an object -> {'annotations[2].text': 'boo'} -> {2: {'text': 'boo'}} + * + * You can combine many edits to different objects. Objects are added and edited + * in ascending order, then removed in descending order. + * For example, starting with [a, b, c], if you want to: + * - replace b with d: + * {'annotations[1]': d, 'annotations[2]': null} (b is item 2 after adding d) + * - add a new item d between a and b, and edit b: + * {'annotations[1]': d, 'annotations[2].x': newX} (b is item 2 after adding d) + * - delete b and edit c: + * {'annotations[1]': null, 'annotations[2].x': newX} (c is edited before b is removed) + * + * You CANNOT combine adding/deleting an item at index `i` with edits to the same index `i` + * You CANNOT combine replacing/deleting the whole array with anything else (for the same array). + * + * @param {HTMLDivElement} gd + * the DOM element of the graph container div + * @param {Lib.nestedProperty} componentType: the array we are editing + * @param {Object} edits + * the changes to make; keys are indices to edit, values are themselves objects: + * {attr: newValue} of changes to make to that index (with add/remove behavior + * in special values of the empty attr) + * @param {Object} flags + * the flags for which actions we're going to perform to display these (and + * any other) changes. If we're already `recalc`ing, we don't need to redraw + * individual items + * @param {function} _nestedProperty + * a (possibly modified for gui edits) nestedProperty constructor + * The modified version takes a 3rd argument, for a prefix to the attribute + * string necessary for storing GUI edits + * + * @returns {bool} `true` if it managed to complete drawing of the changes + * `false` would mean the parent should replot. + */ +exports.applyContainerArrayChanges = function applyContainerArrayChanges(gd, np, edits, flags, _nestedProperty) { + var componentType = np.astr; + var supplyComponentDefaults = Registry.getComponentMethod(componentType, 'supplyLayoutDefaults'); + var draw = Registry.getComponentMethod(componentType, 'draw'); + var drawOne = Registry.getComponentMethod(componentType, 'drawOne'); + var replotLater = flags.replot || flags.recalc || (supplyComponentDefaults === noop) || (draw === noop); + var layout = gd.layout; + var fullLayout = gd._fullLayout; + + if(edits['']) { + if(Object.keys(edits).length > 1) { + Loggers.warn('Full array edits are incompatible with other edits', + componentType); + } + + var fullVal = edits['']['']; + + if(isRemoveVal(fullVal)) np.set(null); + else if(Array.isArray(fullVal)) np.set(fullVal); + else { + Loggers.warn('Unrecognized full array edit value', componentType, fullVal); + return true; + } + + if(replotLater) return false; + + supplyComponentDefaults(layout, fullLayout); + draw(gd); + return true; + } + + var componentNums = Object.keys(edits).map(Number).sort(sorterAsc); + var componentArrayIn = np.get(); + var componentArray = componentArrayIn || []; + // componentArrayFull is used just to keep splices in line between + // full and input arrays, so private keys can be copied over after + // redoing supplyDefaults + // TODO: this assumes componentArray is in gd.layout - which will not be + // true after we extend this to restyle + var componentArrayFull = _nestedProperty(fullLayout, componentType).get(); + + var deletes = []; + var firstIndexChange = -1; + var maxIndex = componentArray.length; + var i; + var j; + var componentNum; + var objEdits; + var objKeys; + var objVal; + var adding, prefix; + + // first make the add and edit changes + for(i = 0; i < componentNums.length; i++) { + componentNum = componentNums[i]; + objEdits = edits[componentNum]; + objKeys = Object.keys(objEdits); + objVal = objEdits[''], + adding = isAddVal(objVal); + + if(componentNum < 0 || componentNum > componentArray.length - (adding ? 0 : 1)) { + Loggers.warn('index out of range', componentType, componentNum); + continue; + } + + if(objVal !== undefined) { + if(objKeys.length > 1) { + Loggers.warn( + 'Insertion & removal are incompatible with edits to the same index.', + componentType, componentNum); + } + + if(isRemoveVal(objVal)) { + deletes.push(componentNum); + } else if(adding) { + if(objVal === 'add') objVal = {}; + componentArray.splice(componentNum, 0, objVal); + if(componentArrayFull) componentArrayFull.splice(componentNum, 0, {}); + } else { + Loggers.warn('Unrecognized full object edit value', + componentType, componentNum, objVal); + } + + if(firstIndexChange === -1) firstIndexChange = componentNum; + } else { + for(j = 0; j < objKeys.length; j++) { + prefix = componentType + '[' + componentNum + '].'; + _nestedProperty(componentArray[componentNum], objKeys[j], prefix) + .set(objEdits[objKeys[j]]); + } + } + } + + // now do deletes + for(i = deletes.length - 1; i >= 0; i--) { + componentArray.splice(deletes[i], 1); + // TODO: this drops private keys that had been stored in componentArrayFull + // does this have any ill effects? + if(componentArrayFull) componentArrayFull.splice(deletes[i], 1); + } + + if(!componentArray.length) np.set(null); + else if(!componentArrayIn) np.set(componentArray); + + if(replotLater) return false; + + supplyComponentDefaults(layout, fullLayout); + + // finally draw all the components we need to + // if we added or removed any, redraw all after it + if(drawOne !== noop) { + var indicesToDraw; + if(firstIndexChange === -1) { + // there's no re-indexing to do, so only redraw components that changed + indicesToDraw = componentNums; + } else { + // in case the component array was shortened, we still need do call + // drawOne on the latter items so they get properly removed + maxIndex = Math.max(componentArray.length, maxIndex); + indicesToDraw = []; + for(i = 0; i < componentNums.length; i++) { + componentNum = componentNums[i]; + if(componentNum >= firstIndexChange) break; + indicesToDraw.push(componentNum); + } + for(i = firstIndexChange; i < maxIndex; i++) { + indicesToDraw.push(i); + } + } + for(i = 0; i < indicesToDraw.length; i++) { + drawOne(gd, indicesToDraw[i]); + } + } else draw(gd); + + return true; +}; + +},{"../lib/is_plain_object":286,"../lib/loggers":289,"../lib/noop":294,"../lib/search":304,"../registry":373,"./container_array_match":312}],317:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); +var hasHover = _dereq_('has-hover'); + +var Lib = _dereq_('../lib'); +var nestedProperty = Lib.nestedProperty; + +var Events = _dereq_('../lib/events'); +var Queue = _dereq_('../lib/queue'); + +var Registry = _dereq_('../registry'); +var PlotSchema = _dereq_('./plot_schema'); +var Plots = _dereq_('../plots/plots'); + +var Axes = _dereq_('../plots/cartesian/axes'); +var Drawing = _dereq_('../components/drawing'); +var Color = _dereq_('../components/color'); +var initInteractions = _dereq_('../plots/cartesian/graph_interact').initInteractions; +var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces'); +var clearSelect = _dereq_('../plots/cartesian/select').clearSelect; + +var dfltConfig = _dereq_('./plot_config').dfltConfig; +var manageArrays = _dereq_('./manage_arrays'); +var helpers = _dereq_('./helpers'); +var subroutines = _dereq_('./subroutines'); +var editTypes = _dereq_('./edit_types'); + +var AX_NAME_PATTERN = _dereq_('../plots/cartesian/constants').AX_NAME_PATTERN; + +var numericNameWarningCount = 0; +var numericNameWarningCountLimit = 5; + +/** + * Internal plot-creation function + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * @param {array of objects} data + * array of traces, containing the data and display information for each trace + * @param {object} layout + * object describing the overall display of the plot, + * all the stuff that doesn't pertain to any individual trace + * @param {object} config + * configuration options (see ./plot_config.js for more info) + * + * OR + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * @param {object} figure + * object containing `data`, `layout`, `config`, and `frames` members + * + */ +function _doPlot(gd, data, layout, config) { + var frames; + + gd = Lib.getGraphDiv(gd); + + // Events.init is idempotent and bails early if gd has already been init'd + Events.init(gd); + + if(Lib.isPlainObject(data)) { + var obj = data; + data = obj.data; + layout = obj.layout; + config = obj.config; + frames = obj.frames; + } + + var okToPlot = Events.triggerHandler(gd, 'plotly_beforeplot', [data, layout, config]); + if(okToPlot === false) return Promise.reject(); + + // if there's no data or layout, and this isn't yet a plotly plot + // container, log a warning to help plotly.js users debug + if(!data && !layout && !Lib.isPlotDiv(gd)) { + Lib.warn('Calling _doPlot as if redrawing ' + + 'but this container doesn\'t yet have a plot.', gd); + } + + function addFrames() { + if(frames) { + return exports.addFrames(gd, frames); + } + } + + // transfer configuration options to gd until we move over to + // a more OO like model + setPlotContext(gd, config); + + if(!layout) layout = {}; + + // hook class for plots main container (in case of plotly.js + // this won't be #embedded-graph or .js-tab-contents) + d3.select(gd).classed('js-plotly-plot', true); + + // off-screen getBoundingClientRect testing space, + // in #js-plotly-tester (and stored as Drawing.tester) + // so we can share cached text across tabs + Drawing.makeTester(); + + // collect promises for any async actions during plotting + // any part of the plotting code can push to gd._promises, then + // before we move to the next step, we check that they're all + // complete, and empty out the promise list again. + if(!Array.isArray(gd._promises)) gd._promises = []; + + var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data)); + + // if there is already data on the graph, append the new data + // if you only want to redraw, pass a non-array for data + if(Array.isArray(data)) { + helpers.cleanData(data); + + if(graphWasEmpty) gd.data = data; + else gd.data.push.apply(gd.data, data); + + // for routines outside graph_obj that want a clean tab + // (rather than appending to an existing one) gd.empty + // is used to determine whether to make a new tab + gd.empty = false; + } + + if(!gd.layout || graphWasEmpty) { + gd.layout = helpers.cleanLayout(layout); + } + + Plots.supplyDefaults(gd); + + var fullLayout = gd._fullLayout; + var hasCartesian = fullLayout._has('cartesian'); + + // so we don't try to re-call _doPlot from inside + // legend and colorbar, if margins changed + fullLayout._replotting = true; + + // make or remake the framework if we need to + if(graphWasEmpty || fullLayout._shouldCreateBgLayer) { + makePlotFramework(gd); + + if(fullLayout._shouldCreateBgLayer) { + delete fullLayout._shouldCreateBgLayer; + } + } + + // clear gradient and pattern defs on each .plot call, because we know we'll loop through all traces + Drawing.initGradients(gd); + Drawing.initPatterns(gd); + + // save initial show spikes once per graph + if(graphWasEmpty) Axes.saveShowSpikeInitial(gd); + + // prepare the data and find the autorange + + // generate calcdata, if we need to + // to force redoing calcdata, just delete it before calling _doPlot + var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length; + if(recalc) Plots.doCalcdata(gd); + + // in case it has changed, attach fullData traces to calcdata + for(var i = 0; i < gd.calcdata.length; i++) { + gd.calcdata[i][0].trace = gd._fullData[i]; + } + + // make the figure responsive + if(gd._context.responsive) { + if(!gd._responsiveChartHandler) { + // Keep a reference to the resize handler to purge it down the road + gd._responsiveChartHandler = function() { if(!Lib.isHidden(gd)) Plots.resize(gd); }; + + // Listen to window resize + window.addEventListener('resize', gd._responsiveChartHandler); + } + } else { + Lib.clearResponsive(gd); + } + + /* + * start async-friendly code - now we're actually drawing things + */ + + var oldMargins = Lib.extendFlat({}, fullLayout._size); + + // draw framework first so that margin-pushing + // components can position themselves correctly + var drawFrameworkCalls = 0; + function drawFramework() { + var basePlotModules = fullLayout._basePlotModules; + + for(var i = 0; i < basePlotModules.length; i++) { + if(basePlotModules[i].drawFramework) { + basePlotModules[i].drawFramework(gd); + } + } + + if(!fullLayout._glcanvas && fullLayout._has('gl')) { + fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{ + key: 'contextLayer', + context: true, + pick: false + }, { + key: 'focusLayer', + context: false, + pick: false + }, { + key: 'pickLayer', + context: false, + pick: true + }], function(d) { return d.key; }); + + fullLayout._glcanvas.enter().append('canvas') + .attr('class', function(d) { + return 'gl-canvas gl-canvas-' + d.key.replace('Layer', ''); + }) + .style({ + position: 'absolute', + top: 0, + left: 0, + overflow: 'visible', + 'pointer-events': 'none' + }); + } + + if(fullLayout._glcanvas) { + fullLayout._glcanvas + .attr('width', fullLayout.width) + .attr('height', fullLayout.height); + + var regl = fullLayout._glcanvas.data()[0].regl; + if(regl) { + // Unfortunately, this can happen when relayouting to large + // width/height on some browsers. + if(Math.floor(fullLayout.width) !== regl._gl.drawingBufferWidth || + Math.floor(fullLayout.height) !== regl._gl.drawingBufferHeight + ) { + var msg = 'WebGL context buffer and canvas dimensions do not match due to browser/WebGL bug.'; + if(drawFrameworkCalls) { + Lib.error(msg); + } else { + Lib.log(msg + ' Clearing graph and plotting again.'); + Plots.cleanPlot([], {}, gd._fullData, fullLayout); + Plots.supplyDefaults(gd); + fullLayout = gd._fullLayout; + Plots.doCalcdata(gd); + drawFrameworkCalls++; + return drawFramework(); + } + } + } + } + + if(fullLayout.modebar.orientation === 'h') { + fullLayout._modebardiv + .style('height', null) + .style('width', '100%'); + } else { + fullLayout._modebardiv + .style('width', null) + .style('height', fullLayout.height + 'px'); + } + + return Plots.previousPromises(gd); + } + + // draw anything that can affect margins. + function marginPushers() { + // First reset the list of things that are allowed to change the margins + // So any deleted traces or components will be wiped out of the + // automargin calculation. + // This means *every* margin pusher must be listed here, even if it + // doesn't actually try to push the margins until later. + Plots.clearAutoMarginIds(gd); + + subroutines.drawMarginPushers(gd); + Axes.allowAutoMargin(gd); + + // TODO can this be moved elsewhere? + if(fullLayout._has('pie')) { + var fullData = gd._fullData; + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + if(trace.type === 'pie' && trace.automargin) { + Plots.allowAutoMargin(gd, 'pie.' + trace.uid + '.automargin'); + } + } + } + + Plots.doAutoMargin(gd); + return Plots.previousPromises(gd); + } + + // in case the margins changed, draw margin pushers again + function marginPushersAgain() { + if(!Plots.didMarginChange(oldMargins, fullLayout._size)) return; + + return Lib.syncOrAsync([ + marginPushers, + subroutines.layoutStyles + ], gd); + } + + function positionAndAutorange() { + if(!recalc) { + doAutoRangeAndConstraints(); + return; + } + + // TODO: autosize extra for text markers and images + // see https://github.com/plotly/plotly.js/issues/1111 + return Lib.syncOrAsync([ + Registry.getComponentMethod('shapes', 'calcAutorange'), + Registry.getComponentMethod('annotations', 'calcAutorange'), + doAutoRangeAndConstraints + ], gd); + } + + function doAutoRangeAndConstraints() { + if(gd._transitioning) return; + + subroutines.doAutoRangeAndConstraints(gd); + + // store initial ranges *after* enforcing constraints, otherwise + // we will never look like we're at the initial ranges + if(graphWasEmpty) Axes.saveRangeInitial(gd); + + // this one is different from shapes/annotations calcAutorange + // the others incorporate those components into ax._extremes, + // this one actually sets the ranges in rangesliders. + Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd); + } + + // draw ticks, titles, and calculate axis scaling (._b, ._m) + function drawAxes() { + return Axes.draw(gd, graphWasEmpty ? '' : 'redraw'); + } + + var seq = [ + Plots.previousPromises, + addFrames, + drawFramework, + marginPushers, + marginPushersAgain + ]; + + if(hasCartesian) seq.push(positionAndAutorange); + + seq.push(subroutines.layoutStyles); + if(hasCartesian) { + seq.push( + drawAxes, + function insideTickLabelsAutorange(gd) { + if(gd._fullLayout._insideTickLabelsAutorange) { + relayout(gd, gd._fullLayout._insideTickLabelsAutorange).then(function() { + gd._fullLayout._insideTickLabelsAutorange = undefined; + }); + } + } + ); + } + + seq.push( + subroutines.drawData, + subroutines.finalDraw, + initInteractions, + Plots.addLinks, + Plots.rehover, + Plots.redrag, + // TODO: doAutoMargin is only needed here for axis automargin, which + // happens outside of marginPushers where all the other automargins are + // calculated. Would be much better to separate margin calculations from + // component drawing - see https://github.com/plotly/plotly.js/issues/2704 + Plots.doAutoMargin, + saveRangeInitialForInsideTickLabels, + Plots.previousPromises + ); + + function saveRangeInitialForInsideTickLabels(gd) { + if(gd._fullLayout._insideTickLabelsAutorange) { + if(graphWasEmpty) Axes.saveRangeInitial(gd, true); + } + } + + // even if everything we did was synchronous, return a promise + // so that the caller doesn't care which route we took + var plotDone = Lib.syncOrAsync(seq, gd); + if(!plotDone || !plotDone.then) plotDone = Promise.resolve(); + + return plotDone.then(function() { + emitAfterPlot(gd); + return gd; + }); +} + +function emitAfterPlot(gd) { + var fullLayout = gd._fullLayout; + + if(fullLayout._redrawFromAutoMarginCount) { + fullLayout._redrawFromAutoMarginCount--; + } else { + gd.emit('plotly_afterplot'); + } +} + +function setPlotConfig(obj) { + return Lib.extendFlat(dfltConfig, obj); +} + +function setBackground(gd, bgColor) { + try { + gd._fullLayout._paper.style('background', bgColor); + } catch(e) { + Lib.error(e); + } +} + +function opaqueSetBackground(gd, bgColor) { + var blend = Color.combine(bgColor, 'white'); + setBackground(gd, blend); +} + +function setPlotContext(gd, config) { + if(!gd._context) { + gd._context = Lib.extendDeep({}, dfltConfig); + + // stash href, used to make robust clipPath URLs + var base = d3.select('base'); + gd._context._baseUrl = base.size() && base.attr('href') ? + window.location.href.split('#')[0] : + ''; + } + + var context = gd._context; + + var i, keys, key; + + if(config) { + keys = Object.keys(config); + for(i = 0; i < keys.length; i++) { + key = keys[i]; + if(key === 'editable' || key === 'edits') continue; + if(key in context) { + if(key === 'setBackground' && config[key] === 'opaque') { + context[key] = opaqueSetBackground; + } else { + context[key] = config[key]; + } + } + } + + // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility + if(config.plot3dPixelRatio && !context.plotGlPixelRatio) { + context.plotGlPixelRatio = context.plot3dPixelRatio; + } + + // now deal with editable and edits - first editable overrides + // everything, then edits refines + var editable = config.editable; + if(editable !== undefined) { + // we're not going to *use* context.editable, we're only going to + // use context.edits... but keep it for the record + context.editable = editable; + + keys = Object.keys(context.edits); + for(i = 0; i < keys.length; i++) { + context.edits[keys[i]] = editable; + } + } + if(config.edits) { + keys = Object.keys(config.edits); + for(i = 0; i < keys.length; i++) { + key = keys[i]; + if(key in context.edits) { + context.edits[key] = config.edits[key]; + } + } + } + + // not part of the user-facing config options + context._exportedPlot = config._exportedPlot; + } + + // staticPlot forces a bunch of others: + if(context.staticPlot) { + context.editable = false; + context.edits = {}; + context.autosizable = false; + context.scrollZoom = false; + context.doubleClick = false; + context.showTips = false; + context.showLink = false; + context.displayModeBar = false; + } + + // make sure hover-only devices have mode bar visible + if(context.displayModeBar === 'hover' && !hasHover) { + context.displayModeBar = true; + } + + // default and fallback for setBackground + if(context.setBackground === 'transparent' || typeof context.setBackground !== 'function') { + context.setBackground = setBackground; + } + + // Check if gd has a specified widht/height to begin with + context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0; + context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0; + + // fill context._scrollZoom helper to help manage scrollZoom flaglist + var szIn = context.scrollZoom; + var szOut = context._scrollZoom = {}; + if(szIn === true) { + szOut.cartesian = 1; + szOut.gl3d = 1; + szOut.geo = 1; + szOut.mapbox = 1; + } else if(typeof szIn === 'string') { + var parts = szIn.split('+'); + for(i = 0; i < parts.length; i++) { + szOut[parts[i]] = 1; + } + } else if(szIn !== false) { + szOut.gl3d = 1; + szOut.geo = 1; + szOut.mapbox = 1; + } +} + + +// convenience function to force a full redraw, mostly for use by plotly.js +function redraw(gd) { + gd = Lib.getGraphDiv(gd); + + if(!Lib.isPlotDiv(gd)) { + throw new Error('This element is not a Plotly plot: ' + gd); + } + + helpers.cleanData(gd.data); + helpers.cleanLayout(gd.layout); + + gd.calcdata = undefined; + return exports._doPlot(gd).then(function() { + gd.emit('plotly_redraw'); + return gd; + }); +} + +/** + * Convenience function to make idempotent plot option obvious to users. + * + * @param gd + * @param {Object[]} data + * @param {Object} layout + * @param {Object} config + */ +function newPlot(gd, data, layout, config) { + gd = Lib.getGraphDiv(gd); + + // remove gl contexts + Plots.cleanPlot([], {}, gd._fullData || [], gd._fullLayout || {}); + + Plots.purge(gd); + return exports._doPlot(gd, data, layout, config); +} + +/** + * Wrap negative indicies to their positive counterparts. + * + * @param {Number[]} indices An array of indices + * @param {Number} maxIndex The maximum index allowable (arr.length - 1) + */ +function positivifyIndices(indices, maxIndex) { + var parentLength = maxIndex + 1; + var positiveIndices = []; + var i; + var index; + + for(i = 0; i < indices.length; i++) { + index = indices[i]; + if(index < 0) { + positiveIndices.push(parentLength + index); + } else { + positiveIndices.push(index); + } + } + return positiveIndices; +} + +/** + * Ensures that an index array for manipulating gd.data is valid. + * + * Intended for use with addTraces, deleteTraces, and moveTraces. + * + * @param gd + * @param indices + * @param arrayName + */ +function assertIndexArray(gd, indices, arrayName) { + var i, + index; + + for(i = 0; i < indices.length; i++) { + index = indices[i]; + + // validate that indices are indeed integers + if(index !== parseInt(index, 10)) { + throw new Error('all values in ' + arrayName + ' must be integers'); + } + + // check that all indices are in bounds for given gd.data array length + if(index >= gd.data.length || index < -gd.data.length) { + throw new Error(arrayName + ' must be valid indices for gd.data.'); + } + + // check that indices aren't repeated + if(indices.indexOf(index, i + 1) > -1 || + index >= 0 && indices.indexOf(-gd.data.length + index) > -1 || + index < 0 && indices.indexOf(gd.data.length + index) > -1) { + throw new Error('each index in ' + arrayName + ' must be unique.'); + } + } +} + +/** + * Private function used by Plotly.moveTraces to check input args + * + * @param gd + * @param currentIndices + * @param newIndices + */ +function checkMoveTracesArgs(gd, currentIndices, newIndices) { + // check that gd has attribute 'data' and 'data' is array + if(!Array.isArray(gd.data)) { + throw new Error('gd.data must be an array.'); + } + + // validate currentIndices array + if(typeof currentIndices === 'undefined') { + throw new Error('currentIndices is a required argument.'); + } else if(!Array.isArray(currentIndices)) { + currentIndices = [currentIndices]; + } + assertIndexArray(gd, currentIndices, 'currentIndices'); + + // validate newIndices array if it exists + if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) { + newIndices = [newIndices]; + } + if(typeof newIndices !== 'undefined') { + assertIndexArray(gd, newIndices, 'newIndices'); + } + + // check currentIndices and newIndices are the same length if newIdices exists + if(typeof newIndices !== 'undefined' && currentIndices.length !== newIndices.length) { + throw new Error('current and new indices must be of equal length.'); + } +} +/** + * A private function to reduce the type checking clutter in addTraces. + * + * @param gd + * @param traces + * @param newIndices + */ +function checkAddTracesArgs(gd, traces, newIndices) { + var i, value; + + // check that gd has attribute 'data' and 'data' is array + if(!Array.isArray(gd.data)) { + throw new Error('gd.data must be an array.'); + } + + // make sure traces exists + if(typeof traces === 'undefined') { + throw new Error('traces must be defined.'); + } + + // make sure traces is an array + if(!Array.isArray(traces)) { + traces = [traces]; + } + + // make sure each value in traces is an object + for(i = 0; i < traces.length; i++) { + value = traces[i]; + if(typeof value !== 'object' || (Array.isArray(value) || value === null)) { + throw new Error('all values in traces array must be non-array objects'); + } + } + + // make sure we have an index for each trace + if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) { + newIndices = [newIndices]; + } + if(typeof newIndices !== 'undefined' && newIndices.length !== traces.length) { + throw new Error( + 'if indices is specified, traces.length must equal indices.length' + ); + } +} + +/** + * A private function to reduce the type checking clutter in spliceTraces. + * Get all update Properties from gd.data. Validate inputs and outputs. + * Used by prependTrace and extendTraces + * + * @param gd + * @param update + * @param indices + * @param maxPoints + */ +function assertExtendTracesArgs(gd, update, indices, maxPoints) { + var maxPointsIsObject = Lib.isPlainObject(maxPoints); + + if(!Array.isArray(gd.data)) { + throw new Error('gd.data must be an array'); + } + if(!Lib.isPlainObject(update)) { + throw new Error('update must be a key:value object'); + } + + if(typeof indices === 'undefined') { + throw new Error('indices must be an integer or array of integers'); + } + + assertIndexArray(gd, indices, 'indices'); + + for(var key in update) { + /* + * Verify that the attribute to be updated contains as many trace updates + * as indices. Failure must result in throw and no-op + */ + if(!Array.isArray(update[key]) || update[key].length !== indices.length) { + throw new Error('attribute ' + key + ' must be an array of length equal to indices array length'); + } + + /* + * if maxPoints is an object it must match keys and array lengths of 'update' 1:1 + */ + if(maxPointsIsObject && + (!(key in maxPoints) || !Array.isArray(maxPoints[key]) || + maxPoints[key].length !== update[key].length)) { + throw new Error('when maxPoints is set as a key:value object it must contain a 1:1 ' + + 'corrispondence with the keys and number of traces in the update object'); + } + } +} + +/** + * A private function to reduce the type checking clutter in spliceTraces. + * + * @param {Object|HTMLDivElement} gd + * @param {Object} update + * @param {Number[]} indices + * @param {Number||Object} maxPoints + * @return {Object[]} + */ +function getExtendProperties(gd, update, indices, maxPoints) { + var maxPointsIsObject = Lib.isPlainObject(maxPoints); + var updateProps = []; + var trace, target, prop, insert, maxp; + + // allow scalar index to represent a single trace position + if(!Array.isArray(indices)) indices = [indices]; + + // negative indices are wrapped around to their positive value. Equivalent to python indexing. + indices = positivifyIndices(indices, gd.data.length - 1); + + // loop through all update keys and traces and harvest validated data. + for(var key in update) { + for(var j = 0; j < indices.length; j++) { + /* + * Choose the trace indexed by the indices map argument and get the prop setter-getter + * instance that references the key and value for this particular trace. + */ + trace = gd.data[indices[j]]; + prop = nestedProperty(trace, key); + + /* + * Target is the existing gd.data.trace.dataArray value like "x" or "marker.size" + * Target must exist as an Array to allow the extend operation to be performed. + */ + target = prop.get(); + insert = update[key][j]; + + if(!Lib.isArrayOrTypedArray(insert)) { + throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array'); + } + if(!Lib.isArrayOrTypedArray(target)) { + throw new Error('cannot extend missing or non-array attribute: ' + key); + } + if(target.constructor !== insert.constructor) { + throw new Error('cannot extend array with an array of a different type: ' + key); + } + + /* + * maxPoints may be an object map or a scalar. If object select the key:value, else + * Use the scalar maxPoints for all key and trace combinations. + */ + maxp = maxPointsIsObject ? maxPoints[key][j] : maxPoints; + + // could have chosen null here, -1 just tells us to not take a window + if(!isNumeric(maxp)) maxp = -1; + + /* + * Wrap the nestedProperty in an object containing required data + * for lengthening and windowing this particular trace - key combination. + * Flooring maxp mirrors the behaviour of floats in the Array.slice JSnative function. + */ + updateProps.push({ + prop: prop, + target: target, + insert: insert, + maxp: Math.floor(maxp) + }); + } + } + + // all target and insertion data now validated + return updateProps; +} + +/** + * A private function to key Extend and Prepend traces DRY + * + * @param {Object|HTMLDivElement} gd + * @param {Object} update + * @param {Number[]} indices + * @param {Number||Object} maxPoints + * @param {Function} updateArray + * @return {Object} + */ +function spliceTraces(gd, update, indices, maxPoints, updateArray) { + assertExtendTracesArgs(gd, update, indices, maxPoints); + + var updateProps = getExtendProperties(gd, update, indices, maxPoints); + var undoUpdate = {}; + var undoPoints = {}; + + for(var i = 0; i < updateProps.length; i++) { + var prop = updateProps[i].prop; + var maxp = updateProps[i].maxp; + + // return new array and remainder + var out = updateArray(updateProps[i].target, updateProps[i].insert, maxp); + prop.set(out[0]); + + // build the inverse update object for the undo operation + if(!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = []; + undoUpdate[prop.astr].push(out[1]); + + // build the matching maxPoints undo object containing original trace lengths + if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = []; + undoPoints[prop.astr].push(updateProps[i].target.length); + } + + return {update: undoUpdate, maxPoints: undoPoints}; +} + +function concatTypedArray(arr0, arr1) { + var arr2 = new arr0.constructor(arr0.length + arr1.length); + arr2.set(arr0); + arr2.set(arr1, arr0.length); + return arr2; +} + +/** + * extend && prepend traces at indices with update arrays, window trace lengths to maxPoints + * + * Extend and Prepend have identical APIs. Prepend inserts an array at the head while Extend + * inserts an array off the tail. Prepend truncates the tail of the array - counting maxPoints + * from the head, whereas Extend truncates the head of the array, counting backward maxPoints + * from the tail. + * + * If maxPoints is undefined, nonNumeric, negative or greater than extended trace length no + * truncation / windowing will be performed. If its zero, well the whole trace is truncated. + * + * @param {Object|HTMLDivElement} gd The graph div + * @param {Object} update The key:array map of target attributes to extend + * @param {Number|Number[]} indices The locations of traces to be extended + * @param {Number|Object} [maxPoints] Number of points for trace window after lengthening. + * + */ +function extendTraces(gd, update, indices, maxPoints) { + gd = Lib.getGraphDiv(gd); + + function updateArray(target, insert, maxp) { + var newArray, remainder; + + if(Lib.isTypedArray(target)) { + if(maxp < 0) { + var none = new target.constructor(0); + var both = concatTypedArray(target, insert); + + if(maxp < 0) { + newArray = both; + remainder = none; + } else { + newArray = none; + remainder = both; + } + } else { + newArray = new target.constructor(maxp); + remainder = new target.constructor(target.length + insert.length - maxp); + + if(maxp === insert.length) { + newArray.set(insert); + remainder.set(target); + } else if(maxp < insert.length) { + var numberOfItemsFromInsert = insert.length - maxp; + + newArray.set(insert.subarray(numberOfItemsFromInsert)); + remainder.set(target); + remainder.set(insert.subarray(0, numberOfItemsFromInsert), target.length); + } else { + var numberOfItemsFromTarget = maxp - insert.length; + var targetBegin = target.length - numberOfItemsFromTarget; + + newArray.set(target.subarray(targetBegin)); + newArray.set(insert, numberOfItemsFromTarget); + remainder.set(target.subarray(0, targetBegin)); + } + } + } else { + newArray = target.concat(insert); + remainder = (maxp >= 0 && maxp < newArray.length) ? + newArray.splice(0, newArray.length - maxp) : + []; + } + + return [newArray, remainder]; + } + + var undo = spliceTraces(gd, update, indices, maxPoints, updateArray); + var promise = exports.redraw(gd); + var undoArgs = [gd, undo.update, indices, undo.maxPoints]; + Queue.add(gd, exports.prependTraces, undoArgs, extendTraces, arguments); + + return promise; +} + +function prependTraces(gd, update, indices, maxPoints) { + gd = Lib.getGraphDiv(gd); + + function updateArray(target, insert, maxp) { + var newArray, remainder; + + if(Lib.isTypedArray(target)) { + if(maxp <= 0) { + var none = new target.constructor(0); + var both = concatTypedArray(insert, target); + + if(maxp < 0) { + newArray = both; + remainder = none; + } else { + newArray = none; + remainder = both; + } + } else { + newArray = new target.constructor(maxp); + remainder = new target.constructor(target.length + insert.length - maxp); + + if(maxp === insert.length) { + newArray.set(insert); + remainder.set(target); + } else if(maxp < insert.length) { + var numberOfItemsFromInsert = insert.length - maxp; + + newArray.set(insert.subarray(0, numberOfItemsFromInsert)); + remainder.set(insert.subarray(numberOfItemsFromInsert)); + remainder.set(target, numberOfItemsFromInsert); + } else { + var numberOfItemsFromTarget = maxp - insert.length; + + newArray.set(insert); + newArray.set(target.subarray(0, numberOfItemsFromTarget), insert.length); + remainder.set(target.subarray(numberOfItemsFromTarget)); + } + } + } else { + newArray = insert.concat(target); + remainder = (maxp >= 0 && maxp < newArray.length) ? + newArray.splice(maxp, newArray.length) : + []; + } + + return [newArray, remainder]; + } + + var undo = spliceTraces(gd, update, indices, maxPoints, updateArray); + var promise = exports.redraw(gd); + var undoArgs = [gd, undo.update, indices, undo.maxPoints]; + Queue.add(gd, exports.extendTraces, undoArgs, prependTraces, arguments); + + return promise; +} + +/** + * Add data traces to an existing graph div. + * + * @param {Object|HTMLDivElement} gd The graph div + * @param {Object[]} gd.data The array of traces we're adding to + * @param {Object[]|Object} traces The object or array of objects to add + * @param {Number[]|Number} [newIndices=[gd.data.length]] Locations to add traces + * + */ +function addTraces(gd, traces, newIndices) { + gd = Lib.getGraphDiv(gd); + + var currentIndices = []; + var undoFunc = exports.deleteTraces; + var redoFunc = addTraces; + var undoArgs = [gd, currentIndices]; + var redoArgs = [gd, traces]; // no newIndices here + var i; + var promise; + + // all validation is done elsewhere to remove clutter here + checkAddTracesArgs(gd, traces, newIndices); + + // make sure traces is an array + if(!Array.isArray(traces)) { + traces = [traces]; + } + + // make sure traces do not repeat existing ones + traces = traces.map(function(trace) { + return Lib.extendFlat({}, trace); + }); + + helpers.cleanData(traces); + + // add the traces to gd.data (no redrawing yet!) + for(i = 0; i < traces.length; i++) { + gd.data.push(traces[i]); + } + + // to continue, we need to call moveTraces which requires currentIndices + for(i = 0; i < traces.length; i++) { + currentIndices.push(-traces.length + i); + } + + // if the user didn't define newIndices, they just want the traces appended + // i.e., we can simply redraw and be done + if(typeof newIndices === 'undefined') { + promise = exports.redraw(gd); + Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); + return promise; + } + + // make sure indices is property defined + if(!Array.isArray(newIndices)) { + newIndices = [newIndices]; + } + + try { + // this is redundant, but necessary to not catch later possible errors! + checkMoveTracesArgs(gd, currentIndices, newIndices); + } catch(error) { + // something went wrong, reset gd to be safe and rethrow error + gd.data.splice(gd.data.length - traces.length, traces.length); + throw error; + } + + // if we're here, the user has defined specific places to place the new traces + // this requires some extra work that moveTraces will do + Queue.startSequence(gd); + Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); + promise = exports.moveTraces(gd, currentIndices, newIndices); + Queue.stopSequence(gd); + return promise; +} + +/** + * Delete traces at `indices` from gd.data array. + * + * @param {Object|HTMLDivElement} gd The graph div + * @param {Object[]} gd.data The array of traces we're removing from + * @param {Number|Number[]} indices The indices + */ +function deleteTraces(gd, indices) { + gd = Lib.getGraphDiv(gd); + + var traces = []; + var undoFunc = exports.addTraces; + var redoFunc = deleteTraces; + var undoArgs = [gd, traces, indices]; + var redoArgs = [gd, indices]; + var i; + var deletedTrace; + + // make sure indices are defined + if(typeof indices === 'undefined') { + throw new Error('indices must be an integer or array of integers.'); + } else if(!Array.isArray(indices)) { + indices = [indices]; + } + assertIndexArray(gd, indices, 'indices'); + + // convert negative indices to positive indices + indices = positivifyIndices(indices, gd.data.length - 1); + + // we want descending here so that splicing later doesn't affect indexing + indices.sort(Lib.sorterDes); + for(i = 0; i < indices.length; i += 1) { + deletedTrace = gd.data.splice(indices[i], 1)[0]; + traces.push(deletedTrace); + } + + var promise = exports.redraw(gd); + Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); + + return promise; +} + +/** + * Move traces at currentIndices array to locations in newIndices array. + * + * If newIndices is omitted, currentIndices will be moved to the end. E.g., + * these are equivalent: + * + * Plotly.moveTraces(gd, [1, 2, 3], [-3, -2, -1]) + * Plotly.moveTraces(gd, [1, 2, 3]) + * + * @param {Object|HTMLDivElement} gd The graph div + * @param {Object[]} gd.data The array of traces we're removing from + * @param {Number|Number[]} currentIndices The locations of traces to be moved + * @param {Number|Number[]} [newIndices] The locations to move traces to + * + * Example calls: + * + * // move trace i to location x + * Plotly.moveTraces(gd, i, x) + * + * // move trace i to end of array + * Plotly.moveTraces(gd, i) + * + * // move traces i, j, k to end of array (i != j != k) + * Plotly.moveTraces(gd, [i, j, k]) + * + * // move traces [i, j, k] to [x, y, z] (i != j != k) (x != y != z) + * Plotly.moveTraces(gd, [i, j, k], [x, y, z]) + * + * // reorder all traces (assume there are 5--a, b, c, d, e) + * Plotly.moveTraces(gd, [b, d, e, a, c]) // same as 'move to end' + */ +function moveTraces(gd, currentIndices, newIndices) { + gd = Lib.getGraphDiv(gd); + + var newData = []; + var movingTraceMap = []; + var undoFunc = moveTraces; + var redoFunc = moveTraces; + var undoArgs = [gd, newIndices, currentIndices]; + var redoArgs = [gd, currentIndices, newIndices]; + var i; + + // to reduce complexity here, check args elsewhere + // this throws errors where appropriate + checkMoveTracesArgs(gd, currentIndices, newIndices); + + // make sure currentIndices is an array + currentIndices = Array.isArray(currentIndices) ? currentIndices : [currentIndices]; + + // if undefined, define newIndices to point to the end of gd.data array + if(typeof newIndices === 'undefined') { + newIndices = []; + for(i = 0; i < currentIndices.length; i++) { + newIndices.push(-currentIndices.length + i); + } + } + + // make sure newIndices is an array if it's user-defined + newIndices = Array.isArray(newIndices) ? newIndices : [newIndices]; + + // convert negative indices to positive indices (they're the same length) + currentIndices = positivifyIndices(currentIndices, gd.data.length - 1); + newIndices = positivifyIndices(newIndices, gd.data.length - 1); + + // at this point, we've coerced the index arrays into predictable forms + + // get the traces that aren't being moved around + for(i = 0; i < gd.data.length; i++) { + // if index isn't in currentIndices, include it in ignored! + if(currentIndices.indexOf(i) === -1) { + newData.push(gd.data[i]); + } + } + + // get a mapping of indices to moving traces + for(i = 0; i < currentIndices.length; i++) { + movingTraceMap.push({newIndex: newIndices[i], trace: gd.data[currentIndices[i]]}); + } + + // reorder this mapping by newIndex, ascending + movingTraceMap.sort(function(a, b) { + return a.newIndex - b.newIndex; + }); + + // now, add the moving traces back in, in order! + for(i = 0; i < movingTraceMap.length; i += 1) { + newData.splice(movingTraceMap[i].newIndex, 0, movingTraceMap[i].trace); + } + + gd.data = newData; + + var promise = exports.redraw(gd); + Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); + + return promise; +} + +/** + * restyle: update trace attributes of an existing plot + * + * Can be called two ways. + * + * Signature 1: + * @param {String | HTMLDivElement} gd + * the id or DOM element of the graph container div + * @param {String} astr + * attribute string (like `'marker.symbol'`) to update + * @param {*} val + * value to give this attribute + * @param {Number[] | Number} [traces] + * integer or array of integers for the traces to alter (all if omitted) + * + * Signature 2: + * @param {String | HTMLDivElement} gd + * (as in signature 1) + * @param {Object} aobj + * attribute object `{astr1: val1, astr2: val2 ...}` + * allows setting multiple attributes simultaneously + * @param {Number[] | Number} [traces] + * (as in signature 1) + * + * `val` (or `val1`, `val2` ... in the object form) can be an array, + * to apply different values to each trace. + * + * If the array is too short, it will wrap around (useful for + * style files that want to specify cyclical default values). + */ +function restyle(gd, astr, val, _traces) { + gd = Lib.getGraphDiv(gd); + helpers.clearPromiseQueue(gd); + + var aobj = {}; + if(typeof astr === 'string') aobj[astr] = val; + else if(Lib.isPlainObject(astr)) { + // the 3-arg form + aobj = Lib.extendFlat({}, astr); + if(_traces === undefined) _traces = val; + } else { + Lib.warn('Restyle fail.', astr, val, _traces); + return Promise.reject(); + } + + if(Object.keys(aobj).length) gd.changed = true; + + var traces = helpers.coerceTraceIndices(gd, _traces); + + var specs = _restyle(gd, aobj, traces); + var flags = specs.flags; + + // clear calcdata and/or axis types if required so they get regenerated + if(flags.calc) gd.calcdata = undefined; + if(flags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, {}); + + // fill in redraw sequence + var seq = []; + + if(flags.fullReplot) { + seq.push(exports._doPlot); + } else { + seq.push(Plots.previousPromises); + + // maybe only call Plots.supplyDataDefaults in the splom case, + // to skip over long and slow axes defaults + Plots.supplyDefaults(gd); + + if(flags.markerSize) { + Plots.doCalcdata(gd); + addAxRangeSequence(seq); + + // TODO + // if all axes have autorange:false, then + // proceed to subroutines.doTraceStyle(), + // otherwise we must go through addAxRangeSequence, + // which in general must redraws 'all' axes + } + + if(flags.style) seq.push(subroutines.doTraceStyle); + if(flags.colorbars) seq.push(subroutines.doColorBars); + + seq.push(emitAfterPlot); + } + + seq.push(Plots.rehover, Plots.redrag); + + Queue.add(gd, + restyle, [gd, specs.undoit, specs.traces], + restyle, [gd, specs.redoit, specs.traces] + ); + + var plotDone = Lib.syncOrAsync(seq, gd); + if(!plotDone || !plotDone.then) plotDone = Promise.resolve(); + + return plotDone.then(function() { + gd.emit('plotly_restyle', specs.eventData); + return gd; + }); +} + +// for undo: undefined initial vals must be turned into nulls +// so that we unset rather than ignore them +function undefinedToNull(val) { + if(val === undefined) return null; + return val; +} + +/** + * Factory function to wrap nestedProperty with GUI edits if necessary + * with GUI edits we add an optional prefix to the nestedProperty constructor + * to prepend to the attribute string in the preGUI store. + */ +function makeNP(preGUI, guiEditFlag) { + if(!guiEditFlag) return nestedProperty; + + return function(container, attr, prefix) { + var np = nestedProperty(container, attr); + var npSet = np.set; + np.set = function(val) { + var fullAttr = (prefix || '') + attr; + storeCurrent(fullAttr, np.get(), val, preGUI); + npSet(val); + }; + return np; + }; +} + +function storeCurrent(attr, val, newVal, preGUI) { + if(Array.isArray(val) || Array.isArray(newVal)) { + var arrayVal = Array.isArray(val) ? val : []; + var arrayNew = Array.isArray(newVal) ? newVal : []; + var maxLen = Math.max(arrayVal.length, arrayNew.length); + for(var i = 0; i < maxLen; i++) { + storeCurrent(attr + '[' + i + ']', arrayVal[i], arrayNew[i], preGUI); + } + } else if(Lib.isPlainObject(val) || Lib.isPlainObject(newVal)) { + var objVal = Lib.isPlainObject(val) ? val : {}; + var objNew = Lib.isPlainObject(newVal) ? newVal : {}; + var objBoth = Lib.extendFlat({}, objVal, objNew); + for(var key in objBoth) { + storeCurrent(attr + '.' + key, objVal[key], objNew[key], preGUI); + } + } else if(preGUI[attr] === undefined) { + preGUI[attr] = undefinedToNull(val); + } +} + +/** + * storeDirectGUIEdit: for routines that skip restyle/relayout and mock it + * by emitting a plotly_restyle or plotly_relayout event, this routine + * keeps track of the initial state in _preGUI for use by uirevision + * Does *not* apply these changes to data/layout - that's the responsibility + * of the calling routine. + * + * @param {object} container: the input attributes container (eg `layout` or a `trace`) + * @param {object} preGUI: where original values should be stored, either + * `layout._preGUI` or `layout._tracePreGUI[uid]` + * @param {object} edits: the {attr: val} object as normally passed to `relayout` etc + */ +function _storeDirectGUIEdit(container, preGUI, edits) { + for(var attr in edits) { + var np = nestedProperty(container, attr); + storeCurrent(attr, np.get(), edits[attr], preGUI); + } +} + +function _restyle(gd, aobj, traces) { + var fullLayout = gd._fullLayout; + var fullData = gd._fullData; + var data = gd.data; + var guiEditFlag = fullLayout._guiEditing; + var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag); + var eventData = Lib.extendDeepAll({}, aobj); + var i; + + cleanDeprecatedAttributeKeys(aobj); + + // initialize flags + var flags = editTypes.traceFlags(); + + // copies of the change (and previous values of anything affected) + // for the undo / redo queue + var redoit = {}; + var undoit = {}; + var axlist; + + // make a new empty vals array for undoit + function a0() { return traces.map(function() { return undefined; }); } + + // for autoranging multiple axes + function addToAxlist(axid) { + var axName = Axes.id2name(axid); + if(axlist.indexOf(axName) === -1) axlist.push(axName); + } + + function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; } + + function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; } + + function getFullTrace(traceIndex) { + // usually fullData maps 1:1 onto data, but with groupby transforms + // the fullData index can be greater. Take the *first* matching trace. + for(var j = traceIndex; j < fullData.length; j++) { + if(fullData[j]._input === data[traceIndex]) return fullData[j]; + } + // should never get here - and if we *do* it should cause an error + // later on undefined fullTrace is passed to nestedProperty. + } + + // for attrs that interact (like scales & autoscales), save the + // old vals before making the change + // val=undefined will not set a value, just record what the value was. + // val=null will delete the attribute + // attr can be an array to set several at once (all to the same val) + function doextra(attr, val, i) { + if(Array.isArray(attr)) { + attr.forEach(function(a) { doextra(a, val, i); }); + return; + } + // quit if explicitly setting this elsewhere + if(attr in aobj || helpers.hasParent(aobj, attr)) return; + + var extraparam; + if(attr.substr(0, 6) === 'LAYOUT') { + extraparam = layoutNP(gd.layout, attr.replace('LAYOUT', '')); + } else { + var tracei = traces[i]; + var preGUI = fullLayout._tracePreGUI[getFullTrace(tracei)._fullInput.uid]; + extraparam = makeNP(preGUI, guiEditFlag)(data[tracei], attr); + } + + if(!(attr in undoit)) { + undoit[attr] = a0(); + } + if(undoit[attr][i] === undefined) { + undoit[attr][i] = undefinedToNull(extraparam.get()); + } + if(val !== undefined) { + extraparam.set(val); + } + } + + function allBins(binAttr) { + return function(j) { + return fullData[j][binAttr]; + }; + } + + function arrayBins(binAttr) { + return function(vij, j) { + return vij === false ? fullData[traces[j]][binAttr] : null; + }; + } + + // now make the changes to gd.data (and occasionally gd.layout) + // and figure out what kind of graphics update we need to do + for(var ai in aobj) { + if(helpers.hasParent(aobj, ai)) { + throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously'); + } + + var vi = aobj[ai]; + var cont; + var contFull; + var param; + var oldVal; + var newVal; + var valObject; + + // Backward compatibility shim for turning histogram autobin on, + // or freezing previous autobinned values. + // Replace obsolete `autobin(x|y): true` with `(x|y)bins: null` + // and `autobin(x|y): false` with the `(x|y)bins` in `fullData` + if(ai === 'autobinx' || ai === 'autobiny') { + ai = ai.charAt(ai.length - 1) + 'bins'; + if(Array.isArray(vi)) vi = vi.map(arrayBins(ai)); + else if(vi === false) vi = traces.map(allBins(ai)); + else vi = null; + } + + redoit[ai] = vi; + + if(ai.substr(0, 6) === 'LAYOUT') { + param = layoutNP(gd.layout, ai.replace('LAYOUT', '')); + undoit[ai] = [undefinedToNull(param.get())]; + // since we're allowing val to be an array, allow it here too, + // even though that's meaningless + param.set(Array.isArray(vi) ? vi[0] : vi); + // ironically, the layout attrs in restyle only require replot, + // not relayout + flags.calc = true; + continue; + } + + // set attribute in gd.data + undoit[ai] = a0(); + for(i = 0; i < traces.length; i++) { + cont = data[traces[i]]; + contFull = getFullTrace(traces[i]); + var preGUI = fullLayout._tracePreGUI[contFull._fullInput.uid]; + param = makeNP(preGUI, guiEditFlag)(cont, ai); + oldVal = param.get(); + newVal = Array.isArray(vi) ? vi[i % vi.length] : vi; + + if(newVal === undefined) continue; + + var finalPart = param.parts[param.parts.length - 1]; + var prefix = ai.substr(0, ai.length - finalPart.length - 1); + var prefixDot = prefix ? prefix + '.' : ''; + var innerContFull = prefix ? + nestedProperty(contFull, prefix).get() : contFull; + + valObject = PlotSchema.getTraceValObject(contFull, param.parts); + + if(valObject && valObject.impliedEdits && newVal !== null) { + for(var impliedKey in valObject.impliedEdits) { + doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey], i); + } + } else if((finalPart === 'thicknessmode' || finalPart === 'lenmode') && + oldVal !== newVal && + (newVal === 'fraction' || newVal === 'pixels') && + innerContFull + ) { + // changing colorbar size modes, + // make the resulting size not change + // note that colorbar fractional sizing is based on the + // original plot size, before anything (like a colorbar) + // increases the margins + + var gs = fullLayout._size; + var orient = innerContFull.orient; + var topOrBottom = (orient === 'top') || (orient === 'bottom'); + if(finalPart === 'thicknessmode') { + var thicknorm = topOrBottom ? gs.h : gs.w; + doextra(prefixDot + 'thickness', innerContFull.thickness * + (newVal === 'fraction' ? 1 / thicknorm : thicknorm), i); + } else { + var lennorm = topOrBottom ? gs.w : gs.h; + doextra(prefixDot + 'len', innerContFull.len * + (newVal === 'fraction' ? 1 / lennorm : lennorm), i); + } + } else if(ai === 'type' && ( + (newVal === 'pie') !== (oldVal === 'pie') || + (newVal === 'funnelarea') !== (oldVal === 'funnelarea') + )) { + var labelsTo = 'x'; + var valuesTo = 'y'; + if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') { + labelsTo = 'y'; + valuesTo = 'x'; + } + Lib.swapAttrs(cont, ['?', '?src'], 'labels', labelsTo); + Lib.swapAttrs(cont, ['d?', '?0'], 'label', labelsTo); + Lib.swapAttrs(cont, ['?', '?src'], 'values', valuesTo); + + if(oldVal === 'pie' || oldVal === 'funnelarea') { + nestedProperty(cont, 'marker.color') + .set(nestedProperty(cont, 'marker.colors').get()); + + // super kludgy - but if all pies are gone we won't remove them otherwise + fullLayout._pielayer.selectAll('g.trace').remove(); + } else if(Registry.traceIs(cont, 'cartesian')) { + nestedProperty(cont, 'marker.colors') + .set(nestedProperty(cont, 'marker.color').get()); + } + } + + undoit[ai][i] = undefinedToNull(oldVal); + // set the new value - if val is an array, it's one el per trace + // first check for attributes that get more complex alterations + var swapAttrs = [ + 'swapxy', 'swapxyaxes', 'orientation', 'orientationaxes' + ]; + if(swapAttrs.indexOf(ai) !== -1) { + // setting an orientation: make sure it's changing + // before we swap everything else + if(ai === 'orientation') { + param.set(newVal); + // obnoxious that we need this level of coupling... but in order to + // properly handle setting orientation to `null` we need to mimic + // the logic inside Bars.supplyDefaults for default orientation + var defaultOrientation = (cont.x && !cont.y) ? 'h' : 'v'; + if((param.get() || defaultOrientation) === contFull.orientation) { + continue; + } + } else if(ai === 'orientationaxes') { + // orientationaxes has no value, + // it flips everything and the axes + + cont.orientation = + {v: 'h', h: 'v'}[contFull.orientation]; + } + helpers.swapXYData(cont); + flags.calc = flags.clearAxisTypes = true; + } else if(Plots.dataArrayContainers.indexOf(param.parts[0]) !== -1) { + // TODO: use manageArrays.applyContainerArrayChanges here too + helpers.manageArrayContainers(param, newVal, undoit); + flags.calc = true; + } else { + if(valObject) { + // must redo calcdata when restyling array values of arrayOk attributes + // ... but no need to this for regl-based traces + if(valObject.arrayOk && + !Registry.traceIs(contFull, 'regl') && + (Lib.isArrayOrTypedArray(newVal) || Lib.isArrayOrTypedArray(oldVal)) + ) { + flags.calc = true; + } else editTypes.update(flags, valObject); + } else { + /* + * if we couldn't find valObject, assume a full recalc. + * This can happen if you're changing type and making + * some other edits too, so the modules we're + * looking at don't have these attributes in them. + */ + flags.calc = true; + } + + // all the other ones, just modify that one attribute + param.set(newVal); + } + } + + // swap the data attributes of the relevant x and y axes? + if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) { + Axes.swap(gd, traces); + } + + // swap hovermode if set to "compare x/y data" + if(ai === 'orientationaxes') { + var hovermode = nestedProperty(gd.layout, 'hovermode'); + var h = hovermode.get(); + if(h === 'x') { + hovermode.set('y'); + } else if(h === 'y') { + hovermode.set('x'); + } else if(h === 'x unified') { + hovermode.set('y unified'); + } else if(h === 'y unified') { + hovermode.set('x unified'); + } + } + + // Major enough changes deserve autoscale and + // non-reversed axes so people don't get confused + // + // Note: autobin (or its new analog bin clearing) is not included here + // since we're not pushing bins back to gd.data, so if we have bin + // info it was explicitly provided by the user. + if(['orientation', 'type'].indexOf(ai) !== -1) { + axlist = []; + for(i = 0; i < traces.length; i++) { + var trace = data[traces[i]]; + + if(Registry.traceIs(trace, 'cartesian')) { + addToAxlist(trace.xaxis || 'x'); + addToAxlist(trace.yaxis || 'y'); + } + } + + doextra(axlist.map(autorangeAttr), true, 0); + doextra(axlist.map(rangeAttr), [0, 1], 0); + } + } + + if(flags.calc || flags.plot) { + flags.fullReplot = true; + } + + return { + flags: flags, + undoit: undoit, + redoit: redoit, + traces: traces, + eventData: Lib.extendDeepNoArrays([], [eventData, traces]) + }; +} + +/** + * Converts deprecated attribute keys to + * the current API to ensure backwards compatibility. + * + * This is needed for the update mechanism to determine which + * subroutines to run based on the actual attribute + * definitions (that don't include the deprecated ones). + * + * E.g. Maps {'xaxis.title': 'A chart'} to {'xaxis.title.text': 'A chart'} + * and {titlefont: {...}} to {'title.font': {...}}. + * + * @param aobj + */ +function cleanDeprecatedAttributeKeys(aobj) { + var oldAxisTitleRegex = Lib.counterRegex('axis', '\.title', false, false); + var colorbarRegex = /colorbar\.title$/; + var keys = Object.keys(aobj); + var i, key, value; + + for(i = 0; i < keys.length; i++) { + key = keys[i]; + value = aobj[key]; + + if((key === 'title' || oldAxisTitleRegex.test(key) || colorbarRegex.test(key)) && + (typeof value === 'string' || typeof value === 'number')) { + replace(key, key.replace('title', 'title.text')); + } else if(key.indexOf('titlefont') > -1) { + replace(key, key.replace('titlefont', 'title.font')); + } else if(key.indexOf('titleposition') > -1) { + replace(key, key.replace('titleposition', 'title.position')); + } else if(key.indexOf('titleside') > -1) { + replace(key, key.replace('titleside', 'title.side')); + } else if(key.indexOf('titleoffset') > -1) { + replace(key, key.replace('titleoffset', 'title.offset')); + } + } + + function replace(oldAttrStr, newAttrStr) { + aobj[newAttrStr] = aobj[oldAttrStr]; + delete aobj[oldAttrStr]; + } +} + +/** + * relayout: update layout attributes of an existing plot + * + * Can be called two ways: + * + * Signature 1: + * @param {String | HTMLDivElement} gd + * the id or dom element of the graph container div + * @param {String} astr + * attribute string (like `'xaxis.range[0]'`) to update + * @param {*} val + * value to give this attribute + * + * Signature 2: + * @param {String | HTMLDivElement} gd + * (as in signature 1) + * @param {Object} aobj + * attribute object `{astr1: val1, astr2: val2 ...}` + * allows setting multiple attributes simultaneously + */ +function relayout(gd, astr, val) { + gd = Lib.getGraphDiv(gd); + helpers.clearPromiseQueue(gd); + + var aobj = {}; + if(typeof astr === 'string') { + aobj[astr] = val; + } else if(Lib.isPlainObject(astr)) { + aobj = Lib.extendFlat({}, astr); + } else { + Lib.warn('Relayout fail.', astr, val); + return Promise.reject(); + } + + if(Object.keys(aobj).length) gd.changed = true; + + var specs = _relayout(gd, aobj); + var flags = specs.flags; + + // clear calcdata if required + if(flags.calc) gd.calcdata = undefined; + + // fill in redraw sequence + + // even if we don't have anything left in aobj, + // something may have happened within relayout that we + // need to wait for + var seq = [Plots.previousPromises]; + + if(flags.layoutReplot) { + seq.push(subroutines.layoutReplot); + } else if(Object.keys(aobj).length) { + axRangeSupplyDefaultsByPass(gd, flags, specs) || Plots.supplyDefaults(gd); + + if(flags.legend) seq.push(subroutines.doLegend); + if(flags.layoutstyle) seq.push(subroutines.layoutStyles); + if(flags.axrange) addAxRangeSequence(seq, specs.rangesAltered); + if(flags.ticks) seq.push(subroutines.doTicksRelayout); + if(flags.modebar) seq.push(subroutines.doModeBar); + if(flags.camera) seq.push(subroutines.doCamera); + if(flags.colorbars) seq.push(subroutines.doColorBars); + + seq.push(emitAfterPlot); + } + + seq.push(Plots.rehover, Plots.redrag); + + Queue.add(gd, + relayout, [gd, specs.undoit], + relayout, [gd, specs.redoit] + ); + + var plotDone = Lib.syncOrAsync(seq, gd); + if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd); + + return plotDone.then(function() { + gd.emit('plotly_relayout', specs.eventData); + return gd; + }); +} + +// Optimization mostly for large splom traces where +// Plots.supplyDefaults can take > 100ms +function axRangeSupplyDefaultsByPass(gd, flags, specs) { + var fullLayout = gd._fullLayout; + + if(!flags.axrange) return false; + + for(var k in flags) { + if(k !== 'axrange' && flags[k]) return false; + } + + for(var axId in specs.rangesAltered) { + var axName = Axes.id2name(axId); + var axIn = gd.layout[axName]; + var axOut = fullLayout[axName]; + axOut.autorange = axIn.autorange; + if(axIn.range) { + axOut.range = axIn.range.slice(); + } + axOut.cleanRange(); + + if(axOut._matchGroup) { + for(var axId2 in axOut._matchGroup) { + if(axId2 !== axId) { + var ax2 = fullLayout[Axes.id2name(axId2)]; + ax2.autorange = axOut.autorange; + ax2.range = axOut.range.slice(); + ax2._input.range = axOut.range.slice(); + } + } + } + } + + return true; +} + +function addAxRangeSequence(seq, rangesAltered) { + // N.B. leave as sequence of subroutines (for now) instead of + // subroutine of its own so that finalDraw always gets + // executed after drawData + var drawAxes = rangesAltered ? + function(gd) { + var axIds = []; + var skipTitle = true; + + for(var id in rangesAltered) { + var ax = Axes.getFromId(gd, id); + axIds.push(id); + + if((ax.ticklabelposition || '').indexOf('inside') !== -1) { + if(ax._anchorAxis) { + axIds.push(ax._anchorAxis._id); + } + } + + if(ax._matchGroup) { + for(var id2 in ax._matchGroup) { + if(!rangesAltered[id2]) { + axIds.push(id2); + } + } + } + + if(ax.automargin) skipTitle = false; + } + + return Axes.draw(gd, axIds, {skipTitle: skipTitle}); + } : + function(gd) { + return Axes.draw(gd, 'redraw'); + }; + + seq.push( + clearSelect, + subroutines.doAutoRangeAndConstraints, + drawAxes, + subroutines.drawData, + subroutines.finalDraw + ); +} + +var AX_RANGE_RE = /^[xyz]axis[0-9]*\.range(\[[0|1]\])?$/; +var AX_AUTORANGE_RE = /^[xyz]axis[0-9]*\.autorange$/; +var AX_DOMAIN_RE = /^[xyz]axis[0-9]*\.domain(\[[0|1]\])?$/; + +function _relayout(gd, aobj) { + var layout = gd.layout; + var fullLayout = gd._fullLayout; + var guiEditFlag = fullLayout._guiEditing; + var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag); + var keys = Object.keys(aobj); + var axes = Axes.list(gd); + var eventData = Lib.extendDeepAll({}, aobj); + var arrayEdits = {}; + + var arrayStr, i, j; + + cleanDeprecatedAttributeKeys(aobj); + keys = Object.keys(aobj); + + // look for 'allaxes', split out into all axes + // in case of 3D the axis are nested within a scene which is held in _id + for(i = 0; i < keys.length; i++) { + if(keys[i].indexOf('allaxes') === 0) { + for(j = 0; j < axes.length; j++) { + var scene = axes[j]._id.substr(1); + var axisAttr = (scene.indexOf('scene') !== -1) ? (scene + '.') : ''; + var newkey = keys[i].replace('allaxes', axisAttr + axes[j]._name); + + if(!aobj[newkey]) aobj[newkey] = aobj[keys[i]]; + } + + delete aobj[keys[i]]; + } + } + + // initialize flags + var flags = editTypes.layoutFlags(); + + // copies of the change (and previous values of anything affected) + // for the undo / redo queue + var redoit = {}; + var undoit = {}; + + // for attrs that interact (like scales & autoscales), save the + // old vals before making the change + // val=undefined will not set a value, just record what the value was. + // attr can be an array to set several at once (all to the same val) + function doextra(attr, val) { + if(Array.isArray(attr)) { + attr.forEach(function(a) { doextra(a, val); }); + return; + } + + // if we have another value for this attribute (explicitly or + // via a parent) do not override with this auto-generated extra + if(attr in aobj || helpers.hasParent(aobj, attr)) return; + + var p = layoutNP(layout, attr); + if(!(attr in undoit)) { + undoit[attr] = undefinedToNull(p.get()); + } + if(val !== undefined) p.set(val); + } + + // for constraint enforcement: keep track of all axes (as {id: name}) + // we're editing the (auto)range of, so we can tell the others constrained + // to scale with them that it's OK for them to shrink + var rangesAltered = {}; + var ax; + + function recordAlteredAxis(pleafPlus) { + var axId = Axes.name2id(pleafPlus.split('.')[0]); + rangesAltered[axId] = 1; + return axId; + } + + // alter gd.layout + for(var ai in aobj) { + if(helpers.hasParent(aobj, ai)) { + throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously'); + } + + var p = layoutNP(layout, ai); + var vi = aobj[ai]; + var plen = p.parts.length; + // p.parts may end with an index integer if the property is an array + var pend = plen - 1; + while(pend > 0 && typeof p.parts[pend] !== 'string') pend--; + // last property in chain (leaf node) + var pleaf = p.parts[pend]; + // leaf plus immediate parent + var pleafPlus = p.parts[pend - 1] + '.' + pleaf; + // trunk nodes (everything except the leaf) + var ptrunk = p.parts.slice(0, pend).join('.'); + var parentIn = nestedProperty(gd.layout, ptrunk).get(); + var parentFull = nestedProperty(fullLayout, ptrunk).get(); + var vOld = p.get(); + + if(vi === undefined) continue; + + redoit[ai] = vi; + + // axis reverse is special - it is its own inverse + // op and has no flag. + undoit[ai] = (pleaf === 'reverse') ? vi : undefinedToNull(vOld); + + var valObject = PlotSchema.getLayoutValObject(fullLayout, p.parts); + + if(valObject && valObject.impliedEdits && vi !== null) { + for(var impliedKey in valObject.impliedEdits) { + doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey]); + } + } + + // Setting width or height to null must reset the graph's width / height + // back to its initial value as computed during the first pass in Plots.plotAutoSize. + // + // To do so, we must manually set them back here using the _initialAutoSize cache. + // can't use impliedEdits for this because behavior depends on vi + if(['width', 'height'].indexOf(ai) !== -1) { + if(vi) { + doextra('autosize', null); + // currently we don't support autosize one dim only - so + // explicitly set the other one. Note that doextra will + // ignore this if the same relayout call also provides oppositeAttr + var oppositeAttr = ai === 'height' ? 'width' : 'height'; + doextra(oppositeAttr, fullLayout[oppositeAttr]); + } else { + fullLayout[ai] = gd._initialAutoSize[ai]; + } + } else if(ai === 'autosize') { + // depends on vi here too, so again can't use impliedEdits + doextra('width', vi ? null : fullLayout.width); + doextra('height', vi ? null : fullLayout.height); + } else if(pleafPlus.match(AX_RANGE_RE)) { + // check autorange vs range + + recordAlteredAxis(pleafPlus); + nestedProperty(fullLayout, ptrunk + '._inputRange').set(null); + } else if(pleafPlus.match(AX_AUTORANGE_RE)) { + recordAlteredAxis(pleafPlus); + nestedProperty(fullLayout, ptrunk + '._inputRange').set(null); + var axFull = nestedProperty(fullLayout, ptrunk).get(); + if(axFull._inputDomain) { + // if we're autoranging and this axis has a constrained domain, + // reset it so we don't get locked into a shrunken size + axFull._input.domain = axFull._inputDomain.slice(); + } + } else if(pleafPlus.match(AX_DOMAIN_RE)) { + nestedProperty(fullLayout, ptrunk + '._inputDomain').set(null); + } + + // toggling axis type between log and linear: we need to convert + // positions for components that are still using linearized values, + // not data values like newer components. + // previously we did this for log <-> not-log, but now only do it + // for log <-> linear + if(pleaf === 'type') { + ax = parentIn; + var toLog = parentFull.type === 'linear' && vi === 'log'; + var fromLog = parentFull.type === 'log' && vi === 'linear'; + + if(toLog || fromLog) { + if(!ax || !ax.range) { + // 2D never gets here, but 3D does + // I don't think this is needed, but left here in case there + // are edge cases I'm not thinking of. + doextra(ptrunk + '.autorange', true); + } else if(!parentFull.autorange) { + // toggling log without autorange: need to also recalculate ranges + // because log axes use linearized values for range endpoints + var r0 = ax.range[0]; + var r1 = ax.range[1]; + if(toLog) { + // if both limits are negative, autorange + if(r0 <= 0 && r1 <= 0) { + doextra(ptrunk + '.autorange', true); + } + // if one is negative, set it 6 orders below the other. + if(r0 <= 0) r0 = r1 / 1e6; + else if(r1 <= 0) r1 = r0 / 1e6; + // now set the range values as appropriate + doextra(ptrunk + '.range[0]', Math.log(r0) / Math.LN10); + doextra(ptrunk + '.range[1]', Math.log(r1) / Math.LN10); + } else { + doextra(ptrunk + '.range[0]', Math.pow(10, r0)); + doextra(ptrunk + '.range[1]', Math.pow(10, r1)); + } + } else if(toLog) { + // just make sure the range is positive and in the right + // order, it'll get recalculated later + ax.range = (ax.range[1] > ax.range[0]) ? [1, 2] : [2, 1]; + } + + // clear polar view initial stash for radial range so that + // value get recomputed in correct units + if(Array.isArray(fullLayout._subplots.polar) && + fullLayout._subplots.polar.length && + fullLayout[p.parts[0]] && + p.parts[1] === 'radialaxis' + ) { + delete fullLayout[p.parts[0]]._subplot.viewInitial['radialaxis.range']; + } + + // Annotations and images also need to convert to/from linearized coords + // Shapes do not need this :) + Registry.getComponentMethod('annotations', 'convertCoords')(gd, parentFull, vi, doextra); + Registry.getComponentMethod('images', 'convertCoords')(gd, parentFull, vi, doextra); + } else { + // any other type changes: the range from the previous type + // will not make sense, so autorange it. + doextra(ptrunk + '.autorange', true); + doextra(ptrunk + '.range', null); + } + nestedProperty(fullLayout, ptrunk + '._inputRange').set(null); + } else if(pleaf.match(AX_NAME_PATTERN)) { + var fullProp = nestedProperty(fullLayout, ai).get(); + var newType = (vi || {}).type; + + // This can potentially cause strange behavior if the autotype is not + // numeric (linear, because we don't auto-log) but the previous type + // was log. That's a very strange edge case though + if(!newType || newType === '-') newType = 'linear'; + Registry.getComponentMethod('annotations', 'convertCoords')(gd, fullProp, newType, doextra); + Registry.getComponentMethod('images', 'convertCoords')(gd, fullProp, newType, doextra); + } + + // alter gd.layout + + // collect array component edits for execution all together + // so we can ensure consistent behavior adding/removing items + // and order-independence for add/remove/edit all together in + // one relayout call + var containerArrayMatch = manageArrays.containerArrayMatch(ai); + if(containerArrayMatch) { + arrayStr = containerArrayMatch.array; + i = containerArrayMatch.index; + var propStr = containerArrayMatch.property; + var updateValObject = valObject || {editType: 'calc'}; + + if(i !== '' && propStr === '') { + // special handling of undoit if we're adding or removing an element + // ie 'annotations[2]' which can be {...} (add) or null, + // does not work when replacing the entire array + if(manageArrays.isAddVal(vi)) { + undoit[ai] = null; + } else if(manageArrays.isRemoveVal(vi)) { + undoit[ai] = (nestedProperty(layout, arrayStr).get() || [])[i]; + } else { + Lib.warn('unrecognized full object value', aobj); + } + } + editTypes.update(flags, updateValObject); + + // prepare the edits object we'll send to applyContainerArrayChanges + if(!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {}; + var objEdits = arrayEdits[arrayStr][i]; + if(!objEdits) objEdits = arrayEdits[arrayStr][i] = {}; + objEdits[propStr] = vi; + + delete aobj[ai]; + } else if(pleaf === 'reverse') { + // handle axis reversal explicitly, as there's no 'reverse' attribute + + if(parentIn.range) parentIn.range.reverse(); + else { + doextra(ptrunk + '.autorange', true); + parentIn.range = [1, 0]; + } + + if(parentFull.autorange) flags.calc = true; + else flags.plot = true; + } else { + if((fullLayout._has('scatter-like') && fullLayout._has('regl')) && + (ai === 'dragmode' && + (vi === 'lasso' || vi === 'select') && + !(vOld === 'lasso' || vOld === 'select')) + ) { + flags.plot = true; + } else if(fullLayout._has('gl2d')) { + flags.plot = true; + } else if(valObject) editTypes.update(flags, valObject); + else flags.calc = true; + + p.set(vi); + } + } + + // now we've collected component edits - execute them all together + for(arrayStr in arrayEdits) { + var finished = manageArrays.applyContainerArrayChanges(gd, + layoutNP(layout, arrayStr), arrayEdits[arrayStr], flags, layoutNP); + if(!finished) flags.plot = true; + } + + // figure out if we need to recalculate axis constraints + for(var axId in rangesAltered) { + ax = Axes.getFromId(gd, axId); + var group = ax && ax._constraintGroup; + if(group) { + // Always recalc if we're changing constrained ranges. + // Otherwise it's possible to violate the constraints by + // specifying arbitrary ranges for all axes in the group. + // this way some ranges may expand beyond what's specified, + // as they do at first draw, to satisfy the constraints. + flags.calc = true; + for(var groupAxId in group) { + if(!rangesAltered[groupAxId]) { + Axes.getFromId(gd, groupAxId)._constraintShrinkable = true; + } + } + } + } + + // If the autosize changed or height or width was explicitly specified, + // this triggers a redraw + // TODO: do we really need special aobj.height/width handling here? + // couldn't editType do this? + if(updateAutosize(gd) || aobj.height || aobj.width) flags.plot = true; + + if(flags.plot || flags.calc) { + flags.layoutReplot = true; + } + + // now all attribute mods are done, as are + // redo and undo so we can save them + + return { + flags: flags, + rangesAltered: rangesAltered, + undoit: undoit, + redoit: redoit, + eventData: eventData + }; +} + +/* + * updateAutosize: we made a change, does it change the autosize result? + * puts the new size into fullLayout + * returns true if either height or width changed + */ +function updateAutosize(gd) { + var fullLayout = gd._fullLayout; + var oldWidth = fullLayout.width; + var oldHeight = fullLayout.height; + + // calculate autosizing + if(gd.layout.autosize) Plots.plotAutoSize(gd, gd.layout, fullLayout); + + return (fullLayout.width !== oldWidth) || (fullLayout.height !== oldHeight); +} + +/** + * update: update trace and layout attributes of an existing plot + * + * @param {String | HTMLDivElement} gd + * the id or DOM element of the graph container div + * @param {Object} traceUpdate + * attribute object `{astr1: val1, astr2: val2 ...}` + * corresponding to updates in the plot's traces + * @param {Object} layoutUpdate + * attribute object `{astr1: val1, astr2: val2 ...}` + * corresponding to updates in the plot's layout + * @param {Number[] | Number} [traces] + * integer or array of integers for the traces to alter (all if omitted) + * + */ +function update(gd, traceUpdate, layoutUpdate, _traces) { + gd = Lib.getGraphDiv(gd); + helpers.clearPromiseQueue(gd); + + if(!Lib.isPlainObject(traceUpdate)) traceUpdate = {}; + if(!Lib.isPlainObject(layoutUpdate)) layoutUpdate = {}; + + if(Object.keys(traceUpdate).length) gd.changed = true; + if(Object.keys(layoutUpdate).length) gd.changed = true; + + var traces = helpers.coerceTraceIndices(gd, _traces); + + var restyleSpecs = _restyle(gd, Lib.extendFlat({}, traceUpdate), traces); + var restyleFlags = restyleSpecs.flags; + + var relayoutSpecs = _relayout(gd, Lib.extendFlat({}, layoutUpdate)); + var relayoutFlags = relayoutSpecs.flags; + + // clear calcdata and/or axis types if required + if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined; + if(restyleFlags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, layoutUpdate); + + // fill in redraw sequence + var seq = []; + + if(relayoutFlags.layoutReplot) { + // N.B. works fine when both + // relayoutFlags.layoutReplot and restyleFlags.fullReplot are true + seq.push(subroutines.layoutReplot); + } else if(restyleFlags.fullReplot) { + seq.push(exports._doPlot); + } else { + seq.push(Plots.previousPromises); + axRangeSupplyDefaultsByPass(gd, relayoutFlags, relayoutSpecs) || Plots.supplyDefaults(gd); + + if(restyleFlags.style) seq.push(subroutines.doTraceStyle); + if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars); + if(relayoutFlags.legend) seq.push(subroutines.doLegend); + if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles); + if(relayoutFlags.axrange) addAxRangeSequence(seq, relayoutSpecs.rangesAltered); + if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout); + if(relayoutFlags.modebar) seq.push(subroutines.doModeBar); + if(relayoutFlags.camera) seq.push(subroutines.doCamera); + + seq.push(emitAfterPlot); + } + + seq.push(Plots.rehover, Plots.redrag); + + Queue.add(gd, + update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces], + update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces] + ); + + var plotDone = Lib.syncOrAsync(seq, gd); + if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd); + + return plotDone.then(function() { + gd.emit('plotly_update', { + data: restyleSpecs.eventData, + layout: relayoutSpecs.eventData + }); + + return gd; + }); +} + +/* + * internal-use-only restyle/relayout/update variants that record the initial + * values in (fullLayout|fullTrace)._preGUI so changes can be persisted across + * Plotly.react data updates, dependent on uirevision attributes + */ +function guiEdit(func) { + return function wrappedEdit(gd) { + gd._fullLayout._guiEditing = true; + var p = func.apply(null, arguments); + gd._fullLayout._guiEditing = false; + return p; + }; +} + +// For connecting edited layout attributes to uirevision attrs +// If no `attr` we use `match[1] + '.uirevision'` +// Ordered by most common edits first, to minimize our search time +var layoutUIControlPatterns = [ + {pattern: /^hiddenlabels/, attr: 'legend.uirevision'}, + {pattern: /^((x|y)axis\d*)\.((auto)?range|title\.text)/}, + + // showspikes and modes include those nested inside scenes + {pattern: /axis\d*\.showspikes$/, attr: 'modebar.uirevision'}, + {pattern: /(hover|drag)mode$/, attr: 'modebar.uirevision'}, + + {pattern: /^(scene\d*)\.camera/}, + {pattern: /^(geo\d*)\.(projection|center|fitbounds)/}, + {pattern: /^(ternary\d*\.[abc]axis)\.(min|title\.text)$/}, + {pattern: /^(polar\d*\.radialaxis)\.((auto)?range|angle|title\.text)/}, + {pattern: /^(polar\d*\.angularaxis)\.rotation/}, + {pattern: /^(mapbox\d*)\.(center|zoom|bearing|pitch)/}, + + {pattern: /^legend\.(x|y)$/, attr: 'editrevision'}, + {pattern: /^(shapes|annotations)/, attr: 'editrevision'}, + {pattern: /^title\.text$/, attr: 'editrevision'} +]; + +// same for trace attributes: if `attr` is given it's in layout, +// or with no `attr` we use `trace.uirevision` +var traceUIControlPatterns = [ + {pattern: /^selectedpoints$/, attr: 'selectionrevision'}, + // "visible" includes trace.transforms[i].styles[j].value.visible + {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'}, + {pattern: /^dimensions\[\d+\]\.constraintrange/}, + {pattern: /^node\.(x|y|groups)/}, // for Sankey nodes + {pattern: /^level$/}, // for Sunburst, Treemap and Icicle traces + + // below this you must be in editable: true mode + // TODO: I still put name and title with `trace.uirevision` + // reasonable or should these be `editrevision`? + // Also applies to axis titles up in the layout section + + // "name" also includes transform.styles + {pattern: /(^|value\.)name$/}, + // including nested colorbar attributes (ie marker.colorbar) + {pattern: /colorbar\.title\.text$/}, + {pattern: /colorbar\.(x|y)$/, attr: 'editrevision'} +]; + +function findUIPattern(key, patternSpecs) { + for(var i = 0; i < patternSpecs.length; i++) { + var spec = patternSpecs[i]; + var match = key.match(spec.pattern); + if(match) { + return {head: match[1], attr: spec.attr}; + } + } +} + +// We're finding the new uirevision before supplyDefaults, so do the +// inheritance manually. Note that only `undefined` inherits - other +// falsy values are returned. +function getNewRev(revAttr, container) { + var newRev = nestedProperty(container, revAttr).get(); + if(newRev !== undefined) return newRev; + + var parts = revAttr.split('.'); + parts.pop(); + while(parts.length > 1) { + parts.pop(); + newRev = nestedProperty(container, parts.join('.') + '.uirevision').get(); + if(newRev !== undefined) return newRev; + } + + return container.uirevision; +} + +function getFullTraceIndexFromUid(uid, fullData) { + for(var i = 0; i < fullData.length; i++) { + if(fullData[i]._fullInput.uid === uid) return i; + } + return -1; +} + +function getTraceIndexFromUid(uid, data, tracei) { + for(var i = 0; i < data.length; i++) { + if(data[i].uid === uid) return i; + } + // fall back on trace order, but only if user didn't provide a uid for that trace + return (!data[tracei] || data[tracei].uid) ? -1 : tracei; +} + +function valsMatch(v1, v2) { + var v1IsObj = Lib.isPlainObject(v1); + var v1IsArray = Array.isArray(v1); + if(v1IsObj || v1IsArray) { + return ( + (v1IsObj && Lib.isPlainObject(v2)) || + (v1IsArray && Array.isArray(v2)) + ) && JSON.stringify(v1) === JSON.stringify(v2); + } + return v1 === v2; +} + +function applyUIRevisions(data, layout, oldFullData, oldFullLayout) { + var layoutPreGUI = oldFullLayout._preGUI; + var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal; + var bothInheritAutorange = []; + var newRangeAccepted = {}; + for(key in layoutPreGUI) { + match = findUIPattern(key, layoutUIControlPatterns); + if(match) { + revAttr = match.attr || (match.head + '.uirevision'); + oldRev = nestedProperty(oldFullLayout, revAttr).get(); + newRev = oldRev && getNewRev(revAttr, layout); + if(newRev && (newRev === oldRev)) { + preGUIVal = layoutPreGUI[key]; + if(preGUIVal === null) preGUIVal = undefined; + newNP = nestedProperty(layout, key); + newVal = newNP.get(); + if(valsMatch(newVal, preGUIVal)) { + if(newVal === undefined && key.substr(key.length - 9) === 'autorange') { + bothInheritAutorange.push(key.substr(0, key.length - 10)); + } + newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get())); + continue; + } + } + } else { + Lib.warn('unrecognized GUI edit: ' + key); + } + // if we got this far, the new value was accepted as the new starting + // point (either because it changed or revision changed) + // so remove it from _preGUI for next time. + delete layoutPreGUI[key]; + + if(key.substr(key.length - 8, 6) === 'range[') { + newRangeAccepted[key.substr(0, key.length - 9)] = 1; + } + } + + // Special logic for `autorange`, since it interacts with `range`: + // If the new figure's matching `range` was kept, and `autorange` + // wasn't supplied explicitly in either the original or the new figure, + // we shouldn't alter that - but we may just have done that, so fix it. + for(var i = 0; i < bothInheritAutorange.length; i++) { + var axAttr = bothInheritAutorange[i]; + if(newRangeAccepted[axAttr]) { + var newAx = nestedProperty(layout, axAttr).get(); + if(newAx) delete newAx.autorange; + } + } + + // Now traces - try to match them up by uid (in case we added/deleted in + // the middle), then fall back on index. + var allTracePreGUI = oldFullLayout._tracePreGUI; + for(var uid in allTracePreGUI) { + var tracePreGUI = allTracePreGUI[uid]; + var newTrace = null; + var fullInput; + for(key in tracePreGUI) { + // wait until we know we have preGUI values to look for traces + // but if we don't find both, stop looking at this uid + if(!newTrace) { + var fulli = getFullTraceIndexFromUid(uid, oldFullData); + if(fulli < 0) { + // Somehow we didn't even have this trace in oldFullData... + // I guess this could happen with `deleteTraces` or something + delete allTracePreGUI[uid]; + break; + } + var fullTrace = oldFullData[fulli]; + fullInput = fullTrace._fullInput; + + var newTracei = getTraceIndexFromUid(uid, data, fullInput.index); + if(newTracei < 0) { + // No match in new data + delete allTracePreGUI[uid]; + break; + } + newTrace = data[newTracei]; + } + + match = findUIPattern(key, traceUIControlPatterns); + if(match) { + if(match.attr) { + oldRev = nestedProperty(oldFullLayout, match.attr).get(); + newRev = oldRev && getNewRev(match.attr, layout); + } else { + oldRev = fullInput.uirevision; + // inheritance for trace.uirevision is simple, just layout.uirevision + newRev = newTrace.uirevision; + if(newRev === undefined) newRev = layout.uirevision; + } + + if(newRev && newRev === oldRev) { + preGUIVal = tracePreGUI[key]; + if(preGUIVal === null) preGUIVal = undefined; + newNP = nestedProperty(newTrace, key); + newVal = newNP.get(); + if(valsMatch(newVal, preGUIVal)) { + newNP.set(undefinedToNull(nestedProperty(fullInput, key).get())); + continue; + } + } + } else { + Lib.warn('unrecognized GUI edit: ' + key + ' in trace uid ' + uid); + } + delete tracePreGUI[key]; + } + } +} + +/** + * Plotly.react: + * A plot/update method that takes the full plot state (same API as plot/newPlot) + * and diffs to determine the minimal update pathway + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * @param {array of objects} data + * array of traces, containing the data and display information for each trace + * @param {object} layout + * object describing the overall display of the plot, + * all the stuff that doesn't pertain to any individual trace + * @param {object} config + * configuration options (see ./plot_config.js for more info) + * + * OR + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * @param {object} figure + * object containing `data`, `layout`, `config`, and `frames` members + * + */ +function react(gd, data, layout, config) { + var frames, plotDone; + + function addFrames() { return exports.addFrames(gd, frames); } + + gd = Lib.getGraphDiv(gd); + helpers.clearPromiseQueue(gd); + + var oldFullData = gd._fullData; + var oldFullLayout = gd._fullLayout; + + // you can use this as the initial draw as well as to update + if(!Lib.isPlotDiv(gd) || !oldFullData || !oldFullLayout) { + plotDone = exports.newPlot(gd, data, layout, config); + } else { + if(Lib.isPlainObject(data)) { + var obj = data; + data = obj.data; + layout = obj.layout; + config = obj.config; + frames = obj.frames; + } + + var configChanged = false; + // assume that if there's a config at all, we're reacting to it too, + // and completely replace the previous config + if(config) { + var oldConfig = Lib.extendDeep({}, gd._context); + gd._context = undefined; + setPlotContext(gd, config); + configChanged = diffConfig(oldConfig, gd._context); + } + + gd.data = data || []; + helpers.cleanData(gd.data); + gd.layout = layout || {}; + helpers.cleanLayout(gd.layout); + + applyUIRevisions(gd.data, gd.layout, oldFullData, oldFullLayout); + + // "true" skips updating calcdata and remapping arrays from calcTransforms, + // which supplyDefaults usually does at the end, but we may need to NOT do + // if the diff (which we haven't determined yet) says we'll recalc + Plots.supplyDefaults(gd, {skipUpdateCalc: true}); + + var newFullData = gd._fullData; + var newFullLayout = gd._fullLayout; + var immutable = newFullLayout.datarevision === undefined; + var transition = newFullLayout.transition; + + var relayoutFlags = diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition); + var newDataRevision = relayoutFlags.newDataRevision; + var restyleFlags = diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision); + + // TODO: how to translate this part of relayout to Plotly.react? + // // Setting width or height to null must reset the graph's width / height + // // back to its initial value as computed during the first pass in Plots.plotAutoSize. + // // + // // To do so, we must manually set them back here using the _initialAutoSize cache. + // if(['width', 'height'].indexOf(ai) !== -1 && vi === null) { + // fullLayout[ai] = gd._initialAutoSize[ai]; + // } + + if(updateAutosize(gd)) relayoutFlags.layoutReplot = true; + + // clear calcdata and empty categories if required + if(restyleFlags.calc || relayoutFlags.calc) { + gd.calcdata = undefined; + var allNames = Object.getOwnPropertyNames(newFullLayout); + for(var q = 0; q < allNames.length; q++) { + var name = allNames[q]; + var start = name.substring(0, 5); + if(start === 'xaxis' || start === 'yaxis') { + var emptyCategories = newFullLayout[name]._emptyCategories; + if(emptyCategories) emptyCategories(); + } + } + // otherwise do the calcdata updates and calcTransform array remaps that we skipped earlier + } else { + Plots.supplyDefaultsUpdateCalc(gd.calcdata, newFullData); + } + + // Note: what restyle/relayout use impliedEdits and clearAxisTypes for + // must be handled by the user when using Plotly.react. + + // fill in redraw sequence + var seq = []; + + if(frames) { + gd._transitionData = {}; + Plots.createTransitionData(gd); + seq.push(addFrames); + } + + // Transition pathway, + // only used when 'transition' is set by user and + // when at least one animatable attribute has changed, + // N.B. config changed aren't animatable + if(newFullLayout.transition && !configChanged && (restyleFlags.anim || relayoutFlags.anim)) { + if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout); + + Plots.doCalcdata(gd); + subroutines.doAutoRangeAndConstraints(gd); + + seq.push(function() { + return Plots.transitionFromReact(gd, restyleFlags, relayoutFlags, oldFullLayout); + }); + } else if(restyleFlags.fullReplot || relayoutFlags.layoutReplot || configChanged) { + gd._fullLayout._skipDefaults = true; + seq.push(exports._doPlot); + } else { + for(var componentType in relayoutFlags.arrays) { + var indices = relayoutFlags.arrays[componentType]; + if(indices.length) { + var drawOne = Registry.getComponentMethod(componentType, 'drawOne'); + if(drawOne !== Lib.noop) { + for(var i = 0; i < indices.length; i++) { + drawOne(gd, indices[i]); + } + } else { + var draw = Registry.getComponentMethod(componentType, 'draw'); + if(draw === Lib.noop) { + throw new Error('cannot draw components: ' + componentType); + } + draw(gd); + } + } + } + + seq.push(Plots.previousPromises); + if(restyleFlags.style) seq.push(subroutines.doTraceStyle); + if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars); + if(relayoutFlags.legend) seq.push(subroutines.doLegend); + if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles); + if(relayoutFlags.axrange) addAxRangeSequence(seq); + if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout); + if(relayoutFlags.modebar) seq.push(subroutines.doModeBar); + if(relayoutFlags.camera) seq.push(subroutines.doCamera); + seq.push(emitAfterPlot); + } + + seq.push(Plots.rehover, Plots.redrag); + + plotDone = Lib.syncOrAsync(seq, gd); + if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd); + } + + return plotDone.then(function() { + gd.emit('plotly_react', { + data: data, + layout: layout + }); + + return gd; + }); +} + +function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision) { + var sameTraceLength = oldFullData.length === newFullData.length; + + if(!transition && !sameTraceLength) { + return { + fullReplot: true, + calc: true + }; + } + + var flags = editTypes.traceFlags(); + flags.arrays = {}; + flags.nChanges = 0; + flags.nChangesAnim = 0; + + var i, trace; + + function getTraceValObject(parts) { + var out = PlotSchema.getTraceValObject(trace, parts); + if(!trace._module.animatable && out.anim) { + out.anim = false; + } + return out; + } + + var diffOpts = { + getValObject: getTraceValObject, + flags: flags, + immutable: immutable, + transition: transition, + newDataRevision: newDataRevision, + gd: gd + }; + + var seenUIDs = {}; + + for(i = 0; i < oldFullData.length; i++) { + if(newFullData[i]) { + trace = newFullData[i]._fullInput; + if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i]; + if(seenUIDs[trace.uid]) continue; + seenUIDs[trace.uid] = 1; + + getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts); + } + } + + if(flags.calc || flags.plot) { + flags.fullReplot = true; + } + + if(transition && flags.nChanges && flags.nChangesAnim) { + flags.anim = (flags.nChanges === flags.nChangesAnim) && sameTraceLength ? 'all' : 'some'; + } + + return flags; +} + +function diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition) { + var flags = editTypes.layoutFlags(); + flags.arrays = {}; + flags.rangesAltered = {}; + flags.nChanges = 0; + flags.nChangesAnim = 0; + + function getLayoutValObject(parts) { + return PlotSchema.getLayoutValObject(newFullLayout, parts); + } + + var diffOpts = { + getValObject: getLayoutValObject, + flags: flags, + immutable: immutable, + transition: transition, + gd: gd + }; + + getDiffFlags(oldFullLayout, newFullLayout, [], diffOpts); + + if(flags.plot || flags.calc) { + flags.layoutReplot = true; + } + + if(transition && flags.nChanges && flags.nChangesAnim) { + flags.anim = flags.nChanges === flags.nChangesAnim ? 'all' : 'some'; + } + + return flags; +} + +function getDiffFlags(oldContainer, newContainer, outerparts, opts) { + var valObject, key, astr; + + var getValObject = opts.getValObject; + var flags = opts.flags; + var immutable = opts.immutable; + var inArray = opts.inArray; + var arrayIndex = opts.arrayIndex; + + function changed() { + var editType = valObject.editType; + if(inArray && editType.indexOf('arraydraw') !== -1) { + Lib.pushUnique(flags.arrays[inArray], arrayIndex); + return; + } + editTypes.update(flags, valObject); + + if(editType !== 'none') { + flags.nChanges++; + } + + // track animatable changes + if(opts.transition && valObject.anim) { + flags.nChangesAnim++; + } + + // track cartesian axes with altered ranges + if(AX_RANGE_RE.test(astr) || AX_AUTORANGE_RE.test(astr)) { + flags.rangesAltered[outerparts[0]] = 1; + } + + // clear _inputDomain on cartesian axes with altered domains + if(AX_DOMAIN_RE.test(astr)) { + nestedProperty(newContainer, '_inputDomain').set(null); + } + + // track datarevision changes + if(key === 'datarevision') { + flags.newDataRevision = 1; + } + } + + function valObjectCanBeDataArray(valObject) { + return valObject.valType === 'data_array' || valObject.arrayOk; + } + + for(key in oldContainer) { + // short-circuit based on previous calls or previous keys that already maximized the pathway + if(flags.calc && !opts.transition) return; + + var oldVal = oldContainer[key]; + var newVal = newContainer[key]; + var parts = outerparts.concat(key); + astr = parts.join('.'); + + if(key.charAt(0) === '_' || typeof oldVal === 'function' || oldVal === newVal) continue; + + // FIXME: ax.tick0 and dtick get filled in during plotting (except for geo subplots), + // and unlike other auto values they don't make it back into the input, + // so newContainer won't have them. + if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') { + var tickMode = newContainer.tickmode; + if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue; + } + // FIXME: Similarly for axis ranges for 3D + // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them. + if(key === 'range' && newContainer.autorange) continue; + if((key === 'zmin' || key === 'zmax') && newContainer.type === 'contourcarpet') continue; + + valObject = getValObject(parts); + + // in case type changed, we may not even *have* a valObject. + if(!valObject) continue; + + if(valObject._compareAsJSON && JSON.stringify(oldVal) === JSON.stringify(newVal)) continue; + + var valType = valObject.valType; + var i; + + var canBeDataArray = valObjectCanBeDataArray(valObject); + var wasArray = Array.isArray(oldVal); + var nowArray = Array.isArray(newVal); + + // hack for traces that modify the data in supplyDefaults, like + // converting 1D to 2D arrays, which will always create new objects + if(wasArray && nowArray) { + var inputKey = '_input_' + key; + var oldValIn = oldContainer[inputKey]; + var newValIn = newContainer[inputKey]; + if(Array.isArray(oldValIn) && oldValIn === newValIn) continue; + } + + if(newVal === undefined) { + if(canBeDataArray && wasArray) flags.calc = true; + else changed(); + } else if(valObject._isLinkedToArray) { + var arrayEditIndices = []; + var extraIndices = false; + if(!inArray) flags.arrays[key] = arrayEditIndices; + + var minLen = Math.min(oldVal.length, newVal.length); + var maxLen = Math.max(oldVal.length, newVal.length); + if(minLen !== maxLen) { + if(valObject.editType === 'arraydraw') { + extraIndices = true; + } else { + changed(); + continue; + } + } + + for(i = 0; i < minLen; i++) { + getDiffFlags(oldVal[i], newVal[i], parts.concat(i), + // add array indices, but not if we're already in an array + Lib.extendFlat({inArray: key, arrayIndex: i}, opts)); + } + + // put this at the end so that we know our collected array indices are sorted + // but the check for length changes happens up front so we can short-circuit + // diffing if appropriate + if(extraIndices) { + for(i = minLen; i < maxLen; i++) { + arrayEditIndices.push(i); + } + } + } else if(!valType && Lib.isPlainObject(oldVal)) { + getDiffFlags(oldVal, newVal, parts, opts); + } else if(canBeDataArray) { + if(wasArray && nowArray) { + // don't try to diff two data arrays. If immutable we know the data changed, + // if not, assume it didn't and let `layout.datarevision` tell us if it did + if(immutable) { + flags.calc = true; + } + + // look for animatable attributes when the data changed + if(immutable || opts.newDataRevision) { + changed(); + } + } else if(wasArray !== nowArray) { + flags.calc = true; + } else changed(); + } else if(wasArray && nowArray) { + // info array, colorscale, 'any' - these are short, just stringify. + // I don't *think* that covers up any real differences post-validation, does it? + // otherwise we need to dive in 1 (info_array) or 2 (colorscale) levels and compare + // all elements. + if(oldVal.length !== newVal.length || String(oldVal) !== String(newVal)) { + changed(); + } + } else { + changed(); + } + } + + for(key in newContainer) { + if(!(key in oldContainer || key.charAt(0) === '_' || typeof newContainer[key] === 'function')) { + valObject = getValObject(outerparts.concat(key)); + + if(valObjectCanBeDataArray(valObject) && Array.isArray(newContainer[key])) { + flags.calc = true; + return; + } else changed(); + } + } +} + +/* + * simple diff for config - for now, just treat all changes as equivalent + */ +function diffConfig(oldConfig, newConfig) { + var key; + + for(key in oldConfig) { + if(key.charAt(0) === '_') continue; + var oldVal = oldConfig[key]; + var newVal = newConfig[key]; + if(oldVal !== newVal) { + if(Lib.isPlainObject(oldVal) && Lib.isPlainObject(newVal)) { + if(diffConfig(oldVal, newVal)) { + return true; + } + } else if(Array.isArray(oldVal) && Array.isArray(newVal)) { + if(oldVal.length !== newVal.length) { + return true; + } + for(var i = 0; i < oldVal.length; i++) { + if(oldVal[i] !== newVal[i]) { + if(Lib.isPlainObject(oldVal[i]) && Lib.isPlainObject(newVal[i])) { + if(diffConfig(oldVal[i], newVal[i])) { + return true; + } + } else { + return true; + } + } + } + } else { + return true; + } + } + } +} + +/** + * Animate to a frame, sequence of frame, frame group, or frame definition + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * + * @param {string or object or array of strings or array of objects} frameOrGroupNameOrFrameList + * a single frame, array of frames, or group to which to animate. The intent is + * inferred by the type of the input. Valid inputs are: + * + * - string, e.g. 'groupname': animate all frames of a given `group` in the order + * in which they are defined via `Plotly.addFrames`. + * + * - array of strings, e.g. ['frame1', frame2']: a list of frames by name to which + * to animate in sequence + * + * - object: {data: ...}: a frame definition to which to animate. The frame is not + * and does not need to be added via `Plotly.addFrames`. It may contain any of + * the properties of a frame, including `data`, `layout`, and `traces`. The + * frame is used as provided and does not use the `baseframe` property. + * + * - array of objects, e.g. [{data: ...}, {data: ...}]: a list of frame objects, + * each following the same rules as a single `object`. + * + * @param {object} animationOpts + * configuration for the animation + */ +function animate(gd, frameOrGroupNameOrFrameList, animationOpts) { + gd = Lib.getGraphDiv(gd); + + if(!Lib.isPlotDiv(gd)) { + throw new Error( + 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' + + 'to create a plot before animating it. For more details, see ' + + 'https://plotly.com/javascript/animations/' + ); + } + + var trans = gd._transitionData; + + // This is the queue of frames that will be animated as soon as possible. They + // are popped immediately upon the *start* of a transition: + if(!trans._frameQueue) { + trans._frameQueue = []; + } + + animationOpts = Plots.supplyAnimationDefaults(animationOpts); + var transitionOpts = animationOpts.transition; + var frameOpts = animationOpts.frame; + + // Since frames are popped immediately, an empty queue only means all frames have + // *started* to transition, not that the animation is complete. To solve that, + // track a separate counter that increments at the same time as frames are added + // to the queue, but decrements only when the transition is complete. + if(trans._frameWaitingCnt === undefined) { + trans._frameWaitingCnt = 0; + } + + function getTransitionOpts(i) { + if(Array.isArray(transitionOpts)) { + if(i >= transitionOpts.length) { + return transitionOpts[0]; + } else { + return transitionOpts[i]; + } + } else { + return transitionOpts; + } + } + + function getFrameOpts(i) { + if(Array.isArray(frameOpts)) { + if(i >= frameOpts.length) { + return frameOpts[0]; + } else { + return frameOpts[i]; + } + } else { + return frameOpts; + } + } + + // Execute a callback after the wrapper function has been called n times. + // This is used to defer the resolution until a transition has resolved *and* + // the frame has completed. If it's not done this way, then we get a race + // condition in which the animation might resolve before a transition is complete + // or vice versa. + function callbackOnNthTime(cb, n) { + var cnt = 0; + return function() { + if(cb && ++cnt === n) { + return cb(); + } + }; + } + + return new Promise(function(resolve, reject) { + function discardExistingFrames() { + if(trans._frameQueue.length === 0) { + return; + } + + while(trans._frameQueue.length) { + var next = trans._frameQueue.pop(); + if(next.onInterrupt) { + next.onInterrupt(); + } + } + + gd.emit('plotly_animationinterrupted', []); + } + + function queueFrames(frameList) { + if(frameList.length === 0) return; + + for(var i = 0; i < frameList.length; i++) { + var computedFrame; + + if(frameList[i].type === 'byname') { + // If it's a named frame, compute it: + computedFrame = Plots.computeFrame(gd, frameList[i].name); + } else { + // Otherwise we must have been given a simple object, so treat + // the input itself as the computed frame. + computedFrame = frameList[i].data; + } + + var frameOpts = getFrameOpts(i); + var transitionOpts = getTransitionOpts(i); + + // It doesn't make much sense for the transition duration to be greater than + // the frame duration, so limit it: + transitionOpts.duration = Math.min(transitionOpts.duration, frameOpts.duration); + + var nextFrame = { + frame: computedFrame, + name: frameList[i].name, + frameOpts: frameOpts, + transitionOpts: transitionOpts, + }; + if(i === frameList.length - 1) { + // The last frame in this .animate call stores the promise resolve + // and reject callbacks. This is how we ensure that the animation + // loop (which may exist as a result of a *different* .animate call) + // still resolves or rejecdts this .animate call's promise. once it's + // complete. + nextFrame.onComplete = callbackOnNthTime(resolve, 2); + nextFrame.onInterrupt = reject; + } + + trans._frameQueue.push(nextFrame); + } + + // Set it as never having transitioned to a frame. This will cause the animation + // loop to immediately transition to the next frame (which, for immediate mode, + // is the first frame in the list since all others would have been discarded + // below) + if(animationOpts.mode === 'immediate') { + trans._lastFrameAt = -Infinity; + } + + // Only it's not already running, start a RAF loop. This could be avoided in the + // case that there's only one frame, but it significantly complicated the logic + // and only sped things up by about 5% or so for a lorenz attractor simulation. + // It would be a fine thing to implement, but the benefit of that optimization + // doesn't seem worth the extra complexity. + if(!trans._animationRaf) { + beginAnimationLoop(); + } + } + + function stopAnimationLoop() { + gd.emit('plotly_animated'); + + // Be sure to unset also since it's how we know whether a loop is already running: + window.cancelAnimationFrame(trans._animationRaf); + trans._animationRaf = null; + } + + function nextFrame() { + if(trans._currentFrame && trans._currentFrame.onComplete) { + // Execute the callback and unset it to ensure it doesn't + // accidentally get called twice + trans._currentFrame.onComplete(); + } + + var newFrame = trans._currentFrame = trans._frameQueue.shift(); + + if(newFrame) { + // Since it's sometimes necessary to do deep digging into frame data, + // we'll consider it not 100% impossible for nulls or numbers to sneak through, + // so check when casting the name, just to be absolutely certain: + var stringName = newFrame.name ? newFrame.name.toString() : null; + gd._fullLayout._currentFrame = stringName; + + trans._lastFrameAt = Date.now(); + trans._timeToNext = newFrame.frameOpts.duration; + + // This is simply called and it's left to .transition to decide how to manage + // interrupting current transitions. That means we don't need to worry about + // how it resolves or what happens after this: + Plots.transition(gd, + newFrame.frame.data, + newFrame.frame.layout, + helpers.coerceTraceIndices(gd, newFrame.frame.traces), + newFrame.frameOpts, + newFrame.transitionOpts + ).then(function() { + if(newFrame.onComplete) { + newFrame.onComplete(); + } + }); + + gd.emit('plotly_animatingframe', { + name: stringName, + frame: newFrame.frame, + animation: { + frame: newFrame.frameOpts, + transition: newFrame.transitionOpts, + } + }); + } else { + // If there are no more frames, then stop the RAF loop: + stopAnimationLoop(); + } + } + + function beginAnimationLoop() { + gd.emit('plotly_animating'); + + // If no timer is running, then set last frame = long ago so that the next + // frame is immediately transitioned: + trans._lastFrameAt = -Infinity; + trans._timeToNext = 0; + trans._runningTransitions = 0; + trans._currentFrame = null; + + var doFrame = function() { + // This *must* be requested before nextFrame since nextFrame may decide + // to cancel it if there's nothing more to animated: + trans._animationRaf = window.requestAnimationFrame(doFrame); + + // Check if we're ready for a new frame: + if(Date.now() - trans._lastFrameAt > trans._timeToNext) { + nextFrame(); + } + }; + + doFrame(); + } + + // This is an animate-local counter that helps match up option input list + // items with the particular frame. + var configCounter = 0; + function setTransitionConfig(frame) { + if(Array.isArray(transitionOpts)) { + if(configCounter >= transitionOpts.length) { + frame.transitionOpts = transitionOpts[configCounter]; + } else { + frame.transitionOpts = transitionOpts[0]; + } + } else { + frame.transitionOpts = transitionOpts; + } + configCounter++; + return frame; + } + + // Disambiguate what's sort of frames have been received + var i, frame; + var frameList = []; + var allFrames = frameOrGroupNameOrFrameList === undefined || frameOrGroupNameOrFrameList === null; + var isFrameArray = Array.isArray(frameOrGroupNameOrFrameList); + var isSingleFrame = !allFrames && !isFrameArray && Lib.isPlainObject(frameOrGroupNameOrFrameList); + + if(isSingleFrame) { + // In this case, a simple object has been passed to animate. + frameList.push({ + type: 'object', + data: setTransitionConfig(Lib.extendFlat({}, frameOrGroupNameOrFrameList)) + }); + } else if(allFrames || ['string', 'number'].indexOf(typeof frameOrGroupNameOrFrameList) !== -1) { + // In this case, null or undefined has been passed so that we want to + // animate *all* currently defined frames + for(i = 0; i < trans._frames.length; i++) { + frame = trans._frames[i]; + + if(!frame) continue; + + if(allFrames || String(frame.group) === String(frameOrGroupNameOrFrameList)) { + frameList.push({ + type: 'byname', + name: String(frame.name), + data: setTransitionConfig({name: frame.name}) + }); + } + } + } else if(isFrameArray) { + for(i = 0; i < frameOrGroupNameOrFrameList.length; i++) { + var frameOrName = frameOrGroupNameOrFrameList[i]; + if(['number', 'string'].indexOf(typeof frameOrName) !== -1) { + frameOrName = String(frameOrName); + // In this case, there's an array and this frame is a string name: + frameList.push({ + type: 'byname', + name: frameOrName, + data: setTransitionConfig({name: frameOrName}) + }); + } else if(Lib.isPlainObject(frameOrName)) { + frameList.push({ + type: 'object', + data: setTransitionConfig(Lib.extendFlat({}, frameOrName)) + }); + } + } + } + + // Verify that all of these frames actually exist; return and reject if not: + for(i = 0; i < frameList.length; i++) { + frame = frameList[i]; + if(frame.type === 'byname' && !trans._frameHash[frame.data.name]) { + Lib.warn('animate failure: frame not found: "' + frame.data.name + '"'); + reject(); + return; + } + } + + // If the mode is either next or immediate, then all currently queued frames must + // be dumped and the corresponding .animate promises rejected. + if(['next', 'immediate'].indexOf(animationOpts.mode) !== -1) { + discardExistingFrames(); + } + + if(animationOpts.direction === 'reverse') { + frameList.reverse(); + } + + var currentFrame = gd._fullLayout._currentFrame; + if(currentFrame && animationOpts.fromcurrent) { + var idx = -1; + for(i = 0; i < frameList.length; i++) { + frame = frameList[i]; + if(frame.type === 'byname' && frame.name === currentFrame) { + idx = i; + break; + } + } + + if(idx > 0 && idx < frameList.length - 1) { + var filteredFrameList = []; + for(i = 0; i < frameList.length; i++) { + frame = frameList[i]; + if(frameList[i].type !== 'byname' || i > idx) { + filteredFrameList.push(frame); + } + } + frameList = filteredFrameList; + } + } + + if(frameList.length > 0) { + queueFrames(frameList); + } else { + // This is the case where there were simply no frames. It's a little strange + // since there's not much to do: + gd.emit('plotly_animated'); + resolve(); + } + }); +} + +/** + * Register new frames + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * + * @param {array of objects} frameList + * list of frame definitions, in which each object includes any of: + * - name: {string} name of frame to add + * - data: {array of objects} trace data + * - layout {object} layout definition + * - traces {array} trace indices + * - baseframe {string} name of frame from which this frame gets defaults + * + * @param {array of integers} indices + * an array of integer indices matching the respective frames in `frameList`. If not + * provided, an index will be provided in serial order. If already used, the frame + * will be overwritten. + */ +function addFrames(gd, frameList, indices) { + gd = Lib.getGraphDiv(gd); + + if(frameList === null || frameList === undefined) { + return Promise.resolve(); + } + + if(!Lib.isPlotDiv(gd)) { + throw new Error( + 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' + + 'to create a plot before adding frames. For more details, see ' + + 'https://plotly.com/javascript/animations/' + ); + } + + var i, frame, j, idx; + var _frames = gd._transitionData._frames; + var _frameHash = gd._transitionData._frameHash; + + + if(!Array.isArray(frameList)) { + throw new Error('addFrames failure: frameList must be an Array of frame definitions' + frameList); + } + + // Create a sorted list of insertions since we run into lots of problems if these + // aren't in ascending order of index: + // + // Strictly for sorting. Make sure this is guaranteed to never collide with any + // already-exisisting indices: + var bigIndex = _frames.length + frameList.length * 2; + + var insertions = []; + var _frameHashLocal = {}; + for(i = frameList.length - 1; i >= 0; i--) { + if(!Lib.isPlainObject(frameList[i])) continue; + + // The entire logic for checking for this type of name collision can be removed once we migrate to ES6 and + // use a Map instead of an Object instance, as Map keys aren't converted to strings. + var lookupName = frameList[i].name; + var name = (_frameHash[lookupName] || _frameHashLocal[lookupName] || {}).name; + var newName = frameList[i].name; + var collisionPresent = _frameHash[name] || _frameHashLocal[name]; + + if(name && newName && typeof newName === 'number' && collisionPresent && numericNameWarningCount < numericNameWarningCountLimit) { + numericNameWarningCount++; + + Lib.warn('addFrames: overwriting frame "' + (_frameHash[name] || _frameHashLocal[name]).name + + '" with a frame whose name of type "number" also equates to "' + + name + '". This is valid but may potentially lead to unexpected ' + + 'behavior since all plotly.js frame names are stored internally ' + + 'as strings.'); + + if(numericNameWarningCount === numericNameWarningCountLimit) { + Lib.warn('addFrames: This API call has yielded too many of these warnings. ' + + 'For the rest of this call, further warnings about numeric frame ' + + 'names will be suppressed.'); + } + } + + _frameHashLocal[lookupName] = {name: lookupName}; + + insertions.push({ + frame: Plots.supplyFrameDefaults(frameList[i]), + index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i + }); + } + + // Sort this, taking note that undefined insertions end up at the end: + insertions.sort(function(a, b) { + if(a.index > b.index) return -1; + if(a.index < b.index) return 1; + return 0; + }); + + var ops = []; + var revops = []; + var frameCount = _frames.length; + + for(i = insertions.length - 1; i >= 0; i--) { + frame = insertions[i].frame; + + if(typeof frame.name === 'number') { + Lib.warn('Warning: addFrames accepts frames with numeric names, but the numbers are' + + 'implicitly cast to strings'); + } + + if(!frame.name) { + // Repeatedly assign a default name, incrementing the counter each time until + // we get a name that's not in the hashed lookup table: + while(_frameHash[(frame.name = 'frame ' + gd._transitionData._counter++)]); + } + + if(_frameHash[frame.name]) { + // If frame is present, overwrite its definition: + for(j = 0; j < _frames.length; j++) { + if((_frames[j] || {}).name === frame.name) break; + } + ops.push({type: 'replace', index: j, value: frame}); + revops.unshift({type: 'replace', index: j, value: _frames[j]}); + } else { + // Otherwise insert it at the end of the list: + idx = Math.max(0, Math.min(insertions[i].index, frameCount)); + + ops.push({type: 'insert', index: idx, value: frame}); + revops.unshift({type: 'delete', index: idx}); + frameCount++; + } + } + + var undoFunc = Plots.modifyFrames; + var redoFunc = Plots.modifyFrames; + var undoArgs = [gd, revops]; + var redoArgs = [gd, ops]; + + if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); + + return Plots.modifyFrames(gd, ops); +} + +/** + * Delete frame + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * + * @param {array of integers} frameList + * list of integer indices of frames to be deleted + */ +function deleteFrames(gd, frameList) { + gd = Lib.getGraphDiv(gd); + + if(!Lib.isPlotDiv(gd)) { + throw new Error('This element is not a Plotly plot: ' + gd); + } + + var i, idx; + var _frames = gd._transitionData._frames; + var ops = []; + var revops = []; + + if(!frameList) { + frameList = []; + for(i = 0; i < _frames.length; i++) { + frameList.push(i); + } + } + + frameList = frameList.slice(); + frameList.sort(); + + for(i = frameList.length - 1; i >= 0; i--) { + idx = frameList[i]; + ops.push({type: 'delete', index: idx}); + revops.unshift({type: 'insert', index: idx, value: _frames[idx]}); + } + + var undoFunc = Plots.modifyFrames; + var redoFunc = Plots.modifyFrames; + var undoArgs = [gd, revops]; + var redoArgs = [gd, ops]; + + if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); + + return Plots.modifyFrames(gd, ops); +} + +/** + * Purge a graph container div back to its initial pre-_doPlot state + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + */ +function purge(gd) { + gd = Lib.getGraphDiv(gd); + + var fullLayout = gd._fullLayout || {}; + var fullData = gd._fullData || []; + + // remove gl contexts + Plots.cleanPlot([], {}, fullData, fullLayout); + + // purge properties + Plots.purge(gd); + + // purge event emitter methods + Events.purge(gd); + + // remove plot container + if(fullLayout._container) fullLayout._container.remove(); + + // in contrast to _doPlots.purge which does NOT clear _context! + delete gd._context; + + return gd; +} + +// determines if the graph div requires a recalculation of its inverse matrix transforms by comparing old + new bounding boxes. +function calcInverseTransform(gd) { + var fullLayout = gd._fullLayout; + + var newBBox = gd.getBoundingClientRect(); + if(Lib.equalDomRects(newBBox, fullLayout._lastBBox)) return; + + var m = fullLayout._invTransform = Lib.inverseTransformMatrix(Lib.getFullTransformMatrix(gd)); + fullLayout._invScaleX = Math.sqrt(m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2]); + fullLayout._invScaleY = Math.sqrt(m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2]); + fullLayout._lastBBox = newBBox; +} + +// ------------------------------------------------------- +// makePlotFramework: Create the plot container and axes +// ------------------------------------------------------- +function makePlotFramework(gd) { + var gd3 = d3.select(gd); + var fullLayout = gd._fullLayout; + + fullLayout._calcInverseTransform = calcInverseTransform; + fullLayout._calcInverseTransform(gd); + + // Plot container + fullLayout._container = gd3.selectAll('.plot-container').data([0]); + fullLayout._container.enter() + .insert('div', ':first-child') + .classed('plot-container', true) + .classed('plotly', true); + + // Make the svg container + fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]); + fullLayout._paperdiv.enter().append('div') + .classed('user-select-none', true) + .classed('svg-container', true) + .style('position', 'relative'); + + // Make the graph containers + // start fresh each time we get here, so we know the order comes out + // right, rather than enter/exit which can muck up the order + // TODO: sort out all the ordering so we don't have to + // explicitly delete anything + // FIXME: parcoords reuses this object, not the best pattern + fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container') + .data([{}]); + + fullLayout._glcontainer.enter().append('div') + .classed('gl-container', true); + + fullLayout._paperdiv.selectAll('.main-svg').remove(); + fullLayout._paperdiv.select('.modebar-container').remove(); + + fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child') + .classed('main-svg', true); + + fullLayout._toppaper = fullLayout._paperdiv.append('svg') + .classed('main-svg', true); + + fullLayout._modebardiv = fullLayout._paperdiv.append('div'); + delete fullLayout._modeBar; + + fullLayout._hoverpaper = fullLayout._paperdiv.append('svg') + .classed('main-svg', true); + + if(!fullLayout._uid) { + var otherUids = {}; + d3.selectAll('defs').each(function() { + if(this.id) otherUids[this.id.split('-')[1]] = 1; + }); + fullLayout._uid = Lib.randstr(otherUids); + } + + fullLayout._paperdiv.selectAll('.main-svg') + .attr(xmlnsNamespaces.svgAttrs); + + fullLayout._defs = fullLayout._paper.append('defs') + .attr('id', 'defs-' + fullLayout._uid); + + fullLayout._clips = fullLayout._defs.append('g') + .classed('clips', true); + + fullLayout._topdefs = fullLayout._toppaper.append('defs') + .attr('id', 'topdefs-' + fullLayout._uid); + + fullLayout._topclips = fullLayout._topdefs.append('g') + .classed('clips', true); + + fullLayout._bgLayer = fullLayout._paper.append('g') + .classed('bglayer', true); + + fullLayout._draggers = fullLayout._paper.append('g') + .classed('draglayer', true); + + // lower shape/image layer - note that this is behind + // all subplots data/grids but above the backgrounds + // except inset subplots, whose backgrounds are drawn + // inside their own group so that they appear above + // the data for the main subplot + // lower shapes and images which are fully referenced to + // a subplot still get drawn within the subplot's group + // so they will work correctly on insets + var layerBelow = fullLayout._paper.append('g') + .classed('layer-below', true); + fullLayout._imageLowerLayer = layerBelow.append('g') + .classed('imagelayer', true); + fullLayout._shapeLowerLayer = layerBelow.append('g') + .classed('shapelayer', true); + + // single cartesian layer for the whole plot + fullLayout._cartesianlayer = fullLayout._paper.append('g').classed('cartesianlayer', true); + + // single polar layer for the whole plot + fullLayout._polarlayer = fullLayout._paper.append('g').classed('polarlayer', true); + + // single ternary layer for the whole plot + fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true); + + // single geo layer for the whole plot + fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true); + + // single funnelarea layer for the whole plot + fullLayout._funnelarealayer = fullLayout._paper.append('g').classed('funnelarealayer', true); + + // single pie layer for the whole plot + fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true); + + // single treemap layer for the whole plot + fullLayout._iciclelayer = fullLayout._paper.append('g').classed('iciclelayer', true); + + // single treemap layer for the whole plot + fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true); + + // single sunburst layer for the whole plot + fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true); + + // single indicator layer for the whole plot + fullLayout._indicatorlayer = fullLayout._toppaper.append('g').classed('indicatorlayer', true); + + // fill in image server scrape-svg + fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true); + + // lastly upper shapes, info (legend, annotations) and hover layers go on top + // these are in a different svg element normally, but get collapsed into a single + // svg when exporting (after inserting 3D) + // upper shapes/images are only those drawn above the whole plot, including subplots + var layerAbove = fullLayout._toppaper.append('g') + .classed('layer-above', true); + fullLayout._imageUpperLayer = layerAbove.append('g') + .classed('imagelayer', true); + fullLayout._shapeUpperLayer = layerAbove.append('g') + .classed('shapelayer', true); + + fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true); + fullLayout._menulayer = fullLayout._toppaper.append('g').classed('menulayer', true); + fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true); + fullLayout._hoverlayer = fullLayout._hoverpaper.append('g').classed('hoverlayer', true); + + // Make the modebar container + fullLayout._modebardiv + .classed('modebar-container', true) + .style('position', 'absolute') + .style('top', '0px') + .style('right', '0px'); + + gd.emit('plotly_framework'); +} + +exports.animate = animate; +exports.addFrames = addFrames; +exports.deleteFrames = deleteFrames; + +exports.addTraces = addTraces; +exports.deleteTraces = deleteTraces; +exports.extendTraces = extendTraces; +exports.moveTraces = moveTraces; +exports.prependTraces = prependTraces; + +exports.newPlot = newPlot; +exports._doPlot = _doPlot; +exports.purge = purge; + +exports.react = react; +exports.redraw = redraw; +exports.relayout = relayout; +exports.restyle = restyle; + +exports.setPlotConfig = setPlotConfig; + +exports.update = update; + +exports._guiRelayout = guiEdit(relayout); +exports._guiRestyle = guiEdit(restyle); +exports._guiUpdate = guiEdit(update); + +exports._storeDirectGUIEdit = _storeDirectGUIEdit; + +},{"../components/color":155,"../components/drawing":177,"../constants/xmlns_namespaces":266,"../lib":285,"../lib/events":278,"../lib/queue":300,"../plots/cartesian/axes":331,"../plots/cartesian/constants":338,"../plots/cartesian/graph_interact":341,"../plots/cartesian/select":351,"../plots/plots":366,"../registry":373,"./edit_types":313,"./helpers":314,"./manage_arrays":316,"./plot_config":318,"./plot_schema":319,"./subroutines":321,"@plotly/d3":20,"fast-isnumeric":31,"has-hover":62}],318:[function(_dereq_,module,exports){ +'use strict'; + +/** + * This will be transferred over to gd and overridden by + * config args to Plotly.newPlot. + * + * The defaults are the appropriate settings for plotly.js, + * so we get the right experience without any config argument. + * + * N.B. the config options are not coerced using Lib.coerce so keys + * like `valType` and `values` are only set for documentation purposes + * at the moment. + */ + +var configAttributes = { + staticPlot: { + valType: 'boolean', + dflt: false, + }, + + plotlyServerURL: { + valType: 'string', + dflt: '', + }, + + editable: { + valType: 'boolean', + dflt: false, + }, + edits: { + annotationPosition: { + valType: 'boolean', + dflt: false, + }, + annotationTail: { + valType: 'boolean', + dflt: false, + }, + annotationText: { + valType: 'boolean', + dflt: false, + }, + axisTitleText: { + valType: 'boolean', + dflt: false, + }, + colorbarPosition: { + valType: 'boolean', + dflt: false, + }, + colorbarTitleText: { + valType: 'boolean', + dflt: false, + }, + legendPosition: { + valType: 'boolean', + dflt: false, + }, + legendText: { + valType: 'boolean', + dflt: false, + }, + shapePosition: { + valType: 'boolean', + dflt: false, + }, + titleText: { + valType: 'boolean', + dflt: false, + } + }, + + autosizable: { + valType: 'boolean', + dflt: false, + }, + responsive: { + valType: 'boolean', + dflt: false, + }, + fillFrame: { + valType: 'boolean', + dflt: false, + }, + frameMargins: { + valType: 'number', + dflt: 0, + min: 0, + max: 0.5, + }, + + scrollZoom: { + valType: 'flaglist', + flags: ['cartesian', 'gl3d', 'geo', 'mapbox'], + extras: [true, false], + dflt: 'gl3d+geo+mapbox', + }, + doubleClick: { + valType: 'enumerated', + values: [false, 'reset', 'autosize', 'reset+autosize'], + dflt: 'reset+autosize', + }, + doubleClickDelay: { + valType: 'number', + dflt: 300, + min: 0, + }, + + showAxisDragHandles: { + valType: 'boolean', + dflt: true, + }, + showAxisRangeEntryBoxes: { + valType: 'boolean', + dflt: true, + }, + + showTips: { + valType: 'boolean', + dflt: true, + }, + + showLink: { + valType: 'boolean', + dflt: false, + }, + linkText: { + valType: 'string', + dflt: 'Edit chart', + noBlank: true, + }, + sendData: { + valType: 'boolean', + dflt: true, + }, + showSources: { + valType: 'any', + dflt: false, + }, + + displayModeBar: { + valType: 'enumerated', + values: ['hover', true, false], + dflt: 'hover', + }, + showSendToCloud: { + valType: 'boolean', + dflt: false, + }, + showEditInChartStudio: { + valType: 'boolean', + dflt: false, + }, + modeBarButtonsToRemove: { + valType: 'any', + dflt: [], + }, + modeBarButtonsToAdd: { + valType: 'any', + dflt: [], + }, + modeBarButtons: { + valType: 'any', + dflt: false, + }, + toImageButtonOptions: { + valType: 'any', + dflt: {}, + }, + displaylogo: { + valType: 'boolean', + dflt: true, + }, + watermark: { + valType: 'boolean', + dflt: false, + }, + + plotGlPixelRatio: { + valType: 'number', + dflt: 2, + min: 1, + max: 4, + }, + + setBackground: { + valType: 'any', + dflt: 'transparent', + }, + + topojsonURL: { + valType: 'string', + noBlank: true, + dflt: 'https://cdn.plot.ly/', + }, + + mapboxAccessToken: { + valType: 'string', + dflt: null, + }, + + logging: { + valType: 'integer', + min: 0, + max: 2, + dflt: 1, + }, + + notifyOnLogging: { + valType: 'integer', + min: 0, + max: 2, + dflt: 0, + }, + + queueLength: { + valType: 'integer', + min: 0, + dflt: 0, + }, + + globalTransforms: { + valType: 'any', + dflt: [], + }, + + locale: { + valType: 'string', + dflt: 'en-US', + }, + + locales: { + valType: 'any', + dflt: {}, + } +}; + +var dfltConfig = {}; + +function crawl(src, target) { + for(var k in src) { + var obj = src[k]; + if(obj.valType) { + target[k] = obj.dflt; + } else { + if(!target[k]) { + target[k] = {}; + } + crawl(obj, target[k]); + } + } +} + +crawl(configAttributes, dfltConfig); + +module.exports = { + configAttributes: configAttributes, + dfltConfig: dfltConfig +}; + +},{}],319:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); +var Lib = _dereq_('../lib'); + +var baseAttributes = _dereq_('../plots/attributes'); +var baseLayoutAttributes = _dereq_('../plots/layout_attributes'); +var frameAttributes = _dereq_('../plots/frame_attributes'); +var animationAttributes = _dereq_('../plots/animation_attributes'); +var configAttributes = _dereq_('./plot_config').configAttributes; + +var editTypes = _dereq_('./edit_types'); + +var extendDeepAll = Lib.extendDeepAll; +var isPlainObject = Lib.isPlainObject; +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; +var nestedProperty = Lib.nestedProperty; +var valObjectMeta = Lib.valObjectMeta; + +var IS_SUBPLOT_OBJ = '_isSubplotObj'; +var IS_LINKED_TO_ARRAY = '_isLinkedToArray'; +var ARRAY_ATTR_REGEXPS = '_arrayAttrRegexps'; +var DEPRECATED = '_deprecated'; +var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, ARRAY_ATTR_REGEXPS, DEPRECATED]; + +exports.IS_SUBPLOT_OBJ = IS_SUBPLOT_OBJ; +exports.IS_LINKED_TO_ARRAY = IS_LINKED_TO_ARRAY; +exports.DEPRECATED = DEPRECATED; +exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS; + +/** Outputs the full plotly.js plot schema + * + * @return {object} + * - defs + * - traces + * - layout + * - transforms + * - frames + * - animations + * - config + */ +exports.get = function() { + var traces = {}; + + Registry.allTypes.forEach(function(type) { + traces[type] = getTraceAttributes(type); + }); + + var transforms = {}; + + Object.keys(Registry.transformsRegistry).forEach(function(type) { + transforms[type] = getTransformAttributes(type); + }); + + return { + defs: { + valObjects: valObjectMeta, + metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role', 'editType', 'impliedEdits']), + editType: { + traces: editTypes.traces, + layout: editTypes.layout + }, + impliedEdits: { + } + }, + + traces: traces, + layout: getLayoutAttributes(), + + transforms: transforms, + + frames: getFramesAttributes(), + animation: formatAttributes(animationAttributes), + + config: formatAttributes(configAttributes) + }; +}; + +/** + * Crawl the attribute tree, recursively calling a callback function + * + * @param {object} attrs + * The node of the attribute tree (e.g. the root) from which recursion originates + * @param {Function} callback + * A callback function with the signature: + * @callback callback + * @param {object} attr an attribute + * @param {String} attrName name string + * @param {object[]} attrs all the attributes + * @param {Number} level the recursion level, 0 at the root + * @param {String} fullAttrString full attribute name (ie 'marker.line') + * @param {Number} [specifiedLevel] + * The level in the tree, in order to let the callback function detect descend or backtrack, + * typically unsupplied (implied 0), just used by the self-recursive call. + * The necessity arises because the tree traversal is not controlled by callback return values. + * The decision to not use callback return values for controlling tree pruning arose from + * the goal of keeping the crawler backwards compatible. Observe that one of the pruning conditions + * precedes the callback call. + * @param {string} [attrString] + * the path to the current attribute, as an attribute string (ie 'marker.line') + * typically unsupplied, but you may supply it if you want to disambiguate which attrs tree you + * are starting from + * + * @return {object} transformOut + * copy of transformIn that contains attribute defaults + */ +exports.crawl = function(attrs, callback, specifiedLevel, attrString) { + var level = specifiedLevel || 0; + attrString = attrString || ''; + + Object.keys(attrs).forEach(function(attrName) { + var attr = attrs[attrName]; + + if(UNDERSCORE_ATTRS.indexOf(attrName) !== -1) return; + + var fullAttrString = (attrString ? attrString + '.' : '') + attrName; + callback(attr, attrName, attrs, level, fullAttrString); + + if(exports.isValObject(attr)) return; + + if(isPlainObject(attr) && attrName !== 'impliedEdits') { + exports.crawl(attr, callback, level + 1, fullAttrString); + } + }); +}; + +/** Is object a value object (or a container object)? + * + * @param {object} obj + * @return {boolean} + * returns true for a valid value object and + * false for tree nodes in the attribute hierarchy + */ +exports.isValObject = function(obj) { + return obj && obj.valType !== undefined; +}; + +/** + * Find all data array attributes in a given trace object - including + * `arrayOk` attributes. + * + * @param {object} trace + * full trace object that contains a reference to `_module.attributes` + * + * @return {array} arrayAttributes + * list of array attributes for the given trace + */ +exports.findArrayAttributes = function(trace) { + var arrayAttributes = []; + var stack = []; + var isArrayStack = []; + var baseContainer, baseAttrName; + + function callback(attr, attrName, attrs, level) { + stack = stack.slice(0, level).concat([attrName]); + isArrayStack = isArrayStack.slice(0, level).concat([attr && attr._isLinkedToArray]); + + var splittableAttr = ( + attr && + (attr.valType === 'data_array' || attr.arrayOk === true) && + !(stack[level - 1] === 'colorbar' && (attrName === 'ticktext' || attrName === 'tickvals')) + ); + + // Manually exclude 'colorbar.tickvals' and 'colorbar.ticktext' for now + // which are declared as `valType: 'data_array'` but scale independently of + // the coordinate arrays. + // + // Down the road, we might want to add a schema field (e.g `uncorrelatedArray: true`) + // to distinguish attributes of the likes. + + if(!splittableAttr) return; + + crawlIntoTrace(baseContainer, 0, ''); + } + + function crawlIntoTrace(container, i, astrPartial) { + var item = container[stack[i]]; + var newAstrPartial = astrPartial + stack[i]; + if(i === stack.length - 1) { + if(isArrayOrTypedArray(item)) { + arrayAttributes.push(baseAttrName + newAstrPartial); + } + } else { + if(isArrayStack[i]) { + if(Array.isArray(item)) { + for(var j = 0; j < item.length; j++) { + if(isPlainObject(item[j])) { + crawlIntoTrace(item[j], i + 1, newAstrPartial + '[' + j + '].'); + } + } + } + } else if(isPlainObject(item)) { + crawlIntoTrace(item, i + 1, newAstrPartial + '.'); + } + } + } + + baseContainer = trace; + baseAttrName = ''; + exports.crawl(baseAttributes, callback); + if(trace._module && trace._module.attributes) { + exports.crawl(trace._module.attributes, callback); + } + + var transforms = trace.transforms; + if(transforms) { + for(var i = 0; i < transforms.length; i++) { + var transform = transforms[i]; + var module = transform._module; + + if(module) { + baseAttrName = 'transforms[' + i + '].'; + baseContainer = transform; + + exports.crawl(module.attributes, callback); + } + } + } + + return arrayAttributes; +}; + +/* + * Find the valObject for one attribute in an existing trace + * + * @param {object} trace + * full trace object that contains a reference to `_module.attributes` + * @param {object} parts + * an array of parts, like ['transforms', 1, 'value'] + * typically from nestedProperty(...).parts + * + * @return {object|false} + * the valObject for this attribute, or the last found parent + * in some cases the innermost valObject will not exist, for example + * `valType: 'any'` attributes where we might set a part of the attribute. + * In that case, stop at the deepest valObject we *do* find. + */ +exports.getTraceValObject = function(trace, parts) { + var head = parts[0]; + var i = 1; // index to start recursing from + var moduleAttrs, valObject; + + if(head === 'transforms') { + if(parts.length === 1) { + return baseAttributes.transforms; + } + var transforms = trace.transforms; + if(!Array.isArray(transforms) || !transforms.length) return false; + var tNum = parts[1]; + if(!isIndex(tNum) || tNum >= transforms.length) { + return false; + } + moduleAttrs = (Registry.transformsRegistry[transforms[tNum].type] || {}).attributes; + valObject = moduleAttrs && moduleAttrs[parts[2]]; + i = 3; // start recursing only inside the transform + } else { + // first look in the module for this trace + // components have already merged their trace attributes in here + var _module = trace._module; + if(!_module) _module = (Registry.modules[trace.type || baseAttributes.type.dflt] || {})._module; + if(!_module) return false; + + moduleAttrs = _module.attributes; + valObject = moduleAttrs && moduleAttrs[head]; + + // then look in the subplot attributes + if(!valObject) { + var subplotModule = _module.basePlotModule; + if(subplotModule && subplotModule.attributes) { + valObject = subplotModule.attributes[head]; + } + } + + // finally look in the global attributes + if(!valObject) valObject = baseAttributes[head]; + } + + return recurseIntoValObject(valObject, parts, i); +}; + +/* + * Find the valObject for one layout attribute + * + * @param {array} parts + * an array of parts, like ['annotations', 1, 'x'] + * typically from nestedProperty(...).parts + * + * @return {object|false} + * the valObject for this attribute, or the last found parent + * in some cases the innermost valObject will not exist, for example + * `valType: 'any'` attributes where we might set a part of the attribute. + * In that case, stop at the deepest valObject we *do* find. + */ +exports.getLayoutValObject = function(fullLayout, parts) { + var valObject = layoutHeadAttr(fullLayout, parts[0]); + + return recurseIntoValObject(valObject, parts, 1); +}; + +function layoutHeadAttr(fullLayout, head) { + var i, key, _module, attributes; + + // look for attributes of the subplot types used on the plot + var basePlotModules = fullLayout._basePlotModules; + if(basePlotModules) { + var out; + for(i = 0; i < basePlotModules.length; i++) { + _module = basePlotModules[i]; + if(_module.attrRegex && _module.attrRegex.test(head)) { + // if a module defines overrides, these take precedence + // initially this is to allow gl2d different editTypes from svg cartesian + if(_module.layoutAttrOverrides) return _module.layoutAttrOverrides; + + // otherwise take the first attributes we find + if(!out && _module.layoutAttributes) out = _module.layoutAttributes; + } + + // a module can also override the behavior of base (and component) module layout attrs + // again see gl2d for initial use case + var baseOverrides = _module.baseLayoutAttrOverrides; + if(baseOverrides && head in baseOverrides) return baseOverrides[head]; + } + if(out) return out; + } + + // look for layout attributes contributed by traces on the plot + var modules = fullLayout._modules; + if(modules) { + for(i = 0; i < modules.length; i++) { + attributes = modules[i].layoutAttributes; + if(attributes && head in attributes) { + return attributes[head]; + } + } + } + + /* + * Next look in components. + * Components that define a schema have already merged this into + * base and subplot attribute defs, so ignore these. + * Others (older style) all put all their attributes + * inside a container matching the module `name` + * eg `attributes` (array) or `legend` (object) + */ + for(key in Registry.componentsRegistry) { + _module = Registry.componentsRegistry[key]; + if(_module.name === 'colorscale' && head.indexOf('coloraxis') === 0) { + return _module.layoutAttributes[head]; + } else if(!_module.schema && (head === _module.name)) { + return _module.layoutAttributes; + } + } + + if(head in baseLayoutAttributes) return baseLayoutAttributes[head]; + + return false; +} + +function recurseIntoValObject(valObject, parts, i) { + if(!valObject) return false; + + if(valObject._isLinkedToArray) { + // skip array index, abort if we try to dive into an array without an index + if(isIndex(parts[i])) i++; + else if(i < parts.length) return false; + } + + // now recurse as far as we can. Occasionally we have an attribute + // setting an internal part below what's in the schema; just return + // the innermost schema item we find. + for(; i < parts.length; i++) { + var newValObject = valObject[parts[i]]; + if(isPlainObject(newValObject)) valObject = newValObject; + else break; + + if(i === parts.length - 1) break; + + if(valObject._isLinkedToArray) { + i++; + if(!isIndex(parts[i])) return false; + } else if(valObject.valType === 'info_array') { + i++; + var index = parts[i]; + if(!isIndex(index)) return false; + + var items = valObject.items; + if(Array.isArray(items)) { + if(index >= items.length) return false; + if(valObject.dimensions === 2) { + i++; + if(parts.length === i) return valObject; + var index2 = parts[i]; + if(!isIndex(index2)) return false; + valObject = items[index][index2]; + } else valObject = items[index]; + } else { + valObject = items; + } + } + } + + return valObject; +} + +// note: this is different from Lib.isIndex, this one doesn't accept numeric +// strings, only actual numbers. +function isIndex(val) { + return val === Math.round(val) && val >= 0; +} + +function getTraceAttributes(type) { + var _module, basePlotModule; + + _module = Registry.modules[type]._module, + basePlotModule = _module.basePlotModule; + + var attributes = {}; + + // make 'type' the first attribute in the object + attributes.type = null; + + var copyBaseAttributes = extendDeepAll({}, baseAttributes); + var copyModuleAttributes = extendDeepAll({}, _module.attributes); + + // prune global-level trace attributes that are already defined in a trace + exports.crawl(copyModuleAttributes, function(attr, attrName, attrs, level, fullAttrString) { + nestedProperty(copyBaseAttributes, fullAttrString).set(undefined); + // Prune undefined attributes + if(attr === undefined) nestedProperty(copyModuleAttributes, fullAttrString).set(undefined); + }); + + // base attributes (same for all trace types) + extendDeepAll(attributes, copyBaseAttributes); + + // prune-out base attributes based on trace module categories + if(Registry.traceIs(type, 'noOpacity')) { + delete attributes.opacity; + } + if(!Registry.traceIs(type, 'showLegend')) { + delete attributes.showlegend; + delete attributes.legendgroup; + } + if(Registry.traceIs(type, 'noHover')) { + delete attributes.hoverinfo; + delete attributes.hoverlabel; + } + if(!_module.selectPoints) { + delete attributes.selectedpoints; + } + + // module attributes + extendDeepAll(attributes, copyModuleAttributes); + + // subplot attributes + if(basePlotModule.attributes) { + extendDeepAll(attributes, basePlotModule.attributes); + } + + // 'type' gets overwritten by baseAttributes; reset it here + attributes.type = type; + + var out = { + meta: _module.meta || {}, + categories: _module.categories || {}, + animatable: Boolean(_module.animatable), + type: type, + attributes: formatAttributes(attributes), + }; + + // trace-specific layout attributes + if(_module.layoutAttributes) { + var layoutAttributes = {}; + + extendDeepAll(layoutAttributes, _module.layoutAttributes); + out.layoutAttributes = formatAttributes(layoutAttributes); + } + + // drop anim:true in non-animatable modules + if(!_module.animatable) { + exports.crawl(out, function(attr) { + if(exports.isValObject(attr) && 'anim' in attr) { + delete attr.anim; + } + }); + } + + return out; +} + +function getLayoutAttributes() { + var layoutAttributes = {}; + var key, _module; + + // global layout attributes + extendDeepAll(layoutAttributes, baseLayoutAttributes); + + // add base plot module layout attributes + for(key in Registry.subplotsRegistry) { + _module = Registry.subplotsRegistry[key]; + + if(!_module.layoutAttributes) continue; + + if(Array.isArray(_module.attr)) { + for(var i = 0; i < _module.attr.length; i++) { + handleBasePlotModule(layoutAttributes, _module, _module.attr[i]); + } + } else { + var astr = _module.attr === 'subplot' ? _module.name : _module.attr; + handleBasePlotModule(layoutAttributes, _module, astr); + } + } + + // add registered components layout attributes + for(key in Registry.componentsRegistry) { + _module = Registry.componentsRegistry[key]; + var schema = _module.schema; + + if(schema && (schema.subplots || schema.layout)) { + /* + * Components with defined schema have already been merged in at register time + * but a few components define attributes that apply only to xaxis + * not yaxis (rangeselector, rangeslider) - delete from y schema. + * Note that the input attributes for xaxis/yaxis are the same object + * so it's not possible to only add them to xaxis from the start. + * If we ever have such asymmetry the other way, or anywhere else, + * we will need to extend both this code and mergeComponentAttrsToSubplot + * (which will not find yaxis only for example) + */ + var subplots = schema.subplots; + if(subplots && subplots.xaxis && !subplots.yaxis) { + for(var xkey in subplots.xaxis) { + delete layoutAttributes.yaxis[xkey]; + } + } + } else if(_module.name === 'colorscale') { + extendDeepAll(layoutAttributes, _module.layoutAttributes); + } else if(_module.layoutAttributes) { + // older style without schema need to be explicitly merged in now + insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name); + } + } + + return { + layoutAttributes: formatAttributes(layoutAttributes) + }; +} + +function getTransformAttributes(type) { + var _module = Registry.transformsRegistry[type]; + var attributes = extendDeepAll({}, _module.attributes); + + // add registered components transform attributes + Object.keys(Registry.componentsRegistry).forEach(function(k) { + var _module = Registry.componentsRegistry[k]; + + if(_module.schema && _module.schema.transforms && _module.schema.transforms[type]) { + Object.keys(_module.schema.transforms[type]).forEach(function(v) { + insertAttrs(attributes, _module.schema.transforms[type][v], v); + }); + } + }); + + return { + attributes: formatAttributes(attributes) + }; +} + +function getFramesAttributes() { + var attrs = { + frames: extendDeepAll({}, frameAttributes) + }; + + formatAttributes(attrs); + + return attrs.frames; +} + +function formatAttributes(attrs) { + mergeValTypeAndRole(attrs); + formatArrayContainers(attrs); + stringify(attrs); + + return attrs; +} + +function mergeValTypeAndRole(attrs) { + function makeSrcAttr(attrName) { + return { + valType: 'string', + editType: 'none' + }; + } + + function callback(attr, attrName, attrs) { + if(exports.isValObject(attr)) { + if(attr.arrayOk === true || attr.valType === 'data_array') { + // all 'arrayOk' and 'data_array' attrs have a corresponding 'src' attr + attrs[attrName + 'src'] = makeSrcAttr(attrName); + } + } else if(isPlainObject(attr)) { + // all attrs container objects get role 'object' + attr.role = 'object'; + } + } + + exports.crawl(attrs, callback); +} + +function formatArrayContainers(attrs) { + function callback(attr, attrName, attrs) { + if(!attr) return; + + var itemName = attr[IS_LINKED_TO_ARRAY]; + + if(!itemName) return; + + delete attr[IS_LINKED_TO_ARRAY]; + + attrs[attrName] = { items: {} }; + attrs[attrName].items[itemName] = attr; + attrs[attrName].role = 'object'; + } + + exports.crawl(attrs, callback); +} + +// this can take around 10ms and should only be run from PlotSchema.get(), +// to ensure JSON.stringify(PlotSchema.get()) gives the intended result. +function stringify(attrs) { + function walk(attr) { + for(var k in attr) { + if(isPlainObject(attr[k])) { + walk(attr[k]); + } else if(Array.isArray(attr[k])) { + for(var i = 0; i < attr[k].length; i++) { + walk(attr[k][i]); + } + } else { + // as JSON.stringify(/test/) // => {} + if(attr[k] instanceof RegExp) { + attr[k] = attr[k].toString(); + } + } + } + } + + walk(attrs); +} + + +function handleBasePlotModule(layoutAttributes, _module, astr) { + var np = nestedProperty(layoutAttributes, astr); + var attrs = extendDeepAll({}, _module.layoutAttributes); + + attrs[IS_SUBPLOT_OBJ] = true; + np.set(attrs); +} + +function insertAttrs(baseAttrs, newAttrs, astr) { + var np = nestedProperty(baseAttrs, astr); + + np.set(extendDeepAll(np.get() || {}, newAttrs)); +} + +},{"../lib":285,"../plots/animation_attributes":325,"../plots/attributes":327,"../plots/frame_attributes":361,"../plots/layout_attributes":364,"../registry":373,"./edit_types":313,"./plot_config":318}],320:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var plotAttributes = _dereq_('../plots/attributes'); + +var TEMPLATEITEMNAME = 'templateitemname'; + +var templateAttrs = { + name: { + valType: 'string', + editType: 'none', + } +}; +templateAttrs[TEMPLATEITEMNAME] = { + valType: 'string', + editType: 'calc', +}; + +/** + * templatedArray: decorate an attributes object with templating (and array) + * properties. + * + * @param {string} name: the singular form of the array name. Sets + * `_isLinkedToArray` to this, so the schema knows to treat this as an array. + * @param {object} attrs: the item attributes. Since all callers are expected + * to be constructing this object on the spot, we mutate it here for + * performance, rather than extending a new object with it. + * + * @returns {object}: the decorated `attrs` object + */ +exports.templatedArray = function(name, attrs) { + attrs._isLinkedToArray = name; + attrs.name = templateAttrs.name; + attrs[TEMPLATEITEMNAME] = templateAttrs[TEMPLATEITEMNAME]; + return attrs; +}; + +/** + * traceTemplater: logic for matching traces to trace templates + * + * @param {object} dataTemplate: collection of {traceType: [{template}, ...]} + * ie each type the template applies to contains a list of template objects, + * to be provided cyclically to data traces of that type. + * + * @returns {object}: {newTrace}, a function: + * newTrace(traceIn): that takes the input traceIn, coerces its type, then + * uses that type to find the next template to apply. returns the output + * traceOut with template attached, ready to continue supplyDefaults. + */ +exports.traceTemplater = function(dataTemplate) { + var traceCounts = {}; + var traceType, typeTemplates; + + for(traceType in dataTemplate) { + typeTemplates = dataTemplate[traceType]; + if(Array.isArray(typeTemplates) && typeTemplates.length) { + traceCounts[traceType] = 0; + } + } + + function newTrace(traceIn) { + traceType = Lib.coerce(traceIn, {}, plotAttributes, 'type'); + var traceOut = {type: traceType, _template: null}; + if(traceType in traceCounts) { + typeTemplates = dataTemplate[traceType]; + // cycle through traces in the template set for this type + var typei = traceCounts[traceType] % typeTemplates.length; + traceCounts[traceType]++; + traceOut._template = typeTemplates[typei]; + } else { + // TODO: anything we should do for types missing from the template? + // try to apply some other type? Or just bail as we do here? + // Actually I think yes, we should apply other types; would be nice + // if all scatter* could inherit from each other, and if histogram + // could inherit from bar, etc... but how to specify this? And do we + // compose them, or if a type is present require it to be complete? + // Actually this could apply to layout too - 3D annotations + // inheriting from 2D, axes of different types inheriting from each + // other... + } + return traceOut; + } + + return { + newTrace: newTrace + // TODO: function to figure out what's left & what didn't work + }; +}; + +/** + * newContainer: Create a new sub-container inside `container` and propagate any + * applicable template to it. If there's no template, still propagates + * `undefined` so relinkPrivate will not retain an old template! + * + * @param {object} container: the outer container, should already have _template + * if there *is* a template for this plot + * @param {string} name: the key of the new container to make + * @param {string} baseName: if applicable, a base attribute to take the + * template from, ie for xaxis3 the base would be xaxis + * + * @returns {object}: an object for inclusion _full*, empty except for the + * appropriate template piece + */ +exports.newContainer = function(container, name, baseName) { + var template = container._template; + var part = template && (template[name] || (baseName && template[baseName])); + if(!Lib.isPlainObject(part)) part = null; + + var out = container[name] = {_template: part}; + return out; +}; + +/** + * arrayTemplater: special logic for templating both defaults and specific items + * in a container array (annotations etc) + * + * @param {object} container: the outer container, should already have _template + * if there *is* a template for this plot + * @param {string} name: the name of the array to template (ie 'annotations') + * will be used to find default ('annotationdefaults' object) and specific + * ('annotations' array) template specs. + * @param {string} inclusionAttr: the attribute determining this item's + * inclusion in the output, usually 'visible' or 'enabled' + * + * @returns {object}: {newItem, defaultItems}, both functions: + * newItem(itemIn): create an output item, bare except for the correct + * template and name(s), as the base for supplyDefaults + * defaultItems(): to be called after all newItem calls, return any + * specific template items that have not already beeen included, + * also as bare output items ready for supplyDefaults. + */ +exports.arrayTemplater = function(container, name, inclusionAttr) { + var template = container._template; + var defaultsTemplate = template && template[arrayDefaultKey(name)]; + var templateItems = template && template[name]; + if(!Array.isArray(templateItems) || !templateItems.length) { + templateItems = []; + } + + var usedNames = {}; + + function newItem(itemIn) { + // include name and templateitemname in the output object for ALL + // container array items. Note: you could potentially use different + // name and templateitemname, if you're using one template to make + // another template. templateitemname would be the name in the original + // template, and name is the new "subclassed" item name. + var out = {name: itemIn.name, _input: itemIn}; + var templateItemName = out[TEMPLATEITEMNAME] = itemIn[TEMPLATEITEMNAME]; + + // no itemname: use the default template + if(!validItemName(templateItemName)) { + out._template = defaultsTemplate; + return out; + } + + // look for an item matching this itemname + // note these do not inherit from the default template, only the item. + for(var i = 0; i < templateItems.length; i++) { + var templateItem = templateItems[i]; + if(templateItem.name === templateItemName) { + // Note: it's OK to use a template item more than once + // but using it at least once will stop it from generating + // a default item at the end. + usedNames[templateItemName] = 1; + out._template = templateItem; + return out; + } + } + + // Didn't find a matching template item, so since this item is intended + // to only be modifications it's most likely broken. Hide it unless + // it's explicitly marked visible - in which case it gets NO template, + // not even the default. + out[inclusionAttr] = itemIn[inclusionAttr] || false; + // special falsy value we can look for in validateTemplate + out._template = false; + return out; + } + + function defaultItems() { + var out = []; + for(var i = 0; i < templateItems.length; i++) { + var templateItem = templateItems[i]; + var name = templateItem.name; + // only allow named items to be added as defaults, + // and only allow each name once + if(validItemName(name) && !usedNames[name]) { + var outi = { + _template: templateItem, + name: name, + _input: {_templateitemname: name} + }; + outi[TEMPLATEITEMNAME] = templateItem[TEMPLATEITEMNAME]; + out.push(outi); + usedNames[name] = 1; + } + } + return out; + } + + return { + newItem: newItem, + defaultItems: defaultItems + }; +}; + +function validItemName(name) { + return name && typeof name === 'string'; +} + +function arrayDefaultKey(name) { + var lastChar = name.length - 1; + if(name.charAt(lastChar) !== 's') { + Lib.warn('bad argument to arrayDefaultKey: ' + name); + } + return name.substr(0, name.length - 1) + 'defaults'; +} +exports.arrayDefaultKey = arrayDefaultKey; + +/** + * arrayEditor: helper for editing array items that may have come from + * template defaults (in which case they will not exist in the input yet) + * + * @param {object} parentIn: the input container (eg gd.layout) + * @param {string} containerStr: the attribute string for the container inside + * `parentIn`. + * @param {object} itemOut: the _full* item (eg gd._fullLayout.annotations[0]) + * that we'll be editing. Assumed to have been created by `arrayTemplater`. + * + * @returns {object}: {modifyBase, modifyItem, getUpdateObj, applyUpdate}, all functions: + * modifyBase(attr, value): Add an update that's *not* related to the item. + * `attr` is the full attribute string. + * modifyItem(attr, value): Add an update to the item. `attr` is just the + * portion of the attribute string inside the item. + * getUpdateObj(): Get the final constructed update object, to use in + * `restyle` or `relayout`. Also resets the update object in case this + * update was canceled. + * applyUpdate(attr, value): optionally add an update `attr: value`, + * then apply it to `parent` which should be the parent of `containerIn`, + * ie the object to which `containerStr` is the attribute string. + */ +exports.arrayEditor = function(parentIn, containerStr, itemOut) { + var lengthIn = (Lib.nestedProperty(parentIn, containerStr).get() || []).length; + var index = itemOut._index; + // Check that we are indeed off the end of this container. + // Otherwise a devious user could put a key `_templateitemname` in their + // own input and break lots of things. + var templateItemName = (index >= lengthIn) && (itemOut._input || {})._templateitemname; + if(templateItemName) index = lengthIn; + var itemStr = containerStr + '[' + index + ']'; + + var update; + function resetUpdate() { + update = {}; + if(templateItemName) { + update[itemStr] = {}; + update[itemStr][TEMPLATEITEMNAME] = templateItemName; + } + } + resetUpdate(); + + function modifyBase(attr, value) { + update[attr] = value; + } + + function modifyItem(attr, value) { + if(templateItemName) { + // we're making a new object: edit that object + Lib.nestedProperty(update[itemStr], attr).set(value); + } else { + // we're editing an existing object: include *just* the edit + update[itemStr + '.' + attr] = value; + } + } + + function getUpdateObj() { + var updateOut = update; + resetUpdate(); + return updateOut; + } + + function applyUpdate(attr, value) { + if(attr) modifyItem(attr, value); + var updateToApply = getUpdateObj(); + for(var key in updateToApply) { + Lib.nestedProperty(parentIn, key).set(updateToApply[key]); + } + } + + return { + modifyBase: modifyBase, + modifyItem: modifyItem, + getUpdateObj: getUpdateObj, + applyUpdate: applyUpdate + }; +}; + +},{"../lib":285,"../plots/attributes":327}],321:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Registry = _dereq_('../registry'); +var Plots = _dereq_('../plots/plots'); + +var Lib = _dereq_('../lib'); +var clearGlCanvases = _dereq_('../lib/clear_gl_canvases'); + +var Color = _dereq_('../components/color'); +var Drawing = _dereq_('../components/drawing'); +var Titles = _dereq_('../components/titles'); +var ModeBar = _dereq_('../components/modebar'); + +var Axes = _dereq_('../plots/cartesian/axes'); +var alignmentConstants = _dereq_('../constants/alignment'); +var axisConstraints = _dereq_('../plots/cartesian/constraints'); +var enforceAxisConstraints = axisConstraints.enforce; +var cleanAxisConstraints = axisConstraints.clean; +var doAutoRange = _dereq_('../plots/cartesian/autorange').doAutoRange; + +var SVG_TEXT_ANCHOR_START = 'start'; +var SVG_TEXT_ANCHOR_MIDDLE = 'middle'; +var SVG_TEXT_ANCHOR_END = 'end'; + +exports.layoutStyles = function(gd) { + return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd); +}; + +function overlappingDomain(xDomain, yDomain, domains) { + for(var i = 0; i < domains.length; i++) { + var existingX = domains[i][0]; + var existingY = domains[i][1]; + + if(existingX[0] >= xDomain[1] || existingX[1] <= xDomain[0]) { + continue; + } + if(existingY[0] < yDomain[1] && existingY[1] > yDomain[0]) { + return true; + } + } + return false; +} + +function lsInner(gd) { + var fullLayout = gd._fullLayout; + var gs = fullLayout._size; + var pad = gs.p; + var axList = Axes.list(gd, '', true); + var i, subplot, plotinfo, ax, xa, ya; + + fullLayout._paperdiv.style({ + width: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroWidth && !gd.layout.width) ? '100%' : fullLayout.width + 'px', + height: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroHeight && !gd.layout.height) ? '100%' : fullLayout.height + 'px' + }) + .selectAll('.main-svg') + .call(Drawing.setSize, fullLayout.width, fullLayout.height); + gd._context.setBackground(gd, fullLayout.paper_bgcolor); + + exports.drawMainTitle(gd); + ModeBar.manage(gd); + + // _has('cartesian') means SVG specifically, not GL2D - but GL2D + // can still get here because it makes some of the SVG structure + // for shared features like selections. + if(!fullLayout._has('cartesian')) { + return Plots.previousPromises(gd); + } + + function getLinePosition(ax, counterAx, side) { + var lwHalf = ax._lw / 2; + + if(ax._id.charAt(0) === 'x') { + if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1); + else if(side === 'top') return counterAx._offset - pad - lwHalf; + return counterAx._offset + counterAx._length + pad + lwHalf; + } + + if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1); + else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf; + return counterAx._offset - pad - lwHalf; + } + + // some preparation of axis position info + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + + var counterAx = ax._anchorAxis; + + // clear axis line positions, to be set in the subplot loop below + ax._linepositions = {}; + + // stash crispRounded linewidth so we don't need to pass gd all over the place + ax._lw = Drawing.crispRound(gd, ax.linewidth, 1); + + // figure out the main axis line and main mirror line position. + // it's easier to follow the logic if we handle these separately from + // ax._linepositions, which are only used by mirror=allticks + // for non-main-subplot ticks, and mirror=all(ticks)? for zero line + // hiding logic + ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side); + ax._mainMirrorPosition = (ax.mirror && counterAx) ? + getLinePosition(ax, counterAx, + alignmentConstants.OPPOSITE_SIDE[ax.side]) : null; + } + + // figure out which backgrounds we need to draw, + // and in which layers to put them + var lowerBackgroundIDs = []; + var backgroundIds = []; + var lowerDomains = []; + // no need to draw background when paper and plot color are the same color, + // activate mode just for large splom (which benefit the most from this + // optimization), but this could apply to all cartesian subplots. + var noNeedForBg = ( + Color.opacity(fullLayout.paper_bgcolor) === 1 && + Color.opacity(fullLayout.plot_bgcolor) === 1 && + fullLayout.paper_bgcolor === fullLayout.plot_bgcolor + ); + + for(subplot in fullLayout._plots) { + plotinfo = fullLayout._plots[subplot]; + + if(plotinfo.mainplot) { + // mainplot is a reference to the main plot this one is overlaid on + // so if it exists, this is an overlaid plot and we don't need to + // give it its own background + if(plotinfo.bg) { + plotinfo.bg.remove(); + } + plotinfo.bg = undefined; + } else { + var xDomain = plotinfo.xaxis.domain; + var yDomain = plotinfo.yaxis.domain; + var plotgroup = plotinfo.plotgroup; + + if(overlappingDomain(xDomain, yDomain, lowerDomains)) { + var pgNode = plotgroup.node(); + var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg'); + pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]); + backgroundIds.push(subplot); + } else { + plotgroup.select('rect.bg').remove(); + lowerDomains.push([xDomain, yDomain]); + if(!noNeedForBg) { + lowerBackgroundIDs.push(subplot); + backgroundIds.push(subplot); + } + } + } + } + + // now create all the lower-layer backgrounds at once now that + // we have the list of subplots that need them + var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg') + .data(lowerBackgroundIDs); + + lowerBackgrounds.enter().append('rect') + .classed('bg', true); + + lowerBackgrounds.exit().remove(); + + lowerBackgrounds.each(function(subplot) { + fullLayout._plots[subplot].bg = d3.select(this); + }); + + // style all backgrounds + for(i = 0; i < backgroundIds.length; i++) { + plotinfo = fullLayout._plots[backgroundIds[i]]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + if(plotinfo.bg && xa._offset !== undefined && ya._offset !== undefined) { + plotinfo.bg + .call(Drawing.setRect, + xa._offset - pad, ya._offset - pad, + xa._length + 2 * pad, ya._length + 2 * pad) + .call(Color.fill, fullLayout.plot_bgcolor) + .style('stroke-width', 0); + } + } + + if(!fullLayout._hasOnlyLargeSploms) { + for(subplot in fullLayout._plots) { + plotinfo = fullLayout._plots[subplot]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + // Clip so that data only shows up on the plot area. + var clipId = plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot'; + + var plotClip = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) { + s.classed('plotclip', true) + .append('rect'); + }); + + plotinfo.clipRect = plotClip.select('rect').attr({ + width: xa._length, + height: ya._length + }); + + Drawing.setTranslate(plotinfo.plot, xa._offset, ya._offset); + + var plotClipId; + var layerClipId; + + if(plotinfo._hasClipOnAxisFalse) { + plotClipId = null; + layerClipId = clipId; + } else { + plotClipId = clipId; + layerClipId = null; + } + + Drawing.setClipUrl(plotinfo.plot, plotClipId, gd); + + // stash layer clipId value (null or same as clipId) + // to DRY up Drawing.setClipUrl calls on trace-module and trace layers + // downstream + plotinfo.layerClipId = layerClipId; + } + } + + var xLinesXLeft, xLinesXRight, xLinesYBottom, xLinesYTop, + leftYLineWidth, rightYLineWidth; + var yLinesYBottom, yLinesYTop, yLinesXLeft, yLinesXRight, + connectYBottom, connectYTop; + var extraSubplot; + + function xLinePath(y) { + return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight; + } + + function xLinePathFree(y) { + return 'M' + xa._offset + ',' + y + 'h' + xa._length; + } + + function yLinePath(x) { + return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom; + } + + function yLinePathFree(x) { + return 'M' + x + ',' + ya._offset + 'v' + ya._length; + } + + function mainPath(ax, pathFn, pathFnFree) { + if(!ax.showline || subplot !== ax._mainSubplot) return ''; + if(!ax._anchorAxis) return pathFnFree(ax._mainLinePosition); + var out = pathFn(ax._mainLinePosition); + if(ax.mirror) out += pathFn(ax._mainMirrorPosition); + return out; + } + + for(subplot in fullLayout._plots) { + plotinfo = fullLayout._plots[subplot]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + /* + * x lines get longer where they meet y lines, to make a crisp corner. + * The x lines get the padding (margin.pad) plus the y line width to + * fill up the corner nicely. Free x lines are excluded - they always + * span exactly the data area of the plot + * + * | XXXXX + * | XXXXX + * | + * +------ + * x1 + * ----- + * x2 + */ + var xPath = 'M0,0'; + if(shouldShowLinesOrTicks(xa, subplot)) { + leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList); + xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0); + rightYLineWidth = findCounterAxisLineWidth(xa, 'right', ya, axList); + xLinesXRight = xa._offset + xa._length + (rightYLineWidth ? (pad + rightYLineWidth) : 0); + xLinesYBottom = getLinePosition(xa, ya, 'bottom'); + xLinesYTop = getLinePosition(xa, ya, 'top'); + + // save axis line positions for extra ticks to reference + // each subplot that gets ticks from "allticks" gets an entry: + // [left or bottom, right or top] + extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot); + if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) { + xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop]; + } + + xPath = mainPath(xa, xLinePath, xLinePathFree); + if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) { + xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop); + } + + plotinfo.xlines + .style('stroke-width', xa._lw + 'px') + .call(Color.stroke, xa.showline ? + xa.linecolor : 'rgba(0,0,0,0)'); + } + plotinfo.xlines.attr('d', xPath); + + /* + * y lines that meet x axes get longer only by margin.pad, because + * the x axes fill in the corner space. Free y axes, like free x axes, + * always span exactly the data area of the plot + * + * | | XXXX + * y2| y1| XXXX + * | | XXXX + * | + * +----- + */ + var yPath = 'M0,0'; + if(shouldShowLinesOrTicks(ya, subplot)) { + connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList); + yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0); + connectYTop = findCounterAxisLineWidth(ya, 'top', xa, axList); + yLinesYTop = ya._offset - (connectYTop ? pad : 0); + yLinesXLeft = getLinePosition(ya, xa, 'left'); + yLinesXRight = getLinePosition(ya, xa, 'right'); + + extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot); + if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) { + ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight]; + } + + yPath = mainPath(ya, yLinePath, yLinePathFree); + if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) { + yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight); + } + + plotinfo.ylines + .style('stroke-width', ya._lw + 'px') + .call(Color.stroke, ya.showline ? + ya.linecolor : 'rgba(0,0,0,0)'); + } + plotinfo.ylines.attr('d', yPath); + } + + Axes.makeClipPaths(gd); + + return Plots.previousPromises(gd); +} + +function shouldShowLinesOrTicks(ax, subplot) { + return (ax.ticks || ax.showline) && + (subplot === ax._mainSubplot || ax.mirror === 'all' || ax.mirror === 'allticks'); +} + +/* + * should we draw a line on counterAx at this side of ax? + * It's assumed that counterAx is known to overlay the subplot we're working on + * but it may not be its main axis. + */ +function shouldShowLineThisSide(ax, side, counterAx) { + // does counterAx get a line at all? + if(!counterAx.showline || !counterAx._lw) return false; + + // are we drawing *all* lines for counterAx? + if(counterAx.mirror === 'all' || counterAx.mirror === 'allticks') return true; + + var anchorAx = counterAx._anchorAxis; + + // is this a free axis? free axes can only have a subplot side-line with all(ticks)? mirroring + if(!anchorAx) return false; + + // in order to handle cases where the user forgot to anchor this axis correctly + // (because its default anchor has the same domain on the relevant end) + // check whether the relevant position is the same. + var sideIndex = alignmentConstants.FROM_BL[side]; + if(counterAx.side === side) { + return anchorAx.domain[sideIndex] === ax.domain[sideIndex]; + } + return counterAx.mirror && anchorAx.domain[1 - sideIndex] === ax.domain[1 - sideIndex]; +} + +/* + * Is there another axis intersecting `side` end of `ax`? + * First look at `counterAx` (the axis for this subplot), + * then at all other potential counteraxes on or overlaying this subplot. + * Take the line width from the first one that has a line. + */ +function findCounterAxisLineWidth(ax, side, counterAx, axList) { + if(shouldShowLineThisSide(ax, side, counterAx)) { + return counterAx._lw; + } + for(var i = 0; i < axList.length; i++) { + var axi = axList[i]; + if(axi._mainAxis === counterAx._mainAxis && shouldShowLineThisSide(ax, side, axi)) { + return axi._lw; + } + } + return 0; +} + +exports.drawMainTitle = function(gd) { + var fullLayout = gd._fullLayout; + + var textAnchor = getMainTitleTextAnchor(fullLayout); + var dy = getMainTitleDy(fullLayout); + + Titles.draw(gd, 'gtitle', { + propContainer: fullLayout, + propName: 'title.text', + placeholder: fullLayout._dfltTitle.plot, + attributes: { + x: getMainTitleX(fullLayout, textAnchor), + y: getMainTitleY(fullLayout, dy), + 'text-anchor': textAnchor, + dy: dy + } + }); +}; + +function getMainTitleX(fullLayout, textAnchor) { + var title = fullLayout.title; + var gs = fullLayout._size; + var hPadShift = 0; + + if(textAnchor === SVG_TEXT_ANCHOR_START) { + hPadShift = title.pad.l; + } else if(textAnchor === SVG_TEXT_ANCHOR_END) { + hPadShift = -title.pad.r; + } + + switch(title.xref) { + case 'paper': + return gs.l + gs.w * title.x + hPadShift; + case 'container': + default: + return fullLayout.width * title.x + hPadShift; + } +} + +function getMainTitleY(fullLayout, dy) { + var title = fullLayout.title; + var gs = fullLayout._size; + var vPadShift = 0; + + if(dy === '0em' || !dy) { + vPadShift = -title.pad.b; + } else if(dy === alignmentConstants.CAP_SHIFT + 'em') { + vPadShift = title.pad.t; + } + + if(title.y === 'auto') { + return gs.t / 2; + } else { + switch(title.yref) { + case 'paper': + return gs.t + gs.h - gs.h * title.y + vPadShift; + case 'container': + default: + return fullLayout.height - fullLayout.height * title.y + vPadShift; + } + } +} + +function getMainTitleTextAnchor(fullLayout) { + var title = fullLayout.title; + + var textAnchor = SVG_TEXT_ANCHOR_MIDDLE; + if(Lib.isRightAnchor(title)) { + textAnchor = SVG_TEXT_ANCHOR_END; + } else if(Lib.isLeftAnchor(title)) { + textAnchor = SVG_TEXT_ANCHOR_START; + } + + return textAnchor; +} + +function getMainTitleDy(fullLayout) { + var title = fullLayout.title; + + var dy = '0em'; + if(Lib.isTopAnchor(title)) { + dy = alignmentConstants.CAP_SHIFT + 'em'; + } else if(Lib.isMiddleAnchor(title)) { + dy = alignmentConstants.MID_SHIFT + 'em'; + } + + return dy; +} + +exports.doTraceStyle = function(gd) { + var calcdata = gd.calcdata; + var editStyleCalls = []; + var i; + + for(i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var cd0 = cd[0] || {}; + var trace = cd0.trace || {}; + var _module = trace._module || {}; + + // See if we need to do arraysToCalcdata + // call it regardless of what change we made, in case + // supplyDefaults brought in an array that was already + // in gd.data but not in gd._fullData previously + var arraysToCalcdata = _module.arraysToCalcdata; + if(arraysToCalcdata) arraysToCalcdata(cd, trace); + + var editStyle = _module.editStyle; + if(editStyle) editStyleCalls.push({fn: editStyle, cd0: cd0}); + } + + if(editStyleCalls.length) { + for(i = 0; i < editStyleCalls.length; i++) { + var edit = editStyleCalls[i]; + edit.fn(gd, edit.cd0); + } + clearGlCanvases(gd); + exports.redrawReglTraces(gd); + } + + Plots.style(gd); + Registry.getComponentMethod('legend', 'draw')(gd); + + return Plots.previousPromises(gd); +}; + +exports.doColorBars = function(gd) { + Registry.getComponentMethod('colorbar', 'draw')(gd); + return Plots.previousPromises(gd); +}; + +// force plot() to redo the layout and replot with the modified layout +exports.layoutReplot = function(gd) { + var layout = gd.layout; + gd.layout = undefined; + return Registry.call('_doPlot', gd, '', layout); +}; + +exports.doLegend = function(gd) { + Registry.getComponentMethod('legend', 'draw')(gd); + return Plots.previousPromises(gd); +}; + +exports.doTicksRelayout = function(gd) { + Axes.draw(gd, 'redraw'); + + if(gd._fullLayout._hasOnlyLargeSploms) { + Registry.subplotsRegistry.splom.updateGrid(gd); + clearGlCanvases(gd); + exports.redrawReglTraces(gd); + } + + exports.drawMainTitle(gd); + return Plots.previousPromises(gd); +}; + +exports.doModeBar = function(gd) { + var fullLayout = gd._fullLayout; + + ModeBar.manage(gd); + + for(var i = 0; i < fullLayout._basePlotModules.length; i++) { + var updateFx = fullLayout._basePlotModules[i].updateFx; + if(updateFx) updateFx(gd); + } + + return Plots.previousPromises(gd); +}; + +exports.doCamera = function(gd) { + var fullLayout = gd._fullLayout; + var sceneIds = fullLayout._subplots.gl3d; + + for(var i = 0; i < sceneIds.length; i++) { + var sceneLayout = fullLayout[sceneIds[i]]; + var scene = sceneLayout._scene; + + scene.setViewport(sceneLayout); + } +}; + +exports.drawData = function(gd) { + var fullLayout = gd._fullLayout; + + clearGlCanvases(gd); + + // loop over the base plot modules present on graph + var basePlotModules = fullLayout._basePlotModules; + for(var i = 0; i < basePlotModules.length; i++) { + basePlotModules[i].plot(gd); + } + + exports.redrawReglTraces(gd); + + // styling separate from drawing + Plots.style(gd); + + // draw components that can be drawn on axes, + // and that do not push the margins + Registry.getComponentMethod('shapes', 'draw')(gd); + Registry.getComponentMethod('annotations', 'draw')(gd); + Registry.getComponentMethod('images', 'draw')(gd); + + // Mark the first render as complete + fullLayout._replotting = false; + + return Plots.previousPromises(gd); +}; + +// Draw (or redraw) all regl-based traces in one go, +// useful during drag and selection where buffers of targeted traces are updated, +// but all traces need to be redrawn following clearGlCanvases. +// +// Note that _module.plot for regl trace does NOT draw things +// on the canvas, they only update the buffers. +// Drawing is perform here. +// +// TODO try adding per-subplot option using gl.SCISSOR_TEST for +// non-overlaying, disjoint subplots. +// +// TODO try to include parcoords in here. +// https://github.com/plotly/plotly.js/issues/3069 +exports.redrawReglTraces = function(gd) { + var fullLayout = gd._fullLayout; + + if(fullLayout._has('regl')) { + var fullData = gd._fullData; + var cartesianIds = []; + var polarIds = []; + var i, sp; + + if(fullLayout._hasOnlyLargeSploms) { + fullLayout._splomGrid.draw(); + } + + // N.B. + // - Loop over fullData (not _splomScenes) to preserve splom trace-to-trace ordering + // - Fill list if subplot ids (instead of fullLayout._subplots) to handle cases where all traces + // of a given module are `visible !== true` + for(i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + + if(trace.visible === true && trace._length !== 0) { + if(trace.type === 'splom') { + fullLayout._splomScenes[trace.uid].draw(); + } else if(trace.type === 'scattergl') { + Lib.pushUnique(cartesianIds, trace.xaxis + trace.yaxis); + } else if(trace.type === 'scatterpolargl') { + Lib.pushUnique(polarIds, trace.subplot); + } + } + } + + for(i = 0; i < cartesianIds.length; i++) { + sp = fullLayout._plots[cartesianIds[i]]; + if(sp._scene) sp._scene.draw(); + } + + for(i = 0; i < polarIds.length; i++) { + sp = fullLayout[polarIds[i]]._subplot; + if(sp._scene) sp._scene.draw(); + } + } +}; + +exports.doAutoRangeAndConstraints = function(gd) { + var axList = Axes.list(gd, '', true); + var ax; + + var autoRangeDone = {}; + + for(var i = 0; i < axList.length; i++) { + ax = axList[i]; + + if(!autoRangeDone[ax._id]) { + autoRangeDone[ax._id] = 1; + cleanAxisConstraints(gd, ax); + doAutoRange(gd, ax); + + // For matching axes, just propagate this autorange to the group. + // The extra arg to doAutoRange avoids recalculating the range, + // since doAutoRange by itself accounts for all matching axes. but + // there are other side-effects of doAutoRange that we still want. + var matchGroup = ax._matchGroup; + if(matchGroup) { + for(var id2 in matchGroup) { + var ax2 = Axes.getFromId(gd, id2); + doAutoRange(gd, ax2, ax.range); + autoRangeDone[id2] = 1; + } + } + } + } + + enforceAxisConstraints(gd); +}; + +// An initial paint must be completed before these components can be +// correctly sized and the whole plot re-margined. fullLayout._replotting must +// be set to false before these will work properly. +exports.finalDraw = function(gd) { + // TODO: rangesliders really belong in marginPushers but they need to be + // drawn after data - can we at least get the margin pushing part separated + // out and done earlier? + Registry.getComponentMethod('rangeslider', 'draw')(gd); + // TODO: rangeselector only needs to be here (in addition to drawMarginPushers) + // because the margins need to be fully determined before we can call + // autorange and update axis ranges (which rangeselector needs to know which + // button is active). Can we break out its automargin step from its draw step? + Registry.getComponentMethod('rangeselector', 'draw')(gd); +}; + +exports.drawMarginPushers = function(gd) { + Registry.getComponentMethod('legend', 'draw')(gd); + Registry.getComponentMethod('rangeselector', 'draw')(gd); + Registry.getComponentMethod('sliders', 'draw')(gd); + Registry.getComponentMethod('updatemenus', 'draw')(gd); + Registry.getComponentMethod('colorbar', 'draw')(gd); +}; + +},{"../components/color":155,"../components/drawing":177,"../components/modebar":218,"../components/titles":253,"../constants/alignment":260,"../lib":285,"../lib/clear_gl_canvases":273,"../plots/cartesian/autorange":330,"../plots/cartesian/axes":331,"../plots/cartesian/constraints":339,"../plots/plots":366,"../registry":373,"@plotly/d3":20}],322:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var isPlainObject = Lib.isPlainObject; +var PlotSchema = _dereq_('./plot_schema'); +var Plots = _dereq_('../plots/plots'); +var plotAttributes = _dereq_('../plots/attributes'); +var Template = _dereq_('./plot_template'); +var dfltConfig = _dereq_('./plot_config').dfltConfig; + +/** + * Plotly.makeTemplate: create a template off an existing figure to reuse + * style attributes on other figures. + * + * Note: separated from the rest of templates because otherwise we get circular + * references due to PlotSchema. + * + * @param {object|DOM element|string} figure: The figure to base the template on + * should contain a trace array `figure.data` + * and a layout object `figure.layout` + * @returns {object} template: the extracted template - can then be used as + * `layout.template` in another figure. + */ +exports.makeTemplate = function(figure) { + figure = Lib.isPlainObject(figure) ? figure : Lib.getGraphDiv(figure); + figure = Lib.extendDeep({_context: dfltConfig}, {data: figure.data, layout: figure.layout}); + Plots.supplyDefaults(figure); + var data = figure.data || []; + var layout = figure.layout || {}; + // copy over a few items to help follow the schema + layout._basePlotModules = figure._fullLayout._basePlotModules; + layout._modules = figure._fullLayout._modules; + + var template = { + data: {}, + layout: {} + }; + + /* + * Note: we do NOT validate template values, we just take what's in the + * user inputs data and layout, not the validated values in fullData and + * fullLayout. Even if we were to validate here, there's no guarantee that + * these values would still be valid when applied to a new figure, which + * may contain different trace modes, different axes, etc. So it's + * important that when applying a template we still validate the template + * values, rather than just using them as defaults. + */ + + data.forEach(function(trace) { + // TODO: What if no style info is extracted for this trace. We may + // not want an empty object as the null value. + // TODO: allow transforms to contribute to templates? + // as it stands they are ignored, which may be for the best... + + var traceTemplate = {}; + walkStyleKeys(trace, traceTemplate, getTraceInfo.bind(null, trace)); + + var traceType = Lib.coerce(trace, {}, plotAttributes, 'type'); + var typeTemplates = template.data[traceType]; + if(!typeTemplates) typeTemplates = template.data[traceType] = []; + typeTemplates.push(traceTemplate); + }); + + walkStyleKeys(layout, template.layout, getLayoutInfo.bind(null, layout)); + + /* + * Compose the new template with an existing one to the same effect + * + * NOTE: there's a possibility of slightly different behavior: if the plot + * has an invalid value and the old template has a valid value for the same + * attribute, the plot will use the old template value but this routine + * will pull the invalid value (resulting in the original default). + * In the general case it's not possible to solve this with a single value, + * since valid options can be context-dependent. It could be solved with + * a *list* of values, but that would be huge complexity for little gain. + */ + delete template.layout.template; + var oldTemplate = layout.template; + if(isPlainObject(oldTemplate)) { + var oldLayoutTemplate = oldTemplate.layout; + + var i, traceType, oldTypeTemplates, oldTypeLen, typeTemplates, typeLen; + + if(isPlainObject(oldLayoutTemplate)) { + mergeTemplates(oldLayoutTemplate, template.layout); + } + var oldDataTemplate = oldTemplate.data; + if(isPlainObject(oldDataTemplate)) { + for(traceType in template.data) { + oldTypeTemplates = oldDataTemplate[traceType]; + if(Array.isArray(oldTypeTemplates)) { + typeTemplates = template.data[traceType]; + typeLen = typeTemplates.length; + oldTypeLen = oldTypeTemplates.length; + for(i = 0; i < typeLen; i++) { + mergeTemplates(oldTypeTemplates[i % oldTypeLen], typeTemplates[i]); + } + for(i = typeLen; i < oldTypeLen; i++) { + typeTemplates.push(Lib.extendDeep({}, oldTypeTemplates[i])); + } + } + } + for(traceType in oldDataTemplate) { + if(!(traceType in template.data)) { + template.data[traceType] = Lib.extendDeep([], oldDataTemplate[traceType]); + } + } + } + } + + return template; +}; + +function mergeTemplates(oldTemplate, newTemplate) { + // we don't care about speed here, just make sure we have a totally + // distinct object from the previous template + oldTemplate = Lib.extendDeep({}, oldTemplate); + + // sort keys so we always get annotationdefaults before annotations etc + // so arrayTemplater will work right + var oldKeys = Object.keys(oldTemplate).sort(); + var i, j; + + function mergeOne(oldVal, newVal, key) { + if(isPlainObject(newVal) && isPlainObject(oldVal)) { + mergeTemplates(oldVal, newVal); + } else if(Array.isArray(newVal) && Array.isArray(oldVal)) { + // Note: omitted `inclusionAttr` from arrayTemplater here, + // it's irrelevant as we only want the resulting `_template`. + var templater = Template.arrayTemplater({_template: oldTemplate}, key); + for(j = 0; j < newVal.length; j++) { + var item = newVal[j]; + var oldItem = templater.newItem(item)._template; + if(oldItem) mergeTemplates(oldItem, item); + } + var defaultItems = templater.defaultItems(); + for(j = 0; j < defaultItems.length; j++) newVal.push(defaultItems[j]._template); + + // templateitemname only applies to receiving plots + for(j = 0; j < newVal.length; j++) delete newVal[j].templateitemname; + } + } + + for(i = 0; i < oldKeys.length; i++) { + var key = oldKeys[i]; + var oldVal = oldTemplate[key]; + if(key in newTemplate) { + mergeOne(oldVal, newTemplate[key], key); + } else newTemplate[key] = oldVal; + + // if this is a base key from the old template (eg xaxis), look for + // extended keys (eg xaxis2) in the new template to merge into + if(getBaseKey(key) === key) { + for(var key2 in newTemplate) { + var baseKey2 = getBaseKey(key2); + if(key2 !== baseKey2 && baseKey2 === key && !(key2 in oldTemplate)) { + mergeOne(oldVal, newTemplate[key2], key); + } + } + } + } +} + +function getBaseKey(key) { + return key.replace(/[0-9]+$/, ''); +} + +function walkStyleKeys(parent, templateOut, getAttributeInfo, path, basePath) { + var pathAttr = basePath && getAttributeInfo(basePath); + for(var key in parent) { + var child = parent[key]; + var nextPath = getNextPath(parent, key, path); + var nextBasePath = getNextPath(parent, key, basePath); + var attr = getAttributeInfo(nextBasePath); + if(!attr) { + var baseKey = getBaseKey(key); + if(baseKey !== key) { + nextBasePath = getNextPath(parent, baseKey, basePath); + attr = getAttributeInfo(nextBasePath); + } + } + + // we'll get an attr if path starts with a valid part, then has an + // invalid ending. Make sure we got all the way to the end. + if(pathAttr && (pathAttr === attr)) continue; + + if(!attr || attr._noTemplating || + attr.valType === 'data_array' || + (attr.arrayOk && Array.isArray(child)) + ) { + continue; + } + + if(!attr.valType && isPlainObject(child)) { + walkStyleKeys(child, templateOut, getAttributeInfo, nextPath, nextBasePath); + } else if(attr._isLinkedToArray && Array.isArray(child)) { + var dfltDone = false; + var namedIndex = 0; + var usedNames = {}; + for(var i = 0; i < child.length; i++) { + var item = child[i]; + if(isPlainObject(item)) { + var name = item.name; + if(name) { + if(!usedNames[name]) { + // named array items: allow all attributes except data arrays + walkStyleKeys(item, templateOut, getAttributeInfo, + getNextPath(child, namedIndex, nextPath), + getNextPath(child, namedIndex, nextBasePath)); + namedIndex++; + usedNames[name] = 1; + } + } else if(!dfltDone) { + var dfltKey = Template.arrayDefaultKey(key); + var dfltPath = getNextPath(parent, dfltKey, path); + + // getAttributeInfo will fail if we try to use dfltKey directly. + // Instead put this item into the next array element, then + // pull it out and move it to dfltKey. + var pathInArray = getNextPath(child, namedIndex, nextPath); + walkStyleKeys(item, templateOut, getAttributeInfo, pathInArray, + getNextPath(child, namedIndex, nextBasePath)); + var itemPropInArray = Lib.nestedProperty(templateOut, pathInArray); + var dfltProp = Lib.nestedProperty(templateOut, dfltPath); + dfltProp.set(itemPropInArray.get()); + itemPropInArray.set(null); + + dfltDone = true; + } + } + } + } else { + var templateProp = Lib.nestedProperty(templateOut, nextPath); + templateProp.set(child); + } + } +} + +function getLayoutInfo(layout, path) { + return PlotSchema.getLayoutValObject( + layout, Lib.nestedProperty({}, path).parts + ); +} + +function getTraceInfo(trace, path) { + return PlotSchema.getTraceValObject( + trace, Lib.nestedProperty({}, path).parts + ); +} + +function getNextPath(parent, key, path) { + var nextPath; + if(!path) nextPath = key; + else if(Array.isArray(parent)) nextPath = path + '[' + key + ']'; + else nextPath = path + '.' + key; + + return nextPath; +} + +/** + * validateTemplate: Test for consistency between the given figure and + * a template, either already included in the figure or given separately. + * Note that not every issue we identify here is necessarily a problem, + * it depends on what you're using the template for. + * + * @param {object|DOM element} figure: the plot, with {data, layout} members, + * to test the template against + * @param {Optional(object)} template: the template, with its own {data, layout}, + * to test. If omitted, we will look for a template already attached as the + * plot's `layout.template` attribute. + * + * @returns {array} array of error objects each containing: + * - {string} code + * error code ('missing', 'unused', 'reused', 'noLayout', 'noData') + * - {string} msg + * a full readable description of the issue. + */ +exports.validateTemplate = function(figureIn, template) { + var figure = Lib.extendDeep({}, { + _context: dfltConfig, + data: figureIn.data, + layout: figureIn.layout + }); + var layout = figure.layout || {}; + if(!isPlainObject(template)) template = layout.template || {}; + var layoutTemplate = template.layout; + var dataTemplate = template.data; + var errorList = []; + + figure.layout = layout; + figure.layout.template = template; + Plots.supplyDefaults(figure); + + var fullLayout = figure._fullLayout; + var fullData = figure._fullData; + + var layoutPaths = {}; + function crawlLayoutForContainers(obj, paths) { + for(var key in obj) { + if(key.charAt(0) !== '_' && isPlainObject(obj[key])) { + var baseKey = getBaseKey(key); + var nextPaths = []; + var i; + for(i = 0; i < paths.length; i++) { + nextPaths.push(getNextPath(obj, key, paths[i])); + if(baseKey !== key) nextPaths.push(getNextPath(obj, baseKey, paths[i])); + } + for(i = 0; i < nextPaths.length; i++) { + layoutPaths[nextPaths[i]] = 1; + } + crawlLayoutForContainers(obj[key], nextPaths); + } + } + } + + function crawlLayoutTemplateForContainers(obj, path) { + for(var key in obj) { + if(key.indexOf('defaults') === -1 && isPlainObject(obj[key])) { + var nextPath = getNextPath(obj, key, path); + if(layoutPaths[nextPath]) { + crawlLayoutTemplateForContainers(obj[key], nextPath); + } else { + errorList.push({code: 'unused', path: nextPath}); + } + } + } + } + + if(!isPlainObject(layoutTemplate)) { + errorList.push({code: 'layout'}); + } else { + crawlLayoutForContainers(fullLayout, ['layout']); + crawlLayoutTemplateForContainers(layoutTemplate, 'layout'); + } + + if(!isPlainObject(dataTemplate)) { + errorList.push({code: 'data'}); + } else { + var typeCount = {}; + var traceType; + for(var i = 0; i < fullData.length; i++) { + var fullTrace = fullData[i]; + traceType = fullTrace.type; + typeCount[traceType] = (typeCount[traceType] || 0) + 1; + if(!fullTrace._fullInput._template) { + // this takes care of the case of traceType in the data but not + // the template + errorList.push({ + code: 'missing', + index: fullTrace._fullInput.index, + traceType: traceType + }); + } + } + for(traceType in dataTemplate) { + var templateCount = dataTemplate[traceType].length; + var dataCount = typeCount[traceType] || 0; + if(templateCount > dataCount) { + errorList.push({ + code: 'unused', + traceType: traceType, + templateCount: templateCount, + dataCount: dataCount + }); + } else if(dataCount > templateCount) { + errorList.push({ + code: 'reused', + traceType: traceType, + templateCount: templateCount, + dataCount: dataCount + }); + } + } + } + + // _template: false is when someone tried to modify an array item + // but there was no template with matching name + function crawlForMissingTemplates(obj, path) { + for(var key in obj) { + if(key.charAt(0) === '_') continue; + var val = obj[key]; + var nextPath = getNextPath(obj, key, path); + if(isPlainObject(val)) { + if(Array.isArray(obj) && val._template === false && val.templateitemname) { + errorList.push({ + code: 'missing', + path: nextPath, + templateitemname: val.templateitemname + }); + } + crawlForMissingTemplates(val, nextPath); + } else if(Array.isArray(val) && hasPlainObject(val)) { + crawlForMissingTemplates(val, nextPath); + } + } + } + crawlForMissingTemplates({data: fullData, layout: fullLayout}, ''); + + if(errorList.length) return errorList.map(format); +}; + +function hasPlainObject(arr) { + for(var i = 0; i < arr.length; i++) { + if(isPlainObject(arr[i])) return true; + } +} + +function format(opts) { + var msg; + switch(opts.code) { + case 'data': + msg = 'The template has no key data.'; + break; + case 'layout': + msg = 'The template has no key layout.'; + break; + case 'missing': + if(opts.path) { + msg = 'There are no templates for item ' + opts.path + + ' with name ' + opts.templateitemname; + } else { + msg = 'There are no templates for trace ' + opts.index + + ', of type ' + opts.traceType + '.'; + } + break; + case 'unused': + if(opts.path) { + msg = 'The template item at ' + opts.path + + ' was not used in constructing the plot.'; + } else if(opts.dataCount) { + msg = 'Some of the templates of type ' + opts.traceType + + ' were not used. The template has ' + opts.templateCount + + ' traces, the data only has ' + opts.dataCount + + ' of this type.'; + } else { + msg = 'The template has ' + opts.templateCount + + ' traces of type ' + opts.traceType + + ' but there are none in the data.'; + } + break; + case 'reused': + msg = 'Some of the templates of type ' + opts.traceType + + ' were used more than once. The template has ' + + opts.templateCount + ' traces, the data has ' + + opts.dataCount + ' of this type.'; + break; + } + opts.msg = msg; + + return opts; +} + +},{"../lib":285,"../plots/attributes":327,"../plots/plots":366,"./plot_config":318,"./plot_schema":319,"./plot_template":320}],323:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var plotApi = _dereq_('./plot_api'); +var plots = _dereq_('../plots/plots'); +var Lib = _dereq_('../lib'); + +var helpers = _dereq_('../snapshot/helpers'); +var toSVG = _dereq_('../snapshot/tosvg'); +var svgToImg = _dereq_('../snapshot/svgtoimg'); +var version = _dereq_('../version').version; + +var attrs = { + format: { + valType: 'enumerated', + values: ['png', 'jpeg', 'webp', 'svg', 'full-json'], + dflt: 'png', + }, + width: { + valType: 'number', + min: 1, + }, + height: { + valType: 'number', + min: 1, + }, + scale: { + valType: 'number', + min: 0, + dflt: 1, + }, + setBackground: { + valType: 'any', + dflt: false, + }, + imageDataOnly: { + valType: 'boolean', + dflt: false, + } +}; + +/** Plotly.toImage + * + * @param {object | string | HTML div} gd + * can either be a data/layout/config object + * or an existing graph
+ * or an id to an existing graph
+ * @param {object} opts (see above) + * @return {promise} + */ +function toImage(gd, opts) { + opts = opts || {}; + + var data; + var layout; + var config; + var fullLayout; + + if(Lib.isPlainObject(gd)) { + data = gd.data || []; + layout = gd.layout || {}; + config = gd.config || {}; + fullLayout = {}; + } else { + gd = Lib.getGraphDiv(gd); + data = Lib.extendDeep([], gd.data); + layout = Lib.extendDeep({}, gd.layout); + config = gd._context; + fullLayout = gd._fullLayout || {}; + } + + function isImpliedOrValid(attr) { + return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]); + } + + if((!isImpliedOrValid('width') && opts.width !== null) || + (!isImpliedOrValid('height') && opts.height !== null)) { + throw new Error('Height and width should be pixel values.'); + } + + if(!isImpliedOrValid('format')) { + throw new Error('Export format is not ' + Lib.join2(attrs.format.values, ', ', ' or ') + '.'); + } + + var fullOpts = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts, fullOpts, attrs, attr, dflt); + } + + var format = coerce('format'); + var width = coerce('width'); + var height = coerce('height'); + var scale = coerce('scale'); + var setBackground = coerce('setBackground'); + var imageDataOnly = coerce('imageDataOnly'); + + // put the cloned div somewhere off screen before attaching to DOM + var clonedGd = document.createElement('div'); + clonedGd.style.position = 'absolute'; + clonedGd.style.left = '-5000px'; + document.body.appendChild(clonedGd); + + // extend layout with image options + var layoutImage = Lib.extendFlat({}, layout); + if(width) { + layoutImage.width = width; + } else if(opts.width === null && isNumeric(fullLayout.width)) { + layoutImage.width = fullLayout.width; + } + if(height) { + layoutImage.height = height; + } else if(opts.height === null && isNumeric(fullLayout.height)) { + layoutImage.height = fullLayout.height; + } + + // extend config for static plot + var configImage = Lib.extendFlat({}, config, { + _exportedPlot: true, + staticPlot: true, + setBackground: setBackground + }); + + var redrawFunc = helpers.getRedrawFunc(clonedGd); + + function wait() { + return new Promise(function(resolve) { + setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout)); + }); + } + + function convert() { + return new Promise(function(resolve, reject) { + var svg = toSVG(clonedGd, format, scale); + var width = clonedGd._fullLayout.width; + var height = clonedGd._fullLayout.height; + + function cleanup() { + plotApi.purge(clonedGd); + document.body.removeChild(clonedGd); + } + + if(format === 'full-json') { + var json = plots.graphJson(clonedGd, false, 'keepdata', 'object', true, true); + json.version = version; + json = JSON.stringify(json); + cleanup(); + if(imageDataOnly) { + return resolve(json); + } else { + return resolve(helpers.encodeJSON(json)); + } + } + + cleanup(); + + if(format === 'svg') { + if(imageDataOnly) { + return resolve(svg); + } else { + return resolve(helpers.encodeSVG(svg)); + } + } + + var canvas = document.createElement('canvas'); + canvas.id = Lib.randstr(); + + svgToImg({ + format: format, + width: width, + height: height, + scale: scale, + canvas: canvas, + svg: svg, + // ask svgToImg to return a Promise + // rather than EventEmitter + // leave EventEmitter for backward + // compatibility + promise: true + }) + .then(resolve) + .catch(reject); + }); + } + + function urlToImageData(url) { + if(imageDataOnly) { + return url.replace(helpers.IMAGE_URL_PREFIX, ''); + } else { + return url; + } + } + + return new Promise(function(resolve, reject) { + plotApi.newPlot(clonedGd, data, layoutImage, configImage) + .then(redrawFunc) + .then(wait) + .then(convert) + .then(function(url) { resolve(urlToImageData(url)); }) + .catch(function(err) { reject(err); }); + }); +} + +module.exports = toImage; + +},{"../lib":285,"../plots/plots":366,"../snapshot/helpers":377,"../snapshot/svgtoimg":379,"../snapshot/tosvg":381,"../version":546,"./plot_api":317,"fast-isnumeric":31}],324:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var Plots = _dereq_('../plots/plots'); +var PlotSchema = _dereq_('./plot_schema'); +var dfltConfig = _dereq_('./plot_config').dfltConfig; + +var isPlainObject = Lib.isPlainObject; +var isArray = Array.isArray; +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; + +/** + * Validate a data array and layout object. + * + * @param {array} data + * @param {object} layout + * + * @return {array} array of error objects each containing: + * - {string} code + * error code ('object', 'array', 'schema', 'unused', 'invisible' or 'value') + * - {string} container + * container where the error occurs ('data' or 'layout') + * - {number} trace + * trace index of the 'data' container where the error occurs + * - {array} path + * nested path to the key that causes the error + * - {string} astr + * attribute string variant of 'path' compatible with Plotly.restyle and + * Plotly.relayout. + * - {string} msg + * error message (shown in console in logger config argument is enable) + */ +module.exports = function validate(data, layout) { + if(data === undefined) data = []; + if(layout === undefined) layout = {}; + + var schema = PlotSchema.get(); + var errorList = []; + var gd = {_context: Lib.extendFlat({}, dfltConfig)}; + + var dataIn, layoutIn; + + if(isArray(data)) { + gd.data = Lib.extendDeep([], data); + dataIn = data; + } else { + gd.data = []; + dataIn = []; + errorList.push(format('array', 'data')); + } + + if(isPlainObject(layout)) { + gd.layout = Lib.extendDeep({}, layout); + layoutIn = layout; + } else { + gd.layout = {}; + layoutIn = {}; + if(arguments.length > 1) { + errorList.push(format('object', 'layout')); + } + } + + // N.B. dataIn and layoutIn are in general not the same as + // gd.data and gd.layout after supplyDefaults as some attributes + // in gd.data and gd.layout (still) get mutated during this step. + + Plots.supplyDefaults(gd); + + var dataOut = gd._fullData; + var len = dataIn.length; + + for(var i = 0; i < len; i++) { + var traceIn = dataIn[i]; + var base = ['data', i]; + + if(!isPlainObject(traceIn)) { + errorList.push(format('object', base)); + continue; + } + + var traceOut = dataOut[i]; + var traceType = traceOut.type; + var traceSchema = schema.traces[traceType].attributes; + + // PlotSchema does something fancy with trace 'type', reset it here + // to make the trace schema compatible with Lib.validate. + traceSchema.type = { + valType: 'enumerated', + values: [traceType] + }; + + if(traceOut.visible === false && traceIn.visible !== false) { + errorList.push(format('invisible', base)); + } + + crawl(traceIn, traceOut, traceSchema, errorList, base); + + var transformsIn = traceIn.transforms; + var transformsOut = traceOut.transforms; + + if(transformsIn) { + if(!isArray(transformsIn)) { + errorList.push(format('array', base, ['transforms'])); + } + + base.push('transforms'); + + for(var j = 0; j < transformsIn.length; j++) { + var path = ['transforms', j]; + var transformType = transformsIn[j].type; + + if(!isPlainObject(transformsIn[j])) { + errorList.push(format('object', base, path)); + continue; + } + + var transformSchema = schema.transforms[transformType] ? + schema.transforms[transformType].attributes : + {}; + + // add 'type' to transform schema to validate the transform type + transformSchema.type = { + valType: 'enumerated', + values: Object.keys(schema.transforms) + }; + + crawl(transformsIn[j], transformsOut[j], transformSchema, errorList, base, path); + } + } + } + + var layoutOut = gd._fullLayout; + var layoutSchema = fillLayoutSchema(schema, dataOut); + + crawl(layoutIn, layoutOut, layoutSchema, errorList, 'layout'); + + // return undefined if no validation errors were found + return (errorList.length === 0) ? void(0) : errorList; +}; + +function crawl(objIn, objOut, schema, list, base, path) { + path = path || []; + + var keys = Object.keys(objIn); + + for(var i = 0; i < keys.length; i++) { + var k = keys[i]; + + // transforms are handled separately + if(k === 'transforms') continue; + + var p = path.slice(); + p.push(k); + + var valIn = objIn[k]; + var valOut = objOut[k]; + + var nestedSchema = getNestedSchema(schema, k); + var nestedValType = (nestedSchema || {}).valType; + var isInfoArray = nestedValType === 'info_array'; + var isColorscale = nestedValType === 'colorscale'; + var items = (nestedSchema || {}).items; + + if(!isInSchema(schema, k)) { + list.push(format('schema', base, p)); + } else if(isPlainObject(valIn) && isPlainObject(valOut) && nestedValType !== 'any') { + crawl(valIn, valOut, nestedSchema, list, base, p); + } else if(isInfoArray && isArray(valIn)) { + if(valIn.length > valOut.length) { + list.push(format('unused', base, p.concat(valOut.length))); + } + var len = valOut.length; + var arrayItems = Array.isArray(items); + if(arrayItems) len = Math.min(len, items.length); + var m, n, item, valInPart, valOutPart; + if(nestedSchema.dimensions === 2) { + for(n = 0; n < len; n++) { + if(isArray(valIn[n])) { + if(valIn[n].length > valOut[n].length) { + list.push(format('unused', base, p.concat(n, valOut[n].length))); + } + var len2 = valOut[n].length; + for(m = 0; m < (arrayItems ? Math.min(len2, items[n].length) : len2); m++) { + item = arrayItems ? items[n][m] : items; + valInPart = valIn[n][m]; + valOutPart = valOut[n][m]; + if(!Lib.validate(valInPart, item)) { + list.push(format('value', base, p.concat(n, m), valInPart)); + } else if(valOutPart !== valInPart && valOutPart !== +valInPart) { + list.push(format('dynamic', base, p.concat(n, m), valInPart, valOutPart)); + } + } + } else { + list.push(format('array', base, p.concat(n), valIn[n])); + } + } + } else { + for(n = 0; n < len; n++) { + item = arrayItems ? items[n] : items; + valInPart = valIn[n]; + valOutPart = valOut[n]; + if(!Lib.validate(valInPart, item)) { + list.push(format('value', base, p.concat(n), valInPart)); + } else if(valOutPart !== valInPart && valOutPart !== +valInPart) { + list.push(format('dynamic', base, p.concat(n), valInPart, valOutPart)); + } + } + } + } else if(nestedSchema.items && !isInfoArray && isArray(valIn)) { + var _nestedSchema = items[Object.keys(items)[0]]; + var indexList = []; + + var j, _p; + + // loop over valOut items while keeping track of their + // corresponding input container index (given by _index) + for(j = 0; j < valOut.length; j++) { + var _index = valOut[j]._index || j; + + _p = p.slice(); + _p.push(_index); + + if(isPlainObject(valIn[_index]) && isPlainObject(valOut[j])) { + indexList.push(_index); + var valInj = valIn[_index]; + var valOutj = valOut[j]; + if(isPlainObject(valInj) && valInj.visible !== false && valOutj.visible === false) { + list.push(format('invisible', base, _p)); + } else crawl(valInj, valOutj, _nestedSchema, list, base, _p); + } + } + + // loop over valIn to determine where it went wrong for some items + for(j = 0; j < valIn.length; j++) { + _p = p.slice(); + _p.push(j); + + if(!isPlainObject(valIn[j])) { + list.push(format('object', base, _p, valIn[j])); + } else if(indexList.indexOf(j) === -1) { + list.push(format('unused', base, _p)); + } + } + } else if(!isPlainObject(valIn) && isPlainObject(valOut)) { + list.push(format('object', base, p, valIn)); + } else if(!isArrayOrTypedArray(valIn) && isArrayOrTypedArray(valOut) && !isInfoArray && !isColorscale) { + list.push(format('array', base, p, valIn)); + } else if(!(k in objOut)) { + list.push(format('unused', base, p, valIn)); + } else if(!Lib.validate(valIn, nestedSchema)) { + list.push(format('value', base, p, valIn)); + } else if(nestedSchema.valType === 'enumerated' && + ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut) + ) { + list.push(format('dynamic', base, p, valIn, valOut)); + } + } + + return list; +} + +// the 'full' layout schema depends on the traces types presents +function fillLayoutSchema(schema, dataOut) { + var layoutSchema = schema.layout.layoutAttributes; + + for(var i = 0; i < dataOut.length; i++) { + var traceOut = dataOut[i]; + var traceSchema = schema.traces[traceOut.type]; + var traceLayoutAttr = traceSchema.layoutAttributes; + + if(traceLayoutAttr) { + if(traceOut.subplot) { + Lib.extendFlat(layoutSchema[traceSchema.attributes.subplot.dflt], traceLayoutAttr); + } else { + Lib.extendFlat(layoutSchema, traceLayoutAttr); + } + } + } + + return layoutSchema; +} + +// validation error codes +var code2msgFunc = { + object: function(base, astr) { + var prefix; + + if(base === 'layout' && astr === '') prefix = 'The layout argument'; + else if(base[0] === 'data' && astr === '') { + prefix = 'Trace ' + base[1] + ' in the data argument'; + } else prefix = inBase(base) + 'key ' + astr; + + return prefix + ' must be linked to an object container'; + }, + array: function(base, astr) { + var prefix; + + if(base === 'data') prefix = 'The data argument'; + else prefix = inBase(base) + 'key ' + astr; + + return prefix + ' must be linked to an array container'; + }, + schema: function(base, astr) { + return inBase(base) + 'key ' + astr + ' is not part of the schema'; + }, + unused: function(base, astr, valIn) { + var target = isPlainObject(valIn) ? 'container' : 'key'; + + return inBase(base) + target + ' ' + astr + ' did not get coerced'; + }, + dynamic: function(base, astr, valIn, valOut) { + return [ + inBase(base) + 'key', + astr, + '(set to \'' + valIn + '\')', + 'got reset to', + '\'' + valOut + '\'', + 'during defaults.' + ].join(' '); + }, + invisible: function(base, astr) { + return ( + astr ? (inBase(base) + 'item ' + astr) : ('Trace ' + base[1]) + ) + ' got defaulted to be not visible'; + }, + value: function(base, astr, valIn) { + return [ + inBase(base) + 'key ' + astr, + 'is set to an invalid value (' + valIn + ')' + ].join(' '); + } +}; + +function inBase(base) { + if(isArray(base)) return 'In data trace ' + base[1] + ', '; + + return 'In ' + base + ', '; +} + +function format(code, base, path, valIn, valOut) { + path = path || ''; + + var container, trace; + + // container is either 'data' or 'layout + // trace is the trace index if 'data', null otherwise + + if(isArray(base)) { + container = base[0]; + trace = base[1]; + } else { + container = base; + trace = null; + } + + var astr = convertPathToAttributeString(path); + var msg = code2msgFunc[code](base, astr, valIn, valOut); + + // log to console if logger config option is enabled + Lib.log(msg); + + return { + code: code, + container: container, + trace: trace, + path: path, + astr: astr, + msg: msg + }; +} + +function isInSchema(schema, key) { + var parts = splitKey(key); + var keyMinusId = parts.keyMinusId; + var id = parts.id; + + if((keyMinusId in schema) && schema[keyMinusId]._isSubplotObj && id) { + return true; + } + + return (key in schema); +} + +function getNestedSchema(schema, key) { + if(key in schema) return schema[key]; + + var parts = splitKey(key); + + return schema[parts.keyMinusId]; +} + +var idRegex = Lib.counterRegex('([a-z]+)'); + +function splitKey(key) { + var idMatch = key.match(idRegex); + + return { + keyMinusId: idMatch && idMatch[1], + id: idMatch && idMatch[2] + }; +} + +function convertPathToAttributeString(path) { + if(!isArray(path)) return String(path); + + var astr = ''; + + for(var i = 0; i < path.length; i++) { + var p = path[i]; + + if(typeof p === 'number') { + astr = astr.substr(0, astr.length - 1) + '[' + p + ']'; + } else { + astr += p; + } + + if(i < path.length - 1) astr += '.'; + } + + return astr; +} + +},{"../lib":285,"../plots/plots":366,"./plot_config":318,"./plot_schema":319}],325:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + mode: { + valType: 'enumerated', + dflt: 'afterall', + values: ['immediate', 'next', 'afterall'], + }, + direction: { + valType: 'enumerated', + values: ['forward', 'reverse'], + dflt: 'forward', + }, + fromcurrent: { + valType: 'boolean', + dflt: false, + }, + frame: { + duration: { + valType: 'number', + min: 0, + dflt: 500, + }, + redraw: { + valType: 'boolean', + dflt: true, + }, + }, + transition: { + duration: { + valType: 'number', + min: 0, + dflt: 500, + editType: 'none', + }, + easing: { + valType: 'enumerated', + dflt: 'cubic-in-out', + values: [ + 'linear', + 'quad', + 'cubic', + 'sin', + 'exp', + 'circle', + 'elastic', + 'back', + 'bounce', + 'linear-in', + 'quad-in', + 'cubic-in', + 'sin-in', + 'exp-in', + 'circle-in', + 'elastic-in', + 'back-in', + 'bounce-in', + 'linear-out', + 'quad-out', + 'cubic-out', + 'sin-out', + 'exp-out', + 'circle-out', + 'elastic-out', + 'back-out', + 'bounce-out', + 'linear-in-out', + 'quad-in-out', + 'cubic-in-out', + 'sin-in-out', + 'exp-in-out', + 'circle-in-out', + 'elastic-in-out', + 'back-in-out', + 'bounce-in-out' + ], + editType: 'none', + }, + ordering: { + valType: 'enumerated', + values: ['layout first', 'traces first'], + dflt: 'layout first', + editType: 'none', + } + } +}; + +},{}],326:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var Template = _dereq_('../plot_api/plot_template'); + +/** Convenience wrapper for making array container logic DRY and consistent + * + * @param {object} parentObjIn + * user input object where the container in question is linked + * (i.e. either a user trace object or the user layout object) + * + * @param {object} parentObjOut + * full object where the coerced container will be linked + * (i.e. either a full trace object or the full layout object) + * + * @param {object} opts + * options object: + * - name {string} + * name of the key linking the container in question + * - inclusionAttr {string} + * name of the item attribute for inclusion/exclusion. Default is 'visible'. + * Since inclusion is true, use eg 'enabled' instead of 'disabled'. + * - handleItemDefaults {function} + * defaults method to be called on each item in the array container in question + * + * Its arguments are: + * - itemIn {object} item in user layout + * - itemOut {object} item in full layout + * - parentObj {object} (as in closure) + * - opts {object} (as in closure) + * N.B. + * + * - opts is passed to handleItemDefaults so it can also store + * links to supplementary data (e.g. fullData for layout components) + * + */ +module.exports = function handleArrayContainerDefaults(parentObjIn, parentObjOut, opts) { + var name = opts.name; + var inclusionAttr = opts.inclusionAttr || 'visible'; + + var previousContOut = parentObjOut[name]; + + var contIn = Lib.isArrayOrTypedArray(parentObjIn[name]) ? parentObjIn[name] : []; + var contOut = parentObjOut[name] = []; + var templater = Template.arrayTemplater(parentObjOut, name, inclusionAttr); + var i, itemOut; + + for(i = 0; i < contIn.length; i++) { + var itemIn = contIn[i]; + + if(!Lib.isPlainObject(itemIn)) { + itemOut = templater.newItem({}); + itemOut[inclusionAttr] = false; + } else { + itemOut = templater.newItem(itemIn); + } + + itemOut._index = i; + + if(itemOut[inclusionAttr] !== false) { + opts.handleItemDefaults(itemIn, itemOut, parentObjOut, opts); + } + + contOut.push(itemOut); + } + + var defaultItems = templater.defaultItems(); + for(i = 0; i < defaultItems.length; i++) { + itemOut = defaultItems[i]; + itemOut._index = contOut.length; + opts.handleItemDefaults({}, itemOut, parentObjOut, opts, {}); + contOut.push(itemOut); + } + + // in case this array gets its defaults rebuilt independent of the whole layout, + // relink the private keys just for this array. + if(Lib.isArrayOrTypedArray(previousContOut)) { + var len = Math.min(previousContOut.length, contOut.length); + for(i = 0; i < len; i++) { + Lib.relinkPrivateKeys(contOut[i], previousContOut[i]); + } + } + + return contOut; +}; + +},{"../lib":285,"../plot_api/plot_template":320}],327:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('./font_attributes'); +var fxAttrs = _dereq_('../components/fx/attributes'); + +module.exports = { + type: { + valType: 'enumerated', + values: [], // listed dynamically + dflt: 'scatter', + editType: 'calc+clearAxisTypes', + _noTemplating: true // we handle this at a higher level + }, + visible: { + valType: 'enumerated', + values: [true, false, 'legendonly'], + dflt: true, + editType: 'calc', + }, + showlegend: { + valType: 'boolean', + dflt: true, + editType: 'style', + }, + legendgroup: { + valType: 'string', + dflt: '', + editType: 'style', + }, + legendgrouptitle: { + text: { + valType: 'string', + dflt: '', + editType: 'style', + }, + font: fontAttrs({ + editType: 'style', + }), + editType: 'style', + }, + legendrank: { + valType: 'number', + dflt: 1000, + editType: 'style', + }, + opacity: { + valType: 'number', + min: 0, + max: 1, + dflt: 1, + editType: 'style', + }, + name: { + valType: 'string', + editType: 'style', + }, + uid: { + valType: 'string', + editType: 'plot', + anim: true, + }, + ids: { + valType: 'data_array', + editType: 'calc', + anim: true, + }, + customdata: { + valType: 'data_array', + editType: 'calc', + }, + meta: { + valType: 'any', + arrayOk: true, + editType: 'plot', + }, + + // N.B. these cannot be 'data_array' as they do not have the same length as + // other data arrays and arrayOk attributes in general + // + // Maybe add another valType: + // https://github.com/plotly/plotly.js/issues/1894 + selectedpoints: { + valType: 'any', + editType: 'calc', + }, + + hoverinfo: { + valType: 'flaglist', + flags: ['x', 'y', 'z', 'text', 'name'], + extras: ['all', 'none', 'skip'], + arrayOk: true, + dflt: 'all', + editType: 'none', + }, + hoverlabel: fxAttrs.hoverlabel, + stream: { + token: { + valType: 'string', + noBlank: true, + strict: true, + editType: 'calc', + }, + maxpoints: { + valType: 'number', + min: 0, + max: 10000, + dflt: 500, + editType: 'calc', + }, + editType: 'calc' + }, + transforms: { + _isLinkedToArray: 'transform', + editType: 'calc', + }, + uirevision: { + valType: 'any', + editType: 'none', + } +}; + +},{"../components/fx/attributes":186,"./font_attributes":360}],328:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); +var dateTime2ms = Lib.dateTime2ms; +var incrementMonth = Lib.incrementMonth; +var constants = _dereq_('../../constants/numerical'); +var ONEAVGMONTH = constants.ONEAVGMONTH; + +module.exports = function alignPeriod(trace, ax, axLetter, vals) { + if(ax.type !== 'date') return vals; + + var alignment = trace[axLetter + 'periodalignment']; + if(!alignment) return vals; + + var period = trace[axLetter + 'period']; + var mPeriod; + if(isNumeric(period)) { + period = +period; + if(period <= 0) return vals; + } else if(typeof period === 'string' && period.charAt(0) === 'M') { + var n = +(period.substring(1)); + if(n > 0 && Math.round(n) === n) { + mPeriod = n; + } else return vals; + } + + var calendar = ax.calendar; + + var isStart = 'start' === alignment; + // var isMiddle = 'middle' === alignment; + var isEnd = 'end' === alignment; + + var period0 = trace[axLetter + 'period0']; + var base = dateTime2ms(period0, calendar) || 0; + + var newVals = []; + var len = vals.length; + for(var i = 0; i < len; i++) { + var v = vals[i]; + + var nEstimated, startTime, endTime; + if(mPeriod) { + // guess at how many periods away from base we are + nEstimated = Math.round((v - base) / (mPeriod * ONEAVGMONTH)); + endTime = incrementMonth(base, mPeriod * nEstimated, calendar); + + // iterate to get the exact bounds before and after v + // there may be ways to make this faster, but most of the time + // we'll only execute each loop zero or one time. + while(endTime > v) { + endTime = incrementMonth(endTime, -mPeriod, calendar); + } + while(endTime <= v) { + endTime = incrementMonth(endTime, mPeriod, calendar); + } + + // now we know endTime is the boundary immediately after v + // so startTime is obtained by incrementing backward one period. + startTime = incrementMonth(endTime, -mPeriod, calendar); + } else { // case of ms + nEstimated = Math.round((v - base) / period); + endTime = base + nEstimated * period; + + while(endTime > v) { + endTime -= period; + } + while(endTime <= v) { + endTime += period; + } + + startTime = endTime - period; + } + + newVals[i] = ( + isStart ? startTime : + isEnd ? endTime : + (startTime + endTime) / 2 + ); + } + return newVals; +}; + +},{"../../constants/numerical":265,"../../lib":285,"fast-isnumeric":31}],329:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + xaxis: { + valType: 'subplotid', + dflt: 'x', + editType: 'calc+clearAxisTypes', + }, + yaxis: { + valType: 'subplotid', + dflt: 'y', + editType: 'calc+clearAxisTypes', + } +}; + +},{}],330:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var FP_SAFE = _dereq_('../../constants/numerical').FP_SAFE; +var Registry = _dereq_('../../registry'); +var Drawing = _dereq_('../../components/drawing'); + +var axIds = _dereq_('./axis_ids'); +var getFromId = axIds.getFromId; +var isLinked = axIds.isLinked; + +module.exports = { + getAutoRange: getAutoRange, + makePadFn: makePadFn, + doAutoRange: doAutoRange, + findExtremes: findExtremes, + concatExtremes: concatExtremes +}; + +/** + * getAutoRange + * + * Collects all _extremes values corresponding to a given axis + * and computes its auto range. + * + * Note that getAutoRange uses return values from findExtremes. + * + * @param {object} gd: + * graph div object with filled-in fullData and fullLayout, in particular + * with filled-in '_extremes' containers: + * { + * val: calcdata value, + * pad: extra pixels beyond this value, + * extrapad: bool, does this point want 5% extra padding + * } + * @param {object} ax: + * full axis object, in particular with filled-in '_traceIndices' + * and '_annIndices' / '_shapeIndices' if applicable + * @return {array} + * an array of [min, max]. These are calcdata for log and category axes + * and data for linear and date axes. + * + * TODO: we want to change log to data as well, but it's hard to do this + * maintaining backward compatibility. category will always have to use calcdata + * though, because otherwise values between categories (or outside all categories) + * would be impossible. + */ +function getAutoRange(gd, ax) { + var i, j; + var newRange = []; + + var fullLayout = gd._fullLayout; + var getPadMin = makePadFn(fullLayout, ax, 0); + var getPadMax = makePadFn(fullLayout, ax, 1); + var extremes = concatExtremes(gd, ax); + var minArray = extremes.min; + var maxArray = extremes.max; + + if(minArray.length === 0 || maxArray.length === 0) { + return Lib.simpleMap(ax.range, ax.r2l); + } + + var minmin = minArray[0].val; + var maxmax = maxArray[0].val; + + for(i = 1; i < minArray.length; i++) { + if(minmin !== maxmax) break; + minmin = Math.min(minmin, minArray[i].val); + } + for(i = 1; i < maxArray.length; i++) { + if(minmin !== maxmax) break; + maxmax = Math.max(maxmax, maxArray[i].val); + } + + var axReverse = false; + + if(ax.range) { + var rng = Lib.simpleMap(ax.range, ax.r2l); + axReverse = rng[1] < rng[0]; + } + // one-time setting to easily reverse the axis + // when plotting from code + if(ax.autorange === 'reversed') { + axReverse = true; + ax.autorange = true; + } + + var rangeMode = ax.rangemode; + var toZero = rangeMode === 'tozero'; + var nonNegative = rangeMode === 'nonnegative'; + var axLen = ax._length; + // don't allow padding to reduce the data to < 10% of the length + var minSpan = axLen / 10; + + var mbest = 0; + var minpt, maxpt, minbest, maxbest, dp, dv; + + for(i = 0; i < minArray.length; i++) { + minpt = minArray[i]; + for(j = 0; j < maxArray.length; j++) { + maxpt = maxArray[j]; + dv = maxpt.val - minpt.val - calcBreaksLength(ax, minpt.val, maxpt.val); + if(dv > 0) { + dp = axLen - getPadMin(minpt) - getPadMax(maxpt); + if(dp > minSpan) { + if(dv / dp > mbest) { + minbest = minpt; + maxbest = maxpt; + mbest = dv / dp; + } + } else if(dv / axLen > mbest) { + // in case of padding longer than the axis + // at least include the unpadded data values. + minbest = {val: minpt.val, nopad: 1}; + maxbest = {val: maxpt.val, nopad: 1}; + mbest = dv / axLen; + } + } + } + } + + function maximumPad(prev, pt) { + return Math.max(prev, getPadMax(pt)); + } + + if(minmin === maxmax) { + var lower = minmin - 1; + var upper = minmin + 1; + if(toZero) { + if(minmin === 0) { + // The only value we have on this axis is 0, and we want to + // autorange so zero is one end. + // In principle this could be [0, 1] or [-1, 0] but usually + // 'tozero' pins 0 to the low end, so follow that. + newRange = [0, 1]; + } else { + var maxPad = (minmin > 0 ? maxArray : minArray).reduce(maximumPad, 0); + // we're pushing a single value away from the edge due to its + // padding, with the other end clamped at zero + // 0.5 means don't push it farther than the center. + var rangeEnd = minmin / (1 - Math.min(0.5, maxPad / axLen)); + newRange = minmin > 0 ? [0, rangeEnd] : [rangeEnd, 0]; + } + } else if(nonNegative) { + newRange = [Math.max(0, lower), Math.max(1, upper)]; + } else { + newRange = [lower, upper]; + } + } else { + if(toZero) { + if(minbest.val >= 0) { + minbest = {val: 0, nopad: 1}; + } + if(maxbest.val <= 0) { + maxbest = {val: 0, nopad: 1}; + } + } else if(nonNegative) { + if(minbest.val - mbest * getPadMin(minbest) < 0) { + minbest = {val: 0, nopad: 1}; + } + if(maxbest.val <= 0) { + maxbest = {val: 1, nopad: 1}; + } + } + + // in case it changed again... + mbest = (maxbest.val - minbest.val - calcBreaksLength(ax, minpt.val, maxpt.val)) / + (axLen - getPadMin(minbest) - getPadMax(maxbest)); + + newRange = [ + minbest.val - mbest * getPadMin(minbest), + maxbest.val + mbest * getPadMax(maxbest) + ]; + } + + // maintain reversal + if(axReverse) newRange.reverse(); + + return Lib.simpleMap(newRange, ax.l2r || Number); +} + +// find axis rangebreaks in [v0,v1] and compute its length in value space +function calcBreaksLength(ax, v0, v1) { + var lBreaks = 0; + if(ax.rangebreaks) { + var rangebreaksOut = ax.locateBreaks(v0, v1); + for(var i = 0; i < rangebreaksOut.length; i++) { + var brk = rangebreaksOut[i]; + lBreaks += brk.max - brk.min; + } + } + return lBreaks; +} + +/* + * calculate the pixel padding for ax._min and ax._max entries with + * optional extrapad as 5% of the total axis length + */ +function makePadFn(fullLayout, ax, max) { + // 5% padding for points that specify extrapad: true + var extrappad = 0.05 * ax._length; + + var anchorAxis = ax._anchorAxis || {}; + + if( + (ax.ticklabelposition || '').indexOf('inside') !== -1 || + (anchorAxis.ticklabelposition || '').indexOf('inside') !== -1 + ) { + var axReverse = ax.autorange === 'reversed'; + if(!axReverse) { + var rng = Lib.simpleMap(ax.range, ax.r2l); + axReverse = rng[1] < rng[0]; + } + if(axReverse) max = !max; + } + + var zero = 0; + if(!isLinked(fullLayout, ax._id)) { + zero = padInsideLabelsOnAnchorAxis(fullLayout, ax, max); + } + extrappad = Math.max(zero, extrappad); + + // domain-constrained axes: base extrappad on the unconstrained + // domain so it's consistent as the domain changes + if((ax.constrain === 'domain') && ax._inputDomain) { + extrappad *= (ax._inputDomain[1] - ax._inputDomain[0]) / + (ax.domain[1] - ax.domain[0]); + } + + return function getPad(pt) { + if(pt.nopad) return 0; + return pt.pad + (pt.extrapad ? extrappad : zero); + }; +} + +var TEXTPAD = 3; + +function padInsideLabelsOnAnchorAxis(fullLayout, ax, max) { + var pad = 0; + + var isX = ax._id.charAt(0) === 'x'; + + for(var subplot in fullLayout._plots) { + var plotinfo = fullLayout._plots[subplot]; + + if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue; + + var anchorAxis = (isX ? plotinfo.yaxis : plotinfo.xaxis) || {}; + + if((anchorAxis.ticklabelposition || '').indexOf('inside') !== -1) { + // increase padding to make more room for inside tick labels of the counter axis + if(( + !max && ( + anchorAxis.side === 'left' || + anchorAxis.side === 'bottom' + ) + ) || ( + max && ( + anchorAxis.side === 'top' || + anchorAxis.side === 'right' + ) + )) { + if(anchorAxis._vals) { + var rad = Lib.deg2rad(anchorAxis._tickAngles[anchorAxis._id + 'tick'] || 0); + var cosA = Math.abs(Math.cos(rad)); + var sinA = Math.abs(Math.sin(rad)); + + // no stashed bounding boxes - stash bounding boxes + if(!anchorAxis._vals[0].bb) { + var cls = anchorAxis._id + 'tick'; + var tickLabels = anchorAxis._selections[cls]; + tickLabels.each(function(d) { + var thisLabel = d3.select(this); + var mathjaxGroup = thisLabel.select('.text-math-group'); + if(mathjaxGroup.empty()) { + d.bb = Drawing.bBox(thisLabel.node()); + } + }); + } + + // use bounding boxes + for(var i = 0; i < anchorAxis._vals.length; i++) { + var t = anchorAxis._vals[i]; + var bb = t.bb; + + if(bb) { + var w = 2 * TEXTPAD + bb.width; + var h = 2 * TEXTPAD + bb.height; + + pad = Math.max(pad, isX ? + Math.max(w * cosA, h * sinA) : + Math.max(h * cosA, w * sinA) + ); + } + } + } + + if(anchorAxis.ticks === 'inside' && anchorAxis.ticklabelposition === 'inside') { + pad += anchorAxis.ticklen || 0; + } + } + } + } + + return pad; +} + +function concatExtremes(gd, ax, noMatch) { + var axId = ax._id; + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + var minArray = []; + var maxArray = []; + var i, j, d; + + function _concat(cont, indices) { + for(i = 0; i < indices.length; i++) { + var item = cont[indices[i]]; + var extremes = (item._extremes || {})[axId]; + if(item.visible === true && extremes) { + for(j = 0; j < extremes.min.length; j++) { + d = extremes.min[j]; + collapseMinArray(minArray, d.val, d.pad, {extrapad: d.extrapad}); + } + for(j = 0; j < extremes.max.length; j++) { + d = extremes.max[j]; + collapseMaxArray(maxArray, d.val, d.pad, {extrapad: d.extrapad}); + } + } + } + } + + _concat(fullData, ax._traceIndices); + _concat(fullLayout.annotations || [], ax._annIndices || []); + _concat(fullLayout.shapes || [], ax._shapeIndices || []); + + // Include the extremes from other matched axes with this one + if(ax._matchGroup && !noMatch) { + for(var axId2 in ax._matchGroup) { + if(axId2 !== ax._id) { + var ax2 = getFromId(gd, axId2); + var extremes2 = concatExtremes(gd, ax2, true); + // convert padding on the second axis to the first with lenRatio + var lenRatio = ax._length / ax2._length; + for(j = 0; j < extremes2.min.length; j++) { + d = extremes2.min[j]; + collapseMinArray(minArray, d.val, d.pad * lenRatio, {extrapad: d.extrapad}); + } + for(j = 0; j < extremes2.max.length; j++) { + d = extremes2.max[j]; + collapseMaxArray(maxArray, d.val, d.pad * lenRatio, {extrapad: d.extrapad}); + } + } + } + } + + return {min: minArray, max: maxArray}; +} + +function doAutoRange(gd, ax, presetRange) { + ax.setScale(); + + if(ax.autorange) { + ax.range = presetRange ? presetRange.slice() : getAutoRange(gd, ax); + + ax._r = ax.range.slice(); + ax._rl = Lib.simpleMap(ax._r, ax.r2l); + + // doAutoRange will get called on fullLayout, + // but we want to report its results back to layout + + var axIn = ax._input; + + // before we edit _input, store preGUI values + var edits = {}; + edits[ax._attr + '.range'] = ax.range; + edits[ax._attr + '.autorange'] = ax.autorange; + Registry.call('_storeDirectGUIEdit', gd.layout, gd._fullLayout._preGUI, edits); + + axIn.range = ax.range.slice(); + axIn.autorange = ax.autorange; + } + + var anchorAx = ax._anchorAxis; + + if(anchorAx && anchorAx.rangeslider) { + var axeRangeOpts = anchorAx.rangeslider[ax._name]; + if(axeRangeOpts) { + if(axeRangeOpts.rangemode === 'auto') { + axeRangeOpts.range = getAutoRange(gd, ax); + } + } + anchorAx._input.rangeslider[ax._name] = Lib.extendFlat({}, axeRangeOpts); + } +} + +/** + * findExtremes + * + * Find min/max extremes of an array of coordinates on a given axis. + * + * Note that findExtremes is called during `calc`, when we don't yet know the axis + * length; all the inputs should be based solely on the trace data, nothing + * about the axis layout. + * + * Note that `ppad` and `vpad` as well as their asymmetric variants refer to + * the before and after padding of the passed `data` array, not to the whole axis. + * + * @param {object} ax: full axis object + * relies on + * - ax.type + * - ax._m (just its sign) + * - ax.d2l + * @param {array} data: + * array of numbers (i.e. already run though ax.d2c) + * @param {object} opts: + * available keys are: + * vpad: (number or number array) pad values (data value +-vpad) + * ppad: (number or number array) pad pixels (pixel location +-ppad) + * ppadplus, ppadminus, vpadplus, vpadminus: + * separate padding for each side, overrides symmetric + * padded: (boolean) add 5% padding to both ends + * (unless one end is overridden by tozero) + * tozero: (boolean) make sure to include zero if axis is linear, + * and make it a tight bound if possible + * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus) + * is linearized (for log scale axes) + * + * @return {object} + * - min {array of objects} + * - max {array of objects} + * each object item has fields: + * - val {number} + * - pad {number} + * - extrappad {number} + * - opts {object}: a ref to the passed "options" object + */ +function findExtremes(ax, data, opts) { + if(!opts) opts = {}; + if(!ax._m) ax.setScale(); + + var minArray = []; + var maxArray = []; + + var len = data.length; + var extrapad = opts.padded || false; + var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-'); + var isLog = ax.type === 'log'; + var hasArrayOption = false; + var vpadLinearized = opts.vpadLinearized || false; + var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax; + + function makePadAccessor(item) { + if(Array.isArray(item)) { + hasArrayOption = true; + return function(i) { return Math.max(Number(item[i]||0), 0); }; + } else { + var v = Math.max(Number(item||0), 0); + return function() { return v; }; + } + } + + var ppadplus = makePadAccessor((ax._m > 0 ? + opts.ppadplus : opts.ppadminus) || opts.ppad || 0); + var ppadminus = makePadAccessor((ax._m > 0 ? + opts.ppadminus : opts.ppadplus) || opts.ppad || 0); + var vpadplus = makePadAccessor(opts.vpadplus || opts.vpad); + var vpadminus = makePadAccessor(opts.vpadminus || opts.vpad); + + if(!hasArrayOption) { + // with no arrays other than `data` we don't need to consider + // every point, only the extreme data points + vmin = Infinity; + vmax = -Infinity; + + if(isLog) { + for(i = 0; i < len; i++) { + v = data[i]; + // data is not linearized yet so we still have to filter out negative logs + if(v < vmin && v > 0) vmin = v; + if(v > vmax && v < FP_SAFE) vmax = v; + } + } else { + for(i = 0; i < len; i++) { + v = data[i]; + if(v < vmin && v > -FP_SAFE) vmin = v; + if(v > vmax && v < FP_SAFE) vmax = v; + } + } + + data = [vmin, vmax]; + len = 2; + } + + var collapseOpts = {tozero: tozero, extrapad: extrapad}; + + function addItem(i) { + di = data[i]; + if(!isNumeric(di)) return; + ppadiplus = ppadplus(i); + ppadiminus = ppadminus(i); + + if(vpadLinearized) { + dmin = ax.c2l(di) - vpadminus(i); + dmax = ax.c2l(di) + vpadplus(i); + } else { + vmin = di - vpadminus(i); + vmax = di + vpadplus(i); + // special case for log axes: if vpad makes this object span + // more than an order of mag, clip it to one order. This is so + // we don't have non-positive errors or absurdly large lower + // range due to rounding errors + if(isLog && vmin < vmax / 10) vmin = vmax / 10; + + dmin = ax.c2l(vmin); + dmax = ax.c2l(vmax); + } + + if(tozero) { + dmin = Math.min(0, dmin); + dmax = Math.max(0, dmax); + } + if(goodNumber(dmin)) { + collapseMinArray(minArray, dmin, ppadiminus, collapseOpts); + } + if(goodNumber(dmax)) { + collapseMaxArray(maxArray, dmax, ppadiplus, collapseOpts); + } + } + + // For efficiency covering monotonic or near-monotonic data, + // check a few points at both ends first and then sweep + // through the middle + var iMax = Math.min(6, len); + for(i = 0; i < iMax; i++) addItem(i); + for(i = len - 1; i >= iMax; i--) addItem(i); + + return { + min: minArray, + max: maxArray, + opts: opts + }; +} + +function collapseMinArray(array, newVal, newPad, opts) { + collapseArray(array, newVal, newPad, opts, lessOrEqual); +} + +function collapseMaxArray(array, newVal, newPad, opts) { + collapseArray(array, newVal, newPad, opts, greaterOrEqual); +} + +/** + * collapseArray + * + * Takes items from 'array' and compares them to 'newVal', 'newPad'. + * + * @param {array} array: + * current set of min or max extremes + * @param {number} newVal: + * new value to compare against + * @param {number} newPad: + * pad value associated with 'newVal' + * @param {object} opts: + * - tozero {boolean} + * - extrapad {number} + * @param {function} atLeastAsExtreme: + * comparison function, use + * - lessOrEqual for min 'array' and + * - greaterOrEqual for max 'array' + * + * In practice, 'array' is either + * - 'extremes[ax._id].min' or + * - 'extremes[ax._id].max + * found in traces and layout items that affect autorange. + * + * Since we don't yet know the relationship between pixels and values + * (that's what we're trying to figure out!) AND we don't yet know how + * many pixels `extrapad` represents (it's going to be 5% of the length, + * but we don't want to have to redo calc just because length changed) + * two point must satisfy three criteria simultaneously for one to supersede the other: + * - at least as extreme a `val` + * - at least as big a `pad` + * - an unpadded point cannot supersede a padded point, but any other combination can + * + * Then: + * - If the item supersedes the new point, set includeThis false + * - If the new pt supersedes the item, delete it from 'array' + */ +function collapseArray(array, newVal, newPad, opts, atLeastAsExtreme) { + var tozero = opts.tozero; + var extrapad = opts.extrapad; + var includeThis = true; + + for(var j = 0; j < array.length && includeThis; j++) { + var v = array[j]; + if(atLeastAsExtreme(v.val, newVal) && v.pad >= newPad && (v.extrapad || !extrapad)) { + includeThis = false; + break; + } else if(atLeastAsExtreme(newVal, v.val) && v.pad <= newPad && (extrapad || !v.extrapad)) { + array.splice(j, 1); + j--; + } + } + if(includeThis) { + var clipAtZero = (tozero && newVal === 0); + array.push({ + val: newVal, + pad: clipAtZero ? 0 : newPad, + extrapad: clipAtZero ? false : extrapad + }); + } +} + +// In order to stop overflow errors, don't consider points +// too close to the limits of js floating point +function goodNumber(v) { + return isNumeric(v) && Math.abs(v) < FP_SAFE; +} + +function lessOrEqual(v0, v1) { return v0 <= v1; } +function greaterOrEqual(v0, v1) { return v0 >= v1; } + +},{"../../components/drawing":177,"../../constants/numerical":265,"../../lib":285,"../../registry":373,"./axis_ids":335,"@plotly/d3":20,"fast-isnumeric":31}],331:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); +var Plots = _dereq_('../../plots/plots'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var Titles = _dereq_('../../components/titles'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); + +var axAttrs = _dereq_('./layout_attributes'); +var cleanTicks = _dereq_('./clean_ticks'); + +var constants = _dereq_('../../constants/numerical'); +var ONEMAXYEAR = constants.ONEMAXYEAR; +var ONEAVGYEAR = constants.ONEAVGYEAR; +var ONEMINYEAR = constants.ONEMINYEAR; +var ONEMAXQUARTER = constants.ONEMAXQUARTER; +var ONEAVGQUARTER = constants.ONEAVGQUARTER; +var ONEMINQUARTER = constants.ONEMINQUARTER; +var ONEMAXMONTH = constants.ONEMAXMONTH; +var ONEAVGMONTH = constants.ONEAVGMONTH; +var ONEMINMONTH = constants.ONEMINMONTH; +var ONEWEEK = constants.ONEWEEK; +var ONEDAY = constants.ONEDAY; +var HALFDAY = ONEDAY / 2; +var ONEHOUR = constants.ONEHOUR; +var ONEMIN = constants.ONEMIN; +var ONESEC = constants.ONESEC; +var MINUS_SIGN = constants.MINUS_SIGN; +var BADNUM = constants.BADNUM; + +var ZERO_PATH = { K: 'zeroline' }; +var GRID_PATH = { K: 'gridline', L: 'path' }; +var TICK_PATH = { K: 'tick', L: 'path' }; +var TICK_TEXT = { K: 'tick', L: 'text' }; + +var alignmentConstants = _dereq_('../../constants/alignment'); +var MID_SHIFT = alignmentConstants.MID_SHIFT; +var CAP_SHIFT = alignmentConstants.CAP_SHIFT; +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var OPPOSITE_SIDE = alignmentConstants.OPPOSITE_SIDE; + +var TEXTPAD = 3; + +var axes = module.exports = {}; + +axes.setConvert = _dereq_('./set_convert'); +var autoType = _dereq_('./axis_autotype'); + +var axisIds = _dereq_('./axis_ids'); +var idSort = axisIds.idSort; +var isLinked = axisIds.isLinked; + +// tight coupling to chart studio +axes.id2name = axisIds.id2name; +axes.name2id = axisIds.name2id; +axes.cleanId = axisIds.cleanId; +axes.list = axisIds.list; +axes.listIds = axisIds.listIds; +axes.getFromId = axisIds.getFromId; +axes.getFromTrace = axisIds.getFromTrace; + +var autorange = _dereq_('./autorange'); +axes.getAutoRange = autorange.getAutoRange; +axes.findExtremes = autorange.findExtremes; + +var epsilon = 0.0001; +function expandRange(range) { + var delta = (range[1] - range[0]) * epsilon; + return [ + range[0] - delta, + range[1] + delta + ]; +} + +/* + * find the list of possible axes to reference with an xref or yref attribute + * and coerce it to that list + * + * attr: the attribute we're generating a reference for. Should end in 'x' or 'y' + * but can be prefixed, like 'ax' for annotation's arrow x + * dflt: the default to coerce to, or blank to use the first axis (falling back on + * extraOption if there is no axis) + * extraOption: aside from existing axes with this letter, what non-axis value is allowed? + * Only required if it's different from `dflt` + */ +axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) { + var axLetter = attr.charAt(attr.length - 1); + var axlist = gd._fullLayout._subplots[axLetter + 'axis']; + var refAttr = attr + 'ref'; + var attrDef = {}; + + if(!dflt) dflt = axlist[0] || (typeof extraOption === 'string' ? extraOption : extraOption[0]); + if(!extraOption) extraOption = dflt; + axlist = axlist.concat(axlist.map(function(x) { return x + ' domain'; })); + + // data-ref annotations are not supported in gl2d yet + + attrDef[refAttr] = { + valType: 'enumerated', + values: axlist.concat(extraOption ? + (typeof extraOption === 'string' ? [extraOption] : extraOption) : + []), + dflt: dflt + }; + + // xref, yref + return Lib.coerce(containerIn, containerOut, attrDef, refAttr); +}; + +/* + * Get the type of an axis reference. This can be 'range', 'domain', or 'paper'. + * This assumes ar is a valid axis reference and returns 'range' if it doesn't + * match the patterns for 'paper' or 'domain'. + * + * ar: the axis reference string + * + */ +axes.getRefType = function(ar) { + if(ar === undefined) { return ar; } + if(ar === 'paper') { return 'paper'; } + if(ar === 'pixel') { return 'pixel'; } + if(/( domain)$/.test(ar)) { return 'domain'; } else { return 'range'; } +}; + +/* + * coerce position attributes (range-type) that can be either on axes or absolute + * (paper or pixel) referenced. The biggest complication here is that we don't know + * before looking at the axis whether the value must be a number or not (it may be + * a date string), so we can't use the regular valType='number' machinery + * + * axRef (string): the axis this position is referenced to, or: + * paper: fraction of the plot area + * pixel: pixels relative to some starting position + * attr (string): the attribute in containerOut we are coercing + * dflt (number): the default position, as a fraction or pixels. If the attribute + * is to be axis-referenced, this will be converted to an axis data value + * + * Also cleans the values, since the attribute definition itself has to say + * valType: 'any' to handle date axes. This allows us to accept: + * - for category axes: category names, and convert them here into serial numbers. + * Note that this will NOT work for axis range endpoints, because we don't know + * the category list yet (it's set by ax.makeCalcdata during calc) + * but it works for component (note, shape, images) positions. + * - for date axes: JS Dates or milliseconds, and convert to date strings + * - for other types: coerce them to numbers + */ +axes.coercePosition = function(containerOut, gd, coerce, axRef, attr, dflt) { + var cleanPos, pos; + var axRefType = axes.getRefType(axRef); + if(axRefType !== 'range') { + cleanPos = Lib.ensureNumber; + pos = coerce(attr, dflt); + } else { + var ax = axes.getFromId(gd, axRef); + dflt = ax.fraction2r(dflt); + pos = coerce(attr, dflt); + cleanPos = ax.cleanPos; + } + containerOut[attr] = cleanPos(pos); +}; + +axes.cleanPosition = function(pos, gd, axRef) { + var cleanPos = (axRef === 'paper' || axRef === 'pixel') ? + Lib.ensureNumber : + axes.getFromId(gd, axRef).cleanPos; + + return cleanPos(pos); +}; + +axes.redrawComponents = function(gd, axIds) { + axIds = axIds ? axIds : axes.listIds(gd); + + var fullLayout = gd._fullLayout; + + function _redrawOneComp(moduleName, methodName, stashName, shortCircuit) { + var method = Registry.getComponentMethod(moduleName, methodName); + var stash = {}; + + for(var i = 0; i < axIds.length; i++) { + var ax = fullLayout[axes.id2name(axIds[i])]; + var indices = ax[stashName]; + + for(var j = 0; j < indices.length; j++) { + var ind = indices[j]; + + if(!stash[ind]) { + method(gd, ind); + stash[ind] = 1; + // once is enough for images (which doesn't use the `i` arg anyway) + if(shortCircuit) return; + } + } + } + } + + // annotations and shapes 'draw' method is slow, + // use the finer-grained 'drawOne' method instead + _redrawOneComp('annotations', 'drawOne', '_annIndices'); + _redrawOneComp('shapes', 'drawOne', '_shapeIndices'); + _redrawOneComp('images', 'draw', '_imgIndices', true); +}; + +var getDataConversions = axes.getDataConversions = function(gd, trace, target, targetArray) { + var ax; + + // If target points to an axis, use the type we already have for that + // axis to find the data type. Otherwise use the values to autotype. + var d2cTarget = (target === 'x' || target === 'y' || target === 'z') ? + target : + targetArray; + + // In the case of an array target, make a mock data array + // and call supplyDefaults to the data type and + // setup the data-to-calc method. + if(Array.isArray(d2cTarget)) { + ax = { + type: autoType(targetArray, undefined, { + autotypenumbers: gd._fullLayout.autotypenumbers + }), + _categories: [] + }; + axes.setConvert(ax); + + // build up ax._categories (usually done during ax.makeCalcdata() + if(ax.type === 'category') { + for(var i = 0; i < targetArray.length; i++) { + ax.d2c(targetArray[i]); + } + } + // TODO what to do for transforms? + } else { + ax = axes.getFromTrace(gd, trace, d2cTarget); + } + + // if 'target' has corresponding axis + // -> use setConvert method + if(ax) return {d2c: ax.d2c, c2d: ax.c2d}; + + // special case for 'ids' + // -> cast to String + if(d2cTarget === 'ids') return {d2c: toString, c2d: toString}; + + // otherwise (e.g. numeric-array of 'marker.color' or 'marker.size') + // -> cast to Number + + return {d2c: toNum, c2d: toNum}; +}; + +function toNum(v) { return +v; } +function toString(v) { return String(v); } + +axes.getDataToCoordFunc = function(gd, trace, target, targetArray) { + return getDataConversions(gd, trace, target, targetArray).d2c; +}; + +// get counteraxis letter for this axis (name or id) +// this can also be used as the id for default counter axis +axes.counterLetter = function(id) { + var axLetter = id.charAt(0); + if(axLetter === 'x') return 'y'; + if(axLetter === 'y') return 'x'; +}; + +// incorporate a new minimum difference and first tick into +// forced +// note that _forceTick0 is linearized, so needs to be turned into +// a range value for setting tick0 +axes.minDtick = function(ax, newDiff, newFirst, allow) { + // doesn't make sense to do forced min dTick on log or category axes, + // and the plot itself may decide to cancel (ie non-grouped bars) + if(['log', 'category', 'multicategory'].indexOf(ax.type) !== -1 || !allow) { + ax._minDtick = 0; + } else if(ax._minDtick === undefined) { + // undefined means there's nothing there yet + + ax._minDtick = newDiff; + ax._forceTick0 = newFirst; + } else if(ax._minDtick) { + if((ax._minDtick / newDiff + 1e-6) % 1 < 2e-6 && + // existing minDtick is an integer multiple of newDiff + // (within rounding err) + // and forceTick0 can be shifted to newFirst + + (((newFirst - ax._forceTick0) / newDiff % 1) + + 1.000001) % 1 < 2e-6) { + ax._minDtick = newDiff; + ax._forceTick0 = newFirst; + } else if((newDiff / ax._minDtick + 1e-6) % 1 > 2e-6 || + // if the converse is true (newDiff is a multiple of minDtick and + // newFirst can be shifted to forceTick0) then do nothing - same + // forcing stands. Otherwise, cancel forced minimum + + (((newFirst - ax._forceTick0) / ax._minDtick % 1) + + 1.000001) % 1 > 2e-6) { + ax._minDtick = 0; + } + } +}; + +// save a copy of the initial axis ranges in fullLayout +// use them in mode bar and dblclick events +axes.saveRangeInitial = function(gd, overwrite) { + var axList = axes.list(gd, '', true); + var hasOneAxisChanged = false; + + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + var isNew = (ax._rangeInitial === undefined); + var hasChanged = isNew || !( + ax.range[0] === ax._rangeInitial[0] && + ax.range[1] === ax._rangeInitial[1] + ); + + if((isNew && ax.autorange === false) || (overwrite && hasChanged)) { + ax._rangeInitial = ax.range.slice(); + hasOneAxisChanged = true; + } + } + + return hasOneAxisChanged; +}; + +// save a copy of the initial spike visibility +axes.saveShowSpikeInitial = function(gd, overwrite) { + var axList = axes.list(gd, '', true); + var hasOneAxisChanged = false; + var allSpikesEnabled = 'on'; + + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + var isNew = (ax._showSpikeInitial === undefined); + var hasChanged = isNew || !(ax.showspikes === ax._showspikes); + + if(isNew || (overwrite && hasChanged)) { + ax._showSpikeInitial = ax.showspikes; + hasOneAxisChanged = true; + } + + if(allSpikesEnabled === 'on' && !ax.showspikes) { + allSpikesEnabled = 'off'; + } + } + gd._fullLayout._cartesianSpikesEnabled = allSpikesEnabled; + return hasOneAxisChanged; +}; + +axes.autoBin = function(data, ax, nbins, is2d, calendar, size) { + var dataMin = Lib.aggNums(Math.min, null, data); + var dataMax = Lib.aggNums(Math.max, null, data); + + if(ax.type === 'category' || ax.type === 'multicategory') { + return { + start: dataMin - 0.5, + end: dataMax + 0.5, + size: Math.max(1, Math.round(size) || 1), + _dataSpan: dataMax - dataMin, + }; + } + + if(!calendar) calendar = ax.calendar; + + // piggyback off tick code to make "nice" bin sizes and edges + var dummyAx; + if(ax.type === 'log') { + dummyAx = { + type: 'linear', + range: [dataMin, dataMax] + }; + } else { + dummyAx = { + type: ax.type, + range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar), + calendar: calendar + }; + } + axes.setConvert(dummyAx); + + size = size && cleanTicks.dtick(size, dummyAx.type); + + if(size) { + dummyAx.dtick = size; + dummyAx.tick0 = cleanTicks.tick0(undefined, dummyAx.type, calendar); + } else { + var size0; + if(nbins) size0 = ((dataMax - dataMin) / nbins); + else { + // totally auto: scale off std deviation so the highest bin is + // somewhat taller than the total number of bins, but don't let + // the size get smaller than the 'nice' rounded down minimum + // difference between values + var distinctData = Lib.distinctVals(data); + var msexp = Math.pow(10, Math.floor( + Math.log(distinctData.minDiff) / Math.LN10)); + var minSize = msexp * Lib.roundUp( + distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true); + size0 = Math.max(minSize, 2 * Lib.stdev(data) / + Math.pow(data.length, is2d ? 0.25 : 0.4)); + + // fallback if ax.d2c output BADNUMs + // e.g. when user try to plot categorical bins + // on a layout.xaxis.type: 'linear' + if(!isNumeric(size0)) size0 = 1; + } + + axes.autoTicks(dummyAx, size0); + } + + var finalSize = dummyAx.dtick; + var binStart = axes.tickIncrement( + axes.tickFirst(dummyAx), finalSize, 'reverse', calendar); + var binEnd, bincount; + + // check for too many data points right at the edges of bins + // (>50% within 1% of bin edges) or all data points integral + // and offset the bins accordingly + if(typeof finalSize === 'number') { + binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax); + + bincount = 1 + Math.floor((dataMax - binStart) / finalSize); + binEnd = binStart + bincount * finalSize; + } else { + // month ticks - should be the only nonlinear kind we have at this point. + // dtick (as supplied by axes.autoTick) only has nonlinear values on + // date and log axes, but even if you display a histogram on a log axis + // we bin it on a linear axis (which one could argue against, but that's + // a separate issue) + if(dummyAx.dtick.charAt(0) === 'M') { + binStart = autoShiftMonthBins(binStart, data, finalSize, dataMin, calendar); + } + + // calculate the endpoint for nonlinear ticks - you have to + // just increment until you're done + binEnd = binStart; + bincount = 0; + while(binEnd <= dataMax) { + binEnd = axes.tickIncrement(binEnd, finalSize, false, calendar); + bincount++; + } + } + + return { + start: ax.c2r(binStart, 0, calendar), + end: ax.c2r(binEnd, 0, calendar), + size: finalSize, + _dataSpan: dataMax - dataMin + }; +}; + + +function autoShiftNumericBins(binStart, data, ax, dataMin, dataMax) { + var edgecount = 0; + var midcount = 0; + var intcount = 0; + var blankCount = 0; + + function nearEdge(v) { + // is a value within 1% of a bin edge? + return (1 + (v - binStart) * 100 / ax.dtick) % 100 < 2; + } + + for(var i = 0; i < data.length; i++) { + if(data[i] % 1 === 0) intcount++; + else if(!isNumeric(data[i])) blankCount++; + + if(nearEdge(data[i])) edgecount++; + if(nearEdge(data[i] + ax.dtick / 2)) midcount++; + } + var dataCount = data.length - blankCount; + + if(intcount === dataCount && ax.type !== 'date') { + if(ax.dtick < 1) { + // all integers: if bin size is <1, it's because + // that was specifically requested (large nbins) + // so respect that... but center the bins containing + // integers on those integers + + binStart = dataMin - 0.5 * ax.dtick; + } else { + // otherwise start half an integer down regardless of + // the bin size, just enough to clear up endpoint + // ambiguity about which integers are in which bins. + + binStart -= 0.5; + if(binStart + ax.dtick < dataMin) binStart += ax.dtick; + } + } else if(midcount < dataCount * 0.1) { + if(edgecount > dataCount * 0.3 || + nearEdge(dataMin) || nearEdge(dataMax)) { + // lots of points at the edge, not many in the middle + // shift half a bin + var binshift = ax.dtick / 2; + binStart += (binStart + binshift < dataMin) ? binshift : -binshift; + } + } + return binStart; +} + + +function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) { + var stats = Lib.findExactDates(data, calendar); + // number of data points that needs to be an exact value + // to shift that increment to (near) the bin center + var threshold = 0.8; + + if(stats.exactDays > threshold) { + var numMonths = Number(dtick.substr(1)); + + if((stats.exactYears > threshold) && (numMonths % 12 === 0)) { + // The exact middle of a non-leap-year is 1.5 days into July + // so if we start the bins here, all but leap years will + // get hover-labeled as exact years. + binStart = axes.tickIncrement(binStart, 'M6', 'reverse') + ONEDAY * 1.5; + } else if(stats.exactMonths > threshold) { + // Months are not as clean, but if we shift half the *longest* + // month (31/2 days) then 31-day months will get labeled exactly + // and shorter months will get labeled with the correct month + // but shifted 12-36 hours into it. + binStart = axes.tickIncrement(binStart, 'M1', 'reverse') + ONEDAY * 15.5; + } else { + // Shifting half a day is exact, but since these are month bins it + // will always give a somewhat odd-looking label, until we do something + // smarter like showing the bin boundaries (or the bounds of the actual + // data in each bin) + binStart -= HALFDAY; + } + var nextBinStart = axes.tickIncrement(binStart, dtick); + + if(nextBinStart <= dataMin) return nextBinStart; + } + return binStart; +} + +// ---------------------------------------------------- +// Ticks and grids +// ---------------------------------------------------- + +// ensure we have tick0, dtick, and tick rounding calculated +axes.prepTicks = function(ax, opts) { + var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts); + + ax._dtickInit = ax.dtick; + ax._tick0Init = ax.tick0; + + // calculate max number of (auto) ticks to display based on plot size + if(ax.tickmode === 'auto' || !ax.dtick) { + var nt = ax.nticks; + var minPx; + + if(!nt) { + if(ax.type === 'category' || ax.type === 'multicategory') { + minPx = ax.tickfont ? Lib.bigFont(ax.tickfont.size || 12) : 15; + nt = ax._length / minPx; + } else { + minPx = ax._id.charAt(0) === 'y' ? 40 : 80; + nt = Lib.constrain(ax._length / minPx, 4, 9) + 1; + } + + // radial axes span half their domain, + // multiply nticks value by two to get correct number of auto ticks. + if(ax._name === 'radialaxis') nt *= 2; + } + + // add a couple of extra digits for filling in ticks when we + // have explicit tickvals without tick text + if(ax.tickmode === 'array') nt *= 100; + + + ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt; + axes.autoTicks(ax, ax._roughDTick); + + // check for a forced minimum dtick + if(ax._minDtick > 0 && ax.dtick < ax._minDtick * 2) { + ax.dtick = ax._minDtick; + ax.tick0 = ax.l2r(ax._forceTick0); + } + } + + if(ax.ticklabelmode === 'period') { + adjustPeriodDelta(ax); + } + + // check for missing tick0 + if(!ax.tick0) { + ax.tick0 = (ax.type === 'date') ? '2000-01-01' : 0; + } + + // ensure we don't try to make ticks below our minimum precision + // see https://github.com/plotly/plotly.js/issues/2892 + if(ax.type === 'date' && ax.dtick < 0.1) ax.dtick = 0.1; + + // now figure out rounding of tick values + autoTickRound(ax); +}; + +function nMonths(dtick) { + return +(dtick.substring(1)); +} + +function adjustPeriodDelta(ax) { // adjusts ax.dtick and sets ax._definedDelta + var definedDelta; + + function mDate() { + return !( + isNumeric(ax.dtick) || + ax.dtick.charAt(0) !== 'M' + ); + } + var isMDate = mDate(); + var tickformat = axes.getTickFormat(ax); + if(tickformat) { + var noDtick = ax._dtickInit !== ax.dtick; + if( + !(/%[fLQsSMX]/.test(tickformat)) + // %f: microseconds as a decimal number [000000, 999999] + // %L: milliseconds as a decimal number [000, 999] + // %Q: milliseconds since UNIX epoch + // %s: seconds since UNIX epoch + // %S: second as a decimal number [00,61] + // %M: minute as a decimal number [00,59] + // %X: the locale’s time, such as %-I:%M:%S %p + ) { + if( + /%[HI]/.test(tickformat) + // %H: hour (24-hour clock) as a decimal number [00,23] + // %I: hour (12-hour clock) as a decimal number [01,12] + ) { + definedDelta = ONEHOUR; + if(noDtick && !isMDate && ax.dtick < ONEHOUR) ax.dtick = ONEHOUR; + } else if( + /%p/.test(tickformat) // %p: either AM or PM + ) { + definedDelta = HALFDAY; + if(noDtick && !isMDate && ax.dtick < HALFDAY) ax.dtick = HALFDAY; + } else if( + /%[Aadejuwx]/.test(tickformat) + // %A: full weekday name + // %a: abbreviated weekday name + // %d: zero-padded day of the month as a decimal number [01,31] + // %e: space-padded day of the month as a decimal number [ 1,31] + // %j: day of the year as a decimal number [001,366] + // %u: Monday-based (ISO 8601) weekday as a decimal number [1,7] + // %w: Sunday-based weekday as a decimal number [0,6] + // %x: the locale’s date, such as %-m/%-d/%Y + ) { + definedDelta = ONEDAY; + if(noDtick && !isMDate && ax.dtick < ONEDAY) ax.dtick = ONEDAY; + } else if( + /%[UVW]/.test(tickformat) + // %U: Sunday-based week of the year as a decimal number [00,53] + // %V: ISO 8601 week of the year as a decimal number [01, 53] + // %W: Monday-based week of the year as a decimal number [00,53] + ) { + definedDelta = ONEWEEK; + if(noDtick && !isMDate && ax.dtick < ONEWEEK) ax.dtick = ONEWEEK; + } else if( + /%[Bbm]/.test(tickformat) + // %B: full month name + // %b: abbreviated month name + // %m: month as a decimal number [01,12] + ) { + definedDelta = ONEAVGMONTH; + if(noDtick && ( + isMDate ? nMonths(ax.dtick) < 1 : ax.dtick < ONEMINMONTH) + ) ax.dtick = 'M1'; + } else if( + /%[q]/.test(tickformat) + // %q: quarter of the year as a decimal number [1,4] + ) { + definedDelta = ONEAVGQUARTER; + if(noDtick && ( + isMDate ? nMonths(ax.dtick) < 3 : ax.dtick < ONEMINQUARTER) + ) ax.dtick = 'M3'; + } else if( + /%[Yy]/.test(tickformat) + // %Y: year with century as a decimal number, such as 1999 + // %y: year without century as a decimal number [00,99] + ) { + definedDelta = ONEAVGYEAR; + if(noDtick && ( + isMDate ? nMonths(ax.dtick) < 12 : ax.dtick < ONEMINYEAR) + ) ax.dtick = 'M12'; + } + } + } + + isMDate = mDate(); + if(isMDate && ax.tick0 === ax._dowTick0) { + // discard Sunday/Monday tweaks + ax.tick0 = ax._rawTick0; + } + + ax._definedDelta = definedDelta; +} + +function positionPeriodTicks(tickVals, ax, definedDelta) { + for(var i = 0; i < tickVals.length; i++) { + var v = tickVals[i].value; + + var a = i; + var b = i + 1; + if(i < tickVals.length - 1) { + a = i; + b = i + 1; + } else if(i > 0) { + a = i - 1; + b = i; + } else { + a = i; + b = i; + } + + var A = tickVals[a].value; + var B = tickVals[b].value; + var actualDelta = Math.abs(B - A); + var delta = definedDelta || actualDelta; + var periodLength = 0; + + if(delta >= ONEMINYEAR) { + if(actualDelta >= ONEMINYEAR && actualDelta <= ONEMAXYEAR) { + periodLength = actualDelta; + } else { + periodLength = ONEAVGYEAR; + } + } else if(definedDelta === ONEAVGQUARTER && delta >= ONEMINQUARTER) { + if(actualDelta >= ONEMINQUARTER && actualDelta <= ONEMAXQUARTER) { + periodLength = actualDelta; + } else { + periodLength = ONEAVGQUARTER; + } + } else if(delta >= ONEMINMONTH) { + if(actualDelta >= ONEMINMONTH && actualDelta <= ONEMAXMONTH) { + periodLength = actualDelta; + } else { + periodLength = ONEAVGMONTH; + } + } else if(definedDelta === ONEWEEK && delta >= ONEWEEK) { + periodLength = ONEWEEK; + } else if(delta >= ONEDAY) { + periodLength = ONEDAY; + } else if(definedDelta === HALFDAY && delta >= HALFDAY) { + periodLength = HALFDAY; + } else if(definedDelta === ONEHOUR && delta >= ONEHOUR) { + periodLength = ONEHOUR; + } + + var inBetween; + if(periodLength >= actualDelta) { + // ensure new label positions remain between ticks + periodLength = actualDelta; + inBetween = true; + } + + var endPeriod = v + periodLength; + if(ax.rangebreaks && periodLength > 0) { + var nAll = 84; // highly divisible 7 * 12 + var n = 0; + for(var c = 0; c < nAll; c++) { + var r = (c + 0.5) / nAll; + if(ax.maskBreaks(v * (1 - r) + r * endPeriod) !== BADNUM) n++; + } + periodLength *= n / nAll; + + if(!periodLength) { + tickVals[i].drop = true; + } + + if(inBetween && actualDelta > ONEWEEK) periodLength = actualDelta; // center monthly & longer periods + } + + if( + periodLength > 0 || // not instant + i === 0 // taking care first tick added + ) { + tickVals[i].periodX = v + periodLength / 2; + } + } +} + +// calculate the ticks: text, values, positioning +// if ticks are set to automatic, determine the right values (tick0,dtick) +// in any case, set tickround to # of digits to round tick labels to, +// or codes to this effect for log and date scales +axes.calcTicks = function calcTicks(ax, opts) { + axes.prepTicks(ax, opts); + var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts); + + // now that we've figured out the auto values for formatting + // in case we're missing some ticktext, we can break out for array ticks + if(ax.tickmode === 'array') return arrayTicks(ax); + + // add a tiny bit so we get ticks which may have rounded out + var exRng = expandRange(rng); + var startTick = exRng[0]; + var endTick = exRng[1]; + // check for reversed axis + var axrev = (rng[1] < rng[0]); + var minRange = Math.min(rng[0], rng[1]); + var maxRange = Math.max(rng[0], rng[1]); + + var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L'); + var isPeriod = ax.ticklabelmode === 'period'; + + // find the first tick + ax._tmin = axes.tickFirst(ax, opts); + + // No visible ticks? Quit. + // I've only seen this on category axes with all categories off the edge. + if((ax._tmin < startTick) !== axrev) return []; + + // return the full set of tick vals + if(ax.type === 'category' || ax.type === 'multicategory') { + endTick = (axrev) ? Math.max(-0.5, endTick) : + Math.min(ax._categories.length - 0.5, endTick); + } + + var x = ax._tmin; + + if(ax.rangebreaks && ax._tick0Init !== ax.tick0) { + // adjust tick0 + x = moveOutsideBreak(x, ax); + if(!axrev) { + x = axes.tickIncrement(x, ax.dtick, !axrev, ax.calendar); + } + } + + if(isPeriod) { + // add one item to label period before tick0 + x = axes.tickIncrement(x, ax.dtick, !axrev, ax.calendar); + } + + var maxTicks = Math.max(1000, ax._length || 0); + var tickVals = []; + var xPrevious = null; + for(; + (axrev) ? (x >= endTick) : (x <= endTick); + x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar) + ) { + if(ax.rangebreaks) { + if(!axrev) { + if(x < startTick) continue; + if(ax.maskBreaks(x) === BADNUM && moveOutsideBreak(x, ax) >= maxRange) break; + } + } + + // prevent infinite loops - no more than one tick per pixel, + // and make sure each value is different from the previous + if(tickVals.length > maxTicks || x === xPrevious) break; + xPrevious = x; + + var minor = false; + if(isDLog && (x !== (x | 0))) { + minor = true; + } + + tickVals.push({ + minor: minor, + value: x + }); + } + + if(isPeriod) positionPeriodTicks(tickVals, ax, ax._definedDelta); + + var i; + if(ax.rangebreaks) { + var flip = ax._id.charAt(0) === 'y'; + + var fontSize = 1; // one pixel minimum + if(ax.tickmode === 'auto') { + fontSize = ax.tickfont ? ax.tickfont.size : 12; + } + + var prevL = NaN; + for(i = tickVals.length - 1; i > -1; i--) { + if(tickVals[i].drop) { + tickVals.splice(i, 1); + continue; + } + + tickVals[i].value = moveOutsideBreak(tickVals[i].value, ax); + + // avoid overlaps + var l = ax.c2p(tickVals[i].value); + if(flip ? + (prevL > l - fontSize) : + (prevL < l + fontSize) + ) { // ensure one pixel minimum + tickVals.splice(axrev ? i + 1 : i, 1); + } else { + prevL = l; + } + } + } + + // If same angle over a full circle, the last tick vals is a duplicate. + // TODO must do something similar for angular date axes. + if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) { + tickVals.pop(); + } + + // save the last tick as well as first, so we can + // show the exponent only on the last one + ax._tmax = (tickVals[tickVals.length - 1] || {}).value; + + // for showing the rest of a date when the main tick label is only the + // latter part: ax._prevDateHead holds what we showed most recently. + // Start with it cleared and mark that we're in calcTicks (ie calculating a + // whole string of these so we should care what the previous date head was!) + ax._prevDateHead = ''; + ax._inCalcTicks = true; + + var ticksOut = []; + var t, p; + for(i = 0; i < tickVals.length; i++) { + var _minor = tickVals[i].minor; + var _value = tickVals[i].value; + + t = axes.tickText( + ax, + _value, + false, // hover + _minor // noSuffixPrefix + ); + + p = tickVals[i].periodX; + if(p !== undefined) { + t.periodX = p; + if(p > maxRange || p < minRange) { // hide label if outside the range + if(p > maxRange) t.periodX = maxRange; + if(p < minRange) t.periodX = minRange; + + t.text = ' '; // don't use an empty string here which can confuse automargin (issue 5132) + ax._prevDateHead = ''; + } + } + + ticksOut.push(t); + } + + ax._inCalcTicks = false; + + return ticksOut; +}; + +function arrayTicks(ax) { + var vals = ax.tickvals; + var text = ax.ticktext; + var ticksOut = new Array(vals.length); + var rng = Lib.simpleMap(ax.range, ax.r2l); + var exRng = expandRange(rng); + var tickMin = Math.min(exRng[0], exRng[1]); + var tickMax = Math.max(exRng[0], exRng[1]); + var j = 0; + + // without a text array, just format the given values as any other ticks + // except with more precision to the numbers + if(!Array.isArray(text)) text = []; + + // make sure showing ticks doesn't accidentally add new categories + // TODO multicategory, if we allow ticktext / tickvals + var tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l; + + // array ticks on log axes always show the full number + // (if no explicit ticktext overrides it) + if(ax.type === 'log' && String(ax.dtick).charAt(0) !== 'L') { + ax.dtick = 'L' + Math.pow(10, Math.floor(Math.min(ax.range[0], ax.range[1])) - 1); + } + + for(var i = 0; i < vals.length; i++) { + var vali = tickVal2l(vals[i]); + if(vali > tickMin && vali < tickMax) { + if(text[i] === undefined) ticksOut[j] = axes.tickText(ax, vali); + else ticksOut[j] = tickTextObj(ax, vali, String(text[i])); + j++; + } + } + + if(j < vals.length) ticksOut.splice(j, vals.length - j); + + if(ax.rangebreaks) { + // remove ticks falling inside rangebreaks + ticksOut = ticksOut.filter(function(d) { + return ax.maskBreaks(d.x) !== BADNUM; + }); + } + + return ticksOut; +} + +var roundBase10 = [2, 5, 10]; +var roundBase24 = [1, 2, 3, 6, 12]; +var roundBase60 = [1, 2, 5, 10, 15, 30]; +// 2&3 day ticks are weird, but need something btwn 1&7 +var roundDays = [1, 2, 3, 7, 14]; +// approx. tick positions for log axes, showing all (1) and just 1, 2, 5 (2) +// these don't have to be exact, just close enough to round to the right value +var roundLog1 = [-0.046, 0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1]; +var roundLog2 = [-0.301, 0, 0.301, 0.699, 1]; +// N.B. `thetaunit; 'radians' angular axes must be converted to degrees +var roundAngles = [15, 30, 45, 90, 180]; + +function roundDTick(roughDTick, base, roundingSet) { + return base * Lib.roundUp(roughDTick / base, roundingSet); +} + +// autoTicks: calculate best guess at pleasant ticks for this axis +// inputs: +// ax - an axis object +// roughDTick - rough tick spacing (to be turned into a nice round number) +// outputs (into ax): +// tick0: starting point for ticks (not necessarily on the graph) +// usually 0 for numeric (=10^0=1 for log) or jan 1, 2000 for dates +// dtick: the actual, nice round tick spacing, usually a little larger than roughDTick +// if the ticks are spaced linearly (linear scale, categories, +// log with only full powers, date ticks < month), +// this will just be a number +// months: M# +// years: M# where # is 12*number of years +// log with linear ticks: L# where # is the linear tick spacing +// log showing powers plus some intermediates: +// D1 shows all digits, D2 shows 2 and 5 +axes.autoTicks = function(ax, roughDTick) { + var base; + + function getBase(v) { + return Math.pow(v, Math.floor(Math.log(roughDTick) / Math.LN10)); + } + + if(ax.type === 'date') { + ax.tick0 = Lib.dateTick0(ax.calendar, 0); + + // the criteria below are all based on the rough spacing we calculate + // being > half of the final unit - so precalculate twice the rough val + var roughX2 = 2 * roughDTick; + + if(roughX2 > ONEAVGYEAR) { + roughDTick /= ONEAVGYEAR; + base = getBase(10); + ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10)); + } else if(roughX2 > ONEAVGMONTH) { + roughDTick /= ONEAVGMONTH; + ax.dtick = 'M' + roundDTick(roughDTick, 1, roundBase24); + } else if(roughX2 > ONEDAY) { + ax.dtick = roundDTick(roughDTick, ONEDAY, ax._hasDayOfWeekBreaks ? [1, 2, 7, 14] : roundDays); + // get week ticks on sunday + // this will also move the base tick off 2000-01-01 if dtick is + // 2 or 3 days... but that's a weird enough case that we'll ignore it. + var tickformat = axes.getTickFormat(ax); + var isPeriod = ax.ticklabelmode === 'period'; + if(isPeriod) ax._rawTick0 = ax.tick0; + + if(/%[uVW]/.test(tickformat)) { + ax.tick0 = Lib.dateTick0(ax.calendar, 2); // Monday + } else { + ax.tick0 = Lib.dateTick0(ax.calendar, 1); // Sunday + } + + if(isPeriod) ax._dowTick0 = ax.tick0; + } else if(roughX2 > ONEHOUR) { + ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24); + } else if(roughX2 > ONEMIN) { + ax.dtick = roundDTick(roughDTick, ONEMIN, roundBase60); + } else if(roughX2 > ONESEC) { + ax.dtick = roundDTick(roughDTick, ONESEC, roundBase60); + } else { + // milliseconds + base = getBase(10); + ax.dtick = roundDTick(roughDTick, base, roundBase10); + } + } else if(ax.type === 'log') { + ax.tick0 = 0; + var rng = Lib.simpleMap(ax.range, ax.r2l); + + if(roughDTick > 0.7) { + // only show powers of 10 + ax.dtick = Math.ceil(roughDTick); + } else if(Math.abs(rng[1] - rng[0]) < 1) { + // span is less than one power of 10 + var nt = 1.5 * Math.abs((rng[1] - rng[0]) / roughDTick); + + // ticks on a linear scale, labeled fully + roughDTick = Math.abs(Math.pow(10, rng[1]) - + Math.pow(10, rng[0])) / nt; + base = getBase(10); + ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10); + } else { + // include intermediates between powers of 10, + // labeled with small digits + // ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits) + ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1'; + } + } else if(ax.type === 'category' || ax.type === 'multicategory') { + ax.tick0 = 0; + ax.dtick = Math.ceil(Math.max(roughDTick, 1)); + } else if(isAngular(ax)) { + ax.tick0 = 0; + base = 1; + ax.dtick = roundDTick(roughDTick, base, roundAngles); + } else { + // auto ticks always start at 0 + ax.tick0 = 0; + base = getBase(10); + ax.dtick = roundDTick(roughDTick, base, roundBase10); + } + + // prevent infinite loops + if(ax.dtick === 0) ax.dtick = 1; + + // TODO: this is from log axis histograms with autorange off + if(!isNumeric(ax.dtick) && typeof ax.dtick !== 'string') { + var olddtick = ax.dtick; + ax.dtick = 1; + throw 'ax.dtick error: ' + String(olddtick); + } +}; + +// after dtick is already known, find tickround = precision +// to display in tick labels +// for numeric ticks, integer # digits after . to round to +// for date ticks, the last date part to show (y,m,d,H,M,S) +// or an integer # digits past seconds +function autoTickRound(ax) { + var dtick = ax.dtick; + + ax._tickexponent = 0; + if(!isNumeric(dtick) && typeof dtick !== 'string') { + dtick = 1; + } + + if(ax.type === 'category' || ax.type === 'multicategory') { + ax._tickround = null; + } + if(ax.type === 'date') { + // If tick0 is unusual, give tickround a bit more information + // not necessarily *all* the information in tick0 though, if it's really odd + // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19 + // take off a leading minus (year < 0) and i (intercalary month) so length is consistent + var tick0ms = ax.r2l(ax.tick0); + var tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, ''); + var tick0len = tick0str.length; + + if(String(dtick).charAt(0) === 'M') { + // any tick0 more specific than a year: alway show the full date + if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd'; + // show the month unless ticks are full multiples of a year + else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm'; + } else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd'; + else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M'; + else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S'; + else { + // tickround is a number of digits of fractional seconds + // of any two adjacent ticks, at least one will have the maximum fractional digits + // of all possible ticks - so take the max. length of tick0 and the next one + var tick1len = ax.l2r(tick0ms + dtick).replace(/^-/, '').length; + ax._tickround = Math.max(tick0len, tick1len) - 20; + + // We shouldn't get here... but in case there's a situation I'm + // not thinking of where tick0str and tick1str are identical or + // something, fall back on maximum precision + if(ax._tickround < 0) ax._tickround = 4; + } + } else if(isNumeric(dtick) || dtick.charAt(0) === 'L') { + // linear or log (except D1, D2) + var rng = ax.range.map(ax.r2d || Number); + if(!isNumeric(dtick)) dtick = Number(dtick.substr(1)); + // 2 digits past largest digit of dtick + ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01); + + var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1])); + var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01); + var minexponent = ax.minexponent === undefined ? 3 : ax.minexponent; + if(Math.abs(rangeexp) > minexponent) { + if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) { + ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3); + } else ax._tickexponent = rangeexp; + } + } else { + // D1 or D2 (log) + ax._tickround = null; + } +} + +// months and years don't have constant millisecond values +// (but a year is always 12 months so we only need months) +// log-scale ticks are also not consistently spaced, except +// for pure powers of 10 +// numeric ticks always have constant differences, other datetime ticks +// can all be calculated as constant number of milliseconds +axes.tickIncrement = function(x, dtick, axrev, calendar) { + var axSign = axrev ? -1 : 1; + + // includes linear, all dates smaller than month, and pure 10^n in log + if(isNumeric(dtick)) return Lib.increment(x, axSign * dtick); + + // everything else is a string, one character plus a number + var tType = dtick.charAt(0); + var dtSigned = axSign * Number(dtick.substr(1)); + + // Dates: months (or years - see Lib.incrementMonth) + if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar); + + // Log scales: Linear, Digits + if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10; + + // log10 of 2,5,10, or all digits (logs just have to be + // close enough to round) + if(tType === 'D') { + var tickset = (dtick === 'D2') ? roundLog2 : roundLog1; + var x2 = x + axSign * 0.01; + var frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev); + + return Math.floor(x2) + + Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10; + } + + throw 'unrecognized dtick ' + String(dtick); +}; + +// calculate the first tick on an axis +axes.tickFirst = function(ax, opts) { + var r2l = ax.r2l || Number; + var rng = Lib.simpleMap(ax.range, r2l, undefined, undefined, opts); + var axrev = rng[1] < rng[0]; + var sRound = axrev ? Math.floor : Math.ceil; + // add a tiny extra bit to make sure we get ticks + // that may have been rounded out + var r0 = expandRange(rng)[0]; + var dtick = ax.dtick; + var tick0 = r2l(ax.tick0); + + if(isNumeric(dtick)) { + var tmin = sRound((r0 - tick0) / dtick) * dtick + tick0; + + // make sure no ticks outside the category list + if(ax.type === 'category' || ax.type === 'multicategory') { + tmin = Lib.constrain(tmin, 0, ax._categories.length - 1); + } + return tmin; + } + + var tType = dtick.charAt(0); + var dtNum = Number(dtick.substr(1)); + + // Dates: months (or years) + if(tType === 'M') { + var cnt = 0; + var t0 = tick0; + var t1, mult, newDTick; + + // This algorithm should work for *any* nonlinear (but close to linear!) + // tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3. + while(cnt < 10) { + t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar); + if((t1 - r0) * (t0 - r0) <= 0) { + // t1 and t0 are on opposite sides of r0! we've succeeded! + if(axrev) return Math.min(t0, t1); + return Math.max(t0, t1); + } + mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0); + newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum); + t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar); + cnt++; + } + Lib.error('tickFirst did not converge', ax); + return t0; + } else if(tType === 'L') { + // Log scales: Linear, Digits + + return Math.log(sRound( + (Math.pow(10, r0) - tick0) / dtNum) * dtNum + tick0) / Math.LN10; + } else if(tType === 'D') { + var tickset = (dtick === 'D2') ? roundLog2 : roundLog1; + var frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev); + + return Math.floor(r0) + + Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10; + } else throw 'unrecognized dtick ' + String(dtick); +}; + +// draw the text for one tick. +// px,py are the location on gd.paper +// prefix is there so the x axis ticks can be dropped a line +// ax is the axis layout, x is the tick value +// hover is a (truthy) flag for whether to show numbers with a bit +// more precision for hovertext +axes.tickText = function(ax, x, hover, noSuffixPrefix) { + var out = tickTextObj(ax, x); + var arrayMode = ax.tickmode === 'array'; + var extraPrecision = hover || arrayMode; + var axType = ax.type; + // TODO multicategory, if we allow ticktext / tickvals + var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l; + var i; + + if(arrayMode && Array.isArray(ax.ticktext)) { + var rng = Lib.simpleMap(ax.range, ax.r2l); + var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000; + + for(i = 0; i < ax.ticktext.length; i++) { + if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break; + } + if(i < ax.ticktext.length) { + out.text = String(ax.ticktext[i]); + return out; + } + } + + function isHidden(showAttr) { + if(showAttr === undefined) return true; + if(hover) return showAttr === 'none'; + + var firstOrLast = { + first: ax._tmin, + last: ax._tmax + }[showAttr]; + + return showAttr !== 'all' && x !== firstOrLast; + } + + var hideexp = hover ? + 'never' : + ax.exponentformat !== 'none' && isHidden(ax.showexponent) ? 'hide' : ''; + + if(axType === 'date') formatDate(ax, out, hover, extraPrecision); + else if(axType === 'log') formatLog(ax, out, hover, extraPrecision, hideexp); + else if(axType === 'category') formatCategory(ax, out); + else if(axType === 'multicategory') formatMultiCategory(ax, out, hover); + else if(isAngular(ax)) formatAngle(ax, out, hover, extraPrecision, hideexp); + else formatLinear(ax, out, hover, extraPrecision, hideexp); + + // add prefix and suffix + if(!noSuffixPrefix) { + if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text; + if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix; + } + + // Setup ticks and grid lines boundaries + // at 1/2 a 'category' to the left/bottom + if(ax.tickson === 'boundaries' || ax.showdividers) { + var inbounds = function(v) { + var p = ax.l2p(v); + return p >= 0 && p <= ax._length ? v : null; + }; + + out.xbnd = [ + inbounds(out.x - 0.5), + inbounds(out.x + ax.dtick - 0.5) + ]; + } + + return out; +}; + +/** + * create text for a hover label on this axis, with special handling of + * log axes (where negative values can't be displayed but can appear in hover text) + * + * @param {object} ax: the axis to format text for + * @param {number or array of numbers} values: calcdata value(s) to format + * @param {Optional(string)} hoverformat: trace (x|y)hoverformat to override axis.hoverformat + * + * @returns {string} `val` formatted as a string appropriate to this axis, or + * first value and second value as a range (ie ' - ') if the second value is provided and + * it's different from the first value. + */ +axes.hoverLabelText = function(ax, values, hoverformat) { + if(hoverformat) ax = Lib.extendFlat({}, ax, {hoverformat: hoverformat}); + + var val = Array.isArray(values) ? values[0] : values; + var val2 = Array.isArray(values) ? values[1] : undefined; + if(val2 !== undefined && val2 !== val) { + return ( + axes.hoverLabelText(ax, val, hoverformat) + ' - ' + + axes.hoverLabelText(ax, val2, hoverformat) + ); + } + + var logOffScale = (ax.type === 'log' && val <= 0); + var tx = axes.tickText(ax, ax.c2l(logOffScale ? -val : val), 'hover').text; + + if(logOffScale) { + return val === 0 ? '0' : MINUS_SIGN + tx; + } + + // TODO: should we do something special if the axis calendar and + // the data calendar are different? Somehow display both dates with + // their system names? Right now it will just display in the axis calendar + // but users could add the other one as text. + return tx; +}; + +function tickTextObj(ax, x, text) { + var tf = ax.tickfont || {}; + + return { + x: x, + dx: 0, + dy: 0, + text: text || '', + fontSize: tf.size, + font: tf.family, + fontColor: tf.color + }; +} + +function formatDate(ax, out, hover, extraPrecision) { + var tr = ax._tickround; + var fmt = (hover && ax.hoverformat) || axes.getTickFormat(ax); + + if(extraPrecision) { + // second or sub-second precision: extra always shows max digits. + // for other fields, extra precision just adds one field. + if(isNumeric(tr)) tr = 4; + else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 4}[tr]; + } + + var dateStr = Lib.formatDate(out.x, fmt, tr, ax._dateFormat, ax.calendar, ax._extraFormat); + var headStr; + + var splitIndex = dateStr.indexOf('\n'); + if(splitIndex !== -1) { + headStr = dateStr.substr(splitIndex + 1); + dateStr = dateStr.substr(0, splitIndex); + } + + if(extraPrecision) { + // if extraPrecision led to trailing zeros, strip them off + // actually, this can lead to removing even more zeros than + // in the original rounding, but that's fine because in these + // contexts uniformity is not so important (if there's even + // anything to be uniform with!) + + // can we remove the whole time part? + if(dateStr === '00:00:00' || dateStr === '00:00') { + dateStr = headStr; + headStr = ''; + } else if(dateStr.length === 8) { + // strip off seconds if they're zero (zero fractional seconds + // are already omitted) + // but we never remove minutes and leave just hours + dateStr = dateStr.replace(/:00$/, ''); + } + } + + if(headStr) { + if(hover) { + // hover puts it all on one line, so headPart works best up front + // except for year headPart: turn this into "Jan 1, 2000" etc. + if(tr === 'd') dateStr += ', ' + headStr; + else dateStr = headStr + (dateStr ? ', ' + dateStr : ''); + } else { + if( + !ax._inCalcTicks || + ax._prevDateHead !== headStr + ) { + ax._prevDateHead = headStr; + dateStr += '
' + headStr; + } else { + var isInside = insideTicklabelposition(ax); + var side = ax._realSide || ax.side; // polar mocks the side of the radial axis + if( + (!isInside && side === 'top') || + (isInside && side === 'bottom') + ) { + dateStr += '
'; + } + } + } + } + + out.text = dateStr; +} + +function formatLog(ax, out, hover, extraPrecision, hideexp) { + var dtick = ax.dtick; + var x = out.x; + var tickformat = ax.tickformat; + var dtChar0 = typeof dtick === 'string' && dtick.charAt(0); + + if(hideexp === 'never') { + // If this is a hover label, then we must *never* hide the exponent + // for the sake of display, which could give the wrong value by + // potentially many orders of magnitude. If hideexp was 'never', then + // it's now succeeded by preventing the other condition from automating + // this choice. Thus we can unset it so that the axis formatting takes + // precedence. + hideexp = ''; + } + + if(extraPrecision && (dtChar0 !== 'L')) { + dtick = 'L3'; + dtChar0 = 'L'; + } + + if(tickformat || (dtChar0 === 'L')) { + out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision); + } else if(isNumeric(dtick) || ((dtChar0 === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) { + var p = Math.round(x); + var absP = Math.abs(p); + var exponentFormat = ax.exponentformat; + if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) { + if(p === 0) out.text = 1; + else if(p === 1) out.text = '10'; + else out.text = '10' + (p > 1 ? '' : MINUS_SIGN) + absP + ''; + + out.fontSize *= 1.25; + } else if((exponentFormat === 'e' || exponentFormat === 'E') && absP > 2) { + out.text = '1' + exponentFormat + (p > 0 ? '+' : MINUS_SIGN) + absP; + } else { + out.text = numFormat(Math.pow(10, x), ax, '', 'fakehover'); + if(dtick === 'D1' && ax._id.charAt(0) === 'y') { + out.dy -= out.fontSize / 6; + } + } + } else if(dtChar0 === 'D') { + out.text = String(Math.round(Math.pow(10, Lib.mod(x, 1)))); + out.fontSize *= 0.75; + } else throw 'unrecognized dtick ' + String(dtick); + + // if 9's are printed on log scale, move the 10's away a bit + if(ax.dtick === 'D1') { + var firstChar = String(out.text).charAt(0); + if(firstChar === '0' || firstChar === '1') { + if(ax._id.charAt(0) === 'y') { + out.dx -= out.fontSize / 4; + } else { + out.dy += out.fontSize / 2; + out.dx += (ax.range[1] > ax.range[0] ? 1 : -1) * + out.fontSize * (x < 0 ? 0.5 : 0.25); + } + } + } +} + +function formatCategory(ax, out) { + var tt = ax._categories[Math.round(out.x)]; + if(tt === undefined) tt = ''; + out.text = String(tt); +} + +function formatMultiCategory(ax, out, hover) { + var v = Math.round(out.x); + var cats = ax._categories[v] || []; + var tt = cats[1] === undefined ? '' : String(cats[1]); + var tt2 = cats[0] === undefined ? '' : String(cats[0]); + + if(hover) { + // TODO is this what we want? + out.text = tt2 + ' - ' + tt; + } else { + // setup for secondary labels + out.text = tt; + out.text2 = tt2; + } +} + +function formatLinear(ax, out, hover, extraPrecision, hideexp) { + if(hideexp === 'never') { + // If this is a hover label, then we must *never* hide the exponent + // for the sake of display, which could give the wrong value by + // potentially many orders of magnitude. If hideexp was 'never', then + // it's now succeeded by preventing the other condition from automating + // this choice. Thus we can unset it so that the axis formatting takes + // precedence. + hideexp = ''; + } else if(ax.showexponent === 'all' && Math.abs(out.x / ax.dtick) < 1e-6) { + // don't add an exponent to zero if we're showing all exponents + // so the only reason you'd show an exponent on zero is if it's the + // ONLY tick to get an exponent (first or last) + hideexp = 'hide'; + } + out.text = numFormat(out.x, ax, hideexp, extraPrecision); +} + +function formatAngle(ax, out, hover, extraPrecision, hideexp) { + if(ax.thetaunit === 'radians' && !hover) { + var num = out.x / 180; + + if(num === 0) { + out.text = '0'; + } else { + var frac = num2frac(num); + + if(frac[1] >= 100) { + out.text = numFormat(Lib.deg2rad(out.x), ax, hideexp, extraPrecision); + } else { + var isNeg = out.x < 0; + + if(frac[1] === 1) { + if(frac[0] === 1) out.text = 'π'; + else out.text = frac[0] + 'π'; + } else { + out.text = [ + '', frac[0], '', + '⁄', + '', frac[1], '', + 'π' + ].join(''); + } + + if(isNeg) out.text = MINUS_SIGN + out.text; + } + } + } else { + out.text = numFormat(out.x, ax, hideexp, extraPrecision); + } +} + +// inspired by +// https://github.com/yisibl/num2fraction/blob/master/index.js +function num2frac(num) { + function almostEq(a, b) { + return Math.abs(a - b) <= 1e-6; + } + + function findGCD(a, b) { + return almostEq(b, 0) ? a : findGCD(b, a % b); + } + + function findPrecision(n) { + var e = 1; + while(!almostEq(Math.round(n * e) / e, n)) { + e *= 10; + } + return e; + } + + var precision = findPrecision(num); + var number = num * precision; + var gcd = Math.abs(findGCD(number, precision)); + + return [ + // numerator + Math.round(number / gcd), + // denominator + Math.round(precision / gcd) + ]; +} + +// format a number (tick value) according to the axis settings +// new, more reliable procedure than d3.round or similar: +// add half the rounding increment, then stringify and truncate +// also automatically switch to sci. notation +var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T']; + +function isSIFormat(exponentFormat) { + return exponentFormat === 'SI' || exponentFormat === 'B'; +} + +// are we beyond the range of common SI prefixes? +// 10^-16 -> 1x10^-16 +// 10^-15 -> 1f +// ... +// 10^14 -> 100T +// 10^15 -> 1x10^15 +// 10^16 -> 1x10^16 +function beyondSI(exponent) { + return exponent > 14 || exponent < -15; +} + +function numFormat(v, ax, fmtoverride, hover) { + var isNeg = v < 0; + // max number of digits past decimal point to show + var tickRound = ax._tickround; + var exponentFormat = fmtoverride || ax.exponentformat || 'B'; + var exponent = ax._tickexponent; + var tickformat = axes.getTickFormat(ax); + var separatethousands = ax.separatethousands; + + // special case for hover: set exponent just for this value, and + // add a couple more digits of precision over tick labels + if(hover) { + // make a dummy axis obj to get the auto rounding and exponent + var ah = { + exponentformat: exponentFormat, + minexponent: ax.minexponent, + dtick: ax.showexponent === 'none' ? ax.dtick : + (isNumeric(v) ? Math.abs(v) || 1 : 1), + // if not showing any exponents, don't change the exponent + // from what we calculate + range: ax.showexponent === 'none' ? ax.range.map(ax.r2d) : [0, v || 1] + }; + autoTickRound(ah); + tickRound = (Number(ah._tickround) || 0) + 4; + exponent = ah._tickexponent; + if(ax.hoverformat) tickformat = ax.hoverformat; + } + + if(tickformat) return ax._numFormat(tickformat)(v).replace(/-/g, MINUS_SIGN); + + // 'epsilon' - rounding increment + var e = Math.pow(10, -tickRound) / 2; + + // exponentFormat codes: + // 'e' (1.2e+6, default) + // 'E' (1.2E+6) + // 'SI' (1.2M) + // 'B' (same as SI except 10^9=B not G) + // 'none' (1200000) + // 'power' (1.2x10^6) + // 'hide' (1.2, use 3rd argument=='hide' to eg + // only show exponent on last tick) + if(exponentFormat === 'none') exponent = 0; + + // take the sign out, put it back manually at the end + // - makes cases easier + v = Math.abs(v); + if(v < e) { + // 0 is just 0, but may get exponent if it's the last tick + v = '0'; + isNeg = false; + } else { + v += e; + // take out a common exponent, if any + if(exponent) { + v *= Math.pow(10, -exponent); + tickRound += exponent; + } + // round the mantissa + if(tickRound === 0) v = String(Math.floor(v)); + else if(tickRound < 0) { + v = String(Math.round(v)); + v = v.substr(0, v.length + tickRound); + for(var i = tickRound; i < 0; i++) v += '0'; + } else { + v = String(v); + var dp = v.indexOf('.') + 1; + if(dp) v = v.substr(0, dp + tickRound).replace(/\.?0+$/, ''); + } + // insert appropriate decimal point and thousands separator + v = Lib.numSeparate(v, ax._separators, separatethousands); + } + + // add exponent + if(exponent && exponentFormat !== 'hide') { + if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power'; + + var signedExponent; + if(exponent < 0) signedExponent = MINUS_SIGN + -exponent; + else if(exponentFormat !== 'power') signedExponent = '+' + exponent; + else signedExponent = String(exponent); + + if(exponentFormat === 'e' || exponentFormat === 'E') { + v += exponentFormat + signedExponent; + } else if(exponentFormat === 'power') { + v += '×10' + signedExponent + ''; + } else if(exponentFormat === 'B' && exponent === 9) { + v += 'B'; + } else if(isSIFormat(exponentFormat)) { + v += SIPREFIXES[exponent / 3 + 5]; + } + } + + // put sign back in and return + // replace standard minus character (which is technically a hyphen) + // with a true minus sign + if(isNeg) return MINUS_SIGN + v; + return v; +} + +axes.getTickFormat = function(ax) { + var i; + + function convertToMs(dtick) { + return typeof dtick !== 'string' ? dtick : Number(dtick.replace('M', '')) * ONEAVGMONTH; + } + + function compareLogTicks(left, right) { + var priority = ['L', 'D']; + if(typeof left === typeof right) { + if(typeof left === 'number') { + return left - right; + } else { + var leftPriority = priority.indexOf(left.charAt(0)); + var rightPriority = priority.indexOf(right.charAt(0)); + if(leftPriority === rightPriority) { + return Number(left.replace(/(L|D)/g, '')) - Number(right.replace(/(L|D)/g, '')); + } else { + return leftPriority - rightPriority; + } + } + } else { + return typeof left === 'number' ? 1 : -1; + } + } + + function isProperStop(dtick, range, convert) { + var convertFn = convert || function(x) { return x;}; + var leftDtick = range[0]; + var rightDtick = range[1]; + return ((!leftDtick && typeof leftDtick !== 'number') || convertFn(leftDtick) <= convertFn(dtick)) && + ((!rightDtick && typeof rightDtick !== 'number') || convertFn(rightDtick) >= convertFn(dtick)); + } + + function isProperLogStop(dtick, range) { + var isLeftDtickNull = range[0] === null; + var isRightDtickNull = range[1] === null; + var isDtickInRangeLeft = compareLogTicks(dtick, range[0]) >= 0; + var isDtickInRangeRight = compareLogTicks(dtick, range[1]) <= 0; + return (isLeftDtickNull || isDtickInRangeLeft) && (isRightDtickNull || isDtickInRangeRight); + } + + var tickstop, stopi; + if(ax.tickformatstops && ax.tickformatstops.length > 0) { + switch(ax.type) { + case 'date': + case 'linear': { + for(i = 0; i < ax.tickformatstops.length; i++) { + stopi = ax.tickformatstops[i]; + if(stopi.enabled && isProperStop(ax.dtick, stopi.dtickrange, convertToMs)) { + tickstop = stopi; + break; + } + } + break; + } + case 'log': { + for(i = 0; i < ax.tickformatstops.length; i++) { + stopi = ax.tickformatstops[i]; + if(stopi.enabled && isProperLogStop(ax.dtick, stopi.dtickrange)) { + tickstop = stopi; + break; + } + } + break; + } + default: + } + } + return tickstop ? tickstop.value : ax.tickformat; +}; + +// getSubplots - extract all subplot IDs we need +// as an array of items like 'xy', 'x2y', 'x2y2'... +// sorted by x (x,x2,x3...) then y +// optionally restrict to only subplots containing axis object ax +// +// NOTE: this is currently only used OUTSIDE plotly.js (toolpanel, webapp) +// ideally we get rid of it there (or just copy this there) and remove it here +axes.getSubplots = function(gd, ax) { + var subplotObj = gd._fullLayout._subplots; + var allSubplots = subplotObj.cartesian.concat(subplotObj.gl2d || []); + + var out = ax ? axes.findSubplotsWithAxis(allSubplots, ax) : allSubplots; + + out.sort(function(a, b) { + var aParts = a.substr(1).split('y'); + var bParts = b.substr(1).split('y'); + + if(aParts[0] === bParts[0]) return +aParts[1] - +bParts[1]; + return +aParts[0] - +bParts[0]; + }); + + return out; +}; + +// find all subplots with axis 'ax' +// NOTE: this is only used in axes.getSubplots (only used outside plotly.js) and +// gl2d/convert (where it restricts axis subplots to only those with gl2d) +axes.findSubplotsWithAxis = function(subplots, ax) { + var axMatch = new RegExp( + (ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$') + ); + var subplotsWithAx = []; + + for(var i = 0; i < subplots.length; i++) { + var sp = subplots[i]; + if(axMatch.test(sp)) subplotsWithAx.push(sp); + } + + return subplotsWithAx; +}; + +// makeClipPaths: prepare clipPaths for all single axes and all possible xy pairings +axes.makeClipPaths = function(gd) { + var fullLayout = gd._fullLayout; + + // for more info: https://github.com/plotly/plotly.js/issues/2595 + if(fullLayout._hasOnlyLargeSploms) return; + + var fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''}; + var fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''}; + var xaList = axes.list(gd, 'x', true); + var yaList = axes.list(gd, 'y', true); + var clipList = []; + var i, j; + + for(i = 0; i < xaList.length; i++) { + clipList.push({x: xaList[i], y: fullHeight}); + for(j = 0; j < yaList.length; j++) { + if(i === 0) clipList.push({x: fullWidth, y: yaList[j]}); + clipList.push({x: xaList[i], y: yaList[j]}); + } + } + + // selectors don't work right with camelCase tags, + // have to use class instead + // https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I + var axClips = fullLayout._clips.selectAll('.axesclip') + .data(clipList, function(d) { return d.x._id + d.y._id; }); + + axClips.enter().append('clipPath') + .classed('axesclip', true) + .attr('id', function(d) { return 'clip' + fullLayout._uid + d.x._id + d.y._id; }) + .append('rect'); + + axClips.exit().remove(); + + axClips.each(function(d) { + d3.select(this).select('rect').attr({ + x: d.x._offset || 0, + y: d.y._offset || 0, + width: d.x._length || 1, + height: d.y._length || 1 + }); + }); +}; + +/** + * Main multi-axis drawing routine! + * + * @param {DOM element} gd : graph div + * @param {string or array of strings} arg : polymorphic argument + * @param {object} opts: + * - @param {boolean} skipTitle : optional flag to skip axis title draw/update + * + * Signature 1: Axes.draw(gd, 'redraw') + * use this to clear and redraw all axes on graph + * + * Signature 2: Axes.draw(gd, '') + * use this to draw all axes on graph w/o the selectAll().remove() + * of the 'redraw' signature + * + * Signature 3: Axes.draw(gd, [axId, axId2, ...]) + * where the items are axis id string, + * use this to update multiple axes in one call + * + * N.B draw updates: + * - ax._r (stored range for use by zoom/pan) + * - ax._rl (stored linearized range for use by zoom/pan) + */ +axes.draw = function(gd, arg, opts) { + var fullLayout = gd._fullLayout; + + if(arg === 'redraw') { + fullLayout._paper.selectAll('g.subplot').each(function(d) { + var id = d[0]; + var plotinfo = fullLayout._plots[id]; + if(plotinfo) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove(); + plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove(); + plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove(); + plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick2').remove(); + plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove(); + plotinfo.yaxislayer.selectAll('.' + ya._id + 'divider').remove(); + + if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove(); + if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove(); + + fullLayout._infolayer.select('.g-' + xa._id + 'title').remove(); + fullLayout._infolayer.select('.g-' + ya._id + 'title').remove(); + } + }); + } + + var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg; + + return Lib.syncOrAsync(axList.map(function(axId) { + return function() { + if(!axId) return; + + var ax = axes.getFromId(gd, axId); + var axDone = axes.drawOne(gd, ax, opts); + + ax._r = ax.range.slice(); + ax._rl = Lib.simpleMap(ax._r, ax.r2l); + + return axDone; + }; + })); +}; + +/** + * Draw one cartesian axis + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * @param {object} opts + * - @param {boolean} skipTitle (set to true to skip axis title draw call) + * + * Depends on: + * - ax._mainSubplot (from linkSubplots) + * - ax._mainAxis + * - ax._anchorAxis + * - ax._subplotsWith + * - ax._counterDomainMin, ax._counterDomainMax (optionally, from linkSubplots) + * - ax._tickAngles (on redraw only, old value relinked during supplyDefaults) + * - ax._mainLinePosition (from lsInner) + * - ax._mainMirrorPosition + * - ax._linepositions + * + * Fills in: + * - ax._vals: + * - ax._gridVals: + * - ax._selections: + * - ax._tickAngles: + * - ax._depth (when required only): + * - and calls ax.setScale + */ +axes.drawOne = function(gd, ax, opts) { + opts = opts || {}; + + var i, sp, plotinfo; + + ax.setScale(); + + var fullLayout = gd._fullLayout; + var axId = ax._id; + var axLetter = axId.charAt(0); + var counterLetter = axes.counterLetter(axId); + var mainPlotinfo = fullLayout._plots[ax._mainSubplot]; + + // this happens when updating matched group with 'missing' axes + if(!mainPlotinfo) return; + + var mainAxLayer = mainPlotinfo[axLetter + 'axislayer']; + var mainLinePosition = ax._mainLinePosition; + var mainMirrorPosition = ax._mainMirrorPosition; + + var vals = ax._vals = axes.calcTicks(ax); + + // Add a couple of axis properties that should cause us to recreate + // elements. Used in d3 data function. + var axInfo = [ax.mirror, mainLinePosition, mainMirrorPosition].join('_'); + for(i = 0; i < vals.length; i++) { + vals[i].axInfo = axInfo; + } + + // stash selections to avoid DOM queries e.g. + // - stash tickLabels selection, so that drawTitle can use it to scoot title + ax._selections = {}; + // stash tick angle (including the computed 'auto' values) per tick-label class + // linkup 'previous' tick angles on redraws + if(ax._tickAngles) ax._prevTickAngles = ax._tickAngles; + ax._tickAngles = {}; + // measure [in px] between axis position and outward-most part of bounding box + // (touching either the tick label or ticks) + // depth can be expansive to compute, so we only do so when required + ax._depth = null; + + // calcLabelLevelBbox can be expensive, + // so make sure to not call it twice during the same Axes.drawOne call + // by stashing label-level bounding boxes per tick-label class + var llbboxes = {}; + function getLabelLevelBbox(suffix) { + var cls = axId + (suffix || 'tick'); + if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls); + return llbboxes[cls]; + } + + if(!ax.visible) return; + + var transTickFn = axes.makeTransTickFn(ax); + var transTickLabelFn = axes.makeTransTickLabelFn(ax); + + var tickVals; + // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end + // The key case here is removing zero lines when the axis bound is zero + var valsClipped; + + var insideTicks = ax.ticks === 'inside'; + var outsideTicks = ax.ticks === 'outside'; + + if(ax.tickson === 'boundaries') { + var boundaryVals = getBoundaryVals(ax, vals); + valsClipped = axes.clipEnds(ax, boundaryVals); + tickVals = insideTicks ? valsClipped : boundaryVals; + } else { + valsClipped = axes.clipEnds(ax, vals); + tickVals = (insideTicks && ax.ticklabelmode !== 'period') ? valsClipped : vals; + } + + var gridVals = ax._gridVals = valsClipped; + var dividerVals = getDividerVals(ax, vals); + + if(!fullLayout._hasOnlyLargeSploms) { + var subplotsWithAx = ax._subplotsWith; + + // keep track of which subplots (by main counter axis) we've already + // drawn grids for, so we don't overdraw overlaying subplots + var finishedGrids = {}; + + for(i = 0; i < subplotsWithAx.length; i++) { + sp = subplotsWithAx[i]; + plotinfo = fullLayout._plots[sp]; + + var counterAxis = plotinfo[counterLetter + 'axis']; + var mainCounterID = counterAxis._mainAxis._id; + if(finishedGrids[mainCounterID]) continue; + finishedGrids[mainCounterID] = 1; + + var gridPath = axLetter === 'x' ? + 'M0,' + counterAxis._offset + 'v' + counterAxis._length : + 'M' + counterAxis._offset + ',0h' + counterAxis._length; + + axes.drawGrid(gd, ax, { + vals: gridVals, + counterAxis: counterAxis, + layer: plotinfo.gridlayer.select('.' + axId), + path: gridPath, + transFn: transTickFn + }); + axes.drawZeroLine(gd, ax, { + counterAxis: counterAxis, + layer: plotinfo.zerolinelayer, + path: gridPath, + transFn: transTickFn + }); + } + } + + var tickSigns = axes.getTickSigns(ax); + var tickSubplots = []; + + if(ax.ticks) { + var mainTickPath = axes.makeTickPath(ax, mainLinePosition, tickSigns[2]); + var mirrorTickPath; + var fullTickPath; + if(ax._anchorAxis && ax.mirror && ax.mirror !== true) { + mirrorTickPath = axes.makeTickPath(ax, mainMirrorPosition, tickSigns[3]); + fullTickPath = mainTickPath + mirrorTickPath; + } else { + mirrorTickPath = ''; + fullTickPath = mainTickPath; + } + + var tickPath; + if(ax.showdividers && outsideTicks && ax.tickson === 'boundaries') { + var dividerLookup = {}; + for(i = 0; i < dividerVals.length; i++) { + dividerLookup[dividerVals[i].x] = 1; + } + tickPath = function(d) { + return dividerLookup[d.x] ? mirrorTickPath : fullTickPath; + }; + } else { + tickPath = fullTickPath; + } + + axes.drawTicks(gd, ax, { + vals: tickVals, + layer: mainAxLayer, + path: tickPath, + transFn: transTickFn + }); + + if(ax.mirror === 'allticks') { + tickSubplots = Object.keys(ax._linepositions || {}); + } + } + + for(i = 0; i < tickSubplots.length; i++) { + sp = tickSubplots[i]; + plotinfo = fullLayout._plots[sp]; + // [bottom or left, top or right], free and main are handled above + var linepositions = ax._linepositions[sp] || []; + var spTickPath = axes.makeTickPath(ax, linepositions[0], tickSigns[0]) + + axes.makeTickPath(ax, linepositions[1], tickSigns[1]); + + axes.drawTicks(gd, ax, { + vals: tickVals, + layer: plotinfo[axLetter + 'axislayer'], + path: spTickPath, + transFn: transTickFn + }); + } + + var seq = []; + + // tick labels - for now just the main labels. + // TODO: mirror labels, esp for subplots + + seq.push(function() { + return axes.drawLabels(gd, ax, { + vals: vals, + layer: mainAxLayer, + plotinfo: plotinfo, + transFn: transTickLabelFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition) + }); + }); + + if(ax.type === 'multicategory') { + var pad = {x: 2, y: 10}[axLetter]; + + seq.push(function() { + var bboxKey = {x: 'height', y: 'width'}[axLetter]; + var standoff = getLabelLevelBbox()[bboxKey] + pad + + (ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0); + + return axes.drawLabels(gd, ax, { + vals: getSecondaryLabelVals(ax, vals), + layer: mainAxLayer, + cls: axId + 'tick2', + repositionOnUpdate: true, + secondary: true, + transFn: transTickFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition + standoff * tickSigns[4]) + }); + }); + + seq.push(function() { + ax._depth = tickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition); + + return drawDividers(gd, ax, { + vals: dividerVals, + layer: mainAxLayer, + path: axes.makeTickPath(ax, mainLinePosition, tickSigns[4], ax._depth), + transFn: transTickFn + }); + }); + } else if(ax.title.hasOwnProperty('standoff')) { + seq.push(function() { + ax._depth = tickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition); + }); + } + + var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax); + + seq.push(function() { + var s = ax.side.charAt(0); + var sMirror = OPPOSITE_SIDE[ax.side].charAt(0); + var pos = axes.getPxPosition(gd, ax); + var outsideTickLen = outsideTicks ? ax.ticklen : 0; + var llbbox; + + var push; + var mirrorPush; + var rangeSliderPush; + + if(ax.automargin || hasRangeSlider) { + if(ax.type === 'multicategory') { + llbbox = getLabelLevelBbox('tick2'); + } else { + llbbox = getLabelLevelBbox(); + if(axLetter === 'x' && s === 'b') { + ax._depth = Math.max(llbbox.width > 0 ? llbbox.bottom - pos : 0, outsideTickLen); + } + } + } + + if(ax.automargin) { + push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + var domainIndices = [0, 1]; + + if(axLetter === 'x') { + if(s === 'b') { + push[s] = ax._depth; + } else { + push[s] = ax._depth = Math.max(llbbox.width > 0 ? pos - llbbox.top : 0, outsideTickLen); + domainIndices.reverse(); + } + + if(llbbox.width > 0) { + var rExtra = llbbox.right - (ax._offset + ax._length); + if(rExtra > 0) { + push.xr = 1; + push.r = rExtra; + } + var lExtra = ax._offset - llbbox.left; + if(lExtra > 0) { + push.xl = 0; + push.l = lExtra; + } + } + } else { + if(s === 'l') { + push[s] = ax._depth = Math.max(llbbox.height > 0 ? pos - llbbox.left : 0, outsideTickLen); + } else { + push[s] = ax._depth = Math.max(llbbox.height > 0 ? llbbox.right - pos : 0, outsideTickLen); + domainIndices.reverse(); + } + + if(llbbox.height > 0) { + var bExtra = llbbox.bottom - (ax._offset + ax._length); + if(bExtra > 0) { + push.yb = 0; + push.b = bExtra; + } + var tExtra = ax._offset - llbbox.top; + if(tExtra > 0) { + push.yt = 1; + push.t = tExtra; + } + } + } + + push[counterLetter] = ax.anchor === 'free' ? + ax.position : + ax._anchorAxis.domain[domainIndices[0]]; + + if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { + push[s] += approxTitleDepth(ax) + (ax.title.standoff || 0); + } + + if(ax.mirror && ax.anchor !== 'free') { + mirrorPush = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + + mirrorPush[sMirror] = ax.linewidth; + if(ax.mirror && ax.mirror !== true) mirrorPush[sMirror] += outsideTickLen; + + if(ax.mirror === true || ax.mirror === 'ticks') { + mirrorPush[counterLetter] = ax._anchorAxis.domain[domainIndices[1]]; + } else if(ax.mirror === 'all' || ax.mirror === 'allticks') { + mirrorPush[counterLetter] = [ax._counterDomainMin, ax._counterDomainMax][domainIndices[1]]; + } + } + } + + if(hasRangeSlider) { + rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax); + } + + Plots.autoMargin(gd, axAutoMarginID(ax), push); + Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush); + Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush); + }); + + if(!opts.skipTitle && + !(hasRangeSlider && ax.side === 'bottom') + ) { + seq.push(function() { return drawTitle(gd, ax); }); + } + + return Lib.syncOrAsync(seq); +}; + +function getBoundaryVals(ax, vals) { + var out = []; + var i; + + // boundaryVals are never used for labels; + // no need to worry about the other tickTextObj keys + var _push = function(d, bndIndex) { + var xb = d.xbnd[bndIndex]; + if(xb !== null) { + out.push(Lib.extendFlat({}, d, {x: xb})); + } + }; + + if(vals.length) { + for(i = 0; i < vals.length; i++) { + _push(vals[i], 0); + } + _push(vals[i - 1], 1); + } + + return out; +} + +function getSecondaryLabelVals(ax, vals) { + var out = []; + var lookup = {}; + + for(var i = 0; i < vals.length; i++) { + var d = vals[i]; + if(lookup[d.text2]) { + lookup[d.text2].push(d.x); + } else { + lookup[d.text2] = [d.x]; + } + } + + for(var k in lookup) { + out.push(tickTextObj(ax, Lib.interp(lookup[k], 0.5), k)); + } + + return out; +} + +function getDividerVals(ax, vals) { + var out = []; + var i, current; + + var reversed = (vals.length && vals[vals.length - 1].x < vals[0].x); + + // never used for labels; + // no need to worry about the other tickTextObj keys + var _push = function(d, bndIndex) { + var xb = d.xbnd[bndIndex]; + if(xb !== null) { + out.push(Lib.extendFlat({}, d, {x: xb})); + } + }; + + if(ax.showdividers && vals.length) { + for(i = 0; i < vals.length; i++) { + var d = vals[i]; + if(d.text2 !== current) { + _push(d, reversed ? 1 : 0); + } + current = d.text2; + } + _push(vals[i - 1], reversed ? 0 : 1); + } + + return out; +} + +function calcLabelLevelBbox(ax, cls) { + var top, bottom; + var left, right; + + if(ax._selections[cls].size()) { + top = Infinity; + bottom = -Infinity; + left = Infinity; + right = -Infinity; + ax._selections[cls].each(function() { + var thisLabel = selectTickLabel(this); + // Use parent node , to make Drawing.bBox + // retrieve a bbox computed with transform info + // + // To improve perf, it would be nice to use `thisLabel.node()` + // (like in fixLabelOverlaps) instead and use Axes.getPxPosition + // together with the makeLabelFns outputs and `tickangle` + // to compute one bbox per (tick value x tick style) + var bb = Drawing.bBox(thisLabel.node().parentNode); + top = Math.min(top, bb.top); + bottom = Math.max(bottom, bb.bottom); + left = Math.min(left, bb.left); + right = Math.max(right, bb.right); + }); + } else { + top = 0; + bottom = 0; + left = 0; + right = 0; + } + + return { + top: top, + bottom: bottom, + left: left, + right: right, + height: bottom - top, + width: right - left + }; +} + +/** + * Which direction do the 'ax.side' values, and free ticks go? + * + * @param {object} ax (full) axis object + * - {string} _id (starting with 'x' or 'y') + * - {string} side + * - {string} ticks + * @return {array} all entries are either -1 or 1 + * - [0]: sign for top/right ticks (i.e. negative SVG direction) + * - [1]: sign for bottom/left ticks (i.e. positive SVG direction) + * - [2]: sign for ticks corresponding to 'ax.side' + * - [3]: sign for ticks mirroring 'ax.side' + * - [4]: sign of arrow starting at axis pointing towards margin + */ +axes.getTickSigns = function(ax) { + var axLetter = ax._id.charAt(0); + var sideOpposite = {x: 'top', y: 'right'}[axLetter]; + var main = ax.side === sideOpposite ? 1 : -1; + var out = [-1, 1, main, -main]; + // then we flip if outside XOR y axis + if((ax.ticks !== 'inside') === (axLetter === 'x')) { + out = out.map(function(v) { return -v; }); + } + // independent of `ticks`; do not flip this one + if(ax.side) { + out.push({l: -1, t: -1, r: 1, b: 1}[ax.side.charAt(0)]); + } + return out; +}; + +/** + * Make axis translate transform function + * + * @param {object} ax (full) axis object + * - {string} _id + * - {number} _offset + * - {fn} l2p + * @return {fn} function of calcTicks items + */ +axes.makeTransTickFn = function(ax) { + return ax._id.charAt(0) === 'x' ? + function(d) { return strTranslate(ax._offset + ax.l2p(d.x), 0); } : + function(d) { return strTranslate(0, ax._offset + ax.l2p(d.x)); }; +}; + +axes.makeTransTickLabelFn = function(ax) { + var uv = getTickLabelUV(ax); + var u = uv[0]; + var v = uv[1]; + + return ax._id.charAt(0) === 'x' ? + function(d) { + return strTranslate( + u + ax._offset + ax.l2p(getPosX(d)), + v + ); + } : + function(d) { + return strTranslate( + v, + u + ax._offset + ax.l2p(getPosX(d)) + ); + }; +}; + +function getPosX(d) { + return d.periodX !== undefined ? d.periodX : d.x; +} + +// u is a shift along the axis, +// v is a shift perpendicular to the axis +function getTickLabelUV(ax) { + var ticklabelposition = ax.ticklabelposition || ''; + var has = function(str) { + return ticklabelposition.indexOf(str) !== -1; + }; + + var isTop = has('top'); + var isLeft = has('left'); + var isRight = has('right'); + var isBottom = has('bottom'); + var isInside = has('inside'); + + var isAligned = isBottom || isLeft || isTop || isRight; + + // early return + if(!isAligned && !isInside) return [0, 0]; + + var side = ax.side; + + var u = isAligned ? (ax.tickwidth || 0) / 2 : 0; + var v = TEXTPAD; + + var fontSize = ax.tickfont ? ax.tickfont.size : 12; + if(isBottom || isTop) { + u += fontSize * CAP_SHIFT; + v += (ax.linewidth || 0) / 2; + } + if(isLeft || isRight) { + u += (ax.linewidth || 0) / 2; + v += TEXTPAD; + } + if(isInside && side === 'top') { + v -= fontSize * (1 - CAP_SHIFT); + } + + if(isLeft || isTop) u = -u; + if(side === 'bottom' || side === 'right') v = -v; + + return [ + isAligned ? u : 0, + isInside ? v : 0 + ]; +} + +/** + * Make axis tick path string + * + * @param {object} ax (full) axis object + * - {string} _id + * - {number} ticklen + * - {number} linewidth + * @param {number} shift along direction of ticklen + * @param {1 or -1} sgn tick sign + * @param {number (optional)} len tick length + * @return {string} + */ +axes.makeTickPath = function(ax, shift, sgn, len) { + len = len !== undefined ? len : ax.ticklen; + + var axLetter = ax._id.charAt(0); + var pad = (ax.linewidth || 1) / 2; + + return axLetter === 'x' ? + 'M0,' + (shift + pad * sgn) + 'v' + (len * sgn) : + 'M' + (shift + pad * sgn) + ',0h' + (len * sgn); +}; + +/** + * Make axis tick label x, y and anchor functions + * + * @param {object} ax (full) axis object + * - {string} _id + * - {string} ticks + * - {number} ticklen + * - {string} side + * - {number} linewidth + * - {number} tickfont.size + * - {boolean} showline + * @param {number} shift + * @param {number} angle [in degrees] ... + * @return {object} + * - {fn} xFn + * - {fn} yFn + * - {fn} anchorFn + * - {fn} heightFn + * - {number} labelStandoff (gap parallel to ticks) + * - {number} labelShift (gap perpendicular to ticks) + */ +axes.makeLabelFns = function(ax, shift, angle) { + var ticklabelposition = ax.ticklabelposition || ''; + var has = function(str) { + return ticklabelposition.indexOf(str) !== -1; + }; + + var isTop = has('top'); + var isLeft = has('left'); + var isRight = has('right'); + var isBottom = has('bottom'); + var isAligned = isBottom || isLeft || isTop || isRight; + + var insideTickLabels = has('inside'); + var labelsOverTicks = + (ticklabelposition === 'inside' && ax.ticks === 'inside') || + (!insideTickLabels && ax.ticks === 'outside' && ax.tickson !== 'boundaries'); + + var labelStandoff = 0; + var labelShift = 0; + + var tickLen = labelsOverTicks ? ax.ticklen : 0; + if(insideTickLabels) { + tickLen *= -1; + } else if(isAligned) { + tickLen = 0; + } + + if(labelsOverTicks) { + labelStandoff += tickLen; + if(angle) { + var rad = Lib.deg2rad(angle); + labelStandoff = tickLen * Math.cos(rad) + 1; + labelShift = tickLen * Math.sin(rad); + } + } + + if(ax.showticklabels && (labelsOverTicks || ax.showline)) { + labelStandoff += 0.2 * ax.tickfont.size; + } + labelStandoff += (ax.linewidth || 1) / 2 * (insideTickLabels ? -1 : 1); + + var out = { + labelStandoff: labelStandoff, + labelShift: labelShift + }; + + var x0, y0, ff, flipIt; + var xQ = 0; + + var side = ax.side; + var axLetter = ax._id.charAt(0); + var tickangle = ax.tickangle; + var endSide; + if(axLetter === 'x') { + endSide = + (!insideTickLabels && side === 'bottom') || + (insideTickLabels && side === 'top'); + + flipIt = endSide ? 1 : -1; + if(insideTickLabels) flipIt *= -1; + + x0 = labelShift * flipIt; + y0 = shift + labelStandoff * flipIt; + ff = endSide ? 1 : -0.2; + if(Math.abs(tickangle) === 90) { + if(insideTickLabels) { + ff += MID_SHIFT; + } else { + if(tickangle === -90 && side === 'bottom') { + ff = CAP_SHIFT; + } else if(tickangle === 90 && side === 'top') { + ff = MID_SHIFT; + } else { + ff = 0.5; + } + } + + xQ = (MID_SHIFT / 2) * (tickangle / 90); + } + + out.xFn = function(d) { return d.dx + x0 + xQ * d.fontSize; }; + out.yFn = function(d) { return d.dy + y0 + d.fontSize * ff; }; + out.anchorFn = function(d, a) { + if(isAligned) { + if(isLeft) return 'end'; + if(isRight) return 'start'; + } + + if(!isNumeric(a) || a === 0 || a === 180) { + return 'middle'; + } + + return ((a * flipIt < 0) !== insideTickLabels) ? 'end' : 'start'; + }; + out.heightFn = function(d, a, h) { + return (a < -60 || a > 60) ? -0.5 * h : + ((ax.side === 'top') !== insideTickLabels) ? -h : + 0; + }; + } else if(axLetter === 'y') { + endSide = + (!insideTickLabels && side === 'left') || + (insideTickLabels && side === 'right'); + + flipIt = endSide ? 1 : -1; + if(insideTickLabels) flipIt *= -1; + + x0 = labelStandoff; + y0 = labelShift * flipIt; + ff = 0; + if(!insideTickLabels && Math.abs(tickangle) === 90) { + if( + (tickangle === -90 && side === 'left') || + (tickangle === 90 && side === 'right') + ) { + ff = CAP_SHIFT; + } else { + ff = 0.5; + } + } + + if(insideTickLabels) { + var ang = isNumeric(tickangle) ? +tickangle : 0; + if(ang !== 0) { + var rA = Lib.deg2rad(ang); + xQ = Math.abs(Math.sin(rA)) * CAP_SHIFT * flipIt; + ff = 0; + } + } + + out.xFn = function(d) { return d.dx + shift - (x0 + d.fontSize * ff) * flipIt + xQ * d.fontSize; }; + out.yFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; }; + out.anchorFn = function(d, a) { + if(isNumeric(a) && Math.abs(a) === 90) { + return 'middle'; + } + + return endSide ? 'end' : 'start'; + }; + out.heightFn = function(d, a, h) { + if(ax.side === 'right') a *= -1; + + return a < -30 ? -h : + a < 30 ? -0.5 * h : + 0; + }; + } + + return out; +}; + +function tickDataFn(d) { + return [d.text, d.x, d.axInfo, d.font, d.fontSize, d.fontColor].join('_'); +} + +/** + * Draw axis ticks + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} ticks + * - {number} linewidth + * - {string} tickcolor + * @param {object} opts + * - {array of object} vals (calcTicks output-like) + * - {d3 selection} layer + * - {string or fn} path + * - {fn} transFn + * - {boolean} crisp (set to false to unset crisp-edge SVG rendering) + */ +axes.drawTicks = function(gd, ax, opts) { + opts = opts || {}; + + var cls = ax._id + 'tick'; + + var vals = opts.vals; + if( + ax.ticklabelmode === 'period' + ) { + // drop very first tick that we added to handle period + vals = vals.slice(); + vals.shift(); + } + + var ticks = opts.layer.selectAll('path.' + cls) + .data(ax.ticks ? vals : [], tickDataFn); + + ticks.exit().remove(); + + ticks.enter().append('path') + .classed(cls, 1) + .classed('ticks', 1) + .classed('crisp', opts.crisp !== false) + .call(Color.stroke, ax.tickcolor) + .style('stroke-width', Drawing.crispRound(gd, ax.tickwidth, 1) + 'px') + .attr('d', opts.path) + .style('display', null); // visible + + hideCounterAxisInsideTickLabels(ax, [TICK_PATH]); + + ticks.attr('transform', opts.transFn); +}; + +/** + * Draw axis grid + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {boolean} showgrid + * - {string} gridcolor + * - {string} gridwidth + * - {boolean} zeroline + * - {string} type + * - {string} dtick + * @param {object} opts + * - {array of object} vals (calcTicks output-like) + * - {d3 selection} layer + * - {object} counterAxis (full axis object corresponding to counter axis) + * optional - only required if this axis supports zero lines + * - {string or fn} path + * - {fn} transFn + * - {boolean} crisp (set to false to unset crisp-edge SVG rendering) + */ +axes.drawGrid = function(gd, ax, opts) { + opts = opts || {}; + + var cls = ax._id + 'grid'; + var vals = opts.vals; + var counterAx = opts.counterAxis; + if(ax.showgrid === false) { + vals = []; + } else if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) { + var isArrayMode = ax.tickmode === 'array'; + for(var i = 0; i < vals.length; i++) { + var xi = vals[i].x; + if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) { + vals = vals.slice(0, i).concat(vals.slice(i + 1)); + // In array mode you can in principle have multiple + // ticks at 0, so test them all. Otherwise once we found + // one we can stop. + if(isArrayMode) i--; + else break; + } + } + } + + var grid = opts.layer.selectAll('path.' + cls) + .data(vals, tickDataFn); + + grid.exit().remove(); + + grid.enter().append('path') + .classed(cls, 1) + .classed('crisp', opts.crisp !== false); + + ax._gw = Drawing.crispRound(gd, ax.gridwidth, 1); + + grid.attr('transform', opts.transFn) + .attr('d', opts.path) + .call(Color.stroke, ax.gridcolor || '#ddd') + .style('stroke-width', ax._gw + 'px') + .style('display', null); // visible + + hideCounterAxisInsideTickLabels(ax, [GRID_PATH]); + + if(typeof opts.path === 'function') grid.attr('d', opts.path); +}; + +/** + * Draw axis zero-line + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {boolean} zeroline + * - {number} zerolinewidth + * - {string} zerolinecolor + * - {number (optional)} _gridWidthCrispRound + * @param {object} opts + * - {d3 selection} layer + * - {object} counterAxis (full axis object corresponding to counter axis) + * - {string or fn} path + * - {fn} transFn + * - {boolean} crisp (set to false to unset crisp-edge SVG rendering) + */ +axes.drawZeroLine = function(gd, ax, opts) { + opts = opts || opts; + + var cls = ax._id + 'zl'; + var show = axes.shouldShowZeroLine(gd, ax, opts.counterAxis); + + var zl = opts.layer.selectAll('path.' + cls) + .data(show ? [{x: 0, id: ax._id}] : []); + + zl.exit().remove(); + + zl.enter().append('path') + .classed(cls, 1) + .classed('zl', 1) + .classed('crisp', opts.crisp !== false) + .each(function() { + // use the fact that only one element can enter to trigger a sort. + // If several zerolines enter at the same time we will sort once per, + // but generally this should be a minimal overhead. + opts.layer.selectAll('path').sort(function(da, db) { + return idSort(da.id, db.id); + }); + }); + + zl.attr('transform', opts.transFn) + .attr('d', opts.path) + .call(Color.stroke, ax.zerolinecolor || Color.defaultLine) + .style('stroke-width', Drawing.crispRound(gd, ax.zerolinewidth, ax._gw || 1) + 'px') + .style('display', null); // visible + + hideCounterAxisInsideTickLabels(ax, [ZERO_PATH]); +}; + +/** + * Draw axis tick labels + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {boolean} showticklabels + * - {number} tickangle + * - {object (optional)} _selections + * - {object} (optional)} _tickAngles + * - {object} (optional)} _prevTickAngles + * @param {object} opts + * - {array of object} vals (calcTicks output-like) + * - {d3 selection} layer + * - {string (optional)} cls (node className) + * - {boolean} repositionOnUpdate (set to true to reposition update selection) + * - {boolean} secondary + * - {fn} transFn + * - {object} labelFns + * + {fn} xFn + * + {fn} yFn + * + {fn} anchorFn + * + {fn} heightFn + */ +axes.drawLabels = function(gd, ax, opts) { + opts = opts || {}; + + var fullLayout = gd._fullLayout; + var axId = ax._id; + var axLetter = axId.charAt(0); + var cls = opts.cls || axId + 'tick'; + var vals = opts.vals; + + var labelFns = opts.labelFns; + var tickAngle = opts.secondary ? 0 : ax.tickangle; + var prevAngle = (ax._prevTickAngles || {})[cls]; + + var tickLabels = opts.layer.selectAll('g.' + cls) + .data(ax.showticklabels ? vals : [], tickDataFn); + + var labelsReady = []; + + tickLabels.enter().append('g') + .classed(cls, 1) + .append('text') + // only so tex has predictable alignment that we can + // alter later + .attr('text-anchor', 'middle') + .each(function(d) { + var thisLabel = d3.select(this); + var newPromise = gd._promises.length; + + thisLabel + .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d)) + .call(Drawing.font, d.font, d.fontSize, d.fontColor) + .text(d.text) + .call(svgTextUtils.convertToTspans, gd); + + if(gd._promises[newPromise]) { + // if we have an async label, we'll deal with that + // all here so take it out of gd._promises and + // instead position the label and promise this in + // labelsReady + labelsReady.push(gd._promises.pop().then(function() { + positionLabels(thisLabel, tickAngle); + })); + } else { + // sync label: just position it now. + positionLabels(thisLabel, tickAngle); + } + }); + + hideCounterAxisInsideTickLabels(ax, [TICK_TEXT]); + + tickLabels.exit().remove(); + + if(opts.repositionOnUpdate) { + tickLabels.each(function(d) { + d3.select(this).select('text') + .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d)); + }); + } + + function positionLabels(s, angle) { + s.each(function(d) { + var thisLabel = d3.select(this); + var mathjaxGroup = thisLabel.select('.text-math-group'); + var anchor = labelFns.anchorFn(d, angle); + + var transform = opts.transFn.call(thisLabel.node(), d) + + ((isNumeric(angle) && +angle !== 0) ? + (' rotate(' + angle + ',' + labelFns.xFn(d) + ',' + + (labelFns.yFn(d) - d.fontSize / 2) + ')') : + ''); + + // how much to shift a multi-line label to center it vertically. + var nLines = svgTextUtils.lineCount(thisLabel); + var lineHeight = LINE_SPACING * d.fontSize; + var anchorHeight = labelFns.heightFn(d, isNumeric(angle) ? +angle : 0, (nLines - 1) * lineHeight); + + if(anchorHeight) { + transform += strTranslate(0, anchorHeight); + } + + if(mathjaxGroup.empty()) { + var thisText = thisLabel.select('text'); + thisText.attr({ + transform: transform, + 'text-anchor': anchor + }); + + thisText.style('opacity', 1); // visible + + if(ax._adjustTickLabelsOverflow) { + ax._adjustTickLabelsOverflow(); + } + } else { + var mjWidth = Drawing.bBox(mathjaxGroup.node()).width; + var mjShift = mjWidth * {end: -0.5, start: 0.5}[anchor]; + mathjaxGroup.attr('transform', transform + strTranslate(mjShift, 0)); + } + }); + } + + ax._adjustTickLabelsOverflow = function() { + var ticklabeloverflow = ax.ticklabeloverflow; + if(!ticklabeloverflow || ticklabeloverflow === 'allow') return; + + var hideOverflow = ticklabeloverflow.indexOf('hide') !== -1; + + var isX = ax._id.charAt(0) === 'x'; + // div positions + var p0 = 0; + var p1 = isX ? + gd._fullLayout.width : + gd._fullLayout.height; + + if(ticklabeloverflow.indexOf('domain') !== -1) { + // domain positions + var rl = Lib.simpleMap(ax.range, ax.r2l); + p0 = ax.l2p(rl[0]) + ax._offset; + p1 = ax.l2p(rl[1]) + ax._offset; + } + + var min = Math.min(p0, p1); + var max = Math.max(p0, p1); + + var side = ax.side; + + var visibleLabelMin = Infinity; + var visibleLabelMax = -Infinity; + + tickLabels.each(function(d) { + var thisLabel = d3.select(this); + var mathjaxGroup = thisLabel.select('.text-math-group'); + + if(mathjaxGroup.empty()) { + var bb = Drawing.bBox(thisLabel.node()); + var adjust = 0; + if(isX) { + if(bb.right > max) adjust = 1; + else if(bb.left < min) adjust = 1; + } else { + if(bb.bottom > max) adjust = 1; + else if(bb.top + (ax.tickangle ? 0 : d.fontSize / 4) < min) adjust = 1; + } + + var t = thisLabel.select('text'); + if(adjust) { + if(hideOverflow) t.style('opacity', 0); // hidden + } else { + t.style('opacity', 1); // visible + + if(side === 'bottom' || side === 'right') { + visibleLabelMin = Math.min(visibleLabelMin, isX ? bb.top : bb.left); + } else { + visibleLabelMin = -Infinity; + } + + if(side === 'top' || side === 'left') { + visibleLabelMax = Math.max(visibleLabelMax, isX ? bb.bottom : bb.right); + } else { + visibleLabelMax = Infinity; + } + } + } // TODO: hide mathjax? + }); + + for(var subplot in fullLayout._plots) { + var plotinfo = fullLayout._plots[subplot]; + if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue; + var anchorAx = isX ? plotinfo.yaxis : plotinfo.xaxis; + if(anchorAx) { + anchorAx['_visibleLabelMin_' + ax._id] = visibleLabelMin; + anchorAx['_visibleLabelMax_' + ax._id] = visibleLabelMax; + } + } + }; + + ax._hideCounterAxisInsideTickLabels = function(partialOpts) { + var isX = ax._id.charAt(0) === 'x'; + + var anchoredAxes = []; + for(var subplot in fullLayout._plots) { + var plotinfo = fullLayout._plots[subplot]; + if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue; + anchoredAxes.push(isX ? plotinfo.yaxis : plotinfo.xaxis); + } + + anchoredAxes.forEach(function(anchorAx, idx) { + if(anchorAx && insideTicklabelposition(anchorAx)) { + (partialOpts || [ + ZERO_PATH, + GRID_PATH, + TICK_PATH, + TICK_TEXT + ]).forEach(function(e) { + var isPeriodLabel = + e.K === 'tick' && + e.L === 'text' && + ax.ticklabelmode === 'period'; + + var mainPlotinfo = fullLayout._plots[ax._mainSubplot]; + + var sel; + if(e.K === ZERO_PATH.K) sel = mainPlotinfo.zerolinelayer.selectAll('.' + ax._id + 'zl'); + else if(e.K === GRID_PATH.K) sel = mainPlotinfo.gridlayer.selectAll('.' + ax._id); + else sel = mainPlotinfo[ax._id.charAt(0) + 'axislayer']; + + sel.each(function() { + var w = d3.select(this); + if(e.L) w = w.selectAll(e.L); + + w.each(function(d) { + var q = ax.l2p( + isPeriodLabel ? getPosX(d) : d.x + ) + ax._offset; + + var t = d3.select(this); + if( + q < ax['_visibleLabelMax_' + anchorAx._id] && + q > ax['_visibleLabelMin_' + anchorAx._id] + ) { + t.style('display', 'none'); // hidden + } else if(e.K === 'tick' && !idx) { + t.style('display', null); // visible + } + }); + }); + }); + } + }); + }; + + // make sure all labels are correctly positioned at their base angle + // the positionLabels call above is only for newly drawn labels. + // do this without waiting, using the last calculated angle to + // minimize flicker, then do it again when we know all labels are + // there, putting back the prescribed angle to check for overlaps. + positionLabels(tickLabels, (prevAngle + 1) ? prevAngle : tickAngle); + + function allLabelsReady() { + return labelsReady.length && Promise.all(labelsReady); + } + + var autoangle = null; + + function fixLabelOverlaps() { + positionLabels(tickLabels, tickAngle); + + // check for auto-angling if x labels overlap + // don't auto-angle at all for log axes with + // base and digit format + if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) && + (ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D') + ) { + autoangle = 0; + + var maxFontSize = 0; + var lbbArray = []; + var i; + + tickLabels.each(function(d) { + maxFontSize = Math.max(maxFontSize, d.fontSize); + + var x = ax.l2p(d.x); + var thisLabel = selectTickLabel(this); + var bb = Drawing.bBox(thisLabel.node()); + + lbbArray.push({ + // ignore about y, just deal with x overlaps + top: 0, + bottom: 10, + height: 10, + left: x - bb.width / 2, + // impose a 2px gap + right: x + bb.width / 2 + 2, + width: bb.width + 2 + }); + }); + + if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) { + var gap = 2; + if(ax.ticks) gap += ax.tickwidth / 2; + + // TODO should secondary labels also fall into this fix-overlap regime? + + for(i = 0; i < lbbArray.length; i++) { + var xbnd = vals[i].xbnd; + var lbb = lbbArray[i]; + if( + (xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) || + (xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap) + ) { + autoangle = 90; + break; + } + } + } else { + var vLen = vals.length; + var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1); + + var ticklabelposition = ax.ticklabelposition || ''; + var has = function(str) { + return ticklabelposition.indexOf(str) !== -1; + }; + var isTop = has('top'); + var isLeft = has('left'); + var isRight = has('right'); + var isBottom = has('bottom'); + var isAligned = isBottom || isLeft || isTop || isRight; + var pad = !isAligned ? 0 : + (ax.tickwidth || 0) + 2 * TEXTPAD; + + var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory'; + + // any overlap at all - set 30 degrees or 90 degrees + for(i = 0; i < lbbArray.length - 1; i++) { + if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) { + autoangle = rotate90 ? 90 : 30; + break; + } + } + } + + if(autoangle) { + positionLabels(tickLabels, autoangle); + } + } + } + + if(ax._selections) { + ax._selections[cls] = tickLabels; + } + + var seq = [allLabelsReady]; + + // N.B. during auto-margin redraws, if the axis fixed its label overlaps + // by rotating 90 degrees, do not attempt to re-fix its label overlaps + // as this can lead to infinite redraw loops! + if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) { + autoangle = 90; + seq.push(function() { + positionLabels(tickLabels, prevAngle); + }); + } else { + seq.push(fixLabelOverlaps); + } + + // save current tick angle for future redraws + if(ax._tickAngles) { + seq.push(function() { + ax._tickAngles[cls] = autoangle === null ? + (isNumeric(tickAngle) ? tickAngle : 0) : + autoangle; + }); + } + + var anchorAx = ax._anchorAxis; + if( + anchorAx && anchorAx.autorange && + insideTicklabelposition(ax) && + !isLinked(fullLayout, ax._id) + ) { + if(!fullLayout._insideTickLabelsAutorange) { + fullLayout._insideTickLabelsAutorange = {}; + } + fullLayout._insideTickLabelsAutorange[anchorAx._name + '.autorange'] = anchorAx.autorange; + + seq.push( + function computeFinalTickLabelBoundingBoxes() { + tickLabels.each(function(d, i) { + var thisLabel = selectTickLabel(this); + var mathjaxGroup = thisLabel.select('.text-math-group'); + if(mathjaxGroup.empty()) { + ax._vals[i].bb = Drawing.bBox(thisLabel.node()); + } + }); + } + ); + } + + var done = Lib.syncOrAsync(seq); + if(done && done.then) gd._promises.push(done); + return done; +}; + +/** + * Draw axis dividers + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} showdividers + * - {number} dividerwidth + * - {string} dividercolor + * @param {object} opts + * - {array of object} vals (calcTicks output-like) + * - {d3 selection} layer + * - {fn} path + * - {fn} transFn + */ +function drawDividers(gd, ax, opts) { + var cls = ax._id + 'divider'; + var vals = opts.vals; + + var dividers = opts.layer.selectAll('path.' + cls) + .data(vals, tickDataFn); + + dividers.exit().remove(); + + dividers.enter().insert('path', ':first-child') + .classed(cls, 1) + .classed('crisp', 1) + .call(Color.stroke, ax.dividercolor) + .style('stroke-width', Drawing.crispRound(gd, ax.dividerwidth, 1) + 'px'); + + dividers + .attr('transform', opts.transFn) + .attr('d', opts.path); +} + +/** + * Get axis position in px, that is the distance for the graph's + * top (left) edge for x (y) axes. + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} side + * if anchored: + * - {object} _anchorAxis + * Otherwise: + * - {number} position + * @return {number} + */ +axes.getPxPosition = function(gd, ax) { + var gs = gd._fullLayout._size; + var axLetter = ax._id.charAt(0); + var side = ax.side; + var anchorAxis; + + if(ax.anchor !== 'free') { + anchorAxis = ax._anchorAxis; + } else if(axLetter === 'x') { + anchorAxis = { + _offset: gs.t + (1 - (ax.position || 0)) * gs.h, + _length: 0 + }; + } else if(axLetter === 'y') { + anchorAxis = { + _offset: gs.l + (ax.position || 0) * gs.w, + _length: 0 + }; + } + + if(side === 'top' || side === 'left') { + return anchorAxis._offset; + } else if(side === 'bottom' || side === 'right') { + return anchorAxis._offset + anchorAxis._length; + } +}; + +/** + * Approximate axis title depth (w/o computing its bounding box) + * + * @param {object} ax (full) axis object + * - {string} title.text + * - {number} title.font.size + * - {number} title.standoff + * @return {number} (in px) + */ +function approxTitleDepth(ax) { + var fontSize = ax.title.font.size; + var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length; + if(ax.title.hasOwnProperty('standoff')) { + return extraLines ? + fontSize * (CAP_SHIFT + (extraLines * LINE_SPACING)) : + fontSize * CAP_SHIFT; + } else { + return extraLines ? + fontSize * (extraLines + 1) * LINE_SPACING : + fontSize; + } +} + +/** + * Draw axis title, compute default standoff if necessary + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} _name + * - {string} side + * - {number} title.font.size + * - {object} _selections + * + * - {number} _depth + * - {number} title.standoff + * OR + * - {number} linewidth + * - {boolean} showticklabels + */ +function drawTitle(gd, ax) { + var fullLayout = gd._fullLayout; + var axId = ax._id; + var axLetter = axId.charAt(0); + var fontSize = ax.title.font.size; + var titleStandoff; + + if(ax.title.hasOwnProperty('standoff')) { + titleStandoff = ax._depth + ax.title.standoff + approxTitleDepth(ax); + } else { + var isInside = insideTicklabelposition(ax); + + if(ax.type === 'multicategory') { + titleStandoff = ax._depth; + } else { + var offsetBase = 1.5 * fontSize; + if(isInside) { + offsetBase = 0.5 * fontSize; + if(ax.ticks === 'outside') { + offsetBase += ax.ticklen; + } + } + titleStandoff = 10 + offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0); + } + + if(!isInside) { + if(axLetter === 'x') { + titleStandoff += ax.side === 'top' ? + fontSize * (ax.showticklabels ? 1 : 0) : + fontSize * (ax.showticklabels ? 1.5 : 0.5); + } else { + titleStandoff += ax.side === 'right' ? + fontSize * (ax.showticklabels ? 1 : 0.5) : + fontSize * (ax.showticklabels ? 0.5 : 0); + } + } + } + + var pos = axes.getPxPosition(gd, ax); + var transform, x, y; + + if(axLetter === 'x') { + x = ax._offset + ax._length / 2; + y = (ax.side === 'top') ? pos - titleStandoff : pos + titleStandoff; + } else { + y = ax._offset + ax._length / 2; + x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; + transform = {rotate: '-90', offset: 0}; + } + + var avoid; + + if(ax.type !== 'multicategory') { + var tickLabels = ax._selections[ax._id + 'tick']; + + avoid = { + selection: tickLabels, + side: ax.side + }; + + if(tickLabels && tickLabels.node() && tickLabels.node().parentNode) { + var translation = Drawing.getTranslate(tickLabels.node().parentNode); + avoid.offsetLeft = translation.x; + avoid.offsetTop = translation.y; + } + + if(ax.title.hasOwnProperty('standoff')) { + avoid.pad = 0; + } + } + + return Titles.draw(gd, axId + 'title', { + propContainer: ax, + propName: ax._name + '.title.text', + placeholder: fullLayout._dfltTitle[axLetter], + avoid: avoid, + transform: transform, + attributes: {x: x, y: y, 'text-anchor': 'middle'} + }); +} + +axes.shouldShowZeroLine = function(gd, ax, counterAxis) { + var rng = Lib.simpleMap(ax.range, ax.r2l); + return ( + (rng[0] * rng[1] <= 0) && + ax.zeroline && + (ax.type === 'linear' || ax.type === '-') && + !(ax.rangebreaks && ax.maskBreaks(0) === BADNUM) && + ( + clipEnds(ax, 0) || + !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) || + hasBarsOrFill(gd, ax) + ) + ); +}; + +axes.clipEnds = function(ax, vals) { + return vals.filter(function(d) { return clipEnds(ax, d.x); }); +}; + +function clipEnds(ax, l) { + var p = ax.l2p(l); + return (p > 1 && p < ax._length - 1); +} + +function anyCounterAxLineAtZero(gd, ax, counterAxis, rng) { + var mainCounterAxis = counterAxis._mainAxis; + if(!mainCounterAxis) return; + + var fullLayout = gd._fullLayout; + var axLetter = ax._id.charAt(0); + var counterLetter = axes.counterLetter(ax._id); + + var zeroPosition = ax._offset + ( + ((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ? + 0 : ax._length + ); + + function lineNearZero(ax2) { + if(!ax2.showline || !ax2.linewidth) return false; + var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1); + + function closeEnough(pos2) { + return typeof pos2 === 'number' && Math.abs(pos2 - zeroPosition) < tolerance; + } + + if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) { + return true; + } + var linePositions = ax2._linepositions || {}; + for(var k in linePositions) { + if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) { + return true; + } + } + } + + var plotinfo = fullLayout._plots[counterAxis._mainSubplot]; + if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) { + return lineNearZero(counterAxis, zeroPosition); + } + + var counterLetterAxes = axes.list(gd, counterLetter); + for(var i = 0; i < counterLetterAxes.length; i++) { + var counterAxis2 = counterLetterAxes[i]; + if( + counterAxis2._mainAxis === mainCounterAxis && + lineNearZero(counterAxis2, zeroPosition) + ) { + return true; + } + } +} + +function hasBarsOrFill(gd, ax) { + var fullData = gd._fullData; + var subplot = ax._mainSubplot; + var axLetter = ax._id.charAt(0); + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + + if(trace.visible === true && (trace.xaxis + trace.yaxis) === subplot) { + if( + Registry.traceIs(trace, 'bar-like') && + trace.orientation === {x: 'h', y: 'v'}[axLetter] + ) return true; + + if( + trace.fill && + trace.fill.charAt(trace.fill.length - 1) === axLetter + ) return true; + } + } + return false; +} + +function selectTickLabel(gTick) { + var s = d3.select(gTick); + var mj = s.select('.text-math-group'); + return mj.empty() ? s.select('text') : mj; +} + +/** + * Find all margin pushers for 2D axes and reserve them for later use + * Both label and rangeslider automargin calculations happen later so + * we need to explicitly allow their ids in order to not delete them. + * + * TODO: can we pull the actual automargin calls forward to avoid this hack? + * We're probably also doing multiple redraws in this case, would be faster + * if we can just do the whole calculation ahead of time and draw once. + */ +axes.allowAutoMargin = function(gd) { + var axList = axes.list(gd, '', true); + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + if(ax.automargin) { + Plots.allowAutoMargin(gd, axAutoMarginID(ax)); + if(ax.mirror) { + Plots.allowAutoMargin(gd, axMirrorAutoMarginID(ax)); + } + } + if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) { + Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax)); + } + } +}; + +function axAutoMarginID(ax) { return ax._id + '.automargin'; } +function axMirrorAutoMarginID(ax) { return axAutoMarginID(ax) + '.mirror'; } +function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; } + +// swap all the presentation attributes of the axes showing these traces +axes.swap = function(gd, traces) { + var axGroups = makeAxisGroups(gd, traces); + + for(var i = 0; i < axGroups.length; i++) { + swapAxisGroup(gd, axGroups[i].x, axGroups[i].y); + } +}; + +function makeAxisGroups(gd, traces) { + var groups = []; + var i, j; + + for(i = 0; i < traces.length; i++) { + var groupsi = []; + var xi = gd._fullData[traces[i]].xaxis; + var yi = gd._fullData[traces[i]].yaxis; + if(!xi || !yi) continue; // not a 2D cartesian trace? + + for(j = 0; j < groups.length; j++) { + if(groups[j].x.indexOf(xi) !== -1 || groups[j].y.indexOf(yi) !== -1) { + groupsi.push(j); + } + } + + if(!groupsi.length) { + groups.push({x: [xi], y: [yi]}); + continue; + } + + var group0 = groups[groupsi[0]]; + var groupj; + + if(groupsi.length > 1) { + for(j = 1; j < groupsi.length; j++) { + groupj = groups[groupsi[j]]; + mergeAxisGroups(group0.x, groupj.x); + mergeAxisGroups(group0.y, groupj.y); + } + } + mergeAxisGroups(group0.x, [xi]); + mergeAxisGroups(group0.y, [yi]); + } + + return groups; +} + +function mergeAxisGroups(intoSet, fromSet) { + for(var i = 0; i < fromSet.length; i++) { + if(intoSet.indexOf(fromSet[i]) === -1) intoSet.push(fromSet[i]); + } +} + +function swapAxisGroup(gd, xIds, yIds) { + var xFullAxes = []; + var yFullAxes = []; + var layout = gd.layout; + var i, j; + + for(i = 0; i < xIds.length; i++) xFullAxes.push(axes.getFromId(gd, xIds[i])); + for(i = 0; i < yIds.length; i++) yFullAxes.push(axes.getFromId(gd, yIds[i])); + + var allAxKeys = Object.keys(axAttrs); + + var noSwapAttrs = [ + 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType' + ]; + var numericTypes = ['linear', 'log']; + + for(i = 0; i < allAxKeys.length; i++) { + var keyi = allAxKeys[i]; + var xVal = xFullAxes[0][keyi]; + var yVal = yFullAxes[0][keyi]; + var allEqual = true; + var coerceLinearX = false; + var coerceLinearY = false; + if(keyi.charAt(0) === '_' || typeof xVal === 'function' || + noSwapAttrs.indexOf(keyi) !== -1) { + continue; + } + for(j = 1; j < xFullAxes.length && allEqual; j++) { + var xVali = xFullAxes[j][keyi]; + if(keyi === 'type' && numericTypes.indexOf(xVal) !== -1 && + numericTypes.indexOf(xVali) !== -1 && xVal !== xVali) { + // type is special - if we find a mixture of linear and log, + // coerce them all to linear on flipping + coerceLinearX = true; + } else if(xVali !== xVal) allEqual = false; + } + for(j = 1; j < yFullAxes.length && allEqual; j++) { + var yVali = yFullAxes[j][keyi]; + if(keyi === 'type' && numericTypes.indexOf(yVal) !== -1 && + numericTypes.indexOf(yVali) !== -1 && yVal !== yVali) { + // type is special - if we find a mixture of linear and log, + // coerce them all to linear on flipping + coerceLinearY = true; + } else if(yFullAxes[j][keyi] !== yVal) allEqual = false; + } + if(allEqual) { + if(coerceLinearX) layout[xFullAxes[0]._name].type = 'linear'; + if(coerceLinearY) layout[yFullAxes[0]._name].type = 'linear'; + swapAxisAttrs(layout, keyi, xFullAxes, yFullAxes, gd._fullLayout._dfltTitle); + } + } + + // now swap x&y for any annotations anchored to these x & y + for(i = 0; i < gd._fullLayout.annotations.length; i++) { + var ann = gd._fullLayout.annotations[i]; + if(xIds.indexOf(ann.xref) !== -1 && + yIds.indexOf(ann.yref) !== -1) { + Lib.swapAttrs(layout.annotations[i], ['?']); + } + } +} + +function swapAxisAttrs(layout, key, xFullAxes, yFullAxes, dfltTitle) { + // in case the value is the default for either axis, + // look at the first axis in each list and see if + // this key's value is undefined + var np = Lib.nestedProperty; + var xVal = np(layout[xFullAxes[0]._name], key).get(); + var yVal = np(layout[yFullAxes[0]._name], key).get(); + var i; + + if(key === 'title') { + // special handling of placeholder titles + if(xVal && xVal.text === dfltTitle.x) { + xVal.text = dfltTitle.y; + } + if(yVal && yVal.text === dfltTitle.y) { + yVal.text = dfltTitle.x; + } + } + + for(i = 0; i < xFullAxes.length; i++) { + np(layout, xFullAxes[i]._name + '.' + key).set(yVal); + } + for(i = 0; i < yFullAxes.length; i++) { + np(layout, yFullAxes[i]._name + '.' + key).set(xVal); + } +} + +function isAngular(ax) { + return ax._id === 'angularaxis'; +} + +function moveOutsideBreak(v, ax) { + var len = ax._rangebreaks.length; + for(var k = 0; k < len; k++) { + var brk = ax._rangebreaks[k]; + if(v >= brk.min && v < brk.max) { + return brk.max; + } + } + return v; +} + +function insideTicklabelposition(ax) { + return ((ax.ticklabelposition || '').indexOf('inside') !== -1); +} + +function hideCounterAxisInsideTickLabels(ax, opts) { + if(insideTicklabelposition(ax._anchorAxis || {})) { + if(ax._hideCounterAxisInsideTickLabels) { + ax._hideCounterAxisInsideTickLabels(opts); + } + } +} + +},{"../../components/color":155,"../../components/drawing":177,"../../components/titles":253,"../../constants/alignment":260,"../../constants/numerical":265,"../../lib":285,"../../lib/svg_text_utils":307,"../../plots/plots":366,"../../registry":373,"./autorange":330,"./axis_autotype":332,"./axis_ids":335,"./clean_ticks":337,"./layout_attributes":346,"./set_convert":352,"@plotly/d3":20,"fast-isnumeric":31}],332:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; +var isDateTime = Lib.isDateTime; +var cleanNumber = Lib.cleanNumber; +var round = Math.round; + +module.exports = function autoType(array, calendar, opts) { + var a = array; + + var noMultiCategory = opts.noMultiCategory; + if(isArrayOrTypedArray(a) && !a.length) return '-'; + if(!noMultiCategory && multiCategory(a)) return 'multicategory'; + if(noMultiCategory && Array.isArray(a[0])) { // no need to flat typed arrays here + var b = []; + for(var i = 0; i < a.length; i++) { + if(isArrayOrTypedArray(a[i])) { + for(var j = 0; j < a[i].length; j++) { + b.push(a[i][j]); + } + } + } + a = b; + } + + if(moreDates(a, calendar)) return 'date'; + + var convertNumeric = opts.autotypenumbers !== 'strict'; // compare against strict, just in case autotypenumbers was not provided in opts + if(category(a, convertNumeric)) return 'category'; + if(linearOK(a, convertNumeric)) return 'linear'; + + return '-'; +}; + +function hasTypeNumber(v, convertNumeric) { + return convertNumeric ? isNumeric(v) : typeof v === 'number'; +} + +// is there at least one number in array? If not, we should leave +// ax.type empty so it can be autoset later +function linearOK(a, convertNumeric) { + var len = a.length; + + for(var i = 0; i < len; i++) { + if(hasTypeNumber(a[i], convertNumeric)) return true; + } + + return false; +} + +// does the array a have mostly dates rather than numbers? +// note: some values can be neither (such as blanks, text) +// 2- or 4-digit integers can be both, so require twice as many +// dates as non-dates, to exclude cases with mostly 2 & 4 digit +// numbers and a few dates +// as with categories, consider DISTINCT values only. +function moreDates(a, calendar) { + var len = a.length; + + var inc = getIncrement(len); + var dats = 0; + var nums = 0; + var seen = {}; + + for(var f = 0; f < len; f += inc) { + var i = round(f); + var ai = a[i]; + var stri = String(ai); + if(seen[stri]) continue; + seen[stri] = 1; + + if(isDateTime(ai, calendar)) dats++; + if(isNumeric(ai)) nums++; + } + + return dats > nums * 2; +} + +// return increment to test at most 1000 points, evenly spaced +function getIncrement(len) { + return Math.max(1, (len - 1) / 1000); +} + +// are the (x,y)-values in gd.data mostly text? +// require twice as many DISTINCT categories as distinct numbers +function category(a, convertNumeric) { + var len = a.length; + + var inc = getIncrement(len); + var nums = 0; + var cats = 0; + var seen = {}; + + for(var f = 0; f < len; f += inc) { + var i = round(f); + var ai = a[i]; + var stri = String(ai); + if(seen[stri]) continue; + seen[stri] = 1; + + var t = typeof ai; + if(t === 'boolean') cats++; + else if(convertNumeric ? cleanNumber(ai) !== BADNUM : t === 'number') nums++; + else if(t === 'string') cats++; + } + + return cats > nums * 2; +} + +// very-loose requirements for multicategory, +// trace modules that should never auto-type to multicategory +// should be declared with 'noMultiCategory' +function multiCategory(a) { + return isArrayOrTypedArray(a[0]) && isArrayOrTypedArray(a[1]); +} + +},{"../../constants/numerical":265,"../../lib":285,"fast-isnumeric":31}],333:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); + +var handleArrayContainerDefaults = _dereq_('../array_container_defaults'); + +var layoutAttributes = _dereq_('./layout_attributes'); +var handleTickValueDefaults = _dereq_('./tick_value_defaults'); +var handleTickMarkDefaults = _dereq_('./tick_mark_defaults'); +var handleTickLabelDefaults = _dereq_('./tick_label_defaults'); +var handleCategoryOrderDefaults = _dereq_('./category_order_defaults'); +var handleLineGridDefaults = _dereq_('./line_grid_defaults'); +var setConvert = _dereq_('./set_convert'); + +var DAY_OF_WEEK = _dereq_('./constants').WEEKDAY_PATTERN; +var HOUR = _dereq_('./constants').HOUR_PATTERN; + +/** + * options: object containing: + * + * letter: 'x' or 'y' + * title: name of the axis (ie 'Colorbar') to go in default title + * font: the default font to inherit + * outerTicks: boolean, should ticks default to outside? + * showGrid: boolean, should gridlines be shown by default? + * noHover: boolean, this axis doesn't support hover effects? + * noTickson: boolean, this axis doesn't support 'tickson' + * data: the plot data, used to manage categories + * bgColor: the plot background color, to calculate default gridline colors + * calendar: + * splomStash: + * visibleDflt: boolean + * reverseDflt: boolean + * automargin: boolean + */ +module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) { + var letter = options.letter; + var font = options.font || {}; + var splomStash = options.splomStash || {}; + + var visible = coerce('visible', !options.visibleDflt); + + var axTemplate = containerOut._template || {}; + var axType = containerOut.type || axTemplate.type || '-'; + + var ticklabelmode; + if(axType === 'date') { + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar); + + if(!options.noTicklabelmode) { + ticklabelmode = coerce('ticklabelmode'); + } + } + + var ticklabelposition = ''; + if(!options.noTicklabelposition || axType === 'multicategory') { + ticklabelposition = Lib.coerce(containerIn, containerOut, { + ticklabelposition: { + valType: 'enumerated', + dflt: 'outside', + values: ticklabelmode === 'period' ? ['outside', 'inside'] : + letter === 'x' ? [ + 'outside', 'inside', + 'outside left', 'inside left', + 'outside right', 'inside right' + ] : [ + 'outside', 'inside', + 'outside top', 'inside top', + 'outside bottom', 'inside bottom' + ] + } + }, 'ticklabelposition'); + } + + if(!options.noTicklabeloverflow) { + coerce('ticklabeloverflow', + ticklabelposition.indexOf('inside') !== -1 ? + 'hide past domain' : + axType === 'category' || + axType === 'multicategory' ? + 'allow' : + 'hide past div' + ); + } + + setConvert(containerOut, layoutOut); + + var autorangeDflt = !containerOut.isValidRange(containerIn.range); + if(autorangeDflt && options.reverseDflt) autorangeDflt = 'reversed'; + var autoRange = coerce('autorange', autorangeDflt); + if(autoRange && (axType === 'linear' || axType === '-')) coerce('rangemode'); + + coerce('range'); + containerOut.cleanRange(); + + handleCategoryOrderDefaults(containerIn, containerOut, coerce, options); + + if(axType !== 'category' && !options.noHover) coerce('hoverformat'); + + var dfltColor = coerce('color'); + // if axis.color was provided, use it for fonts too; otherwise, + // inherit from global font color in case that was provided. + // Compare to dflt rather than to containerIn, so we can provide color via + // template too. + var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color; + // try to get default title from splom trace, fallback to graph-wide value + var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter]; + + handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 1}); + if(!visible) return containerOut; + + coerce('title.text', dfltTitle); + Lib.coerceFont(coerce, 'title.font', { + family: font.family, + size: Lib.bigFont(font.size), + color: dfltFontColor + }); + + handleTickValueDefaults(containerIn, containerOut, coerce, axType); + handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 2}); + handleTickMarkDefaults(containerIn, containerOut, coerce, options); + handleLineGridDefaults(containerIn, containerOut, coerce, { + dfltColor: dfltColor, + bgColor: options.bgColor, + showGrid: options.showGrid, + attributes: layoutAttributes + }); + + if(containerOut.showline || containerOut.ticks) coerce('mirror'); + + if(options.automargin) coerce('automargin'); + + var isMultiCategory = axType === 'multicategory'; + + if(!options.noTickson && + (axType === 'category' || isMultiCategory) && + (containerOut.ticks || containerOut.showgrid) + ) { + var ticksonDflt; + if(isMultiCategory) ticksonDflt = 'boundaries'; + var tickson = coerce('tickson', ticksonDflt); + if(tickson === 'boundaries') { + delete containerOut.ticklabelposition; + } + } + + if(isMultiCategory) { + var showDividers = coerce('showdividers'); + if(showDividers) { + coerce('dividercolor'); + coerce('dividerwidth'); + } + } + + if(axType === 'date') { + handleArrayContainerDefaults(containerIn, containerOut, { + name: 'rangebreaks', + inclusionAttr: 'enabled', + handleItemDefaults: rangebreaksDefaults + }); + + if(!containerOut.rangebreaks.length) { + delete containerOut.rangebreaks; + } else { + for(var k = 0; k < containerOut.rangebreaks.length; k++) { + if(containerOut.rangebreaks[k].pattern === DAY_OF_WEEK) { + containerOut._hasDayOfWeekBreaks = true; + break; + } + } + + setConvert(containerOut, layoutOut); + + if(layoutOut._has('scattergl') || layoutOut._has('splom')) { + for(var i = 0; i < options.data.length; i++) { + var trace = options.data[i]; + if(trace.type === 'scattergl' || trace.type === 'splom') { + trace.visible = false; + Lib.warn(trace.type + + ' traces do not work on axes with rangebreaks.' + + ' Setting trace ' + trace.index + ' to `visible: false`.'); + } + } + } + } + } + + return containerOut; +}; + +function rangebreaksDefaults(itemIn, itemOut, containerOut) { + function coerce(attr, dflt) { + return Lib.coerce(itemIn, itemOut, layoutAttributes.rangebreaks, attr, dflt); + } + + var enabled = coerce('enabled'); + + if(enabled) { + var bnds = coerce('bounds'); + if(bnds && bnds.length >= 2) { + var dfltPattern = ''; + var i, q; + if(bnds.length === 2) { + for(i = 0; i < 2; i++) { + q = indexOfDay(bnds[i]); + if(q) { + dfltPattern = DAY_OF_WEEK; + break; + } + } + } + var pattern = coerce('pattern', dfltPattern); + if(pattern === DAY_OF_WEEK) { + for(i = 0; i < 2; i++) { + q = indexOfDay(bnds[i]); + if(q) { + // convert to integers i.e 'Sunday' --> 0 + itemOut.bounds[i] = bnds[i] = q - 1; + } + } + } + if(pattern) { + // ensure types and ranges + for(i = 0; i < 2; i++) { + q = bnds[i]; + switch(pattern) { + case DAY_OF_WEEK : + if(!isNumeric(q)) { + itemOut.enabled = false; + return; + } + q = +q; + + if( + q !== Math.floor(q) || // don't accept fractional days for mow + q < 0 || q >= 7 + ) { + itemOut.enabled = false; + return; + } + // use number + itemOut.bounds[i] = bnds[i] = q; + break; + + case HOUR : + if(!isNumeric(q)) { + itemOut.enabled = false; + return; + } + q = +q; + + if(q < 0 || q > 24) { // accept 24 + itemOut.enabled = false; + return; + } + // use number + itemOut.bounds[i] = bnds[i] = q; + break; + } + } + } + + if(containerOut.autorange === false) { + var rng = containerOut.range; + + // if bounds are bigger than the (set) range, disable break + if(rng[0] < rng[1]) { + if(bnds[0] < rng[0] && bnds[1] > rng[1]) { + itemOut.enabled = false; + return; + } + } else if(bnds[0] > rng[0] && bnds[1] < rng[1]) { + itemOut.enabled = false; + return; + } + } + } else { + var values = coerce('values'); + + if(values && values.length) { + coerce('dvalue'); + } else { + itemOut.enabled = false; + return; + } + } + } +} + +// these numbers are one more than what bounds would be mapped to +var dayStrToNum = { + sun: 1, + mon: 2, + tue: 3, + wed: 4, + thu: 5, + fri: 6, + sat: 7 +}; + +function indexOfDay(v) { + if(typeof v !== 'string') return; + return dayStrToNum[ + v.substr(0, 3).toLowerCase() + ]; +} + +},{"../../lib":285,"../../registry":373,"../array_container_defaults":326,"./category_order_defaults":336,"./constants":338,"./layout_attributes":346,"./line_grid_defaults":348,"./set_convert":352,"./tick_label_defaults":353,"./tick_mark_defaults":354,"./tick_value_defaults":355,"fast-isnumeric":31}],334:[function(_dereq_,module,exports){ +'use strict'; + +var docs = _dereq_('../../constants/docs'); +var FORMAT_LINK = docs.FORMAT_LINK; +var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK; + +function axisHoverFormat(x, noDates) { + return { + valType: 'string', + dflt: '', + editType: 'none', + description: ( + noDates ? descriptionOnlyNumbers : descriptionWithDates + )('hover text', x) + [ + 'By default the values are formatted using ' + ( + noDates ? + 'generic number format' : + ('`' + x + 'axis.hoverformat`') + ) + '.', + ].join(' ') + }; +} + +function descriptionOnlyNumbers(label, x) { + return [ + 'Sets the ' + label + ' formatting rule' + (x ? 'for `' + x + '` ' : ''), + 'using d3 formatting mini-languages', + 'which are very similar to those in Python. For numbers, see: ' + FORMAT_LINK + '.' + ].join(' '); +} + +function descriptionWithDates(label, x) { + return descriptionOnlyNumbers(label, x) + [ + ' And for dates see: ' + DATE_FORMAT_LINK + '.', + 'We add two items to d3\'s date formatter:', + '*%h* for half of the year as a decimal number as well as', + '*%{n}f* for fractional seconds', + 'with n digits. For example, *2016-10-13 09:15:23.456* with tickformat', + '*%H~%M~%S.%2f* would display *09~15~23.46*' + ].join(' '); +} + +module.exports = { + axisHoverFormat: axisHoverFormat, + descriptionOnlyNumbers: descriptionOnlyNumbers, + descriptionWithDates: descriptionWithDates +}; + +},{"../../constants/docs":262}],335:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); + +var constants = _dereq_('./constants'); + + +// convert between axis names (xaxis, xaxis2, etc, elements of gd.layout) +// and axis id's (x, x2, etc). Would probably have ditched 'xaxis' +// completely in favor of just 'x' if it weren't ingrained in the API etc. +exports.id2name = function id2name(id) { + if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return; + var axNum = id.split(' ')[0].substr(1); + if(axNum === '1') axNum = ''; + return id.charAt(0) + 'axis' + axNum; +}; + +exports.name2id = function name2id(name) { + if(!name.match(constants.AX_NAME_PATTERN)) return; + var axNum = name.substr(5); + if(axNum === '1') axNum = ''; + return name.charAt(0) + axNum; +}; + +/* + * Cleans up the number of an axis, e.g., 'x002'->'x2', 'x0'->'x', 'x1' -> 'x', + * etc. + * If domainId is true, then id could be a domain reference and if it is, the + * ' domain' part is kept at the end of the axis ID string. + */ +exports.cleanId = function cleanId(id, axLetter, domainId) { + var domainTest = /( domain)$/.test(id); + if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return; + if(axLetter && id.charAt(0) !== axLetter) return; + if(domainTest && (!domainId)) return; + var axNum = id.split(' ')[0].substr(1).replace(/^0+/, ''); + if(axNum === '1') axNum = ''; + return id.charAt(0) + axNum + (domainTest && domainId ? ' domain' : ''); +}; + +// get all axis objects, as restricted in listNames +exports.list = function(gd, axLetter, only2d) { + var fullLayout = gd._fullLayout; + if(!fullLayout) return []; + + var idList = exports.listIds(gd, axLetter); + var out = new Array(idList.length); + var i; + + for(i = 0; i < idList.length; i++) { + var idi = idList[i]; + out[i] = fullLayout[idi.charAt(0) + 'axis' + idi.substr(1)]; + } + + if(!only2d) { + var sceneIds3D = fullLayout._subplots.gl3d || []; + + for(i = 0; i < sceneIds3D.length; i++) { + var scene = fullLayout[sceneIds3D[i]]; + + if(axLetter) out.push(scene[axLetter + 'axis']); + else out.push(scene.xaxis, scene.yaxis, scene.zaxis); + } + } + + return out; +}; + +// get all axis ids, optionally restricted by letter +// this only makes sense for 2d axes +exports.listIds = function(gd, axLetter) { + var fullLayout = gd._fullLayout; + if(!fullLayout) return []; + + var subplotLists = fullLayout._subplots; + if(axLetter) return subplotLists[axLetter + 'axis']; + return subplotLists.xaxis.concat(subplotLists.yaxis); +}; + +// get an axis object from its id 'x','x2' etc +// optionally, id can be a subplot (ie 'x2y3') and type gets x or y from it +exports.getFromId = function(gd, id, type) { + var fullLayout = gd._fullLayout; + // remove "domain" suffix + id = ((id === undefined) || (typeof(id) !== 'string')) ? id : id.replace(' domain', ''); + + if(type === 'x') id = id.replace(/y[0-9]*/, ''); + else if(type === 'y') id = id.replace(/x[0-9]*/, ''); + + return fullLayout[exports.id2name(id)]; +}; + +// get an axis object of specified type from the containing trace +exports.getFromTrace = function(gd, fullTrace, type) { + var fullLayout = gd._fullLayout; + var ax = null; + + if(Registry.traceIs(fullTrace, 'gl3d')) { + var scene = fullTrace.scene; + if(scene.substr(0, 5) === 'scene') { + ax = fullLayout[scene][type + 'axis']; + } + } else { + ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type); + } + + return ax; +}; + +// sort x, x2, x10, y, y2, y10... +exports.idSort = function(id1, id2) { + var letter1 = id1.charAt(0); + var letter2 = id2.charAt(0); + if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1; + return +(id1.substr(1) || 1) - +(id2.substr(1) || 1); +}; + +/* + * An axis reference (e.g., the contents at the 'xref' key of an object) might + * have extra information appended. Extract the axis ID only. + * + * ar: the axis reference string + * + */ +exports.ref2id = function(ar) { + // This assumes ar has been coerced via coerceRef, and uses the shortcut of + // checking if the first letter matches [xyz] to determine if it should + // return the axis ID. Otherwise it returns false. + return (/^[xyz]/.test(ar)) ? ar.split(' ')[0] : false; +}; + +function isFound(axId, list) { + if(list && list.length) { + for(var i = 0; i < list.length; i++) { + if(list[i][axId]) return true; + } + } + return false; +} + +exports.isLinked = function(fullLayout, axId) { + return ( + isFound(axId, fullLayout._axisMatchGroups) || + isFound(axId, fullLayout._axisConstraintGroups) + ); +}; + +},{"../../registry":373,"./constants":338}],336:[function(_dereq_,module,exports){ +'use strict'; + +function findCategories(ax, opts) { + var dataAttr = opts.dataAttr || ax._id.charAt(0); + var lookup = {}; + var axData; + var i, j; + + if(opts.axData) { + // non-x/y case + axData = opts.axData; + } else { + // x/y case + axData = []; + for(i = 0; i < opts.data.length; i++) { + var trace = opts.data[i]; + if(trace[dataAttr + 'axis'] === ax._id) { + axData.push(trace); + } + } + } + + for(i = 0; i < axData.length; i++) { + var vals = axData[i][dataAttr]; + for(j = 0; j < vals.length; j++) { + var v = vals[j]; + if(v !== null && v !== undefined) { + lookup[v] = 1; + } + } + } + + return Object.keys(lookup); +} + +/** + * Fills in category* default and initial categories. + * + * @param {object} containerIn : input axis object + * @param {object} containerOut : full axis object + * @param {function} coerce : Lib.coerce fn wrapper + * @param {object} opts : + * - data {array} : (full) data trace + * OR + * - axData {array} : (full) data associated with axis being coerced here + * - dataAttr {string} : attribute name corresponding to coordinate array + */ +module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce, opts) { + if(containerOut.type !== 'category') return; + + var arrayIn = containerIn.categoryarray; + var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0); + + // override default 'categoryorder' value when non-empty array is supplied + var orderDefault; + if(isValidArray) orderDefault = 'array'; + + var order = coerce('categoryorder', orderDefault); + var array; + + // coerce 'categoryarray' only in array order case + if(order === 'array') { + array = coerce('categoryarray'); + } + + // cannot set 'categoryorder' to 'array' with an invalid 'categoryarray' + if(!isValidArray && order === 'array') { + order = containerOut.categoryorder = 'trace'; + } + + // set up things for makeCalcdata + if(order === 'trace') { + containerOut._initialCategories = []; + } else if(order === 'array') { + containerOut._initialCategories = array.slice(); + } else { + array = findCategories(containerOut, opts).sort(); + if(order === 'category ascending') { + containerOut._initialCategories = array; + } else if(order === 'category descending') { + containerOut._initialCategories = array.reverse(); + } + } +}; + +},{}],337:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); +var constants = _dereq_('../../constants/numerical'); +var ONEDAY = constants.ONEDAY; +var ONEWEEK = constants.ONEWEEK; + +/** + * Return a validated dtick value for this axis + * + * @param {any} dtick: the candidate dtick. valid values are numbers and strings, + * and further constrained depending on the axis type. + * @param {string} axType: the axis type + */ +exports.dtick = function(dtick, axType) { + var isLog = axType === 'log'; + var isDate = axType === 'date'; + var isCat = axType === 'category'; + var dtickDflt = isDate ? ONEDAY : 1; + + if(!dtick) return dtickDflt; + + if(isNumeric(dtick)) { + dtick = Number(dtick); + if(dtick <= 0) return dtickDflt; + if(isCat) { + // category dtick must be positive integers + return Math.max(1, Math.round(dtick)); + } + if(isDate) { + // date dtick must be at least 0.1ms (our current precision) + return Math.max(0.1, dtick); + } + return dtick; + } + + if(typeof dtick !== 'string' || !(isDate || isLog)) { + return dtickDflt; + } + + var prefix = dtick.charAt(0); + var dtickNum = dtick.substr(1); + dtickNum = isNumeric(dtickNum) ? Number(dtickNum) : 0; + + if((dtickNum <= 0) || !( + // "M" gives ticks every (integer) n months + (isDate && prefix === 'M' && dtickNum === Math.round(dtickNum)) || + // "L" gives ticks linearly spaced in data (not in position) every (float) f + (isLog && prefix === 'L') || + // "D1" gives powers of 10 with all small digits between, "D2" gives only 2 and 5 + (isLog && prefix === 'D' && (dtickNum === 1 || dtickNum === 2)) + )) { + return dtickDflt; + } + + return dtick; +}; + +/** + * Return a validated tick0 for this axis + * + * @param {any} tick0: the candidate tick0. Valid values are numbers and strings, + * further constrained depending on the axis type + * @param {string} axType: the axis type + * @param {string} calendar: for date axes, the calendar to validate/convert with + * @param {any} dtick: an already valid dtick. Only used for D1 and D2 log dticks, + * which do not support tick0 at all. + */ +exports.tick0 = function(tick0, axType, calendar, dtick) { + if(axType === 'date') { + return Lib.cleanDate(tick0, + Lib.dateTick0(calendar, (dtick % ONEWEEK === 0) ? 1 : 0) + ); + } + if(dtick === 'D1' || dtick === 'D2') { + // D1 and D2 modes ignore tick0 entirely + return undefined; + } + // Aside from date axes, tick0 must be numeric + return isNumeric(tick0) ? Number(tick0) : 0; +}; + +},{"../../constants/numerical":265,"../../lib":285,"fast-isnumeric":31}],338:[function(_dereq_,module,exports){ +'use strict'; + +var counterRegex = _dereq_('../../lib/regex').counter; + +module.exports = { + idRegex: { + x: counterRegex('x', '( domain)?'), + y: counterRegex('y', '( domain)?') + }, + + attrRegex: counterRegex('[xy]axis'), + + // axis match regular expression + xAxisMatch: counterRegex('xaxis'), + yAxisMatch: counterRegex('yaxis'), + + // pattern matching axis ids and names + // note that this is more permissive than counterRegex, as + // id2name, name2id, and cleanId accept "x1" etc + AX_ID_PATTERN: /^[xyz][0-9]*( domain)?$/, + AX_NAME_PATTERN: /^[xyz]axis[0-9]*$/, + + // and for 2D subplots + SUBPLOT_PATTERN: /^x([0-9]*)y([0-9]*)$/, + + HOUR_PATTERN: 'hour', + WEEKDAY_PATTERN: 'day of week', + + // pixels to move mouse before you stop clamping to starting point + MINDRAG: 8, + + // smallest dimension allowed for a select box + MINSELECT: 12, + + // smallest dimension allowed for a zoombox + MINZOOM: 20, + + // width of axis drag regions + DRAGGERSIZE: 20, + + // max pixels off straight before a lasso select line counts as bent + BENDPX: 1.5, + + // delay before a redraw (relayout) after smooth panning and zooming + REDRAWDELAY: 50, + + // throttling limit (ms) for selectPoints calls + SELECTDELAY: 100, + + // cache ID suffix for throttle + SELECTID: '-select', + + // last resort axis ranges for x and y axes if we have no data + DFLTRANGEX: [-1, 6], + DFLTRANGEY: [-1, 4], + + // Layers to keep trace types in the right order + // N.B. each 'unique' plot method must have its own layer + traceLayerClasses: [ + 'imagelayer', + 'heatmaplayer', + 'contourcarpetlayer', 'contourlayer', + 'funnellayer', 'waterfalllayer', 'barlayer', + 'carpetlayer', + 'violinlayer', + 'boxlayer', + 'ohlclayer', + 'scattercarpetlayer', 'scatterlayer' + ], + + clipOnAxisFalseQuery: [ + '.scatterlayer', + '.barlayer', + '.funnellayer', + '.waterfalllayer' + ], + + layerValue2layerClass: { + 'above traces': 'above', + 'below traces': 'below' + } +}; + +},{"../../lib/regex":301}],339:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var autorange = _dereq_('./autorange'); +var id2name = _dereq_('./axis_ids').id2name; +var layoutAttributes = _dereq_('./layout_attributes'); +var scaleZoom = _dereq_('./scale_zoom'); +var setConvert = _dereq_('./set_convert'); + +var ALMOST_EQUAL = _dereq_('../../constants/numerical').ALMOST_EQUAL; +var FROM_BL = _dereq_('../../constants/alignment').FROM_BL; + +exports.handleDefaults = function(layoutIn, layoutOut, opts) { + var axIds = opts.axIds; + var axHasImage = opts.axHasImage; + + // sets of axes linked by `scaleanchor` OR `matches` along with the + // scaleratios compounded together, populated in handleConstraintDefaults + var constraintGroups = layoutOut._axisConstraintGroups = []; + // similar to _axisConstraintGroups, but only matching axes + var matchGroups = layoutOut._axisMatchGroups = []; + + var i, group, axId, axName, axIn, axOut, attr, val; + + for(i = 0; i < axIds.length; i++) { + axName = id2name(axIds[i]); + axIn = layoutIn[axName]; + axOut = layoutOut[axName]; + + handleOneAxDefaults(axIn, axOut, { + axIds: axIds, + layoutOut: layoutOut, + hasImage: axHasImage[axName] + }); + } + + // save matchGroup on each matching axis + function stash(groups, stashAttr) { + for(i = 0; i < groups.length; i++) { + group = groups[i]; + for(axId in group) { + layoutOut[id2name(axId)][stashAttr] = group; + } + } + } + stash(matchGroups, '_matchGroup'); + + // If any axis in a constraint group is fixedrange, they all get fixed + // This covers matches axes, as they're now in the constraintgroup too + // and have not yet been removed (if the group is *only* matching) + for(i = 0; i < constraintGroups.length; i++) { + group = constraintGroups[i]; + for(axId in group) { + axOut = layoutOut[id2name(axId)]; + if(axOut.fixedrange) { + for(var axId2 in group) { + var axName2 = id2name(axId2); + if((layoutIn[axName2] || {}).fixedrange === false) { + Lib.warn( + 'fixedrange was specified as false for axis ' + + axName2 + ' but was overridden because another ' + + 'axis in its constraint group has fixedrange true' + ); + } + layoutOut[axName2].fixedrange = true; + } + break; + } + } + } + + // remove constraint groups that simply duplicate match groups + i = 0; + while(i < constraintGroups.length) { + group = constraintGroups[i]; + for(axId in group) { + axOut = layoutOut[id2name(axId)]; + if(axOut._matchGroup && Object.keys(axOut._matchGroup).length === Object.keys(group).length) { + constraintGroups.splice(i, 1); + i--; + } + break; + } + i++; + } + + // save constraintGroup on each constrained axis + stash(constraintGroups, '_constraintGroup'); + + // make sure `matching` axes share values of necessary attributes + // Precedence (base axis is the one that doesn't list a `matches`, ie others + // all point to it): + // (1) explicitly defined value in the base axis + // (2) explicitly defined in another axis (arbitrary order) + // (3) default in the base axis + var matchAttrs = [ + 'constrain', + 'range', + 'autorange', + 'rangemode', + 'rangebreaks', + 'categoryorder', + 'categoryarray' + ]; + var hasRange = false; + var hasDayOfWeekBreaks = false; + + function setAttrVal() { + val = axOut[attr]; + if(attr === 'rangebreaks') { + hasDayOfWeekBreaks = axOut._hasDayOfWeekBreaks; + } + } + + for(i = 0; i < matchGroups.length; i++) { + group = matchGroups[i]; + + // find 'matching' range attrs + for(var j = 0; j < matchAttrs.length; j++) { + attr = matchAttrs[j]; + val = null; + var baseAx; + for(axId in group) { + axName = id2name(axId); + axIn = layoutIn[axName]; + axOut = layoutOut[axName]; + if(!(attr in axOut)) { + continue; + } + if(!axOut.matches) { + baseAx = axOut; + // top priority: explicit value in base axis + if(attr in axIn) { + setAttrVal(); + break; + } + } + if(val === null && attr in axIn) { + // second priority: first explicit value in another axis + setAttrVal(); + } + } + + // special logic for coupling of range and autorange + // if nobody explicitly specifies autorange, but someone does + // explicitly specify range, autorange must be disabled. + if(attr === 'range' && val) { + hasRange = true; + } + if(attr === 'autorange' && val === null && hasRange) { + val = false; + } + + if(val === null && attr in baseAx) { + // fallback: default value in base axis + val = baseAx[attr]; + } + // but we still might not have a value, which is fine. + if(val !== null) { + for(axId in group) { + axOut = layoutOut[id2name(axId)]; + axOut[attr] = attr === 'range' ? val.slice() : val; + + if(attr === 'rangebreaks') { + axOut._hasDayOfWeekBreaks = hasDayOfWeekBreaks; + setConvert(axOut, layoutOut); + } + } + } + } + } +}; + +function handleOneAxDefaults(axIn, axOut, opts) { + var axIds = opts.axIds; + var layoutOut = opts.layoutOut; + var hasImage = opts.hasImage; + var constraintGroups = layoutOut._axisConstraintGroups; + var matchGroups = layoutOut._axisMatchGroups; + var axId = axOut._id; + var axLetter = axId.charAt(0); + var splomStash = ((layoutOut._splomAxes || {})[axLetter] || {})[axId] || {}; + var thisID = axOut._id; + var isX = thisID.charAt(0) === 'x'; + + // Clear _matchGroup & _constraintGroup so relinkPrivateKeys doesn't keep + // an old one around. If this axis is in a group we'll set this again later + axOut._matchGroup = null; + axOut._constraintGroup = null; + + function coerce(attr, dflt) { + return Lib.coerce(axIn, axOut, layoutAttributes, attr, dflt); + } + + // coerce the constraint mechanics even if this axis has no scaleanchor + // because it may be the anchor of another axis. + coerce('constrain', hasImage ? 'domain' : 'range'); + Lib.coerce(axIn, axOut, { + constraintoward: { + valType: 'enumerated', + values: isX ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'], + dflt: isX ? 'center' : 'middle' + } + }, 'constraintoward'); + + // If this axis is already part of a constraint group, we can't + // scaleanchor any other axis in that group, or we'd make a loop. + // Filter axIds to enforce this, also matching axis types. + var thisType = axOut.type; + var i, idi; + + var linkableAxes = []; + for(i = 0; i < axIds.length; i++) { + idi = axIds[i]; + if(idi === thisID) continue; + + var axi = layoutOut[id2name(idi)]; + if(axi.type === thisType) { + linkableAxes.push(idi); + } + } + + var thisGroup = getConstraintGroup(constraintGroups, thisID); + if(thisGroup) { + var linkableAxesNoLoops = []; + for(i = 0; i < linkableAxes.length; i++) { + idi = linkableAxes[i]; + if(!thisGroup[idi]) linkableAxesNoLoops.push(idi); + } + linkableAxes = linkableAxesNoLoops; + } + + var canLink = linkableAxes.length; + + var matches, scaleanchor; + + if(canLink && (axIn.matches || splomStash.matches)) { + matches = Lib.coerce(axIn, axOut, { + matches: { + valType: 'enumerated', + values: linkableAxes, + dflt: linkableAxes.indexOf(splomStash.matches) !== -1 ? splomStash.matches : undefined + } + }, 'matches'); + } + + // 'matches' wins over 'scaleanchor' - each axis can only specify one + // constraint, but you can chain matches and scaleanchor constraints by + // specifying them in separate axes. + var scaleanchorDflt = hasImage && !isX ? axOut.anchor : undefined; + if(canLink && !matches && (axIn.scaleanchor || scaleanchorDflt)) { + scaleanchor = Lib.coerce(axIn, axOut, { + scaleanchor: { + valType: 'enumerated', + values: linkableAxes + } + }, 'scaleanchor', scaleanchorDflt); + } + + if(matches) { + axOut._matchGroup = updateConstraintGroups(matchGroups, thisID, matches, 1); + + // Also include match constraints in the scale groups + var matchedAx = layoutOut[id2name(matches)]; + var matchRatio = extent(layoutOut, axOut) / extent(layoutOut, matchedAx); + if(isX !== (matches.charAt(0) === 'x')) { + // We don't yet know the actual scale ratio of x/y matches constraints, + // due to possible automargins, so just leave a placeholder for this: + // 'x' means "x size over y size", 'y' means the inverse. + // in principle in the constraint group you could get multiple of these. + matchRatio = (isX ? 'x' : 'y') + matchRatio; + } + updateConstraintGroups(constraintGroups, thisID, matches, matchRatio); + } else if(axIn.matches && axIds.indexOf(axIn.matches) !== -1) { + Lib.warn('ignored ' + axOut._name + '.matches: "' + + axIn.matches + '" to avoid an infinite loop'); + } + + if(scaleanchor) { + var scaleratio = coerce('scaleratio'); + + // TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero, + // but that seems hacky. Better way to say "must be a positive number"? + // Of course if you use several super-tiny values you could eventually + // force a product of these to zero and all hell would break loose... + // Likewise with super-huge values. + if(!scaleratio) scaleratio = axOut.scaleratio = 1; + + updateConstraintGroups(constraintGroups, thisID, scaleanchor, scaleratio); + } else if(axIn.scaleanchor && axIds.indexOf(axIn.scaleanchor) !== -1) { + Lib.warn('ignored ' + axOut._name + '.scaleanchor: "' + + axIn.scaleanchor + '" to avoid either an infinite loop ' + + 'and possibly inconsistent scaleratios, or because this axis ' + + 'declares a *matches* constraint.'); + } +} + +function extent(layoutOut, ax) { + var domain = ax.domain; + if(!domain) { + // at this point overlaying axes haven't yet inherited their main domains + // TODO: constrain: domain with overlaying axes is likely a bug. + domain = layoutOut[id2name(ax.overlaying)].domain; + } + return domain[1] - domain[0]; +} + +function getConstraintGroup(groups, thisID) { + for(var i = 0; i < groups.length; i++) { + if(groups[i][thisID]) { + return groups[i]; + } + } + return null; +} + +/* + * Add this axis to the axis constraint groups, which is the collection + * of axes that are all constrained together on scale (or matching). + * + * constraintGroups: a list of objects. each object is + * {axis_id: scale_within_group}, where scale_within_group is + * only important relative to the rest of the group, and defines + * the relative scales between all axes in the group + * + * thisGroup: the group the current axis is already in + * thisID: the id if the current axis + * thatID: the id of the axis to scale it with + * scaleratio: the ratio of this axis to the thatID axis + */ +function updateConstraintGroups(constraintGroups, thisID, thatID, scaleratio) { + var i, j, groupi, keyj, thisGroupIndex; + + var thisGroup = getConstraintGroup(constraintGroups, thisID); + + if(thisGroup === null) { + thisGroup = {}; + thisGroup[thisID] = 1; + thisGroupIndex = constraintGroups.length; + constraintGroups.push(thisGroup); + } else { + thisGroupIndex = constraintGroups.indexOf(thisGroup); + } + + var thisGroupKeys = Object.keys(thisGroup); + + // we know that this axis isn't in any other groups, but we don't know + // about the thatID axis. If it is, we need to merge the groups. + for(i = 0; i < constraintGroups.length; i++) { + groupi = constraintGroups[i]; + if(i !== thisGroupIndex && groupi[thatID]) { + var baseScale = groupi[thatID]; + for(j = 0; j < thisGroupKeys.length; j++) { + keyj = thisGroupKeys[j]; + groupi[keyj] = multiplyScales(baseScale, multiplyScales(scaleratio, thisGroup[keyj])); + } + constraintGroups.splice(thisGroupIndex, 1); + return; + } + } + + // otherwise, we insert the new thatID axis as the base scale (1) + // in its group, and scale the rest of the group to it + if(scaleratio !== 1) { + for(j = 0; j < thisGroupKeys.length; j++) { + var key = thisGroupKeys[j]; + thisGroup[key] = multiplyScales(scaleratio, thisGroup[key]); + } + } + thisGroup[thatID] = 1; +} + +// scales may be numbers or 'x1.3', 'yy4.5' etc to multiply by as-yet-unknown +// ratios between x and y plot sizes n times +function multiplyScales(a, b) { + var aPrefix = ''; + var bPrefix = ''; + var aLen, bLen; + + if(typeof a === 'string') { + aPrefix = a.match(/^[xy]*/)[0]; + aLen = aPrefix.length; + a = +a.substr(aLen); + } + + if(typeof b === 'string') { + bPrefix = b.match(/^[xy]*/)[0]; + bLen = bPrefix.length; + b = +b.substr(bLen); + } + + var c = a * b; + + // just two numbers + if(!aLen && !bLen) { + return c; + } + + // one or more prefixes of the same type + if(!aLen || !bLen || aPrefix.charAt(0) === bPrefix.charAt(0)) { + return aPrefix + bPrefix + (a * b); + } + + // x and y cancel each other out exactly - back to a number + if(aLen === bLen) { + return c; + } + + // partial cancelation of prefixes + return (aLen > bLen ? aPrefix.substr(bLen) : bPrefix.substr(aLen)) + c; +} + +function finalRatios(group, fullLayout) { + var size = fullLayout._size; + var yRatio = size.h / size.w; + var out = {}; + var keys = Object.keys(group); + for(var i = 0; i < keys.length; i++) { + var key = keys[i]; + var val = group[key]; + + if(typeof val === 'string') { + var prefix = val.match(/^[xy]*/)[0]; + var pLen = prefix.length; + val = +val.substr(pLen); + var mult = prefix.charAt(0) === 'y' ? yRatio : (1 / yRatio); + for(var j = 0; j < pLen; j++) { + val *= mult; + } + } + + out[key] = val; + } + return out; +} + +exports.enforce = function enforce(gd) { + var fullLayout = gd._fullLayout; + var constraintGroups = fullLayout._axisConstraintGroups || []; + + var i, j, group, axisID, ax, normScale, mode, factor; + + // matching constraints are handled in the autorange code when autoranged, + // or in the supplyDefaults code when explicitly ranged. + // now we just need to handle scaleanchor constraints + // matches constraints that chain with scaleanchor constraints are included + // here too, but because matches has already been satisfied, + // any changes here should preserve that. + for(i = 0; i < constraintGroups.length; i++) { + group = finalRatios(constraintGroups[i], fullLayout); + var axisIDs = Object.keys(group); + + var minScale = Infinity; + var maxScale = 0; + // mostly matchScale will be the same as minScale + // ie we expand axis ranges to encompass *everything* + // that's currently in any of their ranges, but during + // autorange of a subset of axes we will ignore other + // axes for this purpose. + var matchScale = Infinity; + var normScales = {}; + var axes = {}; + var hasAnyDomainConstraint = false; + + // find the (normalized) scale of each axis in the group + for(j = 0; j < axisIDs.length; j++) { + axisID = axisIDs[j]; + axes[axisID] = ax = fullLayout[id2name(axisID)]; + + if(ax._inputDomain) ax.domain = ax._inputDomain.slice(); + else ax._inputDomain = ax.domain.slice(); + + if(!ax._inputRange) ax._inputRange = ax.range.slice(); + + // set axis scale here so we can use _m rather than + // having to calculate it from length and range + ax.setScale(); + + // abs: inverted scales still satisfy the constraint + normScales[axisID] = normScale = Math.abs(ax._m) / group[axisID]; + minScale = Math.min(minScale, normScale); + if(ax.constrain === 'domain' || !ax._constraintShrinkable) { + matchScale = Math.min(matchScale, normScale); + } + + // this has served its purpose, so remove it + delete ax._constraintShrinkable; + maxScale = Math.max(maxScale, normScale); + + if(ax.constrain === 'domain') hasAnyDomainConstraint = true; + } + + // Do we have a constraint mismatch? Give a small buffer for rounding errors + if(minScale > ALMOST_EQUAL * maxScale && !hasAnyDomainConstraint) continue; + + // now increase any ranges we need to until all normalized scales are equal + for(j = 0; j < axisIDs.length; j++) { + axisID = axisIDs[j]; + normScale = normScales[axisID]; + ax = axes[axisID]; + mode = ax.constrain; + + // even if the scale didn't change, if we're shrinking domain + // we need to recalculate in case `constraintoward` changed + if(normScale !== matchScale || mode === 'domain') { + factor = normScale / matchScale; + + if(mode === 'range') { + scaleZoom(ax, factor); + } else { + // mode === 'domain' + + var inputDomain = ax._inputDomain; + var domainShrunk = (ax.domain[1] - ax.domain[0]) / + (inputDomain[1] - inputDomain[0]); + var rangeShrunk = (ax.r2l(ax.range[1]) - ax.r2l(ax.range[0])) / + (ax.r2l(ax._inputRange[1]) - ax.r2l(ax._inputRange[0])); + + factor /= domainShrunk; + + if(factor * rangeShrunk < 1) { + // we've asked to magnify the axis more than we can just by + // enlarging the domain - so we need to constrict range + ax.domain = ax._input.domain = inputDomain.slice(); + scaleZoom(ax, factor); + continue; + } + + if(rangeShrunk < 1) { + // the range has previously been constricted by ^^, but we've + // switched to the domain-constricted regime, so reset range + ax.range = ax._input.range = ax._inputRange.slice(); + factor *= rangeShrunk; + } + + if(ax.autorange) { + /* + * range & factor may need to change because range was + * calculated for the larger scaling, so some pixel + * paddings may get cut off when we reduce the domain. + * + * This is easier than the regular autorange calculation + * because we already know the scaling `m`, but we still + * need to cut out impossible constraints (like + * annotations with super-long arrows). That's what + * outerMin/Max are for - if the expansion was going to + * go beyond the original domain, it must be impossible + */ + var rl0 = ax.r2l(ax.range[0]); + var rl1 = ax.r2l(ax.range[1]); + var rangeCenter = (rl0 + rl1) / 2; + var rangeMin = rangeCenter; + var rangeMax = rangeCenter; + var halfRange = Math.abs(rl1 - rangeCenter); + // extra tiny bit for rounding errors, in case we actually + // *are* expanding to the full domain + var outerMin = rangeCenter - halfRange * factor * 1.0001; + var outerMax = rangeCenter + halfRange * factor * 1.0001; + var getPadMin = autorange.makePadFn(fullLayout, ax, 0); + var getPadMax = autorange.makePadFn(fullLayout, ax, 1); + + updateDomain(ax, factor); + var m = Math.abs(ax._m); + var extremes = autorange.concatExtremes(gd, ax); + var minArray = extremes.min; + var maxArray = extremes.max; + var newVal; + var k; + + for(k = 0; k < minArray.length; k++) { + newVal = minArray[k].val - getPadMin(minArray[k]) / m; + if(newVal > outerMin && newVal < rangeMin) { + rangeMin = newVal; + } + } + + for(k = 0; k < maxArray.length; k++) { + newVal = maxArray[k].val + getPadMax(maxArray[k]) / m; + if(newVal < outerMax && newVal > rangeMax) { + rangeMax = newVal; + } + } + + var domainExpand = (rangeMax - rangeMin) / (2 * halfRange); + factor /= domainExpand; + + rangeMin = ax.l2r(rangeMin); + rangeMax = ax.l2r(rangeMax); + ax.range = ax._input.range = (rl0 < rl1) ? + [rangeMin, rangeMax] : [rangeMax, rangeMin]; + } + + updateDomain(ax, factor); + } + } + } + } +}; + +exports.getAxisGroup = function getAxisGroup(fullLayout, axId) { + var matchGroups = fullLayout._axisMatchGroups; + + for(var i = 0; i < matchGroups.length; i++) { + var group = matchGroups[i]; + if(group[axId]) return 'g' + i; + } + return axId; +}; + +// For use before autoranging, check if this axis was previously constrained +// by domain but no longer is +exports.clean = function clean(gd, ax) { + if(ax._inputDomain) { + var isConstrained = false; + var axId = ax._id; + var constraintGroups = gd._fullLayout._axisConstraintGroups; + for(var j = 0; j < constraintGroups.length; j++) { + if(constraintGroups[j][axId]) { + isConstrained = true; + break; + } + } + if(!isConstrained || ax.constrain !== 'domain') { + ax._input.domain = ax.domain = ax._inputDomain; + delete ax._inputDomain; + } + } +}; + +function updateDomain(ax, factor) { + var inputDomain = ax._inputDomain; + var centerFraction = FROM_BL[ax.constraintoward]; + var center = inputDomain[0] + (inputDomain[1] - inputDomain[0]) * centerFraction; + + ax.domain = ax._input.domain = [ + center + (inputDomain[0] - center) / factor, + center + (inputDomain[1] - center) / factor + ]; + ax.setScale(); +} + +},{"../../constants/alignment":260,"../../constants/numerical":265,"../../lib":285,"./autorange":330,"./axis_ids":335,"./layout_attributes":346,"./scale_zoom":350,"./set_convert":352}],340:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var tinycolor = _dereq_('tinycolor2'); +var supportsPassive = _dereq_('has-passive-events'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Fx = _dereq_('../../components/fx'); +var Axes = _dereq_('./axes'); +var setCursor = _dereq_('../../lib/setcursor'); +var dragElement = _dereq_('../../components/dragelement'); +var helpers = _dereq_('../../components/dragelement/helpers'); +var selectingOrDrawing = helpers.selectingOrDrawing; +var freeMode = helpers.freeMode; + +var FROM_TL = _dereq_('../../constants/alignment').FROM_TL; +var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases'); +var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces; + +var Plots = _dereq_('../plots'); + +var getFromId = _dereq_('./axis_ids').getFromId; +var prepSelect = _dereq_('./select').prepSelect; +var clearSelect = _dereq_('./select').clearSelect; +var selectOnClick = _dereq_('./select').selectOnClick; +var scaleZoom = _dereq_('./scale_zoom'); + +var constants = _dereq_('./constants'); +var MINDRAG = constants.MINDRAG; +var MINZOOM = constants.MINZOOM; + +// flag for showing "doubleclick to zoom out" only at the beginning +var SHOWZOOMOUTTIP = true; + +// dragBox: create an element to drag one or more axis ends +// inputs: +// plotinfo - which subplot are we making dragboxes on? +// x,y,w,h - left, top, width, height of the box +// ns - how does this drag the vertical axis? +// 'n' - top only +// 's' - bottom only +// 'ns' - top and bottom together, difference unchanged +// ew - same for horizontal axis +function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { + // mouseDown stores ms of first mousedown event in the last + // `gd._context.doubleClickDelay` ms on the drag bars + // numClicks stores how many mousedowns have been seen + // within `gd._context.doubleClickDelay` so we can check for click or doubleclick events + // dragged stores whether a drag has occurred, so we don't have to + // redraw unnecessarily, ie if no move bigger than MINDRAG or MINZOOM px + var zoomlayer = gd._fullLayout._zoomlayer; + var isMainDrag = (ns + ew === 'nsew'); + var singleEnd = (ns + ew).length === 1; + + // main subplot x and y (i.e. found in plotinfo - the main ones) + var xa0, ya0; + // {ax._id: ax} hash objects + var xaHash, yaHash; + // xaHash/yaHash values (arrays) + var xaxes, yaxes; + // main axis offsets + var xs, ys; + // main axis lengths + var pw, ph; + // contains keys 'xaHash', 'yaHash', 'xaxes', and 'yaxes' + // which are the x/y {ax._id: ax} hash objects and their values + // for linked axis relative to this subplot + var links; + // similar to `links` but for matching axes + var matches; + // set to ew/ns val when active, set to '' when inactive + var xActive, yActive; + // are all axes in this subplot are fixed? + var allFixedRanges; + // do we need to edit x/y ranges? + var editX, editY; + // graph-wide optimization flags + var hasScatterGl, hasSplom, hasSVG; + // collected changes to be made to the plot by relayout at the end + var updates; + // scaling factors from css transform + var scaleX; + var scaleY; + + function recomputeAxisLists() { + xa0 = plotinfo.xaxis; + ya0 = plotinfo.yaxis; + pw = xa0._length; + ph = ya0._length; + xs = xa0._offset; + ys = ya0._offset; + + xaHash = {}; + xaHash[xa0._id] = xa0; + yaHash = {}; + yaHash[ya0._id] = ya0; + + // if we're dragging two axes at once, also drag overlays + if(ns && ew) { + var overlays = plotinfo.overlays; + for(var i = 0; i < overlays.length; i++) { + var xa = overlays[i].xaxis; + xaHash[xa._id] = xa; + var ya = overlays[i].yaxis; + yaHash[ya._id] = ya; + } + } + + xaxes = hashValues(xaHash); + yaxes = hashValues(yaHash); + xActive = isDirectionActive(xaxes, ew); + yActive = isDirectionActive(yaxes, ns); + allFixedRanges = !yActive && !xActive; + + matches = calcLinks(gd, gd._fullLayout._axisMatchGroups, xaHash, yaHash); + links = calcLinks(gd, gd._fullLayout._axisConstraintGroups, xaHash, yaHash, matches); + var spConstrained = links.isSubplotConstrained || matches.isSubplotConstrained; + editX = ew || spConstrained; + editY = ns || spConstrained; + + var fullLayout = gd._fullLayout; + hasScatterGl = fullLayout._has('scattergl'); + hasSplom = fullLayout._has('splom'); + hasSVG = fullLayout._has('svg'); + } + + recomputeAxisLists(); + + var cursor = getDragCursor(yActive + xActive, gd._fullLayout.dragmode, isMainDrag); + var dragger = makeRectDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h); + + // still need to make the element if the axes are disabled + // but nuke its events (except for maindrag which needs them for hover) + // and stop there + if(allFixedRanges && !isMainDrag) { + dragger.onmousedown = null; + dragger.style.pointerEvents = 'none'; + return dragger; + } + + var dragOptions = { + element: dragger, + gd: gd, + plotinfo: plotinfo + }; + + dragOptions.prepFn = function(e, startX, startY) { + var dragModePrev = dragOptions.dragmode; + var dragModeNow = gd._fullLayout.dragmode; + if(dragModeNow !== dragModePrev) { + dragOptions.dragmode = dragModeNow; + } + + recomputeAxisLists(); + + scaleX = gd._fullLayout._invScaleX; + scaleY = gd._fullLayout._invScaleY; + + if(!allFixedRanges) { + if(isMainDrag) { + // main dragger handles all drag modes, and changes + // to pan (or to zoom if it already is pan) on shift + if(e.shiftKey) { + if(dragModeNow === 'pan') dragModeNow = 'zoom'; + else if(!selectingOrDrawing(dragModeNow)) dragModeNow = 'pan'; + } else if(e.ctrlKey) { + dragModeNow = 'pan'; + } + } else { + // all other draggers just pan + dragModeNow = 'pan'; + } + } + + if(freeMode(dragModeNow)) dragOptions.minDrag = 1; + else dragOptions.minDrag = undefined; + + if(selectingOrDrawing(dragModeNow)) { + dragOptions.xaxes = xaxes; + dragOptions.yaxes = yaxes; + // this attaches moveFn, clickFn, doneFn on dragOptions + prepSelect(e, startX, startY, dragOptions, dragModeNow); + } else { + dragOptions.clickFn = clickFn; + if(selectingOrDrawing(dragModePrev)) { + // TODO Fix potential bug + // Note: clearing / resetting selection state only happens, when user + // triggers at least one interaction in pan/zoom mode. Otherwise, the + // select/lasso outlines are deleted (in plots.js.cleanPlot) but the selection + // cache isn't cleared. So when the user switches back to select/lasso and + // 'adds to a selection' with Shift, the "old", seemingly removed outlines + // are redrawn again because the selection cache still holds their coordinates. + // However, this isn't easily solved, since plots.js would need + // to have a reference to the dragOptions object (which holds the + // selection cache). + clearAndResetSelect(); + } + + if(!allFixedRanges) { + if(dragModeNow === 'zoom') { + dragOptions.moveFn = zoomMove; + dragOptions.doneFn = zoomDone; + + // zoomMove takes care of the threshold, but we need to + // minimize this so that constrained zoom boxes will flip + // orientation at the right place + dragOptions.minDrag = 1; + + zoomPrep(e, startX, startY); + } else if(dragModeNow === 'pan') { + dragOptions.moveFn = plotDrag; + dragOptions.doneFn = dragTail; + } + } + } + + gd._fullLayout._redrag = function() { + var dragDataNow = gd._dragdata; + + if(dragDataNow && dragDataNow.element === dragger) { + var dragModeNow = gd._fullLayout.dragmode; + + if(!selectingOrDrawing(dragModeNow)) { + recomputeAxisLists(); + updateSubplots([0, 0, pw, ph]); + dragOptions.moveFn(dragDataNow.dx, dragDataNow.dy); + } + + // TODO should we try to "re-select" under select/lasso modes? + // probably best to wait for https://github.com/plotly/plotly.js/issues/1851 + } + }; + }; + + function clearAndResetSelect() { + // clear selection polygon cache (if any) + dragOptions.plotinfo.selection = false; + // clear selection outlines + clearSelect(gd); + } + + function clickFn(numClicks, evt) { + var gd = dragOptions.gd; + if(gd._fullLayout._activeShapeIndex >= 0) { + gd._fullLayout._deactivateShape(gd); + return; + } + + var clickmode = gd._fullLayout.clickmode; + + removeZoombox(gd); + + if(numClicks === 2 && !singleEnd) doubleClick(); + + if(isMainDrag) { + if(clickmode.indexOf('select') > -1) { + selectOnClick(evt, gd, xaxes, yaxes, plotinfo.id, dragOptions); + } + + if(clickmode.indexOf('event') > -1) { + Fx.click(gd, evt, plotinfo.id); + } + } else if(numClicks === 1 && singleEnd) { + var ax = ns ? ya0 : xa0; + var end = (ns === 's' || ew === 'w') ? 0 : 1; + var attrStr = ax._name + '.range[' + end + ']'; + var initialText = getEndText(ax, end); + var hAlign = 'left'; + var vAlign = 'middle'; + + if(ax.fixedrange) return; + + if(ns) { + vAlign = (ns === 'n') ? 'top' : 'bottom'; + if(ax.side === 'right') hAlign = 'right'; + } else if(ew === 'e') hAlign = 'right'; + + if(gd._context.showAxisRangeEntryBoxes) { + d3.select(dragger) + .call(svgTextUtils.makeEditable, { + gd: gd, + immediate: true, + background: gd._fullLayout.paper_bgcolor, + text: String(initialText), + fill: ax.tickfont ? ax.tickfont.color : '#444', + horizontalAlign: hAlign, + verticalAlign: vAlign + }) + .on('edit', function(text) { + var v = ax.d2r(text); + if(v !== undefined) { + Registry.call('_guiRelayout', gd, attrStr, v); + } + }); + } + } + } + + dragElement.init(dragOptions); + + // x/y px position at start of drag + var x0, y0; + // bbox object of the zoombox + var box; + // luminance of bg behind zoombox + var lum; + // zoombox path outline + var path0; + // is zoombox dimmed (during drag) + var dimmed; + // 'x'-only, 'y' or 'xy' zooming + var zoomMode; + // zoombox d3 selection + var zb; + // zoombox corner d3 selection + var corners; + // zoom takes over minDrag, so it also has to take over gd._dragged + var zoomDragged; + + function zoomPrep(e, startX, startY) { + var dragBBox = dragger.getBoundingClientRect(); + x0 = startX - dragBBox.left; + y0 = startY - dragBBox.top; + + gd._fullLayout._calcInverseTransform(gd); + var transformedCoords = Lib.apply3DTransform(gd._fullLayout._invTransform)(x0, y0); + x0 = transformedCoords[0]; + y0 = transformedCoords[1]; + + box = {l: x0, r: x0, w: 0, t: y0, b: y0, h: 0}; + lum = gd._hmpixcount ? + (gd._hmlumcount / gd._hmpixcount) : + tinycolor(gd._fullLayout.plot_bgcolor).getLuminance(); + path0 = 'M0,0H' + pw + 'V' + ph + 'H0V0'; + dimmed = false; + zoomMode = 'xy'; + zoomDragged = false; + zb = makeZoombox(zoomlayer, lum, xs, ys, path0); + corners = makeCorners(zoomlayer, xs, ys); + } + + function zoomMove(dx0, dy0) { + if(gd._transitioningWithDuration) { + return false; + } + + var x1 = Math.max(0, Math.min(pw, scaleX * dx0 + x0)); + var y1 = Math.max(0, Math.min(ph, scaleY * dy0 + y0)); + var dx = Math.abs(x1 - x0); + var dy = Math.abs(y1 - y0); + + box.l = Math.min(x0, x1); + box.r = Math.max(x0, x1); + box.t = Math.min(y0, y1); + box.b = Math.max(y0, y1); + + function noZoom() { + zoomMode = ''; + box.r = box.l; + box.t = box.b; + corners.attr('d', 'M0,0Z'); + } + + if(links.isSubplotConstrained) { + if(dx > MINZOOM || dy > MINZOOM) { + zoomMode = 'xy'; + if(dx / pw > dy / ph) { + dy = dx * ph / pw; + if(y0 > y1) box.t = y0 - dy; + else box.b = y0 + dy; + } else { + dx = dy * pw / ph; + if(x0 > x1) box.l = x0 - dx; + else box.r = x0 + dx; + } + corners.attr('d', xyCorners(box)); + } else { + noZoom(); + } + } else if(matches.isSubplotConstrained) { + if(dx > MINZOOM || dy > MINZOOM) { + zoomMode = 'xy'; + + var r0 = Math.min(box.l / pw, (ph - box.b) / ph); + var r1 = Math.max(box.r / pw, (ph - box.t) / ph); + + box.l = r0 * pw; + box.r = r1 * pw; + box.b = (1 - r0) * ph; + box.t = (1 - r1) * ph; + corners.attr('d', xyCorners(box)); + } else { + noZoom(); + } + } else if(!yActive || dy < Math.min(Math.max(dx * 0.6, MINDRAG), MINZOOM)) { + // look for small drags in one direction or the other, + // and only drag the other axis + + if(dx < MINDRAG || !xActive) { + noZoom(); + } else { + box.t = 0; + box.b = ph; + zoomMode = 'x'; + corners.attr('d', xCorners(box, y0)); + } + } else if(!xActive || dx < Math.min(dy * 0.6, MINZOOM)) { + box.l = 0; + box.r = pw; + zoomMode = 'y'; + corners.attr('d', yCorners(box, x0)); + } else { + zoomMode = 'xy'; + corners.attr('d', xyCorners(box)); + } + box.w = box.r - box.l; + box.h = box.b - box.t; + + if(zoomMode) zoomDragged = true; + gd._dragged = zoomDragged; + + updateZoombox(zb, corners, box, path0, dimmed, lum); + computeZoomUpdates(); + gd.emit('plotly_relayouting', updates); + dimmed = true; + } + + function computeZoomUpdates() { + updates = {}; + + // TODO: edit linked axes in zoomAxRanges and in dragTail + if(zoomMode === 'xy' || zoomMode === 'x') { + zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes); + updateMatchedAxRange('x', updates); + } + if(zoomMode === 'xy' || zoomMode === 'y') { + zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes); + updateMatchedAxRange('y', updates); + } + } + + function zoomDone() { + computeZoomUpdates(); + removeZoombox(gd); + dragTail(); + showDoubleClickNotifier(gd); + } + + // scroll zoom, on all draggers except corners + var scrollViewBox = [0, 0, pw, ph]; + // wait a little after scrolling before redrawing + var redrawTimer = null; + var REDRAWDELAY = constants.REDRAWDELAY; + var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo; + + function zoomWheel(e) { + // deactivate mousewheel scrolling on embedded graphs + // devs can override this with layout._enablescrollzoom, + // but _ ensures this setting won't leave their page + if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) { + return; + } + + clearAndResetSelect(); + + // If a transition is in progress, then disable any behavior: + if(gd._transitioningWithDuration) { + e.preventDefault(); + e.stopPropagation(); + return; + } + + recomputeAxisLists(); + + clearTimeout(redrawTimer); + + var wheelDelta = -e.deltaY; + if(!isFinite(wheelDelta)) wheelDelta = e.wheelDelta / 10; + if(!isFinite(wheelDelta)) { + Lib.log('Did not find wheel motion attributes: ', e); + return; + } + + var zoom = Math.exp(-Math.min(Math.max(wheelDelta, -20), 20) / 200); + var gbb = mainplot.draglayer.select('.nsewdrag').node().getBoundingClientRect(); + var xfrac = (e.clientX - gbb.left) / gbb.width; + var yfrac = (gbb.bottom - e.clientY) / gbb.height; + var i; + + function zoomWheelOneAxis(ax, centerFraction, zoom) { + if(ax.fixedrange) return; + + var axRange = Lib.simpleMap(ax.range, ax.r2l); + var v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction; + function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); } + ax.range = axRange.map(doZoom); + } + + if(editX) { + // if we're only zooming this axis because of constraints, + // zoom it about the center + if(!ew) xfrac = 0.5; + + for(i = 0; i < xaxes.length; i++) { + zoomWheelOneAxis(xaxes[i], xfrac, zoom); + } + updateMatchedAxRange('x'); + + scrollViewBox[2] *= zoom; + scrollViewBox[0] += scrollViewBox[2] * xfrac * (1 / zoom - 1); + } + if(editY) { + if(!ns) yfrac = 0.5; + + for(i = 0; i < yaxes.length; i++) { + zoomWheelOneAxis(yaxes[i], yfrac, zoom); + } + updateMatchedAxRange('y'); + + scrollViewBox[3] *= zoom; + scrollViewBox[1] += scrollViewBox[3] * (1 - yfrac) * (1 / zoom - 1); + } + + // viewbox redraw at first + updateSubplots(scrollViewBox); + ticksAndAnnotations(); + + gd.emit('plotly_relayouting', updates); + + // then replot after a delay to make sure + // no more scrolling is coming + redrawTimer = setTimeout(function() { + if(!gd._fullLayout) return; + scrollViewBox = [0, 0, pw, ph]; + dragTail(); + }, REDRAWDELAY); + + e.preventDefault(); + return; + } + + // everything but the corners gets wheel zoom + if(ns.length * ew.length !== 1) { + attachWheelEventHandler(dragger, zoomWheel); + } + + // plotDrag: move the plot in response to a drag + function plotDrag(dx, dy) { + dx = dx * scaleX; + dy = dy * scaleY; + // If a transition is in progress, then disable any behavior: + if(gd._transitioningWithDuration) { + return; + } + + // prevent axis drawing from monkeying with margins until we're done + gd._fullLayout._replotting = true; + + if(xActive === 'ew' || yActive === 'ns') { + var spDx = xActive ? -dx : 0; + var spDy = yActive ? -dy : 0; + if(matches.isSubplotConstrained) { + if(xActive && yActive) { + var frac = (dx / pw - dy / ph) / 2; + dx = frac * pw; + dy = -frac * ph; + spDx = -dx; + spDy = -dy; + } + if(yActive) { + spDx = -spDy * pw / ph; + } else { + spDy = -spDx * ph / pw; + } + } + if(xActive) { + dragAxList(xaxes, dx); + updateMatchedAxRange('x'); + } + if(yActive) { + dragAxList(yaxes, dy); + updateMatchedAxRange('y'); + } + updateSubplots([spDx, spDy, pw, ph]); + ticksAndAnnotations(); + gd.emit('plotly_relayouting', updates); + return; + } + + // dz: set a new value for one end (0 or 1) of an axis array axArray, + // and return a pixel shift for that end for the viewbox + // based on pixel drag distance d + // TODO: this makes (generally non-fatal) errors when you get + // near floating point limits + function dz(axArray, end, d) { + var otherEnd = 1 - end; + var movedAx; + var newLinearizedEnd; + for(var i = 0; i < axArray.length; i++) { + var axi = axArray[i]; + if(axi.fixedrange) continue; + movedAx = axi; + newLinearizedEnd = axi._rl[otherEnd] + + (axi._rl[end] - axi._rl[otherEnd]) / dZoom(d / axi._length); + var newEnd = axi.l2r(newLinearizedEnd); + + // if l2r comes back false or undefined, it means we've dragged off + // the end of valid ranges - so stop. + if(newEnd !== false && newEnd !== undefined) axi.range[end] = newEnd; + } + return movedAx._length * (movedAx._rl[end] - newLinearizedEnd) / + (movedAx._rl[end] - movedAx._rl[otherEnd]); + } + + var dxySign = ((xActive === 'w') === (yActive === 'n')) ? 1 : -1; + if(xActive && yActive && (links.isSubplotConstrained || matches.isSubplotConstrained)) { + // dragging a corner of a constrained subplot: + // respect the fixed corner, but harmonize dx and dy + var dxyFraction = (dx / pw + dxySign * dy / ph) / 2; + dx = dxyFraction * pw; + dy = dxySign * dxyFraction * ph; + } + + var xStart, yStart; + + if(xActive === 'w') dx = dz(xaxes, 0, dx); + else if(xActive === 'e') dx = dz(xaxes, 1, -dx); + else if(!xActive) dx = 0; + + if(yActive === 'n') dy = dz(yaxes, 1, dy); + else if(yActive === 's') dy = dz(yaxes, 0, -dy); + else if(!yActive) dy = 0; + + xStart = (xActive === 'w') ? dx : 0; + yStart = (yActive === 'n') ? dy : 0; + + if( + (links.isSubplotConstrained && !matches.isSubplotConstrained) || + // NW or SE on matching axes - create a symmetric zoom + (matches.isSubplotConstrained && xActive && yActive && dxySign > 0) + ) { + var i; + if(matches.isSubplotConstrained || (!xActive && yActive.length === 1)) { + // dragging one end of the y axis of a constrained subplot + // scale the other axis the same about its middle + for(i = 0; i < xaxes.length; i++) { + xaxes[i].range = xaxes[i]._r.slice(); + scaleZoom(xaxes[i], 1 - dy / ph); + } + dx = dy * pw / ph; + xStart = dx / 2; + } + if(matches.isSubplotConstrained || (!yActive && xActive.length === 1)) { + for(i = 0; i < yaxes.length; i++) { + yaxes[i].range = yaxes[i]._r.slice(); + scaleZoom(yaxes[i], 1 - dx / pw); + } + dy = dx * ph / pw; + yStart = dy / 2; + } + } + + if(!matches.isSubplotConstrained || !yActive) { + updateMatchedAxRange('x'); + } + if(!matches.isSubplotConstrained || !xActive) { + updateMatchedAxRange('y'); + } + var xSize = pw - dx; + var ySize = ph - dy; + if(matches.isSubplotConstrained && !(xActive && yActive)) { + if(xActive) { + yStart = xStart ? 0 : (dx * ph / pw); + ySize = xSize * ph / pw; + } else { + xStart = yStart ? 0 : (dy * pw / ph); + xSize = ySize * pw / ph; + } + } + updateSubplots([xStart, yStart, xSize, ySize]); + ticksAndAnnotations(); + gd.emit('plotly_relayouting', updates); + } + + function updateMatchedAxRange(axLetter, out) { + var matchedAxes = matches.isSubplotConstrained ? + {x: yaxes, y: xaxes}[axLetter] : + matches[axLetter + 'axes']; + + var constrainedAxes = matches.isSubplotConstrained ? + {x: xaxes, y: yaxes}[axLetter] : + []; + + for(var i = 0; i < matchedAxes.length; i++) { + var ax = matchedAxes[i]; + var axId = ax._id; + var axId2 = matches.xLinks[axId] || matches.yLinks[axId]; + var ax2 = constrainedAxes[0] || xaHash[axId2] || yaHash[axId2]; + + if(ax2) { + if(out) { + // zoombox case - don't mutate 'range', just add keys in 'updates' + out[ax._name + '.range[0]'] = out[ax2._name + '.range[0]']; + out[ax._name + '.range[1]'] = out[ax2._name + '.range[1]']; + } else { + ax.range = ax2.range.slice(); + } + } + } + } + + // Draw ticks and annotations (and other components) when ranges change. + // Also records the ranges that have changed for use by update at the end. + function ticksAndAnnotations() { + var activeAxIds = []; + var i; + + function pushActiveAxIds(axList) { + for(i = 0; i < axList.length; i++) { + if(!axList[i].fixedrange) activeAxIds.push(axList[i]._id); + } + } + + if(editX) { + pushActiveAxIds(xaxes); + pushActiveAxIds(links.xaxes); + pushActiveAxIds(matches.xaxes); + } + if(editY) { + pushActiveAxIds(yaxes); + pushActiveAxIds(links.yaxes); + pushActiveAxIds(matches.yaxes); + } + + updates = {}; + for(i = 0; i < activeAxIds.length; i++) { + var axId = activeAxIds[i]; + var ax = getFromId(gd, axId); + Axes.drawOne(gd, ax, {skipTitle: true}); + updates[ax._name + '.range[0]'] = ax.range[0]; + updates[ax._name + '.range[1]'] = ax.range[1]; + } + + Axes.redrawComponents(gd, activeAxIds); + } + + function doubleClick() { + if(gd._transitioningWithDuration) return; + + var doubleClickConfig = gd._context.doubleClick; + + var axList = []; + if(xActive) axList = axList.concat(xaxes); + if(yActive) axList = axList.concat(yaxes); + if(matches.xaxes) axList = axList.concat(matches.xaxes); + if(matches.yaxes) axList = axList.concat(matches.yaxes); + + var attrs = {}; + var ax, i, rangeInitial; + + // For reset+autosize mode: + // If *any* of the main axes is not at its initial range + // (or autoranged, if we have no initial range, to match the logic in + // doubleClickConfig === 'reset' below), we reset. + // If they are *all* at their initial ranges, then we autosize. + if(doubleClickConfig === 'reset+autosize') { + doubleClickConfig = 'autosize'; + + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + if((ax._rangeInitial && ( + ax.range[0] !== ax._rangeInitial[0] || + ax.range[1] !== ax._rangeInitial[1] + )) || + (!ax._rangeInitial && !ax.autorange) + ) { + doubleClickConfig = 'reset'; + break; + } + } + } + + if(doubleClickConfig === 'autosize') { + // don't set the linked axes here, so relayout marks them as shrinkable + // and we autosize just to the requested axis/axes + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + if(!ax.fixedrange) attrs[ax._name + '.autorange'] = true; + } + } else if(doubleClickConfig === 'reset') { + // when we're resetting, reset all linked axes too, so we get back + // to the fully-auto-with-constraints situation + if(xActive || links.isSubplotConstrained) axList = axList.concat(links.xaxes); + if(yActive && !links.isSubplotConstrained) axList = axList.concat(links.yaxes); + + if(links.isSubplotConstrained) { + if(!xActive) axList = axList.concat(xaxes); + else if(!yActive) axList = axList.concat(yaxes); + } + + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + + if(!ax.fixedrange) { + if(!ax._rangeInitial) { + attrs[ax._name + '.autorange'] = true; + } else { + rangeInitial = ax._rangeInitial; + attrs[ax._name + '.range[0]'] = rangeInitial[0]; + attrs[ax._name + '.range[1]'] = rangeInitial[1]; + } + } + } + } + + gd.emit('plotly_doubleclick', null); + Registry.call('_guiRelayout', gd, attrs); + } + + // dragTail - finish a drag event with a redraw + function dragTail() { + // put the subplot viewboxes back to default (Because we're going to) + // be repositioning the data in the relayout. But DON'T call + // ticksAndAnnotations again - it's unnecessary and would overwrite `updates` + updateSubplots([0, 0, pw, ph]); + + // since we may have been redrawing some things during the drag, we may have + // accumulated MathJax promises - wait for them before we relayout. + Lib.syncOrAsync([ + Plots.previousPromises, + function() { + gd._fullLayout._replotting = false; + Registry.call('_guiRelayout', gd, updates); + } + ], gd); + } + + // updateSubplots - find all plot viewboxes that should be + // affected by this drag, and update them. look for all plots + // sharing an affected axis (including the one being dragged), + // includes also scattergl and splom logic. + function updateSubplots(viewBox) { + var fullLayout = gd._fullLayout; + var plotinfos = fullLayout._plots; + var subplots = fullLayout._subplots.cartesian; + var i, sp, xa, ya; + + if(hasSplom) { + Registry.subplotsRegistry.splom.drag(gd); + } + + if(hasScatterGl) { + for(i = 0; i < subplots.length; i++) { + sp = plotinfos[subplots[i]]; + xa = sp.xaxis; + ya = sp.yaxis; + + if(sp._scene) { + var xrng = Lib.simpleMap(xa.range, xa.r2l); + var yrng = Lib.simpleMap(ya.range, ya.r2l); + sp._scene.update({range: [xrng[0], yrng[0], xrng[1], yrng[1]]}); + } + } + } + + if(hasSplom || hasScatterGl) { + clearGlCanvases(gd); + redrawReglTraces(gd); + } + + if(hasSVG) { + var xScaleFactor = viewBox[2] / xa0._length; + var yScaleFactor = viewBox[3] / ya0._length; + + for(i = 0; i < subplots.length; i++) { + sp = plotinfos[subplots[i]]; + xa = sp.xaxis; + ya = sp.yaxis; + + var editX2 = (editX || matches.isSubplotConstrained) && !xa.fixedrange && xaHash[xa._id]; + var editY2 = (editY || matches.isSubplotConstrained) && !ya.fixedrange && yaHash[ya._id]; + + var xScaleFactor2, yScaleFactor2; + var clipDx, clipDy; + + if(editX2) { + xScaleFactor2 = xScaleFactor; + clipDx = ew || matches.isSubplotConstrained ? viewBox[0] : getShift(xa, xScaleFactor2); + } else if(matches.xaHash[xa._id]) { + xScaleFactor2 = xScaleFactor; + clipDx = viewBox[0] * xa._length / xa0._length; + } else if(matches.yaHash[xa._id]) { + xScaleFactor2 = yScaleFactor; + clipDx = yActive === 'ns' ? + -viewBox[1] * xa._length / ya0._length : + getShift(xa, xScaleFactor2, {n: 'top', s: 'bottom'}[yActive]); + } else { + xScaleFactor2 = getLinkedScaleFactor(xa, xScaleFactor, yScaleFactor); + clipDx = scaleAndGetShift(xa, xScaleFactor2); + } + + if(editY2) { + yScaleFactor2 = yScaleFactor; + clipDy = ns || matches.isSubplotConstrained ? viewBox[1] : getShift(ya, yScaleFactor2); + } else if(matches.yaHash[ya._id]) { + yScaleFactor2 = yScaleFactor; + clipDy = viewBox[1] * ya._length / ya0._length; + } else if(matches.xaHash[ya._id]) { + yScaleFactor2 = xScaleFactor; + clipDy = xActive === 'ew' ? + -viewBox[0] * ya._length / xa0._length : + getShift(ya, yScaleFactor2, {e: 'right', w: 'left'}[xActive]); + } else { + yScaleFactor2 = getLinkedScaleFactor(ya, xScaleFactor, yScaleFactor); + clipDy = scaleAndGetShift(ya, yScaleFactor2); + } + + // don't scale at all if neither axis is scalable here + if(!xScaleFactor2 && !yScaleFactor2) { + continue; + } + + // but if only one is, reset the other axis scaling + if(!xScaleFactor2) xScaleFactor2 = 1; + if(!yScaleFactor2) yScaleFactor2 = 1; + + var plotDx = xa._offset - clipDx / xScaleFactor2; + var plotDy = ya._offset - clipDy / yScaleFactor2; + + // TODO could be more efficient here: + // setTranslate and setScale do a lot of extra work + // when working independently, should perhaps combine + // them into a single routine. + sp.clipRect + .call(Drawing.setTranslate, clipDx, clipDy) + .call(Drawing.setScale, xScaleFactor2, yScaleFactor2); + + sp.plot + .call(Drawing.setTranslate, plotDx, plotDy) + .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2); + + // apply an inverse scale to individual points to counteract + // the scale of the trace group. + // apply only when scale changes, as adjusting the scale of + // all the points can be expansive. + if(xScaleFactor2 !== sp.xScaleFactor || yScaleFactor2 !== sp.yScaleFactor) { + Drawing.setPointGroupScale(sp.zoomScalePts, xScaleFactor2, yScaleFactor2); + Drawing.setTextPointsScale(sp.zoomScaleTxt, xScaleFactor2, yScaleFactor2); + } + + Drawing.hideOutsideRangePoints(sp.clipOnAxisFalseTraces, sp); + + // update x/y scaleFactor stash + sp.xScaleFactor = xScaleFactor2; + sp.yScaleFactor = yScaleFactor2; + } + } + } + + // Find the appropriate scaling for this axis, if it's linked to the + // dragged axes by constraints. 0 is special, it means this axis shouldn't + // ever be scaled (will be converted to 1 if the other axis is scaled) + function getLinkedScaleFactor(ax, xScaleFactor, yScaleFactor) { + if(ax.fixedrange) return 0; + + if(editX && links.xaHash[ax._id]) { + return xScaleFactor; + } + if(editY && (links.isSubplotConstrained ? links.xaHash : links.yaHash)[ax._id]) { + return yScaleFactor; + } + return 0; + } + + function scaleAndGetShift(ax, scaleFactor) { + if(scaleFactor) { + ax.range = ax._r.slice(); + scaleZoom(ax, scaleFactor); + return getShift(ax, scaleFactor); + } + return 0; + } + + function getShift(ax, scaleFactor, from) { + return ax._length * (1 - scaleFactor) * FROM_TL[from || ax.constraintoward || 'middle']; + } + + return dragger; +} + +function makeDragger(plotinfo, nodeName, dragClass, cursor) { + var dragger3 = Lib.ensureSingle(plotinfo.draglayer, nodeName, dragClass, function(s) { + s.classed('drag', true) + .style({fill: 'transparent', 'stroke-width': 0}) + .attr('data-subplot', plotinfo.id); + }); + + dragger3.call(setCursor, cursor); + + return dragger3.node(); +} + +function makeRectDragger(plotinfo, dragClass, cursor, x, y, w, h) { + var dragger = makeDragger(plotinfo, 'rect', dragClass, cursor); + d3.select(dragger).call(Drawing.setRect, x, y, w, h); + return dragger; +} + +function isDirectionActive(axList, activeVal) { + for(var i = 0; i < axList.length; i++) { + if(!axList[i].fixedrange) return activeVal; + } + return ''; +} + +function getEndText(ax, end) { + var initialVal = ax.range[end]; + var diff = Math.abs(initialVal - ax.range[1 - end]); + var dig; + + // TODO: this should basically be ax.r2d but we're doing extra + // rounding here... can we clean up at all? + if(ax.type === 'date') { + return initialVal; + } else if(ax.type === 'log') { + dig = Math.ceil(Math.max(0, -Math.log(diff) / Math.LN10)) + 3; + return d3.format('.' + dig + 'g')(Math.pow(10, initialVal)); + } else { // linear numeric (or category... but just show numbers here) + dig = Math.floor(Math.log(Math.abs(initialVal)) / Math.LN10) - + Math.floor(Math.log(diff) / Math.LN10) + 4; + return d3.format('.' + String(dig) + 'g')(initialVal); + } +} + +function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) { + for(var i = 0; i < axList.length; i++) { + var axi = axList[i]; + if(axi.fixedrange) continue; + + if(axi.rangebreaks) { + var isY = axi._id.charAt(0) === 'y'; + var r0F = isY ? (1 - r0Fraction) : r0Fraction; + var r1F = isY ? (1 - r1Fraction) : r1Fraction; + + updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l(r0F * axi._length)); + updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l(r1F * axi._length)); + } else { + var axRangeLinear0 = axi._rl[0]; + var axRangeLinearSpan = axi._rl[1] - axRangeLinear0; + updates[axi._name + '.range[0]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction); + updates[axi._name + '.range[1]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction); + } + } + + // zoom linked axes about their centers + if(linkedAxes && linkedAxes.length) { + var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2; + zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates, []); + } +} + +function dragAxList(axList, pix) { + for(var i = 0; i < axList.length; i++) { + var axi = axList[i]; + if(!axi.fixedrange) { + if(axi.rangebreaks) { + var p0 = 0; + var p1 = axi._length; + var d0 = axi.p2l(p0 + pix) - axi.p2l(p0); + var d1 = axi.p2l(p1 + pix) - axi.p2l(p1); + var delta = (d0 + d1) / 2; + + axi.range = [ + axi.l2r(axi._rl[0] - delta), + axi.l2r(axi._rl[1] - delta) + ]; + } else { + axi.range = [ + axi.l2r(axi._rl[0] - pix / axi._m), + axi.l2r(axi._rl[1] - pix / axi._m) + ]; + } + } + } +} + +// common transform for dragging one end of an axis +// d>0 is compressing scale (cursor is over the plot, +// the axis end should move with the cursor) +// d<0 is expanding (cursor is off the plot, axis end moves +// nonlinearly so you can expand far) +function dZoom(d) { + return 1 - ((d >= 0) ? Math.min(d, 0.9) : + 1 / (1 / Math.max(d, -0.3) + 3.222)); +} + +function getDragCursor(nsew, dragmode, isMainDrag) { + if(!nsew) return 'pointer'; + if(nsew === 'nsew') { + // in this case here, clear cursor and + // use the cursor style set on + if(isMainDrag) return ''; + if(dragmode === 'pan') return 'move'; + return 'crosshair'; + } + return nsew.toLowerCase() + '-resize'; +} + +function makeZoombox(zoomlayer, lum, xs, ys, path0) { + return zoomlayer.append('path') + .attr('class', 'zoombox') + .style({ + 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)', + 'stroke-width': 0 + }) + .attr('transform', strTranslate(xs, ys)) + .attr('d', path0 + 'Z'); +} + +function makeCorners(zoomlayer, xs, ys) { + return zoomlayer.append('path') + .attr('class', 'zoombox-corners') + .style({ + fill: Color.background, + stroke: Color.defaultLine, + 'stroke-width': 1, + opacity: 0 + }) + .attr('transform', strTranslate(xs, ys)) + .attr('d', 'M0,0Z'); +} + +function updateZoombox(zb, corners, box, path0, dimmed, lum) { + zb.attr('d', + path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) + + 'h' + (box.w) + 'v-' + (box.h) + 'h-' + (box.w) + 'Z'); + transitionZoombox(zb, corners, dimmed, lum); +} + +function transitionZoombox(zb, corners, dimmed, lum) { + if(!dimmed) { + zb.transition() + .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' : + 'rgba(255,255,255,0.3)') + .duration(200); + corners.transition() + .style('opacity', 1) + .duration(200); + } +} + +function removeZoombox(gd) { + d3.select(gd) + .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners') + .remove(); +} + +function showDoubleClickNotifier(gd) { + if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) { + Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long'); + SHOWZOOMOUTTIP = false; + } +} + +function xCorners(box, y0) { + return 'M' + + (box.l - 0.5) + ',' + (y0 - MINZOOM - 0.5) + + 'h-3v' + (2 * MINZOOM + 1) + 'h3ZM' + + (box.r + 0.5) + ',' + (y0 - MINZOOM - 0.5) + + 'h3v' + (2 * MINZOOM + 1) + 'h-3Z'; +} + +function yCorners(box, x0) { + return 'M' + + (x0 - MINZOOM - 0.5) + ',' + (box.t - 0.5) + + 'v-3h' + (2 * MINZOOM + 1) + 'v3ZM' + + (x0 - MINZOOM - 0.5) + ',' + (box.b + 0.5) + + 'v3h' + (2 * MINZOOM + 1) + 'v-3Z'; +} + +function xyCorners(box) { + var clen = Math.floor(Math.min(box.b - box.t, box.r - box.l, MINZOOM) / 2); + return 'M' + + (box.l - 3.5) + ',' + (box.t - 0.5 + clen) + 'h3v' + (-clen) + + 'h' + clen + 'v-3h-' + (clen + 3) + 'ZM' + + (box.r + 3.5) + ',' + (box.t - 0.5 + clen) + 'h-3v' + (-clen) + + 'h' + (-clen) + 'v-3h' + (clen + 3) + 'ZM' + + (box.r + 3.5) + ',' + (box.b + 0.5 - clen) + 'h-3v' + clen + + 'h' + (-clen) + 'v3h' + (clen + 3) + 'ZM' + + (box.l - 3.5) + ',' + (box.b + 0.5 - clen) + 'h3v' + clen + + 'h' + clen + 'v3h-' + (clen + 3) + 'Z'; +} + +function calcLinks(gd, groups, xaHash, yaHash, exclude) { + var isSubplotConstrained = false; + var xLinks = {}; + var yLinks = {}; + var xID, yID, xLinkID, yLinkID; + var xExclude = (exclude || {}).xaHash; + var yExclude = (exclude || {}).yaHash; + + for(var i = 0; i < groups.length; i++) { + var group = groups[i]; + // check if any of the x axes we're dragging is in this constraint group + for(xID in xaHash) { + if(group[xID]) { + // put the rest of these axes into xLinks, if we're not already + // dragging them, so we know to scale these axes automatically too + // to match the changes in the dragged x axes + for(xLinkID in group) { + if( + !(exclude && (xExclude[xLinkID] || yExclude[xLinkID])) && + !(xLinkID.charAt(0) === 'x' ? xaHash : yaHash)[xLinkID] + ) { + xLinks[xLinkID] = xID; + } + } + + // check if the x and y axes of THIS drag are linked + for(yID in yaHash) { + if( + !(exclude && (xExclude[yID] || yExclude[yID])) && + group[yID] + ) { + isSubplotConstrained = true; + } + } + } + } + + // now check if any of the y axes we're dragging is in this constraint group + // only look for outside links, as we've already checked for links within the dragger + for(yID in yaHash) { + if(group[yID]) { + for(yLinkID in group) { + if( + !(exclude && (xExclude[yLinkID] || yExclude[yLinkID])) && + !(yLinkID.charAt(0) === 'x' ? xaHash : yaHash)[yLinkID] + ) { + yLinks[yLinkID] = yID; + } + } + } + } + } + + if(isSubplotConstrained) { + // merge xLinks and yLinks if the subplot is constrained, + // since we'll always apply both anyway and the two will contain + // duplicates + Lib.extendFlat(xLinks, yLinks); + yLinks = {}; + } + + var xaHashLinked = {}; + var xaxesLinked = []; + for(xLinkID in xLinks) { + var xa = getFromId(gd, xLinkID); + xaxesLinked.push(xa); + xaHashLinked[xa._id] = xa; + } + + var yaHashLinked = {}; + var yaxesLinked = []; + for(yLinkID in yLinks) { + var ya = getFromId(gd, yLinkID); + yaxesLinked.push(ya); + yaHashLinked[ya._id] = ya; + } + + return { + xaHash: xaHashLinked, + yaHash: yaHashLinked, + xaxes: xaxesLinked, + yaxes: yaxesLinked, + xLinks: xLinks, + yLinks: yLinks, + isSubplotConstrained: isSubplotConstrained + }; +} + +// still seems to be some confusion about onwheel vs onmousewheel... +function attachWheelEventHandler(element, handler) { + if(!supportsPassive) { + if(element.onwheel !== undefined) element.onwheel = handler; + else if(element.onmousewheel !== undefined) element.onmousewheel = handler; + else if(!element.isAddedWheelEvent) { + element.isAddedWheelEvent = true; + element.addEventListener('wheel', handler, {passive: false}); + } + } else { + var wheelEventName = element.onwheel !== undefined ? 'wheel' : 'mousewheel'; + + if(element._onwheel) { + element.removeEventListener(wheelEventName, element._onwheel); + } + element._onwheel = handler; + + element.addEventListener(wheelEventName, handler, {passive: false}); + } +} + +function hashValues(hash) { + var out = []; + for(var k in hash) out.push(hash[k]); + return out; +} + +module.exports = { + makeDragBox: makeDragBox, + + makeDragger: makeDragger, + makeRectDragger: makeRectDragger, + makeZoombox: makeZoombox, + makeCorners: makeCorners, + + updateZoombox: updateZoombox, + xyCorners: xyCorners, + transitionZoombox: transitionZoombox, + removeZoombox: removeZoombox, + showDoubleClickNotifier: showDoubleClickNotifier, + + attachWheelEventHandler: attachWheelEventHandler +}; + +},{"../../components/color":155,"../../components/dragelement":174,"../../components/dragelement/helpers":173,"../../components/drawing":177,"../../components/fx":195,"../../constants/alignment":260,"../../lib":285,"../../lib/clear_gl_canvases":273,"../../lib/setcursor":305,"../../lib/svg_text_utils":307,"../../plot_api/subroutines":321,"../../registry":373,"../plots":366,"./axes":331,"./axis_ids":335,"./constants":338,"./scale_zoom":350,"./select":351,"@plotly/d3":20,"has-passive-events":63,"tinycolor2":119}],341:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Fx = _dereq_('../../components/fx'); +var dragElement = _dereq_('../../components/dragelement'); +var setCursor = _dereq_('../../lib/setcursor'); + +var makeDragBox = _dereq_('./dragbox').makeDragBox; +var DRAGGERSIZE = _dereq_('./constants').DRAGGERSIZE; + +exports.initInteractions = function initInteractions(gd) { + var fullLayout = gd._fullLayout; + + if(gd._context.staticPlot) { + // this sweeps up more than just cartesian drag elements... + d3.select(gd).selectAll('.drag').remove(); + return; + } + + if(!fullLayout._has('cartesian') && !fullLayout._has('splom')) return; + + var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) { + // sort overlays last, then by x axis number, then y axis number + if((fullLayout._plots[a].mainplot && true) === + (fullLayout._plots[b].mainplot && true)) { + var aParts = a.split('y'); + var bParts = b.split('y'); + return (aParts[0] === bParts[0]) ? + (Number(aParts[1] || 1) - Number(bParts[1] || 1)) : + (Number(aParts[0] || 1) - Number(bParts[0] || 1)); + } + return fullLayout._plots[a].mainplot ? 1 : -1; + }); + + subplots.forEach(function(subplot) { + var plotinfo = fullLayout._plots[subplot]; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + // main and corner draggers need not be repeated for + // overlaid subplots - these draggers drag them all + if(!plotinfo.mainplot) { + // main dragger goes over the grids and data, so we use its + // mousemove events for all data hover effects + var maindrag = makeDragBox(gd, plotinfo, xa._offset, ya._offset, + xa._length, ya._length, 'ns', 'ew'); + + maindrag.onmousemove = function(evt) { + // This is on `gd._fullLayout`, *not* fullLayout because the reference + // changes by the time this is called again. + gd._fullLayout._rehover = function() { + if((gd._fullLayout._hoversubplot === subplot) && gd._fullLayout._plots[subplot]) { + Fx.hover(gd, evt, subplot); + } + }; + + Fx.hover(gd, evt, subplot); + + // Note that we have *not* used the cached fullLayout variable here + // since that may be outdated when this is called as a callback later on + gd._fullLayout._lasthover = maindrag; + gd._fullLayout._hoversubplot = subplot; + }; + + /* + * IMPORTANT: + * We must check for the presence of the drag cover here. + * If we don't, a 'mouseout' event is triggered on the + * maindrag before each 'click' event, which has the effect + * of clearing the hoverdata; thus, cancelling the click event. + */ + maindrag.onmouseout = function(evt) { + if(gd._dragging) return; + + // When the mouse leaves this maindrag, unset the hovered subplot. + // This may cause problems if it leaves the subplot directly *onto* + // another subplot, but that's a tiny corner case at the moment. + gd._fullLayout._hoversubplot = null; + + dragElement.unhover(gd, evt); + }; + + // corner draggers + if(gd._context.showAxisDragHandles) { + makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE, + DRAGGERSIZE, DRAGGERSIZE, 'n', 'w'); + makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE, + DRAGGERSIZE, DRAGGERSIZE, 'n', 'e'); + makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length, + DRAGGERSIZE, DRAGGERSIZE, 's', 'w'); + makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length, + DRAGGERSIZE, DRAGGERSIZE, 's', 'e'); + } + } + if(gd._context.showAxisDragHandles) { + // x axis draggers - if you have overlaid plots, + // these drag each axis separately + if(subplot === xa._mainSubplot) { + // the y position of the main x axis line + var y0 = xa._mainLinePosition; + if(xa.side === 'top') y0 -= DRAGGERSIZE; + makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0, + xa._length * 0.8, DRAGGERSIZE, '', 'ew'); + makeDragBox(gd, plotinfo, xa._offset, y0, + xa._length * 0.1, DRAGGERSIZE, '', 'w'); + makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0, + xa._length * 0.1, DRAGGERSIZE, '', 'e'); + } + // y axis draggers + if(subplot === ya._mainSubplot) { + // the x position of the main y axis line + var x0 = ya._mainLinePosition; + if(ya.side !== 'right') x0 -= DRAGGERSIZE; + makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1, + DRAGGERSIZE, ya._length * 0.8, 'ns', ''); + makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9, + DRAGGERSIZE, ya._length * 0.1, 's', ''); + makeDragBox(gd, plotinfo, x0, ya._offset, + DRAGGERSIZE, ya._length * 0.1, 'n', ''); + } + } + }); + + // In case you mousemove over some hovertext, send it to Fx.hover too + // we do this so that we can put the hover text in front of everything, + // but still be able to interact with everything as if it isn't there + var hoverLayer = fullLayout._hoverlayer.node(); + + hoverLayer.onmousemove = function(evt) { + evt.target = gd._fullLayout._lasthover; + Fx.hover(gd, evt, fullLayout._hoversubplot); + }; + + hoverLayer.onclick = function(evt) { + evt.target = gd._fullLayout._lasthover; + Fx.click(gd, evt); + }; + + // also delegate mousedowns... TODO: does this actually work? + hoverLayer.onmousedown = function(evt) { + gd._fullLayout._lasthover.onmousedown(evt); + }; + + exports.updateFx(gd); +}; + +// Minimal set of update needed on 'modebar' edits. +// We only need to update the cursor style. +// +// Note that changing the axis configuration and/or the fixedrange attribute +// should trigger a full initInteractions. +exports.updateFx = function(gd) { + var fullLayout = gd._fullLayout; + var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair'; + setCursor(fullLayout._draggers, cursor); +}; + +},{"../../components/dragelement":174,"../../components/fx":195,"../../lib/setcursor":305,"./constants":338,"./dragbox":340,"@plotly/d3":20}],342:[function(_dereq_,module,exports){ +'use strict'; + +function clearOutlineControllers(gd) { + var zoomLayer = gd._fullLayout._zoomlayer; + if(zoomLayer) { + zoomLayer.selectAll('.outline-controllers').remove(); + } +} + +function clearSelect(gd) { + var zoomLayer = gd._fullLayout._zoomlayer; + if(zoomLayer) { + // until we get around to persistent selections, remove the outline + // here. The selection itself will be removed when the plot redraws + // at the end. + zoomLayer.selectAll('.select-outline').remove(); + } + + gd._fullLayout._drawing = false; +} + +module.exports = { + clearOutlineControllers: clearOutlineControllers, + clearSelect: clearSelect +}; + +},{}],343:[function(_dereq_,module,exports){ +'use strict'; + +var strTranslate = _dereq_('../../lib').strTranslate; + +// in v3 (once log ranges are fixed), +// we'll be able to p2r here for all axis types +function p2r(ax, v) { + switch(ax.type) { + case 'log': + return ax.p2d(v); + case 'date': + return ax.p2r(v, 0, ax.calendar); + default: + return ax.p2r(v); + } +} + +function r2p(ax, v) { + switch(ax.type) { + case 'log': + return ax.d2p(v); + case 'date': + return ax.r2p(v, 0, ax.calendar); + default: + return ax.r2p(v); + } +} + +function axValue(ax) { + var index = (ax._id.charAt(0) === 'y') ? 1 : 0; + return function(v) { return p2r(ax, v[index]); }; +} + +function getTransform(plotinfo) { + return strTranslate( + plotinfo.xaxis._offset, + plotinfo.yaxis._offset + ); +} + +module.exports = { + p2r: p2r, + r2p: r2p, + axValue: axValue, + getTransform: getTransform +}; + +},{"../../lib":285}],344:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var axisIds = _dereq_('./axis_ids'); + +/** + * Factory function for checking component arrays for subplot references. + * + * @param {string} containerArrayName: the top-level array in gd.layout to check + * If an item in this container is found that references a cartesian x and/or y axis, + * ensure cartesian is marked as a base plot module and record the axes (and subplot + * if both refs are axes) in gd._fullLayout + * + * @return {function}: with args layoutIn (gd.layout) and layoutOut (gd._fullLayout) + * as expected of a component includeBasePlot method + */ +module.exports = function makeIncludeComponents(containerArrayName) { + return function includeComponents(layoutIn, layoutOut) { + var array = layoutIn[containerArrayName]; + if(!Array.isArray(array)) return; + + var Cartesian = Registry.subplotsRegistry.cartesian; + var idRegex = Cartesian.idRegex; + var subplots = layoutOut._subplots; + var xaList = subplots.xaxis; + var yaList = subplots.yaxis; + var cartesianList = subplots.cartesian; + var hasCartesianOrGL2D = layoutOut._has('cartesian') || layoutOut._has('gl2d'); + + for(var i = 0; i < array.length; i++) { + var itemi = array[i]; + if(!Lib.isPlainObject(itemi)) continue; + + // call cleanId because if xref, or yref has something appended + // (e.g., ' domain') this will get removed. + var xref = axisIds.cleanId(itemi.xref, 'x', false); + var yref = axisIds.cleanId(itemi.yref, 'y', false); + + var hasXref = idRegex.x.test(xref); + var hasYref = idRegex.y.test(yref); + if(hasXref || hasYref) { + if(!hasCartesianOrGL2D) Lib.pushUnique(layoutOut._basePlotModules, Cartesian); + + var newAxis = false; + if(hasXref && xaList.indexOf(xref) === -1) { + xaList.push(xref); + newAxis = true; + } + if(hasYref && yaList.indexOf(yref) === -1) { + yaList.push(yref); + newAxis = true; + } + + /* + * Notice the logic here: only add a subplot for a component if + * it's referencing both x and y axes AND it's creating a new axis + * so for example if your plot already has xy and x2y2, an annotation + * on x2y or xy2 will not create a new subplot. + */ + if(newAxis && hasXref && hasYref) { + cartesianList.push(xref + yref); + } + } + } + }; +}; + +},{"../../lib":285,"../../registry":373,"./axis_ids":335}],345:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Plots = _dereq_('../plots'); +var Drawing = _dereq_('../../components/drawing'); + +var getModuleCalcData = _dereq_('../get_data').getModuleCalcData; +var axisIds = _dereq_('./axis_ids'); +var constants = _dereq_('./constants'); +var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces'); + +var ensureSingle = Lib.ensureSingle; + +function ensureSingleAndAddDatum(parent, nodeType, className) { + return Lib.ensureSingle(parent, nodeType, className, function(s) { + s.datum(className); + }); +} + +exports.name = 'cartesian'; + +exports.attr = ['xaxis', 'yaxis']; + +exports.idRoot = ['x', 'y']; + +exports.idRegex = constants.idRegex; + +exports.attrRegex = constants.attrRegex; + +exports.attributes = _dereq_('./attributes'); + +exports.layoutAttributes = _dereq_('./layout_attributes'); + +exports.supplyLayoutDefaults = _dereq_('./layout_defaults'); + +exports.transitionAxes = _dereq_('./transition_axes'); + +exports.finalizeSubplots = function(layoutIn, layoutOut) { + var subplots = layoutOut._subplots; + var xList = subplots.xaxis; + var yList = subplots.yaxis; + var spSVG = subplots.cartesian; + var spAll = spSVG.concat(subplots.gl2d || []); + var allX = {}; + var allY = {}; + var i, xi, yi; + + for(i = 0; i < spAll.length; i++) { + var parts = spAll[i].split('y'); + allX[parts[0]] = 1; + allY['y' + parts[1]] = 1; + } + + // check for x axes with no subplot, and make one from the anchor of that x axis + for(i = 0; i < xList.length; i++) { + xi = xList[i]; + if(!allX[xi]) { + yi = (layoutIn[axisIds.id2name(xi)] || {}).anchor; + if(!constants.idRegex.y.test(yi)) yi = 'y'; + spSVG.push(xi + yi); + spAll.push(xi + yi); + + if(!allY[yi]) { + allY[yi] = 1; + Lib.pushUnique(yList, yi); + } + } + } + + // same for y axes with no subplot + for(i = 0; i < yList.length; i++) { + yi = yList[i]; + if(!allY[yi]) { + xi = (layoutIn[axisIds.id2name(yi)] || {}).anchor; + if(!constants.idRegex.x.test(xi)) xi = 'x'; + spSVG.push(xi + yi); + spAll.push(xi + yi); + + if(!allX[xi]) { + allX[xi] = 1; + Lib.pushUnique(xList, xi); + } + } + } + + // finally, if we've gotten here we're supposed to show cartesian... + // so if there are NO subplots at all, make one from the first + // x & y axes in the input layout + if(!spAll.length) { + xi = ''; + yi = ''; + for(var ki in layoutIn) { + if(constants.attrRegex.test(ki)) { + var axLetter = ki.charAt(0); + if(axLetter === 'x') { + if(!xi || (+ki.substr(5) < +xi.substr(5))) { + xi = ki; + } + } else if(!yi || (+ki.substr(5) < +yi.substr(5))) { + yi = ki; + } + } + } + xi = xi ? axisIds.name2id(xi) : 'x'; + yi = yi ? axisIds.name2id(yi) : 'y'; + xList.push(xi); + yList.push(yi); + spSVG.push(xi + yi); + } +}; + +/** + * Cartesian.plot + * + * @param {DOM div | object} gd + * @param {array (optional)} traces + * array of traces indices to plot + * if undefined, plots all cartesian traces, + * @param {object} (optional) transitionOpts + * transition option object + * @param {function} (optional) makeOnCompleteCallback + * transition make callback function from Plots.transition + */ +exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { + var fullLayout = gd._fullLayout; + var subplots = fullLayout._subplots.cartesian; + var calcdata = gd.calcdata; + var i; + + if(!Array.isArray(traces)) { + // If traces is not provided, then it's a complete replot and missing + // traces are removed + traces = []; + for(i = 0; i < calcdata.length; i++) traces.push(i); + } + + for(i = 0; i < subplots.length; i++) { + var subplot = subplots[i]; + var subplotInfo = fullLayout._plots[subplot]; + + // Get all calcdata for this subplot: + var cdSubplot = []; + var pcd; + + for(var j = 0; j < calcdata.length; j++) { + var cd = calcdata[j]; + var trace = cd[0].trace; + + // Skip trace if whitelist provided and it's not whitelisted: + // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue; + if(trace.xaxis + trace.yaxis === subplot) { + // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet + // axis has actually changed: + // + // If this trace is specifically requested, add it to the list: + if(traces.indexOf(trace.index) !== -1 || trace.carpet) { + // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate + // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill + // is outdated. So this retroactively adds the previous trace if the + // traces are interdependent. + if( + pcd && + pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot && + ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 && + cdSubplot.indexOf(pcd) === -1 + ) { + cdSubplot.push(pcd); + } + + cdSubplot.push(cd); + } + + // Track the previous trace on this subplot for the retroactive-add step + // above: + pcd = cd; + } + } + + plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback); + } +}; + +function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) { + var traceLayerClasses = constants.traceLayerClasses; + var fullLayout = gd._fullLayout; + var modules = fullLayout._modules; + var _module, cdModuleAndOthers, cdModule; + + var layerData = []; + var zoomScaleQueryParts = []; + + for(var i = 0; i < modules.length; i++) { + _module = modules[i]; + var name = _module.name; + var categories = Registry.modules[name].categories; + + if(categories.svg) { + var className = (_module.layerName || name + 'layer'); + var plotMethod = _module.plot; + + // plot all visible traces of this type on this subplot at once + cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod); + cdModule = cdModuleAndOthers[0]; + // don't need to search the found traces again - in fact we need to NOT + // so that if two modules share the same plotter we don't double-plot + cdSubplot = cdModuleAndOthers[1]; + + if(cdModule.length) { + layerData.push({ + i: traceLayerClasses.indexOf(className), + className: className, + plotMethod: plotMethod, + cdModule: cdModule + }); + } + + if(categories.zoomScale) { + zoomScaleQueryParts.push('.' + className); + } + } + } + + layerData.sort(function(a, b) { return a.i - b.i; }); + + var layers = plotinfo.plot.selectAll('g.mlayer') + .data(layerData, function(d) { return d.className; }); + + layers.enter().append('g') + .attr('class', function(d) { return d.className; }) + .classed('mlayer', true) + .classed('rangeplot', plotinfo.isRangePlot); + + layers.exit().remove(); + + layers.order(); + + layers.each(function(d) { + var sel = d3.select(this); + var className = d.className; + + d.plotMethod( + gd, plotinfo, d.cdModule, sel, + transitionOpts, makeOnCompleteCallback + ); + + // layers that allow `cliponaxis: false` + if(constants.clipOnAxisFalseQuery.indexOf('.' + className) === -1) { + Drawing.setClipUrl(sel, plotinfo.layerClipId, gd); + } + }); + + // call Scattergl.plot separately + if(fullLayout._has('scattergl')) { + _module = Registry.getModule('scattergl'); + cdModule = getModuleCalcData(cdSubplot, _module)[0]; + _module.plot(gd, plotinfo, cdModule); + } + + // stash "hot" selections for faster interaction on drag and scroll + if(!gd._context.staticPlot) { + if(plotinfo._hasClipOnAxisFalse) { + plotinfo.clipOnAxisFalseTraces = plotinfo.plot + .selectAll(constants.clipOnAxisFalseQuery.join(',')) + .selectAll('.trace'); + } + + if(zoomScaleQueryParts.length) { + var traces = plotinfo.plot + .selectAll(zoomScaleQueryParts.join(',')) + .selectAll('.trace'); + + plotinfo.zoomScalePts = traces.selectAll('path.point'); + plotinfo.zoomScaleTxt = traces.selectAll('.textpoint'); + } + } +} + +exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + var oldPlots = oldFullLayout._plots || {}; + var newPlots = newFullLayout._plots || {}; + var oldSubplotList = oldFullLayout._subplots || {}; + var plotinfo; + var i, k; + + // when going from a large splom graph to something else, + // we need to clear so that the new cartesian subplot + // can have the correct layer ordering + if(oldFullLayout._hasOnlyLargeSploms && !newFullLayout._hasOnlyLargeSploms) { + for(k in oldPlots) { + plotinfo = oldPlots[k]; + if(plotinfo.plotgroup) plotinfo.plotgroup.remove(); + } + } + + var hadGl = (oldFullLayout._has && oldFullLayout._has('gl')); + var hasGl = (newFullLayout._has && newFullLayout._has('gl')); + + if(hadGl && !hasGl) { + for(k in oldPlots) { + plotinfo = oldPlots[k]; + if(plotinfo._scene) plotinfo._scene.destroy(); + } + } + + // delete any titles we don't need anymore + // check if axis list has changed, and if so clear old titles + if(oldSubplotList.xaxis && oldSubplotList.yaxis) { + var oldAxIDs = axisIds.listIds({_fullLayout: oldFullLayout}); + for(i = 0; i < oldAxIDs.length; i++) { + var oldAxId = oldAxIDs[i]; + if(!newFullLayout[axisIds.id2name(oldAxId)]) { + oldFullLayout._infolayer.selectAll('.g-' + oldAxId + 'title').remove(); + } + } + } + + var hadCartesian = (oldFullLayout._has && oldFullLayout._has('cartesian')); + var hasCartesian = (newFullLayout._has && newFullLayout._has('cartesian')); + + if(hadCartesian && !hasCartesian) { + // if we've gotten rid of all cartesian traces, remove all the subplot svg items + + purgeSubplotLayers(oldFullLayout._cartesianlayer.selectAll('.subplot'), oldFullLayout); + oldFullLayout._defs.selectAll('.axesclip').remove(); + delete oldFullLayout._axisConstraintGroups; + delete oldFullLayout._axisMatchGroups; + } else if(oldSubplotList.cartesian) { + // otherwise look for subplots we need to remove + + for(i = 0; i < oldSubplotList.cartesian.length; i++) { + var oldSubplotId = oldSubplotList.cartesian[i]; + if(!newPlots[oldSubplotId]) { + var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y'; + oldFullLayout._cartesianlayer.selectAll(selector).remove(); + removeSubplotExtras(oldSubplotId, oldFullLayout); + } + } + } +}; + +exports.drawFramework = function(gd) { + var fullLayout = gd._fullLayout; + var subplotData = makeSubplotData(gd); + + var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot') + .data(subplotData, String); + + subplotLayers.enter().append('g') + .attr('class', function(d) { return 'subplot ' + d[0]; }); + + subplotLayers.order(); + + subplotLayers.exit() + .call(purgeSubplotLayers, fullLayout); + + subplotLayers.each(function(d) { + var id = d[0]; + var plotinfo = fullLayout._plots[id]; + + plotinfo.plotgroup = d3.select(this); + makeSubplotLayer(gd, plotinfo); + + // make separate drag layers for each subplot, + // but append them to paper rather than the plot groups, + // so they end up on top of the rest + plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id); + }); +}; + +exports.rangePlot = function(gd, plotinfo, cdSubplot) { + makeSubplotLayer(gd, plotinfo); + plotOne(gd, plotinfo, cdSubplot); + Plots.style(gd); +}; + +function makeSubplotData(gd) { + var fullLayout = gd._fullLayout; + var ids = fullLayout._subplots.cartesian; + var len = ids.length; + var i, j, id, plotinfo, xa, ya; + + // split 'regular' and 'overlaying' subplots + var regulars = []; + var overlays = []; + + for(i = 0; i < len; i++) { + id = ids[i]; + plotinfo = fullLayout._plots[id]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + var xa2 = xa._mainAxis; + var ya2 = ya._mainAxis; + var mainplot = xa2._id + ya2._id; + var mainplotinfo = fullLayout._plots[mainplot]; + plotinfo.overlays = []; + + if(mainplot !== id && mainplotinfo) { + plotinfo.mainplot = mainplot; + plotinfo.mainplotinfo = mainplotinfo; + overlays.push(id); + } else { + plotinfo.mainplot = undefined; + plotinfo.mainplotinfo = undefined; + regulars.push(id); + } + } + + // fill in list of overlaying subplots in 'main plot' + for(i = 0; i < overlays.length; i++) { + id = overlays[i]; + plotinfo = fullLayout._plots[id]; + plotinfo.mainplotinfo.overlays.push(plotinfo); + } + + // put 'regular' subplot data before 'overlaying' + var subplotIds = regulars.concat(overlays); + var subplotData = new Array(len); + + for(i = 0; i < len; i++) { + id = subplotIds[i]; + plotinfo = fullLayout._plots[id]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + // use info about axis layer and overlaying pattern + // to clean what need to be cleaned up in exit selection + var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || '']; + for(j = 0; j < plotinfo.overlays.length; j++) { + d.push(plotinfo.overlays[j].id); + } + subplotData[i] = d; + } + + return subplotData; +} + +function makeSubplotLayer(gd, plotinfo) { + var plotgroup = plotinfo.plotgroup; + var id = plotinfo.id; + var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer]; + var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer]; + var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms; + + if(!plotinfo.mainplot) { + if(hasOnlyLargeSploms) { + // TODO could do even better + // - we don't need plot (but we would have to mock it in lsInner + // and other places + // - we don't (x|y)lines and (x|y)axislayer for most subplots + // usually just the bottom x and left y axes. + plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above'); + plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above'); + plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above'); + plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above'); + } else { + var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot'); + plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer'); + plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer'); + + plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer'); + plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer'); + + ensureSingle(plotgroup, 'path', 'xlines-below'); + ensureSingle(plotgroup, 'path', 'ylines-below'); + plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below'); + + ensureSingle(plotgroup, 'g', 'xaxislayer-below'); + ensureSingle(plotgroup, 'g', 'yaxislayer-below'); + plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below'); + + plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot'); + plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot'); + + plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above'); + plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above'); + plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above'); + + ensureSingle(plotgroup, 'g', 'xaxislayer-above'); + ensureSingle(plotgroup, 'g', 'yaxislayer-above'); + plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above'); + + // set refs to correct layers as determined by 'axis.layer' + plotinfo.xlines = plotgroup.select('.xlines-' + xLayer); + plotinfo.ylines = plotgroup.select('.ylines-' + yLayer); + plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer); + plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer); + } + } else { + var mainplotinfo = plotinfo.mainplotinfo; + var mainplotgroup = mainplotinfo.plotgroup; + var xId = id + '-x'; + var yId = id + '-y'; + + // now make the components of overlaid subplots + // overlays don't have backgrounds, and append all + // their other components to the corresponding + // extra groups of their main plots. + + plotinfo.gridlayer = mainplotinfo.gridlayer; + plotinfo.zerolinelayer = mainplotinfo.zerolinelayer; + + ensureSingle(mainplotinfo.overlinesBelow, 'path', xId); + ensureSingle(mainplotinfo.overlinesBelow, 'path', yId); + ensureSingle(mainplotinfo.overaxesBelow, 'g', xId); + ensureSingle(mainplotinfo.overaxesBelow, 'g', yId); + + plotinfo.plot = ensureSingle(mainplotinfo.overplot, 'g', id); + + ensureSingle(mainplotinfo.overlinesAbove, 'path', xId); + ensureSingle(mainplotinfo.overlinesAbove, 'path', yId); + ensureSingle(mainplotinfo.overaxesAbove, 'g', xId); + ensureSingle(mainplotinfo.overaxesAbove, 'g', yId); + + // set refs to correct layers as determined by 'abovetraces' + plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId); + plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId); + plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId); + plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId); + } + + // common attributes for all subplots, overlays or not + + if(!hasOnlyLargeSploms) { + ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id); + ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id); + plotinfo.gridlayer.selectAll('g') + .map(function(d) { return d[0]; }) + .sort(axisIds.idSort); + } + + plotinfo.xlines + .style('fill', 'none') + .classed('crisp', true); + + plotinfo.ylines + .style('fill', 'none') + .classed('crisp', true); +} + +function purgeSubplotLayers(layers, fullLayout) { + if(!layers) return; + + var overlayIdsToRemove = {}; + + layers.each(function(d) { + var id = d[0]; + var plotgroup = d3.select(this); + + plotgroup.remove(); + removeSubplotExtras(id, fullLayout); + overlayIdsToRemove[id] = true; + + // do not remove individual axis s here + // as other subplots may need them + }); + + // must remove overlaid subplot trace layers 'manually' + + for(var k in fullLayout._plots) { + var subplotInfo = fullLayout._plots[k]; + var overlays = subplotInfo.overlays || []; + + for(var j = 0; j < overlays.length; j++) { + var overlayInfo = overlays[j]; + + if(overlayIdsToRemove[overlayInfo.id]) { + overlayInfo.plot.selectAll('.trace').remove(); + } + } + } +} + +function removeSubplotExtras(subplotId, fullLayout) { + fullLayout._draggers.selectAll('g.' + subplotId).remove(); + fullLayout._defs.select('#clip' + fullLayout._uid + subplotId + 'plot').remove(); +} + +exports.toSVG = function(gd) { + var imageRoot = gd._fullLayout._glimages; + var root = d3.select(gd).selectAll('.svg-container'); + var canvases = root.filter(function(d, i) {return i === root.size() - 1;}) + .selectAll('.gl-canvas-context, .gl-canvas-focus'); + + function canvasToImage() { + var canvas = this; + var imageData = canvas.toDataURL('image/png'); + var image = imageRoot.append('svg:image'); + + image.attr({ + xmlns: xmlnsNamespaces.svg, + 'xlink:href': imageData, + preserveAspectRatio: 'none', + x: 0, + y: 0, + width: canvas.width, + height: canvas.height + }); + } + + canvases.each(canvasToImage); +}; + +exports.updateFx = _dereq_('./graph_interact').updateFx; + +},{"../../components/drawing":177,"../../constants/xmlns_namespaces":266,"../../lib":285,"../../registry":373,"../get_data":362,"../plots":366,"./attributes":329,"./axis_ids":335,"./constants":338,"./graph_interact":341,"./layout_attributes":346,"./layout_defaults":347,"./transition_axes":356,"@plotly/d3":20}],346:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('../font_attributes'); +var colorAttrs = _dereq_('../../components/color/attributes'); +var dash = _dereq_('../../components/drawing/attributes').dash; +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; +var descriptionWithDates = _dereq_('../../plots/cartesian/axis_format_attributes').descriptionWithDates; + +var ONEDAY = _dereq_('../../constants/numerical').ONEDAY; +var constants = _dereq_('./constants'); +var HOUR = constants.HOUR_PATTERN; +var DAY_OF_WEEK = constants.WEEKDAY_PATTERN; + +module.exports = { + visible: { + valType: 'boolean', + editType: 'plot', + }, + color: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'ticks', + }, + title: { + text: { + valType: 'string', + editType: 'ticks', + }, + font: fontAttrs({ + editType: 'ticks', + }), + standoff: { + valType: 'number', + min: 0, + editType: 'ticks', + }, + editType: 'ticks' + }, + type: { + valType: 'enumerated', + // '-' means we haven't yet run autotype or couldn't find any data + // it gets turned into linear in gd._fullLayout but not copied back + // to gd.data like the others are. + values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'], + dflt: '-', + editType: 'calc', + // we forget when an axis has been autotyped, just writing the auto + // value back to the input - so it doesn't make sense to template this. + // Note: we do NOT prohibit this in `coerce`, so if someone enters a + // type in the template explicitly it will be honored as the default. + _noTemplating: true, + }, + autotypenumbers: { + valType: 'enumerated', + values: ['convert types', 'strict'], + dflt: 'convert types', + editType: 'calc', + }, + autorange: { + valType: 'enumerated', + values: [true, false, 'reversed'], + dflt: true, + editType: 'axrange', + impliedEdits: {'range[0]': undefined, 'range[1]': undefined}, + }, + rangemode: { + valType: 'enumerated', + values: ['normal', 'tozero', 'nonnegative'], + dflt: 'normal', + editType: 'plot', + }, + range: { + valType: 'info_array', + items: [ + {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true}, + {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true} + ], + editType: 'axrange', + impliedEdits: {'autorange': false}, + anim: true, + }, + fixedrange: { + valType: 'boolean', + dflt: false, + editType: 'calc', + }, + // scaleanchor: not used directly, just put here for reference + // values are any opposite-letter axis id + scaleanchor: { + valType: 'enumerated', + values: [ + constants.idRegex.x.toString(), + constants.idRegex.y.toString() + ], + editType: 'plot', + }, + scaleratio: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'plot', + }, + constrain: { + valType: 'enumerated', + values: ['range', 'domain'], + editType: 'plot', + }, + // constraintoward: not used directly, just put here for reference + constraintoward: { + valType: 'enumerated', + values: ['left', 'center', 'right', 'top', 'middle', 'bottom'], + editType: 'plot', + }, + matches: { + valType: 'enumerated', + values: [ + constants.idRegex.x.toString(), + constants.idRegex.y.toString() + ], + editType: 'calc', + }, + + rangebreaks: templatedArray('rangebreak', { + enabled: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + + bounds: { + valType: 'info_array', + items: [ + {valType: 'any', editType: 'calc'}, + {valType: 'any', editType: 'calc'} + ], + editType: 'calc', + }, + + pattern: { + valType: 'enumerated', + values: [DAY_OF_WEEK, HOUR, ''], + editType: 'calc', + }, + + values: { + valType: 'info_array', + freeLength: true, + editType: 'calc', + items: { + valType: 'any', + editType: 'calc' + }, + }, + dvalue: { + // TODO could become 'any' to add support for 'months', 'years' + valType: 'number', + editType: 'calc', + min: 0, + dflt: ONEDAY, + }, + + /* + gap: { + valType: 'number', + min: 0, + dflt: 0, // for *date* axes, maybe something else for *linear* + editType: 'calc', + }, + gapmode: { + valType: 'enumerated', + values: ['pixels', 'fraction'], + dflt: 'pixels', + editType: 'calc', + }, + */ + + // To complete https://github.com/plotly/plotly.js/issues/4210 + // we additionally need `gap` and make this work on *linear*, and + // possibly all other cartesian axis types. We possibly would also need + // some style attributes controlling the zig-zag on the corresponding + // axis. + + editType: 'calc' + }), + + // ticks + tickmode: { + valType: 'enumerated', + values: ['auto', 'linear', 'array'], + editType: 'ticks', + impliedEdits: {tick0: undefined, dtick: undefined}, + }, + nticks: { + valType: 'integer', + min: 0, + dflt: 0, + editType: 'ticks', + }, + tick0: { + valType: 'any', + editType: 'ticks', + impliedEdits: {tickmode: 'linear'}, + }, + dtick: { + valType: 'any', + editType: 'ticks', + impliedEdits: {tickmode: 'linear'}, + }, + tickvals: { + valType: 'data_array', + editType: 'ticks', + }, + ticktext: { + valType: 'data_array', + editType: 'ticks', + }, + ticks: { + valType: 'enumerated', + values: ['outside', 'inside', ''], + editType: 'ticks', + }, + tickson: { + valType: 'enumerated', + values: ['labels', 'boundaries'], + dflt: 'labels', + editType: 'ticks', + }, + ticklabelmode: { + valType: 'enumerated', + values: ['instant', 'period'], + dflt: 'instant', + editType: 'ticks', + }, + // ticklabelposition: not used directly, as values depend on direction (similar to side) + // left/right options are for x axes, and top/bottom options are for y axes + ticklabelposition: { + valType: 'enumerated', + values: [ + 'outside', 'inside', + 'outside top', 'inside top', + 'outside left', 'inside left', + 'outside right', 'inside right', + 'outside bottom', 'inside bottom' + ], + dflt: 'outside', + editType: 'calc', + }, + ticklabeloverflow: { + valType: 'enumerated', + values: [ + 'allow', + 'hide past div', + 'hide past domain' + ], + editType: 'calc', + }, + mirror: { + valType: 'enumerated', + values: [true, 'ticks', false, 'all', 'allticks'], + dflt: false, + editType: 'ticks+layoutstyle', + }, + ticklen: { + valType: 'number', + min: 0, + dflt: 5, + editType: 'ticks', + }, + tickwidth: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'ticks', + }, + tickcolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'ticks', + }, + showticklabels: { + valType: 'boolean', + dflt: true, + editType: 'ticks', + }, + automargin: { + valType: 'boolean', + dflt: false, + editType: 'ticks', + }, + showspikes: { + valType: 'boolean', + dflt: false, + editType: 'modebar', + }, + spikecolor: { + valType: 'color', + dflt: null, + editType: 'none', + }, + spikethickness: { + valType: 'number', + dflt: 3, + editType: 'none', + }, + spikedash: extendFlat({}, dash, {dflt: 'dash', editType: 'none'}), + spikemode: { + valType: 'flaglist', + flags: ['toaxis', 'across', 'marker'], + dflt: 'toaxis', + editType: 'none', + }, + spikesnap: { + valType: 'enumerated', + values: ['data', 'cursor', 'hovered data'], + dflt: 'hovered data', + editType: 'none', + }, + tickfont: fontAttrs({ + editType: 'ticks', + }), + tickangle: { + valType: 'angle', + dflt: 'auto', + editType: 'ticks', + }, + tickprefix: { + valType: 'string', + dflt: '', + editType: 'ticks', + }, + showtickprefix: { + valType: 'enumerated', + values: ['all', 'first', 'last', 'none'], + dflt: 'all', + editType: 'ticks', + }, + ticksuffix: { + valType: 'string', + dflt: '', + editType: 'ticks', + }, + showticksuffix: { + valType: 'enumerated', + values: ['all', 'first', 'last', 'none'], + dflt: 'all', + editType: 'ticks', + }, + showexponent: { + valType: 'enumerated', + values: ['all', 'first', 'last', 'none'], + dflt: 'all', + editType: 'ticks', + }, + exponentformat: { + valType: 'enumerated', + values: ['none', 'e', 'E', 'power', 'SI', 'B'], + dflt: 'B', + editType: 'ticks', + }, + minexponent: { + valType: 'number', + dflt: 3, + min: 0, + editType: 'ticks', + }, + separatethousands: { + valType: 'boolean', + dflt: false, + editType: 'ticks', + }, + tickformat: { + valType: 'string', + dflt: '', + editType: 'ticks', + description: descriptionWithDates('tick label') + }, + tickformatstops: templatedArray('tickformatstop', { + enabled: { + valType: 'boolean', + dflt: true, + editType: 'ticks', + }, + dtickrange: { + valType: 'info_array', + items: [ + {valType: 'any', editType: 'ticks'}, + {valType: 'any', editType: 'ticks'} + ], + editType: 'ticks', + }, + value: { + valType: 'string', + dflt: '', + editType: 'ticks', + }, + editType: 'ticks' + }), + hoverformat: { + valType: 'string', + dflt: '', + editType: 'none', + description: descriptionWithDates('hover text') + }, + // lines and grids + showline: { + valType: 'boolean', + dflt: false, + editType: 'ticks+layoutstyle', + }, + linecolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'layoutstyle', + }, + linewidth: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'ticks+layoutstyle', + }, + showgrid: { + valType: 'boolean', + editType: 'ticks', + }, + gridcolor: { + valType: 'color', + dflt: colorAttrs.lightLine, + editType: 'ticks', + }, + gridwidth: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'ticks', + }, + zeroline: { + valType: 'boolean', + editType: 'ticks', + }, + zerolinecolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'ticks', + }, + zerolinewidth: { + valType: 'number', + dflt: 1, + editType: 'ticks', + }, + + showdividers: { + valType: 'boolean', + dflt: true, + editType: 'ticks', + }, + dividercolor: { + valType: 'color', + dflt: colorAttrs.defaultLine, + editType: 'ticks', + }, + dividerwidth: { + valType: 'number', + dflt: 1, + editType: 'ticks', + }, + // TODO dividerlen: that would override "to label base" length? + + // positioning attributes + // anchor: not used directly, just put here for reference + // values are any opposite-letter axis id + anchor: { + valType: 'enumerated', + values: [ + 'free', + constants.idRegex.x.toString(), + constants.idRegex.y.toString() + ], + editType: 'plot', + }, + // side: not used directly, as values depend on direction + // values are top, bottom for x axes, and left, right for y + side: { + valType: 'enumerated', + values: ['top', 'bottom', 'left', 'right'], + editType: 'plot', + }, + // overlaying: not used directly, just put here for reference + // values are false and any other same-letter axis id that's not + // itself overlaying anything + overlaying: { + valType: 'enumerated', + values: [ + 'free', + constants.idRegex.x.toString(), + constants.idRegex.y.toString() + ], + editType: 'plot', + }, + layer: { + valType: 'enumerated', + values: ['above traces', 'below traces'], + dflt: 'above traces', + editType: 'plot', + }, + domain: { + valType: 'info_array', + items: [ + {valType: 'number', min: 0, max: 1, editType: 'plot'}, + {valType: 'number', min: 0, max: 1, editType: 'plot'} + ], + dflt: [0, 1], + editType: 'plot', + }, + position: { + valType: 'number', + min: 0, + max: 1, + dflt: 0, + editType: 'plot', + }, + categoryorder: { + valType: 'enumerated', + values: [ + 'trace', 'category ascending', 'category descending', 'array', + 'total ascending', 'total descending', + 'min ascending', 'min descending', + 'max ascending', 'max descending', + 'sum ascending', 'sum descending', + 'mean ascending', 'mean descending', + 'median ascending', 'median descending' + ], + dflt: 'trace', + editType: 'calc', + }, + categoryarray: { + valType: 'data_array', + editType: 'calc', + }, + uirevision: { + valType: 'any', + editType: 'none', + }, + editType: 'calc', + + _deprecated: { + autotick: { + valType: 'boolean', + editType: 'ticks', + }, + title: { + valType: 'string', + editType: 'ticks', + }, + titlefont: fontAttrs({ + editType: 'ticks', + }) + } +}; + +},{"../../components/color/attributes":154,"../../components/drawing/attributes":176,"../../constants/numerical":265,"../../lib/extend":279,"../../plot_api/plot_template":320,"../../plots/cartesian/axis_format_attributes":334,"../font_attributes":360,"./constants":338}],347:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../../components/color'); +var isUnifiedHover = _dereq_('../../components/fx/helpers').isUnifiedHover; +var handleHoverModeDefaults = _dereq_('../../components/fx/hovermode_defaults'); +var Template = _dereq_('../../plot_api/plot_template'); +var basePlotLayoutAttributes = _dereq_('../layout_attributes'); + +var layoutAttributes = _dereq_('./layout_attributes'); +var handleTypeDefaults = _dereq_('./type_defaults'); +var handleAxisDefaults = _dereq_('./axis_defaults'); +var constraints = _dereq_('./constraints'); +var handlePositionDefaults = _dereq_('./position_defaults'); + +var axisIds = _dereq_('./axis_ids'); +var id2name = axisIds.id2name; +var name2id = axisIds.name2id; + +var AX_ID_PATTERN = _dereq_('./constants').AX_ID_PATTERN; + +var Registry = _dereq_('../../registry'); +var traceIs = Registry.traceIs; +var getComponentMethod = Registry.getComponentMethod; + +function appendList(cont, k, item) { + if(Array.isArray(cont[k])) cont[k].push(item); + else cont[k] = [item]; +} + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { + var autotypenumbersDflt = layoutOut.autotypenumbers; + + var ax2traces = {}; + var xaMayHide = {}; + var yaMayHide = {}; + var xaMustDisplay = {}; + var yaMustDisplay = {}; + var yaMustNotReverse = {}; + var yaMayReverse = {}; + var axHasImage = {}; + var outerTicks = {}; + var noGrids = {}; + var i, j; + + // look for axes in the data + for(i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + if(!traceIs(trace, 'cartesian') && !traceIs(trace, 'gl2d')) continue; + + var xaName; + if(trace.xaxis) { + xaName = id2name(trace.xaxis); + appendList(ax2traces, xaName, trace); + } else if(trace.xaxes) { + for(j = 0; j < trace.xaxes.length; j++) { + appendList(ax2traces, id2name(trace.xaxes[j]), trace); + } + } + + var yaName; + if(trace.yaxis) { + yaName = id2name(trace.yaxis); + appendList(ax2traces, yaName, trace); + } else if(trace.yaxes) { + for(j = 0; j < trace.yaxes.length; j++) { + appendList(ax2traces, id2name(trace.yaxes[j]), trace); + } + } + + // logic for funnels + if(trace.type === 'funnel') { + if(trace.orientation === 'h') { + if(xaName) xaMayHide[xaName] = true; + if(yaName) yaMayReverse[yaName] = true; + } else { + if(yaName) yaMayHide[yaName] = true; + } + } else if(trace.type === 'image') { + if(yaName) axHasImage[yaName] = true; + if(xaName) axHasImage[xaName] = true; + } else { + if(yaName) { + yaMustDisplay[yaName] = true; + yaMustNotReverse[yaName] = true; + } + + if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) { + if(xaName) xaMustDisplay[xaName] = true; + } + } + + // Two things trigger axis visibility: + // 1. is not carpet + // 2. carpet that's not cheater + + // The above check for definitely-not-cheater is not adequate. This + // second list tracks which axes *could* be a cheater so that the + // full condition triggering hiding is: + // *could* be a cheater and *is not definitely visible* + if(trace.type === 'carpet' && trace._cheater) { + if(xaName) xaMayHide[xaName] = true; + } + + // check for default formatting tweaks + if(traceIs(trace, '2dMap')) { + outerTicks[xaName] = true; + outerTicks[yaName] = true; + } + + if(traceIs(trace, 'oriented')) { + var positionAxis = trace.orientation === 'h' ? yaName : xaName; + noGrids[positionAxis] = true; + } + } + + var subplots = layoutOut._subplots; + var xIds = subplots.xaxis; + var yIds = subplots.yaxis; + var xNames = Lib.simpleMap(xIds, id2name); + var yNames = Lib.simpleMap(yIds, id2name); + var axNames = xNames.concat(yNames); + + // plot_bgcolor only makes sense if there's a (2D) plot! + // TODO: bgcolor for each subplot, to inherit from the main one + var plotBgColor = Color.background; + if(xIds.length && yIds.length) { + plotBgColor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor'); + } + + var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor); + + // name of single axis (e.g. 'xaxis', 'yaxis2') + var axName; + // id of single axis (e.g. 'y', 'x5') + var axId; + // 'x' or 'y' + var axLetter; + // input layout axis container + var axLayoutIn; + // full layout axis container + var axLayoutOut; + + function newAxLayoutOut() { + var traces = ax2traces[axName] || []; + axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; }); + axLayoutOut._annIndices = []; + axLayoutOut._shapeIndices = []; + axLayoutOut._imgIndices = []; + axLayoutOut._subplotsWith = []; + axLayoutOut._counterAxes = []; + axLayoutOut._name = axLayoutOut._attr = axName; + axLayoutOut._id = axId; + } + + function coerce(attr, dflt) { + return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt); + } + + function coerce2(attr, dflt) { + return Lib.coerce2(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt); + } + + function getCounterAxes(axLetter) { + return (axLetter === 'x') ? yIds : xIds; + } + + function getOverlayableAxes(axLetter, axName) { + var list = (axLetter === 'x') ? xNames : yNames; + var out = []; + + for(var j = 0; j < list.length; j++) { + var axName2 = list[j]; + + if(axName2 !== axName && !(layoutIn[axName2] || {}).overlaying) { + out.push(name2id(axName2)); + } + } + + return out; + } + + // list of available counter axis names + var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')}; + // list of all x AND y axis ids + var allAxisIds = counterAxes.x.concat(counterAxes.y); + // lookup and list of axis ids that axes in axNames have a reference to, + // even though they are missing from allAxisIds + var missingMatchedAxisIdsLookup = {}; + var missingMatchedAxisIds = []; + + // fill in 'missing' axis lookup when an axis is set to match an axis + // not part of the allAxisIds list, save axis type so that we can propagate + // it to the missing axes + function addMissingMatchedAxis() { + var matchesIn = axLayoutIn.matches; + if(AX_ID_PATTERN.test(matchesIn) && allAxisIds.indexOf(matchesIn) === -1) { + missingMatchedAxisIdsLookup[matchesIn] = axLayoutIn.type; + missingMatchedAxisIds = Object.keys(missingMatchedAxisIdsLookup); + } + } + + var hovermode = handleHoverModeDefaults(layoutIn, layoutOut); + var unifiedHover = isUnifiedHover(hovermode); + + // first pass creates the containers, determines types, and handles most of the settings + for(i = 0; i < axNames.length; i++) { + axName = axNames[i]; + axId = name2id(axName); + axLetter = axName.charAt(0); + + if(!Lib.isPlainObject(layoutIn[axName])) { + layoutIn[axName] = {}; + } + + axLayoutIn = layoutIn[axName]; + axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis'); + newAxLayoutOut(); + + var visibleDflt = + (axLetter === 'x' && !xaMustDisplay[axName] && xaMayHide[axName]) || + (axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]); + + var reverseDflt = + (axLetter === 'y' && + ( + (!yaMustNotReverse[axName] && yaMayReverse[axName]) || + axHasImage[axName] + )); + + var defaultOptions = { + letter: axLetter, + font: layoutOut.font, + outerTicks: outerTicks[axName], + showGrid: !noGrids[axName], + data: ax2traces[axName] || [], + bgColor: bgColor, + calendar: layoutOut.calendar, + automargin: true, + visibleDflt: visibleDflt, + reverseDflt: reverseDflt, + autotypenumbersDflt: autotypenumbersDflt, + splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId] + }; + + coerce('uirevision', layoutOut.uirevision); + + handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions); + handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut); + + var unifiedSpike = unifiedHover && axLetter === hovermode.charAt(0); + var spikecolor = coerce2('spikecolor', unifiedHover ? axLayoutOut.color : undefined); + var spikethickness = coerce2('spikethickness', unifiedHover ? 1.5 : undefined); + var spikedash = coerce2('spikedash', unifiedHover ? 'dot' : undefined); + var spikemode = coerce2('spikemode', unifiedHover ? 'across' : undefined); + var spikesnap = coerce2('spikesnap'); + var showSpikes = coerce('showspikes', !!unifiedSpike || !!spikecolor || !!spikethickness || !!spikedash || !!spikemode || !!spikesnap); + + if(!showSpikes) { + delete axLayoutOut.spikecolor; + delete axLayoutOut.spikethickness; + delete axLayoutOut.spikedash; + delete axLayoutOut.spikemode; + delete axLayoutOut.spikesnap; + } + + handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, { + letter: axLetter, + counterAxes: counterAxes[axLetter], + overlayableAxes: getOverlayableAxes(axLetter, axName), + grid: layoutOut.grid + }); + + coerce('title.standoff'); + + addMissingMatchedAxis(); + + axLayoutOut._input = axLayoutIn; + } + + // coerce the 'missing' axes + i = 0; + while(i < missingMatchedAxisIds.length) { + axId = missingMatchedAxisIds[i++]; + axName = id2name(axId); + axLetter = axName.charAt(0); + + if(!Lib.isPlainObject(layoutIn[axName])) { + layoutIn[axName] = {}; + } + + axLayoutIn = layoutIn[axName]; + axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis'); + newAxLayoutOut(); + + var defaultOptions2 = { + letter: axLetter, + font: layoutOut.font, + outerTicks: outerTicks[axName], + showGrid: !noGrids[axName], + data: [], + bgColor: bgColor, + calendar: layoutOut.calendar, + automargin: true, + visibleDflt: false, + reverseDflt: false, + autotypenumbersDflt: autotypenumbersDflt, + splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId] + }; + + coerce('uirevision', layoutOut.uirevision); + + axLayoutOut.type = missingMatchedAxisIdsLookup[axId] || 'linear'; + + handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions2, layoutOut); + + handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, { + letter: axLetter, + counterAxes: counterAxes[axLetter], + overlayableAxes: getOverlayableAxes(axLetter, axName), + grid: layoutOut.grid + }); + + coerce('fixedrange'); + + addMissingMatchedAxis(); + + axLayoutOut._input = axLayoutIn; + } + + // quick second pass for range slider and selector defaults + var rangeSliderDefaults = getComponentMethod('rangeslider', 'handleDefaults'); + var rangeSelectorDefaults = getComponentMethod('rangeselector', 'handleDefaults'); + + for(i = 0; i < xNames.length; i++) { + axName = xNames[i]; + axLayoutIn = layoutIn[axName]; + axLayoutOut = layoutOut[axName]; + + rangeSliderDefaults(layoutIn, layoutOut, axName); + + if(axLayoutOut.type === 'date') { + rangeSelectorDefaults( + axLayoutIn, + axLayoutOut, + layoutOut, + yNames, + axLayoutOut.calendar + ); + } + + coerce('fixedrange'); + } + + for(i = 0; i < yNames.length; i++) { + axName = yNames[i]; + axLayoutIn = layoutIn[axName]; + axLayoutOut = layoutOut[axName]; + + var anchoredAxis = layoutOut[id2name(axLayoutOut.anchor)]; + + var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis); + + coerce('fixedrange', fixedRangeDflt); + } + + // Finally, handle scale constraints and matching axes. + // + // We need to do this after all axes have coerced both `type` + // (so we link only axes of the same type) and + // `fixedrange` (so we can avoid linking from OR TO a fixed axis). + constraints.handleDefaults(layoutIn, layoutOut, { + axIds: allAxisIds.concat(missingMatchedAxisIds).sort(axisIds.idSort), + axHasImage: axHasImage + }); +}; + +},{"../../components/color":155,"../../components/fx/helpers":191,"../../components/fx/hovermode_defaults":194,"../../lib":285,"../../plot_api/plot_template":320,"../../registry":373,"../layout_attributes":364,"./axis_defaults":333,"./axis_ids":335,"./constants":338,"./constraints":339,"./layout_attributes":346,"./position_defaults":349,"./type_defaults":357}],348:[function(_dereq_,module,exports){ +'use strict'; + +var colorMix = _dereq_('tinycolor2').mix; +var lightFraction = _dereq_('../../components/color/attributes').lightFraction; +var Lib = _dereq_('../../lib'); + +/** + * @param {object} opts : + * - dfltColor {string} : default axis color + * - bgColor {string} : combined subplot bg color + * - blend {number, optional} : blend percentage (to compute dflt grid color) + * - showLine {boolean} : show line by default + * - showGrid {boolean} : show grid by default + * - noZeroLine {boolean} : don't coerce zeroline* attributes + * - attributes {object} : attribute object associated with input containers + */ +module.exports = function handleLineGridDefaults(containerIn, containerOut, coerce, opts) { + opts = opts || {}; + + var dfltColor = opts.dfltColor; + + function coerce2(attr, dflt) { + return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt); + } + + var lineColor = coerce2('linecolor', dfltColor); + var lineWidth = coerce2('linewidth'); + var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth); + + if(!showLine) { + delete containerOut.linecolor; + delete containerOut.linewidth; + } + + var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString(); + var gridColor = coerce2('gridcolor', gridColorDflt); + var gridWidth = coerce2('gridwidth'); + var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth); + + if(!showGridLines) { + delete containerOut.gridcolor; + delete containerOut.gridwidth; + } + + if(!opts.noZeroLine) { + var zeroLineColor = coerce2('zerolinecolor', dfltColor); + var zeroLineWidth = coerce2('zerolinewidth'); + var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth); + + if(!showZeroLine) { + delete containerOut.zerolinecolor; + delete containerOut.zerolinewidth; + } + } +}; + +},{"../../components/color/attributes":154,"../../lib":285,"tinycolor2":119}],349:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); + + +module.exports = function handlePositionDefaults(containerIn, containerOut, coerce, options) { + var counterAxes = options.counterAxes || []; + var overlayableAxes = options.overlayableAxes || []; + var letter = options.letter; + var grid = options.grid; + + var dfltAnchor, dfltDomain, dfltSide, dfltPosition; + + if(grid) { + dfltDomain = grid._domains[letter][grid._axisMap[containerOut._id]]; + dfltAnchor = grid._anchors[containerOut._id]; + if(dfltDomain) { + dfltSide = grid[letter + 'side'].split(' ')[0]; + dfltPosition = grid.domain[letter][dfltSide === 'right' || dfltSide === 'top' ? 1 : 0]; + } + } + + // Even if there's a grid, this axis may not be in it - fall back on non-grid defaults + dfltDomain = dfltDomain || [0, 1]; + dfltAnchor = dfltAnchor || (isNumeric(containerIn.position) ? 'free' : (counterAxes[0] || 'free')); + dfltSide = dfltSide || (letter === 'x' ? 'bottom' : 'left'); + dfltPosition = dfltPosition || 0; + + var anchor = Lib.coerce(containerIn, containerOut, { + anchor: { + valType: 'enumerated', + values: ['free'].concat(counterAxes), + dflt: dfltAnchor + } + }, 'anchor'); + + if(anchor === 'free') coerce('position', dfltPosition); + + Lib.coerce(containerIn, containerOut, { + side: { + valType: 'enumerated', + values: letter === 'x' ? ['bottom', 'top'] : ['left', 'right'], + dflt: dfltSide + } + }, 'side'); + + var overlaying = false; + if(overlayableAxes.length) { + overlaying = Lib.coerce(containerIn, containerOut, { + overlaying: { + valType: 'enumerated', + values: [false].concat(overlayableAxes), + dflt: false + } + }, 'overlaying'); + } + + if(!overlaying) { + // TODO: right now I'm copying this domain over to overlaying axes + // in ax.setscale()... but this means we still need (imperfect) logic + // in the axes popover to hide domain for the overlaying axis. + // perhaps I should make a private version _domain that all axes get??? + var domain = coerce('domain', dfltDomain); + + // according to https://www.npmjs.com/package/canvas-size + // the minimum value of max canvas width across browsers and devices is 4096 + // which applied in the calculation below: + if(domain[0] > domain[1] - 1 / 4096) containerOut.domain = dfltDomain; + Lib.noneOrAll(containerIn.domain, containerOut.domain, dfltDomain); + } + + coerce('layer'); + + return containerOut; +}; + +},{"../../lib":285,"fast-isnumeric":31}],350:[function(_dereq_,module,exports){ +'use strict'; + +var FROM_BL = _dereq_('../../constants/alignment').FROM_BL; + +module.exports = function scaleZoom(ax, factor, centerFraction) { + if(centerFraction === undefined) { + centerFraction = FROM_BL[ax.constraintoward || 'center']; + } + + var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])]; + var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction; + + ax.range = ax._input.range = [ + ax.l2r(center + (rangeLinear[0] - center) * factor), + ax.l2r(center + (rangeLinear[1] - center) * factor) + ]; + ax.setScale(); +}; + +},{"../../constants/alignment":260}],351:[function(_dereq_,module,exports){ +'use strict'; + +var polybool = _dereq_('polybooljs'); + +var Registry = _dereq_('../../registry'); +var dashStyle = _dereq_('../../components/drawing').dashStyle; +var Color = _dereq_('../../components/color'); +var Fx = _dereq_('../../components/fx'); +var makeEventData = _dereq_('../../components/fx/helpers').makeEventData; +var dragHelpers = _dereq_('../../components/dragelement/helpers'); +var freeMode = dragHelpers.freeMode; +var rectMode = dragHelpers.rectMode; +var drawMode = dragHelpers.drawMode; +var openMode = dragHelpers.openMode; +var selectMode = dragHelpers.selectMode; + +var displayOutlines = _dereq_('../../components/shapes/draw_newshape/display_outlines'); +var handleEllipse = _dereq_('../../components/shapes/draw_newshape/helpers').handleEllipse; +var newShapes = _dereq_('../../components/shapes/draw_newshape/newshapes'); + +var Lib = _dereq_('../../lib'); +var polygon = _dereq_('../../lib/polygon'); +var throttle = _dereq_('../../lib/throttle'); +var getFromId = _dereq_('./axis_ids').getFromId; +var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases'); + +var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces; + +var constants = _dereq_('./constants'); +var MINSELECT = constants.MINSELECT; + +var filteredPolygon = polygon.filter; +var polygonTester = polygon.tester; + +var clearSelect = _dereq_('./handle_outline').clearSelect; + +var helpers = _dereq_('./helpers'); +var p2r = helpers.p2r; +var axValue = helpers.axValue; +var getTransform = helpers.getTransform; + +function prepSelect(e, startX, startY, dragOptions, mode) { + var isFreeMode = freeMode(mode); + var isRectMode = rectMode(mode); + var isOpenMode = openMode(mode); + var isDrawMode = drawMode(mode); + var isSelectMode = selectMode(mode); + + var isLine = mode === 'drawline'; + var isEllipse = mode === 'drawcircle'; + var isLineOrEllipse = isLine || isEllipse; // cases with two start & end positions + + var gd = dragOptions.gd; + var fullLayout = gd._fullLayout; + var zoomLayer = fullLayout._zoomlayer; + var dragBBox = dragOptions.element.getBoundingClientRect(); + var plotinfo = dragOptions.plotinfo; + var transform = getTransform(plotinfo); + var x0 = startX - dragBBox.left; + var y0 = startY - dragBBox.top; + + fullLayout._calcInverseTransform(gd); + var transformedCoords = Lib.apply3DTransform(fullLayout._invTransform)(x0, y0); + x0 = transformedCoords[0]; + y0 = transformedCoords[1]; + var scaleX = fullLayout._invScaleX; + var scaleY = fullLayout._invScaleY; + + var x1 = x0; + var y1 = y0; + var path0 = 'M' + x0 + ',' + y0; + var pw = dragOptions.xaxes[0]._length; + var ph = dragOptions.yaxes[0]._length; + var allAxes = dragOptions.xaxes.concat(dragOptions.yaxes); + var subtract = e.altKey && + !(drawMode(mode) && isOpenMode); + + var filterPoly, selectionTester, mergedPolygons, currentPolygon; + var i, searchInfo, eventData; + + coerceSelectionsCache(e, gd, dragOptions); + + if(isFreeMode) { + filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX); + } + + var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data(isDrawMode ? [0] : [1, 2]); + var drwStyle = fullLayout.newshape; + + outlines.enter() + .append('path') + .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; }) + .style(isDrawMode ? { + opacity: drwStyle.opacity / 2, + fill: isOpenMode ? undefined : drwStyle.fillcolor, + stroke: drwStyle.line.color, + 'stroke-dasharray': dashStyle(drwStyle.line.dash, drwStyle.line.width), + 'stroke-width': drwStyle.line.width + 'px' + } : {}) + .attr('fill-rule', drwStyle.fillrule) + .classed('cursor-move', isDrawMode ? true : false) + .attr('transform', transform) + .attr('d', path0 + 'Z'); + + var corners = zoomLayer.append('path') + .attr('class', 'zoombox-corners') + .style({ + fill: Color.background, + stroke: Color.defaultLine, + 'stroke-width': 1 + }) + .attr('transform', transform) + .attr('d', 'M0,0Z'); + + + var throttleID = fullLayout._uid + constants.SELECTID; + var selection = []; + + // find the traces to search for selection points + var searchTraces = determineSearchTraces(gd, dragOptions.xaxes, + dragOptions.yaxes, dragOptions.subplot); + + function ascending(a, b) { return a - b; } + + // allow subplots to override fillRangeItems routine + var fillRangeItems; + + if(plotinfo.fillRangeItems) { + fillRangeItems = plotinfo.fillRangeItems; + } else { + if(isRectMode) { + fillRangeItems = function(eventData, poly) { + var ranges = eventData.range = {}; + + for(i = 0; i < allAxes.length; i++) { + var ax = allAxes[i]; + var axLetter = ax._id.charAt(0); + + ranges[ax._id] = [ + p2r(ax, poly[axLetter + 'min']), + p2r(ax, poly[axLetter + 'max']) + ].sort(ascending); + } + }; + } else { // case of isFreeMode + fillRangeItems = function(eventData, poly, filterPoly) { + var dataPts = eventData.lassoPoints = {}; + + for(i = 0; i < allAxes.length; i++) { + var ax = allAxes[i]; + dataPts[ax._id] = filterPoly.filtered.map(axValue(ax)); + } + }; + } + } + + dragOptions.moveFn = function(dx0, dy0) { + x1 = Math.max(0, Math.min(pw, scaleX * dx0 + x0)); + y1 = Math.max(0, Math.min(ph, scaleY * dy0 + y0)); + + var dx = Math.abs(x1 - x0); + var dy = Math.abs(y1 - y0); + + if(isRectMode) { + var direction; + var start, end; + + if(isSelectMode) { + var q = fullLayout.selectdirection; + + if(q === 'any') { + if(dy < Math.min(dx * 0.6, MINSELECT)) { + direction = 'h'; + } else if(dx < Math.min(dy * 0.6, MINSELECT)) { + direction = 'v'; + } else { + direction = 'd'; + } + } else { + direction = q; + } + + switch(direction) { + case 'h': + start = isEllipse ? ph / 2 : 0; + end = ph; + break; + case 'v': + start = isEllipse ? pw / 2 : 0; + end = pw; + break; + } + } + + if(isDrawMode) { + switch(fullLayout.newshape.drawdirection) { + case 'vertical': + direction = 'h'; + start = isEllipse ? ph / 2 : 0; + end = ph; + break; + case 'horizontal': + direction = 'v'; + start = isEllipse ? pw / 2 : 0; + end = pw; + break; + case 'ortho': + if(dx < dy) { + direction = 'h'; + start = y0; + end = y1; + } else { + direction = 'v'; + start = x0; + end = x1; + } + break; + default: // i.e. case of 'diagonal' + direction = 'd'; + } + } + + if(direction === 'h') { + // horizontal motion + currentPolygon = isLineOrEllipse ? + handleEllipse(isEllipse, [x1, start], [x1, end]) : // using x1 instead of x0 allows adjusting the line while drawing + [[x0, start], [x0, end], [x1, end], [x1, start]]; // make a vertical box + + currentPolygon.xmin = isLineOrEllipse ? x1 : Math.min(x0, x1); + currentPolygon.xmax = isLineOrEllipse ? x1 : Math.max(x0, x1); + currentPolygon.ymin = Math.min(start, end); + currentPolygon.ymax = Math.max(start, end); + // extras to guide users in keeping a straight selection + corners.attr('d', 'M' + currentPolygon.xmin + ',' + (y0 - MINSELECT) + + 'h-4v' + (2 * MINSELECT) + 'h4Z' + + 'M' + (currentPolygon.xmax - 1) + ',' + (y0 - MINSELECT) + + 'h4v' + (2 * MINSELECT) + 'h-4Z'); + } else if(direction === 'v') { + // vertical motion + currentPolygon = isLineOrEllipse ? + handleEllipse(isEllipse, [start, y1], [end, y1]) : // using y1 instead of y0 allows adjusting the line while drawing + [[start, y0], [start, y1], [end, y1], [end, y0]]; // make a horizontal box + + currentPolygon.xmin = Math.min(start, end); + currentPolygon.xmax = Math.max(start, end); + currentPolygon.ymin = isLineOrEllipse ? y1 : Math.min(y0, y1); + currentPolygon.ymax = isLineOrEllipse ? y1 : Math.max(y0, y1); + corners.attr('d', 'M' + (x0 - MINSELECT) + ',' + currentPolygon.ymin + + 'v-4h' + (2 * MINSELECT) + 'v4Z' + + 'M' + (x0 - MINSELECT) + ',' + (currentPolygon.ymax - 1) + + 'v4h' + (2 * MINSELECT) + 'v-4Z'); + } else if(direction === 'd') { + // diagonal motion + currentPolygon = isLineOrEllipse ? + handleEllipse(isEllipse, [x0, y0], [x1, y1]) : + [[x0, y0], [x0, y1], [x1, y1], [x1, y0]]; + + currentPolygon.xmin = Math.min(x0, x1); + currentPolygon.xmax = Math.max(x0, x1); + currentPolygon.ymin = Math.min(y0, y1); + currentPolygon.ymax = Math.max(y0, y1); + corners.attr('d', 'M0,0Z'); + } + } else if(isFreeMode) { + filterPoly.addPt([x1, y1]); + currentPolygon = filterPoly.filtered; + } + + // create outline & tester + if(dragOptions.selectionDefs && dragOptions.selectionDefs.length) { + mergedPolygons = mergePolygons(dragOptions.mergedPolygons, currentPolygon, subtract); + currentPolygon.subtract = subtract; + selectionTester = multiTester(dragOptions.selectionDefs.concat([currentPolygon])); + } else { + mergedPolygons = [currentPolygon]; + selectionTester = polygonTester(currentPolygon); + } + + // display polygons on the screen + displayOutlines(convertPoly(mergedPolygons, isOpenMode), outlines, dragOptions); + + if(isSelectMode) { + throttle.throttle( + throttleID, + constants.SELECTDELAY, + function() { + selection = []; + + var thisSelection; + var traceSelections = []; + var traceSelection; + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + + traceSelection = searchInfo._module.selectPoints(searchInfo, selectionTester); + traceSelections.push(traceSelection); + + thisSelection = fillSelectionItem(traceSelection, searchInfo); + + if(selection.length) { + for(var j = 0; j < thisSelection.length; j++) { + selection.push(thisSelection[j]); + } + } else selection = thisSelection; + } + + eventData = {points: selection}; + updateSelectedState(gd, searchTraces, eventData); + fillRangeItems(eventData, currentPolygon, filterPoly); + dragOptions.gd.emit('plotly_selecting', eventData); + } + ); + } + }; + + dragOptions.clickFn = function(numClicks, evt) { + corners.remove(); + + if(gd._fullLayout._activeShapeIndex >= 0) { + gd._fullLayout._deactivateShape(gd); + return; + } + if(isDrawMode) return; + + var clickmode = fullLayout.clickmode; + + throttle.done(throttleID).then(function() { + throttle.clear(throttleID); + if(numClicks === 2) { + // clear selection on doubleclick + outlines.remove(); + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + searchInfo._module.selectPoints(searchInfo, false); + } + + updateSelectedState(gd, searchTraces); + + clearSelectionsCache(dragOptions); + + gd.emit('plotly_deselect', null); + } else { + if(clickmode.indexOf('select') > -1) { + selectOnClick(evt, gd, dragOptions.xaxes, dragOptions.yaxes, + dragOptions.subplot, dragOptions, outlines); + } + + if(clickmode === 'event') { + // TODO: remove in v3 - this was probably never intended to work as it does, + // but in case anyone depends on it we don't want to break it now. + // Note that click-to-select introduced pre v3 also emitts proper + // event data when clickmode is having 'select' in its flag list. + gd.emit('plotly_selected', undefined); + } + } + + Fx.click(gd, evt); + }).catch(Lib.error); + }; + + dragOptions.doneFn = function() { + corners.remove(); + + throttle.done(throttleID).then(function() { + throttle.clear(throttleID); + dragOptions.gd.emit('plotly_selected', eventData); + + if(currentPolygon && dragOptions.selectionDefs) { + // save last polygons + currentPolygon.subtract = subtract; + dragOptions.selectionDefs.push(currentPolygon); + + // we have to keep reference to arrays container + dragOptions.mergedPolygons.length = 0; + [].push.apply(dragOptions.mergedPolygons, mergedPolygons); + } + + if(dragOptions.doneFnCompleted) { + dragOptions.doneFnCompleted(selection); + } + }).catch(Lib.error); + + if(isDrawMode) { + clearSelectionsCache(dragOptions); + } + }; +} + +function selectOnClick(evt, gd, xAxes, yAxes, subplot, dragOptions, polygonOutlines) { + var hoverData = gd._hoverdata; + var fullLayout = gd._fullLayout; + var clickmode = fullLayout.clickmode; + var sendEvents = clickmode.indexOf('event') > -1; + var selection = []; + var searchTraces, searchInfo, currentSelectionDef, selectionTester, traceSelection; + var thisTracesSelection, pointOrBinSelected, subtract, eventData, i; + + if(isHoverDataSet(hoverData)) { + coerceSelectionsCache(evt, gd, dragOptions); + searchTraces = determineSearchTraces(gd, xAxes, yAxes, subplot); + var clickedPtInfo = extractClickedPtInfo(hoverData, searchTraces); + var isBinnedTrace = clickedPtInfo.pointNumbers.length > 0; + + + // Note: potentially costly operation isPointOrBinSelected is + // called as late as possible through the use of an assignment + // in an if condition. + if(isBinnedTrace ? + isOnlyThisBinSelected(searchTraces, clickedPtInfo) : + isOnlyOnePointSelected(searchTraces) && + (pointOrBinSelected = isPointOrBinSelected(clickedPtInfo))) { + if(polygonOutlines) polygonOutlines.remove(); + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + searchInfo._module.selectPoints(searchInfo, false); + } + + updateSelectedState(gd, searchTraces); + + clearSelectionsCache(dragOptions); + + if(sendEvents) { + gd.emit('plotly_deselect', null); + } + } else { + subtract = evt.shiftKey && + (pointOrBinSelected !== undefined ? + pointOrBinSelected : + isPointOrBinSelected(clickedPtInfo)); + currentSelectionDef = newPointSelectionDef(clickedPtInfo.pointNumber, clickedPtInfo.searchInfo, subtract); + + var allSelectionDefs = dragOptions.selectionDefs.concat([currentSelectionDef]); + selectionTester = multiTester(allSelectionDefs); + + for(i = 0; i < searchTraces.length; i++) { + traceSelection = searchTraces[i]._module.selectPoints(searchTraces[i], selectionTester); + thisTracesSelection = fillSelectionItem(traceSelection, searchTraces[i]); + + if(selection.length) { + for(var j = 0; j < thisTracesSelection.length; j++) { + selection.push(thisTracesSelection[j]); + } + } else selection = thisTracesSelection; + } + + eventData = {points: selection}; + updateSelectedState(gd, searchTraces, eventData); + + if(currentSelectionDef && dragOptions) { + dragOptions.selectionDefs.push(currentSelectionDef); + } + + if(polygonOutlines) { + var polygons = dragOptions.mergedPolygons; + var isOpenMode = openMode(dragOptions.dragmode); + + // display polygons on the screen + displayOutlines(convertPoly(polygons, isOpenMode), polygonOutlines, dragOptions); + } + + if(sendEvents) { + gd.emit('plotly_selected', eventData); + } + } + } +} + +/** + * Constructs a new point selection definition object. + */ +function newPointSelectionDef(pointNumber, searchInfo, subtract) { + return { + pointNumber: pointNumber, + searchInfo: searchInfo, + subtract: subtract + }; +} + +function isPointSelectionDef(o) { + return 'pointNumber' in o && 'searchInfo' in o; +} + +/* + * Constructs a new point number tester. + */ +function newPointNumTester(pointSelectionDef) { + return { + xmin: 0, + xmax: 0, + ymin: 0, + ymax: 0, + pts: [], + contains: function(pt, omitFirstEdge, pointNumber, searchInfo) { + var idxWantedTrace = pointSelectionDef.searchInfo.cd[0].trace._expandedIndex; + var idxActualTrace = searchInfo.cd[0].trace._expandedIndex; + return idxActualTrace === idxWantedTrace && + pointNumber === pointSelectionDef.pointNumber; + }, + isRect: false, + degenerate: false, + subtract: pointSelectionDef.subtract + }; +} + +/** + * Wraps multiple selection testers. + * + * @param {Array} list - An array of selection testers. + * + * @return a selection tester object with a contains function + * that can be called to evaluate a point against all wrapped + * selection testers that were passed in list. + */ +function multiTester(list) { + var testers = []; + var xmin = isPointSelectionDef(list[0]) ? 0 : list[0][0][0]; + var xmax = xmin; + var ymin = isPointSelectionDef(list[0]) ? 0 : list[0][0][1]; + var ymax = ymin; + + for(var i = 0; i < list.length; i++) { + if(isPointSelectionDef(list[i])) { + testers.push(newPointNumTester(list[i])); + } else { + var tester = polygon.tester(list[i]); + tester.subtract = list[i].subtract; + testers.push(tester); + xmin = Math.min(xmin, tester.xmin); + xmax = Math.max(xmax, tester.xmax); + ymin = Math.min(ymin, tester.ymin); + ymax = Math.max(ymax, tester.ymax); + } + } + + /** + * Tests if the given point is within this tester. + * + * @param {Array} pt - [0] is the x coordinate, [1] is the y coordinate of the point. + * @param {*} arg - An optional parameter to pass down to wrapped testers. + * @param {number} pointNumber - The point number of the point within the underlying data array. + * @param {number} searchInfo - An object identifying the trace the point is contained in. + * + * @return {boolean} true if point is considered to be selected, false otherwise. + */ + function contains(pt, arg, pointNumber, searchInfo) { + var contained = false; + for(var i = 0; i < testers.length; i++) { + if(testers[i].contains(pt, arg, pointNumber, searchInfo)) { + // if contained by subtract tester - exclude the point + contained = testers[i].subtract === false; + } + } + + return contained; + } + + return { + xmin: xmin, + xmax: xmax, + ymin: ymin, + ymax: ymax, + pts: [], + contains: contains, + isRect: false, + degenerate: false + }; +} + +function coerceSelectionsCache(evt, gd, dragOptions) { + gd._fullLayout._drawing = false; + + var fullLayout = gd._fullLayout; + var plotinfo = dragOptions.plotinfo; + var dragmode = dragOptions.dragmode; + + var selectingOnSameSubplot = ( + fullLayout._lastSelectedSubplot && + fullLayout._lastSelectedSubplot === plotinfo.id + ); + + var hasModifierKey = (evt.shiftKey || evt.altKey) && + !(drawMode(dragmode) && openMode(dragmode)); + + if(selectingOnSameSubplot && hasModifierKey && + (plotinfo.selection && plotinfo.selection.selectionDefs) && !dragOptions.selectionDefs) { + // take over selection definitions from prev mode, if any + dragOptions.selectionDefs = plotinfo.selection.selectionDefs; + dragOptions.mergedPolygons = plotinfo.selection.mergedPolygons; + } else if(!hasModifierKey || !plotinfo.selection) { + clearSelectionsCache(dragOptions); + } + + // clear selection outline when selecting a different subplot + if(!selectingOnSameSubplot) { + clearSelect(gd); + fullLayout._lastSelectedSubplot = plotinfo.id; + } +} + +function clearSelectionsCache(dragOptions) { + var dragmode = dragOptions.dragmode; + var plotinfo = dragOptions.plotinfo; + + var gd = dragOptions.gd; + if(gd._fullLayout._activeShapeIndex >= 0) { + gd._fullLayout._deactivateShape(gd); + } + + if(drawMode(dragmode)) { + var fullLayout = gd._fullLayout; + var zoomLayer = fullLayout._zoomlayer; + + var outlines = zoomLayer.selectAll('.select-outline-' + plotinfo.id); + if(outlines && gd._fullLayout._drawing) { + // add shape + var shapes = newShapes(outlines, dragOptions); + if(shapes) { + Registry.call('_guiRelayout', gd, { + shapes: shapes + }); + } + + gd._fullLayout._drawing = false; + } + } + + plotinfo.selection = {}; + plotinfo.selection.selectionDefs = dragOptions.selectionDefs = []; + plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = []; +} + +function determineSearchTraces(gd, xAxes, yAxes, subplot) { + var searchTraces = []; + var xAxisIds = xAxes.map(function(ax) { return ax._id; }); + var yAxisIds = yAxes.map(function(ax) { return ax._id; }); + var cd, trace, i; + + for(i = 0; i < gd.calcdata.length; i++) { + cd = gd.calcdata[i]; + trace = cd[0].trace; + + if(trace.visible !== true || !trace._module || !trace._module.selectPoints) continue; + + if(subplot && (trace.subplot === subplot || trace.geo === subplot)) { + searchTraces.push(createSearchInfo(trace._module, cd, xAxes[0], yAxes[0])); + } else if( + trace.type === 'splom' && + // FIXME: make sure we don't have more than single axis for splom + trace._xaxes[xAxisIds[0]] && trace._yaxes[yAxisIds[0]] + ) { + var info = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]); + info.scene = gd._fullLayout._splomScenes[trace.uid]; + searchTraces.push(info); + } else if( + trace.type === 'sankey' + ) { + var sankeyInfo = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]); + searchTraces.push(sankeyInfo); + } else { + if(xAxisIds.indexOf(trace.xaxis) === -1) continue; + if(yAxisIds.indexOf(trace.yaxis) === -1) continue; + + searchTraces.push(createSearchInfo(trace._module, cd, + getFromId(gd, trace.xaxis), getFromId(gd, trace.yaxis))); + } + } + + return searchTraces; + + function createSearchInfo(module, calcData, xaxis, yaxis) { + return { + _module: module, + cd: calcData, + xaxis: xaxis, + yaxis: yaxis + }; + } +} + +function isHoverDataSet(hoverData) { + return hoverData && + Array.isArray(hoverData) && + hoverData[0].hoverOnBox !== true; +} + +function extractClickedPtInfo(hoverData, searchTraces) { + var hoverDatum = hoverData[0]; + var pointNumber = -1; + var pointNumbers = []; + var searchInfo, i; + + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + if(hoverDatum.fullData._expandedIndex === searchInfo.cd[0].trace._expandedIndex) { + // Special case for box (and violin) + if(hoverDatum.hoverOnBox === true) { + break; + } + + // Hint: in some traces like histogram, one graphical element + // doesn't correspond to one particular data point, but to + // bins of data points. Thus, hoverDatum can have a binNumber + // property instead of pointNumber. + if(hoverDatum.pointNumber !== undefined) { + pointNumber = hoverDatum.pointNumber; + } else if(hoverDatum.binNumber !== undefined) { + pointNumber = hoverDatum.binNumber; + pointNumbers = hoverDatum.pointNumbers; + } + + break; + } + } + + return { + pointNumber: pointNumber, + pointNumbers: pointNumbers, + searchInfo: searchInfo + }; +} + +function isPointOrBinSelected(clickedPtInfo) { + var trace = clickedPtInfo.searchInfo.cd[0].trace; + var ptNum = clickedPtInfo.pointNumber; + var ptNums = clickedPtInfo.pointNumbers; + var ptNumsSet = ptNums.length > 0; + + // When pointsNumbers is set (e.g. histogram's binning), + // it is assumed that when the first point of + // a bin is selected, all others are as well + var ptNumToTest = ptNumsSet ? ptNums[0] : ptNum; + + // TODO potential performance improvement + // Primarily we need this function to determine if a click adds + // or subtracts from a selection. + // In cases `trace.selectedpoints` is a huge array, indexOf + // might be slow. One remedy would be to introduce a hash somewhere. + return trace.selectedpoints ? trace.selectedpoints.indexOf(ptNumToTest) > -1 : false; +} + +function isOnlyThisBinSelected(searchTraces, clickedPtInfo) { + var tracesWithSelectedPts = []; + var searchInfo, trace, isSameTrace, i; + + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + if(searchInfo.cd[0].trace.selectedpoints && searchInfo.cd[0].trace.selectedpoints.length > 0) { + tracesWithSelectedPts.push(searchInfo); + } + } + + if(tracesWithSelectedPts.length === 1) { + isSameTrace = tracesWithSelectedPts[0] === clickedPtInfo.searchInfo; + if(isSameTrace) { + trace = clickedPtInfo.searchInfo.cd[0].trace; + if(trace.selectedpoints.length === clickedPtInfo.pointNumbers.length) { + for(i = 0; i < clickedPtInfo.pointNumbers.length; i++) { + if(trace.selectedpoints.indexOf(clickedPtInfo.pointNumbers[i]) < 0) { + return false; + } + } + return true; + } + } + } + + return false; +} + +function isOnlyOnePointSelected(searchTraces) { + var len = 0; + var searchInfo, trace, i; + + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + trace = searchInfo.cd[0].trace; + if(trace.selectedpoints) { + if(trace.selectedpoints.length > 1) return false; + + len += trace.selectedpoints.length; + if(len > 1) return false; + } + } + + return len === 1; +} + +function updateSelectedState(gd, searchTraces, eventData) { + var i, searchInfo, cd, trace; + + // before anything else, update preGUI if necessary + for(i = 0; i < searchTraces.length; i++) { + var fullInputTrace = searchTraces[i].cd[0].trace._fullInput; + var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid] || {}; + if(tracePreGUI.selectedpoints === undefined) { + tracePreGUI.selectedpoints = fullInputTrace._input.selectedpoints || null; + } + } + + if(eventData) { + var pts = eventData.points || []; + + for(i = 0; i < searchTraces.length; i++) { + trace = searchTraces[i].cd[0].trace; + trace._input.selectedpoints = trace._fullInput.selectedpoints = []; + if(trace._fullInput !== trace) trace.selectedpoints = []; + } + + for(i = 0; i < pts.length; i++) { + var pt = pts[i]; + var data = pt.data; + var fullData = pt.fullData; + + if(pt.pointIndices) { + [].push.apply(data.selectedpoints, pt.pointIndices); + if(trace._fullInput !== trace) { + [].push.apply(fullData.selectedpoints, pt.pointIndices); + } + } else { + data.selectedpoints.push(pt.pointIndex); + if(trace._fullInput !== trace) { + fullData.selectedpoints.push(pt.pointIndex); + } + } + } + } else { + for(i = 0; i < searchTraces.length; i++) { + trace = searchTraces[i].cd[0].trace; + delete trace.selectedpoints; + delete trace._input.selectedpoints; + if(trace._fullInput !== trace) { + delete trace._fullInput.selectedpoints; + } + } + } + + var hasRegl = false; + + for(i = 0; i < searchTraces.length; i++) { + searchInfo = searchTraces[i]; + cd = searchInfo.cd; + trace = cd[0].trace; + + if(Registry.traceIs(trace, 'regl')) { + hasRegl = true; + } + + var _module = searchInfo._module; + var fn = _module.styleOnSelect || _module.style; + if(fn) { + fn(gd, cd, cd[0].node3); + if(cd[0].nodeRangePlot3) fn(gd, cd, cd[0].nodeRangePlot3); + } + } + + if(hasRegl) { + clearGlCanvases(gd); + redrawReglTraces(gd); + } +} + +function mergePolygons(list, poly, subtract) { + var res; + + if(subtract) { + res = polybool.difference({ + regions: list, + inverted: false + }, { + regions: [poly], + inverted: false + }); + + return res.regions; + } + + res = polybool.union({ + regions: list, + inverted: false + }, { + regions: [poly], + inverted: false + }); + + return res.regions; +} + +function fillSelectionItem(selection, searchInfo) { + if(Array.isArray(selection)) { + var cd = searchInfo.cd; + var trace = searchInfo.cd[0].trace; + + for(var i = 0; i < selection.length; i++) { + selection[i] = makeEventData(selection[i], trace, cd); + } + } + + return selection; +} + +function convertPoly(polygonsIn, isOpenMode) { // add M and L command to draft positions + var polygonsOut = []; + for(var i = 0; i < polygonsIn.length; i++) { + polygonsOut[i] = []; + for(var j = 0; j < polygonsIn[i].length; j++) { + polygonsOut[i][j] = []; + polygonsOut[i][j][0] = j ? 'L' : 'M'; + for(var k = 0; k < polygonsIn[i][j].length; k++) { + polygonsOut[i][j].push( + polygonsIn[i][j][k] + ); + } + } + + if(!isOpenMode) { + polygonsOut[i].push([ + 'Z', + polygonsOut[i][0][1], // initial x + polygonsOut[i][0][2] // initial y + ]); + } + } + + return polygonsOut; +} + +module.exports = { + prepSelect: prepSelect, + clearSelect: clearSelect, + clearSelectionsCache: clearSelectionsCache, + selectOnClick: selectOnClick +}; + +},{"../../components/color":155,"../../components/dragelement/helpers":173,"../../components/drawing":177,"../../components/fx":195,"../../components/fx/helpers":191,"../../components/shapes/draw_newshape/display_outlines":243,"../../components/shapes/draw_newshape/helpers":244,"../../components/shapes/draw_newshape/newshapes":245,"../../lib":285,"../../lib/clear_gl_canvases":273,"../../lib/polygon":297,"../../lib/throttle":308,"../../plot_api/subroutines":321,"../../registry":373,"./axis_ids":335,"./constants":338,"./handle_outline":342,"./helpers":343,"polybooljs":73}],352:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var utcFormat = _dereq_('d3-time-format').utcFormat; +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var cleanNumber = Lib.cleanNumber; +var ms2DateTime = Lib.ms2DateTime; +var dateTime2ms = Lib.dateTime2ms; +var ensureNumber = Lib.ensureNumber; +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; + +var numConstants = _dereq_('../../constants/numerical'); +var FP_SAFE = numConstants.FP_SAFE; +var BADNUM = numConstants.BADNUM; +var LOG_CLIP = numConstants.LOG_CLIP; +var ONEWEEK = numConstants.ONEWEEK; +var ONEDAY = numConstants.ONEDAY; +var ONEHOUR = numConstants.ONEHOUR; +var ONEMIN = numConstants.ONEMIN; +var ONESEC = numConstants.ONESEC; + +var axisIds = _dereq_('./axis_ids'); +var constants = _dereq_('./constants'); +var HOUR_PATTERN = constants.HOUR_PATTERN; +var WEEKDAY_PATTERN = constants.WEEKDAY_PATTERN; + +function fromLog(v) { + return Math.pow(10, v); +} + +function isValidCategory(v) { + return v !== null && v !== undefined; +} + +/** + * Define the conversion functions for an axis data is used in 5 ways: + * + * d: data, in whatever form it's provided + * c: calcdata: turned into numbers, but not linearized + * l: linearized - same as c except for log axes (and other nonlinear + * mappings later?) this is used when we need to know if it's + * *possible* to show some data on this axis, without caring about + * the current range + * p: pixel value - mapped to the screen with current size and zoom + * r: ranges, tick0, and annotation positions match one of the above + * but are handled differently for different types: + * - linear and date: data format (d) + * - category: calcdata format (c), and will stay that way because + * the data format has no continuous mapping + * - log: linearized (l) format + * TODO: in v3.0 we plan to change it to data format. At that point + * shapes will work the same way as ranges, tick0, and annotations + * so they can use this conversion too. + * + * Creates/updates these conversion functions, and a few more utilities + * like cleanRange, and makeCalcdata + * + * also clears the autotick constraints ._minDtick, ._forceTick0 + */ +module.exports = function setConvert(ax, fullLayout) { + fullLayout = fullLayout || {}; + + var axId = (ax._id || 'x'); + var axLetter = axId.charAt(0); + + function toLog(v, clip) { + if(v > 0) return Math.log(v) / Math.LN10; + + else if(v <= 0 && clip && ax.range && ax.range.length === 2) { + // clip NaN (ie past negative infinity) to LOG_CLIP axis + // length past the negative edge + var r0 = ax.range[0]; + var r1 = ax.range[1]; + return 0.5 * (r0 + r1 - 2 * LOG_CLIP * Math.abs(r0 - r1)); + } else return BADNUM; + } + + /* + * wrapped dateTime2ms that: + * - accepts ms numbers for backward compatibility + * - inserts a dummy arg so calendar is the 3rd arg (see notes below). + * - defaults to ax.calendar + */ + function dt2ms(v, _, calendar, opts) { + if((opts || {}).msUTC && isNumeric(v)) { + // For now it is only used + // to fix bar length in milliseconds & gl3d ticks + // It could be applied in other places in v3 + return +v; + } + + // NOTE: Changed this behavior: previously we took any numeric value + // to be a ms, even if it was a string that could be a bare year. + // Now we convert it as a date if at all possible, and only try + // as (local) ms if that fails. + var ms = dateTime2ms(v, calendar || ax.calendar); + if(ms === BADNUM) { + if(isNumeric(v)) { + v = +v; + // keep track of tenths of ms, that `new Date` will drop + // same logic as in Lib.ms2DateTime + var msecTenths = Math.floor(Lib.mod(v + 0.05, 1) * 10); + var msRounded = Math.round(v - msecTenths / 10); + ms = dateTime2ms(new Date(msRounded)) + msecTenths / 10; + } else return BADNUM; + } + return ms; + } + + // wrapped ms2DateTime to insert default ax.calendar + function ms2dt(v, r, calendar) { + return ms2DateTime(v, r, calendar || ax.calendar); + } + + function getCategoryName(v) { + return ax._categories[Math.round(v)]; + } + + /* + * setCategoryIndex: return the index of category v, + * inserting it in the list if it's not already there + * + * this will enter the categories in the order it + * encounters them, ie all the categories from the + * first data set, then all the ones from the second + * that aren't in the first etc. + * + * it is assumed that this function is being invoked in the + * already sorted category order; otherwise there would be + * a disconnect between the array and the index returned + */ + function setCategoryIndex(v) { + if(isValidCategory(v)) { + if(ax._categoriesMap === undefined) { + ax._categoriesMap = {}; + } + + if(ax._categoriesMap[v] !== undefined) { + return ax._categoriesMap[v]; + } else { + ax._categories.push(typeof v === 'number' ? String(v) : v); + + var curLength = ax._categories.length - 1; + ax._categoriesMap[v] = curLength; + + return curLength; + } + } + return BADNUM; + } + + function setMultiCategoryIndex(arrayIn, len) { + var arrayOut = new Array(len); + + for(var i = 0; i < len; i++) { + var v0 = (arrayIn[0] || [])[i]; + var v1 = (arrayIn[1] || [])[i]; + arrayOut[i] = getCategoryIndex([v0, v1]); + } + + return arrayOut; + } + + function getCategoryIndex(v) { + if(ax._categoriesMap) { + return ax._categoriesMap[v]; + } + } + + function getCategoryPosition(v) { + // d2l/d2c variant that that won't add categories but will also + // allow numbers to be mapped to the linearized axis positions + var index = getCategoryIndex(v); + if(index !== undefined) return index; + if(isNumeric(v)) return +v; + } + + function getRangePosition(v) { + return isNumeric(v) ? +v : getCategoryIndex(v); + } + + // include 2 fractional digits on pixel, for PDF zooming etc + function _l2p(v, m, b) { return d3.round(b + m * v, 2); } + + function _p2l(px, m, b) { return (px - b) / m; } + + var l2p = function l2p(v) { + if(!isNumeric(v)) return BADNUM; + return _l2p(v, ax._m, ax._b); + }; + + var p2l = function(px) { + return _p2l(px, ax._m, ax._b); + }; + + if(ax.rangebreaks) { + var isY = axLetter === 'y'; + + l2p = function(v) { + if(!isNumeric(v)) return BADNUM; + var len = ax._rangebreaks.length; + if(!len) return _l2p(v, ax._m, ax._b); + + var flip = isY; + if(ax.range[0] > ax.range[1]) flip = !flip; + var signAx = flip ? -1 : 1; + var pos = signAx * v; + + var q = 0; + for(var i = 0; i < len; i++) { + var min = signAx * ax._rangebreaks[i].min; + var max = signAx * ax._rangebreaks[i].max; + + if(pos < min) break; + if(pos > max) q = i + 1; + else { + // when falls into break, pick 'closest' offset + q = pos < (min + max) / 2 ? i : i + 1; + break; + } + } + var b2 = ax._B[q] || 0; + if(!isFinite(b2)) return 0; // avoid NaN translate e.g. in positionLabels if one keep zooming exactly into a break + return _l2p(v, ax._m2, b2); + }; + + p2l = function(px) { + var len = ax._rangebreaks.length; + if(!len) return _p2l(px, ax._m, ax._b); + + var q = 0; + for(var i = 0; i < len; i++) { + if(px < ax._rangebreaks[i].pmin) break; + if(px > ax._rangebreaks[i].pmax) q = i + 1; + } + return _p2l(px, ax._m2, ax._B[q]); + }; + } + + // conversions among c/l/p are fairly simple - do them together for all axis types + ax.c2l = (ax.type === 'log') ? toLog : ensureNumber; + ax.l2c = (ax.type === 'log') ? fromLog : ensureNumber; + + ax.l2p = l2p; + ax.p2l = p2l; + + ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p; + ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l; + + /* + * now type-specific conversions for **ALL** other combinations + * they're all written out, instead of being combinations of each other, for + * both clarity and speed. + */ + if(['linear', '-'].indexOf(ax.type) !== -1) { + // all are data vals, but d and r need cleaning + ax.d2r = ax.r2d = ax.d2c = ax.r2c = ax.d2l = ax.r2l = cleanNumber; + ax.c2d = ax.c2r = ax.l2d = ax.l2r = ensureNumber; + + ax.d2p = ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); }; + ax.p2d = ax.p2r = p2l; + + ax.cleanPos = ensureNumber; + } else if(ax.type === 'log') { + // d and c are data vals, r and l are logged (but d and r need cleaning) + ax.d2r = ax.d2l = function(v, clip) { return toLog(cleanNumber(v), clip); }; + ax.r2d = ax.r2c = function(v) { return fromLog(cleanNumber(v)); }; + + ax.d2c = ax.r2l = cleanNumber; + ax.c2d = ax.l2r = ensureNumber; + + ax.c2r = toLog; + ax.l2d = fromLog; + + ax.d2p = function(v, clip) { return ax.l2p(ax.d2r(v, clip)); }; + ax.p2d = function(px) { return fromLog(p2l(px)); }; + + ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); }; + ax.p2r = p2l; + + ax.cleanPos = ensureNumber; + } else if(ax.type === 'date') { + // r and d are date strings, l and c are ms + + /* + * Any of these functions with r and d on either side, calendar is the + * **3rd** argument. log has reserved the second argument. + * + * Unless you need the special behavior of the second arg (ms2DateTime + * uses this to limit precision, toLog uses true to clip negatives + * to offscreen low rather than undefined), it's safe to pass 0. + */ + ax.d2r = ax.r2d = Lib.identity; + + ax.d2c = ax.r2c = ax.d2l = ax.r2l = dt2ms; + ax.c2d = ax.c2r = ax.l2d = ax.l2r = ms2dt; + + ax.d2p = ax.r2p = function(v, _, calendar) { return ax.l2p(dt2ms(v, 0, calendar)); }; + ax.p2d = ax.p2r = function(px, r, calendar) { return ms2dt(p2l(px), r, calendar); }; + + ax.cleanPos = function(v) { return Lib.cleanDate(v, BADNUM, ax.calendar); }; + } else if(ax.type === 'category') { + // d is categories (string) + // c and l are indices (numbers) + // r is categories or numbers + + ax.d2c = ax.d2l = setCategoryIndex; + ax.r2d = ax.c2d = ax.l2d = getCategoryName; + + ax.d2r = ax.d2l_noadd = getCategoryPosition; + + ax.r2c = function(v) { + var index = getRangePosition(v); + return index !== undefined ? index : ax.fraction2r(0.5); + }; + + ax.l2r = ax.c2r = ensureNumber; + ax.r2l = getRangePosition; + + ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); }; + ax.p2d = function(px) { return getCategoryName(p2l(px)); }; + ax.r2p = ax.d2p; + ax.p2r = p2l; + + ax.cleanPos = function(v) { + if(typeof v === 'string' && v !== '') return v; + return ensureNumber(v); + }; + } else if(ax.type === 'multicategory') { + // N.B. multicategory axes don't define d2c and d2l, + // as 'data-to-calcdata' conversion needs to take into + // account all data array items as in ax.makeCalcdata. + + ax.r2d = ax.c2d = ax.l2d = getCategoryName; + ax.d2r = ax.d2l_noadd = getCategoryPosition; + + ax.r2c = function(v) { + var index = getCategoryPosition(v); + return index !== undefined ? index : ax.fraction2r(0.5); + }; + + ax.r2c_just_indices = getCategoryIndex; + + ax.l2r = ax.c2r = ensureNumber; + ax.r2l = getCategoryPosition; + + ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); }; + ax.p2d = function(px) { return getCategoryName(p2l(px)); }; + ax.r2p = ax.d2p; + ax.p2r = p2l; + + ax.cleanPos = function(v) { + if(Array.isArray(v) || (typeof v === 'string' && v !== '')) return v; + return ensureNumber(v); + }; + + ax.setupMultiCategory = function(fullData) { + var traceIndices = ax._traceIndices; + var i, j; + + var group = ax._matchGroup; + if(group && ax._categories.length === 0) { + for(var axId2 in group) { + if(axId2 !== axId) { + var ax2 = fullLayout[axisIds.id2name(axId2)]; + traceIndices = traceIndices.concat(ax2._traceIndices); + } + } + } + + // [ [cnt, {$cat: index}], for 1,2 ] + var seen = [[0, {}], [0, {}]]; + // [ [arrayIn[0][i], arrayIn[1][i]], for i .. N ] + var list = []; + + for(i = 0; i < traceIndices.length; i++) { + var trace = fullData[traceIndices[i]]; + + if(axLetter in trace) { + var arrayIn = trace[axLetter]; + var len = trace._length || Lib.minRowLength(arrayIn); + + if(isArrayOrTypedArray(arrayIn[0]) && isArrayOrTypedArray(arrayIn[1])) { + for(j = 0; j < len; j++) { + var v0 = arrayIn[0][j]; + var v1 = arrayIn[1][j]; + + if(isValidCategory(v0) && isValidCategory(v1)) { + list.push([v0, v1]); + + if(!(v0 in seen[0][1])) { + seen[0][1][v0] = seen[0][0]++; + } + if(!(v1 in seen[1][1])) { + seen[1][1][v1] = seen[1][0]++; + } + } + } + } + } + } + + list.sort(function(a, b) { + var ind0 = seen[0][1]; + var d = ind0[a[0]] - ind0[b[0]]; + if(d) return d; + + var ind1 = seen[1][1]; + return ind1[a[1]] - ind1[b[1]]; + }); + + for(i = 0; i < list.length; i++) { + setCategoryIndex(list[i]); + } + }; + } + + // find the range value at the specified (linear) fraction of the axis + ax.fraction2r = function(v) { + var rl0 = ax.r2l(ax.range[0]); + var rl1 = ax.r2l(ax.range[1]); + return ax.l2r(rl0 + v * (rl1 - rl0)); + }; + + // find the fraction of the range at the specified range value + ax.r2fraction = function(v) { + var rl0 = ax.r2l(ax.range[0]); + var rl1 = ax.r2l(ax.range[1]); + return (ax.r2l(v) - rl0) / (rl1 - rl0); + }; + + /* + * cleanRange: make sure range is a couplet of valid & distinct values + * keep numbers away from the limits of floating point numbers, + * and dates away from the ends of our date system (+/- 9999 years) + * + * optional param rangeAttr: operate on a different attribute, like + * ax._r, rather than ax.range + */ + ax.cleanRange = function(rangeAttr, opts) { + if(!opts) opts = {}; + if(!rangeAttr) rangeAttr = 'range'; + + var range = Lib.nestedProperty(ax, rangeAttr).get(); + var i, dflt; + + if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar); + else if(axLetter === 'y') dflt = constants.DFLTRANGEY; + else dflt = opts.dfltRange || constants.DFLTRANGEX; + + // make sure we don't later mutate the defaults + dflt = dflt.slice(); + + if(ax.rangemode === 'tozero' || ax.rangemode === 'nonnegative') { + dflt[0] = 0; + } + + if(!range || range.length !== 2) { + Lib.nestedProperty(ax, rangeAttr).set(dflt); + return; + } + + if(ax.type === 'date' && !ax.autorange) { + // check if milliseconds or js date objects are provided for range + // and convert to date strings + range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar); + range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar); + } + + for(i = 0; i < 2; i++) { + if(ax.type === 'date') { + if(!Lib.isDateTime(range[i], ax.calendar)) { + ax[rangeAttr] = dflt; + break; + } + + if(ax.r2l(range[0]) === ax.r2l(range[1])) { + // split by +/- 1 second + var linCenter = Lib.constrain(ax.r2l(range[0]), + Lib.MIN_MS + 1000, Lib.MAX_MS - 1000); + range[0] = ax.l2r(linCenter - 1000); + range[1] = ax.l2r(linCenter + 1000); + break; + } + } else { + if(!isNumeric(range[i])) { + if(isNumeric(range[1 - i])) { + range[i] = range[1 - i] * (i ? 10 : 0.1); + } else { + ax[rangeAttr] = dflt; + break; + } + } + + if(range[i] < -FP_SAFE) range[i] = -FP_SAFE; + else if(range[i] > FP_SAFE) range[i] = FP_SAFE; + + if(range[0] === range[1]) { + // somewhat arbitrary: split by 1 or 1ppm, whichever is bigger + var inc = Math.max(1, Math.abs(range[0] * 1e-6)); + range[0] -= inc; + range[1] += inc; + } + } + } + }; + + // set scaling to pixels + ax.setScale = function(usePrivateRange) { + var gs = fullLayout._size; + + // make sure we have a domain (pull it in from the axis + // this one is overlaying if necessary) + if(ax.overlaying) { + var ax2 = axisIds.getFromId({ _fullLayout: fullLayout }, ax.overlaying); + ax.domain = ax2.domain; + } + + // While transitions are occurring, we get a double-transform + // issue if we transform the drawn layer *and* use the new axis range to + // draw the data. This allows us to construct setConvert using the pre- + // interaction values of the range: + var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range'; + var calendar = ax.calendar; + ax.cleanRange(rangeAttr); + + var rl0 = ax.r2l(ax[rangeAttr][0], calendar); + var rl1 = ax.r2l(ax[rangeAttr][1], calendar); + + var isY = axLetter === 'y'; + if(isY) { + ax._offset = gs.t + (1 - ax.domain[1]) * gs.h; + ax._length = gs.h * (ax.domain[1] - ax.domain[0]); + ax._m = ax._length / (rl0 - rl1); + ax._b = -ax._m * rl1; + } else { + ax._offset = gs.l + ax.domain[0] * gs.w; + ax._length = gs.w * (ax.domain[1] - ax.domain[0]); + ax._m = ax._length / (rl1 - rl0); + ax._b = -ax._m * rl0; + } + + // set of "N" disjoint rangebreaks inside the range + ax._rangebreaks = []; + // length of these rangebreaks in value space - negative on reversed axes + ax._lBreaks = 0; + // l2p slope (same for all intervals) + ax._m2 = 0; + // set of l2p offsets (one for each of the (N+1) piecewise intervals) + ax._B = []; + + if(ax.rangebreaks) { + var i, brk; + + ax._rangebreaks = ax.locateBreaks( + Math.min(rl0, rl1), + Math.max(rl0, rl1) + ); + + if(ax._rangebreaks.length) { + for(i = 0; i < ax._rangebreaks.length; i++) { + brk = ax._rangebreaks[i]; + ax._lBreaks += Math.abs(brk.max - brk.min); + } + + var flip = isY; + if(rl0 > rl1) flip = !flip; + if(flip) ax._rangebreaks.reverse(); + var sign = flip ? -1 : 1; + + ax._m2 = sign * ax._length / (Math.abs(rl1 - rl0) - ax._lBreaks); + ax._B.push(-ax._m2 * (isY ? rl1 : rl0)); + for(i = 0; i < ax._rangebreaks.length; i++) { + brk = ax._rangebreaks[i]; + ax._B.push( + ax._B[ax._B.length - 1] - + sign * ax._m2 * (brk.max - brk.min) + ); + } + + // fill pixel (i.e. 'p') min/max here, + // to not have to loop through the _rangebreaks twice during `p2l` + for(i = 0; i < ax._rangebreaks.length; i++) { + brk = ax._rangebreaks[i]; + brk.pmin = l2p(brk.min); + brk.pmax = l2p(brk.max); + } + } + } + + if(!isFinite(ax._m) || !isFinite(ax._b) || ax._length < 0) { + fullLayout._replotting = false; + throw new Error('Something went wrong with axis scaling'); + } + }; + + ax.maskBreaks = function(v) { + var rangebreaksIn = ax.rangebreaks || []; + var bnds, b0, b1, vb, vDate; + + + if(!rangebreaksIn._cachedPatterns) { + rangebreaksIn._cachedPatterns = rangebreaksIn.map(function(brk) { + return brk.enabled && brk.bounds ? Lib.simpleMap(brk.bounds, brk.pattern ? + cleanNumber : + ax.d2c // case of pattern: '' + ) : null; + }); + } + if(!rangebreaksIn._cachedValues) { + rangebreaksIn._cachedValues = rangebreaksIn.map(function(brk) { + return brk.enabled && brk.values ? Lib.simpleMap(brk.values, ax.d2c).sort(Lib.sorterAsc) : null; + }); + } + + + for(var i = 0; i < rangebreaksIn.length; i++) { + var brk = rangebreaksIn[i]; + + if(brk.enabled) { + if(brk.bounds) { + var pattern = brk.pattern; + bnds = rangebreaksIn._cachedPatterns[i]; + b0 = bnds[0]; + b1 = bnds[1]; + + switch(pattern) { + case WEEKDAY_PATTERN: + vDate = new Date(v); + vb = vDate.getUTCDay(); + + if(b0 > b1) { + b1 += 7; + if(vb < b0) vb += 7; + } + + break; + case HOUR_PATTERN: + vDate = new Date(v); + var hours = vDate.getUTCHours(); + var minutes = vDate.getUTCMinutes(); + var seconds = vDate.getUTCSeconds(); + var milliseconds = vDate.getUTCMilliseconds(); + + vb = hours + ( + minutes / 60 + + seconds / 3600 + + milliseconds / 3600000 + ); + + if(b0 > b1) { + b1 += 24; + if(vb < b0) vb += 24; + } + + break; + case '': + // N.B. should work on date axes as well! + // e.g. { bounds: ['2020-01-04', '2020-01-05 23:59'] } + // TODO should work with reversed-range axes + vb = v; + break; + } + + if(vb >= b0 && vb < b1) return BADNUM; + } else { + var vals = rangebreaksIn._cachedValues[i]; + for(var j = 0; j < vals.length; j++) { + b0 = vals[j]; + b1 = b0 + brk.dvalue; + if(v >= b0 && v < b1) return BADNUM; + } + } + } + } + return v; + }; + + ax.locateBreaks = function(r0, r1) { + var i, bnds, b0, b1; + + var rangebreaksOut = []; + if(!ax.rangebreaks) return rangebreaksOut; + + var rangebreaksIn = ax.rangebreaks.slice().sort(function(a, b) { + if(a.pattern === WEEKDAY_PATTERN && b.pattern === HOUR_PATTERN) return -1; + if(b.pattern === WEEKDAY_PATTERN && a.pattern === HOUR_PATTERN) return 1; + return 0; + }); + + var addBreak = function(min, max) { + min = Lib.constrain(min, r0, r1); + max = Lib.constrain(max, r0, r1); + if(min === max) return; + + var isNewBreak = true; + for(var j = 0; j < rangebreaksOut.length; j++) { + var brkj = rangebreaksOut[j]; + if(min < brkj.max && max >= brkj.min) { + if(min < brkj.min) { + brkj.min = min; + } + if(max > brkj.max) { + brkj.max = max; + } + isNewBreak = false; + } + } + if(isNewBreak) { + rangebreaksOut.push({min: min, max: max}); + } + }; + + for(i = 0; i < rangebreaksIn.length; i++) { + var brk = rangebreaksIn[i]; + + if(brk.enabled) { + if(brk.bounds) { + var t0 = r0; + var t1 = r1; + if(brk.pattern) { + // to remove decimal (most often found in auto ranges) + t0 = Math.floor(t0); + } + + bnds = Lib.simpleMap(brk.bounds, brk.pattern ? cleanNumber : ax.r2l); + b0 = bnds[0]; + b1 = bnds[1]; + + // r0 value as date + var t0Date = new Date(t0); + // r0 value for break pattern + var bndDelta; + // step in ms between rangebreaks + var step; + + switch(brk.pattern) { + case WEEKDAY_PATTERN: + step = ONEWEEK; + + bndDelta = ( + (b1 < b0 ? 7 : 0) + + (b1 - b0) + ) * ONEDAY; + + t0 += b0 * ONEDAY - ( + t0Date.getUTCDay() * ONEDAY + + t0Date.getUTCHours() * ONEHOUR + + t0Date.getUTCMinutes() * ONEMIN + + t0Date.getUTCSeconds() * ONESEC + + t0Date.getUTCMilliseconds() + ); + break; + case HOUR_PATTERN: + step = ONEDAY; + + bndDelta = ( + (b1 < b0 ? 24 : 0) + + (b1 - b0) + ) * ONEHOUR; + + t0 += b0 * ONEHOUR - ( + t0Date.getUTCHours() * ONEHOUR + + t0Date.getUTCMinutes() * ONEMIN + + t0Date.getUTCSeconds() * ONESEC + + t0Date.getUTCMilliseconds() + ); + break; + default: + t0 = Math.min(bnds[0], bnds[1]); + t1 = Math.max(bnds[0], bnds[1]); + step = t1 - t0; + bndDelta = step; + } + + for(var t = t0; t < t1; t += step) { + addBreak(t, t + bndDelta); + } + } else { + var vals = Lib.simpleMap(brk.values, ax.d2c); + for(var j = 0; j < vals.length; j++) { + b0 = vals[j]; + b1 = b0 + brk.dvalue; + addBreak(b0, b1); + } + } + } + } + + rangebreaksOut.sort(function(a, b) { return a.min - b.min; }); + + return rangebreaksOut; + }; + + // makeCalcdata: takes an x or y array and converts it + // to a position on the axis object "ax" + // inputs: + // trace - a data object from gd.data + // axLetter - a string, either 'x' or 'y', for which item + // to convert (TODO: is this now always the same as + // the first letter of ax._id?) + // in case the expected data isn't there, make a list of + // integers based on the opposite data + ax.makeCalcdata = function(trace, axLetter, opts) { + var arrayIn, arrayOut, i, len; + + var axType = ax.type; + var cal = axType === 'date' && trace[axLetter + 'calendar']; + + if(axLetter in trace) { + arrayIn = trace[axLetter]; + len = trace._length || Lib.minRowLength(arrayIn); + + if(Lib.isTypedArray(arrayIn) && (axType === 'linear' || axType === 'log')) { + if(len === arrayIn.length) { + return arrayIn; + } else if(arrayIn.subarray) { + return arrayIn.subarray(0, len); + } + } + + if(axType === 'multicategory') { + return setMultiCategoryIndex(arrayIn, len); + } + + arrayOut = new Array(len); + for(i = 0; i < len; i++) { + arrayOut[i] = ax.d2c(arrayIn[i], 0, cal, opts); + } + } else { + var v0 = ((axLetter + '0') in trace) ? ax.d2c(trace[axLetter + '0'], 0, cal) : 0; + var dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1; + + // the opposing data, for size if we have x and dx etc + arrayIn = trace[{x: 'y', y: 'x'}[axLetter]]; + len = trace._length || arrayIn.length; + arrayOut = new Array(len); + + for(i = 0; i < len; i++) { + arrayOut[i] = v0 + i * dv; + } + } + + // mask (i.e. set to BADNUM) coords that fall inside rangebreaks + if(ax.rangebreaks) { + for(i = 0; i < len; i++) { + arrayOut[i] = ax.maskBreaks(arrayOut[i]); + } + } + + return arrayOut; + }; + + ax.isValidRange = function(range) { + return ( + Array.isArray(range) && + range.length === 2 && + isNumeric(ax.r2l(range[0])) && + isNumeric(ax.r2l(range[1])) + ); + }; + + ax.isPtWithinRange = function(d, calendar) { + var coord = ax.c2l(d[axLetter], null, calendar); + var r0 = ax.r2l(ax.range[0]); + var r1 = ax.r2l(ax.range[1]); + + if(r0 < r1) { + return r0 <= coord && coord <= r1; + } else { + // Reversed axis case. + return r1 <= coord && coord <= r0; + } + }; + + ax._emptyCategories = function() { + ax._categories = []; + ax._categoriesMap = {}; + }; + + // should skip if not category nor multicategory + ax.clearCalc = function() { + var group = ax._matchGroup; + if(group) { + var categories = null; + var categoriesMap = null; + + for(var axId2 in group) { + var ax2 = fullLayout[axisIds.id2name(axId2)]; + if(ax2._categories) { + categories = ax2._categories; + categoriesMap = ax2._categoriesMap; + break; + } + } + + if(categories && categoriesMap) { + ax._categories = categories; + ax._categoriesMap = categoriesMap; + } else { + ax._emptyCategories(); + } + } else { + ax._emptyCategories(); + } + + if(ax._initialCategories) { + for(var j = 0; j < ax._initialCategories.length; j++) { + setCategoryIndex(ax._initialCategories[j]); + } + } + }; + + // sort the axis (and all the matching ones) by _initialCategories + // returns the indices of the traces affected by the reordering + ax.sortByInitialCategories = function() { + var affectedTraces = []; + + ax._emptyCategories(); + + if(ax._initialCategories) { + for(var j = 0; j < ax._initialCategories.length; j++) { + setCategoryIndex(ax._initialCategories[j]); + } + } + + affectedTraces = affectedTraces.concat(ax._traceIndices); + + // Propagate to matching axes + var group = ax._matchGroup; + for(var axId2 in group) { + if(axId === axId2) continue; + var ax2 = fullLayout[axisIds.id2name(axId2)]; + ax2._categories = ax._categories; + ax2._categoriesMap = ax._categoriesMap; + affectedTraces = affectedTraces.concat(ax2._traceIndices); + } + return affectedTraces; + }; + + // Propagate localization into the axis so that + // methods in Axes can use it w/o having to pass fullLayout + // Default (non-d3) number formatting uses separators directly + // dates and d3-formatted numbers use the d3 locale + // Fall back on default format for dummy axes that don't care about formatting + var locale = fullLayout._d3locale; + if(ax.type === 'date') { + ax._dateFormat = locale ? locale.timeFormat : utcFormat; + ax._extraFormat = fullLayout._extraFormat; + } + // occasionally we need _numFormat to pass through + // even though it won't be needed by this axis + ax._separators = fullLayout.separators; + ax._numFormat = locale ? locale.numberFormat : d3.format; + + // and for bar charts and box plots: reset forced minimum tick spacing + delete ax._minDtick; + delete ax._forceTick0; +}; + +},{"../../constants/numerical":265,"../../lib":285,"./axis_ids":335,"./constants":338,"@plotly/d3":20,"d3-time-format":29,"fast-isnumeric":31}],353:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var contrast = _dereq_('../../components/color').contrast; +var layoutAttributes = _dereq_('./layout_attributes'); +var handleArrayContainerDefaults = _dereq_('../array_container_defaults'); + +module.exports = function handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, config) { + if(!config || config.pass === 1) { + handlePrefixSuffix(containerIn, containerOut, coerce, axType, options); + } + + if(!config || config.pass === 2) { + handleOtherDefaults(containerIn, containerOut, coerce, axType, options); + } +}; + +function handlePrefixSuffix(containerIn, containerOut, coerce, axType, options) { + var showAttrDflt = getShowAttrDflt(containerIn); + + var tickPrefix = coerce('tickprefix'); + if(tickPrefix) coerce('showtickprefix', showAttrDflt); + + var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt); + if(tickSuffix) coerce('showticksuffix', showAttrDflt); +} + +function handleOtherDefaults(containerIn, containerOut, coerce, axType, options) { + var showAttrDflt = getShowAttrDflt(containerIn); + + var tickPrefix = coerce('tickprefix'); + if(tickPrefix) coerce('showtickprefix', showAttrDflt); + + var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt); + if(tickSuffix) coerce('showticksuffix', showAttrDflt); + + var showTickLabels = coerce('showticklabels'); + if(showTickLabels) { + var font = options.font || {}; + var contColor = containerOut.color; + var position = containerOut.ticklabelposition || ''; + var dfltFontColor = position.indexOf('inside') !== -1 ? + contrast(options.bgColor) : + // as with titlefont.color, inherit axis.color only if one was + // explicitly provided + (contColor && contColor !== layoutAttributes.color.dflt) ? + contColor : font.color; + + Lib.coerceFont(coerce, 'tickfont', { + family: font.family, + size: font.size, + color: dfltFontColor + }); + coerce('tickangle'); + + if(axType !== 'category') { + var tickFormat = coerce('tickformat'); + + handleArrayContainerDefaults(containerIn, containerOut, { + name: 'tickformatstops', + inclusionAttr: 'enabled', + handleItemDefaults: tickformatstopDefaults + }); + if(!containerOut.tickformatstops.length) { + delete containerOut.tickformatstops; + } + + if(!tickFormat && axType !== 'date') { + coerce('showexponent', showAttrDflt); + coerce('exponentformat'); + coerce('minexponent'); + coerce('separatethousands'); + } + } + } +} + +/* + * Attributes 'showexponent', 'showtickprefix' and 'showticksuffix' + * share values. + * + * If only 1 attribute is set, + * the remaining attributes inherit that value. + * + * If 2 attributes are set to the same value, + * the remaining attribute inherits that value. + * + * If 2 attributes are set to different values, + * the remaining is set to its dflt value. + * + */ +function getShowAttrDflt(containerIn) { + var showAttrsAll = ['showexponent', 'showtickprefix', 'showticksuffix']; + var showAttrs = showAttrsAll.filter(function(a) { + return containerIn[a] !== undefined; + }); + var sameVal = function(a) { + return containerIn[a] === containerIn[showAttrs[0]]; + }; + + if(showAttrs.every(sameVal) || showAttrs.length === 1) { + return containerIn[showAttrs[0]]; + } +} + +function tickformatstopDefaults(valueIn, valueOut) { + function coerce(attr, dflt) { + return Lib.coerce(valueIn, valueOut, layoutAttributes.tickformatstops, attr, dflt); + } + + var enabled = coerce('enabled'); + if(enabled) { + coerce('dtickrange'); + coerce('value'); + } +} + +},{"../../components/color":155,"../../lib":285,"../array_container_defaults":326,"./layout_attributes":346}],354:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var layoutAttributes = _dereq_('./layout_attributes'); + + +/** + * options: inherits outerTicks from axes.handleAxisDefaults + */ +module.exports = function handleTickDefaults(containerIn, containerOut, coerce, options) { + var tickLen = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'ticklen'); + var tickWidth = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickwidth'); + var tickColor = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickcolor', containerOut.color); + var showTicks = coerce('ticks', (options.outerTicks || tickLen || tickWidth || tickColor) ? 'outside' : ''); + + if(!showTicks) { + delete containerOut.ticklen; + delete containerOut.tickwidth; + delete containerOut.tickcolor; + } +}; + +},{"../../lib":285,"./layout_attributes":346}],355:[function(_dereq_,module,exports){ +'use strict'; + +var cleanTicks = _dereq_('./clean_ticks'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; + +module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) { + function readInput(attr) { + var v = containerIn[attr]; + return ( + v !== undefined + ) ? v : (containerOut._template || {})[attr]; + } + + var _tick0 = readInput('tick0'); + var _dtick = readInput('dtick'); + var _tickvals = readInput('tickvals'); + + var tickmodeDefault = isArrayOrTypedArray(_tickvals) ? 'array' : + _dtick ? 'linear' : + 'auto'; + var tickmode = coerce('tickmode', tickmodeDefault); + + if(tickmode === 'auto') coerce('nticks'); + else if(tickmode === 'linear') { + // dtick is usually a positive number, but there are some + // special strings available for log or date axes + // tick0 also has special logic + var dtick = containerOut.dtick = cleanTicks.dtick( + _dtick, axType); + containerOut.tick0 = cleanTicks.tick0( + _tick0, axType, containerOut.calendar, dtick); + } else if(axType !== 'multicategory') { + var tickvals = coerce('tickvals'); + if(tickvals === undefined) containerOut.tickmode = 'auto'; + else coerce('ticktext'); + } +}; + +},{"../../lib":285,"./clean_ticks":337}],356:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); +var Axes = _dereq_('./axes'); + +/** + * transitionAxes + * + * transition axes from one set of ranges to another, using a svg + * transformations, similar to during panning. + * + * @param {DOM element | object} gd + * @param {array} edits : array of 'edits', each item with + * - plotinfo {object} subplot object + * - xr0 {array} initial x-range + * - xr1 {array} end x-range + * - yr0 {array} initial y-range + * - yr1 {array} end y-range + * @param {object} transitionOpts + * @param {function} makeOnCompleteCallback + */ +module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnCompleteCallback) { + var fullLayout = gd._fullLayout; + + // special case for redraw:false Plotly.animate that relies on this + // to update axis-referenced layout components + if(edits.length === 0) { + Axes.redrawComponents(gd); + return; + } + + function unsetSubplotTransform(subplot) { + var xa = subplot.xaxis; + var ya = subplot.yaxis; + + fullLayout._defs.select('#' + subplot.clipId + '> rect') + .call(Drawing.setTranslate, 0, 0) + .call(Drawing.setScale, 1, 1); + + subplot.plot + .call(Drawing.setTranslate, xa._offset, ya._offset) + .call(Drawing.setScale, 1, 1); + + var traceGroups = subplot.plot.selectAll('.scatterlayer .trace'); + + // This is specifically directed at scatter traces, applying an inverse + // scale to individual points to counteract the scale of the trace + // as a whole: + traceGroups.selectAll('.point') + .call(Drawing.setPointGroupScale, 1, 1); + traceGroups.selectAll('.textpoint') + .call(Drawing.setTextPointsScale, 1, 1); + traceGroups + .call(Drawing.hideOutsideRangePoints, subplot); + } + + function updateSubplot(edit, progress) { + var plotinfo = edit.plotinfo; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var xlen = xa._length; + var ylen = ya._length; + var editX = !!edit.xr1; + var editY = !!edit.yr1; + var viewBox = []; + + if(editX) { + var xr0 = Lib.simpleMap(edit.xr0, xa.r2l); + var xr1 = Lib.simpleMap(edit.xr1, xa.r2l); + var dx0 = xr0[1] - xr0[0]; + var dx1 = xr1[1] - xr1[0]; + viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen; + viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0); + xa.range[0] = xa.l2r(xr0[0] * (1 - progress) + progress * xr1[0]); + xa.range[1] = xa.l2r(xr0[1] * (1 - progress) + progress * xr1[1]); + } else { + viewBox[0] = 0; + viewBox[2] = xlen; + } + + if(editY) { + var yr0 = Lib.simpleMap(edit.yr0, ya.r2l); + var yr1 = Lib.simpleMap(edit.yr1, ya.r2l); + var dy0 = yr0[1] - yr0[0]; + var dy1 = yr1[1] - yr1[0]; + viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen; + viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0); + ya.range[0] = xa.l2r(yr0[0] * (1 - progress) + progress * yr1[0]); + ya.range[1] = ya.l2r(yr0[1] * (1 - progress) + progress * yr1[1]); + } else { + viewBox[1] = 0; + viewBox[3] = ylen; + } + + Axes.drawOne(gd, xa, {skipTitle: true}); + Axes.drawOne(gd, ya, {skipTitle: true}); + Axes.redrawComponents(gd, [xa._id, ya._id]); + + var xScaleFactor = editX ? xlen / viewBox[2] : 1; + var yScaleFactor = editY ? ylen / viewBox[3] : 1; + var clipDx = editX ? viewBox[0] : 0; + var clipDy = editY ? viewBox[1] : 0; + var fracDx = editX ? (viewBox[0] / viewBox[2] * xlen) : 0; + var fracDy = editY ? (viewBox[1] / viewBox[3] * ylen) : 0; + var plotDx = xa._offset - fracDx; + var plotDy = ya._offset - fracDy; + + plotinfo.clipRect + .call(Drawing.setTranslate, clipDx, clipDy) + .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor); + + plotinfo.plot + .call(Drawing.setTranslate, plotDx, plotDy) + .call(Drawing.setScale, xScaleFactor, yScaleFactor); + + // apply an inverse scale to individual points to counteract + // the scale of the trace group. + Drawing.setPointGroupScale(plotinfo.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor); + Drawing.setTextPointsScale(plotinfo.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor); + } + + var onComplete; + if(makeOnCompleteCallback) { + // This module makes the choice whether or not it notifies Plotly.transition + // about completion: + onComplete = makeOnCompleteCallback(); + } + + function transitionComplete() { + var aobj = {}; + + for(var i = 0; i < edits.length; i++) { + var edit = edits[i]; + var xa = edit.plotinfo.xaxis; + var ya = edit.plotinfo.yaxis; + if(edit.xr1) aobj[xa._name + '.range'] = edit.xr1.slice(); + if(edit.yr1) aobj[ya._name + '.range'] = edit.yr1.slice(); + } + + // Signal that this transition has completed: + onComplete && onComplete(); + + return Registry.call('relayout', gd, aobj).then(function() { + for(var i = 0; i < edits.length; i++) { + unsetSubplotTransform(edits[i].plotinfo); + } + }); + } + + function transitionInterrupt() { + var aobj = {}; + + for(var i = 0; i < edits.length; i++) { + var edit = edits[i]; + var xa = edit.plotinfo.xaxis; + var ya = edit.plotinfo.yaxis; + if(edit.xr0) aobj[xa._name + '.range'] = edit.xr0.slice(); + if(edit.yr0) aobj[ya._name + '.range'] = edit.yr0.slice(); + } + + return Registry.call('relayout', gd, aobj).then(function() { + for(var i = 0; i < edits.length; i++) { + unsetSubplotTransform(edits[i].plotinfo); + } + }); + } + + var t1, t2, raf; + var easeFn = d3.ease(transitionOpts.easing); + + gd._transitionData._interruptCallbacks.push(function() { + window.cancelAnimationFrame(raf); + raf = null; + return transitionInterrupt(); + }); + + function doFrame() { + t2 = Date.now(); + + var tInterp = Math.min(1, (t2 - t1) / transitionOpts.duration); + var progress = easeFn(tInterp); + + for(var i = 0; i < edits.length; i++) { + updateSubplot(edits[i], progress); + } + + if(t2 - t1 > transitionOpts.duration) { + transitionComplete(); + raf = window.cancelAnimationFrame(doFrame); + } else { + raf = window.requestAnimationFrame(doFrame); + } + } + + t1 = Date.now(); + raf = window.requestAnimationFrame(doFrame); + + return Promise.resolve(); +}; + +},{"../../components/drawing":177,"../../lib":285,"../../registry":373,"./axes":331,"@plotly/d3":20}],357:[function(_dereq_,module,exports){ +'use strict'; + +var traceIs = _dereq_('../../registry').traceIs; +var autoType = _dereq_('./axis_autotype'); + +/* + * data: the plot data to use in choosing auto type + * name: axis object name (ie 'xaxis') if one should be stored + */ +module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) { + coerce('autotypenumbers', options.autotypenumbersDflt); + var axType = coerce('type', (options.splomStash || {}).type); + + if(axType === '-') { + setAutoType(containerOut, options.data); + + if(containerOut.type === '-') { + containerOut.type = 'linear'; + } else { + // copy autoType back to input axis + // note that if this object didn't exist + // in the input layout, we have to put it in + // this happens in the main supplyDefaults function + containerIn.type = containerOut.type; + } + } +}; + +function setAutoType(ax, data) { + // new logic: let people specify any type they want, + // only autotype if type is '-' + if(ax.type !== '-') return; + + var id = ax._id; + var axLetter = id.charAt(0); + var i; + + // support 3d + if(id.indexOf('scene') !== -1) id = axLetter; + + var d0 = getFirstNonEmptyTrace(data, id, axLetter); + if(!d0) return; + + // first check for histograms, as the count direction + // should always default to a linear axis + if(d0.type === 'histogram' && + axLetter === {v: 'y', h: 'x'}[d0.orientation || 'v'] + ) { + ax.type = 'linear'; + return; + } + + var calAttr = axLetter + 'calendar'; + var calendar = d0[calAttr]; + var opts = {noMultiCategory: !traceIs(d0, 'cartesian') || traceIs(d0, 'noMultiCategory')}; + + // To not confuse 2D x/y used for per-box sample points for multicategory coordinates + if(d0.type === 'box' && d0._hasPreCompStats && + axLetter === {h: 'x', v: 'y'}[d0.orientation || 'v'] + ) { + opts.noMultiCategory = true; + } + + opts.autotypenumbers = ax.autotypenumbers; + + // check all boxes on this x axis to see + // if they're dates, numbers, or categories + if(isBoxWithoutPositionCoords(d0, axLetter)) { + var posLetter = getBoxPosLetter(d0); + var boxPositions = []; + + for(i = 0; i < data.length; i++) { + var trace = data[i]; + if(!traceIs(trace, 'box-violin') || (trace[axLetter + 'axis'] || axLetter) !== id) continue; + + if(trace[posLetter] !== undefined) boxPositions.push(trace[posLetter][0]); + else if(trace.name !== undefined) boxPositions.push(trace.name); + else boxPositions.push('text'); + + if(trace[calAttr] !== calendar) calendar = undefined; + } + + ax.type = autoType(boxPositions, calendar, opts); + } else if(d0.type === 'splom') { + var dimensions = d0.dimensions; + var dim = dimensions[d0._axesDim[id]]; + if(dim.visible) ax.type = autoType(dim.values, calendar, opts); + } else { + ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']], calendar, opts); + } +} + +function getFirstNonEmptyTrace(data, id, axLetter) { + for(var i = 0; i < data.length; i++) { + var trace = data[i]; + + if(trace.type === 'splom' && + trace._length > 0 && + (trace['_' + axLetter + 'axes'] || {})[id] + ) { + return trace; + } + + if((trace[axLetter + 'axis'] || axLetter) === id) { + if(isBoxWithoutPositionCoords(trace, axLetter)) { + return trace; + } else if((trace[axLetter] || []).length || trace[axLetter + '0']) { + return trace; + } + } + } +} + +function getBoxPosLetter(trace) { + return {v: 'x', h: 'y'}[trace.orientation || 'v']; +} + +function isBoxWithoutPositionCoords(trace, axLetter) { + var posLetter = getBoxPosLetter(trace); + var isBox = traceIs(trace, 'box-violin'); + var isCandlestick = traceIs(trace._fullInput || {}, 'candlestick'); + + return ( + isBox && + !isCandlestick && + axLetter === posLetter && + trace[posLetter] === undefined && + trace[posLetter + '0'] === undefined + ); +} + +},{"../../registry":373,"./axis_autotype":332}],358:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); +var Lib = _dereq_('../lib'); + +/* + * Create or update an observer. This function is designed to be + * idempotent so that it can be called over and over as the component + * updates, and will attach and detach listeners as needed. + * + * @param {optional object} container + * An object on which the observer is stored. This is the mechanism + * by which it is idempotent. If it already exists, another won't be + * added. Each time it's called, the value lookup table is updated. + * @param {array} commandList + * An array of commands, following either `buttons` of `updatemenus` + * or `steps` of `sliders`. + * @param {function} onchange + * A listener called when the value is changed. Receives data object + * with information about the new state. + */ +exports.manageCommandObserver = function(gd, container, commandList, onchange) { + var ret = {}; + var enabled = true; + + if(container && container._commandObserver) { + ret = container._commandObserver; + } + + if(!ret.cache) { + ret.cache = {}; + } + + // Either create or just recompute this: + ret.lookupTable = {}; + + var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable); + + if(container && container._commandObserver) { + if(!binding) { + // If container exists and there are no longer any bindings, + // remove existing: + if(container._commandObserver.remove) { + container._commandObserver.remove(); + container._commandObserver = null; + return ret; + } + } else { + // If container exists and there *are* bindings, then the lookup + // table should have been updated and check is already attached, + // so there's nothing to be done: + return ret; + } + } + + // Determine whether there's anything to do for this binding: + + if(binding) { + // Build the cache: + bindingValueHasChanged(gd, binding, ret.cache); + + ret.check = function check() { + if(!enabled) return; + + var update = bindingValueHasChanged(gd, binding, ret.cache); + + if(update.changed && onchange) { + // Disable checks for the duration of this command in order to avoid + // infinite loops: + if(ret.lookupTable[update.value] !== undefined) { + ret.disable(); + Promise.resolve(onchange({ + value: update.value, + type: binding.type, + prop: binding.prop, + traces: binding.traces, + index: ret.lookupTable[update.value] + })).then(ret.enable, ret.enable); + } + } + + return update.changed; + }; + + var checkEvents = [ + 'plotly_relayout', + 'plotly_redraw', + 'plotly_restyle', + 'plotly_update', + 'plotly_animatingframe', + 'plotly_afterplot' + ]; + + for(var i = 0; i < checkEvents.length; i++) { + gd._internalOn(checkEvents[i], ret.check); + } + + ret.remove = function() { + for(var i = 0; i < checkEvents.length; i++) { + gd._removeInternalListener(checkEvents[i], ret.check); + } + }; + } else { + // TODO: It'd be really neat to actually give a *reason* for this, but at least a warning + // is a start + Lib.log('Unable to automatically bind plot updates to API command'); + + ret.lookupTable = {}; + ret.remove = function() {}; + } + + ret.disable = function disable() { + enabled = false; + }; + + ret.enable = function enable() { + enabled = true; + }; + + if(container) { + container._commandObserver = ret; + } + + return ret; +}; + +/* + * This function checks to see if an array of objects containing + * method and args properties is compatible with automatic two-way + * binding. The criteria right now are that + * + * 1. multiple traces may be affected + * 2. only one property may be affected + * 3. the same property must be affected by all commands + */ +exports.hasSimpleAPICommandBindings = function(gd, commandList, bindingsByValue) { + var i; + var n = commandList.length; + + var refBinding; + + for(i = 0; i < n; i++) { + var binding; + var command = commandList[i]; + var method = command.method; + var args = command.args; + + if(!Array.isArray(args)) args = []; + + // If any command has no method, refuse to bind: + if(!method) { + return false; + } + var bindings = exports.computeAPICommandBindings(gd, method, args); + + // Right now, handle one and *only* one property being set: + if(bindings.length !== 1) { + return false; + } + + if(!refBinding) { + refBinding = bindings[0]; + if(Array.isArray(refBinding.traces)) { + refBinding.traces.sort(); + } + } else { + binding = bindings[0]; + if(binding.type !== refBinding.type) { + return false; + } + if(binding.prop !== refBinding.prop) { + return false; + } + if(Array.isArray(refBinding.traces)) { + if(Array.isArray(binding.traces)) { + binding.traces.sort(); + for(var j = 0; j < refBinding.traces.length; j++) { + if(refBinding.traces[j] !== binding.traces[j]) { + return false; + } + } + } else { + return false; + } + } else { + if(binding.prop !== refBinding.prop) { + return false; + } + } + } + + binding = bindings[0]; + var value = binding.value; + if(Array.isArray(value)) { + if(value.length === 1) { + value = value[0]; + } else { + return false; + } + } + if(bindingsByValue) { + bindingsByValue[value] = i; + } + } + + return refBinding; +}; + +function bindingValueHasChanged(gd, binding, cache) { + var container, value, obj; + var changed = false; + + if(binding.type === 'data') { + // If it's data, we need to get a trace. Based on the limited scope + // of what we cover, we can just take the first trace from the list, + // or otherwise just the first trace: + container = gd._fullData[binding.traces !== null ? binding.traces[0] : 0]; + } else if(binding.type === 'layout') { + container = gd._fullLayout; + } else { + return false; + } + + value = Lib.nestedProperty(container, binding.prop).get(); + + obj = cache[binding.type] = cache[binding.type] || {}; + + if(obj.hasOwnProperty(binding.prop)) { + if(obj[binding.prop] !== value) { + changed = true; + } + } + + obj[binding.prop] = value; + + return { + changed: changed, + value: value + }; +} + +/* + * Execute an API command. There's really not much to this; it just provides + * a common hook so that implementations don't need to be synchronized across + * multiple components with the ability to invoke API commands. + * + * @param {string} method + * The name of the plotly command to execute. Must be one of 'animate', + * 'restyle', 'relayout', 'update'. + * @param {array} args + * A list of arguments passed to the API command + */ +exports.executeAPICommand = function(gd, method, args) { + if(method === 'skip') return Promise.resolve(); + + var _method = Registry.apiMethodRegistry[method]; + var allArgs = [gd]; + if(!Array.isArray(args)) args = []; + + for(var i = 0; i < args.length; i++) { + allArgs.push(args[i]); + } + + return _method.apply(null, allArgs).catch(function(err) { + Lib.warn('API call to Plotly.' + method + ' rejected.', err); + return Promise.reject(err); + }); +}; + +exports.computeAPICommandBindings = function(gd, method, args) { + var bindings; + + if(!Array.isArray(args)) args = []; + + switch(method) { + case 'restyle': + bindings = computeDataBindings(gd, args); + break; + case 'relayout': + bindings = computeLayoutBindings(gd, args); + break; + case 'update': + bindings = computeDataBindings(gd, [args[0], args[2]]) + .concat(computeLayoutBindings(gd, [args[1]])); + break; + case 'animate': + bindings = computeAnimateBindings(gd, args); + break; + default: + // This is the case where intelligent logic about what affects + // this command is not implemented. It causes no ill effects. + // For example, addFrames simply won't bind to a control component. + bindings = []; + } + return bindings; +}; + +function computeAnimateBindings(gd, args) { + // We'll assume that the only relevant modification an animation + // makes that's meaningfully tracked is the frame: + if(Array.isArray(args[0]) && args[0].length === 1 && ['string', 'number'].indexOf(typeof args[0][0]) !== -1) { + return [{type: 'layout', prop: '_currentFrame', value: args[0][0].toString()}]; + } else { + return []; + } +} + +function computeLayoutBindings(gd, args) { + var bindings = []; + + var astr = args[0]; + var aobj = {}; + if(typeof astr === 'string') { + aobj[astr] = args[1]; + } else if(Lib.isPlainObject(astr)) { + aobj = astr; + } else { + return bindings; + } + + crawl(aobj, function(path, attrName, attr) { + bindings.push({type: 'layout', prop: path, value: attr}); + }, '', 0); + + return bindings; +} + +function computeDataBindings(gd, args) { + var traces, astr, val, aobj; + var bindings = []; + + // Logic copied from Plotly.restyle: + astr = args[0]; + val = args[1]; + traces = args[2]; + aobj = {}; + if(typeof astr === 'string') { + aobj[astr] = val; + } else if(Lib.isPlainObject(astr)) { + // the 3-arg form + aobj = astr; + + if(traces === undefined) { + traces = val; + } + } else { + return bindings; + } + + if(traces === undefined) { + // Explicitly assign this to null instead of undefined: + traces = null; + } + + crawl(aobj, function(path, attrName, _attr) { + var thisTraces; + var attr; + + if(Array.isArray(_attr)) { + attr = _attr.slice(); + + var nAttr = Math.min(attr.length, gd.data.length); + if(traces) { + nAttr = Math.min(nAttr, traces.length); + } + thisTraces = []; + for(var j = 0; j < nAttr; j++) { + thisTraces[j] = traces ? traces[j] : j; + } + } else { + attr = _attr; + thisTraces = traces ? traces.slice() : null; + } + + // Convert [7] to just 7 when traces is null: + if(thisTraces === null) { + if(Array.isArray(attr)) { + attr = attr[0]; + } + } else if(Array.isArray(thisTraces)) { + if(!Array.isArray(attr)) { + var tmp = attr; + attr = []; + for(var i = 0; i < thisTraces.length; i++) { + attr[i] = tmp; + } + } + attr.length = Math.min(thisTraces.length, attr.length); + } + + bindings.push({ + type: 'data', + prop: path, + traces: thisTraces, + value: attr + }); + }, '', 0); + + return bindings; +} + +function crawl(attrs, callback, path, depth) { + Object.keys(attrs).forEach(function(attrName) { + var attr = attrs[attrName]; + + if(attrName[0] === '_') return; + + var thisPath = path + (depth > 0 ? '.' : '') + attrName; + + if(Lib.isPlainObject(attr)) { + crawl(attr, callback, thisPath, depth + 1); + } else { + // Only execute the callback on leaf nodes: + callback(thisPath, attrName, attr); + } + }); +} + +},{"../lib":285,"../registry":373}],359:[function(_dereq_,module,exports){ +'use strict'; + +var extendFlat = _dereq_('../lib/extend').extendFlat; + +/** + * Make a xy domain attribute group + * + * @param {object} opts + * @param {string} + * opts.name: name to be inserted in the default description + * @param {boolean} + * opts.trace: set to true for trace containers + * @param {string} + * opts.editType: editType for all pieces + * @param {boolean} + * opts.noGridCell: set to true to omit `row` and `column` + * + * @param {object} extra + * @param {string} + * extra.description: extra description. N.B we use + * a separate extra container to make it compatible with + * the compress_attributes transform. + * + * @return {object} attributes object containing {x,y} as specified + */ +exports.attributes = function(opts, extra) { + opts = opts || {}; + extra = extra || {}; + + var base = { + valType: 'info_array', + editType: opts.editType, + items: [ + {valType: 'number', min: 0, max: 1, editType: opts.editType}, + {valType: 'number', min: 0, max: 1, editType: opts.editType} + ], + dflt: [0, 1] + }; + + var namePart = opts.name ? opts.name + ' ' : ''; + var contPart = opts.trace ? 'trace ' : 'subplot '; + var descPart = extra.description ? ' ' + extra.description : ''; + + var out = { + x: extendFlat({}, base, { + }), + y: extendFlat({}, base, { + }), + editType: opts.editType + }; + + if(!opts.noGridCell) { + out.row = { + valType: 'integer', + min: 0, + dflt: 0, + editType: opts.editType, + }; + out.column = { + valType: 'integer', + min: 0, + dflt: 0, + editType: opts.editType, + }; + } + + return out; +}; + +exports.defaults = function(containerOut, layout, coerce, dfltDomains) { + var dfltX = (dfltDomains && dfltDomains.x) || [0, 1]; + var dfltY = (dfltDomains && dfltDomains.y) || [0, 1]; + + var grid = layout.grid; + if(grid) { + var column = coerce('domain.column'); + if(column !== undefined) { + if(column < grid.columns) dfltX = grid._domains.x[column]; + else delete containerOut.domain.column; + } + + var row = coerce('domain.row'); + if(row !== undefined) { + if(row < grid.rows) dfltY = grid._domains.y[row]; + else delete containerOut.domain.row; + } + } + + var x = coerce('domain.x', dfltX); + var y = coerce('domain.y', dfltY); + + // don't accept bad input data + if(!(x[0] < x[1])) containerOut.domain.x = dfltX.slice(); + if(!(y[0] < y[1])) containerOut.domain.y = dfltY.slice(); +}; + +},{"../lib/extend":279}],360:[function(_dereq_,module,exports){ +'use strict'; + +/* + * make a font attribute group + * + * @param {object} opts + * @param {string} + * opts.description: where & how this font is used + * @param {optional bool} arrayOk: + * should each part (family, size, color) be arrayOk? default false. + * @param {string} editType: + * the editType for all pieces of this font + * @param {optional string} colorEditType: + * a separate editType just for color + * + * @return {object} attributes object containing {family, size, color} as specified + */ +module.exports = function(opts) { + var editType = opts.editType; + var colorEditType = opts.colorEditType; + if(colorEditType === undefined) colorEditType = editType; + var attrs = { + family: { + valType: 'string', + noBlank: true, + strict: true, + editType: editType, + }, + size: { + valType: 'number', + min: 1, + editType: editType + }, + color: { + valType: 'color', + editType: colorEditType + }, + editType: editType, + // blank strings so compress_attributes can remove + // TODO - that's uber hacky... better solution? + }; + + if(opts.arrayOk) { + attrs.family.arrayOk = true; + attrs.size.arrayOk = true; + attrs.color.arrayOk = true; + } + + return attrs; +}; + +},{}],361:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + _isLinkedToArray: 'frames_entry', + + group: { + valType: 'string', + }, + name: { + valType: 'string', + }, + traces: { + valType: 'any', + }, + baseframe: { + valType: 'string', + }, + data: { + valType: 'any', + }, + layout: { + valType: 'any', + } +}; + +},{}],362:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); +var SUBPLOT_PATTERN = _dereq_('./cartesian/constants').SUBPLOT_PATTERN; + +/** + * Get calcdata trace(s) associated with a given subplot + * + * @param {array} calcData: as in gd.calcdata + * @param {string} type: subplot type + * @param {string} subplotId: subplot id to look for + * + * @return {array} array of calcdata traces + */ +exports.getSubplotCalcData = function(calcData, type, subplotId) { + var basePlotModule = Registry.subplotsRegistry[type]; + if(!basePlotModule) return []; + + var attr = basePlotModule.attr; + var subplotCalcData = []; + + for(var i = 0; i < calcData.length; i++) { + var calcTrace = calcData[i]; + var trace = calcTrace[0].trace; + + if(trace[attr] === subplotId) subplotCalcData.push(calcTrace); + } + + return subplotCalcData; +}; +/** + * Get calcdata trace(s) that can be plotted with a given module + * NOTE: this isn't necessarily just exactly matching trace type, + * if multiple trace types use the same plotting routine, they will be + * collected here. + * In order to not plot the same thing multiple times, we return two arrays, + * the calcdata we *will* plot with this module, and the ones we *won't* + * + * @param {array} calcdata: as in gd.calcdata + * @param {object|string|fn} arg1: + * the plotting module, or its name, or its plot method + * + * @return {array[array]} [foundCalcdata, remainingCalcdata] + */ +exports.getModuleCalcData = function(calcdata, arg1) { + var moduleCalcData = []; + var remainingCalcData = []; + + var plotMethod; + if(typeof arg1 === 'string') { + plotMethod = Registry.getModule(arg1).plot; + } else if(typeof arg1 === 'function') { + plotMethod = arg1; + } else { + plotMethod = arg1.plot; + } + if(!plotMethod) { + return [moduleCalcData, calcdata]; + } + + for(var i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var trace = cd[0].trace; + // N.B. + // - 'legendonly' traces do not make it past here + // - skip over 'visible' traces that got trimmed completely during calc transforms + if(trace.visible !== true || trace._length === 0) continue; + + // group calcdata trace not by 'module' (as the name of this function + // would suggest), but by 'module plot method' so that if some traces + // share the same module plot method (e.g. bar and histogram), we + // only call it one! + if(trace._module.plot === plotMethod) { + moduleCalcData.push(cd); + } else { + remainingCalcData.push(cd); + } + } + + return [moduleCalcData, remainingCalcData]; +}; + +/** + * Get the data trace(s) associated with a given subplot. + * + * @param {array} data plotly full data array. + * @param {string} type subplot type to look for. + * @param {string} subplotId subplot id to look for. + * + * @return {array} list of trace objects. + * + */ +exports.getSubplotData = function getSubplotData(data, type, subplotId) { + if(!Registry.subplotsRegistry[type]) return []; + + var attr = Registry.subplotsRegistry[type].attr; + var subplotData = []; + var trace, subplotX, subplotY; + + if(type === 'gl2d') { + var spmatch = subplotId.match(SUBPLOT_PATTERN); + subplotX = 'x' + spmatch[1]; + subplotY = 'y' + spmatch[2]; + } + + for(var i = 0; i < data.length; i++) { + trace = data[i]; + + if(type === 'gl2d' && Registry.traceIs(trace, 'gl2d')) { + if(trace[attr[0]] === subplotX && trace[attr[1]] === subplotY) { + subplotData.push(trace); + } + } else { + if(trace[attr] === subplotId) subplotData.push(trace); + } + } + + return subplotData; +}; + +},{"../registry":373,"./cartesian/constants":338}],363:[function(_dereq_,module,exports){ +'use strict'; + +function xformMatrix(m, v) { + var out = [0, 0, 0, 0]; + var i, j; + + for(i = 0; i < 4; ++i) { + for(j = 0; j < 4; ++j) { + out[j] += m[4 * i + j] * v[i]; + } + } + + return out; +} + +function project(camera, v) { + var p = xformMatrix(camera.projection, + xformMatrix(camera.view, + xformMatrix(camera.model, [v[0], v[1], v[2], 1]))); + return p; +} + +module.exports = project; + +},{}],364:[function(_dereq_,module,exports){ +'use strict'; + +var fontAttrs = _dereq_('./font_attributes'); +var animationAttrs = _dereq_('./animation_attributes'); +var colorAttrs = _dereq_('../components/color/attributes'); +var drawNewShapeAttrs = _dereq_('../components/shapes/draw_newshape/attributes'); +var padAttrs = _dereq_('./pad_attributes'); +var extendFlat = _dereq_('../lib/extend').extendFlat; + +var globalFont = fontAttrs({ + editType: 'calc', +}); +globalFont.family.dflt = '"Open Sans", verdana, arial, sans-serif'; +globalFont.size.dflt = 12; +globalFont.color.dflt = colorAttrs.defaultLine; + +module.exports = { + font: globalFont, + title: { + text: { + valType: 'string', + editType: 'layoutstyle', + }, + font: fontAttrs({ + editType: 'layoutstyle', + }), + xref: { + valType: 'enumerated', + dflt: 'container', + values: ['container', 'paper'], + editType: 'layoutstyle', + }, + yref: { + valType: 'enumerated', + dflt: 'container', + values: ['container', 'paper'], + editType: 'layoutstyle', + }, + x: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.5, + editType: 'layoutstyle', + }, + y: { + valType: 'number', + min: 0, + max: 1, + dflt: 'auto', + editType: 'layoutstyle', + }, + xanchor: { + valType: 'enumerated', + dflt: 'auto', + values: ['auto', 'left', 'center', 'right'], + editType: 'layoutstyle', + }, + yanchor: { + valType: 'enumerated', + dflt: 'auto', + values: ['auto', 'top', 'middle', 'bottom'], + editType: 'layoutstyle', + }, + pad: extendFlat(padAttrs({editType: 'layoutstyle'}), { + }), + editType: 'layoutstyle' + }, + uniformtext: { + mode: { + valType: 'enumerated', + values: [false, 'hide', 'show'], + dflt: false, + editType: 'plot', + }, + minsize: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'plot', + }, + editType: 'plot' + }, + autosize: { + valType: 'boolean', + dflt: false, + // autosize, width, and height get special editType treatment in _relayout + // so we can handle noop resizes more efficiently + editType: 'none', + }, + width: { + valType: 'number', + min: 10, + dflt: 700, + editType: 'plot', + }, + height: { + valType: 'number', + min: 10, + dflt: 450, + editType: 'plot', + }, + margin: { + l: { + valType: 'number', + min: 0, + dflt: 80, + editType: 'plot', + }, + r: { + valType: 'number', + min: 0, + dflt: 80, + editType: 'plot', + }, + t: { + valType: 'number', + min: 0, + dflt: 100, + editType: 'plot', + }, + b: { + valType: 'number', + min: 0, + dflt: 80, + editType: 'plot', + }, + pad: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'plot', + }, + autoexpand: { + valType: 'boolean', + dflt: true, + editType: 'plot', + }, + editType: 'plot' + }, + computed: { + valType: 'any', + editType: 'none', + }, + paper_bgcolor: { + valType: 'color', + dflt: colorAttrs.background, + editType: 'plot', + }, + plot_bgcolor: { + // defined here, but set in cartesian.supplyLayoutDefaults + // because it needs to know if there are (2D) axes or not + valType: 'color', + dflt: colorAttrs.background, + editType: 'layoutstyle', + }, + autotypenumbers: { + valType: 'enumerated', + values: ['convert types', 'strict'], + dflt: 'convert types', + editType: 'calc', + }, + separators: { + valType: 'string', + editType: 'plot', + }, + hidesources: { + valType: 'boolean', + dflt: false, + editType: 'plot', + }, + showlegend: { + // handled in legend.supplyLayoutDefaults + // but included here because it's not in the legend object + valType: 'boolean', + editType: 'legend', + }, + colorway: { + valType: 'colorlist', + dflt: colorAttrs.defaults, + editType: 'calc', + }, + datarevision: { + valType: 'any', + editType: 'calc', + }, + uirevision: { + valType: 'any', + editType: 'none', + }, + editrevision: { + valType: 'any', + editType: 'none', + }, + selectionrevision: { + valType: 'any', + editType: 'none', + }, + template: { + valType: 'any', + editType: 'calc', + }, + + newshape: drawNewShapeAttrs.newshape, + activeshape: drawNewShapeAttrs.activeshape, + + meta: { + valType: 'any', + arrayOk: true, + editType: 'plot', + }, + + transition: extendFlat({}, animationAttrs.transition, { + editType: 'none' + }), + _deprecated: { + title: { + valType: 'string', + editType: 'layoutstyle', + }, + titlefont: fontAttrs({ + editType: 'layoutstyle', + }) + } +}; + +},{"../components/color/attributes":154,"../components/shapes/draw_newshape/attributes":240,"../lib/extend":279,"./animation_attributes":325,"./font_attributes":360,"./pad_attributes":365}],365:[function(_dereq_,module,exports){ +'use strict'; + +/** + * Creates a set of padding attributes. + * + * @param {object} opts + * @param {string} editType: + * the editType for all pieces of this padding definition + * + * @return {object} attributes object containing {t, r, b, l} as specified + */ +module.exports = function(opts) { + var editType = opts.editType; + return { + t: { + valType: 'number', + dflt: 0, + editType: editType, + }, + r: { + valType: 'number', + dflt: 0, + editType: editType, + }, + b: { + valType: 'number', + dflt: 0, + editType: editType, + }, + l: { + valType: 'number', + dflt: 0, + editType: editType, + }, + editType: editType + }; +}; + +},{}],366:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var timeFormatLocale = _dereq_('d3-time-format').timeFormatLocale; +var isNumeric = _dereq_('fast-isnumeric'); + +var Registry = _dereq_('../registry'); +var PlotSchema = _dereq_('../plot_api/plot_schema'); +var Template = _dereq_('../plot_api/plot_template'); +var Lib = _dereq_('../lib'); +var Color = _dereq_('../components/color'); +var BADNUM = _dereq_('../constants/numerical').BADNUM; + +var axisIDs = _dereq_('./cartesian/axis_ids'); +var clearSelect = _dereq_('./cartesian/handle_outline').clearSelect; + +var animationAttrs = _dereq_('./animation_attributes'); +var frameAttrs = _dereq_('./frame_attributes'); + +var getModuleCalcData = _dereq_('../plots/get_data').getModuleCalcData; + +var relinkPrivateKeys = Lib.relinkPrivateKeys; +var _ = Lib._; + +var plots = module.exports = {}; + +// Expose registry methods on Plots for backward-compatibility +Lib.extendFlat(plots, Registry); + +plots.attributes = _dereq_('./attributes'); +plots.attributes.type.values = plots.allTypes; +plots.fontAttrs = _dereq_('./font_attributes'); +plots.layoutAttributes = _dereq_('./layout_attributes'); + +// TODO make this a plot attribute? +plots.fontWeight = 'normal'; + +var transformsRegistry = plots.transformsRegistry; + +var commandModule = _dereq_('./command'); +plots.executeAPICommand = commandModule.executeAPICommand; +plots.computeAPICommandBindings = commandModule.computeAPICommandBindings; +plots.manageCommandObserver = commandModule.manageCommandObserver; +plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings; + +// in some cases the browser doesn't seem to know how big +// the text is at first, so it needs to draw it, +// then wait a little, then draw it again +plots.redrawText = function(gd) { + gd = Lib.getGraphDiv(gd); + + return new Promise(function(resolve) { + setTimeout(function() { + if(!gd._fullLayout) return; + Registry.getComponentMethod('annotations', 'draw')(gd); + Registry.getComponentMethod('legend', 'draw')(gd); + Registry.getComponentMethod('colorbar', 'draw')(gd); + resolve(plots.previousPromises(gd)); + }, 300); + }); +}; + +// resize plot about the container size +plots.resize = function(gd) { + gd = Lib.getGraphDiv(gd); + + var resolveLastResize; + var p = new Promise(function(resolve, reject) { + if(!gd || Lib.isHidden(gd)) { + reject(new Error('Resize must be passed a displayed plot div element.')); + } + + if(gd._redrawTimer) clearTimeout(gd._redrawTimer); + if(gd._resolveResize) resolveLastResize = gd._resolveResize; + gd._resolveResize = resolve; + + gd._redrawTimer = setTimeout(function() { + // return if there is nothing to resize or is hidden + if(!gd.layout || (gd.layout.width && gd.layout.height) || Lib.isHidden(gd)) { + resolve(gd); + return; + } + + delete gd.layout.width; + delete gd.layout.height; + + // autosizing doesn't count as a change that needs saving + var oldchanged = gd.changed; + + // nor should it be included in the undo queue + gd.autoplay = true; + + Registry.call('relayout', gd, {autosize: true}).then(function() { + gd.changed = oldchanged; + // Only resolve if a new call hasn't been made! + if(gd._resolveResize === resolve) { + delete gd._resolveResize; + resolve(gd); + } + }); + }, 100); + }); + + if(resolveLastResize) resolveLastResize(p); + return p; +}; + + +// for use in Lib.syncOrAsync, check if there are any +// pending promises in this plot and wait for them +plots.previousPromises = function(gd) { + if((gd._promises || []).length) { + return Promise.all(gd._promises) + .then(function() { gd._promises = []; }); + } +}; + +/** + * Adds the 'Edit chart' link. + * Note that now _doPlot calls this so it can regenerate whenever it replots + * + * Add source links to your graph inside the 'showSources' config argument. + */ +plots.addLinks = function(gd) { + // Do not do anything if showLink and showSources are not set to true in config + if(!gd._context.showLink && !gd._context.showSources) return; + + var fullLayout = gd._fullLayout; + + var linkContainer = Lib.ensureSingle(fullLayout._paper, 'text', 'js-plot-link-container', function(s) { + s.style({ + 'font-family': '"Open Sans", Arial, sans-serif', + 'font-size': '12px', + 'fill': Color.defaultLine, + 'pointer-events': 'all' + }) + .each(function() { + var links = d3.select(this); + links.append('tspan').classed('js-link-to-tool', true); + links.append('tspan').classed('js-link-spacer', true); + links.append('tspan').classed('js-sourcelinks', true); + }); + }); + + // The text node inside svg + var text = linkContainer.node(); + var attrs = {y: fullLayout._paper.attr('height') - 9}; + + // If text's width is bigger than the layout + // Check that text is a child node or document.body + // because otherwise IE/Edge might throw an exception + // when calling getComputedTextLength(). + // Apparently offsetParent is null for invisibles. + if(document.body.contains(text) && text.getComputedTextLength() >= (fullLayout.width - 20)) { + // Align the text at the left + attrs['text-anchor'] = 'start'; + attrs.x = 5; + } else { + // Align the text at the right + attrs['text-anchor'] = 'end'; + attrs.x = fullLayout._paper.attr('width') - 7; + } + + linkContainer.attr(attrs); + + var toolspan = linkContainer.select('.js-link-to-tool'); + var spacespan = linkContainer.select('.js-link-spacer'); + var sourcespan = linkContainer.select('.js-sourcelinks'); + + if(gd._context.showSources) gd._context.showSources(gd); + + // 'view in plotly' link for embedded plots + if(gd._context.showLink) positionPlayWithData(gd, toolspan); + + // separator if we have both sources and tool link + spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : ''); +}; + +// note that now this function is only adding the brand in +// iframes and 3rd-party apps +function positionPlayWithData(gd, container) { + container.text(''); + var link = container.append('a') + .attr({ + 'xlink:xlink:href': '#', + 'class': 'link--impt link--embedview', + 'font-weight': 'bold' + }) + .text(gd._context.linkText + ' ' + String.fromCharCode(187)); + + if(gd._context.sendData) { + link.on('click', function() { + plots.sendDataToCloud(gd); + }); + } else { + var path = window.location.pathname.split('/'); + var query = window.location.search; + link.attr({ + 'xlink:xlink:show': 'new', + 'xlink:xlink:href': '/' + path[2].split('.')[0] + '/' + path[1] + query + }); + } +} + +plots.sendDataToCloud = function(gd) { + var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL; + if(!baseUrl) return; + + gd.emit('plotly_beforeexport'); + + var hiddenformDiv = d3.select(gd) + .append('div') + .attr('id', 'hiddenform') + .style('display', 'none'); + + var hiddenform = hiddenformDiv + .append('form') + .attr({ + action: baseUrl + '/external', + method: 'post', + target: '_blank' + }); + + var hiddenformInput = hiddenform + .append('input') + .attr({ + type: 'text', + name: 'data' + }); + + hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata'); + hiddenform.node().submit(); + hiddenformDiv.remove(); + + gd.emit('plotly_afterexport'); + return false; +}; + +var d3FormatKeys = [ + 'days', 'shortDays', 'months', 'shortMonths', 'periods', + 'dateTime', 'date', 'time', + 'decimal', 'thousands', 'grouping', 'currency' +]; + +var extraFormatKeys = [ + 'year', 'month', 'dayMonth', 'dayMonthYear' +]; + +/* + * Fill in default values + * @param {DOM element} gd + * @param {object} opts + * @param {boolean} opts.skipUpdateCalc: normally if the existing gd.calcdata looks + * compatible with the new gd._fullData we finish by linking the new _fullData traces + * to the old gd.calcdata, so it's correctly set if we're not going to recalc. But also, + * if there are calcTransforms on the trace, we first remap data arrays from the old full + * trace into the new one. Use skipUpdateCalc to defer this (needed by Plotly.react) + * + * gd.data, gd.layout: + * are precisely what the user specified (except as modified by cleanData/cleanLayout), + * these fields shouldn't be modified (except for filling in some auto values) + * nor used directly after the supply defaults step. + * + * gd._fullData, gd._fullLayout: + * are complete descriptions of how to draw the plot, + * use these fields in all required computations. + * + * gd._fullLayout._modules + * is a list of all the trace modules required to draw the plot. + * + * gd._fullLayout._visibleModules + * subset of _modules, a list of modules corresponding to visible:true traces. + * + * gd._fullLayout._basePlotModules + * is a list of all the plot modules required to draw the plot. + * + * gd._fullLayout._transformModules + * is a list of all the transform modules invoked. + * + */ +plots.supplyDefaults = function(gd, opts) { + var skipUpdateCalc = opts && opts.skipUpdateCalc; + var oldFullLayout = gd._fullLayout || {}; + + if(oldFullLayout._skipDefaults) { + delete oldFullLayout._skipDefaults; + return; + } + + var newFullLayout = gd._fullLayout = {}; + var newLayout = gd.layout || {}; + + var oldFullData = gd._fullData || []; + var newFullData = gd._fullData = []; + var newData = gd.data || []; + + var oldCalcdata = gd.calcdata || []; + + var context = gd._context || {}; + + var i; + + // Create all the storage space for frames, but only if doesn't already exist + if(!gd._transitionData) plots.createTransitionData(gd); + + // So we only need to do this once (and since we have gd here) + // get the translated placeholder titles. + // These ones get used as default values so need to be known at supplyDefaults + // others keep their blank defaults but render the placeholder as desired later + // TODO: make these work the same way, only inserting the placeholder text at draw time? + // The challenge is that this has slightly different behavior right now in editable mode: + // using the placeholder as default makes this text permanently (but lightly) visible, + // but explicit '' for these titles gives you a placeholder that's hidden until you mouse + // over it - so you're not distracted by it if you really don't want a title, but if you do + // and you're new to plotly you may not be able to find it. + // When editable=false the two behave the same, no title is drawn. + newFullLayout._dfltTitle = { + plot: _(gd, 'Click to enter Plot title'), + x: _(gd, 'Click to enter X axis title'), + y: _(gd, 'Click to enter Y axis title'), + colorbar: _(gd, 'Click to enter Colorscale title'), + annotation: _(gd, 'new text') + }; + newFullLayout._traceWord = _(gd, 'trace'); + + var formatObj = getFormatObj(gd, d3FormatKeys); + + // stash the token from context so mapbox subplots can use it as default + newFullLayout._mapboxAccessToken = context.mapboxAccessToken; + + // first fill in what we can of layout without looking at data + // because fullData needs a few things from layout + if(oldFullLayout._initialAutoSizeIsDone) { + // coerce the updated layout while preserving width and height + var oldWidth = oldFullLayout.width; + var oldHeight = oldFullLayout.height; + + plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj); + + if(!newLayout.width) newFullLayout.width = oldWidth; + if(!newLayout.height) newFullLayout.height = oldHeight; + plots.sanitizeMargins(newFullLayout); + } else { + // coerce the updated layout and autosize if needed + plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj); + + var missingWidthOrHeight = (!newLayout.width || !newLayout.height); + var autosize = newFullLayout.autosize; + var autosizable = context.autosizable; + var initialAutoSize = missingWidthOrHeight && (autosize || autosizable); + + if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout); + else if(missingWidthOrHeight) plots.sanitizeMargins(newFullLayout); + + // for backwards-compatibility with Plotly v1.x.x + if(!autosize && missingWidthOrHeight) { + newLayout.width = newFullLayout.width; + newLayout.height = newFullLayout.height; + } + } + + newFullLayout._d3locale = getFormatter(formatObj, newFullLayout.separators); + newFullLayout._extraFormat = getFormatObj(gd, extraFormatKeys); + + newFullLayout._initialAutoSizeIsDone = true; + + // keep track of how many traces are inputted + newFullLayout._dataLength = newData.length; + + // clear the lists of trace and baseplot modules, and subplots + newFullLayout._modules = []; + newFullLayout._visibleModules = []; + newFullLayout._basePlotModules = []; + var subplots = newFullLayout._subplots = emptySubplotLists(); + + // initialize axis and subplot hash objects for splom-generated grids + var splomAxes = newFullLayout._splomAxes = {x: {}, y: {}}; + var splomSubplots = newFullLayout._splomSubplots = {}; + // initialize splom grid defaults + newFullLayout._splomGridDflt = {}; + + // for stacked area traces to share config across traces + newFullLayout._scatterStackOpts = {}; + // for the first scatter trace on each subplot (so it knows tonext->tozero) + newFullLayout._firstScatter = {}; + // for grouped bar/box/violin trace to share config across traces + newFullLayout._alignmentOpts = {}; + // track color axes referenced in the data + newFullLayout._colorAxes = {}; + + // for traces to request a default rangeslider on their x axes + // eg set `_requestRangeslider.x2 = true` for xaxis2 + newFullLayout._requestRangeslider = {}; + + // pull uids from old data to use as new defaults + newFullLayout._traceUids = getTraceUids(oldFullData, newData); + + // then do the data + newFullLayout._globalTransforms = (gd._context || {}).globalTransforms; + plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout); + + // redo grid size defaults with info about splom x/y axes, + // and fill in generated cartesian axes and subplots + var splomXa = Object.keys(splomAxes.x); + var splomYa = Object.keys(splomAxes.y); + if(splomXa.length > 1 && splomYa.length > 1) { + Registry.getComponentMethod('grid', 'sizeDefaults')(newLayout, newFullLayout); + + for(i = 0; i < splomXa.length; i++) { + Lib.pushUnique(subplots.xaxis, splomXa[i]); + } + for(i = 0; i < splomYa.length; i++) { + Lib.pushUnique(subplots.yaxis, splomYa[i]); + } + for(var k in splomSubplots) { + Lib.pushUnique(subplots.cartesian, k); + } + } + + // attach helper method to check whether a plot type is present on graph + newFullLayout._has = plots._hasPlotType.bind(newFullLayout); + + if(oldFullData.length === newFullData.length) { + for(i = 0; i < newFullData.length; i++) { + relinkPrivateKeys(newFullData[i], oldFullData[i]); + } + } + + // finally, fill in the pieces of layout that may need to look at data + plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData); + + // Special cases that introduce interactions between traces. + // This is after relinkPrivateKeys so we can use those in crossTraceDefaults + // and after layout module defaults, so we can use eg barmode + var _modules = newFullLayout._visibleModules; + var crossTraceDefaultsFuncs = []; + for(i = 0; i < _modules.length; i++) { + var funci = _modules[i].crossTraceDefaults; + // some trace types share crossTraceDefaults (ie histogram2d, histogram2dcontour) + if(funci) Lib.pushUnique(crossTraceDefaultsFuncs, funci); + } + for(i = 0; i < crossTraceDefaultsFuncs.length; i++) { + crossTraceDefaultsFuncs[i](newFullData, newFullLayout); + } + + // turn on flag to optimize large splom-only graphs + // mostly by omitting SVG layers during Cartesian.drawFramework + newFullLayout._hasOnlyLargeSploms = ( + newFullLayout._basePlotModules.length === 1 && + newFullLayout._basePlotModules[0].name === 'splom' && + splomXa.length > 15 && + splomYa.length > 15 && + newFullLayout.shapes.length === 0 && + newFullLayout.images.length === 0 + ); + + // relink / initialize subplot axis objects + plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout); + + // clean subplots and other artifacts from previous plot calls + plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout); + + var hadGL2D = !!(oldFullLayout._has && oldFullLayout._has('gl2d')); + var hasGL2D = !!(newFullLayout._has && newFullLayout._has('gl2d')); + var hadCartesian = !!(oldFullLayout._has && oldFullLayout._has('cartesian')); + var hasCartesian = !!(newFullLayout._has && newFullLayout._has('cartesian')); + var hadBgLayer = hadCartesian || hadGL2D; + var hasBgLayer = hasCartesian || hasGL2D; + if(hadBgLayer && !hasBgLayer) { + // remove bgLayer + oldFullLayout._bgLayer.remove(); + } else if(hasBgLayer && !hadBgLayer) { + // create bgLayer + newFullLayout._shouldCreateBgLayer = true; + } + + // clear selection outline until we implement persistent selection, + // don't clear them though when drag handlers (e.g. listening to + // `plotly_selecting`) update the graph. + // we should try to come up with a better solution when implementing + // https://github.com/plotly/plotly.js/issues/1851 + if(oldFullLayout._zoomlayer && !gd._dragging) { + clearSelect({ // mock old gd + _fullLayout: oldFullLayout + }); + } + + + // fill in meta helpers + fillMetaTextHelpers(newFullData, newFullLayout); + + // relink functions and _ attributes to promote consistency between plots + relinkPrivateKeys(newFullLayout, oldFullLayout); + + // colorscale crossTraceDefaults needs newFullLayout with relinked keys + Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout); + + // For persisting GUI-driven changes in layout + // _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys + if(!newFullLayout._preGUI) newFullLayout._preGUI = {}; + // track trace GUI changes by uid rather than by trace index + if(!newFullLayout._tracePreGUI) newFullLayout._tracePreGUI = {}; + var tracePreGUI = newFullLayout._tracePreGUI; + var uids = {}; + var uid; + for(uid in tracePreGUI) uids[uid] = 'old'; + for(i = 0; i < newFullData.length; i++) { + uid = newFullData[i]._fullInput.uid; + if(!uids[uid]) tracePreGUI[uid] = {}; + uids[uid] = 'new'; + } + for(uid in uids) { + if(uids[uid] === 'old') delete tracePreGUI[uid]; + } + + // set up containers for margin calculations + initMargins(newFullLayout); + + // collect and do some initial calculations for rangesliders + Registry.getComponentMethod('rangeslider', 'makeData')(newFullLayout); + + // update object references in calcdata + if(!skipUpdateCalc && oldCalcdata.length === newFullData.length) { + plots.supplyDefaultsUpdateCalc(oldCalcdata, newFullData); + } +}; + +plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) { + for(var i = 0; i < newFullData.length; i++) { + var newTrace = newFullData[i]; + var cd0 = (oldCalcdata[i] || [])[0]; + if(cd0 && cd0.trace) { + var oldTrace = cd0.trace; + if(oldTrace._hasCalcTransform) { + var arrayAttrs = oldTrace._arrayAttrs; + var j, astr, oldArrayVal; + + for(j = 0; j < arrayAttrs.length; j++) { + astr = arrayAttrs[j]; + oldArrayVal = Lib.nestedProperty(oldTrace, astr).get().slice(); + Lib.nestedProperty(newTrace, astr).set(oldArrayVal); + } + } + cd0.trace = newTrace; + } + } +}; + +/** + * Create a list of uid strings satisfying (in this order of importance): + * 1. all unique, all strings + * 2. matches input uids if provided + * 3. matches previous data uids + */ +function getTraceUids(oldFullData, newData) { + var len = newData.length; + var oldFullInput = []; + var i, prevFullInput; + for(i = 0; i < oldFullData.length; i++) { + var thisFullInput = oldFullData[i]._fullInput; + if(thisFullInput !== prevFullInput) oldFullInput.push(thisFullInput); + prevFullInput = thisFullInput; + } + var oldLen = oldFullInput.length; + var out = new Array(len); + var seenUids = {}; + + function setUid(uid, i) { + out[i] = uid; + seenUids[uid] = 1; + } + + function tryUid(uid, i) { + if(uid && typeof uid === 'string' && !seenUids[uid]) { + setUid(uid, i); + return true; + } + } + + for(i = 0; i < len; i++) { + var newUid = newData[i].uid; + if(typeof newUid === 'number') newUid = String(newUid); + + if(tryUid(newUid, i)) continue; + if(i < oldLen && tryUid(oldFullInput[i].uid, i)) continue; + setUid(Lib.randstr(seenUids), i); + } + + return out; +} + +/** + * Make a container for collecting subplots we need to display. + * + * Finds all subplot types we need to enumerate once and caches it, + * but makes a new output object each time. + * Single-trace subplots (which have no `id`) such as pie, table, etc + * do not need to be collected because we just draw all visible traces. + */ +function emptySubplotLists() { + var collectableSubplotTypes = Registry.collectableSubplotTypes; + var out = {}; + var i, j; + + if(!collectableSubplotTypes) { + collectableSubplotTypes = []; + + var subplotsRegistry = Registry.subplotsRegistry; + + for(var subplotType in subplotsRegistry) { + var subplotModule = subplotsRegistry[subplotType]; + var subplotAttr = subplotModule.attr; + + if(subplotAttr) { + collectableSubplotTypes.push(subplotType); + + // special case, currently just for cartesian: + // we need to enumerate axes, not just subplots + if(Array.isArray(subplotAttr)) { + for(j = 0; j < subplotAttr.length; j++) { + Lib.pushUnique(collectableSubplotTypes, subplotAttr[j]); + } + } + } + } + } + + for(i = 0; i < collectableSubplotTypes.length; i++) { + out[collectableSubplotTypes[i]] = []; + } + return out; +} + +/** + * getFormatObj: use _context to get the format object from locale. + * Used to get d3.locale argument object and extraFormat argument object + * + * Regarding d3.locale argument : + * decimal and thousands can be overridden later by layout.separators + * grouping and currency are not presently used by our automatic number + * formatting system but can be used by custom formats. + * + * @returns {object} d3.locale format object + */ +function getFormatObj(gd, formatKeys) { + var locale = gd._context.locale; + if(!locale) locale = 'en-US'; + + var formatDone = false; + var formatObj = {}; + + function includeFormat(newFormat) { + var formatFinished = true; + for(var i = 0; i < formatKeys.length; i++) { + var formatKey = formatKeys[i]; + if(!formatObj[formatKey]) { + if(newFormat[formatKey]) { + formatObj[formatKey] = newFormat[formatKey]; + } else formatFinished = false; + } + } + if(formatFinished) formatDone = true; + } + + // same as localize, look for format parts in each format spec in the chain + for(var i = 0; i < 2; i++) { + var locales = gd._context.locales; + for(var j = 0; j < 2; j++) { + var formatj = (locales[locale] || {}).format; + if(formatj) { + includeFormat(formatj); + if(formatDone) break; + } + locales = Registry.localeRegistry; + } + + var baseLocale = locale.split('-')[0]; + if(formatDone || baseLocale === locale) break; + locale = baseLocale; + } + + // lastly pick out defaults from english (non-US, as DMY is so much more common) + if(!formatDone) includeFormat(Registry.localeRegistry.en.format); + + return formatObj; +} + +/** + * getFormatter: combine the final separators with the locale formatting object + * we pulled earlier to generate number and time formatters + * TODO: remove separators in v3, only use locale, so we don't need this step? + * + * @param {object} formatObj: d3.locale format object + * @param {string} separators: length-2 string to override decimal and thousands + * separators in number formatting + * + * @returns {object} {numberFormat, timeFormat} d3 formatter factory functions + * for numbers and time + */ +function getFormatter(formatObj, separators) { + formatObj.decimal = separators.charAt(0); + formatObj.thousands = separators.charAt(1); + + return { + numberFormat: d3.locale(formatObj).numberFormat, + timeFormat: timeFormatLocale(formatObj).utcFormat + }; +} + +function fillMetaTextHelpers(newFullData, newFullLayout) { + var _meta; + var meta4data = []; + + if(newFullLayout.meta) { + _meta = newFullLayout._meta = { + meta: newFullLayout.meta, + layout: {meta: newFullLayout.meta} + }; + } + + for(var i = 0; i < newFullData.length; i++) { + var trace = newFullData[i]; + + if(trace.meta) { + meta4data[trace.index] = trace._meta = {meta: trace.meta}; + } else if(newFullLayout.meta) { + trace._meta = {meta: newFullLayout.meta}; + } + if(newFullLayout.meta) { + trace._meta.layout = {meta: newFullLayout.meta}; + } + } + + if(meta4data.length) { + if(!_meta) { + _meta = newFullLayout._meta = {}; + } + _meta.data = meta4data; + } +} + +// Create storage for all of the data related to frames and transitions: +plots.createTransitionData = function(gd) { + // Set up the default keyframe if it doesn't exist: + if(!gd._transitionData) { + gd._transitionData = {}; + } + + if(!gd._transitionData._frames) { + gd._transitionData._frames = []; + } + + if(!gd._transitionData._frameHash) { + gd._transitionData._frameHash = {}; + } + + if(!gd._transitionData._counter) { + gd._transitionData._counter = 0; + } + + if(!gd._transitionData._interruptCallbacks) { + gd._transitionData._interruptCallbacks = []; + } +}; + +// helper function to be bound to fullLayout to check +// whether a certain plot type is present on plot +// or trace has a category +plots._hasPlotType = function(category) { + var i; + + // check base plot modules + var basePlotModules = this._basePlotModules || []; + for(i = 0; i < basePlotModules.length; i++) { + if(basePlotModules[i].name === category) return true; + } + + // check trace modules (including non-visible:true) + var modules = this._modules || []; + for(i = 0; i < modules.length; i++) { + var name = modules[i].name; + if(name === category) return true; + // N.B. this is modules[i] along with 'categories' as a hash object + var _module = Registry.modules[name]; + if(_module && _module.categories[category]) return true; + } + + return false; +}; + +plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + var i, j; + + var basePlotModules = oldFullLayout._basePlotModules || []; + for(i = 0; i < basePlotModules.length; i++) { + var _module = basePlotModules[i]; + + if(_module.clean) { + _module.clean(newFullData, newFullLayout, oldFullData, oldFullLayout); + } + } + + var hadGl = oldFullLayout._has && oldFullLayout._has('gl'); + var hasGl = newFullLayout._has && newFullLayout._has('gl'); + + if(hadGl && !hasGl) { + if(oldFullLayout._glcontainer !== undefined) { + oldFullLayout._glcontainer.selectAll('.gl-canvas').remove(); + oldFullLayout._glcontainer.selectAll('.no-webgl').remove(); + oldFullLayout._glcanvas = null; + } + } + + var hasInfoLayer = !!oldFullLayout._infolayer; + + oldLoop: + for(i = 0; i < oldFullData.length; i++) { + var oldTrace = oldFullData[i]; + var oldUid = oldTrace.uid; + + for(j = 0; j < newFullData.length; j++) { + var newTrace = newFullData[j]; + + if(oldUid === newTrace.uid) continue oldLoop; + } + + // clean old colorbars + if(hasInfoLayer) { + oldFullLayout._infolayer.select('.cb' + oldUid).remove(); + } + } +}; + +plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + var i, j; + + var oldSubplots = oldFullLayout._plots || {}; + var newSubplots = newFullLayout._plots = {}; + var newSubplotList = newFullLayout._subplots; + + var mockGd = { + _fullData: newFullData, + _fullLayout: newFullLayout + }; + + var ids = newSubplotList.cartesian.concat(newSubplotList.gl2d || []); + + for(i = 0; i < ids.length; i++) { + var id = ids[i]; + var oldSubplot = oldSubplots[id]; + var xaxis = axisIDs.getFromId(mockGd, id, 'x'); + var yaxis = axisIDs.getFromId(mockGd, id, 'y'); + var plotinfo; + + // link or create subplot object + if(oldSubplot) { + plotinfo = newSubplots[id] = oldSubplot; + } else { + plotinfo = newSubplots[id] = {}; + plotinfo.id = id; + } + + // add these axis ids to each others' subplot lists + xaxis._counterAxes.push(yaxis._id); + yaxis._counterAxes.push(xaxis._id); + xaxis._subplotsWith.push(id); + yaxis._subplotsWith.push(id); + + // update x and y axis layout object refs + plotinfo.xaxis = xaxis; + plotinfo.yaxis = yaxis; + + // By default, we clip at the subplot level, + // but if one trace on a given subplot has *cliponaxis* set to false, + // we need to clip at the trace module layer level; + // find this out here, once of for all. + plotinfo._hasClipOnAxisFalse = false; + + for(j = 0; j < newFullData.length; j++) { + var trace = newFullData[j]; + + if( + trace.xaxis === plotinfo.xaxis._id && + trace.yaxis === plotinfo.yaxis._id && + trace.cliponaxis === false + ) { + plotinfo._hasClipOnAxisFalse = true; + break; + } + } + } + + // while we're at it, link overlaying axes to their main axes and + // anchored axes to the axes they're anchored to + var axList = axisIDs.list(mockGd, null, true); + var ax; + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + var mainAx = null; + + if(ax.overlaying) { + mainAx = axisIDs.getFromId(mockGd, ax.overlaying); + + // you cannot overlay an axis that's already overlaying another + if(mainAx && mainAx.overlaying) { + ax.overlaying = false; + mainAx = null; + } + } + ax._mainAxis = mainAx || ax; + + /* + * For now force overlays to overlay completely... so they + * can drag together correctly and share backgrounds. + * Later perhaps we make separate axis domain and + * tick/line domain or something, so they can still share + * the (possibly larger) dragger and background but don't + * have to both be drawn over that whole domain + */ + if(mainAx) ax.domain = mainAx.domain.slice(); + + ax._anchorAxis = ax.anchor === 'free' ? + null : + axisIDs.getFromId(mockGd, ax.anchor); + } + + // finally, we can find the main subplot for each axis + // (on which the ticks & labels are drawn) + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + ax._counterAxes.sort(axisIDs.idSort); + ax._subplotsWith.sort(Lib.subplotSort); + ax._mainSubplot = findMainSubplot(ax, newFullLayout); + + // find "full" domain span of counter axes, + // this loop can be costly, so only compute it when required + if(ax._counterAxes.length && ( + (ax.spikemode && ax.spikemode.indexOf('across') !== -1) || + (ax.automargin && ax.mirror && ax.anchor !== 'free') || + Registry.getComponentMethod('rangeslider', 'isVisible')(ax) + )) { + var min = 1; + var max = 0; + for(j = 0; j < ax._counterAxes.length; j++) { + var ax2 = axisIDs.getFromId(mockGd, ax._counterAxes[j]); + min = Math.min(min, ax2.domain[0]); + max = Math.max(max, ax2.domain[1]); + } + if(min < max) { + ax._counterDomainMin = min; + ax._counterDomainMax = max; + } + } + } +}; + +function findMainSubplot(ax, fullLayout) { + var mockGd = {_fullLayout: fullLayout}; + + var isX = ax._id.charAt(0) === 'x'; + var anchorAx = ax._mainAxis._anchorAxis; + var mainSubplotID = ''; + var nextBestMainSubplotID = ''; + var anchorID = ''; + + // First try the main ID with the anchor + if(anchorAx) { + anchorID = anchorAx._mainAxis._id; + mainSubplotID = isX ? (ax._id + anchorID) : (anchorID + ax._id); + } + + // Then look for a subplot with the counteraxis overlaying the anchor + // If that fails just use the first subplot including this axis + if(!mainSubplotID || !fullLayout._plots[mainSubplotID]) { + mainSubplotID = ''; + + var counterIDs = ax._counterAxes; + for(var j = 0; j < counterIDs.length; j++) { + var counterPart = counterIDs[j]; + var id = isX ? (ax._id + counterPart) : (counterPart + ax._id); + if(!nextBestMainSubplotID) nextBestMainSubplotID = id; + var counterAx = axisIDs.getFromId(mockGd, counterPart); + if(anchorID && counterAx.overlaying === anchorID) { + mainSubplotID = id; + break; + } + } + } + + return mainSubplotID || nextBestMainSubplotID; +} + +// This function clears any trace attributes with valType: color and +// no set dflt filed in the plot schema. This is needed because groupby (which +// is the only transform for which this currently applies) supplies parent +// trace defaults, then expanded trace defaults. The result is that `null` +// colors are default-supplied and inherited as a color instead of a null. +// The result is that expanded trace default colors have no effect, with +// the final result that groups are indistinguishable. This function clears +// those colors so that individual groupby groups get unique colors. +plots.clearExpandedTraceDefaultColors = function(trace) { + var colorAttrs, path, i; + + // This uses weird closure state in order to satisfy the linter rule + // that we can't create functions in a loop. + function locateColorAttrs(attr, attrName, attrs, level) { + path[level] = attrName; + path.length = level + 1; + if(attr.valType === 'color' && attr.dflt === undefined) { + colorAttrs.push(path.join('.')); + } + } + + path = []; + + // Get the cached colorAttrs: + colorAttrs = trace._module._colorAttrs; + + // Or else compute and cache the colorAttrs on the module: + if(!colorAttrs) { + trace._module._colorAttrs = colorAttrs = []; + PlotSchema.crawl( + trace._module.attributes, + locateColorAttrs + ); + } + + for(i = 0; i < colorAttrs.length; i++) { + var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]); + + if(!origprop.get()) { + Lib.nestedProperty(trace, colorAttrs[i]).set(null); + } + } +}; + + +plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { + var modules = fullLayout._modules; + var visibleModules = fullLayout._visibleModules; + var basePlotModules = fullLayout._basePlotModules; + var cnt = 0; + var colorCnt = 0; + + var i, fullTrace, trace; + + fullLayout._transformModules = []; + + function pushModule(fullTrace) { + dataOut.push(fullTrace); + + var _module = fullTrace._module; + if(!_module) return; + + Lib.pushUnique(modules, _module); + if(fullTrace.visible === true) Lib.pushUnique(visibleModules, _module); + Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule); + cnt++; + + // TODO: do we really want color not to increment for explicitly invisible traces? + // This logic is weird, but matches previous behavior: traces that you explicitly + // set to visible:false do not increment the color, but traces WE determine to be + // empty or invalid (and thus set to visible:false) DO increment color. + // I kind of think we should just let all traces increment color, visible or not. + // see mock: axes-autotype-empty vs. a test of restyling visible: false that + // I can't find right now... + if(fullTrace._input.visible !== false) colorCnt++; + } + + var carpetIndex = {}; + var carpetDependents = []; + var dataTemplate = (layout.template || {}).data || {}; + var templater = Template.traceTemplater(dataTemplate); + + for(i = 0; i < dataIn.length; i++) { + trace = dataIn[i]; + + // reuse uid we may have pulled out of oldFullData + // Note: templater supplies trace type + fullTrace = templater.newTrace(trace); + fullTrace.uid = fullLayout._traceUids[i]; + plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i); + + fullTrace.index = i; + fullTrace._input = trace; + fullTrace._expandedIndex = cnt; + + if(fullTrace.transforms && fullTrace.transforms.length) { + var sdInvisible = trace.visible !== false && fullTrace.visible === false; + + var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout); + + for(var j = 0; j < expandedTraces.length; j++) { + var expandedTrace = expandedTraces[j]; + + // No further templating during transforms. + var fullExpandedTrace = { + _template: fullTrace._template, + type: fullTrace.type, + // set uid using parent uid and expanded index + // to promote consistency between update calls + uid: fullTrace.uid + j + }; + + // If the first supplyDefaults created `visible: false`, + // clear it before running supplyDefaults a second time, + // because sometimes there are items we still want to coerce + // inside trace modules before determining that the trace is + // again `visible: false`, for example partial visibilities + // in `splom` traces. + if(sdInvisible && expandedTrace.visible === false) { + delete expandedTrace.visible; + } + + plots.supplyTraceDefaults(expandedTrace, fullExpandedTrace, cnt, fullLayout, i); + + // relink private (i.e. underscore) keys expanded trace to full expanded trace so + // that transform supply-default methods can set _ keys for future use. + relinkPrivateKeys(fullExpandedTrace, expandedTrace); + + // add info about parent data trace + fullExpandedTrace.index = i; + fullExpandedTrace._input = trace; + fullExpandedTrace._fullInput = fullTrace; + + // add info about the expanded data + fullExpandedTrace._expandedIndex = cnt; + fullExpandedTrace._expandedInput = expandedTrace; + + pushModule(fullExpandedTrace); + } + } else { + // add identify refs for consistency with transformed traces + fullTrace._fullInput = fullTrace; + fullTrace._expandedInput = fullTrace; + + pushModule(fullTrace); + } + + if(Registry.traceIs(fullTrace, 'carpetAxis')) { + carpetIndex[fullTrace.carpet] = fullTrace; + } + + if(Registry.traceIs(fullTrace, 'carpetDependent')) { + carpetDependents.push(i); + } + } + + for(i = 0; i < carpetDependents.length; i++) { + fullTrace = dataOut[carpetDependents[i]]; + + if(!fullTrace.visible) continue; + + var carpetAxis = carpetIndex[fullTrace.carpet]; + fullTrace._carpet = carpetAxis; + + if(!carpetAxis || !carpetAxis.visible) { + fullTrace.visible = false; + continue; + } + + fullTrace.xaxis = carpetAxis.xaxis; + fullTrace.yaxis = carpetAxis.yaxis; + } +}; + +plots.supplyAnimationDefaults = function(opts) { + opts = opts || {}; + var i; + var optsOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt); + } + + coerce('mode'); + coerce('direction'); + coerce('fromcurrent'); + + if(Array.isArray(opts.frame)) { + optsOut.frame = []; + for(i = 0; i < opts.frame.length; i++) { + optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {}); + } + } else { + optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {}); + } + + if(Array.isArray(opts.transition)) { + optsOut.transition = []; + for(i = 0; i < opts.transition.length; i++) { + optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {}); + } + } else { + optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {}); + } + + return optsOut; +}; + +plots.supplyAnimationFrameDefaults = function(opts) { + var optsOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt); + } + + coerce('duration'); + coerce('redraw'); + + return optsOut; +}; + +plots.supplyAnimationTransitionDefaults = function(opts) { + var optsOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt); + } + + coerce('duration'); + coerce('easing'); + + return optsOut; +}; + +plots.supplyFrameDefaults = function(frameIn) { + var frameOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt); + } + + coerce('group'); + coerce('name'); + coerce('traces'); + coerce('baseframe'); + coerce('data'); + coerce('layout'); + + return frameOut; +}; + +plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, traceInIndex) { + var colorway = layout.colorway || Color.defaults; + var defaultColor = colorway[colorIndex % colorway.length]; + + var i; + + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt); + } + + var visible = coerce('visible'); + + coerce('type'); + coerce('name', layout._traceWord + ' ' + traceInIndex); + + coerce('uirevision', layout.uirevision); + + // we want even invisible traces to make their would-be subplots visible + // so coerce the subplot id(s) now no matter what + var _module = plots.getModule(traceOut); + + traceOut._module = _module; + if(_module) { + var basePlotModule = _module.basePlotModule; + var subplotAttr = basePlotModule.attr; + var subplotAttrs = basePlotModule.attributes; + if(subplotAttr && subplotAttrs) { + var subplots = layout._subplots; + var subplotId = ''; + + if( + visible || + basePlotModule.name !== 'gl2d' // for now just drop empty gl2d subplots + // TODO - currently if we draw an empty gl2d subplot, it draws + // nothing then gets stuck and you can't get it back without newPlot + // sort this out in the regl refactor? + ) { + if(Array.isArray(subplotAttr)) { + for(i = 0; i < subplotAttr.length; i++) { + var attri = subplotAttr[i]; + var vali = Lib.coerce(traceIn, traceOut, subplotAttrs, attri); + + if(subplots[attri]) Lib.pushUnique(subplots[attri], vali); + subplotId += vali; + } + } else { + subplotId = Lib.coerce(traceIn, traceOut, subplotAttrs, subplotAttr); + } + + if(subplots[basePlotModule.name]) { + Lib.pushUnique(subplots[basePlotModule.name], subplotId); + } + } + } + } + + if(visible) { + coerce('customdata'); + coerce('ids'); + coerce('meta'); + + if(Registry.traceIs(traceOut, 'showLegend')) { + Lib.coerce(traceIn, traceOut, + _module.attributes.showlegend ? _module.attributes : plots.attributes, + 'showlegend' + ); + + coerce('legendgroup'); + var titleText = coerce('legendgrouptitle.text'); + if(titleText) { + Lib.coerceFont(coerce, 'legendgrouptitle.font', Lib.extendFlat({}, layout.font, { + size: Math.round(layout.font.size * 1.1) // default to larger font size + })); + } + + coerce('legendrank'); + + traceOut._dfltShowLegend = true; + } else { + traceOut._dfltShowLegend = false; + } + + if(_module) { + _module.supplyDefaults(traceIn, traceOut, defaultColor, layout); + } + + if(!Registry.traceIs(traceOut, 'noOpacity')) { + coerce('opacity'); + } + + if(Registry.traceIs(traceOut, 'notLegendIsolatable')) { + // This clears out the legendonly state for traces like carpet that + // cannot be isolated in the legend + traceOut.visible = !!traceOut.visible; + } + + if(!Registry.traceIs(traceOut, 'noHover')) { + if(!traceOut.hovertemplate) Lib.coerceHoverinfo(traceIn, traceOut, layout); + + // parcats support hover, but not hoverlabel stylings (yet) + if(traceOut.type !== 'parcats') { + Registry.getComponentMethod('fx', 'supplyDefaults')(traceIn, traceOut, defaultColor, layout); + } + } + + if(_module && _module.selectPoints) { + coerce('selectedpoints'); + } + + plots.supplyTransformDefaults(traceIn, traceOut, layout); + } + + return traceOut; +}; + +/** + * hasMakesDataTransform: does this trace have a transform that makes its own + * data, either by grabbing it from somewhere else or by creating it from input + * parameters? If so, we should still keep going with supplyDefaults + * even if the trace is invisible, which may just be because it has no data yet. + */ +function hasMakesDataTransform(trace) { + var transforms = trace.transforms; + if(Array.isArray(transforms) && transforms.length) { + for(var i = 0; i < transforms.length; i++) { + var ti = transforms[i]; + var _module = ti._module || transformsRegistry[ti.type]; + if(_module && _module.makesData) return true; + } + } + return false; +} + +plots.hasMakesDataTransform = hasMakesDataTransform; + +plots.supplyTransformDefaults = function(traceIn, traceOut, layout) { + // For now we only allow transforms on 1D traces, ie those that specify a _length. + // If we were to implement 2D transforms, we'd need to have each transform + // describe its own applicability and disable itself when it doesn't apply. + // Also allow transforms that make their own data, but not in globalTransforms + if(!(traceOut._length || hasMakesDataTransform(traceIn))) return; + + var globalTransforms = layout._globalTransforms || []; + var transformModules = layout._transformModules || []; + + if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return; + + var containerIn = traceIn.transforms || []; + var transformList = globalTransforms.concat(containerIn); + var containerOut = traceOut.transforms = []; + + for(var i = 0; i < transformList.length; i++) { + var transformIn = transformList[i]; + var type = transformIn.type; + var _module = transformsRegistry[type]; + var transformOut; + + /* + * Supply defaults may run twice. First pass runs all supply defaults steps + * and adds the _module to any output transforms. + * If transforms exist another pass is run so that any generated traces also + * go through supply defaults. This has the effect of rerunning + * supplyTransformDefaults. If the transform does not have a `transform` + * function it could not have generated any new traces and the second stage + * is unnecessary. We detect this case with the following variables. + */ + var isFirstStage = !(transformIn._module && transformIn._module === _module); + var doLaterStages = _module && typeof _module.transform === 'function'; + + if(!_module) Lib.warn('Unrecognized transform type ' + type + '.'); + + if(_module && _module.supplyDefaults && (isFirstStage || doLaterStages)) { + transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn); + transformOut.type = type; + transformOut._module = _module; + + Lib.pushUnique(transformModules, _module); + } else { + transformOut = Lib.extendFlat({}, transformIn); + } + + containerOut.push(transformOut); + } +}; + +function applyTransforms(fullTrace, fullData, layout, fullLayout) { + var container = fullTrace.transforms; + var dataOut = [fullTrace]; + + for(var i = 0; i < container.length; i++) { + var transform = container[i]; + var _module = transformsRegistry[transform.type]; + + if(_module && _module.transform) { + dataOut = _module.transform(dataOut, { + transform: transform, + fullTrace: fullTrace, + fullData: fullData, + layout: layout, + fullLayout: fullLayout, + transformIndex: i + }); + } + } + + return dataOut; +} + +plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt); + } + + var template = layoutIn.template; + if(Lib.isPlainObject(template)) { + layoutOut.template = template; + layoutOut._template = template.layout; + layoutOut._dataTemplate = template.data; + } + + coerce('autotypenumbers'); + + var globalFont = Lib.coerceFont(coerce, 'font'); + + coerce('title.text', layoutOut._dfltTitle.plot); + + Lib.coerceFont(coerce, 'title.font', { + family: globalFont.family, + size: Math.round(globalFont.size * 1.4), + color: globalFont.color + }); + + coerce('title.xref'); + coerce('title.yref'); + coerce('title.x'); + coerce('title.y'); + coerce('title.xanchor'); + coerce('title.yanchor'); + coerce('title.pad.t'); + coerce('title.pad.r'); + coerce('title.pad.b'); + coerce('title.pad.l'); + + var uniformtextMode = coerce('uniformtext.mode'); + if(uniformtextMode) { + coerce('uniformtext.minsize'); + } + + // Make sure that autosize is defaulted to *true* + // on layouts with no set width and height for backward compatibly, + // in particular https://plotly.com/javascript/responsive-fluid-layout/ + // + // Before https://github.com/plotly/plotly.js/pull/635 , + // layouts with no set width and height were set temporary set to 'initial' + // to pass through the autosize routine + // + // This behavior is subject to change in v3. + coerce('autosize', !(layoutIn.width && layoutIn.height)); + + coerce('width'); + coerce('height'); + coerce('margin.l'); + coerce('margin.r'); + coerce('margin.t'); + coerce('margin.b'); + coerce('margin.pad'); + coerce('margin.autoexpand'); + + if(layoutIn.width && layoutIn.height) plots.sanitizeMargins(layoutOut); + + Registry.getComponentMethod('grid', 'sizeDefaults')(layoutIn, layoutOut); + + coerce('paper_bgcolor'); + + coerce('separators', formatObj.decimal + formatObj.thousands); + coerce('hidesources'); + + coerce('colorway'); + + coerce('datarevision'); + var uirevision = coerce('uirevision'); + coerce('editrevision', uirevision); + coerce('selectionrevision', uirevision); + + Registry.getComponentMethod( + 'modebar', + 'supplyLayoutDefaults' + )(layoutIn, layoutOut); + + Registry.getComponentMethod( + 'shapes', + 'supplyDrawNewShapeDefaults' + )(layoutIn, layoutOut, coerce); + + coerce('meta'); + + // do not include defaults in fullLayout when users do not set transition + if(Lib.isPlainObject(layoutIn.transition)) { + coerce('transition.duration'); + coerce('transition.easing'); + coerce('transition.ordering'); + } + + Registry.getComponentMethod( + 'calendars', + 'handleDefaults' + )(layoutIn, layoutOut, 'calendar'); + + Registry.getComponentMethod( + 'fx', + 'supplyLayoutGlobalDefaults' + )(layoutIn, layoutOut, coerce); +}; + +function getComputedSize(attr) { + return ( + (typeof attr === 'string') && + (attr.substr(attr.length - 2) === 'px') && + parseFloat(attr) + ); +} + + +plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) { + var context = gd._context || {}; + var frameMargins = context.frameMargins; + var newWidth; + var newHeight; + + var isPlotDiv = Lib.isPlotDiv(gd); + + if(isPlotDiv) gd.emit('plotly_autosize'); + + // embedded in an iframe - just take the full iframe size + // if we get to this point, with no aspect ratio restrictions + if(context.fillFrame) { + newWidth = window.innerWidth; + newHeight = window.innerHeight; + + // somehow we get a few extra px height sometimes... + // just hide it + document.body.style.overflow = 'hidden'; + } else { + // plotly.js - let the developers do what they want, either + // provide height and width for the container div, + // specify size in layout, or take the defaults, + // but don't enforce any ratio restrictions + var computedStyle = isPlotDiv ? window.getComputedStyle(gd) : {}; + + newWidth = getComputedSize(computedStyle.width) || getComputedSize(computedStyle.maxWidth) || fullLayout.width; + newHeight = getComputedSize(computedStyle.height) || getComputedSize(computedStyle.maxHeight) || fullLayout.height; + + if(isNumeric(frameMargins) && frameMargins > 0) { + var factor = 1 - 2 * frameMargins; + newWidth = Math.round(factor * newWidth); + newHeight = Math.round(factor * newHeight); + } + } + + var minWidth = plots.layoutAttributes.width.min; + var minHeight = plots.layoutAttributes.height.min; + if(newWidth < minWidth) newWidth = minWidth; + if(newHeight < minHeight) newHeight = minHeight; + + var widthHasChanged = !layout.width && + (Math.abs(fullLayout.width - newWidth) > 1); + var heightHasChanged = !layout.height && + (Math.abs(fullLayout.height - newHeight) > 1); + + if(heightHasChanged || widthHasChanged) { + if(widthHasChanged) fullLayout.width = newWidth; + if(heightHasChanged) fullLayout.height = newHeight; + } + + // cache initial autosize value, used in relayout when + // width or height values are set to null + if(!gd._initialAutoSize) { + gd._initialAutoSize = { width: newWidth, height: newHeight }; + } + + plots.sanitizeMargins(fullLayout); +}; + +plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) { + var componentsRegistry = Registry.componentsRegistry; + var basePlotModules = layoutOut._basePlotModules; + var component, i, _module; + + var Cartesian = Registry.subplotsRegistry.cartesian; + + // check if any components need to add more base plot modules + // that weren't captured by traces + for(component in componentsRegistry) { + _module = componentsRegistry[component]; + + if(_module.includeBasePlot) { + _module.includeBasePlot(layoutIn, layoutOut); + } + } + + // make sure we *at least* have some cartesian axes + if(!basePlotModules.length) { + basePlotModules.push(Cartesian); + } + + // ensure all cartesian axes have at least one subplot + if(layoutOut._has('cartesian')) { + Registry.getComponentMethod('grid', 'contentDefaults')(layoutIn, layoutOut); + Cartesian.finalizeSubplots(layoutIn, layoutOut); + } + + // sort subplot lists + for(var subplotType in layoutOut._subplots) { + layoutOut._subplots[subplotType].sort(Lib.subplotSort); + } + + // base plot module layout defaults + for(i = 0; i < basePlotModules.length; i++) { + _module = basePlotModules[i]; + + // e.g. pie does not have a layout-defaults step + if(_module.supplyLayoutDefaults) { + _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + } + } + + // trace module layout defaults + // use _modules rather than _visibleModules so that even + // legendonly traces can include settings - eg barmode, which affects + // legend.traceorder default value. + var modules = layoutOut._modules; + for(i = 0; i < modules.length; i++) { + _module = modules[i]; + + if(_module.supplyLayoutDefaults) { + _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + } + } + + // transform module layout defaults + var transformModules = layoutOut._transformModules; + for(i = 0; i < transformModules.length; i++) { + _module = transformModules[i]; + + if(_module.supplyLayoutDefaults) { + _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData, transitionData); + } + } + + for(component in componentsRegistry) { + _module = componentsRegistry[component]; + + if(_module.supplyLayoutDefaults) { + _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + } + } +}; + +// Remove all plotly attributes from a div so it can be replotted fresh +// TODO: these really need to be encapsulated into a much smaller set... +plots.purge = function(gd) { + // note: we DO NOT remove _context because it doesn't change when we insert + // a new plot, and may have been set outside of our scope. + + var fullLayout = gd._fullLayout || {}; + if(fullLayout._glcontainer !== undefined) { + fullLayout._glcontainer.selectAll('.gl-canvas').remove(); + fullLayout._glcontainer.remove(); + fullLayout._glcanvas = null; + } + + // remove modebar + if(fullLayout._modeBar) fullLayout._modeBar.destroy(); + + if(gd._transitionData) { + // Ensure any dangling callbacks are simply dropped if the plot is purged. + // This is more or less only actually important for testing. + if(gd._transitionData._interruptCallbacks) { + gd._transitionData._interruptCallbacks.length = 0; + } + + if(gd._transitionData._animationRaf) { + window.cancelAnimationFrame(gd._transitionData._animationRaf); + } + } + + // remove any planned throttles + Lib.clearThrottle(); + + // remove responsive handler + Lib.clearResponsive(gd); + + // data and layout + delete gd.data; + delete gd.layout; + delete gd._fullData; + delete gd._fullLayout; + delete gd.calcdata; + delete gd.empty; + + delete gd.fid; + + delete gd.undoqueue; // action queue + delete gd.undonum; + delete gd.autoplay; // are we doing an action that doesn't go in undo queue? + delete gd.changed; + + // these get recreated on _doPlot anyway, but just to be safe + // (and to have a record of them...) + delete gd._promises; + delete gd._redrawTimer; + delete gd._hmlumcount; + delete gd._hmpixcount; + delete gd._transitionData; + delete gd._transitioning; + delete gd._initialAutoSize; + delete gd._transitioningWithDuration; + + // created during certain events, that *should* clean them up + // themselves, but may not if there was an error + delete gd._dragging; + delete gd._dragged; + delete gd._dragdata; + delete gd._hoverdata; + delete gd._snapshotInProgress; + delete gd._editing; + delete gd._mouseDownTime; + delete gd._legendMouseDownTime; + + // remove all event listeners + if(gd.removeAllListeners) gd.removeAllListeners(); +}; + +plots.style = function(gd) { + var _modules = gd._fullLayout._visibleModules; + var styleModules = []; + var i; + + // some trace modules reuse the same style method, + // make sure to not unnecessary call them multiple times. + + for(i = 0; i < _modules.length; i++) { + var _module = _modules[i]; + if(_module.style) { + Lib.pushUnique(styleModules, _module.style); + } + } + + for(i = 0; i < styleModules.length; i++) { + styleModules[i](gd); + } +}; + +plots.sanitizeMargins = function(fullLayout) { + // polar doesn't do margins... + if(!fullLayout || !fullLayout.margin) return; + + var width = fullLayout.width; + var height = fullLayout.height; + var margin = fullLayout.margin; + var plotWidth = width - (margin.l + margin.r); + var plotHeight = height - (margin.t + margin.b); + var correction; + + // if margin.l + margin.r = 0 then plotWidth > 0 + // as width >= 10 by supplyDefaults + // similarly for margin.t + margin.b + + if(plotWidth < 0) { + correction = (width - 1) / (margin.l + margin.r); + margin.l = Math.floor(correction * margin.l); + margin.r = Math.floor(correction * margin.r); + } + + if(plotHeight < 0) { + correction = (height - 1) / (margin.t + margin.b); + margin.t = Math.floor(correction * margin.t); + margin.b = Math.floor(correction * margin.b); + } +}; + +plots.clearAutoMarginIds = function(gd) { + gd._fullLayout._pushmarginIds = {}; +}; + +plots.allowAutoMargin = function(gd, id) { + gd._fullLayout._pushmarginIds[id] = 1; +}; + +function initMargins(fullLayout) { + var margin = fullLayout.margin; + + if(!fullLayout._size) { + var gs = fullLayout._size = { + l: Math.round(margin.l), + r: Math.round(margin.r), + t: Math.round(margin.t), + b: Math.round(margin.b), + p: Math.round(margin.pad) + }; + gs.w = Math.round(fullLayout.width) - gs.l - gs.r; + gs.h = Math.round(fullLayout.height) - gs.t - gs.b; + } + if(!fullLayout._pushmargin) fullLayout._pushmargin = {}; + if(!fullLayout._pushmarginIds) fullLayout._pushmarginIds = {}; +} + +// non-negotiable - this is the smallest height we will allow users to specify via explicit margins +var MIN_SPECIFIED_WIDTH = 2; +var MIN_SPECIFIED_HEIGHT = 2; + +// could be exposed as an option - the smallest we will allow automargin to shrink a larger plot +var MIN_REDUCED_WIDTH = 64; +var MIN_REDUCED_HEIGHT = 64; + +/** + * autoMargin: called by components that may need to expand the margins to + * be rendered on-plot. + * + * @param {DOM element} gd + * @param {string} id - an identifier unique (within this plot) to this object, + * so we can remove a previous margin expansion from the same object. + * @param {object} o - the margin requirements of this object, or omit to delete + * this entry (like if it's hidden). Keys are: + * x, y: plot fraction of the anchor point. + * xl, xr, yt, yb: if the object has an extent defined in plot fraction, + * you can specify both edges as plot fractions in each dimension + * l, r, t, b: the pixels to pad past the plot fraction x[l|r] and y[t|b] + * pad: extra pixels to add in all directions, default 12 (why?) + */ +plots.autoMargin = function(gd, id, o) { + var fullLayout = gd._fullLayout; + var width = fullLayout.width; + var height = fullLayout.height; + var margin = fullLayout.margin; + + var minFinalWidth = Lib.constrain( + width - margin.l - margin.r, + MIN_SPECIFIED_WIDTH, + MIN_REDUCED_WIDTH + ); + + var minFinalHeight = Lib.constrain( + height - margin.t - margin.b, + MIN_SPECIFIED_HEIGHT, + MIN_REDUCED_HEIGHT + ); + + var maxSpaceW = Math.max(0, width - minFinalWidth); + var maxSpaceH = Math.max(0, height - minFinalHeight); + + var pushMargin = fullLayout._pushmargin; + var pushMarginIds = fullLayout._pushmarginIds; + + if(margin.autoexpand !== false) { + if(!o) { + delete pushMargin[id]; + delete pushMarginIds[id]; + } else { + var pad = o.pad; + if(pad === undefined) { + // if no explicit pad is given, use 12px unless there's a + // specified margin that's smaller than that + pad = Math.min(12, margin.l, margin.r, margin.t, margin.b); + } + + // if the item is too big, just give it enough automargin to + // make sure you can still grab it and bring it back + if(maxSpaceW) { + var rW = (o.l + o.r) / maxSpaceW; + if(rW > 1) { + o.l /= rW; + o.r /= rW; + } + } + if(maxSpaceH) { + var rH = (o.t + o.b) / maxSpaceH; + if(rH > 1) { + o.t /= rH; + o.b /= rH; + } + } + + var xl = o.xl !== undefined ? o.xl : o.x; + var xr = o.xr !== undefined ? o.xr : o.x; + var yt = o.yt !== undefined ? o.yt : o.y; + var yb = o.yb !== undefined ? o.yb : o.y; + + pushMargin[id] = { + l: {val: xl, size: o.l + pad}, + r: {val: xr, size: o.r + pad}, + b: {val: yb, size: o.b + pad}, + t: {val: yt, size: o.t + pad} + }; + pushMarginIds[id] = 1; + } + + if(!fullLayout._replotting) { + return plots.doAutoMargin(gd); + } + } +}; + +plots.doAutoMargin = function(gd) { + var fullLayout = gd._fullLayout; + var width = fullLayout.width; + var height = fullLayout.height; + + if(!fullLayout._size) fullLayout._size = {}; + initMargins(fullLayout); + + var gs = fullLayout._size; + var margin = fullLayout.margin; + var oldMargins = Lib.extendFlat({}, gs); + + // adjust margins for outside components + // fullLayout.margin is the requested margin, + // fullLayout._size has margins and plotsize after adjustment + var ml = margin.l; + var mr = margin.r; + var mt = margin.t; + var mb = margin.b; + var pushMargin = fullLayout._pushmargin; + var pushMarginIds = fullLayout._pushmarginIds; + + if(fullLayout.margin.autoexpand !== false) { + for(var k in pushMargin) { + if(!pushMarginIds[k]) delete pushMargin[k]; + } + + // fill in the requested margins + pushMargin.base = { + l: {val: 0, size: ml}, + r: {val: 1, size: mr}, + t: {val: 1, size: mt}, + b: {val: 0, size: mb} + }; + + // now cycle through all the combinations of l and r + // (and t and b) to find the required margins + + for(var k1 in pushMargin) { + var pushleft = pushMargin[k1].l || {}; + var pushbottom = pushMargin[k1].b || {}; + var fl = pushleft.val; + var pl = pushleft.size; + var fb = pushbottom.val; + var pb = pushbottom.size; + + for(var k2 in pushMargin) { + if(isNumeric(pl) && pushMargin[k2].r) { + var fr = pushMargin[k2].r.val; + var pr = pushMargin[k2].r.size; + if(fr > fl) { + var newL = (pl * fr + (pr - width) * fl) / (fr - fl); + var newR = (pr * (1 - fl) + (pl - width) * (1 - fr)) / (fr - fl); + if(newL + newR > ml + mr) { + ml = newL; + mr = newR; + } + } + } + + if(isNumeric(pb) && pushMargin[k2].t) { + var ft = pushMargin[k2].t.val; + var pt = pushMargin[k2].t.size; + if(ft > fb) { + var newB = (pb * ft + (pt - height) * fb) / (ft - fb); + var newT = (pt * (1 - fb) + (pb - height) * (1 - ft)) / (ft - fb); + if(newB + newT > mb + mt) { + mb = newB; + mt = newT; + } + } + } + } + } + } + + var minFinalWidth = Lib.constrain( + width - margin.l - margin.r, + MIN_SPECIFIED_WIDTH, + MIN_REDUCED_WIDTH + ); + + var minFinalHeight = Lib.constrain( + height - margin.t - margin.b, + MIN_SPECIFIED_HEIGHT, + MIN_REDUCED_HEIGHT + ); + + var maxSpaceW = Math.max(0, width - minFinalWidth); + var maxSpaceH = Math.max(0, height - minFinalHeight); + + if(maxSpaceW) { + var rW = (ml + mr) / maxSpaceW; + if(rW > 1) { + ml /= rW; + mr /= rW; + } + } + + if(maxSpaceH) { + var rH = (mb + mt) / maxSpaceH; + if(rH > 1) { + mb /= rH; + mt /= rH; + } + } + + gs.l = Math.round(ml); + gs.r = Math.round(mr); + gs.t = Math.round(mt); + gs.b = Math.round(mb); + gs.p = Math.round(margin.pad); + gs.w = Math.round(width) - gs.l - gs.r; + gs.h = Math.round(height) - gs.t - gs.b; + + // if things changed and we're not already redrawing, trigger a redraw + if(!fullLayout._replotting && plots.didMarginChange(oldMargins, gs)) { + if('_redrawFromAutoMarginCount' in fullLayout) { + fullLayout._redrawFromAutoMarginCount++; + } else { + fullLayout._redrawFromAutoMarginCount = 1; + } + + // Always allow at least one redraw and give each margin-push + // call 3 loops to converge. Of course, for most cases this way too many, + // but let's keep things on the safe side until we fix our + // auto-margin pipeline problems: + // https://github.com/plotly/plotly.js/issues/2704 + var maxNumberOfRedraws = 3 * (1 + Object.keys(pushMarginIds).length); + + if(fullLayout._redrawFromAutoMarginCount < maxNumberOfRedraws) { + return Registry.call('_doPlot', gd); + } else { + fullLayout._size = oldMargins; + Lib.warn('Too many auto-margin redraws.'); + } + } + + refineTicks(gd); +}; + +function refineTicks(gd) { + var axList = axisIDs.list(gd, '', true); + + [ + '_adjustTickLabelsOverflow', + '_hideCounterAxisInsideTickLabels' + ].forEach(function(k) { + for(var i = 0; i < axList.length; i++) { + var hideFn = axList[i][k]; + if(hideFn) hideFn(); + } + }); +} + +var marginKeys = ['l', 'r', 't', 'b', 'p', 'w', 'h']; + +plots.didMarginChange = function(margin0, margin1) { + for(var i = 0; i < marginKeys.length; i++) { + var k = marginKeys[i]; + var m0 = margin0[k]; + var m1 = margin1[k]; + // use 1px tolerance in case we old/new differ only + // by rounding errors, which can lead to infinite loops + if(!isNumeric(m0) || Math.abs(m1 - m0) > 1) { + return true; + } + } + return false; +}; + +/** + * JSONify the graph data and layout + * + * This function needs to recurse because some src can be inside + * sub-objects. + * + * It also strips out functions and private (starts with _) elements. + * Therefore, we can add temporary things to data and layout that don't + * get saved. + * + * @param gd The graphDiv + * @param {Boolean} dataonly If true, don't return layout. + * @param {'keepref'|'keepdata'|'keepall'} [mode='keepref'] Filter what's kept + * keepref: remove data for which there's a src present + * eg if there's xsrc present (and xsrc is well-formed, + * ie has : and some chars before it), strip out x + * keepdata: remove all src tags, don't remove the data itself + * keepall: keep data and src + * @param {String} output If you specify 'object', the result will not be stringified + * @param {Boolean} useDefaults If truthy, use _fullLayout and _fullData + * @param {Boolean} includeConfig If truthy, include _context + * @returns {Object|String} + */ +plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfig) { + // if the defaults aren't supplied yet, we need to do that... + if((useDefaults && dataonly && !gd._fullData) || + (useDefaults && !dataonly && !gd._fullLayout)) { + plots.supplyDefaults(gd); + } + + var data = (useDefaults) ? gd._fullData : gd.data; + var layout = (useDefaults) ? gd._fullLayout : gd.layout; + var frames = (gd._transitionData || {})._frames; + + function stripObj(d, keepFunction) { + if(typeof d === 'function') { + return keepFunction ? '_function_' : null; + } + if(Lib.isPlainObject(d)) { + var o = {}; + var src; + Object.keys(d).sort().forEach(function(v) { + // remove private elements and functions + // _ is for private, [ is a mistake ie [object Object] + if(['_', '['].indexOf(v.charAt(0)) !== -1) return; + + // if a function, add if necessary then move on + if(typeof d[v] === 'function') { + if(keepFunction) o[v] = '_function'; + return; + } + + // look for src/data matches and remove the appropriate one + if(mode === 'keepdata') { + // keepdata: remove all ...src tags + if(v.substr(v.length - 3) === 'src') { + return; + } + } else if(mode === 'keepstream') { + // keep sourced data if it's being streamed. + // similar to keepref, but if the 'stream' object exists + // in a trace, we will keep the data array. + src = d[v + 'src']; + if(typeof src === 'string' && src.indexOf(':') > 0) { + if(!Lib.isPlainObject(d.stream)) { + return; + } + } + } else if(mode !== 'keepall') { + // keepref: remove sourced data but only + // if the source tag is well-formed + src = d[v + 'src']; + if(typeof src === 'string' && src.indexOf(':') > 0) { + return; + } + } + + // OK, we're including this... recurse into it + o[v] = stripObj(d[v], keepFunction); + }); + return o; + } + + if(Array.isArray(d)) { + return d.map(function(x) {return stripObj(x, keepFunction);}); + } + + if(Lib.isTypedArray(d)) { + return Lib.simpleMap(d, Lib.identity); + } + + // convert native dates to date strings... + // mostly for external users exporting to plotly + if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d); + + return d; + } + + var obj = { + data: (data || []).map(function(v) { + var d = stripObj(v); + // fit has some little arrays in it that don't contain data, + // just fit params and meta + if(dataonly) { delete d.fit; } + return d; + }) + }; + if(!dataonly) { + obj.layout = stripObj(layout); + if(useDefaults) { + var gs = layout._size; + obj.layout.computed = { + margin: { + b: gs.b, + l: gs.l, + r: gs.r, + t: gs.t + } + }; + } + } + + if(frames) obj.frames = stripObj(frames); + + if(includeConfig) obj.config = stripObj(gd._context, true); + + return (output === 'object') ? obj : JSON.stringify(obj); +}; + +/** + * Modify a keyframe using a list of operations: + * + * @param {array of objects} operations + * Sequence of operations to be performed on the keyframes + */ +plots.modifyFrames = function(gd, operations) { + var i, op, frame; + var _frames = gd._transitionData._frames; + var _frameHash = gd._transitionData._frameHash; + + for(i = 0; i < operations.length; i++) { + op = operations[i]; + + switch(op.type) { + // No reason this couldn't exist, but is currently unused/untested: + /* case 'rename': + frame = _frames[op.index]; + delete _frameHash[frame.name]; + _frameHash[op.name] = frame; + frame.name = op.name; + break;*/ + case 'replace': + frame = op.value; + var oldName = (_frames[op.index] || {}).name; + var newName = frame.name; + _frames[op.index] = _frameHash[newName] = frame; + + if(newName !== oldName) { + // If name has changed in addition to replacement, then update + // the lookup table: + delete _frameHash[oldName]; + _frameHash[newName] = frame; + } + + break; + case 'insert': + frame = op.value; + _frameHash[frame.name] = frame; + _frames.splice(op.index, 0, frame); + break; + case 'delete': + frame = _frames[op.index]; + delete _frameHash[frame.name]; + _frames.splice(op.index, 1); + break; + } + } + + return Promise.resolve(); +}; + +/* + * Compute a keyframe. Merge a keyframe into its base frame(s) and + * expand properties. + * + * @param {object} frameLookup + * An object containing frames keyed by name (i.e. gd._transitionData._frameHash) + * @param {string} frame + * The name of the keyframe to be computed + * + * Returns: a new object with the merged content + */ +plots.computeFrame = function(gd, frameName) { + var frameLookup = gd._transitionData._frameHash; + var i, traceIndices, traceIndex, destIndex; + + // Null or undefined will fail on .toString(). We'll allow numbers since we + // make it clear frames must be given string names, but we'll allow numbers + // here since they're otherwise fine for looking up frames as long as they're + // properly cast to strings. We really just want to ensure here that this + // 1) doesn't fail, and + // 2) doens't give an incorrect answer (which String(frameName) would) + if(!frameName) { + throw new Error('computeFrame must be given a string frame name'); + } + + var framePtr = frameLookup[frameName.toString()]; + + // Return false if the name is invalid: + if(!framePtr) { + return false; + } + + var frameStack = [framePtr]; + var frameNameStack = [framePtr.name]; + + // Follow frame pointers: + while(framePtr.baseframe && (framePtr = frameLookup[framePtr.baseframe.toString()])) { + // Avoid infinite loops: + if(frameNameStack.indexOf(framePtr.name) !== -1) break; + + frameStack.push(framePtr); + frameNameStack.push(framePtr.name); + } + + // A new object for the merged result: + var result = {}; + + // Merge, starting with the last and ending with the desired frame: + while((framePtr = frameStack.pop())) { + if(framePtr.layout) { + result.layout = plots.extendLayout(result.layout, framePtr.layout); + } + + if(framePtr.data) { + if(!result.data) { + result.data = []; + } + traceIndices = framePtr.traces; + + if(!traceIndices) { + // If not defined, assume serial order starting at zero + traceIndices = []; + for(i = 0; i < framePtr.data.length; i++) { + traceIndices[i] = i; + } + } + + if(!result.traces) { + result.traces = []; + } + + for(i = 0; i < framePtr.data.length; i++) { + // Loop through this frames data, find out where it should go, + // and merge it! + traceIndex = traceIndices[i]; + if(traceIndex === undefined || traceIndex === null) { + continue; + } + + destIndex = result.traces.indexOf(traceIndex); + if(destIndex === -1) { + destIndex = result.data.length; + result.traces[destIndex] = traceIndex; + } + + result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]); + } + } + } + + return result; +}; + +/* + * Recompute the lookup table that maps frame name -> frame object. addFrames/ + * deleteFrames already manages this data one at a time, so the only time this + * is necessary is if you poke around manually in `gd._transitionData._frames` + * and create and haven't updated the lookup table. + */ +plots.recomputeFrameHash = function(gd) { + var hash = gd._transitionData._frameHash = {}; + var frames = gd._transitionData._frames; + for(var i = 0; i < frames.length; i++) { + var frame = frames[i]; + if(frame && frame.name) { + hash[frame.name] = frame; + } + } +}; + +/** + * Extend an object, treating container arrays very differently by extracting + * their contents and merging them separately. + * + * This exists so that we can extendDeepNoArrays and avoid stepping into data + * arrays without knowledge of the plot schema, but so that we may also manually + * recurse into known container arrays, such as transforms. + * + * See extendTrace and extendLayout below for usage. + */ +plots.extendObjectWithContainers = function(dest, src, containerPaths) { + var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer; + var copy = Lib.extendDeepNoArrays({}, src || {}); + var expandedObj = Lib.expandObjectPaths(copy); + var containerObj = {}; + + // Step through and extract any container properties. Otherwise extendDeepNoArrays + // will clobber any existing properties with an empty array and then supplyDefaults + // will reset everything to defaults. + if(containerPaths && containerPaths.length) { + for(i = 0; i < containerPaths.length; i++) { + containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]); + containerVal = containerProp.get(); + + if(containerVal === undefined) { + Lib.nestedProperty(containerObj, containerPaths[i]).set(null); + } else { + containerProp.set(null); + Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal); + } + } + } + + dest = Lib.extendDeepNoArrays(dest || {}, expandedObj); + + if(containerPaths && containerPaths.length) { + for(i = 0; i < containerPaths.length; i++) { + srcProp = Lib.nestedProperty(containerObj, containerPaths[i]); + srcContainer = srcProp.get(); + + if(!srcContainer) continue; + + destProp = Lib.nestedProperty(dest, containerPaths[i]); + destContainer = destProp.get(); + + if(!Array.isArray(destContainer)) { + destContainer = []; + destProp.set(destContainer); + } + + for(j = 0; j < srcContainer.length; j++) { + var srcObj = srcContainer[j]; + + if(srcObj === null) destContainer[j] = null; + else { + destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcObj); + } + } + + destProp.set(destContainer); + } + } + + return dest; +}; + +plots.dataArrayContainers = ['transforms', 'dimensions']; +plots.layoutArrayContainers = Registry.layoutArrayContainers; + +/* + * Extend a trace definition. This method: + * + * 1. directly transfers any array references + * 2. manually recurses into container arrays like transforms + * + * The result is the original object reference with the new contents merged in. + */ +plots.extendTrace = function(destTrace, srcTrace) { + return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers); +}; + +/* + * Extend a layout definition. This method: + * + * 1. directly transfers any array references (not critically important for + * layout since there aren't really data arrays) + * 2. manually recurses into container arrays like annotations + * + * The result is the original object reference with the new contents merged in. + */ +plots.extendLayout = function(destLayout, srcLayout) { + return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers); +}; + +/** + * Transition to a set of new data and layout properties from Plotly.animate + * + * @param {DOM element} gd + * @param {Object[]} data + * an array of data objects following the normal Plotly data definition format + * @param {Object} layout + * a layout object, following normal Plotly layout format + * @param {Number[]} traces + * indices of the corresponding traces specified in `data` + * @param {Object} frameOpts + * options for the frame (i.e. whether to redraw post-transition) + * @param {Object} transitionOpts + * options for the transition + */ +plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) { + var opts = {redraw: frameOpts.redraw}; + var transitionedTraces = {}; + var axEdits = []; + + opts.prepareFn = function() { + var dataLength = Array.isArray(data) ? data.length : 0; + var traceIndices = traces.slice(0, dataLength); + + for(var i = 0; i < traceIndices.length; i++) { + var traceIdx = traceIndices[i]; + var trace = gd._fullData[traceIdx]; + var _module = trace._module; + + // There's nothing to do if this module is not defined: + if(!_module) continue; + + // Don't register the trace as transitioned if it doesn't know what to do. + // If it *is* registered, it will receive a callback that it's responsible + // for calling in order to register the transition as having completed. + if(_module.animatable) { + var n = _module.basePlotModule.name; + if(!transitionedTraces[n]) transitionedTraces[n] = []; + transitionedTraces[n].push(traceIdx); + } + + gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]); + } + + // Follow the same procedure. Clone it so we don't mangle the input, then + // expand any object paths so we can merge deep into gd.layout: + var layoutUpdate = Lib.expandObjectPaths(Lib.extendDeepNoArrays({}, layout)); + + // Before merging though, we need to modify the incoming layout. We only + // know how to *transition* layout ranges, so it's imperative that a new + // range not be sent to the layout before the transition has started. So + // we must remove the things we can transition: + var axisAttrRe = /^[xy]axis[0-9]*$/; + for(var attr in layoutUpdate) { + if(!axisAttrRe.test(attr)) continue; + delete layoutUpdate[attr].range; + } + + plots.extendLayout(gd.layout, layoutUpdate); + + // Supply defaults after applying the incoming properties. Note that any attempt + // to simplify this step and reduce the amount of work resulted in the reconstruction + // of essentially the whole supplyDefaults step, so that it seems sensible to just use + // supplyDefaults even though it's heavier than would otherwise be desired for + // transitions: + + // first delete calcdata so supplyDefaults knows a calc step is coming + delete gd.calcdata; + + plots.supplyDefaults(gd); + plots.doCalcdata(gd); + + var newLayout = Lib.expandObjectPaths(layout); + + if(newLayout) { + var subplots = gd._fullLayout._plots; + + for(var k in subplots) { + var plotinfo = subplots[k]; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var xr0 = xa.range.slice(); + var yr0 = ya.range.slice(); + + var xr1 = null; + var yr1 = null; + var editX = null; + var editY = null; + + if(Array.isArray(newLayout[xa._name + '.range'])) { + xr1 = newLayout[xa._name + '.range'].slice(); + } else if(Array.isArray((newLayout[xa._name] || {}).range)) { + xr1 = newLayout[xa._name].range.slice(); + } + if(Array.isArray(newLayout[ya._name + '.range'])) { + yr1 = newLayout[ya._name + '.range'].slice(); + } else if(Array.isArray((newLayout[ya._name] || {}).range)) { + yr1 = newLayout[ya._name].range.slice(); + } + + if(xr0 && xr1 && + (xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) + ) { + editX = {xr0: xr0, xr1: xr1}; + } + if(yr0 && yr1 && + (ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) + ) { + editY = {yr0: yr0, yr1: yr1}; + } + + if(editX || editY) { + axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY)); + } + } + } + + return Promise.resolve(); + }; + + opts.runFn = function(makeCallback) { + var traceTransitionOpts; + var basePlotModules = gd._fullLayout._basePlotModules; + var hasAxisTransition = axEdits.length; + var i; + + if(layout) { + for(i = 0; i < basePlotModules.length; i++) { + if(basePlotModules[i].transitionAxes) { + basePlotModules[i].transitionAxes(gd, axEdits, transitionOpts, makeCallback); + } + } + } + + // Here handle the exception that we refuse to animate scales and axes at the same + // time. In other words, if there's an axis transition, then set the data transition + // to instantaneous. + if(hasAxisTransition) { + traceTransitionOpts = Lib.extendFlat({}, transitionOpts); + traceTransitionOpts.duration = 0; + // This means do not transition cartesian traces, + // this happens on layout-only (e.g. axis range) animations + delete transitionedTraces.cartesian; + } else { + traceTransitionOpts = transitionOpts; + } + + // Note that we pass a callback to *create* the callback that must be invoked on completion. + // This is since not all traces know about transitions, so it greatly simplifies matters if + // the trace is responsible for creating a callback, if needed, and then executing it when + // the time is right. + for(var n in transitionedTraces) { + var traceIndices = transitionedTraces[n]; + var _module = gd._fullData[traceIndices[0]]._module; + _module.basePlotModule.plot(gd, traceIndices, traceTransitionOpts, makeCallback); + } + }; + + return _transition(gd, transitionOpts, opts); +}; + +/** + * Transition to a set of new data and layout properties from Plotly.react + * + * @param {DOM element} gd + * @param {object} restyleFlags + * - anim {'all'|'some'} + * @param {object} relayoutFlags + * - anim {'all'|'some'} + * @param {object} oldFullLayout : old (pre Plotly.react) fullLayout + */ +plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLayout) { + var fullLayout = gd._fullLayout; + var transitionOpts = fullLayout.transition; + var opts = {}; + var axEdits = []; + + opts.prepareFn = function() { + var subplots = fullLayout._plots; + + // no need to redraw at end of transition, + // if all changes are animatable + opts.redraw = false; + if(restyleFlags.anim === 'some') opts.redraw = true; + if(relayoutFlags.anim === 'some') opts.redraw = true; + + for(var k in subplots) { + var plotinfo = subplots[k]; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var xr0 = oldFullLayout[xa._name].range.slice(); + var yr0 = oldFullLayout[ya._name].range.slice(); + var xr1 = xa.range.slice(); + var yr1 = ya.range.slice(); + + xa.setScale(); + ya.setScale(); + + var editX = null; + var editY = null; + + if(xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) { + editX = {xr0: xr0, xr1: xr1}; + } + if(ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) { + editY = {yr0: yr0, yr1: yr1}; + } + + if(editX || editY) { + axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY)); + } + } + + return Promise.resolve(); + }; + + opts.runFn = function(makeCallback) { + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + var basePlotModules = fullLayout._basePlotModules; + + var axisTransitionOpts; + var traceTransitionOpts; + var transitionedTraces; + + var allTraceIndices = []; + for(var i = 0; i < fullData.length; i++) { + allTraceIndices.push(i); + } + + function transitionAxes() { + if(!gd._fullLayout) return; + for(var j = 0; j < basePlotModules.length; j++) { + if(basePlotModules[j].transitionAxes) { + basePlotModules[j].transitionAxes(gd, axEdits, axisTransitionOpts, makeCallback); + } + } + } + + function transitionTraces() { + if(!gd._fullLayout) return; + for(var j = 0; j < basePlotModules.length; j++) { + basePlotModules[j].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback); + } + } + + if(axEdits.length && restyleFlags.anim) { + if(transitionOpts.ordering === 'traces first') { + axisTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0}); + transitionedTraces = allTraceIndices; + traceTransitionOpts = transitionOpts; + setTimeout(transitionAxes, transitionOpts.duration); + transitionTraces(); + } else { + axisTransitionOpts = transitionOpts; + transitionedTraces = null; + traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0}); + setTimeout(transitionTraces, axisTransitionOpts.duration); + transitionAxes(); + } + } else if(axEdits.length) { + axisTransitionOpts = transitionOpts; + transitionAxes(); + } else if(restyleFlags.anim) { + transitionedTraces = allTraceIndices; + traceTransitionOpts = transitionOpts; + transitionTraces(); + } + }; + + return _transition(gd, transitionOpts, opts); +}; + +/** + * trace/layout transition wrapper that works + * for transitions initiated by Plotly.animate and Plotly.react. + * + * @param {DOM element} gd + * @param {object} transitionOpts + * @param {object} opts + * - redraw {boolean} + * - prepareFn {function} *should return a Promise* + * - runFn {function} ran inside executeTransitions + */ +function _transition(gd, transitionOpts, opts) { + var aborted = false; + + function executeCallbacks(list) { + var p = Promise.resolve(); + if(!list) return p; + while(list.length) { + p = p.then((list.shift())); + } + return p; + } + + function flushCallbacks(list) { + if(!list) return; + while(list.length) { + list.shift(); + } + } + + function executeTransitions() { + gd.emit('plotly_transitioning', []); + + return new Promise(function(resolve) { + // This flag is used to disabled things like autorange: + gd._transitioning = true; + + // When instantaneous updates are coming through quickly, it's too much to simply disable + // all interaction, so store this flag so we can disambiguate whether mouse interactions + // should be fully disabled or not: + if(transitionOpts.duration > 0) { + gd._transitioningWithDuration = true; + } + + // If another transition is triggered, this callback will be executed simply because it's + // in the interruptCallbacks queue. If this transition completes, it will instead flush + // that queue and forget about this callback. + gd._transitionData._interruptCallbacks.push(function() { + aborted = true; + }); + + if(opts.redraw) { + gd._transitionData._interruptCallbacks.push(function() { + return Registry.call('redraw', gd); + }); + } + + // Emit this and make sure it happens last: + gd._transitionData._interruptCallbacks.push(function() { + gd.emit('plotly_transitioninterrupted', []); + }); + + // Construct callbacks that are executed on transition end. This ensures the d3 transitions + // are *complete* before anything else is done. + var numCallbacks = 0; + var numCompleted = 0; + function makeCallback() { + numCallbacks++; + return function() { + numCompleted++; + // When all are complete, perform a redraw: + if(!aborted && numCompleted === numCallbacks) { + completeTransition(resolve); + } + }; + } + + opts.runFn(makeCallback); + + // If nothing else creates a callback, then this will trigger the completion in the next tick: + setTimeout(makeCallback()); + }); + } + + function completeTransition(callback) { + // This a simple workaround for tests which purge the graph before animations + // have completed. That's not a very common case, so this is the simplest + // fix. + if(!gd._transitionData) return; + + flushCallbacks(gd._transitionData._interruptCallbacks); + + return Promise.resolve().then(function() { + if(opts.redraw) { + return Registry.call('redraw', gd); + } + }).then(function() { + // Set transitioning false again once the redraw has occurred. This is used, for example, + // to prevent the trailing redraw from autoranging: + gd._transitioning = false; + gd._transitioningWithDuration = false; + + gd.emit('plotly_transitioned', []); + }).then(callback); + } + + function interruptPreviousTransitions() { + // Fail-safe against purged plot: + if(!gd._transitionData) return; + + // If a transition is interrupted, set this to false. At the moment, the only thing that would + // interrupt a transition is another transition, so that it will momentarily be set to true + // again, but this determines whether autorange or dragbox work, so it's for the sake of + // cleanliness: + gd._transitioning = false; + + return executeCallbacks(gd._transitionData._interruptCallbacks); + } + + var seq = [ + plots.previousPromises, + interruptPreviousTransitions, + opts.prepareFn, + plots.rehover, + executeTransitions + ]; + + var transitionStarting = Lib.syncOrAsync(seq, gd); + + if(!transitionStarting || !transitionStarting.then) { + transitionStarting = Promise.resolve(); + } + + return transitionStarting.then(function() { return gd; }); +} + +plots.doCalcdata = function(gd, traces) { + var axList = axisIDs.list(gd); + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + + var trace, _module, i, j; + + // XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without + // *all* needing doCalcdata: + var calcdata = new Array(fullData.length); + var oldCalcdata = (gd.calcdata || []).slice(); + gd.calcdata = calcdata; + + // extra helper variables + + // how many box/violins plots do we have (in case they're grouped) + fullLayout._numBoxes = 0; + fullLayout._numViolins = 0; + + // initialize violin per-scale-group stats container + fullLayout._violinScaleGroupStats = {}; + + // for calculating avg luminosity of heatmaps + gd._hmpixcount = 0; + gd._hmlumcount = 0; + + // for sharing colors across pies / sunbursts / treemap / icicle / funnelarea (and for legend) + fullLayout._piecolormap = {}; + fullLayout._sunburstcolormap = {}; + fullLayout._treemapcolormap = {}; + fullLayout._iciclecolormap = {}; + fullLayout._funnelareacolormap = {}; + + // If traces were specified and this trace was not included, + // then transfer it over from the old calcdata: + for(i = 0; i < fullData.length; i++) { + if(Array.isArray(traces) && traces.indexOf(i) === -1) { + calcdata[i] = oldCalcdata[i]; + continue; + } + } + + for(i = 0; i < fullData.length; i++) { + trace = fullData[i]; + + trace._arrayAttrs = PlotSchema.findArrayAttributes(trace); + + // keep track of trace extremes (for autorange) in here + trace._extremes = {}; + } + + // add polar axes to axis list + var polarIds = fullLayout._subplots.polar || []; + for(i = 0; i < polarIds.length; i++) { + axList.push( + fullLayout[polarIds[i]].radialaxis, + fullLayout[polarIds[i]].angularaxis + ); + } + + // clear relinked cmin/cmax values in shared axes to start aggregation from scratch + for(var k in fullLayout._colorAxes) { + var cOpts = fullLayout[k]; + if(cOpts.cauto !== false) { + delete cOpts.cmin; + delete cOpts.cmax; + } + } + + var hasCalcTransform = false; + + function transformCalci(i) { + trace = fullData[i]; + _module = trace._module; + + if(trace.visible === true && trace.transforms) { + // we need one round of trace module calc before + // the calc transform to 'fill in' the categories list + // used for example in the data-to-coordinate method + if(_module && _module.calc) { + var cdi = _module.calc(gd, trace); + + // must clear scene 'batches', so that 2nd + // _module.calc call starts from scratch + if(cdi[0] && cdi[0].t && cdi[0].t._scene) { + delete cdi[0].t._scene.dirty; + } + } + + for(j = 0; j < trace.transforms.length; j++) { + var transform = trace.transforms[j]; + + _module = transformsRegistry[transform.type]; + if(_module && _module.calcTransform) { + trace._hasCalcTransform = true; + hasCalcTransform = true; + _module.calcTransform(gd, trace, transform); + } + } + } + } + + function calci(i, isContainer) { + trace = fullData[i]; + _module = trace._module; + + if(!!_module.isContainer !== isContainer) return; + + var cd = []; + + if(trace.visible === true && trace._length !== 0) { + // clear existing ref in case it got relinked + delete trace._indexToPoints; + // keep ref of index-to-points map object of the *last* enabled transform, + // this index-to-points map object is required to determine the calcdata indices + // that correspond to input indices (e.g. from 'selectedpoints') + var transforms = trace.transforms || []; + for(j = transforms.length - 1; j >= 0; j--) { + if(transforms[j].enabled) { + trace._indexToPoints = transforms[j]._indexToPoints; + break; + } + } + + if(_module && _module.calc) { + cd = _module.calc(gd, trace); + } + } + + // Make sure there is a first point. + // + // This ensures there is a calcdata item for every trace, + // even if cartesian logic doesn't handle it (for things like legends). + if(!Array.isArray(cd) || !cd[0]) { + cd = [{x: BADNUM, y: BADNUM}]; + } + + // add the trace-wide properties to the first point, + // per point properties to every point + // t is the holder for trace-wide properties + if(!cd[0].t) cd[0].t = {}; + cd[0].trace = trace; + + calcdata[i] = cd; + } + + setupAxisCategories(axList, fullData, fullLayout); + + // 'transform' loop - must calc container traces first + // so that if their dependent traces can get transform properly + for(i = 0; i < fullData.length; i++) calci(i, true); + for(i = 0; i < fullData.length; i++) transformCalci(i); + + // clear stuff that should recomputed in 'regular' loop + if(hasCalcTransform) setupAxisCategories(axList, fullData, fullLayout); + + // 'regular' loop - make sure container traces (eg carpet) calc before + // contained traces (eg contourcarpet) + for(i = 0; i < fullData.length; i++) calci(i, true); + for(i = 0; i < fullData.length; i++) calci(i, false); + + doCrossTraceCalc(gd); + + // Sort axis categories per value if specified + var sorted = sortAxisCategoriesByValue(axList, gd); + if(sorted.length) { + // how many box/violins plots do we have (in case they're grouped) + fullLayout._numBoxes = 0; + fullLayout._numViolins = 0; + // If a sort operation was performed, run calc() again + for(i = 0; i < sorted.length; i++) calci(sorted[i], true); + for(i = 0; i < sorted.length; i++) calci(sorted[i], false); + doCrossTraceCalc(gd); + } + + Registry.getComponentMethod('fx', 'calc')(gd); + Registry.getComponentMethod('errorbars', 'calc')(gd); +}; + +var sortAxisCategoriesByValueRegex = /(total|sum|min|max|mean|median) (ascending|descending)/; + +function sortAxisCategoriesByValue(axList, gd) { + var affectedTraces = []; + var i, j, k, l, o; + + function zMapCategory(type, ax, value) { + var axLetter = ax._id.charAt(0); + if(type === 'histogram2dcontour') { + var counterAxLetter = ax._counterAxes[0]; + var counterAx = axisIDs.getFromId(gd, counterAxLetter); + + var xCategorical = axLetter === 'x' || (counterAxLetter === 'x' && counterAx.type === 'category'); + var yCategorical = axLetter === 'y' || (counterAxLetter === 'y' && counterAx.type === 'category'); + + return function(o, l) { + if(o === 0 || l === 0) return -1; // Skip first row and column + if(xCategorical && o === value[l].length - 1) return -1; + if(yCategorical && l === value.length - 1) return -1; + + return (axLetter === 'y' ? l : o) - 1; + }; + } else { + return function(o, l) { + return axLetter === 'y' ? l : o; + }; + } + } + + var aggFn = { + 'min': function(values) {return Lib.aggNums(Math.min, null, values);}, + 'max': function(values) {return Lib.aggNums(Math.max, null, values);}, + 'sum': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);}, + 'total': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);}, + 'mean': function(values) {return Lib.mean(values);}, + 'median': function(values) {return Lib.median(values);} + }; + + for(i = 0; i < axList.length; i++) { + var ax = axList[i]; + if(ax.type !== 'category') continue; + + // Order by value + var match = ax.categoryorder.match(sortAxisCategoriesByValueRegex); + if(match) { + var aggregator = match[1]; + var order = match[2]; + + var axLetter = ax._id.charAt(0); + var isX = axLetter === 'x'; + + // Store values associated with each category + var categoriesValue = []; + for(j = 0; j < ax._categories.length; j++) { + categoriesValue.push([ax._categories[j], []]); + } + + // Collect values across traces + for(j = 0; j < ax._traceIndices.length; j++) { + var traceIndex = ax._traceIndices[j]; + var fullTrace = gd._fullData[traceIndex]; + + // Skip over invisible traces + if(fullTrace.visible !== true) continue; + + var type = fullTrace.type; + if(Registry.traceIs(fullTrace, 'histogram')) { + delete fullTrace._xautoBinFinished; + delete fullTrace._yautoBinFinished; + } + var isSplom = type === 'splom'; + var isScattergl = type === 'scattergl'; + + var cd = gd.calcdata[traceIndex]; + for(k = 0; k < cd.length; k++) { + var cdi = cd[k]; + var catIndex, value; + + if(isSplom) { + // If `splom`, collect values across dimensions + // Find which dimension the current axis is representing + var currentDimensionIndex = fullTrace._axesDim[ax._id]; + + // Apply logic to associated x axis if it's defined + if(!isX) { + var associatedXAxisID = fullTrace._diag[currentDimensionIndex][0]; + if(associatedXAxisID) ax = gd._fullLayout[axisIDs.id2name(associatedXAxisID)]; + } + + var categories = cdi.trace.dimensions[currentDimensionIndex].values; + for(l = 0; l < categories.length; l++) { + catIndex = ax._categoriesMap[categories[l]]; + + // Collect associated values at index `l` over all other dimensions + for(o = 0; o < cdi.trace.dimensions.length; o++) { + if(o === currentDimensionIndex) continue; + var dimension = cdi.trace.dimensions[o]; + categoriesValue[catIndex][1].push(dimension.values[l]); + } + } + } else if(isScattergl) { + // If `scattergl`, collect all values stashed under cdi.t + for(l = 0; l < cdi.t.x.length; l++) { + if(isX) { + catIndex = cdi.t.x[l]; + value = cdi.t.y[l]; + } else { + catIndex = cdi.t.y[l]; + value = cdi.t.x[l]; + } + categoriesValue[catIndex][1].push(value); + } + // must clear scene 'batches', so that 2nd + // _module.calc call starts from scratch + if(cdi.t && cdi.t._scene) { + delete cdi.t._scene.dirty; + } + } else if(cdi.hasOwnProperty('z')) { + // If 2dMap, collect values in `z` + value = cdi.z; + var mapping = zMapCategory(fullTrace.type, ax, value); + + for(l = 0; l < value.length; l++) { + for(o = 0; o < value[l].length; o++) { + catIndex = mapping(o, l); + if(catIndex + 1) categoriesValue[catIndex][1].push(value[l][o]); + } + } + } else { + // For all other 2d cartesian traces + catIndex = cdi.p; + if(catIndex === undefined) catIndex = cdi[axLetter]; + + value = cdi.s; + if(value === undefined) value = cdi.v; + if(value === undefined) value = isX ? cdi.y : cdi.x; + + if(!Array.isArray(value)) { + if(value === undefined) value = []; + else value = [value]; + } + for(l = 0; l < value.length; l++) { + categoriesValue[catIndex][1].push(value[l]); + } + } + } + } + + ax._categoriesValue = categoriesValue; + + var categoriesAggregatedValue = []; + for(j = 0; j < categoriesValue.length; j++) { + categoriesAggregatedValue.push([ + categoriesValue[j][0], + aggFn[aggregator](categoriesValue[j][1]) + ]); + } + + // Sort by aggregated value + categoriesAggregatedValue.sort(function(a, b) { + return a[1] - b[1]; + }); + + ax._categoriesAggregatedValue = categoriesAggregatedValue; + + // Set new category order + ax._initialCategories = categoriesAggregatedValue.map(function(c) { + return c[0]; + }); + + // Reverse if descending + if(order === 'descending') { + ax._initialCategories.reverse(); + } + + // Sort all matching axes + affectedTraces = affectedTraces.concat(ax.sortByInitialCategories()); + } + } + return affectedTraces; +} + +function setupAxisCategories(axList, fullData, fullLayout) { + var axLookup = {}; + + function setupOne(ax) { + ax.clearCalc(); + if(ax.type === 'multicategory') { + ax.setupMultiCategory(fullData); + } + + axLookup[ax._id] = 1; + } + + Lib.simpleMap(axList, setupOne); + + // look into match groups for 'missing' axes + var matchGroups = fullLayout._axisMatchGroups || []; + for(var i = 0; i < matchGroups.length; i++) { + for(var axId in matchGroups[i]) { + if(!axLookup[axId]) { + setupOne(fullLayout[axisIDs.id2name(axId)]); + } + } + } +} + +function doCrossTraceCalc(gd) { + var fullLayout = gd._fullLayout; + var modules = fullLayout._visibleModules; + var hash = {}; + var i, j, k; + + // position and range calculations for traces that + // depend on each other ie bars (stacked or grouped) + // and boxes (grouped) push each other out of the way + + for(j = 0; j < modules.length; j++) { + var _module = modules[j]; + var fn = _module.crossTraceCalc; + if(fn) { + var spType = _module.basePlotModule.name; + if(hash[spType]) { + Lib.pushUnique(hash[spType], fn); + } else { + hash[spType] = [fn]; + } + } + } + + for(k in hash) { + var methods = hash[k]; + var subplots = fullLayout._subplots[k]; + + if(Array.isArray(subplots)) { + for(i = 0; i < subplots.length; i++) { + var sp = subplots[i]; + var spInfo = k === 'cartesian' ? + fullLayout._plots[sp] : + fullLayout[sp]; + + for(j = 0; j < methods.length; j++) { + methods[j](gd, spInfo, sp); + } + } + } else { + for(j = 0; j < methods.length; j++) { + methods[j](gd); + } + } + } +} + +plots.rehover = function(gd) { + if(gd._fullLayout._rehover) { + gd._fullLayout._rehover(); + } +}; + +plots.redrag = function(gd) { + if(gd._fullLayout._redrag) { + gd._fullLayout._redrag(); + } +}; + +plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) { + var traceHashOld = subplot.traceHash; + var traceHash = {}; + var i; + + // build up moduleName -> calcData hash + for(i = 0; i < subplotCalcData.length; i++) { + var calcTraces = subplotCalcData[i]; + var trace = calcTraces[0].trace; + + // skip over visible === false traces + // as they don't have `_module` ref + if(trace.visible) { + traceHash[trace.type] = traceHash[trace.type] || []; + traceHash[trace.type].push(calcTraces); + } + } + + // when a trace gets deleted, make sure that its module's + // plot method is called so that it is properly + // removed from the DOM. + for(var moduleNameOld in traceHashOld) { + if(!traceHash[moduleNameOld]) { + var fakeCalcTrace = traceHashOld[moduleNameOld][0]; + var fakeTrace = fakeCalcTrace[0].trace; + + fakeTrace.visible = false; + traceHash[moduleNameOld] = [fakeCalcTrace]; + } + } + + // call module plot method + for(var moduleName in traceHash) { + var moduleCalcData = traceHash[moduleName]; + var _module = moduleCalcData[0][0].trace._module; + + _module.plot(gd, subplot, Lib.filterVisible(moduleCalcData), subplotLayout); + } + + // update moduleName -> calcData hash + subplot.traceHash = traceHash; +}; + +plots.plotBasePlot = function(desiredType, gd, traces, transitionOpts, makeOnCompleteCallback) { + var _module = Registry.getModule(desiredType); + var cdmodule = getModuleCalcData(gd.calcdata, _module)[0]; + _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback); +}; + +plots.cleanBasePlot = function(desiredType, newFullData, newFullLayout, oldFullData, oldFullLayout) { + var had = (oldFullLayout._has && oldFullLayout._has(desiredType)); + var has = (newFullLayout._has && newFullLayout._has(desiredType)); + + if(had && !has) { + oldFullLayout['_' + desiredType + 'layer'].selectAll('g.trace').remove(); + } +}; + +},{"../components/color":155,"../constants/numerical":265,"../lib":285,"../plot_api/plot_schema":319,"../plot_api/plot_template":320,"../plots/get_data":362,"../registry":373,"./animation_attributes":325,"./attributes":327,"./cartesian/axis_ids":335,"./cartesian/handle_outline":342,"./command":358,"./font_attributes":360,"./frame_attributes":361,"./layout_attributes":364,"@plotly/d3":20,"d3-time-format":29,"fast-isnumeric":31}],367:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var Template = _dereq_('../plot_api/plot_template'); +var handleDomainDefaults = _dereq_('./domain').defaults; + + +/** + * Find and supply defaults to all subplots of a given type + * This handles subplots that are contained within one container - so + * gl3d, geo, ternary... but not 2d axes which have separate x and y axes + * finds subplots, coerces their `domain` attributes, then calls the + * given handleDefaults function to fill in everything else. + * + * layoutIn: the complete user-supplied input layout + * layoutOut: the complete finished layout + * fullData: the finished data array, used only to find subplots + * opts: { + * type: subplot type string + * attributes: subplot attributes object + * partition: 'x' or 'y', which direction to divide domain space by default + * (default 'x', ie side-by-side subplots) + * TODO: this option is only here because 3D and geo made opposite + * choices in this regard previously and I didn't want to change it. + * Instead we should do: + * - something consistent + * - something more square (4 cuts 2x2, 5/6 cuts 2x3, etc.) + * - something that includes all subplot types in one arrangement, + * now that we can have them together! + * handleDefaults: function of (subplotLayoutIn, subplotLayoutOut, coerce, opts) + * this opts object is passed through to handleDefaults, so attach any + * additional items needed by this function here as well + * } + */ +module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, opts) { + var subplotType = opts.type; + var subplotAttributes = opts.attributes; + var handleDefaults = opts.handleDefaults; + var partition = opts.partition || 'x'; + + var ids = layoutOut._subplots[subplotType]; + var idsLength = ids.length; + + var baseId = idsLength && ids[0].replace(/\d+$/, ''); + + var subplotLayoutIn, subplotLayoutOut; + + function coerce(attr, dflt) { + return Lib.coerce(subplotLayoutIn, subplotLayoutOut, subplotAttributes, attr, dflt); + } + + for(var i = 0; i < idsLength; i++) { + var id = ids[i]; + + // ternary traces get a layout ternary for free! + if(layoutIn[id]) subplotLayoutIn = layoutIn[id]; + else subplotLayoutIn = layoutIn[id] = {}; + + subplotLayoutOut = Template.newContainer(layoutOut, id, baseId); + + // All subplot containers get a `uirevision` inheriting from the base. + // Currently all subplots containers have some user interaction + // attributes, but if we ever add one that doesn't, we would need an + // option to skip this step. + coerce('uirevision', layoutOut.uirevision); + + var dfltDomains = {}; + dfltDomains[partition] = [i / idsLength, (i + 1) / idsLength]; + handleDomainDefaults(subplotLayoutOut, layoutOut, coerce, dfltDomains); + + opts.id = id; + handleDefaults(subplotLayoutIn, subplotLayoutOut, coerce, opts); + } +}; + +},{"../lib":285,"../plot_api/plot_template":320,"./domain":359}],368:[function(_dereq_,module,exports){ +'use strict'; + +var docs = _dereq_('../constants/docs'); +var FORMAT_LINK = docs.FORMAT_LINK; +var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK; + +function templateFormatStringDescription(opts) { + var supportOther = opts && opts.supportOther; + + return [ + 'Variables are inserted using %{variable},', + 'for example "y: %{y}"' + ( + supportOther ? + ' as well as %{xother}, {%_xother}, {%_xother_}, {%xother_}. When showing info for several points, *xother* will be added to those with different x positions from the first point. An underscore before or after *(x|y)other* will add a space on that side, only when this field is shown.' : + '.' + ), + 'Numbers are formatted using d3-format\'s syntax %{variable:d3-format}, for example "Price: %{y:$.2f}".', + FORMAT_LINK, + 'for details on the formatting syntax.', + 'Dates are formatted using d3-time-format\'s syntax %{variable|d3-time-format}, for example "Day: %{2019-01-01|%A}".', + DATE_FORMAT_LINK, + 'for details on the date formatting syntax.' + ].join(' '); +} + +function describeVariables(extra) { + var descPart = extra.description ? ' ' + extra.description : ''; + var keys = extra.keys || []; + if(keys.length > 0) { + var quotedKeys = []; + for(var i = 0; i < keys.length; i++) { + quotedKeys[i] = '`' + keys[i] + '`'; + } + descPart = descPart + 'Finally, the template string has access to '; + if(keys.length === 1) { + descPart = 'variable ' + quotedKeys[0]; + } else { + descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.'; + } + } + return descPart; +} + +exports.hovertemplateAttrs = function(opts, extra) { + opts = opts || {}; + extra = extra || {}; + + var descPart = describeVariables(extra); + + var hovertemplate = { + valType: 'string', + dflt: '', + editType: opts.editType || 'none', + }; + + if(opts.arrayOk !== false) { + hovertemplate.arrayOk = true; + } + + return hovertemplate; +}; + +exports.texttemplateAttrs = function(opts, extra) { + opts = opts || {}; + extra = extra || {}; + + var descPart = describeVariables(extra); + + var texttemplate = { + valType: 'string', + dflt: '', + editType: opts.editType || 'calc', + }; + + if(opts.arrayOk !== false) { + texttemplate.arrayOk = true; + } + return texttemplate; +}; + +},{"../constants/docs":262}],369:[function(_dereq_,module,exports){ +'use strict'; + +var Ternary = _dereq_('./ternary'); + +var getSubplotCalcData = _dereq_('../../plots/get_data').getSubplotCalcData; +var counterRegex = _dereq_('../../lib').counterRegex; +var TERNARY = 'ternary'; + +exports.name = TERNARY; + +var attr = exports.attr = 'subplot'; + +exports.idRoot = TERNARY; + +exports.idRegex = exports.attrRegex = counterRegex(TERNARY); + +var attributes = exports.attributes = {}; +attributes[attr] = { + valType: 'subplotid', + dflt: 'ternary', + editType: 'calc', +}; + +exports.layoutAttributes = _dereq_('./layout_attributes'); + +exports.supplyLayoutDefaults = _dereq_('./layout_defaults'); + +exports.plot = function plot(gd) { + var fullLayout = gd._fullLayout; + var calcData = gd.calcdata; + var ternaryIds = fullLayout._subplots[TERNARY]; + + for(var i = 0; i < ternaryIds.length; i++) { + var ternaryId = ternaryIds[i]; + var ternaryCalcData = getSubplotCalcData(calcData, TERNARY, ternaryId); + var ternary = fullLayout[ternaryId]._subplot; + + // If ternary is not instantiated, create one! + if(!ternary) { + ternary = new Ternary({ + id: ternaryId, + graphDiv: gd, + container: fullLayout._ternarylayer.node() + }, + fullLayout + ); + + fullLayout[ternaryId]._subplot = ternary; + } + + ternary.plot(ternaryCalcData, fullLayout, gd._promises); + } +}; + +exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + var oldTernaryKeys = oldFullLayout._subplots[TERNARY] || []; + + for(var i = 0; i < oldTernaryKeys.length; i++) { + var oldTernaryKey = oldTernaryKeys[i]; + var oldTernary = oldFullLayout[oldTernaryKey]._subplot; + + if(!newFullLayout[oldTernaryKey] && !!oldTernary) { + oldTernary.plotContainer.remove(); + oldTernary.clipDef.remove(); + oldTernary.clipDefRelative.remove(); + oldTernary.layers['a-title'].remove(); + oldTernary.layers['b-title'].remove(); + oldTernary.layers['c-title'].remove(); + } + } +}; + +},{"../../lib":285,"../../plots/get_data":362,"./layout_attributes":370,"./layout_defaults":371,"./ternary":372}],370:[function(_dereq_,module,exports){ +'use strict'; + +var colorAttrs = _dereq_('../../components/color/attributes'); +var domainAttrs = _dereq_('../domain').attributes; +var axesAttrs = _dereq_('../cartesian/layout_attributes'); + +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var ternaryAxesAttrs = { + title: { + text: axesAttrs.title.text, + font: axesAttrs.title.font + // TODO does standoff here make sense? + }, + color: axesAttrs.color, + // ticks + tickmode: axesAttrs.tickmode, + nticks: extendFlat({}, axesAttrs.nticks, {dflt: 6, min: 1}), + tick0: axesAttrs.tick0, + dtick: axesAttrs.dtick, + tickvals: axesAttrs.tickvals, + ticktext: axesAttrs.ticktext, + ticks: axesAttrs.ticks, + ticklen: axesAttrs.ticklen, + tickwidth: axesAttrs.tickwidth, + tickcolor: axesAttrs.tickcolor, + showticklabels: axesAttrs.showticklabels, + showtickprefix: axesAttrs.showtickprefix, + tickprefix: axesAttrs.tickprefix, + showticksuffix: axesAttrs.showticksuffix, + ticksuffix: axesAttrs.ticksuffix, + showexponent: axesAttrs.showexponent, + exponentformat: axesAttrs.exponentformat, + minexponent: axesAttrs.minexponent, + separatethousands: axesAttrs.separatethousands, + tickfont: axesAttrs.tickfont, + tickangle: axesAttrs.tickangle, + tickformat: axesAttrs.tickformat, + tickformatstops: axesAttrs.tickformatstops, + hoverformat: axesAttrs.hoverformat, + // lines and grids + showline: extendFlat({}, axesAttrs.showline, {dflt: true}), + linecolor: axesAttrs.linecolor, + linewidth: axesAttrs.linewidth, + showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}), + gridcolor: axesAttrs.gridcolor, + gridwidth: axesAttrs.gridwidth, + layer: axesAttrs.layer, + // range + min: { + valType: 'number', + dflt: 0, + min: 0, + }, + _deprecated: { + title: axesAttrs._deprecated.title, + titlefont: axesAttrs._deprecated.titlefont + } +}; + +var attrs = module.exports = overrideAll({ + domain: domainAttrs({name: 'ternary'}), + + bgcolor: { + valType: 'color', + dflt: colorAttrs.background, + }, + sum: { + valType: 'number', + dflt: 1, + min: 0, + }, + aaxis: ternaryAxesAttrs, + baxis: ternaryAxesAttrs, + caxis: ternaryAxesAttrs +}, 'plot', 'from-root'); + +// set uirevisions outside of `overrideAll` so we can get `editType: none` +attrs.uirevision = { + valType: 'any', + editType: 'none', +}; + +attrs.aaxis.uirevision = attrs.baxis.uirevision = attrs.caxis.uirevision = { + valType: 'any', + editType: 'none', +}; + +},{"../../components/color/attributes":154,"../../lib/extend":279,"../../plot_api/edit_types":313,"../cartesian/layout_attributes":346,"../domain":359}],371:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); +var Template = _dereq_('../../plot_api/plot_template'); +var Lib = _dereq_('../../lib'); + +var handleSubplotDefaults = _dereq_('../subplot_defaults'); +var handleTickLabelDefaults = _dereq_('../cartesian/tick_label_defaults'); +var handleTickMarkDefaults = _dereq_('../cartesian/tick_mark_defaults'); +var handleTickValueDefaults = _dereq_('../cartesian/tick_value_defaults'); +var handleLineGridDefaults = _dereq_('../cartesian/line_grid_defaults'); +var layoutAttributes = _dereq_('./layout_attributes'); + +var axesNames = ['aaxis', 'baxis', 'caxis']; + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { + handleSubplotDefaults(layoutIn, layoutOut, fullData, { + type: 'ternary', + attributes: layoutAttributes, + handleDefaults: handleTernaryDefaults, + font: layoutOut.font, + paper_bgcolor: layoutOut.paper_bgcolor + }); +}; + +function handleTernaryDefaults(ternaryLayoutIn, ternaryLayoutOut, coerce, options) { + var bgColor = coerce('bgcolor'); + var sum = coerce('sum'); + options.bgColor = Color.combine(bgColor, options.paper_bgcolor); + var axName, containerIn, containerOut; + + // TODO: allow most (if not all) axis attributes to be set + // in the outer container and used as defaults in the individual axes? + + for(var j = 0; j < axesNames.length; j++) { + axName = axesNames[j]; + containerIn = ternaryLayoutIn[axName] || {}; + containerOut = Template.newContainer(ternaryLayoutOut, axName); + containerOut._name = axName; + + handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut); + } + + // if the min values contradict each other, set them all to default (0) + // and delete *all* the inputs so the user doesn't get confused later by + // changing one and having them all change. + var aaxis = ternaryLayoutOut.aaxis; + var baxis = ternaryLayoutOut.baxis; + var caxis = ternaryLayoutOut.caxis; + if(aaxis.min + baxis.min + caxis.min >= sum) { + aaxis.min = 0; + baxis.min = 0; + caxis.min = 0; + if(ternaryLayoutIn.aaxis) delete ternaryLayoutIn.aaxis.min; + if(ternaryLayoutIn.baxis) delete ternaryLayoutIn.baxis.min; + if(ternaryLayoutIn.caxis) delete ternaryLayoutIn.caxis.min; + } +} + +function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut) { + var axAttrs = layoutAttributes[containerOut._name]; + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, axAttrs, attr, dflt); + } + + coerce('uirevision', ternaryLayoutOut.uirevision); + + containerOut.type = 'linear'; // no other types allowed for ternary + + var dfltColor = coerce('color'); + // if axis.color was provided, use it for fonts too; otherwise, + // inherit from global font color in case that was provided. + var dfltFontColor = (dfltColor !== axAttrs.color.dflt) ? dfltColor : options.font.color; + + var axName = containerOut._name; + var letterUpper = axName.charAt(0).toUpperCase(); + var dfltTitle = 'Component ' + letterUpper; + + var title = coerce('title.text', dfltTitle); + containerOut._hovertitle = title === dfltTitle ? title : letterUpper; + + Lib.coerceFont(coerce, 'title.font', { + family: options.font.family, + size: Lib.bigFont(options.font.size), + color: dfltFontColor + }); + + // range is just set by 'min' - max is determined by the other axes mins + coerce('min'); + + handleTickValueDefaults(containerIn, containerOut, coerce, 'linear'); + handleTickLabelDefaults(containerIn, containerOut, coerce, 'linear', {}); + handleTickMarkDefaults(containerIn, containerOut, coerce, + { outerTicks: true }); + + var showTickLabels = coerce('showticklabels'); + if(showTickLabels) { + Lib.coerceFont(coerce, 'tickfont', { + family: options.font.family, + size: options.font.size, + color: dfltFontColor + }); + coerce('tickangle'); + coerce('tickformat'); + } + + handleLineGridDefaults(containerIn, containerOut, coerce, { + dfltColor: dfltColor, + bgColor: options.bgColor, + // default grid color is darker here (60%, vs cartesian default ~91%) + // because the grid is not square so the eye needs heavier cues to follow + blend: 60, + showLine: true, + showGrid: true, + noZeroLine: true, + attributes: axAttrs + }); + + coerce('hoverformat'); + coerce('layer'); +} + +},{"../../components/color":155,"../../lib":285,"../../plot_api/plot_template":320,"../cartesian/line_grid_defaults":348,"../cartesian/tick_label_defaults":353,"../cartesian/tick_mark_defaults":354,"../cartesian/tick_value_defaults":355,"../subplot_defaults":367,"./layout_attributes":370}],372:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var tinycolor = _dereq_('tinycolor2'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var _ = Lib._; +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var setConvert = _dereq_('../cartesian/set_convert'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var Plots = _dereq_('../plots'); +var Axes = _dereq_('../cartesian/axes'); +var dragElement = _dereq_('../../components/dragelement'); +var Fx = _dereq_('../../components/fx'); +var dragHelpers = _dereq_('../../components/dragelement/helpers'); +var freeMode = dragHelpers.freeMode; +var rectMode = dragHelpers.rectMode; +var Titles = _dereq_('../../components/titles'); +var prepSelect = _dereq_('../cartesian/select').prepSelect; +var selectOnClick = _dereq_('../cartesian/select').selectOnClick; +var clearSelect = _dereq_('../cartesian/select').clearSelect; +var clearSelectionsCache = _dereq_('../cartesian/select').clearSelectionsCache; +var constants = _dereq_('../cartesian/constants'); + +function Ternary(options, fullLayout) { + this.id = options.id; + this.graphDiv = options.graphDiv; + this.init(fullLayout); + this.makeFramework(fullLayout); + + // unfortunately, we have to keep track of some axis tick settings + // as ternary subplots do not implement the 'ticks' editType + this.aTickLayout = null; + this.bTickLayout = null; + this.cTickLayout = null; +} + +module.exports = Ternary; + +var proto = Ternary.prototype; + +proto.init = function(fullLayout) { + this.container = fullLayout._ternarylayer; + this.defs = fullLayout._defs; + this.layoutId = fullLayout._uid; + this.traceHash = {}; + this.layers = {}; +}; + +proto.plot = function(ternaryCalcData, fullLayout) { + var _this = this; + var ternaryLayout = fullLayout[_this.id]; + var graphSize = fullLayout._size; + + _this._hasClipOnAxisFalse = false; + for(var i = 0; i < ternaryCalcData.length; i++) { + var trace = ternaryCalcData[i][0].trace; + + if(trace.cliponaxis === false) { + _this._hasClipOnAxisFalse = true; + break; + } + } + + _this.updateLayers(ternaryLayout); + _this.adjustLayout(ternaryLayout, graphSize); + Plots.generalUpdatePerTraceModule(_this.graphDiv, _this, ternaryCalcData, ternaryLayout); + _this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor); +}; + +proto.makeFramework = function(fullLayout) { + var _this = this; + var gd = _this.graphDiv; + var ternaryLayout = fullLayout[_this.id]; + + var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id; + var clipIdRelative = _this.clipIdRelative = 'clip-relative' + _this.layoutId + _this.id; + + // clippath for this ternary subplot + _this.clipDef = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) { + s.append('path').attr('d', 'M0,0Z'); + }); + + // 'relative' clippath (i.e. no translation) for this ternary subplot + _this.clipDefRelative = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipIdRelative, function(s) { + s.append('path').attr('d', 'M0,0Z'); + }); + + // container for everything in this ternary subplot + _this.plotContainer = Lib.ensureSingle(_this.container, 'g', _this.id); + _this.updateLayers(ternaryLayout); + + Drawing.setClipUrl(_this.layers.backplot, clipId, gd); + Drawing.setClipUrl(_this.layers.grids, clipId, gd); +}; + +proto.updateLayers = function(ternaryLayout) { + var _this = this; + var layers = _this.layers; + + // inside that container, we have one container for the data, and + // one each for the three axes around it. + + var plotLayers = ['draglayer', 'plotbg', 'backplot', 'grids']; + + if(ternaryLayout.aaxis.layer === 'below traces') { + plotLayers.push('aaxis', 'aline'); + } + if(ternaryLayout.baxis.layer === 'below traces') { + plotLayers.push('baxis', 'bline'); + } + if(ternaryLayout.caxis.layer === 'below traces') { + plotLayers.push('caxis', 'cline'); + } + + plotLayers.push('frontplot'); + + if(ternaryLayout.aaxis.layer === 'above traces') { + plotLayers.push('aaxis', 'aline'); + } + if(ternaryLayout.baxis.layer === 'above traces') { + plotLayers.push('baxis', 'bline'); + } + if(ternaryLayout.caxis.layer === 'above traces') { + plotLayers.push('caxis', 'cline'); + } + + var toplevel = _this.plotContainer.selectAll('g.toplevel') + .data(plotLayers, String); + + var grids = ['agrid', 'bgrid', 'cgrid']; + + toplevel.enter().append('g') + .attr('class', function(d) { return 'toplevel ' + d; }) + .each(function(d) { + var s = d3.select(this); + layers[d] = s; + + // containers for different trace types. + // NOTE - this is different from cartesian, where all traces + // are in front of grids. Here I'm putting maps behind the grids + // so the grids will always be visible if they're requested. + // Perhaps we want that for cartesian too? + if(d === 'frontplot') { + s.append('g').classed('scatterlayer', true); + } else if(d === 'backplot') { + s.append('g').classed('maplayer', true); + } else if(d === 'plotbg') { + s.append('path').attr('d', 'M0,0Z'); + } else if(d === 'aline' || d === 'bline' || d === 'cline') { + s.append('path'); + } else if(d === 'grids') { + grids.forEach(function(d) { + layers[d] = s.append('g').classed('grid ' + d, true); + }); + } + }); + + toplevel.order(); +}; + +var whRatio = Math.sqrt(4 / 3); + +proto.adjustLayout = function(ternaryLayout, graphSize) { + var _this = this; + var domain = ternaryLayout.domain; + var xDomainCenter = (domain.x[0] + domain.x[1]) / 2; + var yDomainCenter = (domain.y[0] + domain.y[1]) / 2; + var xDomain = domain.x[1] - domain.x[0]; + var yDomain = domain.y[1] - domain.y[0]; + var wmax = xDomain * graphSize.w; + var hmax = yDomain * graphSize.h; + var sum = ternaryLayout.sum; + var amin = ternaryLayout.aaxis.min; + var bmin = ternaryLayout.baxis.min; + var cmin = ternaryLayout.caxis.min; + + var x0, y0, w, h, xDomainFinal, yDomainFinal; + + if(wmax > whRatio * hmax) { + h = hmax; + w = h * whRatio; + } else { + w = wmax; + h = w / whRatio; + } + + xDomainFinal = xDomain * w / wmax; + yDomainFinal = yDomain * h / hmax; + + x0 = graphSize.l + graphSize.w * xDomainCenter - w / 2; + y0 = graphSize.t + graphSize.h * (1 - yDomainCenter) - h / 2; + + _this.x0 = x0; + _this.y0 = y0; + _this.w = w; + _this.h = h; + _this.sum = sum; + + // set up the x and y axis objects we'll use to lay out the points + _this.xaxis = { + type: 'linear', + range: [amin + 2 * cmin - sum, sum - amin - 2 * bmin], + domain: [ + xDomainCenter - xDomainFinal / 2, + xDomainCenter + xDomainFinal / 2 + ], + _id: 'x' + }; + setConvert(_this.xaxis, _this.graphDiv._fullLayout); + _this.xaxis.setScale(); + _this.xaxis.isPtWithinRange = function(d) { + return ( + d.a >= _this.aaxis.range[0] && + d.a <= _this.aaxis.range[1] && + d.b >= _this.baxis.range[1] && + d.b <= _this.baxis.range[0] && + d.c >= _this.caxis.range[1] && + d.c <= _this.caxis.range[0] + ); + }; + + _this.yaxis = { + type: 'linear', + range: [amin, sum - bmin - cmin], + domain: [ + yDomainCenter - yDomainFinal / 2, + yDomainCenter + yDomainFinal / 2 + ], + _id: 'y' + }; + setConvert(_this.yaxis, _this.graphDiv._fullLayout); + _this.yaxis.setScale(); + _this.yaxis.isPtWithinRange = function() { return true; }; + + // set up the modified axes for tick drawing + var yDomain0 = _this.yaxis.domain[0]; + + // aaxis goes up the left side. Set it up as a y axis, but with + // fictitious angles and domain, but then rotate and translate + // it into place at the end + var aaxis = _this.aaxis = extendFlat({}, ternaryLayout.aaxis, { + range: [amin, sum - bmin - cmin], + side: 'left', + // tickangle = 'auto' means 0 anyway for a y axis, need to coerce to 0 here + // so we can shift by 30. + tickangle: (+ternaryLayout.aaxis.tickangle || 0) - 30, + domain: [yDomain0, yDomain0 + yDomainFinal * whRatio], + anchor: 'free', + position: 0, + _id: 'y', + _length: w + }); + setConvert(aaxis, _this.graphDiv._fullLayout); + aaxis.setScale(); + + // baxis goes across the bottom (backward). We can set it up as an x axis + // without any enclosing transformation. + var baxis = _this.baxis = extendFlat({}, ternaryLayout.baxis, { + range: [sum - amin - cmin, bmin], + side: 'bottom', + domain: _this.xaxis.domain, + anchor: 'free', + position: 0, + _id: 'x', + _length: w + }); + setConvert(baxis, _this.graphDiv._fullLayout); + baxis.setScale(); + + // caxis goes down the right side. Set it up as a y axis, with + // post-transformation similar to aaxis + var caxis = _this.caxis = extendFlat({}, ternaryLayout.caxis, { + range: [sum - amin - bmin, cmin], + side: 'right', + tickangle: (+ternaryLayout.caxis.tickangle || 0) + 30, + domain: [yDomain0, yDomain0 + yDomainFinal * whRatio], + anchor: 'free', + position: 0, + _id: 'y', + _length: w + }); + setConvert(caxis, _this.graphDiv._fullLayout); + caxis.setScale(); + + var triangleClip = 'M' + x0 + ',' + (y0 + h) + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z'; + _this.clipDef.select('path').attr('d', triangleClip); + _this.layers.plotbg.select('path').attr('d', triangleClip); + + var triangleClipRelative = 'M0,' + h + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z'; + _this.clipDefRelative.select('path').attr('d', triangleClipRelative); + + var plotTransform = strTranslate(x0, y0); + _this.plotContainer.selectAll('.scatterlayer,.maplayer') + .attr('transform', plotTransform); + + _this.clipDefRelative.select('path').attr('transform', null); + + // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle + + // TODO: there's probably an easier way to handle these translations/offsets now... + var bTransform = strTranslate(x0 - baxis._offset, y0 + h); + + _this.layers.baxis.attr('transform', bTransform); + _this.layers.bgrid.attr('transform', bTransform); + + var aTransform = strTranslate(x0 + w / 2, y0) + + 'rotate(30)' + strTranslate(0, -aaxis._offset); + _this.layers.aaxis.attr('transform', aTransform); + _this.layers.agrid.attr('transform', aTransform); + + var cTransform = strTranslate(x0 + w / 2, y0) + + 'rotate(-30)' + strTranslate(0, -caxis._offset); + _this.layers.caxis.attr('transform', cTransform); + _this.layers.cgrid.attr('transform', cTransform); + + _this.drawAxes(true); + + _this.layers.aline.select('path') + .attr('d', aaxis.showline ? + 'M' + x0 + ',' + (y0 + h) + 'l' + (w / 2) + ',-' + h : 'M0,0') + .call(Color.stroke, aaxis.linecolor || '#000') + .style('stroke-width', (aaxis.linewidth || 0) + 'px'); + _this.layers.bline.select('path') + .attr('d', baxis.showline ? + 'M' + x0 + ',' + (y0 + h) + 'h' + w : 'M0,0') + .call(Color.stroke, baxis.linecolor || '#000') + .style('stroke-width', (baxis.linewidth || 0) + 'px'); + _this.layers.cline.select('path') + .attr('d', caxis.showline ? + 'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0') + .call(Color.stroke, caxis.linecolor || '#000') + .style('stroke-width', (caxis.linewidth || 0) + 'px'); + + if(!_this.graphDiv._context.staticPlot) { + _this.initInteractions(); + } + + Drawing.setClipUrl( + _this.layers.frontplot, + _this._hasClipOnAxisFalse ? null : _this.clipId, + _this.graphDiv + ); +}; + +proto.drawAxes = function(doTitles) { + var _this = this; + var gd = _this.graphDiv; + var titlesuffix = _this.id.substr(7) + 'title'; + var layers = _this.layers; + var aaxis = _this.aaxis; + var baxis = _this.baxis; + var caxis = _this.caxis; + + _this.drawAx(aaxis); + _this.drawAx(baxis); + _this.drawAx(caxis); + + if(doTitles) { + var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0, + (caxis.showticklabels ? caxis.tickfont.size * 0.75 : 0) + + (caxis.ticks === 'outside' ? caxis.ticklen * 0.87 : 0)); + var bpad = (baxis.showticklabels ? baxis.tickfont.size : 0) + + (baxis.ticks === 'outside' ? baxis.ticklen : 0) + 3; + + layers['a-title'] = Titles.draw(gd, 'a' + titlesuffix, { + propContainer: aaxis, + propName: _this.id + '.aaxis.title', + placeholder: _(gd, 'Click to enter Component A title'), + attributes: { + x: _this.x0 + _this.w / 2, + y: _this.y0 - aaxis.title.font.size / 3 - apad, + 'text-anchor': 'middle' + } + }); + layers['b-title'] = Titles.draw(gd, 'b' + titlesuffix, { + propContainer: baxis, + propName: _this.id + '.baxis.title', + placeholder: _(gd, 'Click to enter Component B title'), + attributes: { + x: _this.x0 - bpad, + y: _this.y0 + _this.h + baxis.title.font.size * 0.83 + bpad, + 'text-anchor': 'middle' + } + }); + layers['c-title'] = Titles.draw(gd, 'c' + titlesuffix, { + propContainer: caxis, + propName: _this.id + '.caxis.title', + placeholder: _(gd, 'Click to enter Component C title'), + attributes: { + x: _this.x0 + _this.w + bpad, + y: _this.y0 + _this.h + caxis.title.font.size * 0.83 + bpad, + 'text-anchor': 'middle' + } + }); + } +}; + +proto.drawAx = function(ax) { + var _this = this; + var gd = _this.graphDiv; + var axName = ax._name; + var axLetter = axName.charAt(0); + var axId = ax._id; + var axLayer = _this.layers[axName]; + var counterAngle = 30; + + var stashKey = axLetter + 'tickLayout'; + var newTickLayout = strTickLayout(ax); + if(_this[stashKey] !== newTickLayout) { + axLayer.selectAll('.' + axId + 'tick').remove(); + _this[stashKey] = newTickLayout; + } + + ax.setScale(); + + var vals = Axes.calcTicks(ax); + var valsClipped = Axes.clipEnds(ax, vals); + var transFn = Axes.makeTransTickFn(ax); + var tickSign = Axes.getTickSigns(ax)[2]; + + var caRad = Lib.deg2rad(counterAngle); + var pad = tickSign * (ax.linewidth || 1) / 2; + var len = tickSign * ax.ticklen; + var w = _this.w; + var h = _this.h; + + var tickPath = axLetter === 'b' ? + 'M0,' + pad + 'l' + (Math.sin(caRad) * len) + ',' + (Math.cos(caRad) * len) : + 'M' + pad + ',0l' + (Math.cos(caRad) * len) + ',' + (-Math.sin(caRad) * len); + + var gridPath = { + a: 'M0,0l' + h + ',-' + (w / 2), + b: 'M0,0l-' + (w / 2) + ',-' + h, + c: 'M0,0l-' + h + ',' + (w / 2) + }[axLetter]; + + Axes.drawTicks(gd, ax, { + vals: ax.ticks === 'inside' ? valsClipped : vals, + layer: axLayer, + path: tickPath, + transFn: transFn, + crisp: false + }); + + Axes.drawGrid(gd, ax, { + vals: valsClipped, + layer: _this.layers[axLetter + 'grid'], + path: gridPath, + transFn: transFn, + crisp: false + }); + + Axes.drawLabels(gd, ax, { + vals: vals, + layer: axLayer, + transFn: transFn, + labelFns: Axes.makeLabelFns(ax, 0, counterAngle) + }); +}; + +function strTickLayout(axLayout) { + return axLayout.ticks + String(axLayout.ticklen) + String(axLayout.showticklabels); +} + +// hard coded paths for zoom corners +// uses the same sizing as cartesian, length is MINZOOM/2, width is 3px +var CLEN = constants.MINZOOM / 2 + 0.87; +var BLPATH = 'm-0.87,.5h' + CLEN + 'v3h-' + (CLEN + 5.2) + + 'l' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) + + 'l2.6,1.5l-' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z'; +var BRPATH = 'm0.87,.5h-' + CLEN + 'v3h' + (CLEN + 5.2) + + 'l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) + + 'l-2.6,1.5l' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z'; +var TOPPATH = 'm0,1l' + (CLEN / 2) + ',' + (CLEN * 0.87) + + 'l2.6,-1.5l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) + + 'l-' + (CLEN / 2 + 2.6) + ',' + (CLEN * 0.87 + 4.5) + + 'l2.6,1.5l' + (CLEN / 2) + ',-' + (CLEN * 0.87) + 'Z'; +var STARTMARKER = 'm0.5,0.5h5v-2h-5v-5h-2v5h-5v2h5v5h2Z'; + +// I guess this could be shared with cartesian... but for now it's separate. +var SHOWZOOMOUTTIP = true; + +proto.clearSelect = function() { + clearSelectionsCache(this.dragOptions); + clearSelect(this.dragOptions.gd); +}; + +proto.initInteractions = function() { + var _this = this; + var dragger = _this.layers.plotbg.select('path').node(); + var gd = _this.graphDiv; + var zoomLayer = gd._fullLayout._zoomlayer; + var scaleX; + var scaleY; + + // use plotbg for the main interactions + this.dragOptions = { + element: dragger, + gd: gd, + plotinfo: { + id: _this.id, + domain: gd._fullLayout[_this.id].domain, + xaxis: _this.xaxis, + yaxis: _this.yaxis + }, + subplot: _this.id, + prepFn: function(e, startX, startY) { + // these aren't available yet when initInteractions + // is called + _this.dragOptions.xaxes = [_this.xaxis]; + _this.dragOptions.yaxes = [_this.yaxis]; + + scaleX = gd._fullLayout._invScaleX; + scaleY = gd._fullLayout._invScaleY; + + var dragModeNow = _this.dragOptions.dragmode = gd._fullLayout.dragmode; + + if(freeMode(dragModeNow)) _this.dragOptions.minDrag = 1; + else _this.dragOptions.minDrag = undefined; + + if(dragModeNow === 'zoom') { + _this.dragOptions.moveFn = zoomMove; + _this.dragOptions.clickFn = clickZoomPan; + _this.dragOptions.doneFn = zoomDone; + zoomPrep(e, startX, startY); + } else if(dragModeNow === 'pan') { + _this.dragOptions.moveFn = plotDrag; + _this.dragOptions.clickFn = clickZoomPan; + _this.dragOptions.doneFn = dragDone; + panPrep(); + _this.clearSelect(gd); + } else if(rectMode(dragModeNow) || freeMode(dragModeNow)) { + prepSelect(e, startX, startY, _this.dragOptions, dragModeNow); + } + } + }; + + var x0, y0, mins0, span0, mins, lum, path0, dimmed, zb, corners; + + function makeUpdate(_mins) { + var attrs = {}; + attrs[_this.id + '.aaxis.min'] = _mins.a; + attrs[_this.id + '.baxis.min'] = _mins.b; + attrs[_this.id + '.caxis.min'] = _mins.c; + return attrs; + } + + function clickZoomPan(numClicks, evt) { + var clickMode = gd._fullLayout.clickmode; + + removeZoombox(gd); + + if(numClicks === 2) { + gd.emit('plotly_doubleclick', null); + Registry.call('_guiRelayout', gd, makeUpdate({a: 0, b: 0, c: 0})); + } + + if(clickMode.indexOf('select') > -1 && numClicks === 1) { + selectOnClick(evt, gd, [_this.xaxis], [_this.yaxis], _this.id, _this.dragOptions); + } + + if(clickMode.indexOf('event') > -1) { + Fx.click(gd, evt, _this.id); + } + } + + function zoomPrep(e, startX, startY) { + var dragBBox = dragger.getBoundingClientRect(); + x0 = startX - dragBBox.left; + y0 = startY - dragBBox.top; + + gd._fullLayout._calcInverseTransform(gd); + var inverse = gd._fullLayout._invTransform; + var transformedCoords = Lib.apply3DTransform(inverse)(x0, y0); + x0 = transformedCoords[0]; + y0 = transformedCoords[1]; + + mins0 = { + a: _this.aaxis.range[0], + b: _this.baxis.range[1], + c: _this.caxis.range[1] + }; + mins = mins0; + span0 = _this.aaxis.range[1] - mins0.a; + lum = tinycolor(_this.graphDiv._fullLayout[_this.id].bgcolor).getLuminance(); + path0 = 'M0,' + _this.h + 'L' + (_this.w / 2) + ', 0L' + _this.w + ',' + _this.h + 'Z'; + dimmed = false; + + zb = zoomLayer.append('path') + .attr('class', 'zoombox') + .attr('transform', strTranslate(_this.x0, _this.y0)) + .style({ + 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)', + 'stroke-width': 0 + }) + .attr('d', path0); + + corners = zoomLayer.append('path') + .attr('class', 'zoombox-corners') + .attr('transform', strTranslate(_this.x0, _this.y0)) + .style({ + fill: Color.background, + stroke: Color.defaultLine, + 'stroke-width': 1, + opacity: 0 + }) + .attr('d', 'M0,0Z'); + + _this.clearSelect(gd); + } + + function getAFrac(x, y) { return 1 - (y / _this.h); } + function getBFrac(x, y) { return 1 - ((x + (_this.h - y) / Math.sqrt(3)) / _this.w); } + function getCFrac(x, y) { return ((x - (_this.h - y) / Math.sqrt(3)) / _this.w); } + + function zoomMove(dx0, dy0) { + var x1 = x0 + dx0 * scaleX; + var y1 = y0 + dy0 * scaleY; + var afrac = Math.max(0, Math.min(1, getAFrac(x0, y0), getAFrac(x1, y1))); + var bfrac = Math.max(0, Math.min(1, getBFrac(x0, y0), getBFrac(x1, y1))); + var cfrac = Math.max(0, Math.min(1, getCFrac(x0, y0), getCFrac(x1, y1))); + var xLeft = ((afrac / 2) + cfrac) * _this.w; + var xRight = (1 - (afrac / 2) - bfrac) * _this.w; + var xCenter = (xLeft + xRight) / 2; + var xSpan = xRight - xLeft; + var yBottom = (1 - afrac) * _this.h; + var yTop = yBottom - xSpan / whRatio; + + if(xSpan < constants.MINZOOM) { + mins = mins0; + zb.attr('d', path0); + corners.attr('d', 'M0,0Z'); + } else { + mins = { + a: mins0.a + afrac * span0, + b: mins0.b + bfrac * span0, + c: mins0.c + cfrac * span0 + }; + zb.attr('d', path0 + 'M' + xLeft + ',' + yBottom + + 'H' + xRight + 'L' + xCenter + ',' + yTop + + 'L' + xLeft + ',' + yBottom + 'Z'); + corners.attr('d', 'M' + x0 + ',' + y0 + STARTMARKER + + 'M' + xLeft + ',' + yBottom + BLPATH + + 'M' + xRight + ',' + yBottom + BRPATH + + 'M' + xCenter + ',' + yTop + TOPPATH); + } + + if(!dimmed) { + zb.transition() + .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' : + 'rgba(255,255,255,0.3)') + .duration(200); + corners.transition() + .style('opacity', 1) + .duration(200); + dimmed = true; + } + + gd.emit('plotly_relayouting', makeUpdate(mins)); + } + + function zoomDone() { + removeZoombox(gd); + + if(mins === mins0) return; + + Registry.call('_guiRelayout', gd, makeUpdate(mins)); + + if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) { + Lib.notifier(_(gd, 'Double-click to zoom back out'), 'long'); + SHOWZOOMOUTTIP = false; + } + } + + function panPrep() { + mins0 = { + a: _this.aaxis.range[0], + b: _this.baxis.range[1], + c: _this.caxis.range[1] + }; + mins = mins0; + } + + function plotDrag(dx, dy) { + var dxScaled = dx / _this.xaxis._m; + var dyScaled = dy / _this.yaxis._m; + mins = { + a: mins0.a - dyScaled, + b: mins0.b + (dxScaled + dyScaled) / 2, + c: mins0.c - (dxScaled - dyScaled) / 2 + }; + var minsorted = [mins.a, mins.b, mins.c].sort(Lib.sorterAsc); + var minindices = { + a: minsorted.indexOf(mins.a), + b: minsorted.indexOf(mins.b), + c: minsorted.indexOf(mins.c) + }; + if(minsorted[0] < 0) { + if(minsorted[1] + minsorted[0] / 2 < 0) { + minsorted[2] += minsorted[0] + minsorted[1]; + minsorted[0] = minsorted[1] = 0; + } else { + minsorted[2] += minsorted[0] / 2; + minsorted[1] += minsorted[0] / 2; + minsorted[0] = 0; + } + mins = { + a: minsorted[minindices.a], + b: minsorted[minindices.b], + c: minsorted[minindices.c] + }; + dy = (mins0.a - mins.a) * _this.yaxis._m; + dx = (mins0.c - mins.c - mins0.b + mins.b) * _this.xaxis._m; + } + + // move the data (translate, don't redraw) + var plotTransform = strTranslate(_this.x0 + dx, _this.y0 + dy); + _this.plotContainer.selectAll('.scatterlayer,.maplayer') + .attr('transform', plotTransform); + + var plotTransform2 = strTranslate(-dx, -dy); + _this.clipDefRelative.select('path').attr('transform', plotTransform2); + + // move the ticks + _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c]; + _this.baxis.range = [_this.sum - mins.a - mins.c, mins.b]; + _this.caxis.range = [_this.sum - mins.a - mins.b, mins.c]; + + _this.drawAxes(false); + + if(_this._hasClipOnAxisFalse) { + _this.plotContainer + .select('.scatterlayer').selectAll('.trace') + .call(Drawing.hideOutsideRangePoints, _this); + } + + gd.emit('plotly_relayouting', makeUpdate(mins)); + } + + function dragDone() { + Registry.call('_guiRelayout', gd, makeUpdate(mins)); + } + + // finally, set up hover and click + // these event handlers must already be set before dragElement.init + // so it can stash them and override them. + dragger.onmousemove = function(evt) { + Fx.hover(gd, evt, _this.id); + gd._fullLayout._lasthover = dragger; + gd._fullLayout._hoversubplot = _this.id; + }; + + dragger.onmouseout = function(evt) { + if(gd._dragging) return; + + dragElement.unhover(gd, evt); + }; + + dragElement.init(this.dragOptions); +}; + +function removeZoombox(gd) { + d3.select(gd) + .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners') + .remove(); +} + +},{"../../components/color":155,"../../components/dragelement":174,"../../components/dragelement/helpers":173,"../../components/drawing":177,"../../components/fx":195,"../../components/titles":253,"../../lib":285,"../../lib/extend":279,"../../registry":373,"../cartesian/axes":331,"../cartesian/constants":338,"../cartesian/select":351,"../cartesian/set_convert":352,"../plots":366,"@plotly/d3":20,"tinycolor2":119}],373:[function(_dereq_,module,exports){ +'use strict'; + +var Loggers = _dereq_('./lib/loggers'); +var noop = _dereq_('./lib/noop'); +var pushUnique = _dereq_('./lib/push_unique'); +var isPlainObject = _dereq_('./lib/is_plain_object'); +var addStyleRule = _dereq_('./lib/dom').addStyleRule; +var ExtendModule = _dereq_('./lib/extend'); + +var basePlotAttributes = _dereq_('./plots/attributes'); +var baseLayoutAttributes = _dereq_('./plots/layout_attributes'); + +var extendFlat = ExtendModule.extendFlat; +var extendDeepAll = ExtendModule.extendDeepAll; + +exports.modules = {}; +exports.allCategories = {}; +exports.allTypes = []; +exports.subplotsRegistry = {}; +exports.transformsRegistry = {}; +exports.componentsRegistry = {}; +exports.layoutArrayContainers = []; +exports.layoutArrayRegexes = []; +exports.traceLayoutAttributes = {}; +exports.localeRegistry = {}; +exports.apiMethodRegistry = {}; +exports.collectableSubplotTypes = null; + +/** + * Top-level register routine, exported as Plotly.register + * + * @param {object array or array of objects} _modules : + * module object or list of module object to register. + * + * A valid `moduleType: 'trace'` module has fields: + * - name {string} : the trace type + * - categories {array} : categories associated with this trace type, + * tested with Register.traceIs() + * - meta {object} : meta info (mostly for plot-schema) + * + * A valid `moduleType: 'locale'` module has fields: + * - name {string} : the locale name. Should be a 2-digit language string ('en', 'de') + * optionally with a country/region code ('en-GB', 'de-CH'). If a country + * code is used but the base language locale has not yet been supplied, + * we will use this locale for the base as well. + * - dictionary {object} : the dictionary mapping input strings to localized strings + * generally the keys should be the literal input strings, but + * if default translations are provided you can use any string as a key. + * - format {object} : a `d3.locale` format specifier for this locale + * any omitted keys we'll fall back on en-US. + * + * A valid `moduleType: 'transform'` module has fields: + * - name {string} : transform name + * - transform {function} : default-level transform function + * - calcTransform {function} : calc-level transform function + * - attributes {object} : transform attributes declarations + * - supplyDefaults {function} : attributes default-supply function + * + * A valid `moduleType: 'component'` module has fields: + * - name {string} : the component name, used it with Register.getComponentMethod() + * to employ component method. + * + * A valid `moduleType: 'apiMethod'` module has fields: + * - name {string} : the api method name. + * - fn {function} : the api method called with Register.call(); + * + */ +exports.register = function register(_modules) { + exports.collectableSubplotTypes = null; + + if(!_modules) { + throw new Error('No argument passed to Plotly.register.'); + } else if(_modules && !Array.isArray(_modules)) { + _modules = [_modules]; + } + + for(var i = 0; i < _modules.length; i++) { + var newModule = _modules[i]; + + if(!newModule) { + throw new Error('Invalid module was attempted to be registered!'); + } + + switch(newModule.moduleType) { + case 'trace': + registerTraceModule(newModule); + break; + case 'transform': + registerTransformModule(newModule); + break; + case 'component': + registerComponentModule(newModule); + break; + case 'locale': + registerLocale(newModule); + break; + case 'apiMethod': + var name = newModule.name; + exports.apiMethodRegistry[name] = newModule.fn; + break; + default: + throw new Error('Invalid module was attempted to be registered!'); + } + } +}; + +/** + * Get registered module using trace object or trace type + * + * @param {object||string} trace + * trace object with prop 'type' or trace type as a string + * @return {object} + * module object corresponding to trace type + */ +exports.getModule = function(trace) { + var _module = exports.modules[getTraceType(trace)]; + if(!_module) return false; + return _module._module; +}; + +/** + * Determine if this trace type is in a given category + * + * @param {object||string} traceType + * a trace (object) or trace type (string) + * @param {string} category + * category in question + * @return {boolean} + */ +exports.traceIs = function(traceType, category) { + traceType = getTraceType(traceType); + + // old Chart Studio Cloud workspace hack, nothing to see here + if(traceType === 'various') return false; + + var _module = exports.modules[traceType]; + + if(!_module) { + if(traceType) { + Loggers.log('Unrecognized trace type ' + traceType + '.'); + } + + _module = exports.modules[basePlotAttributes.type.dflt]; + } + + return !!_module.categories[category]; +}; + +/** + * Determine if this trace has a transform of the given type and return + * array of matching indices. + * + * @param {object} data + * a trace object (member of data or fullData) + * @param {string} type + * type of trace to test + * @return {array} + * array of matching indices. If none found, returns [] + */ +exports.getTransformIndices = function(data, type) { + var indices = []; + var transforms = data.transforms || []; + for(var i = 0; i < transforms.length; i++) { + if(transforms[i].type === type) { + indices.push(i); + } + } + return indices; +}; + +/** + * Determine if this trace has a transform of the given type + * + * @param {object} data + * a trace object (member of data or fullData) + * @param {string} type + * type of trace to test + * @return {boolean} + */ +exports.hasTransform = function(data, type) { + var transforms = data.transforms || []; + for(var i = 0; i < transforms.length; i++) { + if(transforms[i].type === type) { + return true; + } + } + return false; +}; + +/** + * Retrieve component module method. Falls back on noop if either the + * module or the method is missing, so the result can always be safely called + * + * @param {string} name + * name of component (as declared in component module) + * @param {string} method + * name of component module method + * @return {function} + */ +exports.getComponentMethod = function(name, method) { + var _module = exports.componentsRegistry[name]; + + if(!_module) return noop; + return _module[method] || noop; +}; + +/** + * Call registered api method. + * + * @param {string} name : api method name + * @param {...array} args : arguments passed to api method + * @return {any} : returns api method output + */ +exports.call = function() { + var name = arguments[0]; + var args = [].slice.call(arguments, 1); + return exports.apiMethodRegistry[name].apply(null, args); +}; + +function registerTraceModule(_module) { + var thisType = _module.name; + var categoriesIn = _module.categories; + var meta = _module.meta; + + if(exports.modules[thisType]) { + Loggers.log('Type ' + thisType + ' already registered'); + return; + } + + if(!exports.subplotsRegistry[_module.basePlotModule.name]) { + registerSubplot(_module.basePlotModule); + } + + var categoryObj = {}; + for(var i = 0; i < categoriesIn.length; i++) { + categoryObj[categoriesIn[i]] = true; + exports.allCategories[categoriesIn[i]] = true; + } + + exports.modules[thisType] = { + _module: _module, + categories: categoryObj + }; + + if(meta && Object.keys(meta).length) { + exports.modules[thisType].meta = meta; + } + + exports.allTypes.push(thisType); + + for(var componentName in exports.componentsRegistry) { + mergeComponentAttrsToTrace(componentName, thisType); + } + + /* + * Collect all trace layout attributes in one place for easier lookup later + * but don't merge them into the base schema as it would confuse the docs + * (at least after https://github.com/plotly/documentation/issues/202 gets done!) + */ + if(_module.layoutAttributes) { + extendFlat(exports.traceLayoutAttributes, _module.layoutAttributes); + } + + var basePlotModule = _module.basePlotModule; + var bpmName = basePlotModule.name; + + // add mapbox-gl CSS here to avoid console warning on instantiation + if(bpmName === 'mapbox') { + var styleRules = basePlotModule.constants.styleRules; + for(var k in styleRules) { + addStyleRule('.js-plotly-plot .plotly .mapboxgl-' + k, styleRules[k]); + } + } + + // if `plotly-geo-assets.js` is not included, + // add `PlotlyGeoAssets` global to stash references to all fetched + // topojson / geojson data + if((bpmName === 'geo' || bpmName === 'mapbox') && + (typeof window !== undefined && window.PlotlyGeoAssets === undefined) + ) { + window.PlotlyGeoAssets = {topojson: {}}; + } +} + +function registerSubplot(_module) { + var plotType = _module.name; + + if(exports.subplotsRegistry[plotType]) { + Loggers.log('Plot type ' + plotType + ' already registered.'); + return; + } + + // relayout array handling will look for component module methods with this + // name and won't find them because this is a subplot module... but that + // should be fine, it will just fall back on redrawing the plot. + findArrayRegexps(_module); + + // not sure what's best for the 'cartesian' type at this point + exports.subplotsRegistry[plotType] = _module; + + for(var componentName in exports.componentsRegistry) { + mergeComponentAttrsToSubplot(componentName, _module.name); + } +} + +function registerComponentModule(_module) { + if(typeof _module.name !== 'string') { + throw new Error('Component module *name* must be a string.'); + } + + var name = _module.name; + exports.componentsRegistry[name] = _module; + + if(_module.layoutAttributes) { + if(_module.layoutAttributes._isLinkedToArray) { + pushUnique(exports.layoutArrayContainers, name); + } + findArrayRegexps(_module); + } + + for(var traceType in exports.modules) { + mergeComponentAttrsToTrace(name, traceType); + } + + for(var subplotName in exports.subplotsRegistry) { + mergeComponentAttrsToSubplot(name, subplotName); + } + + for(var transformType in exports.transformsRegistry) { + mergeComponentAttrsToTransform(name, transformType); + } + + if(_module.schema && _module.schema.layout) { + extendDeepAll(baseLayoutAttributes, _module.schema.layout); + } +} + +function registerTransformModule(_module) { + if(typeof _module.name !== 'string') { + throw new Error('Transform module *name* must be a string.'); + } + + var prefix = 'Transform module ' + _module.name; + var hasTransform = typeof _module.transform === 'function'; + var hasCalcTransform = typeof _module.calcTransform === 'function'; + + if(!hasTransform && !hasCalcTransform) { + throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.'); + } + if(hasTransform && hasCalcTransform) { + Loggers.log([ + prefix + ' has both a *transform* and *calcTransform* methods.', + 'Please note that all *transform* methods are executed', + 'before all *calcTransform* methods.' + ].join(' ')); + } + if(!isPlainObject(_module.attributes)) { + Loggers.log(prefix + ' registered without an *attributes* object.'); + } + if(typeof _module.supplyDefaults !== 'function') { + Loggers.log(prefix + ' registered without a *supplyDefaults* method.'); + } + + exports.transformsRegistry[_module.name] = _module; + + for(var componentName in exports.componentsRegistry) { + mergeComponentAttrsToTransform(componentName, _module.name); + } +} + +function registerLocale(_module) { + var locale = _module.name; + var baseLocale = locale.split('-')[0]; + + var newDict = _module.dictionary; + var newFormat = _module.format; + var hasDict = newDict && Object.keys(newDict).length; + var hasFormat = newFormat && Object.keys(newFormat).length; + + var locales = exports.localeRegistry; + + var localeObj = locales[locale]; + if(!localeObj) locales[locale] = localeObj = {}; + + // Should we use this dict for the base locale? + // In case we're overwriting a previous dict for this locale, check + // whether the base matches the full locale dict now. If we're not + // overwriting, locales[locale] is undefined so this just checks if + // baseLocale already had a dict or not. + // Same logic for dateFormats + if(baseLocale !== locale) { + var baseLocaleObj = locales[baseLocale]; + if(!baseLocaleObj) locales[baseLocale] = baseLocaleObj = {}; + + if(hasDict && baseLocaleObj.dictionary === localeObj.dictionary) { + baseLocaleObj.dictionary = newDict; + } + if(hasFormat && baseLocaleObj.format === localeObj.format) { + baseLocaleObj.format = newFormat; + } + } + + if(hasDict) localeObj.dictionary = newDict; + if(hasFormat) localeObj.format = newFormat; +} + +function findArrayRegexps(_module) { + if(_module.layoutAttributes) { + var arrayAttrRegexps = _module.layoutAttributes._arrayAttrRegexps; + if(arrayAttrRegexps) { + for(var i = 0; i < arrayAttrRegexps.length; i++) { + pushUnique(exports.layoutArrayRegexes, arrayAttrRegexps[i]); + } + } + } +} + +function mergeComponentAttrsToTrace(componentName, traceType) { + var componentSchema = exports.componentsRegistry[componentName].schema; + if(!componentSchema || !componentSchema.traces) return; + + var traceAttrs = componentSchema.traces[traceType]; + if(traceAttrs) { + extendDeepAll(exports.modules[traceType]._module.attributes, traceAttrs); + } +} + +function mergeComponentAttrsToTransform(componentName, transformType) { + var componentSchema = exports.componentsRegistry[componentName].schema; + if(!componentSchema || !componentSchema.transforms) return; + + var transformAttrs = componentSchema.transforms[transformType]; + if(transformAttrs) { + extendDeepAll(exports.transformsRegistry[transformType].attributes, transformAttrs); + } +} + +function mergeComponentAttrsToSubplot(componentName, subplotName) { + var componentSchema = exports.componentsRegistry[componentName].schema; + if(!componentSchema || !componentSchema.subplots) return; + + var subplotModule = exports.subplotsRegistry[subplotName]; + var subplotAttrs = subplotModule.layoutAttributes; + var subplotAttr = subplotModule.attr === 'subplot' ? subplotModule.name : subplotModule.attr; + if(Array.isArray(subplotAttr)) subplotAttr = subplotAttr[0]; + + var componentLayoutAttrs = componentSchema.subplots[subplotAttr]; + if(subplotAttrs && componentLayoutAttrs) { + extendDeepAll(subplotAttrs, componentLayoutAttrs); + } +} + +function getTraceType(traceType) { + if(typeof traceType === 'object') traceType = traceType.type; + return traceType; +} + +},{"./lib/dom":277,"./lib/extend":279,"./lib/is_plain_object":286,"./lib/loggers":289,"./lib/noop":294,"./lib/push_unique":299,"./plots/attributes":327,"./plots/layout_attributes":364}],374:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); +var Lib = _dereq_('../lib'); + +var extendFlat = Lib.extendFlat; +var extendDeep = Lib.extendDeep; + +// Put default plotTile layouts here +function cloneLayoutOverride(tileClass) { + var override; + + switch(tileClass) { + case 'themes__thumb': + override = { + autosize: true, + width: 150, + height: 150, + title: {text: ''}, + showlegend: false, + margin: {l: 5, r: 5, t: 5, b: 5, pad: 0}, + annotations: [] + }; + break; + + case 'thumbnail': + override = { + title: {text: ''}, + hidesources: true, + showlegend: false, + borderwidth: 0, + bordercolor: '', + margin: {l: 1, r: 1, t: 1, b: 1, pad: 0}, + annotations: [] + }; + break; + + default: + override = {}; + } + + + return override; +} + +function keyIsAxis(keyName) { + var types = ['xaxis', 'yaxis', 'zaxis']; + return (types.indexOf(keyName.slice(0, 5)) > -1); +} + + +module.exports = function clonePlot(graphObj, options) { + var i; + var oldData = graphObj.data; + var oldLayout = graphObj.layout; + var newData = extendDeep([], oldData); + var newLayout = extendDeep({}, oldLayout, cloneLayoutOverride(options.tileClass)); + var context = graphObj._context || {}; + + if(options.width) newLayout.width = options.width; + if(options.height) newLayout.height = options.height; + + if(options.tileClass === 'thumbnail' || options.tileClass === 'themes__thumb') { + // kill annotations + newLayout.annotations = []; + var keys = Object.keys(newLayout); + + for(i = 0; i < keys.length; i++) { + if(keyIsAxis(keys[i])) { + newLayout[keys[i]].title = {text: ''}; + } + } + + // kill colorbar and pie labels + for(i = 0; i < newData.length; i++) { + var trace = newData[i]; + trace.showscale = false; + if(trace.marker) trace.marker.showscale = false; + if(Registry.traceIs(trace, 'pie-like')) trace.textposition = 'none'; + } + } + + if(Array.isArray(options.annotations)) { + for(i = 0; i < options.annotations.length; i++) { + newLayout.annotations.push(options.annotations[i]); + } + } + + // TODO: does this scene modification really belong here? + // If we still need it, can it move into the gl3d module? + var sceneIds = Object.keys(newLayout).filter(function(key) { + return key.match(/^scene\d*$/); + }); + if(sceneIds.length) { + var axesImageOverride = {}; + if(options.tileClass === 'thumbnail') { + axesImageOverride = { + title: {text: ''}, + showaxeslabels: false, + showticklabels: false, + linetickenable: false + }; + } + for(i = 0; i < sceneIds.length; i++) { + var scene = newLayout[sceneIds[i]]; + + if(!scene.xaxis) { + scene.xaxis = {}; + } + + if(!scene.yaxis) { + scene.yaxis = {}; + } + + if(!scene.zaxis) { + scene.zaxis = {}; + } + + extendFlat(scene.xaxis, axesImageOverride); + extendFlat(scene.yaxis, axesImageOverride); + extendFlat(scene.zaxis, axesImageOverride); + + // TODO what does this do? + scene._scene = null; + } + } + + var gd = document.createElement('div'); + if(options.tileClass) gd.className = options.tileClass; + + var plotTile = { + gd: gd, + td: gd, // for external (image server) compatibility + layout: newLayout, + data: newData, + config: { + staticPlot: (options.staticPlot === undefined) ? + true : + options.staticPlot, + plotGlPixelRatio: (options.plotGlPixelRatio === undefined) ? + 2 : + options.plotGlPixelRatio, + displaylogo: options.displaylogo || false, + showLink: options.showLink || false, + showTips: options.showTips || false, + mapboxAccessToken: context.mapboxAccessToken + } + }; + + if(options.setBackground !== 'transparent') { + plotTile.config.setBackground = options.setBackground || 'opaque'; + } + + // attaching the default Layout the gd, so you can grab it later + plotTile.gd.defaultLayout = cloneLayoutOverride(options.tileClass); + + return plotTile; +}; + +},{"../lib":285,"../registry":373}],375:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); + +var toImage = _dereq_('../plot_api/to_image'); + +var fileSaver = _dereq_('./filesaver'); +var helpers = _dereq_('./helpers'); + +/** + * Plotly.downloadImage + * + * @param {object | string | HTML div} gd + * can either be a data/layout/config object + * or an existing graph
+ * or an id to an existing graph
+ * @param {object} opts (see Plotly.toImage in ../plot_api/to_image) + * @return {promise} + */ +function downloadImage(gd, opts) { + var _gd; + if(!Lib.isPlainObject(gd)) _gd = Lib.getGraphDiv(gd); + + opts = opts || {}; + opts.format = opts.format || 'png'; + opts.width = opts.width || null; + opts.height = opts.height || null; + opts.imageDataOnly = true; + + return new Promise(function(resolve, reject) { + if(_gd && _gd._snapshotInProgress) { + reject(new Error('Snapshotting already in progress.')); + } + + // see comments within svgtoimg for additional + // discussion of problems with IE + // can now draw to canvas, but CORS tainted canvas + // does not allow toDataURL + // svg format will work though + if(Lib.isIE() && opts.format !== 'svg') { + reject(new Error(helpers.MSG_IE_BAD_FORMAT)); + } + + if(_gd) _gd._snapshotInProgress = true; + var promise = toImage(gd, opts); + + var filename = opts.filename || gd.fn || 'newplot'; + filename += '.' + opts.format.replace('-', '.'); + + promise.then(function(result) { + if(_gd) _gd._snapshotInProgress = false; + return fileSaver(result, filename, opts.format); + }).then(function(name) { + resolve(name); + }).catch(function(err) { + if(_gd) _gd._snapshotInProgress = false; + reject(err); + }); + }); +} + +module.exports = downloadImage; + +},{"../lib":285,"../plot_api/to_image":323,"./filesaver":376,"./helpers":377}],376:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var helpers = _dereq_('./helpers'); + +/* +* substantial portions of this code from FileSaver.js +* https://github.com/eligrey/FileSaver.js +* License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md +* FileSaver.js +* A saveAs() FileSaver implementation. +* 1.1.20160328 +* +* By Eli Grey, http://eligrey.com +* License: MIT +* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md +*/ +function fileSaver(url, name, format) { + var saveLink = document.createElement('a'); + var canUseSaveLink = 'download' in saveLink; + + var promise = new Promise(function(resolve, reject) { + var blob; + var objectUrl; + + // Safari doesn't allow downloading of blob urls + if(Lib.isSafari()) { + var prefix = format === 'svg' ? ',' : ';base64,'; + helpers.octetStream(prefix + encodeURIComponent(url)); + return resolve(name); + } + + // IE 10+ (native saveAs) + if(Lib.isIE()) { + // At this point we are only dealing with a decoded SVG as + // a data URL (since IE only supports SVG) + blob = helpers.createBlob(url, 'svg'); + window.navigator.msSaveBlob(blob, name); + blob = null; + return resolve(name); + } + + if(canUseSaveLink) { + blob = helpers.createBlob(url, format); + objectUrl = helpers.createObjectURL(blob); + + saveLink.href = objectUrl; + saveLink.download = name; + document.body.appendChild(saveLink); + saveLink.click(); + + document.body.removeChild(saveLink); + helpers.revokeObjectURL(objectUrl); + blob = null; + + return resolve(name); + } + + reject(new Error('download error')); + }); + + return promise; +} + + +module.exports = fileSaver; + +},{"../lib":285,"./helpers":377}],377:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../registry'); + +exports.getDelay = function(fullLayout) { + if(!fullLayout._has) return 0; + + return ( + fullLayout._has('gl3d') || + fullLayout._has('gl2d') || + fullLayout._has('mapbox') + ) ? 500 : 0; +}; + +exports.getRedrawFunc = function(gd) { + return function() { + Registry.getComponentMethod('colorbar', 'draw')(gd); + }; +}; + +exports.encodeSVG = function(svg) { + return 'data:image/svg+xml,' + encodeURIComponent(svg); +}; + +exports.encodeJSON = function(json) { + return 'data:application/json,' + encodeURIComponent(json); +}; + +var DOM_URL = window.URL || window.webkitURL; + +exports.createObjectURL = function(blob) { + return DOM_URL.createObjectURL(blob); +}; + +exports.revokeObjectURL = function(url) { + return DOM_URL.revokeObjectURL(url); +}; + +exports.createBlob = function(url, format) { + if(format === 'svg') { + return new window.Blob([url], {type: 'image/svg+xml;charset=utf-8'}); + } else if(format === 'full-json') { + return new window.Blob([url], {type: 'application/json;charset=utf-8'}); + } else { + var binary = fixBinary(window.atob(url)); + return new window.Blob([binary], {type: 'image/' + format}); + } +}; + +exports.octetStream = function(s) { + document.location.href = 'data:application/octet-stream' + s; +}; + +// Taken from https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752 +function fixBinary(b) { + var len = b.length; + var buf = new ArrayBuffer(len); + var arr = new Uint8Array(buf); + for(var i = 0; i < len; i++) { + arr[i] = b.charCodeAt(i); + } + return buf; +} + +exports.IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/; + +exports.MSG_IE_BAD_FORMAT = 'Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.'; + +},{"../registry":373}],378:[function(_dereq_,module,exports){ +'use strict'; + +var helpers = _dereq_('./helpers'); + +var Snapshot = { + getDelay: helpers.getDelay, + getRedrawFunc: helpers.getRedrawFunc, + clone: _dereq_('./cloneplot'), + toSVG: _dereq_('./tosvg'), + svgToImg: _dereq_('./svgtoimg'), + toImage: _dereq_('./toimage'), + downloadImage: _dereq_('./download') +}; + +module.exports = Snapshot; + +},{"./cloneplot":374,"./download":375,"./helpers":377,"./svgtoimg":379,"./toimage":380,"./tosvg":381}],379:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var EventEmitter = _dereq_('events').EventEmitter; + +var helpers = _dereq_('./helpers'); + +function svgToImg(opts) { + var ev = opts.emitter || new EventEmitter(); + + var promise = new Promise(function(resolve, reject) { + var Image = window.Image; + var svg = opts.svg; + var format = opts.format || 'png'; + + // IE only support svg + if(Lib.isIE() && format !== 'svg') { + var ieSvgError = new Error(helpers.MSG_IE_BAD_FORMAT); + reject(ieSvgError); + // eventually remove the ev + // in favor of promises + if(!opts.promise) { + return ev.emit('error', ieSvgError); + } else { + return promise; + } + } + + var canvas = opts.canvas; + var scale = opts.scale || 1; + var w0 = opts.width || 300; + var h0 = opts.height || 150; + var w1 = scale * w0; + var h1 = scale * h0; + + var ctx = canvas.getContext('2d'); + var img = new Image(); + var svgBlob, url; + + if(format === 'svg' || Lib.isSafari()) { + url = helpers.encodeSVG(svg); + } else { + svgBlob = helpers.createBlob(svg, 'svg'); + url = helpers.createObjectURL(svgBlob); + } + + canvas.width = w1; + canvas.height = h1; + + img.onload = function() { + var imgData; + + svgBlob = null; + helpers.revokeObjectURL(url); + + // don't need to draw to canvas if svg + // save some time and also avoid failure on IE + if(format !== 'svg') { + ctx.drawImage(img, 0, 0, w1, h1); + } + + switch(format) { + case 'jpeg': + imgData = canvas.toDataURL('image/jpeg'); + break; + case 'png': + imgData = canvas.toDataURL('image/png'); + break; + case 'webp': + imgData = canvas.toDataURL('image/webp'); + break; + case 'svg': + imgData = url; + break; + default: + var errorMsg = 'Image format is not jpeg, png, svg or webp.'; + reject(new Error(errorMsg)); + // eventually remove the ev + // in favor of promises + if(!opts.promise) { + return ev.emit('error', errorMsg); + } + } + resolve(imgData); + // eventually remove the ev + // in favor of promises + if(!opts.promise) { + ev.emit('success', imgData); + } + }; + + img.onerror = function(err) { + svgBlob = null; + helpers.revokeObjectURL(url); + + reject(err); + // eventually remove the ev + // in favor of promises + if(!opts.promise) { + return ev.emit('error', err); + } + }; + + img.src = url; + }); + + // temporary for backward compatibility + // move to only Promise in 2.0.0 + // and eliminate the EventEmitter + if(opts.promise) { + return promise; + } + + return ev; +} + +module.exports = svgToImg; + +},{"../lib":285,"./helpers":377,"events":27}],380:[function(_dereq_,module,exports){ +'use strict'; + +var EventEmitter = _dereq_('events').EventEmitter; + +var Registry = _dereq_('../registry'); +var Lib = _dereq_('../lib'); + +var helpers = _dereq_('./helpers'); +var clonePlot = _dereq_('./cloneplot'); +var toSVG = _dereq_('./tosvg'); +var svgToImg = _dereq_('./svgtoimg'); + +/** + * @param {object} gd figure Object + * @param {object} opts option object + * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' + */ +function toImage(gd, opts) { + // first clone the GD so we can operate in a clean environment + var ev = new EventEmitter(); + + var clone = clonePlot(gd, {format: 'png'}); + var clonedGd = clone.gd; + + // put the cloned div somewhere off screen before attaching to DOM + clonedGd.style.position = 'absolute'; + clonedGd.style.left = '-5000px'; + document.body.appendChild(clonedGd); + + function wait() { + var delay = helpers.getDelay(clonedGd._fullLayout); + + setTimeout(function() { + var svg = toSVG(clonedGd); + + var canvas = document.createElement('canvas'); + canvas.id = Lib.randstr(); + + ev = svgToImg({ + format: opts.format, + width: clonedGd._fullLayout.width, + height: clonedGd._fullLayout.height, + canvas: canvas, + emitter: ev, + svg: svg + }); + + ev.clean = function() { + if(clonedGd) document.body.removeChild(clonedGd); + }; + }, delay); + } + + var redrawFunc = helpers.getRedrawFunc(clonedGd); + + Registry.call('_doPlot', clonedGd, clone.data, clone.layout, clone.config) + .then(redrawFunc) + .then(wait) + .catch(function(err) { + ev.emit('error', err); + }); + + + return ev; +} + +module.exports = toImage; + +},{"../lib":285,"../registry":373,"./cloneplot":374,"./helpers":377,"./svgtoimg":379,"./tosvg":381,"events":27}],381:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Lib = _dereq_('../lib'); +var Drawing = _dereq_('../components/drawing'); +var Color = _dereq_('../components/color'); + +var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces'); +var DOUBLEQUOTE_REGEX = /"/g; +var DUMMY_SUB = 'TOBESTRIPPED'; +var DUMMY_REGEX = new RegExp('("' + DUMMY_SUB + ')|(' + DUMMY_SUB + '")', 'g'); + +function htmlEntityDecode(s) { + var hiddenDiv = d3.select('body').append('div').style({display: 'none'}).html(''); + var replaced = s.replace(/(&[^;]*;)/gi, function(d) { + if(d === '<') { return '<'; } // special handling for brackets + if(d === '&rt;') { return '>'; } + if(d.indexOf('<') !== -1 || d.indexOf('>') !== -1) { return ''; } + return hiddenDiv.html(d).text(); // everything else, let the browser decode it to unicode + }); + hiddenDiv.remove(); + return replaced; +} + +function xmlEntityEncode(str) { + return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&'); +} + +module.exports = function toSVG(gd, format, scale) { + var fullLayout = gd._fullLayout; + var svg = fullLayout._paper; + var toppaper = fullLayout._toppaper; + var width = fullLayout.width; + var height = fullLayout.height; + var i, k; + + // make background color a rect in the svg, then revert after scraping + // all other alterations have been dealt with by properly preparing the svg + // in the first place... like setting cursors with css classes so we don't + // have to remove them, and providing the right namespaces in the svg to + // begin with + svg.insert('rect', ':first-child') + .call(Drawing.setRect, 0, 0, width, height) + .call(Color.fill, fullLayout.paper_bgcolor); + + // subplot-specific to-SVG methods + // which notably add the contents of the gl-container + // into the main svg node + var basePlotModules = fullLayout._basePlotModules || []; + for(i = 0; i < basePlotModules.length; i++) { + var _module = basePlotModules[i]; + + if(_module.toSVG) _module.toSVG(gd); + } + + // add top items above them assumes everything in toppaper is either + // a group or a defs, and if it's empty (like hoverlayer) we can ignore it. + if(toppaper) { + var nodes = toppaper.node().childNodes; + + // make copy of nodes as childNodes prop gets mutated in loop below + var topGroups = Array.prototype.slice.call(nodes); + + for(i = 0; i < topGroups.length; i++) { + var topGroup = topGroups[i]; + + if(topGroup.childNodes.length) svg.node().appendChild(topGroup); + } + } + + // remove draglayer for Adobe Illustrator compatibility + if(fullLayout._draggers) { + fullLayout._draggers.remove(); + } + + // in case the svg element had an explicit background color, remove this + // we want the rect to get the color so it's the right size; svg bg will + // fill whatever container it's displayed in regardless of plot size. + svg.node().style.background = ''; + + svg.selectAll('text') + .attr({'data-unformatted': null, 'data-math': null}) + .each(function() { + var txt = d3.select(this); + + // hidden text is pre-formatting mathjax, the browser ignores it + // but in a static plot it's useless and it can confuse batik + // we've tried to standardize on display:none but make sure we still + // catch visibility:hidden if it ever arises + if(this.style.visibility === 'hidden' || this.style.display === 'none') { + txt.remove(); + return; + } else { + // clear other visibility/display values to default + // to not potentially confuse non-browser SVG implementations + txt.style({visibility: null, display: null}); + } + + // Font family styles break things because of quotation marks, + // so we must remove them *after* the SVG DOM has been serialized + // to a string (browsers convert singles back) + var ff = this.style.fontFamily; + if(ff && ff.indexOf('"') !== -1) { + txt.style('font-family', ff.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB)); + } + }); + + var queryParts = []; + if(fullLayout._gradientUrlQueryParts) { + for(k in fullLayout._gradientUrlQueryParts) queryParts.push(k); + } + + if(fullLayout._patternUrlQueryParts) { + for(k in fullLayout._patternUrlQueryParts) queryParts.push(k); + } + + if(queryParts.length) { + svg.selectAll(queryParts.join(',')).each(function() { + var pt = d3.select(this); + + // similar to font family styles above, + // we must remove " after the SVG DOM has been serialized + var fill = this.style.fill; + if(fill && fill.indexOf('url(') !== -1) { + pt.style('fill', fill.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB)); + } + + var stroke = this.style.stroke; + if(stroke && stroke.indexOf('url(') !== -1) { + pt.style('stroke', stroke.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB)); + } + }); + } + + if(format === 'pdf' || format === 'eps') { + // these formats make the extra line MathJax adds around symbols look super thick in some cases + // it looks better if this is removed entirely. + svg.selectAll('#MathJax_SVG_glyphs path') + .attr('stroke-width', 0); + } + + // fix for IE namespacing quirk? + // http://stackoverflow.com/questions/19610089/unwanted-namespaces-on-svg-markup-when-using-xmlserializer-in-javascript-with-ie + svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg); + svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink); + + if(format === 'svg' && scale) { + svg.attr('width', scale * width); + svg.attr('height', scale * height); + svg.attr('viewBox', '0 0 ' + width + ' ' + height); + } + + var s = new window.XMLSerializer().serializeToString(svg.node()); + s = htmlEntityDecode(s); + s = xmlEntityEncode(s); + + // Fix quotations around font strings and gradient URLs + s = s.replace(DUMMY_REGEX, '\''); + + // Do we need this process now that IE9 and IE10 are not supported? + + // IE is very strict, so we will need to clean + // svg with the following regex + // yes this is messy, but do not know a better way + // Even with this IE will not work due to tainted canvas + // see https://github.com/kangax/fabric.js/issues/1957 + // http://stackoverflow.com/questions/18112047/canvas-todataurl-working-in-all-browsers-except-ie10 + // Leave here just in case the CORS/tainted IE issue gets resolved + if(Lib.isIE()) { + // replace double quote with single quote + s = s.replace(/"/gi, '\''); + // url in svg are single quoted + // since we changed double to single + // we'll need to change these to double-quoted + s = s.replace(/(\('#)([^']*)('\))/gi, '(\"#$2\")'); + // font names with spaces will be escaped single-quoted + // we'll need to change these to double-quoted + s = s.replace(/(\\')/gi, '\"'); + } + + return s; +}; + +},{"../components/color":155,"../components/drawing":177,"../constants/xmlns_namespaces":266,"../lib":285,"@plotly/d3":20}],382:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +// arrayOk attributes, merge them into calcdata array +module.exports = function arraysToCalcdata(cd, trace) { + for(var i = 0; i < cd.length; i++) cd[i].i = i; + + Lib.mergeArray(trace.text, cd, 'tx'); + Lib.mergeArray(trace.hovertext, cd, 'htx'); + + var marker = trace.marker; + if(marker) { + Lib.mergeArray(marker.opacity, cd, 'mo', true); + Lib.mergeArray(marker.color, cd, 'mc'); + + var markerLine = marker.line; + if(markerLine) { + Lib.mergeArray(markerLine.color, cd, 'mlc'); + Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw'); + } + } +}; + +},{"../../lib":285}],383:[function(_dereq_,module,exports){ +'use strict'; + +var scatterAttrs = _dereq_('../scatter/attributes'); +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var fontAttrs = _dereq_('../../plots/font_attributes'); +var constants = _dereq_('./constants'); +var pattern = _dereq_('../../components/drawing/attributes').pattern; + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var textFontAttrs = fontAttrs({ + editType: 'calc', + arrayOk: true, + colorEditType: 'style', +}); + +var scatterMarkerAttrs = scatterAttrs.marker; +var scatterMarkerLineAttrs = scatterMarkerAttrs.line; + +var markerLineWidth = extendFlat({}, + scatterMarkerLineAttrs.width, { dflt: 0 }); + +var markerLine = extendFlat({ + width: markerLineWidth, + editType: 'calc' +}, colorScaleAttrs('marker.line')); + +var marker = extendFlat({ + line: markerLine, + editType: 'calc' +}, colorScaleAttrs('marker'), { + opacity: { + valType: 'number', + arrayOk: true, + dflt: 1, + min: 0, + max: 1, + editType: 'style', + }, + pattern: pattern +}); + +module.exports = { + x: scatterAttrs.x, + x0: scatterAttrs.x0, + dx: scatterAttrs.dx, + y: scatterAttrs.y, + y0: scatterAttrs.y0, + dy: scatterAttrs.dy, + + xperiod: scatterAttrs.xperiod, + yperiod: scatterAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: scatterAttrs.xperiodalignment, + yperiodalignment: scatterAttrs.yperiodalignment, + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + + text: scatterAttrs.text, + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: constants.eventDataKeys + }), + hovertext: scatterAttrs.hovertext, + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + + textposition: { + valType: 'enumerated', + values: ['inside', 'outside', 'auto', 'none'], + dflt: 'auto', + arrayOk: true, + editType: 'calc', + }, + + insidetextanchor: { + valType: 'enumerated', + values: ['end', 'middle', 'start'], + dflt: 'end', + editType: 'plot', + }, + + textangle: { + valType: 'angle', + dflt: 'auto', + editType: 'plot', + }, + + textfont: extendFlat({}, textFontAttrs, { + }), + + insidetextfont: extendFlat({}, textFontAttrs, { + }), + + outsidetextfont: extendFlat({}, textFontAttrs, { + }), + + constraintext: { + valType: 'enumerated', + values: ['inside', 'outside', 'both', 'none'], + dflt: 'both', + editType: 'calc', + }, + + cliponaxis: extendFlat({}, scatterAttrs.cliponaxis, { + }), + + orientation: { + valType: 'enumerated', + values: ['v', 'h'], + editType: 'calc+clearAxisTypes', + }, + + base: { + valType: 'any', + dflt: null, + arrayOk: true, + editType: 'calc', + }, + + offset: { + valType: 'number', + dflt: null, + arrayOk: true, + editType: 'calc', + }, + + width: { + valType: 'number', + dflt: null, + min: 0, + arrayOk: true, + editType: 'calc', + }, + + marker: marker, + + offsetgroup: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + alignmentgroup: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + + selected: { + marker: { + opacity: scatterAttrs.selected.marker.opacity, + color: scatterAttrs.selected.marker.color, + editType: 'style' + }, + textfont: scatterAttrs.selected.textfont, + editType: 'style' + }, + unselected: { + marker: { + opacity: scatterAttrs.unselected.marker.opacity, + color: scatterAttrs.unselected.marker.color, + editType: 'style' + }, + textfont: scatterAttrs.unselected.textfont, + editType: 'style' + }, + + _deprecated: { + bardir: { + valType: 'enumerated', + editType: 'calc', + values: ['v', 'h'], + } + } +}; + +},{"../../components/colorscale/attributes":162,"../../components/drawing/attributes":176,"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../../plots/font_attributes":360,"../../plots/template_attributes":368,"../scatter/attributes":494,"./constants":385}],384:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +var alignPeriod = _dereq_('../../plots/cartesian/align_period'); +var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale; +var colorscaleCalc = _dereq_('../../components/colorscale/calc'); +var arraysToCalcdata = _dereq_('./arrays_to_calcdata'); +var calcSelection = _dereq_('../scatter/calc_selection'); + +module.exports = function calc(gd, trace) { + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var size, pos, origPos; + + var sizeOpts = { + msUTC: !!(trace.base || trace.base === 0) + }; + + var hasPeriod; + if(trace.orientation === 'h') { + size = xa.makeCalcdata(trace, 'x', sizeOpts); + origPos = ya.makeCalcdata(trace, 'y'); + pos = alignPeriod(trace, ya, 'y', origPos); + hasPeriod = !!trace.yperiodalignment; + } else { + size = ya.makeCalcdata(trace, 'y', sizeOpts); + origPos = xa.makeCalcdata(trace, 'x'); + pos = alignPeriod(trace, xa, 'x', origPos); + hasPeriod = !!trace.xperiodalignment; + } + + // create the "calculated data" to plot + var serieslen = Math.min(pos.length, size.length); + var cd = new Array(serieslen); + + // set position and size + for(var i = 0; i < serieslen; i++) { + cd[i] = { p: pos[i], s: size[i] }; + + if(hasPeriod) { + cd[i].orig_p = origPos[i]; // used by hover + } + + if(trace.ids) { + cd[i].id = String(trace.ids[i]); + } + } + + // auto-z and autocolorscale if applicable + if(hasColorscale(trace, 'marker')) { + colorscaleCalc(gd, trace, { + vals: trace.marker.color, + containerStr: 'marker', + cLetter: 'c' + }); + } + if(hasColorscale(trace, 'marker.line')) { + colorscaleCalc(gd, trace, { + vals: trace.marker.line.color, + containerStr: 'marker.line', + cLetter: 'c' + }); + } + + arraysToCalcdata(cd, trace); + calcSelection(cd, trace); + + return cd; +}; + +},{"../../components/colorscale/calc":163,"../../components/colorscale/helpers":166,"../../plots/cartesian/align_period":328,"../../plots/cartesian/axes":331,"../scatter/calc_selection":496,"./arrays_to_calcdata":382}],385:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + // padding in pixels around text + TEXTPAD: 3, + // 'value' and 'label' are not really necessary for bar traces, + // but they were made available to `texttemplate` (maybe by accident) + // via tokens `%{value}` and `%{label}` starting in 1.50.0, + // so let's include them in the event data also. + eventDataKeys: ['value', 'label'] +}; + +},{}],386:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +var Registry = _dereq_('../../registry'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup; +var Sieve = _dereq_('./sieve.js'); + +/* + * Bar chart stacking/grouping positioning and autoscaling calculations + * for each direction separately calculate the ranges and positions + * note that this handles histograms too + * now doing this one subplot at a time + */ + +function crossTraceCalc(gd, plotinfo) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var fullLayout = gd._fullLayout; + var fullTraces = gd._fullData; + var calcTraces = gd.calcdata; + var calcTracesHorz = []; + var calcTracesVert = []; + + for(var i = 0; i < fullTraces.length; i++) { + var fullTrace = fullTraces[i]; + if( + fullTrace.visible === true && + Registry.traceIs(fullTrace, 'bar') && + fullTrace.xaxis === xa._id && + fullTrace.yaxis === ya._id + ) { + if(fullTrace.orientation === 'h') { + calcTracesHorz.push(calcTraces[i]); + } else { + calcTracesVert.push(calcTraces[i]); + } + + if(fullTrace._computePh) { + var cd = gd.calcdata[i]; + for(var j = 0; j < cd.length; j++) { + if(typeof cd[j].ph0 === 'function') cd[j].ph0 = cd[j].ph0(); + if(typeof cd[j].ph1 === 'function') cd[j].ph1 = cd[j].ph1(); + } + } + } + } + + var opts = { + xCat: xa.type === 'category' || xa.type === 'multicategory', + yCat: ya.type === 'category' || ya.type === 'multicategory', + + mode: fullLayout.barmode, + norm: fullLayout.barnorm, + gap: fullLayout.bargap, + groupgap: fullLayout.bargroupgap + }; + + setGroupPositions(gd, xa, ya, calcTracesVert, opts); + setGroupPositions(gd, ya, xa, calcTracesHorz, opts); +} + +function setGroupPositions(gd, pa, sa, calcTraces, opts) { + if(!calcTraces.length) return; + + var excluded; + var included; + var i, calcTrace, fullTrace; + + initBase(sa, calcTraces); + + switch(opts.mode) { + case 'overlay': + setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts); + break; + + case 'group': + // exclude from the group those traces for which the user set an offset + excluded = []; + included = []; + for(i = 0; i < calcTraces.length; i++) { + calcTrace = calcTraces[i]; + fullTrace = calcTrace[0].trace; + + if(fullTrace.offset === undefined) included.push(calcTrace); + else excluded.push(calcTrace); + } + + if(included.length) { + setGroupPositionsInGroupMode(gd, pa, sa, included, opts); + } + if(excluded.length) { + setGroupPositionsInOverlayMode(pa, sa, excluded, opts); + } + break; + + case 'stack': + case 'relative': + // exclude from the stack those traces for which the user set a base + excluded = []; + included = []; + for(i = 0; i < calcTraces.length; i++) { + calcTrace = calcTraces[i]; + fullTrace = calcTrace[0].trace; + + if(fullTrace.base === undefined) included.push(calcTrace); + else excluded.push(calcTrace); + } + + if(included.length) { + setGroupPositionsInStackOrRelativeMode(gd, pa, sa, included, opts); + } + if(excluded.length) { + setGroupPositionsInOverlayMode(pa, sa, excluded, opts); + } + break; + } + + collectExtents(calcTraces, pa); +} + +function initBase(sa, calcTraces) { + var i, j; + + for(i = 0; i < calcTraces.length; i++) { + var cd = calcTraces[i]; + var trace = cd[0].trace; + var base = (trace.type === 'funnel') ? trace._base : trace.base; + var b; + + // not sure if it really makes sense to have dates for bar size data... + // ideally if we want to make gantt charts or something we'd treat + // the actual size (trace.x or y) as time delta but base as absolute + // time. But included here for completeness. + var scalendar = trace.orientation === 'h' ? trace.xcalendar : trace.ycalendar; + + // 'base' on categorical axes makes no sense + var d2c = sa.type === 'category' || sa.type === 'multicategory' ? + function() { return null; } : + sa.d2c; + + if(isArrayOrTypedArray(base)) { + for(j = 0; j < Math.min(base.length, cd.length); j++) { + b = d2c(base[j], 0, scalendar); + if(isNumeric(b)) { + cd[j].b = +b; + cd[j].hasB = 1; + } else cd[j].b = 0; + } + for(; j < cd.length; j++) { + cd[j].b = 0; + } + } else { + b = d2c(base, 0, scalendar); + var hasBase = isNumeric(b); + b = hasBase ? b : 0; + for(j = 0; j < cd.length; j++) { + cd[j].b = b; + if(hasBase) cd[j].hasB = 1; + } + } + } +} + +function setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts) { + // update position axis and set bar offsets and widths + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + + var sieve = new Sieve([calcTrace], { + posAxis: pa, + sepNegVal: false, + overlapNoMerge: !opts.norm + }); + + // set bar offsets and widths, and update position axis + setOffsetAndWidth(pa, sieve, opts); + + // set bar bases and sizes, and update size axis + // + // (note that `setGroupPositionsInOverlayMode` handles the case barnorm + // is defined, because this function is also invoked for traces that + // can't be grouped or stacked) + if(opts.norm) { + sieveBars(sieve); + normalizeBars(sa, sieve, opts); + } else { + setBaseAndTop(sa, sieve); + } + } +} + +function setGroupPositionsInGroupMode(gd, pa, sa, calcTraces, opts) { + var sieve = new Sieve(calcTraces, { + posAxis: pa, + sepNegVal: false, + overlapNoMerge: !opts.norm + }); + + // set bar offsets and widths, and update position axis + setOffsetAndWidthInGroupMode(gd, pa, sieve, opts); + + // relative-stack bars within the same trace that would otherwise + // be hidden + unhideBarsWithinTrace(sieve, pa); + + // set bar bases and sizes, and update size axis + if(opts.norm) { + sieveBars(sieve); + normalizeBars(sa, sieve, opts); + } else { + setBaseAndTop(sa, sieve); + } +} + +function setGroupPositionsInStackOrRelativeMode(gd, pa, sa, calcTraces, opts) { + var sieve = new Sieve(calcTraces, { + posAxis: pa, + sepNegVal: opts.mode === 'relative', + overlapNoMerge: !(opts.norm || opts.mode === 'stack' || opts.mode === 'relative') + }); + + // set bar offsets and widths, and update position axis + setOffsetAndWidth(pa, sieve, opts); + + // set bar bases and sizes, and update size axis + stackBars(sa, sieve, opts); + + // flag the outmost bar (for text display purposes) + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + + for(var j = 0; j < calcTrace.length; j++) { + var bar = calcTrace[j]; + + if(bar.s !== BADNUM) { + var isOutmostBar = ((bar.b + bar.s) === sieve.get(bar.p, bar.s)); + if(isOutmostBar) bar._outmost = true; + } + } + } + + // Note that marking the outmost bars has to be done + // before `normalizeBars` changes `bar.b` and `bar.s`. + if(opts.norm) normalizeBars(sa, sieve, opts); +} + +function setOffsetAndWidth(pa, sieve, opts) { + var minDiff = sieve.minDiff; + var calcTraces = sieve.traces; + + // set bar offsets and widths + var barGroupWidth = minDiff * (1 - opts.gap); + var barWidthPlusGap = barGroupWidth; + var barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0)); + + // computer bar group center and bar offset + var offsetFromCenter = -barWidth / 2; + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var t = calcTrace[0].t; + + // store bar width and offset for this trace + t.barwidth = barWidth; + t.poffset = offsetFromCenter; + t.bargroupwidth = barGroupWidth; + t.bardelta = minDiff; + } + + // stack bars that only differ by rounding + sieve.binWidth = calcTraces[0][0].t.barwidth / 100; + + // if defined, apply trace offset and width + applyAttributes(sieve); + + // store the bar center in each calcdata item + setBarCenterAndWidth(pa, sieve); + + // update position axes + updatePositionAxis(pa, sieve); +} + +function setOffsetAndWidthInGroupMode(gd, pa, sieve, opts) { + var fullLayout = gd._fullLayout; + var positions = sieve.positions; + var distinctPositions = sieve.distinctPositions; + var minDiff = sieve.minDiff; + var calcTraces = sieve.traces; + var nTraces = calcTraces.length; + + // if there aren't any overlapping positions, + // let them have full width even if mode is group + var overlap = (positions.length !== distinctPositions.length); + var barGroupWidth = minDiff * (1 - opts.gap); + + var groupId = getAxisGroup(fullLayout, pa._id) + calcTraces[0][0].trace.orientation; + var alignmentGroups = fullLayout._alignmentOpts[groupId] || {}; + + for(var i = 0; i < nTraces; i++) { + var calcTrace = calcTraces[i]; + var trace = calcTrace[0].trace; + + var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {}; + var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length; + + var barWidthPlusGap; + if(nOffsetGroups) { + barWidthPlusGap = barGroupWidth / nOffsetGroups; + } else { + barWidthPlusGap = overlap ? barGroupWidth / nTraces : barGroupWidth; + } + + var barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0)); + + var offsetFromCenter; + if(nOffsetGroups) { + offsetFromCenter = ((2 * trace._offsetIndex + 1 - nOffsetGroups) * barWidthPlusGap - barWidth) / 2; + } else { + offsetFromCenter = overlap ? + ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 : + -barWidth / 2; + } + + var t = calcTrace[0].t; + t.barwidth = barWidth; + t.poffset = offsetFromCenter; + t.bargroupwidth = barGroupWidth; + t.bardelta = minDiff; + } + + // stack bars that only differ by rounding + sieve.binWidth = calcTraces[0][0].t.barwidth / 100; + + // if defined, apply trace width + applyAttributes(sieve); + + // store the bar center in each calcdata item + setBarCenterAndWidth(pa, sieve); + + // update position axes + updatePositionAxis(pa, sieve, overlap); +} + +function applyAttributes(sieve) { + var calcTraces = sieve.traces; + var i, j; + + for(i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var calcTrace0 = calcTrace[0]; + var fullTrace = calcTrace0.trace; + var t = calcTrace0.t; + var offset = fullTrace._offset || fullTrace.offset; + var initialPoffset = t.poffset; + var newPoffset; + + if(isArrayOrTypedArray(offset)) { + // if offset is an array, then clone it into t.poffset. + newPoffset = Array.prototype.slice.call(offset, 0, calcTrace.length); + + // guard against non-numeric items + for(j = 0; j < newPoffset.length; j++) { + if(!isNumeric(newPoffset[j])) { + newPoffset[j] = initialPoffset; + } + } + + // if the length of the array is too short, + // then extend it with the initial value of t.poffset + for(j = newPoffset.length; j < calcTrace.length; j++) { + newPoffset.push(initialPoffset); + } + + t.poffset = newPoffset; + } else if(offset !== undefined) { + t.poffset = offset; + } + + var width = fullTrace._width || fullTrace.width; + var initialBarwidth = t.barwidth; + + if(isArrayOrTypedArray(width)) { + // if width is an array, then clone it into t.barwidth. + var newBarwidth = Array.prototype.slice.call(width, 0, calcTrace.length); + + // guard against non-numeric items + for(j = 0; j < newBarwidth.length; j++) { + if(!isNumeric(newBarwidth[j])) newBarwidth[j] = initialBarwidth; + } + + // if the length of the array is too short, + // then extend it with the initial value of t.barwidth + for(j = newBarwidth.length; j < calcTrace.length; j++) { + newBarwidth.push(initialBarwidth); + } + + t.barwidth = newBarwidth; + + // if user didn't set offset, + // then correct t.poffset to ensure bars remain centered + if(offset === undefined) { + newPoffset = []; + for(j = 0; j < calcTrace.length; j++) { + newPoffset.push( + initialPoffset + (initialBarwidth - newBarwidth[j]) / 2 + ); + } + t.poffset = newPoffset; + } + } else if(width !== undefined) { + t.barwidth = width; + + // if user didn't set offset, + // then correct t.poffset to ensure bars remain centered + if(offset === undefined) { + t.poffset = initialPoffset + (initialBarwidth - width) / 2; + } + } + } +} + +function setBarCenterAndWidth(pa, sieve) { + var calcTraces = sieve.traces; + var pLetter = getAxisLetter(pa); + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var t = calcTrace[0].t; + var poffset = t.poffset; + var poffsetIsArray = Array.isArray(poffset); + var barwidth = t.barwidth; + var barwidthIsArray = Array.isArray(barwidth); + + for(var j = 0; j < calcTrace.length; j++) { + var calcBar = calcTrace[j]; + + // store the actual bar width and position, for use by hover + var width = calcBar.w = barwidthIsArray ? barwidth[j] : barwidth; + calcBar[pLetter] = calcBar.p + (poffsetIsArray ? poffset[j] : poffset) + width / 2; + } + } +} + +function updatePositionAxis(pa, sieve, allowMinDtick) { + var calcTraces = sieve.traces; + var minDiff = sieve.minDiff; + var vpad = minDiff / 2; + + Axes.minDtick(pa, sieve.minDiff, sieve.distinctPositions[0], allowMinDtick); + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var calcTrace0 = calcTrace[0]; + var fullTrace = calcTrace0.trace; + var pts = []; + var bar, l, r, j; + + for(j = 0; j < calcTrace.length; j++) { + bar = calcTrace[j]; + l = bar.p - vpad; + r = bar.p + vpad; + pts.push(l, r); + } + + if(fullTrace.width || fullTrace.offset) { + var t = calcTrace0.t; + var poffset = t.poffset; + var barwidth = t.barwidth; + var poffsetIsArray = Array.isArray(poffset); + var barwidthIsArray = Array.isArray(barwidth); + + for(j = 0; j < calcTrace.length; j++) { + bar = calcTrace[j]; + var calcBarOffset = poffsetIsArray ? poffset[j] : poffset; + var calcBarWidth = barwidthIsArray ? barwidth[j] : barwidth; + l = bar.p + calcBarOffset; + r = l + calcBarWidth; + pts.push(l, r); + } + } + + fullTrace._extremes[pa._id] = Axes.findExtremes(pa, pts, {padded: false}); + } +} + +// store these bar bases and tops in calcdata +// and make sure the size axis includes zero, +// along with the bases and tops of each bar. +function setBaseAndTop(sa, sieve) { + var calcTraces = sieve.traces; + var sLetter = getAxisLetter(sa); + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var fullTrace = calcTrace[0].trace; + var pts = []; + var tozero = false; + + for(var j = 0; j < calcTrace.length; j++) { + var bar = calcTrace[j]; + var base = bar.b; + var top = base + bar.s; + + bar[sLetter] = top; + pts.push(top); + if(bar.hasB) pts.push(base); + + if(!bar.hasB || !bar.b) { + tozero = true; + } + } + + fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, { + tozero: tozero, + padded: true + }); + } +} + +function stackBars(sa, sieve, opts) { + var sLetter = getAxisLetter(sa); + var calcTraces = sieve.traces; + var calcTrace; + var fullTrace; + var isFunnel; + var i, j; + var bar; + + for(i = 0; i < calcTraces.length; i++) { + calcTrace = calcTraces[i]; + fullTrace = calcTrace[0].trace; + + if(fullTrace.type === 'funnel') { + for(j = 0; j < calcTrace.length; j++) { + bar = calcTrace[j]; + + if(bar.s !== BADNUM) { + // create base of funnels + sieve.put(bar.p, -0.5 * bar.s); + } + } + } + } + + for(i = 0; i < calcTraces.length; i++) { + calcTrace = calcTraces[i]; + fullTrace = calcTrace[0].trace; + + isFunnel = (fullTrace.type === 'funnel'); + + var pts = []; + + for(j = 0; j < calcTrace.length; j++) { + bar = calcTrace[j]; + + if(bar.s !== BADNUM) { + // stack current bar and get previous sum + var value; + if(isFunnel) { + value = bar.s; + } else { + value = bar.s + bar.b; + } + + var base = sieve.put(bar.p, value); + + var top = base + value; + + // store the bar base and top in each calcdata item + bar.b = base; + bar[sLetter] = top; + + if(!opts.norm) { + pts.push(top); + if(bar.hasB) { + pts.push(base); + } + } + } + } + + // if barnorm is set, let normalizeBars update the axis range + if(!opts.norm) { + fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, { + // N.B. we don't stack base with 'base', + // so set tozero:true always! + tozero: true, + padded: true + }); + } + } +} + +function sieveBars(sieve) { + var calcTraces = sieve.traces; + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + + for(var j = 0; j < calcTrace.length; j++) { + var bar = calcTrace[j]; + + if(bar.s !== BADNUM) { + sieve.put(bar.p, bar.b + bar.s); + } + } + } +} + +function unhideBarsWithinTrace(sieve, pa) { + var calcTraces = sieve.traces; + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var fullTrace = calcTrace[0].trace; + + if(fullTrace.base === undefined) { + var inTraceSieve = new Sieve([calcTrace], { + posAxis: pa, + sepNegVal: true, + overlapNoMerge: true + }); + + for(var j = 0; j < calcTrace.length; j++) { + var bar = calcTrace[j]; + + if(bar.p !== BADNUM) { + // stack current bar and get previous sum + var base = inTraceSieve.put(bar.p, bar.b + bar.s); + + // if previous sum if non-zero, this means: + // multiple bars have same starting point are potentially hidden, + // shift them vertically so that all bars are visible by default + if(base) bar.b = base; + } + } + } + } +} + +// Note: +// +// normalizeBars requires that either sieveBars or stackBars has been +// previously invoked. +function normalizeBars(sa, sieve, opts) { + var calcTraces = sieve.traces; + var sLetter = getAxisLetter(sa); + var sTop = opts.norm === 'fraction' ? 1 : 100; + var sTiny = sTop / 1e9; // in case of rounding error in sum + var sMin = sa.l2c(sa.c2l(0)); + var sMax = opts.mode === 'stack' ? sTop : sMin; + + function needsPadding(v) { + return ( + isNumeric(sa.c2l(v)) && + ((v < sMin - sTiny) || (v > sMax + sTiny) || !isNumeric(sMin)) + ); + } + + for(var i = 0; i < calcTraces.length; i++) { + var calcTrace = calcTraces[i]; + var fullTrace = calcTrace[0].trace; + var pts = []; + var tozero = false; + var padded = false; + + for(var j = 0; j < calcTrace.length; j++) { + var bar = calcTrace[j]; + + if(bar.s !== BADNUM) { + var scale = Math.abs(sTop / sieve.get(bar.p, bar.s)); + bar.b *= scale; + bar.s *= scale; + + var base = bar.b; + var top = base + bar.s; + + bar[sLetter] = top; + pts.push(top); + padded = padded || needsPadding(top); + + if(bar.hasB) { + pts.push(base); + padded = padded || needsPadding(base); + } + + if(!bar.hasB || !bar.b) { + tozero = true; + } + } + } + + fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, { + tozero: tozero, + padded: padded + }); + } +} + +// find the full position span of bars at each position +// for use by hover, to ensure labels move in if bars are +// narrower than the space they're in. +// run once per trace group (subplot & direction) and +// the same mapping is attached to all calcdata traces +function collectExtents(calcTraces, pa) { + var pLetter = getAxisLetter(pa); + var extents = {}; + var i, j, cd; + + var pMin = Infinity; + var pMax = -Infinity; + + for(i = 0; i < calcTraces.length; i++) { + cd = calcTraces[i]; + for(j = 0; j < cd.length; j++) { + var p = cd[j].p; + if(isNumeric(p)) { + pMin = Math.min(pMin, p); + pMax = Math.max(pMax, p); + } + } + } + + // this is just for positioning of hover labels, and nobody will care if + // the label is 1px too far out; so round positions to 1/10K in case + // position values don't exactly match from trace to trace + var roundFactor = 10000 / (pMax - pMin); + var round = extents.round = function(p) { + return String(Math.round(roundFactor * (p - pMin))); + }; + + for(i = 0; i < calcTraces.length; i++) { + cd = calcTraces[i]; + cd[0].t.extents = extents; + + var poffset = cd[0].t.poffset; + var poffsetIsArray = Array.isArray(poffset); + + for(j = 0; j < cd.length; j++) { + var di = cd[j]; + var p0 = di[pLetter] - di.w / 2; + + if(isNumeric(p0)) { + var p1 = di[pLetter] + di.w / 2; + var pVal = round(di.p); + if(extents[pVal]) { + extents[pVal] = [Math.min(p0, extents[pVal][0]), Math.max(p1, extents[pVal][1])]; + } else { + extents[pVal] = [p0, p1]; + } + } + + di.p0 = di.p + (poffsetIsArray ? poffset[j] : poffset); + di.p1 = di.p0 + di.w; + di.s0 = di.b; + di.s1 = di.s0 + di.s; + } + } +} + +function getAxisLetter(ax) { + return ax._id.charAt(0); +} + +module.exports = { + crossTraceCalc: crossTraceCalc, + setGroupPositions: setGroupPositions +}; + +},{"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/axes":331,"../../plots/cartesian/constraints":339,"../../registry":373,"./sieve.js":396,"fast-isnumeric":31}],387:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../../components/color'); +var Registry = _dereq_('../../registry'); + +var handleXYDefaults = _dereq_('../scatter/xy_defaults'); +var handlePeriodDefaults = _dereq_('../scatter/period_defaults'); +var handleStyleDefaults = _dereq_('./style_defaults'); +var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup; +var attributes = _dereq_('./attributes'); + +var coerceFont = Lib.coerceFont; + +function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var len = handleXYDefaults(traceIn, traceOut, layout, coerce); + if(!len) { + traceOut.visible = false; + return; + } + + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('xhoverformat'); + coerce('yhoverformat'); + + coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v'); + coerce('base'); + coerce('offset'); + coerce('width'); + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + + var textposition = coerce('textposition'); + handleText(traceIn, traceOut, layout, coerce, textposition, { + moduleHasSelected: true, + moduleHasUnselected: true, + moduleHasConstrain: true, + moduleHasCliponaxis: true, + moduleHasTextangle: true, + moduleHasInsideanchor: true + }); + + handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout); + + var lineColor = (traceOut.marker.line || {}).color; + + // override defaultColor for error bars with defaultLine + var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults'); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'}); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'}); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +} + +function handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce) { + var orientation = traceOut.orientation; + // N.B. grouping is done across all trace types that support it + var posAxId = traceOut[{v: 'x', h: 'y'}[orientation] + 'axis']; + var groupId = getAxisGroup(fullLayout, posAxId) + orientation; + + var alignmentOpts = fullLayout._alignmentOpts || {}; + var alignmentgroup = coerce('alignmentgroup'); + + var alignmentGroups = alignmentOpts[groupId]; + if(!alignmentGroups) alignmentGroups = alignmentOpts[groupId] = {}; + + var alignmentGroupOpts = alignmentGroups[alignmentgroup]; + + if(alignmentGroupOpts) { + alignmentGroupOpts.traces.push(traceOut); + } else { + alignmentGroupOpts = alignmentGroups[alignmentgroup] = { + traces: [traceOut], + alignmentIndex: Object.keys(alignmentGroups).length, + offsetGroups: {} + }; + } + + var offsetgroup = coerce('offsetgroup'); + var offsetGroups = alignmentGroupOpts.offsetGroups; + var offsetGroupOpts = offsetGroups[offsetgroup]; + + if(offsetgroup) { + if(!offsetGroupOpts) { + offsetGroupOpts = offsetGroups[offsetgroup] = { + offsetIndex: Object.keys(offsetGroups).length + }; + } + + traceOut._offsetIndex = offsetGroupOpts.offsetIndex; + } +} + +function crossTraceDefaults(fullData, fullLayout) { + var traceIn, traceOut; + + function coerce(attr) { + return Lib.coerce(traceOut._input, traceOut, attributes, attr); + } + + if(fullLayout.barmode === 'group') { + for(var i = 0; i < fullData.length; i++) { + traceOut = fullData[i]; + + if(traceOut.type === 'bar') { + traceIn = traceOut._input; + handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce); + } + } + } +} + +function handleText(traceIn, traceOut, layout, coerce, textposition, opts) { + opts = opts || {}; + var moduleHasSelected = !(opts.moduleHasSelected === false); + var moduleHasUnselected = !(opts.moduleHasUnselected === false); + var moduleHasConstrain = !(opts.moduleHasConstrain === false); + var moduleHasCliponaxis = !(opts.moduleHasCliponaxis === false); + var moduleHasTextangle = !(opts.moduleHasTextangle === false); + var moduleHasInsideanchor = !(opts.moduleHasInsideanchor === false); + var hasPathbar = !!opts.hasPathbar; + + var hasBoth = Array.isArray(textposition) || textposition === 'auto'; + var hasInside = hasBoth || textposition === 'inside'; + var hasOutside = hasBoth || textposition === 'outside'; + + if(hasInside || hasOutside) { + var dfltFont = coerceFont(coerce, 'textfont', layout.font); + + // Note that coercing `insidetextfont` is always needed – + // even if `textposition` is `outside` for each trace – since + // an outside label can become an inside one, for example because + // of a bar being stacked on top of it. + var insideTextFontDefault = Lib.extendFlat({}, dfltFont); + var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color; + var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet; + if(isColorInheritedFromLayoutFont) { + delete insideTextFontDefault.color; + } + coerceFont(coerce, 'insidetextfont', insideTextFontDefault); + + if(hasPathbar) { + var pathbarTextFontDefault = Lib.extendFlat({}, dfltFont); + if(isColorInheritedFromLayoutFont) { + delete pathbarTextFontDefault.color; + } + coerceFont(coerce, 'pathbar.textfont', pathbarTextFontDefault); + } + + if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont); + + if(moduleHasSelected) coerce('selected.textfont.color'); + if(moduleHasUnselected) coerce('unselected.textfont.color'); + if(moduleHasConstrain) coerce('constraintext'); + if(moduleHasCliponaxis) coerce('cliponaxis'); + if(moduleHasTextangle) coerce('textangle'); + + coerce('texttemplate'); + } + + if(hasInside) { + if(moduleHasInsideanchor) coerce('insidetextanchor'); + } +} + +module.exports = { + supplyDefaults: supplyDefaults, + crossTraceDefaults: crossTraceDefaults, + handleGroupingDefaults: handleGroupingDefaults, + handleText: handleText +}; + +},{"../../components/color":155,"../../lib":285,"../../plots/cartesian/constraints":339,"../../registry":373,"../scatter/period_defaults":514,"../scatter/xy_defaults":521,"./attributes":383,"./style_defaults":398}],388:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function eventData(out, pt, trace) { + // standard cartesian event data + out.x = 'xVal' in pt ? pt.xVal : pt.x; + out.y = 'yVal' in pt ? pt.yVal : pt.y; + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + if(trace.orientation === 'h') { + out.label = out.y; + out.value = out.x; + } else { + out.label = out.x; + out.value = out.y; + } + + return out; +}; + +},{}],389:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; + +exports.coerceString = function(attributeDefinition, value, defaultValue) { + if(typeof value === 'string') { + if(value || !attributeDefinition.noBlank) return value; + } else if(typeof value === 'number' || value === true) { + if(!attributeDefinition.strict) return String(value); + } + + return (defaultValue !== undefined) ? + defaultValue : + attributeDefinition.dflt; +}; + +exports.coerceNumber = function(attributeDefinition, value, defaultValue) { + if(isNumeric(value)) { + value = +value; + + var min = attributeDefinition.min; + var max = attributeDefinition.max; + var isOutOfBounds = (min !== undefined && value < min) || + (max !== undefined && value > max); + + if(!isOutOfBounds) return value; + } + + return (defaultValue !== undefined) ? + defaultValue : + attributeDefinition.dflt; +}; + +exports.coerceColor = function(attributeDefinition, value, defaultValue) { + if(tinycolor(value).isValid()) return value; + + return (defaultValue !== undefined) ? + defaultValue : + attributeDefinition.dflt; +}; + +exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) { + if(attributeDefinition.coerceNumber) value = +value; + + if(attributeDefinition.values.indexOf(value) !== -1) return value; + + return (defaultValue !== undefined) ? + defaultValue : + attributeDefinition.dflt; +}; + +exports.getValue = function(arrayOrScalar, index) { + var value; + if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar; + else if(index < arrayOrScalar.length) value = arrayOrScalar[index]; + return value; +}; + +exports.getLineWidth = function(trace, di) { + var w = + (0 < di.mlw) ? di.mlw : + !isArrayOrTypedArray(trace.marker.line.width) ? trace.marker.line.width : + 0; + + return w; +}; + +},{"../../lib":285,"fast-isnumeric":31,"tinycolor2":119}],390:[function(_dereq_,module,exports){ +'use strict'; + +var Fx = _dereq_('../../components/fx'); +var Registry = _dereq_('../../registry'); +var Color = _dereq_('../../components/color'); + +var fillText = _dereq_('../../lib').fillText; +var getLineWidth = _dereq_('./helpers').getLineWidth; +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +function hoverPoints(pointData, xval, yval, hovermode, opts) { + var barPointData = hoverOnBars(pointData, xval, yval, hovermode, opts); + + if(barPointData) { + var cd = barPointData.cd; + var trace = cd[0].trace; + var di = cd[barPointData.index]; + + barPointData.color = getTraceColor(trace, di); + Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, barPointData); + + return [barPointData]; + } +} + +function hoverOnBars(pointData, xval, yval, hovermode, opts) { + var cd = pointData.cd; + var trace = cd[0].trace; + var t = cd[0].t; + var isClosest = (hovermode === 'closest'); + var isWaterfall = (trace.type === 'waterfall'); + var maxHoverDistance = pointData.maxHoverDistance; + var maxSpikeDistance = pointData.maxSpikeDistance; + + var posVal, sizeVal, posLetter, sizeLetter, dx, dy, pRangeCalc; + + if(trace.orientation === 'h') { + posVal = yval; + sizeVal = xval; + posLetter = 'y'; + sizeLetter = 'x'; + dx = sizeFn; + dy = positionFn; + } else { + posVal = xval; + sizeVal = yval; + posLetter = 'x'; + sizeLetter = 'y'; + dy = sizeFn; + dx = positionFn; + } + + var period = trace[posLetter + 'period']; + + function thisBarMinPos(di) { return thisBarExtPos(di, -1); } + function thisBarMaxPos(di) { return thisBarExtPos(di, 1); } + + function thisBarExtPos(di, sgn) { + if(period) { + return di.p + sgn * Math.abs(di.p - di.orig_p); + } + return di[posLetter] + sgn * di.w / 2; + } + + var minPos = isClosest || period ? + thisBarMinPos : + function(di) { + /* + * In compare mode, accept a bar if you're on it *or* its group. + * Nearly always it's the group that matters, but in case the bar + * was explicitly set wider than its group we'd better accept the + * whole bar. + * + * use `bardelta` instead of `bargroupwidth` so we accept hover + * in the gap. That way hover doesn't flash on and off as you + * mouse over the plot in compare modes. + * In 'closest' mode though the flashing seems inevitable, + * without far more complex logic + */ + return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2); + }; + + var maxPos = isClosest || period ? + thisBarMaxPos : + function(di) { + return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2); + }; + + function inbox(_minPos, _maxPos, maxDistance) { + if(opts.finiteRange) maxDistance = 0; + + // add a little to the pseudo-distance for wider bars, so that like scatter, + // if you are over two overlapping bars, the narrower one wins. + return Fx.inbox(_minPos - posVal, _maxPos - posVal, + maxDistance + Math.min(1, Math.abs(_maxPos - _minPos) / pRangeCalc) - 1); + } + + function positionFn(di) { + return inbox(minPos(di), maxPos(di), maxHoverDistance); + } + + function thisBarPositionFn(di) { + return inbox(thisBarMinPos(di), thisBarMaxPos(di), maxSpikeDistance); + } + + function getSize(di) { + var s = di[sizeLetter]; + + if(isWaterfall) { + var rawS = Math.abs(di.rawS) || 0; + if(sizeVal > 0) { + s += rawS; + } else if(sizeVal < 0) { + s -= rawS; + } + } + + return s; + } + + function sizeFn(di) { + var v = sizeVal; + var b = di.b; + var s = getSize(di); + + // add a gradient so hovering near the end of a + // bar makes it a little closer match + return Fx.inbox(b - v, s - v, maxHoverDistance + (s - v) / (s - b) - 1); + } + + function thisBarSizeFn(di) { + var v = sizeVal; + var b = di.b; + var s = getSize(di); + + // add a gradient so hovering near the end of a + // bar makes it a little closer match + return Fx.inbox(b - v, s - v, maxSpikeDistance + (s - v) / (s - b) - 1); + } + + var pa = pointData[posLetter + 'a']; + var sa = pointData[sizeLetter + 'a']; + + pRangeCalc = Math.abs(pa.r2c(pa.range[1]) - pa.r2c(pa.range[0])); + + function dxy(di) { return (dx(di) + dy(di)) / 2; } + var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); + Fx.getClosest(cd, distfn, pointData); + + // skip the rest (for this trace) if we didn't find a close point + if(pointData.index === false) return; + + // skip points inside axis rangebreaks + if(cd[pointData.index].p === BADNUM) return; + + // if we get here and we're not in 'closest' mode, push min/max pos back + // onto the group - even though that means occasionally the mouse will be + // over the hover label. + if(!isClosest) { + minPos = function(di) { + return Math.min(thisBarMinPos(di), di.p - t.bargroupwidth / 2); + }; + maxPos = function(di) { + return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2); + }; + } + + // the closest data point + var index = pointData.index; + var di = cd[index]; + + var size = (trace.base) ? di.b + di.s : di.s; + pointData[sizeLetter + '0'] = pointData[sizeLetter + '1'] = sa.c2p(di[sizeLetter], true); + pointData[sizeLetter + 'LabelVal'] = size; + + var extent = t.extents[t.extents.round(di.p)]; + pointData[posLetter + '0'] = pa.c2p(isClosest ? minPos(di) : extent[0], true); + pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true); + + var hasPeriod = di.orig_p !== undefined; + pointData[posLetter + 'LabelVal'] = hasPeriod ? di.orig_p : di.p; + + pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal'], trace[posLetter + 'hoverformat']); + pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal'], trace[sizeLetter + 'hoverformat']); + pointData.baseLabel = hoverLabelText(sa, di.b, trace[sizeLetter + 'hoverformat']); + + // spikelines always want "closest" distance regardless of hovermode + pointData.spikeDistance = (thisBarSizeFn(di) + thisBarPositionFn(di)) / 2; + // they also want to point to the data value, regardless of where the label goes + // in case of bars shifted within groups + pointData[posLetter + 'Spike'] = pa.c2p(di.p, true); + + fillText(di, trace, pointData); + pointData.hovertemplate = trace.hovertemplate; + + return pointData; +} + +function getTraceColor(trace, di) { + var mc = di.mcc || trace.marker.color; + var mlc = di.mlcc || trace.marker.line.color; + var mlw = getLineWidth(trace, di); + + if(Color.opacity(mc)) return mc; + else if(Color.opacity(mlc) && mlw) return mlc; +} + +module.exports = { + hoverPoints: hoverPoints, + hoverOnBars: hoverOnBars, + getTraceColor: getTraceColor +}; + +},{"../../components/color":155,"../../components/fx":195,"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/axes":331,"../../registry":373,"./helpers":389}],391:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + layoutAttributes: _dereq_('./layout_attributes'), + supplyDefaults: _dereq_('./defaults').supplyDefaults, + crossTraceDefaults: _dereq_('./defaults').crossTraceDefaults, + supplyLayoutDefaults: _dereq_('./layout_defaults'), + calc: _dereq_('./calc'), + crossTraceCalc: _dereq_('./cross_trace_calc').crossTraceCalc, + colorbar: _dereq_('../scatter/marker_colorbar'), + arraysToCalcdata: _dereq_('./arrays_to_calcdata'), + plot: _dereq_('./plot').plot, + style: _dereq_('./style').style, + styleOnSelect: _dereq_('./style').styleOnSelect, + hoverPoints: _dereq_('./hover').hoverPoints, + eventData: _dereq_('./event_data'), + selectPoints: _dereq_('./select'), + + moduleType: 'trace', + name: 'bar', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['bar-like', 'cartesian', 'svg', 'bar', 'oriented', 'errorBarsOK', 'showLegend', 'zoomScale'], + animatable: true, + meta: { + } +}; + +},{"../../plots/cartesian":345,"../scatter/marker_colorbar":512,"./arrays_to_calcdata":382,"./attributes":383,"./calc":384,"./cross_trace_calc":386,"./defaults":387,"./event_data":388,"./hover":390,"./layout_attributes":392,"./layout_defaults":393,"./plot":394,"./select":395,"./style":397}],392:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + barmode: { + valType: 'enumerated', + values: ['stack', 'group', 'overlay', 'relative'], + dflt: 'group', + editType: 'calc', + }, + barnorm: { + valType: 'enumerated', + values: ['', 'fraction', 'percent'], + dflt: '', + editType: 'calc', + }, + bargap: { + valType: 'number', + min: 0, + max: 1, + editType: 'calc', + }, + bargroupgap: { + valType: 'number', + min: 0, + max: 1, + dflt: 0, + editType: 'calc', + } +}; + +},{}],393:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); + +var layoutAttributes = _dereq_('./layout_attributes'); + +module.exports = function(layoutIn, layoutOut, fullData) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + + var hasBars = false; + var shouldBeGapless = false; + var gappedAnyway = false; + var usedSubplots = {}; + + var mode = coerce('barmode'); + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + if(Registry.traceIs(trace, 'bar') && trace.visible) hasBars = true; + else continue; + + // if we have at least 2 grouped bar traces on the same subplot, + // we should default to a gap anyway, even if the data is histograms + if(mode === 'group') { + var subploti = trace.xaxis + trace.yaxis; + if(usedSubplots[subploti]) gappedAnyway = true; + usedSubplots[subploti] = true; + } + + if(trace.visible && trace.type === 'histogram') { + var pa = Axes.getFromId({_fullLayout: layoutOut}, + trace[trace.orientation === 'v' ? 'xaxis' : 'yaxis']); + if(pa.type !== 'category') shouldBeGapless = true; + } + } + + if(!hasBars) { + delete layoutOut.barmode; + return; + } + + if(mode !== 'overlay') coerce('barnorm'); + + coerce('bargap', (shouldBeGapless && !gappedAnyway) ? 0 : 0.2); + coerce('bargroupgap'); +}; + +},{"../../lib":285,"../../plots/cartesian/axes":331,"../../registry":373,"./layout_attributes":392}],394:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); + +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Registry = _dereq_('../../registry'); +var tickText = _dereq_('../../plots/cartesian/axes').tickText; + +var uniformText = _dereq_('./uniform_text'); +var recordMinTextSize = uniformText.recordMinTextSize; +var clearMinTextSize = uniformText.clearMinTextSize; + +var style = _dereq_('./style'); +var helpers = _dereq_('./helpers'); +var constants = _dereq_('./constants'); +var attributes = _dereq_('./attributes'); + +var attributeText = attributes.text; +var attributeTextPosition = attributes.textposition; + +var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue; + +var TEXTPAD = constants.TEXTPAD; + +function keyFunc(d) {return d.id;} +function getKeyFunc(trace) { + if(trace.ids) { + return keyFunc; + } +} + +function dirSign(a, b) { + return (a < b) ? 1 : -1; +} + +function getXY(di, xa, ya, isHorizontal) { + var s = []; + var p = []; + + var sAxis = isHorizontal ? xa : ya; + var pAxis = isHorizontal ? ya : xa; + + s[0] = sAxis.c2p(di.s0, true); + p[0] = pAxis.c2p(di.p0, true); + + s[1] = sAxis.c2p(di.s1, true); + p[1] = pAxis.c2p(di.p1, true); + + return isHorizontal ? [s, p] : [p, s]; +} + +function transition(selection, fullLayout, opts, makeOnCompleteCallback) { + if(!fullLayout.uniformtext.mode && hasTransition(opts)) { + var onComplete; + if(makeOnCompleteCallback) { + onComplete = makeOnCompleteCallback(); + } + return selection + .transition() + .duration(opts.duration) + .ease(opts.easing) + .each('end', function() { onComplete && onComplete(); }) + .each('interrupt', function() { onComplete && onComplete(); }); + } else { + return selection; + } +} + +function hasTransition(transitionOpts) { + return transitionOpts && transitionOpts.duration > 0; +} + +function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var fullLayout = gd._fullLayout; + + if(!opts) { + opts = { + mode: fullLayout.barmode, + norm: fullLayout.barmode, + gap: fullLayout.bargap, + groupgap: fullLayout.bargroupgap + }; + + // don't clear bar when this is called from waterfall or funnel + clearMinTextSize('bar', fullLayout); + } + + var bartraces = Lib.makeTraceGroups(traceLayer, cdModule, 'trace bars').each(function(cd) { + var plotGroup = d3.select(this); + var trace = cd[0].trace; + var isWaterfall = (trace.type === 'waterfall'); + var isFunnel = (trace.type === 'funnel'); + var isBar = (trace.type === 'bar'); + var shouldDisplayZeros = (isBar || isFunnel); + + var adjustPixel = 0; + if(isWaterfall && trace.connector.visible && trace.connector.mode === 'between') { + adjustPixel = trace.connector.line.width / 2; + } + + var isHorizontal = (trace.orientation === 'h'); + var withTransition = hasTransition(opts); + + var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points'); + + var keyFunc = getKeyFunc(trace); + var bars = pointGroup.selectAll('g.point').data(Lib.identity, keyFunc); + + bars.enter().append('g') + .classed('point', true); + + bars.exit().remove(); + + bars.each(function(di, i) { + var bar = d3.select(this); + + // now display the bar + // clipped xf/yf (2nd arg true): non-positive + // log values go off-screen by plotwidth + // so you see them continue if you drag the plot + var xy = getXY(di, xa, ya, isHorizontal); + + var x0 = xy[0][0]; + var x1 = xy[0][1]; + var y0 = xy[1][0]; + var y1 = xy[1][1]; + + // empty bars + var isBlank = (isHorizontal ? x1 - x0 : y1 - y0) === 0; + + // display zeros if line.width > 0 + if(isBlank && shouldDisplayZeros && helpers.getLineWidth(trace, di)) { + isBlank = false; + } + + // skip nulls + if(!isBlank) { + isBlank = ( + !isNumeric(x0) || + !isNumeric(x1) || + !isNumeric(y0) || + !isNumeric(y1) + ); + } + + // record isBlank + di.isBlank = isBlank; + + // for blank bars, ensure start and end positions are equal - important for smooth transitions + if(isBlank) { + if(isHorizontal) { + x1 = x0; + } else { + y1 = y0; + } + } + + // in waterfall mode `between` we need to adjust bar end points to match the connector width + if(adjustPixel && !isBlank) { + if(isHorizontal) { + x0 -= dirSign(x0, x1) * adjustPixel; + x1 += dirSign(x0, x1) * adjustPixel; + } else { + y0 -= dirSign(y0, y1) * adjustPixel; + y1 += dirSign(y0, y1) * adjustPixel; + } + } + + var lw; + var mc; + + if(trace.type === 'waterfall') { + if(!isBlank) { + var cont = trace[di.dir].marker; + lw = cont.line.width; + mc = cont.color; + } + } else { + lw = helpers.getLineWidth(trace, di); + mc = di.mc || trace.marker.color; + } + + function roundWithLine(v) { + var offset = d3.round((lw / 2) % 1, 2); + + // if there are explicit gaps, don't round, + // it can make the gaps look crappy + return (opts.gap === 0 && opts.groupgap === 0) ? + d3.round(Math.round(v) - offset, 2) : v; + } + + function expandToVisible(v, vc, hideZeroSpan) { + if(hideZeroSpan && v === vc) { + // should not expand zero span bars + // when start and end positions are identical + // i.e. for vertical when y0 === y1 + // and for horizontal when x0 === x1 + return v; + } + + // if it's not in danger of disappearing entirely, + // round more precisely + return Math.abs(v - vc) >= 2 ? roundWithLine(v) : + // but if it's very thin, expand it so it's + // necessarily visible, even if it might overlap + // its neighbor + (v > vc ? Math.ceil(v) : Math.floor(v)); + } + + if(!gd._context.staticPlot) { + // if bars are not fully opaque or they have a line + // around them, round to integer pixels, mainly for + // safari so we prevent overlaps from its expansive + // pixelation. if the bars ARE fully opaque and have + // no line, expand to a full pixel to make sure we + // can see them + + var op = Color.opacity(mc); + var fixpx = (op < 1 || lw > 0.01) ? roundWithLine : expandToVisible; + + x0 = fixpx(x0, x1, isHorizontal); + x1 = fixpx(x1, x0, isHorizontal); + y0 = fixpx(y0, y1, !isHorizontal); + y1 = fixpx(y1, y0, !isHorizontal); + } + + var sel = transition(Lib.ensureSingle(bar, 'path'), fullLayout, opts, makeOnCompleteCallback); + sel + .style('vector-effect', 'non-scaling-stroke') + .attr('d', (isNaN((x1 - x0) * (y1 - y0)) || (isBlank && gd._context.staticPlot)) ? 'M0,0Z' : 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z') + .call(Drawing.setClipUrl, plotinfo.layerClipId, gd); + + if(!fullLayout.uniformtext.mode && withTransition) { + var styleFns = Drawing.makePointStyleFns(trace); + Drawing.singlePointStyle(di, sel, trace, styleFns, gd); + } + + appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback); + + if(plotinfo.layerClipId) { + Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar); + } + }); + + // lastly, clip points groups of `cliponaxis !== false` traces + // on `plotinfo._hasClipOnAxisFalse === true` subplots + var hasClipOnAxisFalse = trace.cliponaxis === false; + Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId, gd); + }); + + // error bars are on the top + Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo, opts); +} + +function appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var fullLayout = gd._fullLayout; + var textPosition; + + function appendTextNode(bar, text, font) { + var textSelection = Lib.ensureSingle(bar, 'text') + .text(text) + .attr({ + 'class': 'bartext bartext-' + textPosition, + 'text-anchor': 'middle', + // prohibit tex interpretation until we can handle + // tex and regular text together + 'data-notex': 1 + }) + .call(Drawing.font, font) + .call(svgTextUtils.convertToTspans, gd); + + return textSelection; + } + + // get trace attributes + var trace = cd[0].trace; + var isHorizontal = (trace.orientation === 'h'); + + var text = getText(fullLayout, cd, i, xa, ya); + textPosition = getTextPosition(trace, i); + + // compute text position + var inStackOrRelativeMode = + opts.mode === 'stack' || + opts.mode === 'relative'; + + var calcBar = cd[i]; + var isOutmostBar = !inStackOrRelativeMode || calcBar._outmost; + + if(!text || + textPosition === 'none' || + ((calcBar.isBlank || x0 === x1 || y0 === y1) && ( + textPosition === 'auto' || + textPosition === 'inside'))) { + bar.select('text').remove(); + return; + } + + var layoutFont = fullLayout.font; + var barColor = style.getBarColor(cd[i], trace); + var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor); + var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont); + + // Special case: don't use the c2p(v, true) value on log size axes, + // so that we can get correctly inside text scaling + var di = bar.datum(); + if(isHorizontal) { + if(xa.type === 'log' && di.s0 <= 0) { + if(xa.range[0] < xa.range[1]) { + x0 = 0; + } else { + x0 = xa._length; + } + } + } else { + if(ya.type === 'log' && di.s0 <= 0) { + if(ya.range[0] < ya.range[1]) { + y0 = ya._length; + } else { + y0 = 0; + } + } + } + + // padding excluded + var barWidth = Math.abs(x1 - x0) - 2 * TEXTPAD; + var barHeight = Math.abs(y1 - y0) - 2 * TEXTPAD; + + var textSelection; + var textBB; + var textWidth; + var textHeight; + var font; + + if(textPosition === 'outside') { + if(!isOutmostBar && !calcBar.hasB) textPosition = 'inside'; + } + + if(textPosition === 'auto') { + if(isOutmostBar) { + // draw text using insideTextFont and check if it fits inside bar + textPosition = 'inside'; + + font = Lib.ensureUniformFontSize(gd, insideTextFont); + + textSelection = appendTextNode(bar, text, font); + + textBB = Drawing.bBox(textSelection.node()), + textWidth = textBB.width, + textHeight = textBB.height; + + var textHasSize = (textWidth > 0 && textHeight > 0); + var fitsInside = (textWidth <= barWidth && textHeight <= barHeight); + var fitsInsideIfRotated = (textWidth <= barHeight && textHeight <= barWidth); + var fitsInsideIfShrunk = (isHorizontal) ? + (barWidth >= textWidth * (barHeight / textHeight)) : + (barHeight >= textHeight * (barWidth / textWidth)); + + if(textHasSize && ( + fitsInside || + fitsInsideIfRotated || + fitsInsideIfShrunk) + ) { + textPosition = 'inside'; + } else { + textPosition = 'outside'; + textSelection.remove(); + textSelection = null; + } + } else { + textPosition = 'inside'; + } + } + + if(!textSelection) { + font = Lib.ensureUniformFontSize(gd, (textPosition === 'outside') ? outsideTextFont : insideTextFont); + + textSelection = appendTextNode(bar, text, font); + + var currentTransform = textSelection.attr('transform'); + textSelection.attr('transform', ''); + textBB = Drawing.bBox(textSelection.node()), + textWidth = textBB.width, + textHeight = textBB.height; + textSelection.attr('transform', currentTransform); + + if(textWidth <= 0 || textHeight <= 0) { + textSelection.remove(); + return; + } + } + + var angle = trace.textangle; + + // compute text transform + var transform, constrained; + if(textPosition === 'outside') { + constrained = + trace.constraintext === 'both' || + trace.constraintext === 'outside'; + + transform = toMoveOutsideBar(x0, x1, y0, y1, textBB, { + isHorizontal: isHorizontal, + constrained: constrained, + angle: angle + }); + } else { + constrained = + trace.constraintext === 'both' || + trace.constraintext === 'inside'; + + transform = toMoveInsideBar(x0, x1, y0, y1, textBB, { + isHorizontal: isHorizontal, + constrained: constrained, + angle: angle, + anchor: trace.insidetextanchor + }); + } + + transform.fontSize = font.size; + recordMinTextSize(trace.type, transform, fullLayout); + calcBar.transform = transform; + + transition(textSelection, fullLayout, opts, makeOnCompleteCallback) + .attr('transform', Lib.getTextTransform(transform)); +} + +function getRotateFromAngle(angle) { + return (angle === 'auto') ? 0 : angle; +} + +function getRotatedTextSize(textBB, rotate) { + var a = Math.PI / 180 * rotate; + var absSin = Math.abs(Math.sin(a)); + var absCos = Math.abs(Math.cos(a)); + + return { + x: textBB.width * absCos + textBB.height * absSin, + y: textBB.width * absSin + textBB.height * absCos + }; +} + +function toMoveInsideBar(x0, x1, y0, y1, textBB, opts) { + var isHorizontal = !!opts.isHorizontal; + var constrained = !!opts.constrained; + var angle = opts.angle || 0; + var anchor = opts.anchor || 'end'; + var isEnd = anchor === 'end'; + var isStart = anchor === 'start'; + var leftToRight = opts.leftToRight || 0; // left: -1, center: 0, right: 1 + var toRight = (leftToRight + 1) / 2; + var toLeft = 1 - toRight; + + var textWidth = textBB.width; + var textHeight = textBB.height; + var lx = Math.abs(x1 - x0); + var ly = Math.abs(y1 - y0); + + // compute remaining space + var textpad = ( + lx > (2 * TEXTPAD) && + ly > (2 * TEXTPAD) + ) ? TEXTPAD : 0; + + lx -= 2 * textpad; + ly -= 2 * textpad; + + var rotate = getRotateFromAngle(angle); + if((angle === 'auto') && + !(textWidth <= lx && textHeight <= ly) && + (textWidth > lx || textHeight > ly) && ( + !(textWidth > ly || textHeight > lx) || + ((textWidth < textHeight) !== (lx < ly)) + )) { + rotate += 90; + } + + var t = getRotatedTextSize(textBB, rotate); + + var scale = 1; + if(constrained) { + scale = Math.min( + 1, + lx / t.x, + ly / t.y + ); + } + + // compute text and target positions + var textX = ( + textBB.left * toLeft + + textBB.right * toRight + ); + var textY = (textBB.top + textBB.bottom) / 2; + var targetX = ( + (x0 + TEXTPAD) * toLeft + + (x1 - TEXTPAD) * toRight + ); + var targetY = (y0 + y1) / 2; + var anchorX = 0; + var anchorY = 0; + if(isStart || isEnd) { + var extrapad = (isHorizontal ? t.x : t.y) / 2; + var dir = isHorizontal ? dirSign(x0, x1) : dirSign(y0, y1); + + if(isHorizontal) { + if(isStart) { + targetX = x0 + dir * textpad; + anchorX = -dir * extrapad; + } else { + targetX = x1 - dir * textpad; + anchorX = dir * extrapad; + } + } else { + if(isStart) { + targetY = y0 + dir * textpad; + anchorY = -dir * extrapad; + } else { + targetY = y1 - dir * textpad; + anchorY = dir * extrapad; + } + } + } + + return { + textX: textX, + textY: textY, + targetX: targetX, + targetY: targetY, + anchorX: anchorX, + anchorY: anchorY, + scale: scale, + rotate: rotate + }; +} + +function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) { + var isHorizontal = !!opts.isHorizontal; + var constrained = !!opts.constrained; + var angle = opts.angle || 0; + + var textWidth = textBB.width; + var textHeight = textBB.height; + var lx = Math.abs(x1 - x0); + var ly = Math.abs(y1 - y0); + + var textpad; + // Keep the padding so the text doesn't sit right against + // the bars, but don't factor it into barWidth + if(isHorizontal) { + textpad = (ly > 2 * TEXTPAD) ? TEXTPAD : 0; + } else { + textpad = (lx > 2 * TEXTPAD) ? TEXTPAD : 0; + } + + // compute rotate and scale + var scale = 1; + if(constrained) { + scale = (isHorizontal) ? + Math.min(1, ly / textHeight) : + Math.min(1, lx / textWidth); + } + + var rotate = getRotateFromAngle(angle); + var t = getRotatedTextSize(textBB, rotate); + + // compute text and target positions + var extrapad = (isHorizontal ? t.x : t.y) / 2; + var textX = (textBB.left + textBB.right) / 2; + var textY = (textBB.top + textBB.bottom) / 2; + var targetX = (x0 + x1) / 2; + var targetY = (y0 + y1) / 2; + var anchorX = 0; + var anchorY = 0; + + var dir = isHorizontal ? dirSign(x1, x0) : dirSign(y0, y1); + if(isHorizontal) { + targetX = x1 - dir * textpad; + anchorX = dir * extrapad; + } else { + targetY = y1 + dir * textpad; + anchorY = -dir * extrapad; + } + + return { + textX: textX, + textY: textY, + targetX: targetX, + targetY: targetY, + anchorX: anchorX, + anchorY: anchorY, + scale: scale, + rotate: rotate + }; +} + +function getText(fullLayout, cd, index, xa, ya) { + var trace = cd[0].trace; + var texttemplate = trace.texttemplate; + + var value; + if(texttemplate) { + value = calcTexttemplate(fullLayout, cd, index, xa, ya); + } else if(trace.textinfo) { + value = calcTextinfo(cd, index, xa, ya); + } else { + value = helpers.getValue(trace.text, index); + } + + return helpers.coerceString(attributeText, value); +} + +function getTextPosition(trace, index) { + var value = helpers.getValue(trace.textposition, index); + return helpers.coerceEnumerated(attributeTextPosition, value); +} + +function calcTexttemplate(fullLayout, cd, index, xa, ya) { + var trace = cd[0].trace; + var texttemplate = Lib.castOption(trace, index, 'texttemplate'); + if(!texttemplate) return ''; + var isWaterfall = (trace.type === 'waterfall'); + var isFunnel = (trace.type === 'funnel'); + + var pLetter, pAxis; + var vLetter, vAxis; + if(trace.orientation === 'h') { + pLetter = 'y'; + pAxis = ya; + vLetter = 'x'; + vAxis = xa; + } else { + pLetter = 'x'; + pAxis = xa; + vLetter = 'y'; + vAxis = ya; + } + + function formatLabel(u) { + return tickText(pAxis, pAxis.c2l(u), true).text; + } + + function formatNumber(v) { + return tickText(vAxis, vAxis.c2l(v), true).text; + } + + var cdi = cd[index]; + var obj = {}; + + obj.label = cdi.p; + obj.labelLabel = obj[pLetter + 'Label'] = formatLabel(cdi.p); + + var tx = Lib.castOption(trace, cdi.i, 'text'); + if(tx === 0 || tx) obj.text = tx; + + obj.value = cdi.s; + obj.valueLabel = obj[vLetter + 'Label'] = formatNumber(cdi.s); + + var pt = {}; + appendArrayPointValue(pt, trace, cdi.i); + + if(isWaterfall) { + obj.delta = +cdi.rawS || cdi.s; + obj.deltaLabel = formatNumber(obj.delta); + obj.final = cdi.v; + obj.finalLabel = formatNumber(obj.final); + obj.initial = obj.final - obj.delta; + obj.initialLabel = formatNumber(obj.initial); + } + + if(isFunnel) { + obj.value = cdi.s; + obj.valueLabel = formatNumber(obj.value); + + obj.percentInitial = cdi.begR; + obj.percentInitialLabel = Lib.formatPercent(cdi.begR); + obj.percentPrevious = cdi.difR; + obj.percentPreviousLabel = Lib.formatPercent(cdi.difR); + obj.percentTotal = cdi.sumR; + obj.percenTotalLabel = Lib.formatPercent(cdi.sumR); + } + + var customdata = Lib.castOption(trace, cdi.i, 'customdata'); + if(customdata) obj.customdata = customdata; + return Lib.texttemplateString(texttemplate, obj, fullLayout._d3locale, pt, obj, trace._meta || {}); +} + +function calcTextinfo(cd, index, xa, ya) { + var trace = cd[0].trace; + var isHorizontal = (trace.orientation === 'h'); + var isWaterfall = (trace.type === 'waterfall'); + var isFunnel = (trace.type === 'funnel'); + + function formatLabel(u) { + var pAxis = isHorizontal ? ya : xa; + return tickText(pAxis, u, true).text; + } + + function formatNumber(v) { + var sAxis = isHorizontal ? xa : ya; + return tickText(sAxis, +v, true).text; + } + + var textinfo = trace.textinfo; + var cdi = cd[index]; + + var parts = textinfo.split('+'); + var text = []; + var tx; + + var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; }; + + if(hasFlag('label')) { + text.push(formatLabel(cd[index].p)); + } + + if(hasFlag('text')) { + tx = Lib.castOption(trace, cdi.i, 'text'); + if(tx === 0 || tx) text.push(tx); + } + + if(isWaterfall) { + var delta = +cdi.rawS || cdi.s; + var final = cdi.v; + var initial = final - delta; + + if(hasFlag('initial')) text.push(formatNumber(initial)); + if(hasFlag('delta')) text.push(formatNumber(delta)); + if(hasFlag('final')) text.push(formatNumber(final)); + } + + if(isFunnel) { + if(hasFlag('value')) text.push(formatNumber(cdi.s)); + + var nPercent = 0; + if(hasFlag('percent initial')) nPercent++; + if(hasFlag('percent previous')) nPercent++; + if(hasFlag('percent total')) nPercent++; + + var hasMultiplePercents = nPercent > 1; + + if(hasFlag('percent initial')) { + tx = Lib.formatPercent(cdi.begR); + if(hasMultiplePercents) tx += ' of initial'; + text.push(tx); + } + if(hasFlag('percent previous')) { + tx = Lib.formatPercent(cdi.difR); + if(hasMultiplePercents) tx += ' of previous'; + text.push(tx); + } + if(hasFlag('percent total')) { + tx = Lib.formatPercent(cdi.sumR); + if(hasMultiplePercents) tx += ' of total'; + text.push(tx); + } + } + + return text.join('
'); +} + +module.exports = { + plot: plot, + toMoveInsideBar: toMoveInsideBar +}; + +},{"../../components/color":155,"../../components/drawing":177,"../../components/fx/helpers":191,"../../lib":285,"../../lib/svg_text_utils":307,"../../plots/cartesian/axes":331,"../../registry":373,"./attributes":383,"./constants":385,"./helpers":389,"./style":397,"./uniform_text":399,"@plotly/d3":20,"fast-isnumeric":31}],395:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function selectPoints(searchInfo, selectionTester) { + var cd = searchInfo.cd; + var xa = searchInfo.xaxis; + var ya = searchInfo.yaxis; + var trace = cd[0].trace; + var isFunnel = (trace.type === 'funnel'); + var isHorizontal = (trace.orientation === 'h'); + var selection = []; + var i; + + if(selectionTester === false) { + // clear selection + for(i = 0; i < cd.length; i++) { + cd[i].selected = 0; + } + } else { + for(i = 0; i < cd.length; i++) { + var di = cd[i]; + var ct = 'ct' in di ? di.ct : getCentroid(di, xa, ya, isHorizontal, isFunnel); + + if(selectionTester.contains(ct, false, i, searchInfo)) { + selection.push({ + pointNumber: i, + x: xa.c2d(di.x), + y: ya.c2d(di.y) + }); + di.selected = 1; + } else { + di.selected = 0; + } + } + } + + return selection; +}; + +function getCentroid(d, xa, ya, isHorizontal, isFunnel) { + var x0 = xa.c2p(isHorizontal ? d.s0 : d.p0, true); + var x1 = xa.c2p(isHorizontal ? d.s1 : d.p1, true); + var y0 = ya.c2p(isHorizontal ? d.p0 : d.s0, true); + var y1 = ya.c2p(isHorizontal ? d.p1 : d.s1, true); + + if(isFunnel) { + return [(x0 + x1) / 2, (y0 + y1) / 2]; + } else { + if(isHorizontal) { + return [x1, (y0 + y1) / 2]; + } else { + return [(x0 + x1) / 2, y1]; + } + } +} + +},{}],396:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = Sieve; + +var distinctVals = _dereq_('../../lib').distinctVals; +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +/** + * Helper class to sieve data from traces into bins + * + * @class + * + * @param {Array} traces +* Array of calculated traces + * @param {object} opts + * - @param {boolean} [sepNegVal] + * If true, then split data at the same position into a bar + * for positive values and another for negative values + * - @param {boolean} [overlapNoMerge] + * If true, then don't merge overlapping bars into a single bar + */ +function Sieve(traces, opts) { + this.traces = traces; + this.sepNegVal = opts.sepNegVal; + this.overlapNoMerge = opts.overlapNoMerge; + + // for single-bin histograms - see histogram/calc + var width1 = Infinity; + + var positions = []; + for(var i = 0; i < traces.length; i++) { + var trace = traces[i]; + for(var j = 0; j < trace.length; j++) { + var bar = trace[j]; + if(bar.p !== BADNUM) positions.push(bar.p); + } + if(trace[0] && trace[0].width1) { + width1 = Math.min(trace[0].width1, width1); + } + } + this.positions = positions; + + var dv = distinctVals(positions); + + this.distinctPositions = dv.vals; + if(dv.vals.length === 1 && width1 !== Infinity) this.minDiff = width1; + else this.minDiff = Math.min(dv.minDiff, width1); + + var type = (opts.posAxis || {}).type; + if(type === 'category' || type === 'multicategory') { + this.minDiff = 1; + } + + this.binWidth = this.minDiff; + + this.bins = {}; +} + +/** + * Sieve datum + * + * @method + * @param {number} position + * @param {number} value + * @returns {number} Previous bin value + */ +Sieve.prototype.put = function put(position, value) { + var label = this.getLabel(position, value); + var oldValue = this.bins[label] || 0; + + this.bins[label] = oldValue + value; + + return oldValue; +}; + +/** + * Get current bin value for a given datum + * + * @method + * @param {number} position Position of datum + * @param {number} [value] Value of datum + * (required if this.sepNegVal is true) + * @returns {number} Current bin value + */ +Sieve.prototype.get = function get(position, value) { + var label = this.getLabel(position, value); + return this.bins[label] || 0; +}; + +/** + * Get bin label for a given datum + * + * @method + * @param {number} position Position of datum + * @param {number} [value] Value of datum + * (required if this.sepNegVal is true) + * @returns {string} Bin label + * (prefixed with a 'v' if value is negative and this.sepNegVal is + * true; otherwise prefixed with '^') + */ +Sieve.prototype.getLabel = function getLabel(position, value) { + var prefix = (value < 0 && this.sepNegVal) ? 'v' : '^'; + var label = (this.overlapNoMerge) ? + position : + Math.round(position / this.binWidth); + return prefix + label; +}; + +},{"../../constants/numerical":265,"../../lib":285}],397:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); + +var resizeText = _dereq_('./uniform_text').resizeText; +var attributes = _dereq_('./attributes'); +var attributeTextFont = attributes.textfont; +var attributeInsideTextFont = attributes.insidetextfont; +var attributeOutsideTextFont = attributes.outsidetextfont; +var helpers = _dereq_('./helpers'); + +function style(gd) { + var s = d3.select(gd).selectAll('g.barlayer').selectAll('g.trace'); + resizeText(gd, s, 'bar'); + + var barcount = s.size(); + var fullLayout = gd._fullLayout; + + // trace styling + s.style('opacity', function(d) { return d[0].trace.opacity; }) + + // for gapless (either stacked or neighboring grouped) bars use + // crispEdges to turn off antialiasing so an artificial gap + // isn't introduced. + .each(function(d) { + if((fullLayout.barmode === 'stack' && barcount > 1) || + (fullLayout.bargap === 0 && + fullLayout.bargroupgap === 0 && + !d[0].trace.marker.line.width)) { + d3.select(this).attr('shape-rendering', 'crispEdges'); + } + }); + + s.selectAll('g.points').each(function(d) { + var sel = d3.select(this); + var trace = d[0].trace; + stylePoints(sel, trace, gd); + }); + + Registry.getComponentMethod('errorbars', 'style')(s); +} + +function stylePoints(sel, trace, gd) { + Drawing.pointStyle(sel.selectAll('path'), trace, gd); + styleTextPoints(sel, trace, gd); +} + +function styleTextPoints(sel, trace, gd) { + sel.selectAll('text').each(function(d) { + var tx = d3.select(this); + var font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd)); + + Drawing.font(tx, font); + }); +} + +function styleOnSelect(gd, cd, sel) { + var trace = cd[0].trace; + + if(trace.selectedpoints) { + stylePointsInSelectionMode(sel, trace, gd); + } else { + stylePoints(sel, trace, gd); + Registry.getComponentMethod('errorbars', 'style')(sel); + } +} + +function stylePointsInSelectionMode(s, trace, gd) { + Drawing.selectedPointStyle(s.selectAll('path'), trace); + styleTextInSelectionMode(s.selectAll('text'), trace, gd); +} + +function styleTextInSelectionMode(txs, trace, gd) { + txs.each(function(d) { + var tx = d3.select(this); + var font; + + if(d.selected) { + font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd)); + + var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color; + if(selectedFontColor) { + font.color = selectedFontColor; + } + + Drawing.font(tx, font); + } else { + Drawing.selectedTextStyle(tx, trace); + } + }); +} + +function determineFont(tx, d, trace, gd) { + var layoutFont = gd._fullLayout.font; + var textFont = trace.textfont; + + if(tx.classed('bartext-inside')) { + var barColor = getBarColor(d, trace); + textFont = getInsideTextFont(trace, d.i, layoutFont, barColor); + } else if(tx.classed('bartext-outside')) { + textFont = getOutsideTextFont(trace, d.i, layoutFont); + } + + return textFont; +} + +function getTextFont(trace, index, defaultValue) { + return getFontValue( + attributeTextFont, trace.textfont, index, defaultValue); +} + +function getInsideTextFont(trace, index, layoutFont, barColor) { + var defaultFont = getTextFont(trace, index, layoutFont); + + var wouldFallBackToLayoutFont = + (trace._input.textfont === undefined || trace._input.textfont.color === undefined) || + (Array.isArray(trace.textfont.color) && trace.textfont.color[index] === undefined); + if(wouldFallBackToLayoutFont) { + defaultFont = { + color: Color.contrast(barColor), + family: defaultFont.family, + size: defaultFont.size + }; + } + + return getFontValue( + attributeInsideTextFont, trace.insidetextfont, index, defaultFont); +} + +function getOutsideTextFont(trace, index, layoutFont) { + var defaultFont = getTextFont(trace, index, layoutFont); + return getFontValue( + attributeOutsideTextFont, trace.outsidetextfont, index, defaultFont); +} + +function getFontValue(attributeDefinition, attributeValue, index, defaultValue) { + attributeValue = attributeValue || {}; + + var familyValue = helpers.getValue(attributeValue.family, index); + var sizeValue = helpers.getValue(attributeValue.size, index); + var colorValue = helpers.getValue(attributeValue.color, index); + + return { + family: helpers.coerceString( + attributeDefinition.family, familyValue, defaultValue.family), + size: helpers.coerceNumber( + attributeDefinition.size, sizeValue, defaultValue.size), + color: helpers.coerceColor( + attributeDefinition.color, colorValue, defaultValue.color) + }; +} + +function getBarColor(cd, trace) { + if(trace.type === 'waterfall') { + return trace[cd.dir].marker.color; + } + return cd.mcc || cd.mc || trace.marker.color; +} + +module.exports = { + style: style, + styleTextPoints: styleTextPoints, + styleOnSelect: styleOnSelect, + getInsideTextFont: getInsideTextFont, + getOutsideTextFont: getOutsideTextFont, + getBarColor: getBarColor, + resizeText: resizeText +}; + +},{"../../components/color":155,"../../components/drawing":177,"../../lib":285,"../../registry":373,"./attributes":383,"./helpers":389,"./uniform_text":399,"@plotly/d3":20}],398:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); +var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale; +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); +var coercePattern = _dereq_('../../lib').coercePattern; + +module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) { + var markerColor = coerce('marker.color', defaultColor); + var hasMarkerColorscale = hasColorscale(traceIn, 'marker'); + if(hasMarkerColorscale) { + colorscaleDefaults( + traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'} + ); + } + + coerce('marker.line.color', Color.defaultLine); + + if(hasColorscale(traceIn, 'marker.line')) { + colorscaleDefaults( + traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'} + ); + } + + coerce('marker.line.width'); + coerce('marker.opacity'); + coercePattern(coerce, 'marker.pattern', markerColor, hasMarkerColorscale); + coerce('selected.marker.color'); + coerce('unselected.marker.color'); +}; + +},{"../../components/color":155,"../../components/colorscale/defaults":165,"../../components/colorscale/helpers":166,"../../lib":285}],399:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Lib = _dereq_('../../lib'); + +function resizeText(gd, gTrace, traceType) { + var fullLayout = gd._fullLayout; + var minSize = fullLayout['_' + traceType + 'Text_minsize']; + if(minSize) { + var shouldHide = fullLayout.uniformtext.mode === 'hide'; + + var selector; + switch(traceType) { + case 'funnelarea' : + case 'pie' : + case 'sunburst' : + selector = 'g.slice'; + break; + case 'treemap' : + case 'icicle' : + selector = 'g.slice, g.pathbar'; + break; + default : + selector = 'g.points > g.point'; + } + + gTrace.selectAll(selector).each(function(d) { + var transform = d.transform; + if(transform) { + transform.scale = (shouldHide && transform.hide) ? 0 : minSize / transform.fontSize; + + var el = d3.select(this).select('text'); + el.attr('transform', Lib.getTextTransform(transform)); + } + }); + } +} + +function recordMinTextSize( + traceType, // in + transform, // inout + fullLayout // inout +) { + if(fullLayout.uniformtext.mode) { + var minKey = getMinKey(traceType); + var minSize = fullLayout.uniformtext.minsize; + var size = transform.scale * transform.fontSize; + + transform.hide = size < minSize; + + fullLayout[minKey] = fullLayout[minKey] || Infinity; + if(!transform.hide) { + fullLayout[minKey] = Math.min( + fullLayout[minKey], + Math.max(size, minSize) + ); + } + } +} + +function clearMinTextSize( + traceType, // in + fullLayout // inout +) { + var minKey = getMinKey(traceType); + fullLayout[minKey] = undefined; +} + +function getMinKey(traceType) { + return '_' + traceType + 'Text_minsize'; +} + +module.exports = { + recordMinTextSize: recordMinTextSize, + clearMinTextSize: clearMinTextSize, + resizeText: resizeText +}; + +},{"../../lib":285,"@plotly/d3":20}],400:[function(_dereq_,module,exports){ +'use strict'; + +var scatterAttrs = _dereq_('../scatter/attributes'); +var barAttrs = _dereq_('../bar/attributes'); +var colorAttrs = _dereq_('../../components/color/attributes'); +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var scatterMarkerAttrs = scatterAttrs.marker; +var scatterMarkerLineAttrs = scatterMarkerAttrs.line; + +module.exports = { + y: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + x: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + x0: { + valType: 'any', + editType: 'calc+clearAxisTypes', + }, + y0: { + valType: 'any', + editType: 'calc+clearAxisTypes', + }, + + dx: { + valType: 'number', + editType: 'calc', + }, + dy: { + valType: 'number', + editType: 'calc', + }, + + xperiod: scatterAttrs.xperiod, + yperiod: scatterAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: scatterAttrs.xperiodalignment, + yperiodalignment: scatterAttrs.yperiodalignment, + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + + name: { + valType: 'string', + editType: 'calc+clearAxisTypes', + }, + + q1: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + median: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + q3: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + lowerfence: { + valType: 'data_array', + editType: 'calc', + }, + upperfence: { + valType: 'data_array', + editType: 'calc', + }, + + notched: { + valType: 'boolean', + editType: 'calc', + }, + notchwidth: { + valType: 'number', + min: 0, + max: 0.5, + dflt: 0.25, + editType: 'calc', + }, + notchspan: { + valType: 'data_array', + editType: 'calc', + }, + + // TODO + // maybe add + // - loweroutlierbound / upperoutlierbound + // - lowersuspectedoutlierbound / uppersuspectedoutlierbound + + boxpoints: { + valType: 'enumerated', + values: ['all', 'outliers', 'suspectedoutliers', false], + editType: 'calc', + }, + jitter: { + valType: 'number', + min: 0, + max: 1, + editType: 'calc', + }, + pointpos: { + valType: 'number', + min: -2, + max: 2, + editType: 'calc', + }, + + boxmean: { + valType: 'enumerated', + values: [true, 'sd', false], + editType: 'calc', + }, + mean: { + valType: 'data_array', + editType: 'calc', + }, + sd: { + valType: 'data_array', + editType: 'calc', + }, + + orientation: { + valType: 'enumerated', + values: ['v', 'h'], + editType: 'calc+clearAxisTypes', + }, + + quartilemethod: { + valType: 'enumerated', + values: ['linear', 'exclusive', 'inclusive'], + dflt: 'linear', + editType: 'calc', + }, + + width: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'calc', + }, + + marker: { + outliercolor: { + valType: 'color', + dflt: 'rgba(0, 0, 0, 0)', + editType: 'style', + }, + symbol: extendFlat({}, scatterMarkerAttrs.symbol, + {arrayOk: false, editType: 'plot'}), + opacity: extendFlat({}, scatterMarkerAttrs.opacity, + {arrayOk: false, dflt: 1, editType: 'style'}), + size: extendFlat({}, scatterMarkerAttrs.size, + {arrayOk: false, editType: 'calc'}), + color: extendFlat({}, scatterMarkerAttrs.color, + {arrayOk: false, editType: 'style'}), + line: { + color: extendFlat({}, scatterMarkerLineAttrs.color, + {arrayOk: false, dflt: colorAttrs.defaultLine, editType: 'style'} + ), + width: extendFlat({}, scatterMarkerLineAttrs.width, + {arrayOk: false, dflt: 0, editType: 'style'} + ), + outliercolor: { + valType: 'color', + editType: 'style', + }, + outlierwidth: { + valType: 'number', + min: 0, + dflt: 1, + editType: 'style', + }, + editType: 'style' + }, + editType: 'plot' + }, + + line: { + color: { + valType: 'color', + editType: 'style', + }, + width: { + valType: 'number', + min: 0, + dflt: 2, + editType: 'style', + }, + editType: 'plot' + }, + + fillcolor: scatterAttrs.fillcolor, + + whiskerwidth: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.5, + editType: 'calc', + }, + + offsetgroup: barAttrs.offsetgroup, + alignmentgroup: barAttrs.alignmentgroup, + + selected: { + marker: scatterAttrs.selected.marker, + editType: 'style' + }, + unselected: { + marker: scatterAttrs.unselected.marker, + editType: 'style' + }, + + text: extendFlat({}, scatterAttrs.text, { + }), + hovertext: extendFlat({}, scatterAttrs.hovertext, { + }), + hovertemplate: hovertemplateAttrs({ + }), + + hoveron: { + valType: 'flaglist', + flags: ['boxes', 'points'], + dflt: 'boxes+points', + editType: 'style', + } +}; + +},{"../../components/color/attributes":154,"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../../plots/template_attributes":368,"../bar/attributes":383,"../scatter/attributes":494}],401:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Axes = _dereq_('../../plots/cartesian/axes'); +var alignPeriod = _dereq_('../../plots/cartesian/align_period'); +var Lib = _dereq_('../../lib'); + +var BADNUM = _dereq_('../../constants/numerical').BADNUM; +var _ = Lib._; + +module.exports = function calc(gd, trace) { + var fullLayout = gd._fullLayout; + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var cd = []; + + // N.B. violin reuses same Box.calc + var numKey = trace.type === 'violin' ? '_numViolins' : '_numBoxes'; + + var i, j; + var valAxis, valLetter; + var posAxis, posLetter; + + var hasPeriod; + if(trace.orientation === 'h') { + valAxis = xa; + valLetter = 'x'; + posAxis = ya; + posLetter = 'y'; + hasPeriod = !!trace.yperiodalignment; + } else { + valAxis = ya; + valLetter = 'y'; + posAxis = xa; + posLetter = 'x'; + hasPeriod = !!trace.xperiodalignment; + } + + var allPosArrays = getPosArrays(trace, posLetter, posAxis, fullLayout[numKey]); + var posArray = allPosArrays[0]; + var origPos = allPosArrays[1]; + var dv = Lib.distinctVals(posArray, posAxis); + var posDistinct = dv.vals; + var dPos = dv.minDiff / 2; + + // item in trace calcdata + var cdi; + // array of {v: v, i, i} sample pts + var pts; + // values of the `pts` array of objects + var boxVals; + // length of sample + var N; + // single sample point + var pt; + // single sample value + var v; + + // filter function for outlier pts + // outlier definition based on http://www.physics.csbsju.edu/stats/box2.html + var ptFilterFn = (trace.boxpoints || trace.points) === 'all' ? + Lib.identity : + function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); }; + + if(trace._hasPreCompStats) { + var valArrayRaw = trace[valLetter]; + var d2c = function(k) { return valAxis.d2c((trace[k] || [])[i]); }; + var minVal = Infinity; + var maxVal = -Infinity; + + for(i = 0; i < trace._length; i++) { + var posi = posArray[i]; + if(!isNumeric(posi)) continue; + + cdi = {}; + cdi.pos = cdi[posLetter] = posi; + if(hasPeriod && origPos) { + cdi.orig_p = origPos[i]; // used by hover + } + + cdi.q1 = d2c('q1'); + cdi.med = d2c('median'); + cdi.q3 = d2c('q3'); + + pts = []; + if(valArrayRaw && Lib.isArrayOrTypedArray(valArrayRaw[i])) { + for(j = 0; j < valArrayRaw[i].length; j++) { + v = valAxis.d2c(valArrayRaw[i][j]); + if(v !== BADNUM) { + pt = {v: v, i: [i, j]}; + arraysToCalcdata(pt, trace, [i, j]); + pts.push(pt); + } + } + } + cdi.pts = pts.sort(sortByVal); + boxVals = cdi[valLetter] = pts.map(extractVal); + N = boxVals.length; + + if(cdi.med !== BADNUM && cdi.q1 !== BADNUM && cdi.q3 !== BADNUM && + cdi.med >= cdi.q1 && cdi.q3 >= cdi.med + ) { + var lf = d2c('lowerfence'); + cdi.lf = (lf !== BADNUM && lf <= cdi.q1) ? + lf : + computeLowerFence(cdi, boxVals, N); + + var uf = d2c('upperfence'); + cdi.uf = (uf !== BADNUM && uf >= cdi.q3) ? + uf : + computeUpperFence(cdi, boxVals, N); + + var mean = d2c('mean'); + cdi.mean = (mean !== BADNUM) ? + mean : + (N ? Lib.mean(boxVals, N) : (cdi.q1 + cdi.q3) / 2); + + var sd = d2c('sd'); + cdi.sd = (mean !== BADNUM && sd >= 0) ? + sd : + (N ? Lib.stdev(boxVals, N, cdi.mean) : (cdi.q3 - cdi.q1)); + + cdi.lo = computeLowerOutlierBound(cdi); + cdi.uo = computeUpperOutlierBound(cdi); + + var ns = d2c('notchspan'); + ns = (ns !== BADNUM && ns > 0) ? ns : computeNotchSpan(cdi, N); + cdi.ln = cdi.med - ns; + cdi.un = cdi.med + ns; + + var imin = cdi.lf; + var imax = cdi.uf; + if(trace.boxpoints && boxVals.length) { + imin = Math.min(imin, boxVals[0]); + imax = Math.max(imax, boxVals[N - 1]); + } + if(trace.notched) { + imin = Math.min(imin, cdi.ln); + imax = Math.max(imax, cdi.un); + } + cdi.min = imin; + cdi.max = imax; + } else { + Lib.warn([ + 'Invalid input - make sure that q1 <= median <= q3', + 'q1 = ' + cdi.q1, + 'median = ' + cdi.med, + 'q3 = ' + cdi.q3 + ].join('\n')); + + var v0; + if(cdi.med !== BADNUM) { + v0 = cdi.med; + } else if(cdi.q1 !== BADNUM) { + if(cdi.q3 !== BADNUM) v0 = (cdi.q1 + cdi.q3) / 2; + else v0 = cdi.q1; + } else if(cdi.q3 !== BADNUM) { + v0 = cdi.q3; + } else { + v0 = 0; + } + + // draw box as line segment + cdi.med = v0; + cdi.q1 = cdi.q3 = v0; + cdi.lf = cdi.uf = v0; + cdi.mean = cdi.sd = v0; + cdi.ln = cdi.un = v0; + cdi.min = cdi.max = v0; + } + + minVal = Math.min(minVal, cdi.min); + maxVal = Math.max(maxVal, cdi.max); + + cdi.pts2 = pts.filter(ptFilterFn); + + cd.push(cdi); + } + + trace._extremes[valAxis._id] = Axes.findExtremes(valAxis, + [minVal, maxVal], + {padded: true} + ); + } else { + var valArray = valAxis.makeCalcdata(trace, valLetter); + var posBins = makeBins(posDistinct, dPos); + var pLen = posDistinct.length; + var ptsPerBin = initNestedArray(pLen); + + // bin pts info per position bins + for(i = 0; i < trace._length; i++) { + v = valArray[i]; + if(!isNumeric(v)) continue; + + var n = Lib.findBin(posArray[i], posBins); + if(n >= 0 && n < pLen) { + pt = {v: v, i: i}; + arraysToCalcdata(pt, trace, i); + ptsPerBin[n].push(pt); + } + } + + var minLowerNotch = Infinity; + var maxUpperNotch = -Infinity; + + var quartilemethod = trace.quartilemethod; + var usesExclusive = quartilemethod === 'exclusive'; + var usesInclusive = quartilemethod === 'inclusive'; + + // build calcdata trace items, one item per distinct position + for(i = 0; i < pLen; i++) { + if(ptsPerBin[i].length > 0) { + cdi = {}; + cdi.pos = cdi[posLetter] = posDistinct[i]; + + pts = cdi.pts = ptsPerBin[i].sort(sortByVal); + boxVals = cdi[valLetter] = pts.map(extractVal); + N = boxVals.length; + + cdi.min = boxVals[0]; + cdi.max = boxVals[N - 1]; + cdi.mean = Lib.mean(boxVals, N); + cdi.sd = Lib.stdev(boxVals, N, cdi.mean); + cdi.med = Lib.interp(boxVals, 0.5); + + if((N % 2) && (usesExclusive || usesInclusive)) { + var lower; + var upper; + + if(usesExclusive) { + // do NOT include the median in either half + lower = boxVals.slice(0, N / 2); + upper = boxVals.slice(N / 2 + 1); + } else if(usesInclusive) { + // include the median in either half + lower = boxVals.slice(0, N / 2 + 1); + upper = boxVals.slice(N / 2); + } + + cdi.q1 = Lib.interp(lower, 0.5); + cdi.q3 = Lib.interp(upper, 0.5); + } else { + cdi.q1 = Lib.interp(boxVals, 0.25); + cdi.q3 = Lib.interp(boxVals, 0.75); + } + + // lower and upper fences + cdi.lf = computeLowerFence(cdi, boxVals, N); + cdi.uf = computeUpperFence(cdi, boxVals, N); + + // lower and upper outliers bounds + cdi.lo = computeLowerOutlierBound(cdi); + cdi.uo = computeUpperOutlierBound(cdi); + + // lower and upper notches + var mci = computeNotchSpan(cdi, N); + cdi.ln = cdi.med - mci; + cdi.un = cdi.med + mci; + minLowerNotch = Math.min(minLowerNotch, cdi.ln); + maxUpperNotch = Math.max(maxUpperNotch, cdi.un); + + cdi.pts2 = pts.filter(ptFilterFn); + + cd.push(cdi); + } + } + + trace._extremes[valAxis._id] = Axes.findExtremes(valAxis, + trace.notched ? valArray.concat([minLowerNotch, maxUpperNotch]) : valArray, + {padded: true} + ); + } + + calcSelection(cd, trace); + + if(cd.length > 0) { + cd[0].t = { + num: fullLayout[numKey], + dPos: dPos, + posLetter: posLetter, + valLetter: valLetter, + labels: { + med: _(gd, 'median:'), + min: _(gd, 'min:'), + q1: _(gd, 'q1:'), + q3: _(gd, 'q3:'), + max: _(gd, 'max:'), + mean: trace.boxmean === 'sd' ? _(gd, 'mean ± σ:') : _(gd, 'mean:'), + lf: _(gd, 'lower fence:'), + uf: _(gd, 'upper fence:') + } + }; + + fullLayout[numKey]++; + return cd; + } else { + return [{t: {empty: true}}]; + } +}; + +// In vertical (horizontal) box plots: +// if no x (y) data, use x0 (y0), or name +// so if you want one box +// per trace, set x0 (y0) to the x (y) value or category for this trace +// (or set x (y) to a constant array matching y (x)) +function getPosArrays(trace, posLetter, posAxis, num) { + var hasPosArray = posLetter in trace; + var hasPos0 = posLetter + '0' in trace; + var hasPosStep = 'd' + posLetter in trace; + + if(hasPosArray || (hasPos0 && hasPosStep)) { + var origPos = posAxis.makeCalcdata(trace, posLetter); + var pos = alignPeriod(trace, posAxis, posLetter, origPos); + return [pos, origPos]; + } + + var pos0; + if(hasPos0) { + pos0 = trace[posLetter + '0']; + } else if('name' in trace && ( + posAxis.type === 'category' || ( + isNumeric(trace.name) && + ['linear', 'log'].indexOf(posAxis.type) !== -1 + ) || ( + Lib.isDateTime(trace.name) && + posAxis.type === 'date' + ) + )) { + pos0 = trace.name; + } else { + pos0 = num; + } + + var pos0c = posAxis.type === 'multicategory' ? + posAxis.r2c_just_indices(pos0) : + posAxis.d2c(pos0, 0, trace[posLetter + 'calendar']); + + var len = trace._length; + var out = new Array(len); + for(var i = 0; i < len; i++) out[i] = pos0c; + + return [out]; +} + +function makeBins(x, dx) { + var len = x.length; + var bins = new Array(len + 1); + + for(var i = 0; i < len; i++) { + bins[i] = x[i] - dx; + } + bins[len] = x[len - 1] + dx; + + return bins; +} + +function initNestedArray(len) { + var arr = new Array(len); + for(var i = 0; i < len; i++) { + arr[i] = []; + } + return arr; +} + +var TRACE_TO_CALC = { + text: 'tx', + hovertext: 'htx' +}; + +function arraysToCalcdata(pt, trace, ptNumber) { + for(var k in TRACE_TO_CALC) { + if(Lib.isArrayOrTypedArray(trace[k])) { + if(Array.isArray(ptNumber)) { + if(Lib.isArrayOrTypedArray(trace[k][ptNumber[0]])) { + pt[TRACE_TO_CALC[k]] = trace[k][ptNumber[0]][ptNumber[1]]; + } + } else { + pt[TRACE_TO_CALC[k]] = trace[k][ptNumber]; + } + } + } +} + +function calcSelection(cd, trace) { + if(Lib.isArrayOrTypedArray(trace.selectedpoints)) { + for(var i = 0; i < cd.length; i++) { + var pts = cd[i].pts || []; + var ptNumber2cdIndex = {}; + + for(var j = 0; j < pts.length; j++) { + ptNumber2cdIndex[pts[j].i] = j; + } + + Lib.tagSelected(pts, trace, ptNumber2cdIndex); + } + } +} + +function sortByVal(a, b) { return a.v - b.v; } + +function extractVal(o) { return o.v; } + +// last point below 1.5 * IQR +function computeLowerFence(cdi, boxVals, N) { + if(N === 0) return cdi.q1; + return Math.min( + cdi.q1, + boxVals[Math.min( + Lib.findBin(2.5 * cdi.q1 - 1.5 * cdi.q3, boxVals, true) + 1, + N - 1 + )] + ); +} + +// last point above 1.5 * IQR +function computeUpperFence(cdi, boxVals, N) { + if(N === 0) return cdi.q3; + return Math.max( + cdi.q3, + boxVals[Math.max( + Lib.findBin(2.5 * cdi.q3 - 1.5 * cdi.q1, boxVals), + 0 + )] + ); +} + +// 3 IQR below (don't clip to max/min, +// this is only for discriminating suspected & far outliers) +function computeLowerOutlierBound(cdi) { + return 4 * cdi.q1 - 3 * cdi.q3; +} + +// 3 IQR above (don't clip to max/min, +// this is only for discriminating suspected & far outliers) +function computeUpperOutlierBound(cdi) { + return 4 * cdi.q3 - 3 * cdi.q1; +} + +// 95% confidence intervals for median +function computeNotchSpan(cdi, N) { + if(N === 0) return 0; + return 1.57 * (cdi.q3 - cdi.q1) / Math.sqrt(N); +} + +},{"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/align_period":328,"../../plots/cartesian/axes":331,"fast-isnumeric":31}],402:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); +var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup; + +var orientations = ['v', 'h']; + +function crossTraceCalc(gd, plotinfo) { + var calcdata = gd.calcdata; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + for(var i = 0; i < orientations.length; i++) { + var orientation = orientations[i]; + var posAxis = orientation === 'h' ? ya : xa; + var boxList = []; + + // make list of boxes / candlesticks + // For backward compatibility, candlesticks are treated as if they *are* box traces here + for(var j = 0; j < calcdata.length; j++) { + var cd = calcdata[j]; + var t = cd[0].t; + var trace = cd[0].trace; + + if(trace.visible === true && + (trace.type === 'box' || trace.type === 'candlestick') && + !t.empty && + (trace.orientation || 'v') === orientation && + trace.xaxis === xa._id && + trace.yaxis === ya._id + ) { + boxList.push(j); + } + } + + setPositionOffset('box', gd, boxList, posAxis); + } +} + +function setPositionOffset(traceType, gd, boxList, posAxis) { + var calcdata = gd.calcdata; + var fullLayout = gd._fullLayout; + var axId = posAxis._id; + var axLetter = axId.charAt(0); + + var i, j, calcTrace; + var pointList = []; + var shownPts = 0; + + // make list of box points + for(i = 0; i < boxList.length; i++) { + calcTrace = calcdata[boxList[i]]; + for(j = 0; j < calcTrace.length; j++) { + pointList.push(posAxis.c2l(calcTrace[j].pos, true)); + shownPts += (calcTrace[j].pts2 || []).length; + } + } + + if(!pointList.length) return; + + // box plots - update dPos based on multiple traces + var boxdv = Lib.distinctVals(pointList); + if(posAxis.type === 'category' || posAxis.type === 'multicategory') { + boxdv.minDiff = 1; + } + + var dPos0 = boxdv.minDiff / 2; + + // check for forced minimum dtick + Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true); + + var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes'; + var numTotal = fullLayout[numKey]; + var group = fullLayout[traceType + 'mode'] === 'group' && numTotal > 1; + var groupFraction = 1 - fullLayout[traceType + 'gap']; + var groupGapFraction = 1 - fullLayout[traceType + 'groupgap']; + + for(i = 0; i < boxList.length; i++) { + calcTrace = calcdata[boxList[i]]; + + var trace = calcTrace[0].trace; + var t = calcTrace[0].t; + var width = trace.width; + var side = trace.side; + + // position coordinate delta + var dPos; + // box half width; + var bdPos; + // box center offset + var bPos; + // half-width within which to accept hover for this box/violin + // always split the distance to the closest box/violin + var wHover; + + if(width) { + dPos = bdPos = wHover = width / 2; + bPos = 0; + } else { + dPos = dPos0; + + if(group) { + var groupId = getAxisGroup(fullLayout, posAxis._id) + trace.orientation; + var alignmentGroups = fullLayout._alignmentOpts[groupId] || {}; + var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {}; + var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length; + var num = nOffsetGroups || numTotal; + var shift = nOffsetGroups ? trace._offsetIndex : t.num; + + bdPos = dPos * groupFraction * groupGapFraction / num; + bPos = 2 * dPos * (-0.5 + (shift + 0.5) / num) * groupFraction; + wHover = dPos * groupFraction / num; + } else { + bdPos = dPos * groupFraction * groupGapFraction; + bPos = 0; + wHover = dPos; + } + } + t.dPos = dPos; + t.bPos = bPos; + t.bdPos = bdPos; + t.wHover = wHover; + + // box/violin-only value-space push value + var pushplus; + var pushminus; + // edge of box/violin + var edge = bPos + bdPos; + var edgeplus; + var edgeminus; + // value-space padding + var vpadplus; + var vpadminus; + // pixel-space padding + var ppadplus; + var ppadminus; + // do we add 5% of both sides (more logic for points beyond box/violin below) + var padded = Boolean(width); + // does this trace show points? + var hasPts = (trace.boxpoints || trace.points) && (shownPts > 0); + + if(side === 'positive') { + pushplus = dPos * (width ? 1 : 0.5); + edgeplus = edge; + pushminus = edgeplus = bPos; + } else if(side === 'negative') { + pushplus = edgeplus = bPos; + pushminus = dPos * (width ? 1 : 0.5); + edgeminus = edge; + } else { + pushplus = pushminus = dPos; + edgeplus = edgeminus = edge; + } + + if(hasPts) { + var pointpos = trace.pointpos; + var jitter = trace.jitter; + var ms = trace.marker.size / 2; + + var pp = 0; + if((pointpos + jitter) >= 0) { + pp = edge * (pointpos + jitter); + if(pp > pushplus) { + // (++) beyond plus-value, use pp + padded = true; + ppadplus = ms; + vpadplus = pp; + } else if(pp > edgeplus) { + // (+), use push-value (it's bigger), but add px-pad + ppadplus = ms; + vpadplus = pushplus; + } + } + if(pp <= pushplus) { + // (->) fallback to push value + vpadplus = pushplus; + } + + var pm = 0; + if((pointpos - jitter) <= 0) { + pm = -edge * (pointpos - jitter); + if(pm > pushminus) { + // (--) beyond plus-value, use pp + padded = true; + ppadminus = ms; + vpadminus = pm; + } else if(pm > edgeminus) { + // (-), use push-value (it's bigger), but add px-pad + ppadminus = ms; + vpadminus = pushminus; + } + } + if(pm <= pushminus) { + // (<-) fallback to push value + vpadminus = pushminus; + } + } else { + vpadplus = pushplus; + vpadminus = pushminus; + } + + var pos = new Array(calcTrace.length); + for(j = 0; j < calcTrace.length; j++) { + pos[j] = calcTrace[j].pos; + } + + trace._extremes[axId] = Axes.findExtremes(posAxis, pos, { + padded: padded, + vpadminus: vpadminus, + vpadplus: vpadplus, + vpadLinearized: true, + // N.B. SVG px-space positive/negative + ppadminus: {x: ppadminus, y: ppadplus}[axLetter], + ppadplus: {x: ppadplus, y: ppadminus}[axLetter], + }); + } +} + +module.exports = { + crossTraceCalc: crossTraceCalc, + setPositionOffset: setPositionOffset +}; + +},{"../../lib":285,"../../plots/cartesian/axes":331,"../../plots/cartesian/constraints":339}],403:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); +var Color = _dereq_('../../components/color'); +var handlePeriodDefaults = _dereq_('../scatter/period_defaults'); +var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults; +var autoType = _dereq_('../../plots/cartesian/axis_autotype'); +var attributes = _dereq_('./attributes'); + +function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + handleSampleDefaults(traceIn, traceOut, coerce, layout); + if(traceOut.visible === false) return; + + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('xhoverformat'); + coerce('yhoverformat'); + + var hasPreCompStats = traceOut._hasPreCompStats; + + if(hasPreCompStats) { + coerce('lowerfence'); + coerce('upperfence'); + } + + coerce('line.color', (traceIn.marker || {}).color || defaultColor); + coerce('line.width'); + coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5)); + + var boxmeanDflt = false; + if(hasPreCompStats) { + var mean = coerce('mean'); + var sd = coerce('sd'); + if(mean && mean.length) { + boxmeanDflt = true; + if(sd && sd.length) boxmeanDflt = 'sd'; + } + } + coerce('boxmean', boxmeanDflt); + + coerce('whiskerwidth'); + coerce('width'); + coerce('quartilemethod'); + + var notchedDflt = false; + if(hasPreCompStats) { + var notchspan = coerce('notchspan'); + if(notchspan && notchspan.length) { + notchedDflt = true; + } + } else if(Lib.validate(traceIn.notchwidth, attributes.notchwidth)) { + notchedDflt = true; + } + var notched = coerce('notched', notchedDflt); + if(notched) coerce('notchwidth'); + + handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'}); +} + +function handleSampleDefaults(traceIn, traceOut, coerce, layout) { + function getDims(arr) { + var dims = 0; + if(arr && arr.length) { + dims += 1; + if(Lib.isArrayOrTypedArray(arr[0]) && arr[0].length) { + dims += 1; + } + } + return dims; + } + + function valid(astr) { + return Lib.validate(traceIn[astr], attributes[astr]); + } + + var y = coerce('y'); + var x = coerce('x'); + + var sLen; + if(traceOut.type === 'box') { + var q1 = coerce('q1'); + var median = coerce('median'); + var q3 = coerce('q3'); + + traceOut._hasPreCompStats = ( + q1 && q1.length && + median && median.length && + q3 && q3.length + ); + sLen = Math.min( + Lib.minRowLength(q1), + Lib.minRowLength(median), + Lib.minRowLength(q3) + ); + } + + var yDims = getDims(y); + var xDims = getDims(x); + var yLen = yDims && Lib.minRowLength(y); + var xLen = xDims && Lib.minRowLength(x); + + var calendar = layout.calendar; + var opts = { + autotypenumbers: layout.autotypenumbers + }; + + var defaultOrientation, len; + if(traceOut._hasPreCompStats) { + switch(String(xDims) + String(yDims)) { + // no x / no y + case '00': + var setInX = valid('x0') || valid('dx'); + var setInY = valid('y0') || valid('dy'); + + if(setInY && !setInX) { + defaultOrientation = 'h'; + } else { + defaultOrientation = 'v'; + } + + len = sLen; + break; + // just x + case '10': + defaultOrientation = 'v'; + len = Math.min(sLen, xLen); + break; + case '20': + defaultOrientation = 'h'; + len = Math.min(sLen, x.length); + break; + // just y + case '01': + defaultOrientation = 'h'; + len = Math.min(sLen, yLen); + break; + case '02': + defaultOrientation = 'v'; + len = Math.min(sLen, y.length); + break; + // both + case '12': + defaultOrientation = 'v'; + len = Math.min(sLen, xLen, y.length); + break; + case '21': + defaultOrientation = 'h'; + len = Math.min(sLen, x.length, yLen); + break; + case '11': + // this one is ill-defined + len = 0; + break; + case '22': + var hasCategories = false; + var i; + for(i = 0; i < x.length; i++) { + if(autoType(x[i], calendar, opts) === 'category') { + hasCategories = true; + break; + } + } + + if(hasCategories) { + defaultOrientation = 'v'; + len = Math.min(sLen, xLen, y.length); + } else { + for(i = 0; i < y.length; i++) { + if(autoType(y[i], calendar, opts) === 'category') { + hasCategories = true; + break; + } + } + + if(hasCategories) { + defaultOrientation = 'h'; + len = Math.min(sLen, x.length, yLen); + } else { + defaultOrientation = 'v'; + len = Math.min(sLen, xLen, y.length); + } + } + break; + } + } else if(yDims > 0) { + defaultOrientation = 'v'; + if(xDims > 0) { + len = Math.min(xLen, yLen); + } else { + len = Math.min(yLen); + } + } else if(xDims > 0) { + defaultOrientation = 'h'; + len = Math.min(xLen); + } else { + len = 0; + } + + if(!len) { + traceOut.visible = false; + return; + } + traceOut._length = len; + + var orientation = coerce('orientation', defaultOrientation); + + // these are just used for positioning, they never define the sample + if(traceOut._hasPreCompStats) { + if(orientation === 'v' && xDims === 0) { + coerce('x0', 0); + coerce('dx', 1); + } else if(orientation === 'h' && yDims === 0) { + coerce('y0', 0); + coerce('dy', 1); + } + } else { + if(orientation === 'v' && xDims === 0) { + coerce('x0'); + } else if(orientation === 'h' && yDims === 0) { + coerce('y0'); + } + } + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); +} + +function handlePointsDefaults(traceIn, traceOut, coerce, opts) { + var prefix = opts.prefix; + + var outlierColorDflt = Lib.coerce2(traceIn, traceOut, attributes, 'marker.outliercolor'); + var lineoutliercolor = coerce('marker.line.outliercolor'); + + var modeDflt = 'outliers'; + if(traceOut._hasPreCompStats) { + modeDflt = 'all'; + } else if(outlierColorDflt || lineoutliercolor) { + modeDflt = 'suspectedoutliers'; + } + + var mode = coerce(prefix + 'points', modeDflt); + + if(mode) { + coerce('jitter', mode === 'all' ? 0.3 : 0); + coerce('pointpos', mode === 'all' ? -1.5 : 0); + + coerce('marker.symbol'); + coerce('marker.opacity'); + coerce('marker.size'); + coerce('marker.color', traceOut.line.color); + coerce('marker.line.color'); + coerce('marker.line.width'); + + if(mode === 'suspectedoutliers') { + coerce('marker.line.outliercolor', traceOut.marker.color); + coerce('marker.line.outlierwidth'); + } + + coerce('selected.marker.color'); + coerce('unselected.marker.color'); + coerce('selected.marker.size'); + coerce('unselected.marker.size'); + + coerce('text'); + coerce('hovertext'); + } else { + delete traceOut.marker; + } + + var hoveron = coerce('hoveron'); + if(hoveron === 'all' || hoveron.indexOf('points') !== -1) { + coerce('hovertemplate'); + } + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +} + +function crossTraceDefaults(fullData, fullLayout) { + var traceIn, traceOut; + + function coerce(attr) { + return Lib.coerce(traceOut._input, traceOut, attributes, attr); + } + + for(var i = 0; i < fullData.length; i++) { + traceOut = fullData[i]; + var traceType = traceOut.type; + + if(traceType === 'box' || traceType === 'violin') { + traceIn = traceOut._input; + if(fullLayout[traceType + 'mode'] === 'group') { + handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce); + } + } + } +} + +module.exports = { + supplyDefaults: supplyDefaults, + crossTraceDefaults: crossTraceDefaults, + + handleSampleDefaults: handleSampleDefaults, + handlePointsDefaults: handlePointsDefaults +}; + +},{"../../components/color":155,"../../lib":285,"../../plots/cartesian/axis_autotype":332,"../../registry":373,"../bar/defaults":387,"../scatter/period_defaults":514,"./attributes":400}],404:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function eventData(out, pt) { + // Note: hoverOnBox property is needed for click-to-select + // to ignore when a box was clicked. This is the reason box + // implements this custom eventData function. + if(pt.hoverOnBox) out.hoverOnBox = pt.hoverOnBox; + + if('xVal' in pt) out.x = pt.xVal; + if('yVal' in pt) out.y = pt.yVal; + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + return out; +}; + +},{}],405:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); +var Fx = _dereq_('../../components/fx'); +var Color = _dereq_('../../components/color'); +var fillText = Lib.fillText; + +function hoverPoints(pointData, xval, yval, hovermode) { + var cd = pointData.cd; + var trace = cd[0].trace; + var hoveron = trace.hoveron; + var closeBoxData = []; + var closePtData; + + if(hoveron.indexOf('boxes') !== -1) { + closeBoxData = closeBoxData.concat(hoverOnBoxes(pointData, xval, yval, hovermode)); + } + + if(hoveron.indexOf('points') !== -1) { + closePtData = hoverOnPoints(pointData, xval, yval); + } + + // If there's a point in range and hoveron has points, show the best single point only. + // If hoveron has boxes and there's no point in range (or hoveron doesn't have points), show the box stats. + if(hovermode === 'closest') { + if(closePtData) return [closePtData]; + return closeBoxData; + } + + // Otherwise in compare mode, allow a point AND the box stats to be labeled + // If there are multiple boxes in range (ie boxmode = 'overlay') we'll see stats for all of them. + if(closePtData) { + closeBoxData.push(closePtData); + return closeBoxData; + } + return closeBoxData; +} + +function hoverOnBoxes(pointData, xval, yval, hovermode) { + var cd = pointData.cd; + var xa = pointData.xa; + var ya = pointData.ya; + var trace = cd[0].trace; + var t = cd[0].t; + var isViolin = trace.type === 'violin'; + var closeBoxData = []; + + var pLetter, vLetter, pAxis, vAxis, vVal, pVal, dx, dy, dPos, + hoverPseudoDistance, spikePseudoDistance; + + var boxDelta = t.bdPos; + var boxDeltaPos, boxDeltaNeg; + var posAcceptance = t.wHover; + var shiftPos = function(di) { return pAxis.c2l(di.pos) + t.bPos - pAxis.c2l(pVal); }; + + if(isViolin && trace.side !== 'both') { + if(trace.side === 'positive') { + dPos = function(di) { + var pos = shiftPos(di); + return Fx.inbox(pos, pos + posAcceptance, hoverPseudoDistance); + }; + boxDeltaPos = boxDelta; + boxDeltaNeg = 0; + } + if(trace.side === 'negative') { + dPos = function(di) { + var pos = shiftPos(di); + return Fx.inbox(pos - posAcceptance, pos, hoverPseudoDistance); + }; + boxDeltaPos = 0; + boxDeltaNeg = boxDelta; + } + } else { + dPos = function(di) { + var pos = shiftPos(di); + return Fx.inbox(pos - posAcceptance, pos + posAcceptance, hoverPseudoDistance); + }; + boxDeltaPos = boxDeltaNeg = boxDelta; + } + + var dVal; + + if(isViolin) { + dVal = function(di) { + return Fx.inbox(di.span[0] - vVal, di.span[1] - vVal, hoverPseudoDistance); + }; + } else { + dVal = function(di) { + return Fx.inbox(di.min - vVal, di.max - vVal, hoverPseudoDistance); + }; + } + + if(trace.orientation === 'h') { + vVal = xval; + pVal = yval; + dx = dVal; + dy = dPos; + pLetter = 'y'; + pAxis = ya; + vLetter = 'x'; + vAxis = xa; + } else { + vVal = yval; + pVal = xval; + dx = dPos; + dy = dVal; + pLetter = 'x'; + pAxis = xa; + vLetter = 'y'; + vAxis = ya; + } + + // if two boxes are overlaying, let the narrowest one win + var pseudoDistance = Math.min(1, boxDelta / Math.abs(pAxis.r2c(pAxis.range[1]) - pAxis.r2c(pAxis.range[0]))); + hoverPseudoDistance = pointData.maxHoverDistance - pseudoDistance; + spikePseudoDistance = pointData.maxSpikeDistance - pseudoDistance; + + function dxy(di) { return (dx(di) + dy(di)) / 2; } + var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); + Fx.getClosest(cd, distfn, pointData); + + // skip the rest (for this trace) if we didn't find a close point + // and create the item(s) in closedata for this point + if(pointData.index === false) return []; + + var di = cd[pointData.index]; + var lc = trace.line.color; + var mc = (trace.marker || {}).color; + + if(Color.opacity(lc) && trace.line.width) pointData.color = lc; + else if(Color.opacity(mc) && trace.boxpoints) pointData.color = mc; + else pointData.color = trace.fillcolor; + + pointData[pLetter + '0'] = pAxis.c2p(di.pos + t.bPos - boxDeltaNeg, true); + pointData[pLetter + '1'] = pAxis.c2p(di.pos + t.bPos + boxDeltaPos, true); + + pointData[pLetter + 'LabelVal'] = di.orig_p !== undefined ? di.orig_p : di.pos; + + var spikePosAttr = pLetter + 'Spike'; + pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance; + pointData[spikePosAttr] = pAxis.c2p(di.pos, true); + + // box plots: each "point" gets many labels + var usedVals = {}; + var attrs = ['med', 'q1', 'q3', 'min', 'max']; + + if(trace.boxmean || (trace.meanline || {}).visible) { + attrs.push('mean'); + } + if(trace.boxpoints || trace.points) { + attrs.push('lf', 'uf'); + } + + for(var i = 0; i < attrs.length; i++) { + var attr = attrs[i]; + + if(!(attr in di) || (di[attr] in usedVals)) continue; + usedVals[di[attr]] = true; + + // copy out to a new object for each value to label + var val = di[attr]; + var valPx = vAxis.c2p(val, true); + var pointData2 = Lib.extendFlat({}, pointData); + + pointData2.attr = attr; + pointData2[vLetter + '0'] = pointData2[vLetter + '1'] = valPx; + pointData2[vLetter + 'LabelVal'] = val; + pointData2[vLetter + 'Label'] = (t.labels ? t.labels[attr] + ' ' : '') + Axes.hoverLabelText(vAxis, val, trace[vLetter + 'hoverformat']); + + // Note: introduced to be able to distinguish a + // clicked point from a box during click-to-select + pointData2.hoverOnBox = true; + + if(attr === 'mean' && ('sd' in di) && trace.boxmean === 'sd') { + pointData2[vLetter + 'err'] = di.sd; + } + + // only keep name and spikes on the first item (median) + pointData.name = ''; + pointData.spikeDistance = undefined; + pointData[spikePosAttr] = undefined; + + // no hovertemplate support yet + pointData2.hovertemplate = false; + + closeBoxData.push(pointData2); + } + + return closeBoxData; +} + +function hoverOnPoints(pointData, xval, yval) { + var cd = pointData.cd; + var xa = pointData.xa; + var ya = pointData.ya; + var trace = cd[0].trace; + var xPx = xa.c2p(xval); + var yPx = ya.c2p(yval); + var closePtData; + + var dx = function(di) { + var rad = Math.max(3, di.mrc || 0); + return Math.max(Math.abs(xa.c2p(di.x) - xPx) - rad, 1 - 3 / rad); + }; + var dy = function(di) { + var rad = Math.max(3, di.mrc || 0); + return Math.max(Math.abs(ya.c2p(di.y) - yPx) - rad, 1 - 3 / rad); + }; + var distfn = Fx.quadrature(dx, dy); + + // show one point per trace + var ijClosest = false; + var di, pt; + + for(var i = 0; i < cd.length; i++) { + di = cd[i]; + + for(var j = 0; j < (di.pts || []).length; j++) { + pt = di.pts[j]; + + var newDistance = distfn(pt); + if(newDistance <= pointData.distance) { + pointData.distance = newDistance; + ijClosest = [i, j]; + } + } + } + + if(!ijClosest) return false; + + di = cd[ijClosest[0]]; + pt = di.pts[ijClosest[1]]; + + var xc = xa.c2p(pt.x, true); + var yc = ya.c2p(pt.y, true); + var rad = pt.mrc || 1; + + closePtData = Lib.extendFlat({}, pointData, { + // corresponds to index in x/y input data array + index: pt.i, + color: (trace.marker || {}).color, + name: trace.name, + x0: xc - rad, + x1: xc + rad, + y0: yc - rad, + y1: yc + rad, + spikeDistance: pointData.distance, + hovertemplate: trace.hovertemplate + }); + + var origPos = di.orig_p; + var pos = origPos !== undefined ? origPos : di.pos; + var pa; + if(trace.orientation === 'h') { + pa = ya; + closePtData.xLabelVal = pt.x; + closePtData.yLabelVal = pos; + } else { + pa = xa; + closePtData.xLabelVal = pos; + closePtData.yLabelVal = pt.y; + } + + var pLetter = pa._id.charAt(0); + closePtData[pLetter + 'Spike'] = pa.c2p(di.pos, true); + + fillText(pt, trace, closePtData); + + return closePtData; +} + +module.exports = { + hoverPoints: hoverPoints, + hoverOnBoxes: hoverOnBoxes, + hoverOnPoints: hoverOnPoints +}; + +},{"../../components/color":155,"../../components/fx":195,"../../lib":285,"../../plots/cartesian/axes":331}],406:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + layoutAttributes: _dereq_('./layout_attributes'), + supplyDefaults: _dereq_('./defaults').supplyDefaults, + crossTraceDefaults: _dereq_('./defaults').crossTraceDefaults, + supplyLayoutDefaults: _dereq_('./layout_defaults').supplyLayoutDefaults, + calc: _dereq_('./calc'), + crossTraceCalc: _dereq_('./cross_trace_calc').crossTraceCalc, + plot: _dereq_('./plot').plot, + style: _dereq_('./style').style, + styleOnSelect: _dereq_('./style').styleOnSelect, + hoverPoints: _dereq_('./hover').hoverPoints, + eventData: _dereq_('./event_data'), + selectPoints: _dereq_('./select'), + + moduleType: 'trace', + name: 'box', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'boxLayout', 'zoomScale'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"./attributes":400,"./calc":401,"./cross_trace_calc":402,"./defaults":403,"./event_data":404,"./hover":405,"./layout_attributes":407,"./layout_defaults":408,"./plot":409,"./select":410,"./style":411}],407:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + boxmode: { + valType: 'enumerated', + values: ['group', 'overlay'], + dflt: 'overlay', + editType: 'calc', + }, + boxgap: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.3, + editType: 'calc', + }, + boxgroupgap: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.3, + editType: 'calc', + } +}; + +},{}],408:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var layoutAttributes = _dereq_('./layout_attributes'); + +function _supply(layoutIn, layoutOut, fullData, coerce, traceType) { + var category = traceType + 'Layout'; + var hasTraceType = false; + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + + if(Registry.traceIs(trace, category)) { + hasTraceType = true; + break; + } + } + if(!hasTraceType) return; + + coerce(traceType + 'mode'); + coerce(traceType + 'gap'); + coerce(traceType + 'groupgap'); +} + +function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + _supply(layoutIn, layoutOut, fullData, coerce, 'box'); +} + +module.exports = { + supplyLayoutDefaults: supplyLayoutDefaults, + _supply: _supply +}; + +},{"../../lib":285,"../../registry":373,"./layout_attributes":407}],409:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); + +// constants for dynamic jitter (ie less jitter for sparser points) +var JITTERCOUNT = 5; // points either side of this to include +var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense" + +function plot(gd, plotinfo, cdbox, boxLayer) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + Lib.makeTraceGroups(boxLayer, cdbox, 'trace boxes').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var t = cd0.t; + var trace = cd0.trace; + + // whisker width + t.wdPos = t.bdPos * trace.whiskerwidth; + + if(trace.visible !== true || t.empty) { + plotGroup.remove(); + return; + } + + var posAxis, valAxis; + + if(trace.orientation === 'h') { + posAxis = ya; + valAxis = xa; + } else { + posAxis = xa; + valAxis = ya; + } + + plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t); + plotPoints(plotGroup, {x: xa, y: ya}, trace, t); + plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t); + }); +} + +function plotBoxAndWhiskers(sel, axes, trace, t) { + var isHorizontal = trace.orientation === 'h'; + var valAxis = axes.val; + var posAxis = axes.pos; + var posHasRangeBreaks = !!posAxis.rangebreaks; + + var bPos = t.bPos; + var wdPos = t.wdPos || 0; + var bPosPxOffset = t.bPosPxOffset || 0; + var whiskerWidth = trace.whiskerwidth || 0; + var notched = trace.notched || false; + var nw = notched ? 1 - 2 * trace.notchwidth : 1; + + // to support for one-sided box + var bdPos0; + var bdPos1; + if(Array.isArray(t.bdPos)) { + bdPos0 = t.bdPos[0]; + bdPos1 = t.bdPos[1]; + } else { + bdPos0 = t.bdPos; + bdPos1 = t.bdPos; + } + + var paths = sel.selectAll('path.box').data(( + trace.type !== 'violin' || + trace.box.visible + ) ? Lib.identity : []); + + paths.enter().append('path') + .style('vector-effect', 'non-scaling-stroke') + .attr('class', 'box'); + + paths.exit().remove(); + + paths.each(function(d) { + if(d.empty) return 'M0,0Z'; + + var lcenter = posAxis.c2l(d.pos + bPos, true); + + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; + var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; + var posc = posHasRangeBreaks ? (pos0 + pos1) / 2 : posAxis.l2p(lcenter) + bPosPxOffset; + + var r = trace.whiskerwidth; + var posw0 = posHasRangeBreaks ? pos0 * r + (1 - r) * posc : posAxis.l2p(lcenter - wdPos) + bPosPxOffset; + var posw1 = posHasRangeBreaks ? pos1 * r + (1 - r) * posc : posAxis.l2p(lcenter + wdPos) + bPosPxOffset; + + var posm0 = posAxis.l2p(lcenter - bdPos0 * nw) + bPosPxOffset; + var posm1 = posAxis.l2p(lcenter + bdPos1 * nw) + bPosPxOffset; + var q1 = valAxis.c2p(d.q1, true); + var q3 = valAxis.c2p(d.q3, true); + // make sure median isn't identical to either of the + // quartiles, so we can see it + var m = Lib.constrain( + valAxis.c2p(d.med, true), + Math.min(q1, q3) + 1, Math.max(q1, q3) - 1 + ); + + // for compatibility with box, violin, and candlestick + // perhaps we should put this into cd0.t instead so it's more explicit, + // but what we have now is: + // - box always has d.lf, but boxpoints can be anything + // - violin has d.lf and should always use it (boxpoints is undefined) + // - candlestick has only min/max + var useExtremes = (d.lf === undefined) || (trace.boxpoints === false); + var lf = valAxis.c2p(useExtremes ? d.min : d.lf, true); + var uf = valAxis.c2p(useExtremes ? d.max : d.uf, true); + var ln = valAxis.c2p(d.ln, true); + var un = valAxis.c2p(d.un, true); + + if(isHorizontal) { + d3.select(this).attr('d', + 'M' + m + ',' + posm0 + 'V' + posm1 + // median line + 'M' + q1 + ',' + pos0 + 'V' + pos1 + // left edge + (notched ? + 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 : + '' + ) + // top notched edge + 'H' + q3 + // end of the top edge + 'V' + pos0 + // right edge + (notched ? 'H' + un + 'L' + m + ',' + posm0 + 'L' + ln + ',' + pos0 : '') + // bottom notched edge + 'Z' + // end of the box + 'M' + q1 + ',' + posc + 'H' + lf + 'M' + q3 + ',' + posc + 'H' + uf + // whiskers + (whiskerWidth === 0 ? + '' : // whisker caps + 'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1 + ) + ); + } else { + d3.select(this).attr('d', + 'M' + posm0 + ',' + m + 'H' + posm1 + // median line + 'M' + pos0 + ',' + q1 + 'H' + pos1 + // top of the box + (notched ? + 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un : + '' + ) + // notched right edge + 'V' + q3 + // end of the right edge + 'H' + pos0 + // bottom of the box + (notched ? + 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln : + '' + ) + // notched left edge + 'Z' + // end of the box + 'M' + posc + ',' + q1 + 'V' + lf + 'M' + posc + ',' + q3 + 'V' + uf + // whiskers + (whiskerWidth === 0 ? + '' : // whisker caps + 'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1 + ) + ); + } + }); +} + +function plotPoints(sel, axes, trace, t) { + var xa = axes.x; + var ya = axes.y; + var bdPos = t.bdPos; + var bPos = t.bPos; + + // to support violin points + var mode = trace.boxpoints || trace.points; + + // repeatable pseudo-random number generator + Lib.seedPseudoRandom(); + + // since box plot points get an extra level of nesting, each + // box needs the trace styling info + var fn = function(d) { + d.forEach(function(v) { + v.t = t; + v.trace = trace; + }); + return d; + }; + + var gPoints = sel.selectAll('g.points') + .data(mode ? fn : []); + + gPoints.enter().append('g') + .attr('class', 'points'); + + gPoints.exit().remove(); + + var paths = gPoints.selectAll('path') + .data(function(d) { + var i; + var pts = d.pts2; + + // normally use IQR, but if this is 0 or too small, use max-min + var typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1); + var minSpread = typicalSpread * 1e-9; + var spreadLimit = typicalSpread * JITTERSPREAD; + var jitterFactors = []; + var maxJitterFactor = 0; + var newJitter; + + // dynamic jitter + if(trace.jitter) { + if(typicalSpread === 0) { + // edge case of no spread at all: fall back to max jitter + maxJitterFactor = 1; + jitterFactors = new Array(pts.length); + for(i = 0; i < pts.length; i++) { + jitterFactors[i] = 1; + } + } else { + for(i = 0; i < pts.length; i++) { + var i0 = Math.max(0, i - JITTERCOUNT); + var pmin = pts[i0].v; + var i1 = Math.min(pts.length - 1, i + JITTERCOUNT); + var pmax = pts[i1].v; + + if(mode !== 'all') { + if(pts[i].v < d.lf) pmax = Math.min(pmax, d.lf); + else pmin = Math.max(pmin, d.uf); + } + + var jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0; + jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1); + + jitterFactors.push(jitterFactor); + maxJitterFactor = Math.max(jitterFactor, maxJitterFactor); + } + } + newJitter = trace.jitter * 2 / (maxJitterFactor || 1); + } + + // fills in 'x' and 'y' in calcdata 'pts' item + for(i = 0; i < pts.length; i++) { + var pt = pts[i]; + var v = pt.v; + + var jitterOffset = trace.jitter ? + (newJitter * jitterFactors[i] * (Lib.pseudoRandom() - 0.5)) : + 0; + + var posPx = d.pos + bPos + bdPos * (trace.pointpos + jitterOffset); + + if(trace.orientation === 'h') { + pt.y = posPx; + pt.x = v; + } else { + pt.x = posPx; + pt.y = v; + } + + // tag suspected outliers + if(mode === 'suspectedoutliers' && v < d.uo && v > d.lo) { + pt.so = true; + } + } + + return pts; + }); + + paths.enter().append('path') + .classed('point', true); + + paths.exit().remove(); + + paths.call(Drawing.translatePoints, xa, ya); +} + +function plotBoxMean(sel, axes, trace, t) { + var valAxis = axes.val; + var posAxis = axes.pos; + var posHasRangeBreaks = !!posAxis.rangebreaks; + + var bPos = t.bPos; + var bPosPxOffset = t.bPosPxOffset || 0; + + // to support violin mean lines + var mode = trace.boxmean || (trace.meanline || {}).visible; + + // to support for one-sided box + var bdPos0; + var bdPos1; + if(Array.isArray(t.bdPos)) { + bdPos0 = t.bdPos[0]; + bdPos1 = t.bdPos[1]; + } else { + bdPos0 = t.bdPos; + bdPos1 = t.bdPos; + } + + var paths = sel.selectAll('path.mean').data(( + (trace.type === 'box' && trace.boxmean) || + (trace.type === 'violin' && trace.box.visible && trace.meanline.visible) + ) ? Lib.identity : []); + + paths.enter().append('path') + .attr('class', 'mean') + .style({ + fill: 'none', + 'vector-effect': 'non-scaling-stroke' + }); + + paths.exit().remove(); + + paths.each(function(d) { + var lcenter = posAxis.c2l(d.pos + bPos, true); + + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; + var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; + var posc = posHasRangeBreaks ? (pos0 + pos1) / 2 : posAxis.l2p(lcenter) + bPosPxOffset; + + var m = valAxis.c2p(d.mean, true); + var sl = valAxis.c2p(d.mean - d.sd, true); + var sh = valAxis.c2p(d.mean + d.sd, true); + + if(trace.orientation === 'h') { + d3.select(this).attr('d', + 'M' + m + ',' + pos0 + 'V' + pos1 + + (mode === 'sd' ? + 'm0,0L' + sl + ',' + posc + 'L' + m + ',' + pos0 + 'L' + sh + ',' + posc + 'Z' : + '') + ); + } else { + d3.select(this).attr('d', + 'M' + pos0 + ',' + m + 'H' + pos1 + + (mode === 'sd' ? + 'm0,0L' + posc + ',' + sl + 'L' + pos0 + ',' + m + 'L' + posc + ',' + sh + 'Z' : + '') + ); + } + }); +} + +module.exports = { + plot: plot, + plotBoxAndWhiskers: plotBoxAndWhiskers, + plotPoints: plotPoints, + plotBoxMean: plotBoxMean +}; + +},{"../../components/drawing":177,"../../lib":285,"@plotly/d3":20}],410:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function selectPoints(searchInfo, selectionTester) { + var cd = searchInfo.cd; + var xa = searchInfo.xaxis; + var ya = searchInfo.yaxis; + var selection = []; + var i, j; + + if(selectionTester === false) { + for(i = 0; i < cd.length; i++) { + for(j = 0; j < (cd[i].pts || []).length; j++) { + // clear selection + cd[i].pts[j].selected = 0; + } + } + } else { + for(i = 0; i < cd.length; i++) { + for(j = 0; j < (cd[i].pts || []).length; j++) { + var pt = cd[i].pts[j]; + var x = xa.c2p(pt.x); + var y = ya.c2p(pt.y); + + if(selectionTester.contains([x, y], null, pt.i, searchInfo)) { + selection.push({ + pointNumber: pt.i, + x: xa.c2d(pt.x), + y: ya.c2d(pt.y) + }); + pt.selected = 1; + } else { + pt.selected = 0; + } + } + } + } + + return selection; +}; + +},{}],411:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); + +function style(gd, cd, sel) { + var s = sel ? sel : d3.select(gd).selectAll('g.trace.boxes'); + + s.style('opacity', function(d) { return d[0].trace.opacity; }); + + s.each(function(d) { + var el = d3.select(this); + var trace = d[0].trace; + var lineWidth = trace.line.width; + + function styleBox(boxSel, lineWidth, lineColor, fillColor) { + boxSel.style('stroke-width', lineWidth + 'px') + .call(Color.stroke, lineColor) + .call(Color.fill, fillColor); + } + + var allBoxes = el.selectAll('path.box'); + + if(trace.type === 'candlestick') { + allBoxes.each(function(boxData) { + if(boxData.empty) return; + + var thisBox = d3.select(this); + var container = trace[boxData.dir]; // dir = 'increasing' or 'decreasing' + styleBox(thisBox, container.line.width, container.line.color, container.fillcolor); + // TODO: custom selection style for candlesticks + thisBox.style('opacity', trace.selectedpoints && !boxData.selected ? 0.3 : 1); + }); + } else { + styleBox(allBoxes, lineWidth, trace.line.color, trace.fillcolor); + el.selectAll('path.mean') + .style({ + 'stroke-width': lineWidth, + 'stroke-dasharray': (2 * lineWidth) + 'px,' + lineWidth + 'px' + }) + .call(Color.stroke, trace.line.color); + + var pts = el.selectAll('path.point'); + Drawing.pointStyle(pts, trace, gd); + } + }); +} + +function styleOnSelect(gd, cd, sel) { + var trace = cd[0].trace; + var pts = sel.selectAll('path.point'); + + if(trace.selectedpoints) { + Drawing.selectedPointStyle(pts, trace); + } else { + Drawing.pointStyle(pts, trace, gd); + } +} + +module.exports = { + style: style, + styleOnSelect: styleOnSelect +}; + +},{"../../components/color":155,"../../components/drawing":177,"@plotly/d3":20}],412:[function(_dereq_,module,exports){ +'use strict'; + +var heatmapAttrs = _dereq_('../heatmap/attributes'); +var scatterAttrs = _dereq_('../scatter/attributes'); +var axisFormat = _dereq_('../../plots/cartesian/axis_format_attributes'); +var axisHoverFormat = axisFormat.axisHoverFormat; +var descriptionOnlyNumbers = axisFormat.descriptionOnlyNumbers; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var dash = _dereq_('../../components/drawing/attributes').dash; +var fontAttrs = _dereq_('../../plots/font_attributes'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var filterOps = _dereq_('../../constants/filter_ops'); +var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2; +var INTERVAL_OPS = filterOps.INTERVAL_OPS; + + +var scatterLineAttrs = scatterAttrs.line; + +module.exports = extendFlat({ + z: heatmapAttrs.z, + x: heatmapAttrs.x, + x0: heatmapAttrs.x0, + dx: heatmapAttrs.dx, + y: heatmapAttrs.y, + y0: heatmapAttrs.y0, + dy: heatmapAttrs.dy, + + xperiod: heatmapAttrs.xperiod, + yperiod: heatmapAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: heatmapAttrs.xperiodalignment, + yperiodalignment: heatmapAttrs.yperiodalignment, + + text: heatmapAttrs.text, + hovertext: heatmapAttrs.hovertext, + transpose: heatmapAttrs.transpose, + xtype: heatmapAttrs.xtype, + ytype: heatmapAttrs.ytype, + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + zhoverformat: axisHoverFormat('z', 1), + hovertemplate: heatmapAttrs.hovertemplate, + hoverongaps: heatmapAttrs.hoverongaps, + connectgaps: extendFlat({}, heatmapAttrs.connectgaps, { + }), + + fillcolor: { + valType: 'color', + editType: 'calc', + }, + + autocontour: { + valType: 'boolean', + dflt: true, + editType: 'calc', + impliedEdits: { + 'contours.start': undefined, + 'contours.end': undefined, + 'contours.size': undefined + }, + }, + ncontours: { + valType: 'integer', + dflt: 15, + min: 1, + editType: 'calc', + }, + + contours: { + type: { + valType: 'enumerated', + values: ['levels', 'constraint'], + dflt: 'levels', + editType: 'calc', + }, + start: { + valType: 'number', + dflt: null, + editType: 'plot', + impliedEdits: {'^autocontour': false}, + }, + end: { + valType: 'number', + dflt: null, + editType: 'plot', + impliedEdits: {'^autocontour': false}, + }, + size: { + valType: 'number', + dflt: null, + min: 0, + editType: 'plot', + impliedEdits: {'^autocontour': false}, + }, + coloring: { + valType: 'enumerated', + values: ['fill', 'heatmap', 'lines', 'none'], + dflt: 'fill', + editType: 'calc', + }, + showlines: { + valType: 'boolean', + dflt: true, + editType: 'plot', + }, + showlabels: { + valType: 'boolean', + dflt: false, + editType: 'plot', + }, + labelfont: fontAttrs({ + editType: 'plot', + colorEditType: 'style', + }), + labelformat: { + valType: 'string', + dflt: '', + editType: 'plot', + description: descriptionOnlyNumbers('contour label') + }, + operation: { + valType: 'enumerated', + values: [].concat(COMPARISON_OPS2).concat(INTERVAL_OPS), + dflt: '=', + editType: 'calc', + }, + value: { + valType: 'any', + dflt: 0, + editType: 'calc', + }, + editType: 'calc', + impliedEdits: {'autocontour': false} + }, + + line: { + color: extendFlat({}, scatterLineAttrs.color, { + editType: 'style+colorbars', + }), + width: { + valType: 'number', + min: 0, + editType: 'style+colorbars', + }, + dash: dash, + smoothing: extendFlat({}, scatterLineAttrs.smoothing, { + }), + editType: 'plot' + } +}, + colorScaleAttrs('', { + cLetter: 'z', + autoColorDflt: false, + editTypeOverride: 'calc' + }) +); + +},{"../../components/colorscale/attributes":162,"../../components/drawing/attributes":176,"../../constants/filter_ops":263,"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../../plots/font_attributes":360,"../heatmap/attributes":434,"../scatter/attributes":494}],413:[function(_dereq_,module,exports){ +'use strict'; + +var Colorscale = _dereq_('../../components/colorscale'); + +var heatmapCalc = _dereq_('../heatmap/calc'); +var setContours = _dereq_('./set_contours'); +var endPlus = _dereq_('./end_plus'); + +// most is the same as heatmap calc, then adjust it +// though a few things inside heatmap calc still look for +// contour maps, because the makeBoundArray calls are too entangled +module.exports = function calc(gd, trace) { + var cd = heatmapCalc(gd, trace); + + var zOut = cd[0].z; + setContours(trace, zOut); + + var contours = trace.contours; + var cOpts = Colorscale.extractOpts(trace); + var cVals; + + if(contours.coloring === 'heatmap' && cOpts.auto && trace.autocontour === false) { + var start = contours.start; + var end = endPlus(contours); + var cs = contours.size || 1; + var nc = Math.floor((end - start) / cs) + 1; + + if(!isFinite(cs)) { + cs = 1; + nc = 1; + } + + var min0 = start - cs / 2; + var max0 = min0 + nc * cs; + cVals = [min0, max0]; + } else { + cVals = zOut; + } + + Colorscale.calc(gd, trace, {vals: cVals, cLetter: 'z'}); + + return cd; +}; + +},{"../../components/colorscale":167,"../heatmap/calc":435,"./end_plus":423,"./set_contours":431}],414:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function(pathinfo, contours) { + var pi0 = pathinfo[0]; + var z = pi0.z; + var i; + + switch(contours.type) { + case 'levels': + // Why (just) use z[0][0] and z[0][1]? + // + // N.B. using boundaryMin instead of edgeVal2 here makes the + // `contour_scatter` mock fail + var edgeVal2 = Math.min(z[0][0], z[0][1]); + + for(i = 0; i < pathinfo.length; i++) { + var pi = pathinfo[i]; + pi.prefixBoundary = !pi.edgepaths.length && + (edgeVal2 > pi.level || pi.starts.length && edgeVal2 === pi.level); + } + break; + case 'constraint': + // after convertToConstraints, pathinfo has length=0 + pi0.prefixBoundary = false; + + // joinAllPaths does enough already when edgepaths are present + if(pi0.edgepaths.length) return; + + var na = pi0.x.length; + var nb = pi0.y.length; + var boundaryMax = -Infinity; + var boundaryMin = Infinity; + + for(i = 0; i < nb; i++) { + boundaryMin = Math.min(boundaryMin, z[i][0]); + boundaryMin = Math.min(boundaryMin, z[i][na - 1]); + boundaryMax = Math.max(boundaryMax, z[i][0]); + boundaryMax = Math.max(boundaryMax, z[i][na - 1]); + } + for(i = 1; i < na - 1; i++) { + boundaryMin = Math.min(boundaryMin, z[0][i]); + boundaryMin = Math.min(boundaryMin, z[nb - 1][i]); + boundaryMax = Math.max(boundaryMax, z[0][i]); + boundaryMax = Math.max(boundaryMax, z[nb - 1][i]); + } + + var contoursValue = contours.value; + var v1, v2; + + switch(contours._operation) { + case '>': + if(contoursValue > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + case '<': + if(contoursValue < boundaryMin || + (pi0.starts.length && contoursValue === boundaryMin)) { + pi0.prefixBoundary = true; + } + break; + case '[]': + v1 = Math.min(contoursValue[0], contoursValue[1]); + v2 = Math.max(contoursValue[0], contoursValue[1]); + if(v2 < boundaryMin || v1 > boundaryMax || + (pi0.starts.length && v2 === boundaryMin)) { + pi0.prefixBoundary = true; + } + break; + case '][': + v1 = Math.min(contoursValue[0], contoursValue[1]); + v2 = Math.max(contoursValue[0], contoursValue[1]); + if(v1 < boundaryMin && v2 > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + } + break; + } +}; + +},{}],415:[function(_dereq_,module,exports){ +'use strict'; + +var Colorscale = _dereq_('../../components/colorscale'); +var makeColorMap = _dereq_('./make_color_map'); +var endPlus = _dereq_('./end_plus'); + +function calc(gd, trace, opts) { + var contours = trace.contours; + var line = trace.line; + var cs = contours.size || 1; + var coloring = contours.coloring; + var colorMap = makeColorMap(trace, {isColorbar: true}); + + if(coloring === 'heatmap') { + var cOpts = Colorscale.extractOpts(trace); + opts._fillgradient = cOpts.reversescale ? + Colorscale.flipScale(cOpts.colorscale) : + cOpts.colorscale; + opts._zrange = [cOpts.min, cOpts.max]; + } else if(coloring === 'fill') { + opts._fillcolor = colorMap; + } + + opts._line = { + color: coloring === 'lines' ? colorMap : line.color, + width: contours.showlines !== false ? line.width : 0, + dash: line.dash + }; + + opts._levels = { + start: contours.start, + end: endPlus(contours), + size: cs + }; +} + +module.exports = { + min: 'zmin', + max: 'zmax', + calc: calc +}; + +},{"../../components/colorscale":167,"./end_plus":423,"./make_color_map":428}],416:[function(_dereq_,module,exports){ +'use strict'; +module.exports = { + // some constants to help with marching squares algorithm + // where does the path start for each index? + BOTTOMSTART: [1, 9, 13, 104, 713], + TOPSTART: [4, 6, 7, 104, 713], + LEFTSTART: [8, 12, 14, 208, 1114], + RIGHTSTART: [2, 3, 11, 208, 1114], + + // which way [dx,dy] do we leave a given index? + // saddles are already disambiguated + NEWDELTA: [ + null, [-1, 0], [0, -1], [-1, 0], + [1, 0], null, [0, -1], [-1, 0], + [0, 1], [0, 1], null, [0, 1], + [1, 0], [1, 0], [0, -1] + ], + + // for each saddle, the first index here is used + // for dx||dy<0, the second for dx||dy>0 + CHOOSESADDLE: { + 104: [4, 1], + 208: [2, 8], + 713: [7, 13], + 1114: [11, 14] + }, + + // after one index has been used for a saddle, which do we + // substitute to be used up later? + SADDLEREMAINDER: {1: 4, 2: 8, 4: 1, 7: 13, 8: 2, 11: 14, 13: 7, 14: 11}, + + // length of a contour, as a multiple of the plot area diagonal, per label + LABELDISTANCE: 2, + + // number of contour levels after which we start increasing the number of + // labels we draw. Many contours means they will generally be close + // together, so it will be harder to follow a long way to find a label + LABELINCREASE: 10, + + // minimum length of a contour line, as a multiple of the label length, + // at which we draw *any* labels + LABELMIN: 3, + + // max number of labels to draw on a single contour path, no matter how long + LABELMAX: 10, + + // constants for the label position cost function + LABELOPTIMIZER: { + // weight given to edge proximity + EDGECOST: 1, + // weight given to the angle off horizontal + ANGLECOST: 1, + // weight given to distance from already-placed labels + NEIGHBORCOST: 5, + // cost multiplier for labels on the same level + SAMELEVELFACTOR: 10, + // minimum distance (as a multiple of the label length) + // for labels on the same level + SAMELEVELDISTANCE: 5, + // maximum cost before we won't even place the label + MAXCOST: 100, + // number of evenly spaced points to look at in the first + // iteration of the search + INITIALSEARCHPOINTS: 10, + // number of binary search iterations after the initial wide search + ITERATIONS: 5 + } +}; + +},{}],417:[function(_dereq_,module,exports){ +'use strict'; +var isNumeric = _dereq_('fast-isnumeric'); + +var handleLabelDefaults = _dereq_('./label_defaults'); + +var Color = _dereq_('../../components/color'); +var addOpacity = Color.addOpacity; +var opacity = Color.opacity; + +var filterOps = _dereq_('../../constants/filter_ops'); +var CONSTRAINT_REDUCTION = filterOps.CONSTRAINT_REDUCTION; +var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2; + +module.exports = function handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor, opts) { + var contours = traceOut.contours; + var showLines, lineColor, fillColor; + + var operation = coerce('contours.operation'); + contours._operation = CONSTRAINT_REDUCTION[operation]; + + handleConstraintValueDefaults(coerce, contours); + + if(operation === '=') { + showLines = contours.showlines = true; + } else { + showLines = coerce('contours.showlines'); + fillColor = coerce('fillcolor', addOpacity( + (traceIn.line || {}).color || defaultColor, 0.5 + )); + } + + if(showLines) { + var lineDfltColor = fillColor && opacity(fillColor) ? + addOpacity(traceOut.fillcolor, 1) : + defaultColor; + lineColor = coerce('line.color', lineDfltColor); + coerce('line.width', 2); + coerce('line.dash'); + } + + coerce('line.smoothing'); + + handleLabelDefaults(coerce, layout, lineColor, opts); +}; + +function handleConstraintValueDefaults(coerce, contours) { + var zvalue; + + if(COMPARISON_OPS2.indexOf(contours.operation) === -1) { + // Requires an array of two numbers: + coerce('contours.value', [0, 1]); + + if(!Array.isArray(contours.value)) { + if(isNumeric(contours.value)) { + zvalue = parseFloat(contours.value); + contours.value = [zvalue, zvalue + 1]; + } + } else if(contours.value.length > 2) { + contours.value = contours.value.slice(2); + } else if(contours.length === 0) { + contours.value = [0, 1]; + } else if(contours.length < 2) { + zvalue = parseFloat(contours.value[0]); + contours.value = [zvalue, zvalue + 1]; + } else { + contours.value = [ + parseFloat(contours.value[0]), + parseFloat(contours.value[1]) + ]; + } + } else { + // Requires a single scalar: + coerce('contours.value', 0); + + if(!isNumeric(contours.value)) { + if(Array.isArray(contours.value)) { + contours.value = parseFloat(contours.value[0]); + } else { + contours.value = 0; + } + } + } +} + +},{"../../components/color":155,"../../constants/filter_ops":263,"./label_defaults":427,"fast-isnumeric":31}],418:[function(_dereq_,module,exports){ +'use strict'; + +var filterOps = _dereq_('../../constants/filter_ops'); +var isNumeric = _dereq_('fast-isnumeric'); + +// This syntax conforms to the existing filter transform syntax, but we don't care +// about open vs. closed intervals for simply drawing contours constraints: +module.exports = { + '[]': makeRangeSettings('[]'), + '][': makeRangeSettings(']['), + '>': makeInequalitySettings('>'), + '<': makeInequalitySettings('<'), + '=': makeInequalitySettings('=') +}; + +// This does not in any way shape or form support calendars. It's adapted from +// transforms/filter.js. +function coerceValue(operation, value) { + var hasArrayValue = Array.isArray(value); + + var coercedValue; + + function coerce(value) { + return isNumeric(value) ? (+value) : null; + } + + if(filterOps.COMPARISON_OPS2.indexOf(operation) !== -1) { + coercedValue = hasArrayValue ? coerce(value[0]) : coerce(value); + } else if(filterOps.INTERVAL_OPS.indexOf(operation) !== -1) { + coercedValue = hasArrayValue ? + [coerce(value[0]), coerce(value[1])] : + [coerce(value), coerce(value)]; + } else if(filterOps.SET_OPS.indexOf(operation) !== -1) { + coercedValue = hasArrayValue ? value.map(coerce) : [coerce(value)]; + } + + return coercedValue; +} + +// Returns a parabola scaled so that the min/max is either +/- 1 and zero at the two values +// provided. The data is mapped by this function when constructing intervals so that it's +// very easy to construct contours as normal. +function makeRangeSettings(operation) { + return function(value) { + value = coerceValue(operation, value); + + // Ensure proper ordering: + var min = Math.min(value[0], value[1]); + var max = Math.max(value[0], value[1]); + + return { + start: min, + end: max, + size: max - min + }; + }; +} + +function makeInequalitySettings(operation) { + return function(value) { + value = coerceValue(operation, value); + + return { + start: value, + end: Infinity, + size: Infinity + }; + }; +} + +},{"../../constants/filter_ops":263,"fast-isnumeric":31}],419:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function handleContourDefaults(traceIn, traceOut, coerce, coerce2) { + var contourStart = coerce2('contours.start'); + var contourEnd = coerce2('contours.end'); + var missingEnd = (contourStart === false) || (contourEnd === false); + + // normally we only need size if autocontour is off. But contour.calc + // pushes its calculated contour size back to the input trace, so for + // things like restyle that can call supplyDefaults without calc + // after the initial draw, we can just reuse the previous calculation + var contourSize = coerce('contours.size'); + var autoContour; + + if(missingEnd) autoContour = traceOut.autocontour = true; + else autoContour = coerce('autocontour', false); + + if(autoContour || !contourSize) coerce('ncontours'); +}; + +},{}],420:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +// The contour extraction is great, except it totally fails for constraints because we +// need weird range loops and flipped contours instead of the usual format. This function +// does some weird manipulation of the extracted pathinfo data such that it magically +// draws contours correctly *as* constraints. +// +// ** I do not know which "weird range loops" the comment above is referring to. +module.exports = function(pathinfo, operation) { + var i, pi0, pi1; + + var op0 = function(arr) { return arr.reverse(); }; + var op1 = function(arr) { return arr; }; + + switch(operation) { + case '=': + case '<': + return pathinfo; + case '>': + if(pathinfo.length !== 1) { + Lib.warn('Contour data invalid for the specified inequality operation.'); + } + + // In this case there should be exactly one contour levels in pathinfo. + // We flip all of the data. This will draw the contour as closed. + pi0 = pathinfo[0]; + + for(i = 0; i < pi0.edgepaths.length; i++) { + pi0.edgepaths[i] = op0(pi0.edgepaths[i]); + } + for(i = 0; i < pi0.paths.length; i++) { + pi0.paths[i] = op0(pi0.paths[i]); + } + for(i = 0; i < pi0.starts.length; i++) { + pi0.starts[i] = op0(pi0.starts[i]); + } + + return pathinfo; + case '][': + var tmp = op0; + op0 = op1; + op1 = tmp; + // It's a nice rule, except this definitely *is* what's intended here. + /* eslint-disable: no-fallthrough */ + case '[]': + /* eslint-enable: no-fallthrough */ + if(pathinfo.length !== 2) { + Lib.warn('Contour data invalid for the specified inequality range operation.'); + } + + // In this case there should be exactly two contour levels in pathinfo. + // - We concatenate the info into one pathinfo. + // - We must also flip all of the data in the `[]` case. + // This will draw the contours as closed. + pi0 = copyPathinfo(pathinfo[0]); + pi1 = copyPathinfo(pathinfo[1]); + + for(i = 0; i < pi0.edgepaths.length; i++) { + pi0.edgepaths[i] = op0(pi0.edgepaths[i]); + } + for(i = 0; i < pi0.paths.length; i++) { + pi0.paths[i] = op0(pi0.paths[i]); + } + for(i = 0; i < pi0.starts.length; i++) { + pi0.starts[i] = op0(pi0.starts[i]); + } + + while(pi1.edgepaths.length) { + pi0.edgepaths.push(op1(pi1.edgepaths.shift())); + } + while(pi1.paths.length) { + pi0.paths.push(op1(pi1.paths.shift())); + } + while(pi1.starts.length) { + pi0.starts.push(op1(pi1.starts.shift())); + } + + return [pi0]; + } +}; + +function copyPathinfo(pi) { + return Lib.extendFlat({}, pi, { + edgepaths: Lib.extendDeep([], pi.edgepaths), + paths: Lib.extendDeep([], pi.paths), + starts: Lib.extendDeep([], pi.starts) + }); +} + +},{"../../lib":285}],421:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var handleXYZDefaults = _dereq_('../heatmap/xyz_defaults'); +var handlePeriodDefaults = _dereq_('../scatter/period_defaults'); +var handleConstraintDefaults = _dereq_('./constraint_defaults'); +var handleContoursDefaults = _dereq_('./contours_defaults'); +var handleStyleDefaults = _dereq_('./style_defaults'); +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + function coerce2(attr) { + return Lib.coerce2(traceIn, traceOut, attributes, attr); + } + + var len = handleXYZDefaults(traceIn, traceOut, coerce, layout); + if(!len) { + traceOut.visible = false; + return; + } + + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('xhoverformat'); + coerce('yhoverformat'); + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + coerce('hoverongaps'); + + var isConstraint = (coerce('contours.type') === 'constraint'); + coerce('connectgaps', Lib.isArray1D(traceOut.z)); + + if(isConstraint) { + handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor); + } else { + handleContoursDefaults(traceIn, traceOut, coerce, coerce2); + handleStyleDefaults(traceIn, traceOut, coerce, layout); + } +}; + +},{"../../lib":285,"../heatmap/xyz_defaults":448,"../scatter/period_defaults":514,"./attributes":412,"./constraint_defaults":417,"./contours_defaults":419,"./style_defaults":433}],422:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var constraintMapping = _dereq_('./constraint_mapping'); +var endPlus = _dereq_('./end_plus'); + +module.exports = function emptyPathinfo(contours, plotinfo, cd0) { + var contoursFinal = (contours.type === 'constraint') ? + constraintMapping[contours._operation](contours.value) : + contours; + + var cs = contoursFinal.size; + var pathinfo = []; + var end = endPlus(contoursFinal); + + var carpet = cd0.trace._carpetTrace; + + var basePathinfo = carpet ? { + // store axes so we can convert to px + xaxis: carpet.aaxis, + yaxis: carpet.baxis, + // full data arrays to use for interpolation + x: cd0.a, + y: cd0.b + } : { + xaxis: plotinfo.xaxis, + yaxis: plotinfo.yaxis, + x: cd0.x, + y: cd0.y + }; + + for(var ci = contoursFinal.start; ci < end; ci += cs) { + pathinfo.push(Lib.extendFlat({ + level: ci, + // all the cells with nontrivial marching index + crossings: {}, + // starting points on the edges of the lattice for each contour + starts: [], + // all unclosed paths (may have less items than starts, + // if a path is closed by rounding) + edgepaths: [], + // all closed paths + paths: [], + z: cd0.z, + smoothing: cd0.trace.line.smoothing + }, basePathinfo)); + + if(pathinfo.length > 1000) { + Lib.warn('Too many contours, clipping at 1000', contours); + break; + } + } + return pathinfo; +}; + +},{"../../lib":285,"./constraint_mapping":418,"./end_plus":423}],423:[function(_dereq_,module,exports){ +'use strict'; + +/* + * tiny helper to move the end of the contours a little to prevent + * losing the last contour to rounding errors + */ +module.exports = function endPlus(contours) { + return contours.end + contours.size / 1e6; +}; + +},{}],424:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var constants = _dereq_('./constants'); + +module.exports = function findAllPaths(pathinfo, xtol, ytol) { + var cnt, + startLoc, + i, + pi, + j; + + // Default just passes these values through as they were before: + xtol = xtol || 0.01; + ytol = ytol || 0.01; + + for(i = 0; i < pathinfo.length; i++) { + pi = pathinfo[i]; + + for(j = 0; j < pi.starts.length; j++) { + startLoc = pi.starts[j]; + makePath(pi, startLoc, 'edge', xtol, ytol); + } + + cnt = 0; + while(Object.keys(pi.crossings).length && cnt < 10000) { + cnt++; + startLoc = Object.keys(pi.crossings)[0].split(',').map(Number); + makePath(pi, startLoc, undefined, xtol, ytol); + } + if(cnt === 10000) Lib.log('Infinite loop in contour?'); + } +}; + +function equalPts(pt1, pt2, xtol, ytol) { + return Math.abs(pt1[0] - pt2[0]) < xtol && + Math.abs(pt1[1] - pt2[1]) < ytol; +} + +// distance in index units - uses the 3rd and 4th items in points +function ptDist(pt1, pt2) { + var dx = pt1[2] - pt2[2]; + var dy = pt1[3] - pt2[3]; + return Math.sqrt(dx * dx + dy * dy); +} + +function makePath(pi, loc, edgeflag, xtol, ytol) { + var locStr = loc.join(','); + var mi = pi.crossings[locStr]; + var marchStep = getStartStep(mi, edgeflag, loc); + // start by going backward a half step and finding the crossing point + var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])]; + var m = pi.z.length; + var n = pi.z[0].length; + var startLoc = loc.slice(); + var startStep = marchStep.slice(); + var cnt; + + // now follow the path + for(cnt = 0; cnt < 10000; cnt++) { // just to avoid infinite loops + if(mi > 20) { + mi = constants.CHOOSESADDLE[mi][(marchStep[0] || marchStep[1]) < 0 ? 0 : 1]; + pi.crossings[locStr] = constants.SADDLEREMAINDER[mi]; + } else { + delete pi.crossings[locStr]; + } + + marchStep = constants.NEWDELTA[mi]; + if(!marchStep) { + Lib.log('Found bad marching index:', mi, loc, pi.level); + break; + } + + // find the crossing a half step forward, and then take the full step + pts.push(getInterpPx(pi, loc, marchStep)); + loc[0] += marchStep[0]; + loc[1] += marchStep[1]; + locStr = loc.join(','); + + // don't include the same point multiple times + if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop(); + + var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) || + (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2)); + + var closedLoop = loc[0] === startLoc[0] && loc[1] === startLoc[1] && + marchStep[0] === startStep[0] && marchStep[1] === startStep[1]; + + // have we completed a loop, or reached an edge? + if((closedLoop) || (edgeflag && atEdge)) break; + + mi = pi.crossings[locStr]; + } + + if(cnt === 10000) { + Lib.log('Infinite loop in contour?'); + } + var closedpath = equalPts(pts[0], pts[pts.length - 1], xtol, ytol); + var totaldist = 0; + var distThresholdFactor = 0.2 * pi.smoothing; + var alldists = []; + var cropstart = 0; + var distgroup, cnt2, cnt3, newpt, ptcnt, ptavg, thisdist, + i, j, edgepathi, edgepathj; + + /* + * Check for points that are too close together (<1/5 the average dist + * *in grid index units* (important for log axes and nonuniform grids), + * less if less smoothed) and just take the center (or avg of center 2). + * This cuts down on funny behavior when a point is very close to a + * contour level. + */ + for(cnt = 1; cnt < pts.length; cnt++) { + thisdist = ptDist(pts[cnt], pts[cnt - 1]); + totaldist += thisdist; + alldists.push(thisdist); + } + + var distThreshold = totaldist / alldists.length * distThresholdFactor; + + function getpt(i) { return pts[i % pts.length]; } + + for(cnt = pts.length - 2; cnt >= cropstart; cnt--) { + distgroup = alldists[cnt]; + if(distgroup < distThreshold) { + cnt3 = 0; + for(cnt2 = cnt - 1; cnt2 >= cropstart; cnt2--) { + if(distgroup + alldists[cnt2] < distThreshold) { + distgroup += alldists[cnt2]; + } else break; + } + + // closed path with close points wrapping around the boundary? + if(closedpath && cnt === pts.length - 2) { + for(cnt3 = 0; cnt3 < cnt2; cnt3++) { + if(distgroup + alldists[cnt3] < distThreshold) { + distgroup += alldists[cnt3]; + } else break; + } + } + ptcnt = cnt - cnt2 + cnt3 + 1; + ptavg = Math.floor((cnt + cnt2 + cnt3 + 2) / 2); + + // either endpoint included: keep the endpoint + if(!closedpath && cnt === pts.length - 2) newpt = pts[pts.length - 1]; + else if(!closedpath && cnt2 === -1) newpt = pts[0]; + + // odd # of points - just take the central one + else if(ptcnt % 2) newpt = getpt(ptavg); + + // even # of pts - average central two + else { + newpt = [(getpt(ptavg)[0] + getpt(ptavg + 1)[0]) / 2, + (getpt(ptavg)[1] + getpt(ptavg + 1)[1]) / 2]; + } + + pts.splice(cnt2 + 1, cnt - cnt2 + 1, newpt); + cnt = cnt2 + 1; + if(cnt3) cropstart = cnt3; + if(closedpath) { + if(cnt === pts.length - 2) pts[cnt3] = pts[pts.length - 1]; + else if(cnt === 0) pts[pts.length - 1] = pts[0]; + } + } + } + pts.splice(0, cropstart); + + // done with the index parts - remove them so path generation works right + // because it depends on only having [xpx, ypx] + for(cnt = 0; cnt < pts.length; cnt++) pts[cnt].length = 2; + + // don't return single-point paths (ie all points were the same + // so they got deleted?) + if(pts.length < 2) return; + else if(closedpath) { + pts.pop(); + pi.paths.push(pts); + } else { + if(!edgeflag) { + Lib.log('Unclosed interior contour?', + pi.level, startLoc.join(','), pts.join('L')); + } + + // edge path - does it start where an existing edge path ends, or vice versa? + var merged = false; + for(i = 0; i < pi.edgepaths.length; i++) { + edgepathi = pi.edgepaths[i]; + if(!merged && equalPts(edgepathi[0], pts[pts.length - 1], xtol, ytol)) { + pts.pop(); + merged = true; + + // now does it ALSO meet the end of another (or the same) path? + var doublemerged = false; + for(j = 0; j < pi.edgepaths.length; j++) { + edgepathj = pi.edgepaths[j]; + if(equalPts(edgepathj[edgepathj.length - 1], pts[0], xtol, ytol)) { + doublemerged = true; + pts.shift(); + pi.edgepaths.splice(i, 1); + if(j === i) { + // the path is now closed + pi.paths.push(pts.concat(edgepathj)); + } else { + if(j > i) j--; + pi.edgepaths[j] = edgepathj.concat(pts, edgepathi); + } + break; + } + } + if(!doublemerged) { + pi.edgepaths[i] = pts.concat(edgepathi); + } + } + } + for(i = 0; i < pi.edgepaths.length; i++) { + if(merged) break; + edgepathi = pi.edgepaths[i]; + if(equalPts(edgepathi[edgepathi.length - 1], pts[0], xtol, ytol)) { + pts.shift(); + pi.edgepaths[i] = edgepathi.concat(pts); + merged = true; + } + } + + if(!merged) pi.edgepaths.push(pts); + } +} + +// special function to get the marching step of the +// first point in the path (leading to loc) +function getStartStep(mi, edgeflag, loc) { + var dx = 0; + var dy = 0; + if(mi > 20 && edgeflag) { + // these saddles start at +/- x + if(mi === 208 || mi === 1114) { + // if we're starting at the left side, we must be going right + dx = loc[0] === 0 ? 1 : -1; + } else { + // if we're starting at the bottom, we must be going up + dy = loc[1] === 0 ? 1 : -1; + } + } else if(constants.BOTTOMSTART.indexOf(mi) !== -1) dy = 1; + else if(constants.LEFTSTART.indexOf(mi) !== -1) dx = 1; + else if(constants.TOPSTART.indexOf(mi) !== -1) dy = -1; + else dx = -1; + return [dx, dy]; +} + +/* + * Find the pixel coordinates of a particular crossing + * + * @param {object} pi: the pathinfo object at this level + * @param {array} loc: the grid index [x, y] of the crossing + * @param {array} step: the direction [dx, dy] we're moving on the grid + * + * @return {array} [xpx, ypx, xi, yi]: the first two are the pixel location, + * the next two are the interpolated grid indices, which we use for + * distance calculations to delete points that are too close together. + * This is important when the grid is nonuniform (and most dramatically when + * we're on log axes and include invalid (0 or negative) values. + * It's crucial to delete these extra two before turning an array of these + * points into a path, because those routines require length-2 points. + */ +function getInterpPx(pi, loc, step) { + var locx = loc[0] + Math.max(step[0], 0); + var locy = loc[1] + Math.max(step[1], 0); + var zxy = pi.z[locy][locx]; + var xa = pi.xaxis; + var ya = pi.yaxis; + + if(step[1]) { + var dx = (pi.level - zxy) / (pi.z[locy][locx + 1] - zxy); + + return [xa.c2p((1 - dx) * pi.x[locx] + dx * pi.x[locx + 1], true), + ya.c2p(pi.y[locy], true), + locx + dx, locy]; + } else { + var dy = (pi.level - zxy) / (pi.z[locy + 1][locx] - zxy); + return [xa.c2p(pi.x[locx], true), + ya.c2p((1 - dy) * pi.y[locy] + dy * pi.y[locy + 1], true), + locx, locy + dy]; + } +} + +},{"../../lib":285,"./constants":416}],425:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); + +var heatmapHoverPoints = _dereq_('../heatmap/hover'); + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) { + if(!opts) opts = {}; + opts.isContour = true; + + var hoverData = heatmapHoverPoints(pointData, xval, yval, hovermode, opts); + + if(hoverData) { + hoverData.forEach(function(hoverPt) { + var trace = hoverPt.trace; + if(trace.contours.type === 'constraint') { + if(trace.fillcolor && Color.opacity(trace.fillcolor)) { + hoverPt.color = Color.addOpacity(trace.fillcolor, 1); + } else if(trace.contours.showlines && Color.opacity(trace.line.color)) { + hoverPt.color = Color.addOpacity(trace.line.color, 1); + } + } + }); + } + + return hoverData; +}; + +},{"../../components/color":155,"../heatmap/hover":441}],426:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + calc: _dereq_('./calc'), + plot: _dereq_('./plot').plot, + style: _dereq_('./style'), + colorbar: _dereq_('./colorbar'), + hoverPoints: _dereq_('./hover'), + + moduleType: 'trace', + name: 'contour', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', '2dMap', 'contour', 'showLegend'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"./attributes":412,"./calc":413,"./colorbar":415,"./defaults":421,"./hover":425,"./plot":430,"./style":432}],427:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +module.exports = function handleLabelDefaults(coerce, layout, lineColor, opts) { + if(!opts) opts = {}; + var showLabels = coerce('contours.showlabels'); + if(showLabels) { + var globalFont = layout.font; + Lib.coerceFont(coerce, 'contours.labelfont', { + family: globalFont.family, + size: globalFont.size, + color: lineColor + }); + coerce('contours.labelformat'); + } + + if(opts.hasHover !== false) coerce('zhoverformat'); +}; + +},{"../../lib":285}],428:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Colorscale = _dereq_('../../components/colorscale'); +var endPlus = _dereq_('./end_plus'); + +module.exports = function makeColorMap(trace) { + var contours = trace.contours; + var start = contours.start; + var end = endPlus(contours); + var cs = contours.size || 1; + var nc = Math.floor((end - start) / cs) + 1; + var extra = contours.coloring === 'lines' ? 0 : 1; + var cOpts = Colorscale.extractOpts(trace); + + if(!isFinite(cs)) { + cs = 1; + nc = 1; + } + + var scl = cOpts.reversescale ? + Colorscale.flipScale(cOpts.colorscale) : + cOpts.colorscale; + + var len = scl.length; + var domain = new Array(len); + var range = new Array(len); + + var si, i; + + if(contours.coloring === 'heatmap') { + var zmin0 = cOpts.min; + var zmax0 = cOpts.max; + + for(i = 0; i < len; i++) { + si = scl[i]; + domain[i] = si[0] * (zmax0 - zmin0) + zmin0; + range[i] = si[1]; + } + + // do the contours extend beyond the colorscale? + // if so, extend the colorscale with constants + var zRange = d3.extent([ + zmin0, + zmax0, + contours.start, + contours.start + cs * (nc - 1) + ]); + var zmin = zRange[zmin0 < zmax0 ? 0 : 1]; + var zmax = zRange[zmin0 < zmax0 ? 1 : 0]; + + if(zmin !== zmin0) { + domain.splice(0, 0, zmin); + range.splice(0, 0, range[0]); + } + + if(zmax !== zmax0) { + domain.push(zmax); + range.push(range[range.length - 1]); + } + } else { + for(i = 0; i < len; i++) { + si = scl[i]; + domain[i] = (si[0] * (nc + extra - 1) - (extra / 2)) * cs + start; + range[i] = si[1]; + } + } + + return Colorscale.makeColorScaleFunc( + {domain: domain, range: range}, + {noNumericCheck: true} + ); +}; + +},{"../../components/colorscale":167,"./end_plus":423,"@plotly/d3":20}],429:[function(_dereq_,module,exports){ +'use strict'; + +var constants = _dereq_('./constants'); + +// Calculate all the marching indices, for ALL levels at once. +// since we want to be exhaustive we'll check for contour crossings +// at every intersection, rather than just following a path +// TODO: shorten the inner loop to only the relevant levels +module.exports = function makeCrossings(pathinfo) { + var z = pathinfo[0].z; + var m = z.length; + var n = z[0].length; // we already made sure z isn't ragged in interp2d + var twoWide = m === 2 || n === 2; + var xi; + var yi; + var startIndices; + var ystartIndices; + var label; + var corners; + var mi; + var pi; + var i; + + for(yi = 0; yi < m - 1; yi++) { + ystartIndices = []; + if(yi === 0) ystartIndices = ystartIndices.concat(constants.BOTTOMSTART); + if(yi === m - 2) ystartIndices = ystartIndices.concat(constants.TOPSTART); + + for(xi = 0; xi < n - 1; xi++) { + startIndices = ystartIndices.slice(); + if(xi === 0) startIndices = startIndices.concat(constants.LEFTSTART); + if(xi === n - 2) startIndices = startIndices.concat(constants.RIGHTSTART); + + label = xi + ',' + yi; + corners = [[z[yi][xi], z[yi][xi + 1]], + [z[yi + 1][xi], z[yi + 1][xi + 1]]]; + for(i = 0; i < pathinfo.length; i++) { + pi = pathinfo[i]; + mi = getMarchingIndex(pi.level, corners); + if(!mi) continue; + + pi.crossings[label] = mi; + if(startIndices.indexOf(mi) !== -1) { + pi.starts.push([xi, yi]); + if(twoWide && startIndices.indexOf(mi, + startIndices.indexOf(mi) + 1) !== -1) { + // the same square has starts from opposite sides + // it's not possible to have starts on opposite edges + // of a corner, only a start and an end... + // but if the array is only two points wide (either way) + // you can have starts on opposite sides. + pi.starts.push([xi, yi]); + } + } + } + } + } +}; + +// modified marching squares algorithm, +// so we disambiguate the saddle points from the start +// and we ignore the cases with no crossings +// the index I'm using is based on: +// http://en.wikipedia.org/wiki/Marching_squares +// except that the saddles bifurcate and I represent them +// as the decimal combination of the two appropriate +// non-saddle indices +function getMarchingIndex(val, corners) { + var mi = (corners[0][0] > val ? 0 : 1) + + (corners[0][1] > val ? 0 : 2) + + (corners[1][1] > val ? 0 : 4) + + (corners[1][0] > val ? 0 : 8); + if(mi === 5 || mi === 10) { + var avg = (corners[0][0] + corners[0][1] + + corners[1][0] + corners[1][1]) / 4; + // two peaks with a big valley + if(val > avg) return (mi === 5) ? 713 : 1114; + // two valleys with a big ridge + return (mi === 5) ? 104 : 208; + } + return (mi === 15) ? 0 : mi; +} + +},{"./constants":416}],430:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); +var Colorscale = _dereq_('../../components/colorscale'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var setConvert = _dereq_('../../plots/cartesian/set_convert'); + +var heatmapPlot = _dereq_('../heatmap/plot'); +var makeCrossings = _dereq_('./make_crossings'); +var findAllPaths = _dereq_('./find_all_paths'); +var emptyPathinfo = _dereq_('./empty_pathinfo'); +var convertToConstraints = _dereq_('./convert_to_constraints'); +var closeBoundaries = _dereq_('./close_boundaries'); +var constants = _dereq_('./constants'); +var costConstants = constants.LABELOPTIMIZER; + +exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + Lib.makeTraceGroups(contourLayer, cdcontours, 'contour').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + var x = cd0.x; + var y = cd0.y; + var contours = trace.contours; + var pathinfo = emptyPathinfo(contours, plotinfo, cd0); + + // use a heatmap to fill - draw it behind the lines + var heatmapColoringLayer = Lib.ensureSingle(plotGroup, 'g', 'heatmapcoloring'); + var cdheatmaps = []; + if(contours.coloring === 'heatmap') { + cdheatmaps = [cd]; + } + heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer); + + makeCrossings(pathinfo); + findAllPaths(pathinfo); + + var leftedge = xa.c2p(x[0], true); + var rightedge = xa.c2p(x[x.length - 1], true); + var bottomedge = ya.c2p(y[0], true); + var topedge = ya.c2p(y[y.length - 1], true); + var perimeter = [ + [leftedge, topedge], + [rightedge, topedge], + [rightedge, bottomedge], + [leftedge, bottomedge] + ]; + + var fillPathinfo = pathinfo; + if(contours.type === 'constraint') { + // N.B. this also mutates pathinfo + fillPathinfo = convertToConstraints(pathinfo, contours._operation); + } + + // draw everything + makeBackground(plotGroup, perimeter, contours); + makeFills(plotGroup, fillPathinfo, perimeter, contours); + makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours); + clipGaps(plotGroup, plotinfo, gd, cd0, perimeter); + }); +}; + +function makeBackground(plotgroup, perimeter, contours) { + var bggroup = Lib.ensureSingle(plotgroup, 'g', 'contourbg'); + + var bgfill = bggroup.selectAll('path') + .data(contours.coloring === 'fill' ? [0] : []); + bgfill.enter().append('path'); + bgfill.exit().remove(); + bgfill + .attr('d', 'M' + perimeter.join('L') + 'Z') + .style('stroke', 'none'); +} + +function makeFills(plotgroup, pathinfo, perimeter, contours) { + var hasFills = contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '='); + var boundaryPath = 'M' + perimeter.join('L') + 'Z'; + + // fills prefixBoundary in pathinfo items + if(hasFills) { + closeBoundaries(pathinfo, contours); + } + + var fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill'); + + var fillitems = fillgroup.selectAll('path').data(hasFills ? pathinfo : []); + fillitems.enter().append('path'); + fillitems.exit().remove(); + fillitems.each(function(pi) { + // join all paths for this level together into a single path + // first follow clockwise around the perimeter to close any open paths + // if the whole perimeter is above this level, start with a path + // enclosing the whole thing. With all that, the parity should mean + // that we always fill everything above the contour, nothing below + var fullpath = (pi.prefixBoundary ? boundaryPath : '') + + joinAllPaths(pi, perimeter); + + if(!fullpath) { + d3.select(this).remove(); + } else { + d3.select(this) + .attr('d', fullpath) + .style('stroke', 'none'); + } + }); +} + +function joinAllPaths(pi, perimeter) { + var fullpath = ''; + var i = 0; + var startsleft = pi.edgepaths.map(function(v, i) { return i; }); + var newloop = true; + var endpt; + var newendpt; + var cnt; + var nexti; + var possiblei; + var addpath; + + function istop(pt) { return Math.abs(pt[1] - perimeter[0][1]) < 0.01; } + function isbottom(pt) { return Math.abs(pt[1] - perimeter[2][1]) < 0.01; } + function isleft(pt) { return Math.abs(pt[0] - perimeter[0][0]) < 0.01; } + function isright(pt) { return Math.abs(pt[0] - perimeter[2][0]) < 0.01; } + + while(startsleft.length) { + addpath = Drawing.smoothopen(pi.edgepaths[i], pi.smoothing); + fullpath += newloop ? addpath : addpath.replace(/^M/, 'L'); + startsleft.splice(startsleft.indexOf(i), 1); + endpt = pi.edgepaths[i][pi.edgepaths[i].length - 1]; + nexti = -1; + + // now loop through sides, moving our endpoint until we find a new start + for(cnt = 0; cnt < 4; cnt++) { // just to prevent infinite loops + if(!endpt) { + Lib.log('Missing end?', i, pi); + break; + } + + if(istop(endpt) && !isright(endpt)) newendpt = perimeter[1]; // right top + else if(isleft(endpt)) newendpt = perimeter[0]; // left top + else if(isbottom(endpt)) newendpt = perimeter[3]; // right bottom + else if(isright(endpt)) newendpt = perimeter[2]; // left bottom + + for(possiblei = 0; possiblei < pi.edgepaths.length; possiblei++) { + var ptNew = pi.edgepaths[possiblei][0]; + // is ptNew on the (horz. or vert.) segment from endpt to newendpt? + if(Math.abs(endpt[0] - newendpt[0]) < 0.01) { + if(Math.abs(endpt[0] - ptNew[0]) < 0.01 && + (ptNew[1] - endpt[1]) * (newendpt[1] - ptNew[1]) >= 0) { + newendpt = ptNew; + nexti = possiblei; + } + } else if(Math.abs(endpt[1] - newendpt[1]) < 0.01) { + if(Math.abs(endpt[1] - ptNew[1]) < 0.01 && + (ptNew[0] - endpt[0]) * (newendpt[0] - ptNew[0]) >= 0) { + newendpt = ptNew; + nexti = possiblei; + } + } else { + Lib.log('endpt to newendpt is not vert. or horz.', + endpt, newendpt, ptNew); + } + } + + endpt = newendpt; + + if(nexti >= 0) break; + fullpath += 'L' + newendpt; + } + + if(nexti === pi.edgepaths.length) { + Lib.log('unclosed perimeter path'); + break; + } + + i = nexti; + + // if we closed back on a loop we already included, + // close it and start a new loop + newloop = (startsleft.indexOf(i) === -1); + if(newloop) { + i = startsleft[0]; + fullpath += 'Z'; + } + } + + // finally add the interior paths + for(i = 0; i < pi.paths.length; i++) { + fullpath += Drawing.smoothclosed(pi.paths[i], pi.smoothing); + } + + return fullpath; +} + +function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours) { + var lineContainer = Lib.ensureSingle(plotgroup, 'g', 'contourlines'); + var showLines = contours.showlines !== false; + var showLabels = contours.showlabels; + var clipLinesForLabels = showLines && showLabels; + + // Even if we're not going to show lines, we need to create them + // if we're showing labels, because the fill paths include the perimeter + // so can't be used to position the labels correctly. + // In this case we'll remove the lines after making the labels. + var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo); + + var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid); + + var labelGroup = plotgroup.selectAll('g.contourlabels') + .data(showLabels ? [0] : []); + + labelGroup.exit().remove(); + + labelGroup.enter().append('g') + .classed('contourlabels', true); + + if(showLabels) { + var labelClipPathData = []; + var labelData = []; + + // invalidate the getTextLocation cache in case paths changed + Lib.clearLocationCache(); + + var contourFormat = exports.labelFormatter(gd, cd0); + + var dummyText = Drawing.tester.append('text') + .attr('data-notex', 1) + .call(Drawing.font, contours.labelfont); + + var xa = pathinfo[0].xaxis; + var ya = pathinfo[0].yaxis; + var xLen = xa._length; + var yLen = ya._length; + var xRng = xa.range; + var yRng = ya.range; + var xMin = Lib.aggNums(Math.min, null, cd0.x); + var xMax = Lib.aggNums(Math.max, null, cd0.x); + var yMin = Lib.aggNums(Math.min, null, cd0.y); + var yMax = Lib.aggNums(Math.max, null, cd0.y); + var x0 = Math.max(xa.c2p(xMin, true), 0); + var x1 = Math.min(xa.c2p(xMax, true), xLen); + var y0 = Math.max(ya.c2p(yMax, true), 0); + var y1 = Math.min(ya.c2p(yMin, true), yLen); + + // visible bounds of the contour trace (and the midpoints, to + // help with cost calculations) + var bounds = {}; + + if(xRng[0] < xRng[1]) { + bounds.left = x0; + bounds.right = x1; + } else { + bounds.left = x1; + bounds.right = x0; + } + + if(yRng[0] < yRng[1]) { + bounds.top = y0; + bounds.bottom = y1; + } else { + bounds.top = y1; + bounds.bottom = y0; + } + + bounds.middle = (bounds.top + bounds.bottom) / 2; + bounds.center = (bounds.left + bounds.right) / 2; + + labelClipPathData.push([ + [bounds.left, bounds.top], + [bounds.right, bounds.top], + [bounds.right, bounds.bottom], + [bounds.left, bounds.bottom] + ]); + + var plotDiagonal = Math.sqrt(xLen * xLen + yLen * yLen); + + // the path length to use to scale the number of labels to draw: + var normLength = constants.LABELDISTANCE * plotDiagonal / + Math.max(1, pathinfo.length / constants.LABELINCREASE); + + linegroup.each(function(d) { + var textOpts = exports.calcTextOpts(d.level, contourFormat, dummyText, gd); + + d3.select(this).selectAll('path').each(function() { + var path = this; + var pathBounds = Lib.getVisibleSegment(path, bounds, textOpts.height / 2); + if(!pathBounds) return; + + if(pathBounds.len < (textOpts.width + textOpts.height) * constants.LABELMIN) return; + + var maxLabels = Math.min(Math.ceil(pathBounds.len / normLength), + constants.LABELMAX); + + for(var i = 0; i < maxLabels; i++) { + var loc = exports.findBestTextLocation(path, pathBounds, textOpts, + labelData, bounds); + + if(!loc) break; + + exports.addLabelData(loc, textOpts, labelData, labelClipPathData); + } + }); + }); + + dummyText.remove(); + + exports.drawLabels(labelGroup, labelData, gd, lineClip, + clipLinesForLabels ? labelClipPathData : null); + } + + if(showLabels && !showLines) linegroup.remove(); +} + +exports.createLines = function(lineContainer, makeLines, pathinfo) { + var smoothing = pathinfo[0].smoothing; + + var linegroup = lineContainer.selectAll('g.contourlevel') + .data(makeLines ? pathinfo : []); + + linegroup.exit().remove(); + linegroup.enter().append('g') + .classed('contourlevel', true); + + if(makeLines) { + // pedgepaths / ppaths are used by contourcarpet, for the paths transformed from a/b to x/y + // edgepaths / paths are used by contour since it's in x/y from the start + var opencontourlines = linegroup.selectAll('path.openline') + .data(function(d) { return d.pedgepaths || d.edgepaths; }); + + opencontourlines.exit().remove(); + opencontourlines.enter().append('path') + .classed('openline', true); + + opencontourlines + .attr('d', function(d) { + return Drawing.smoothopen(d, smoothing); + }) + .style('stroke-miterlimit', 1) + .style('vector-effect', 'non-scaling-stroke'); + + var closedcontourlines = linegroup.selectAll('path.closedline') + .data(function(d) { return d.ppaths || d.paths; }); + + closedcontourlines.exit().remove(); + closedcontourlines.enter().append('path') + .classed('closedline', true); + + closedcontourlines + .attr('d', function(d) { + return Drawing.smoothclosed(d, smoothing); + }) + .style('stroke-miterlimit', 1) + .style('vector-effect', 'non-scaling-stroke'); + } + + return linegroup; +}; + +exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) { + var clips = gd._fullLayout._clips; + var clipId = clipLinesForLabels ? ('clipline' + uid) : null; + + var lineClip = clips.selectAll('#' + clipId) + .data(clipLinesForLabels ? [0] : []); + lineClip.exit().remove(); + + lineClip.enter().append('clipPath') + .classed('contourlineclip', true) + .attr('id', clipId); + + Drawing.setClipUrl(lineContainer, clipId, gd); + + return lineClip; +}; + +exports.labelFormatter = function(gd, cd0) { + var fullLayout = gd._fullLayout; + var trace = cd0.trace; + var contours = trace.contours; + + var formatAxis = { + type: 'linear', + _id: 'ycontour', + showexponent: 'all', + exponentformat: 'B' + }; + + if(contours.labelformat) { + formatAxis.tickformat = contours.labelformat; + setConvert(formatAxis, fullLayout); + } else { + var cOpts = Colorscale.extractOpts(trace); + if(cOpts && cOpts.colorbar && cOpts.colorbar._axis) { + formatAxis = cOpts.colorbar._axis; + } else { + if(contours.type === 'constraint') { + var value = contours.value; + if(Array.isArray(value)) { + formatAxis.range = [value[0], value[value.length - 1]]; + } else formatAxis.range = [value, value]; + } else { + formatAxis.range = [contours.start, contours.end]; + formatAxis.nticks = (contours.end - contours.start) / contours.size; + } + + if(formatAxis.range[0] === formatAxis.range[1]) { + formatAxis.range[1] += formatAxis.range[0] || 1; + } + if(!formatAxis.nticks) formatAxis.nticks = 1000; + + setConvert(formatAxis, fullLayout); + Axes.prepTicks(formatAxis); + formatAxis._tmin = null; + formatAxis._tmax = null; + } + } + + return function(v) { return Axes.tickText(formatAxis, v).text; }; +}; + +exports.calcTextOpts = function(level, contourFormat, dummyText, gd) { + var text = contourFormat(level); + dummyText.text(text) + .call(svgTextUtils.convertToTspans, gd); + + var el = dummyText.node(); + var bBox = Drawing.bBox(el, true); + + return { + text: text, + width: bBox.width, + height: bBox.height, + fontSize: +(el.style['font-size'].replace('px', '')), + level: level, + dy: (bBox.top + bBox.bottom) / 2 + }; +}; + +exports.findBestTextLocation = function(path, pathBounds, textOpts, labelData, plotBounds) { + var textWidth = textOpts.width; + + var p0, dp, pMax, pMin, loc; + if(pathBounds.isClosed) { + dp = pathBounds.len / costConstants.INITIALSEARCHPOINTS; + p0 = pathBounds.min + dp / 2; + pMax = pathBounds.max; + } else { + dp = (pathBounds.len - textWidth) / (costConstants.INITIALSEARCHPOINTS + 1); + p0 = pathBounds.min + dp + textWidth / 2; + pMax = pathBounds.max - (dp + textWidth) / 2; + } + + var cost = Infinity; + for(var j = 0; j < costConstants.ITERATIONS; j++) { + for(var p = p0; p < pMax; p += dp) { + var newLocation = Lib.getTextLocation(path, pathBounds.total, p, textWidth); + var newCost = locationCost(newLocation, textOpts, labelData, plotBounds); + if(newCost < cost) { + cost = newCost; + loc = newLocation; + pMin = p; + } + } + if(cost > costConstants.MAXCOST * 2) break; + + // subsequent iterations just look half steps away from the + // best we found in the previous iteration + if(j) dp /= 2; + p0 = pMin - dp / 2; + pMax = p0 + dp * 1.5; + } + if(cost <= costConstants.MAXCOST) return loc; +}; + +/* + * locationCost: a cost function for label locations + * composed of three kinds of penalty: + * - for open paths, being close to the end of the path + * - the angle away from horizontal + * - being too close to already placed neighbors + */ +function locationCost(loc, textOpts, labelData, bounds) { + var halfWidth = textOpts.width / 2; + var halfHeight = textOpts.height / 2; + var x = loc.x; + var y = loc.y; + var theta = loc.theta; + var dx = Math.cos(theta) * halfWidth; + var dy = Math.sin(theta) * halfWidth; + + // cost for being near an edge + var normX = ((x > bounds.center) ? (bounds.right - x) : (x - bounds.left)) / + (dx + Math.abs(Math.sin(theta) * halfHeight)); + var normY = ((y > bounds.middle) ? (bounds.bottom - y) : (y - bounds.top)) / + (Math.abs(dy) + Math.cos(theta) * halfHeight); + if(normX < 1 || normY < 1) return Infinity; + var cost = costConstants.EDGECOST * (1 / (normX - 1) + 1 / (normY - 1)); + + // cost for not being horizontal + cost += costConstants.ANGLECOST * theta * theta; + + // cost for being close to other labels + var x1 = x - dx; + var y1 = y - dy; + var x2 = x + dx; + var y2 = y + dy; + for(var i = 0; i < labelData.length; i++) { + var labeli = labelData[i]; + var dxd = Math.cos(labeli.theta) * labeli.width / 2; + var dyd = Math.sin(labeli.theta) * labeli.width / 2; + var dist = Lib.segmentDistance( + x1, y1, + x2, y2, + labeli.x - dxd, labeli.y - dyd, + labeli.x + dxd, labeli.y + dyd + ) * 2 / (textOpts.height + labeli.height); + + var sameLevel = labeli.level === textOpts.level; + var distOffset = sameLevel ? costConstants.SAMELEVELDISTANCE : 1; + + if(dist <= distOffset) return Infinity; + + var distFactor = costConstants.NEIGHBORCOST * + (sameLevel ? costConstants.SAMELEVELFACTOR : 1); + + cost += distFactor / (dist - distOffset); + } + + return cost; +} + +exports.addLabelData = function(loc, textOpts, labelData, labelClipPathData) { + var fontSize = textOpts.fontSize; + var w = textOpts.width + fontSize / 3; + var h = Math.max(0, textOpts.height - fontSize / 3); + + var x = loc.x; + var y = loc.y; + var theta = loc.theta; + + var sin = Math.sin(theta); + var cos = Math.cos(theta); + + var rotateXY = function(dx, dy) { + return [ + x + dx * cos - dy * sin, + y + dx * sin + dy * cos + ]; + }; + + var bBoxPts = [ + rotateXY(-w / 2, -h / 2), + rotateXY(-w / 2, h / 2), + rotateXY(w / 2, h / 2), + rotateXY(w / 2, -h / 2) + ]; + + labelData.push({ + text: textOpts.text, + x: x, + y: y, + dy: textOpts.dy, + theta: theta, + level: textOpts.level, + width: w, + height: h + }); + + labelClipPathData.push(bBoxPts); +}; + +exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPathData) { + var labels = labelGroup.selectAll('text') + .data(labelData, function(d) { + return d.text + ',' + d.x + ',' + d.y + ',' + d.theta; + }); + + labels.exit().remove(); + + labels.enter().append('text') + .attr({ + 'data-notex': 1, + 'text-anchor': 'middle' + }) + .each(function(d) { + var x = d.x + Math.sin(d.theta) * d.dy; + var y = d.y - Math.cos(d.theta) * d.dy; + d3.select(this) + .text(d.text) + .attr({ + x: x, + y: y, + transform: 'rotate(' + (180 * d.theta / Math.PI) + ' ' + x + ' ' + y + ')' + }) + .call(svgTextUtils.convertToTspans, gd); + }); + + if(labelClipPathData) { + var clipPath = ''; + for(var i = 0; i < labelClipPathData.length; i++) { + clipPath += 'M' + labelClipPathData[i].join('L') + 'Z'; + } + + var lineClipPath = Lib.ensureSingle(lineClip, 'path', ''); + lineClipPath.attr('d', clipPath); + } +}; + +function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) { + var trace = cd0.trace; + var clips = gd._fullLayout._clips; + var clipId = 'clip' + trace.uid; + + var clipPath = clips.selectAll('#' + clipId) + .data(trace.connectgaps ? [] : [0]); + clipPath.enter().append('clipPath') + .classed('contourclip', true) + .attr('id', clipId); + clipPath.exit().remove(); + + if(trace.connectgaps === false) { + var clipPathInfo = { + // fraction of the way from missing to present point + // to draw the boundary. + // if you make this 1 (or 1-epsilon) then a point in + // a sea of missing data will disappear entirely. + level: 0.9, + crossings: {}, + starts: [], + edgepaths: [], + paths: [], + xaxis: plotinfo.xaxis, + yaxis: plotinfo.yaxis, + x: cd0.x, + y: cd0.y, + // 0 = no data, 1 = data + z: makeClipMask(cd0), + smoothing: 0 + }; + + makeCrossings([clipPathInfo]); + findAllPaths([clipPathInfo]); + closeBoundaries([clipPathInfo], {type: 'levels'}); + + var path = Lib.ensureSingle(clipPath, 'path', ''); + path.attr('d', + (clipPathInfo.prefixBoundary ? 'M' + perimeter.join('L') + 'Z' : '') + + joinAllPaths(clipPathInfo, perimeter) + ); + } else clipId = null; + + Drawing.setClipUrl(plotGroup, clipId, gd); +} + +function makeClipMask(cd0) { + var empties = cd0.trace._emptypoints; + var z = []; + var m = cd0.z.length; + var n = cd0.z[0].length; + var i; + var row = []; + var emptyPoint; + + for(i = 0; i < n; i++) row.push(1); + for(i = 0; i < m; i++) z.push(row.slice()); + for(i = 0; i < empties.length; i++) { + emptyPoint = empties[i]; + z[emptyPoint[0]][emptyPoint[1]] = 0; + } + // save this mask to determine whether to show this data in hover + cd0.zmask = z; + return z; +} + +},{"../../components/colorscale":167,"../../components/drawing":177,"../../lib":285,"../../lib/svg_text_utils":307,"../../plots/cartesian/axes":331,"../../plots/cartesian/set_convert":352,"../heatmap/plot":445,"./close_boundaries":414,"./constants":416,"./convert_to_constraints":420,"./empty_pathinfo":422,"./find_all_paths":424,"./make_crossings":429,"@plotly/d3":20}],431:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); + +module.exports = function setContours(trace, vals) { + var contours = trace.contours; + + // check if we need to auto-choose contour levels + if(trace.autocontour) { + // N.B. do not try to use coloraxis cmin/cmax, + // these values here are meant to remain "per-trace" for now + var zmin = trace.zmin; + var zmax = trace.zmax; + if(trace.zauto || zmin === undefined) { + zmin = Lib.aggNums(Math.min, null, vals); + } + if(trace.zauto || zmax === undefined) { + zmax = Lib.aggNums(Math.max, null, vals); + } + + var dummyAx = autoContours(zmin, zmax, trace.ncontours); + contours.size = dummyAx.dtick; + contours.start = Axes.tickFirst(dummyAx); + dummyAx.range.reverse(); + contours.end = Axes.tickFirst(dummyAx); + + if(contours.start === zmin) contours.start += contours.size; + if(contours.end === zmax) contours.end -= contours.size; + + // if you set a small ncontours, *and* the ends are exactly on zmin/zmax + // there's an edge case where start > end now. Make sure there's at least + // one meaningful contour, put it midway between the crossed values + if(contours.start > contours.end) { + contours.start = contours.end = (contours.start + contours.end) / 2; + } + + // copy auto-contour info back to the source data. + // previously we copied the whole contours object back, but that had + // other info (coloring, showlines) that should be left to supplyDefaults + if(!trace._input.contours) trace._input.contours = {}; + Lib.extendFlat(trace._input.contours, { + start: contours.start, + end: contours.end, + size: contours.size + }); + trace._input.autocontour = true; + } else if(contours.type !== 'constraint') { + // sanity checks on manually-supplied start/end/size + var start = contours.start; + var end = contours.end; + var inputContours = trace._input.contours; + + if(start > end) { + contours.start = inputContours.start = end; + end = contours.end = inputContours.end = start; + start = contours.start; + } + + if(!(contours.size > 0)) { + var sizeOut; + if(start === end) sizeOut = 1; + else sizeOut = autoContours(start, end, trace.ncontours).dtick; + + inputContours.size = contours.size = sizeOut; + } + } +}; + + +/* + * autoContours: make a dummy axis object with dtick we can use + * as contours.size, and if needed we can use Axes.tickFirst + * with this axis object to calculate the start and end too + * + * start: the value to start the contours at + * end: the value to end at (must be > start) + * ncontours: max number of contours to make, like roughDTick + * + * returns: an axis object + */ +function autoContours(start, end, ncontours) { + var dummyAx = { + type: 'linear', + range: [start, end] + }; + + Axes.autoTicks( + dummyAx, + (end - start) / (ncontours || 15) + ); + + return dummyAx; +} + +},{"../../lib":285,"../../plots/cartesian/axes":331}],432:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Drawing = _dereq_('../../components/drawing'); +var heatmapStyle = _dereq_('../heatmap/style'); + +var makeColorMap = _dereq_('./make_color_map'); + + +module.exports = function style(gd) { + var contours = d3.select(gd).selectAll('g.contour'); + + contours.style('opacity', function(d) { + return d[0].trace.opacity; + }); + + contours.each(function(d) { + var c = d3.select(this); + var trace = d[0].trace; + var contours = trace.contours; + var line = trace.line; + var cs = contours.size || 1; + var start = contours.start; + + // for contourcarpet only - is this a constraint-type contour trace? + var isConstraintType = contours.type === 'constraint'; + var colorLines = !isConstraintType && contours.coloring === 'lines'; + var colorFills = !isConstraintType && contours.coloring === 'fill'; + + var colorMap = (colorLines || colorFills) ? makeColorMap(trace) : null; + + c.selectAll('g.contourlevel').each(function(d) { + d3.select(this).selectAll('path') + .call(Drawing.lineGroupStyle, + line.width, + colorLines ? colorMap(d.level) : line.color, + line.dash); + }); + + var labelFont = contours.labelfont; + c.selectAll('g.contourlabels text').each(function(d) { + Drawing.font(d3.select(this), { + family: labelFont.family, + size: labelFont.size, + color: labelFont.color || (colorLines ? colorMap(d.level) : line.color) + }); + }); + + if(isConstraintType) { + c.selectAll('g.contourfill path') + .style('fill', trace.fillcolor); + } else if(colorFills) { + var firstFill; + + c.selectAll('g.contourfill path') + .style('fill', function(d) { + if(firstFill === undefined) firstFill = d.level; + return colorMap(d.level + 0.5 * cs); + }); + + if(firstFill === undefined) firstFill = start; + + c.selectAll('g.contourbg path') + .style('fill', colorMap(firstFill - 0.5 * cs)); + } + }); + + heatmapStyle(gd); +}; + +},{"../../components/drawing":177,"../heatmap/style":446,"./make_color_map":428,"@plotly/d3":20}],433:[function(_dereq_,module,exports){ +'use strict'; + +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); +var handleLabelDefaults = _dereq_('./label_defaults'); + + +module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, opts) { + var coloring = coerce('contours.coloring'); + + var showLines; + var lineColor = ''; + if(coloring === 'fill') showLines = coerce('contours.showlines'); + + if(showLines !== false) { + if(coloring !== 'lines') lineColor = coerce('line.color', '#000'); + coerce('line.width', 0.5); + coerce('line.dash'); + } + + if(coloring !== 'none') { + // plots/plots always coerces showlegend to true, but in this case + // we default to false and (by default) show a colorbar instead + if(traceIn.showlegend !== true) traceOut.showlegend = false; + traceOut._dfltShowLegend = false; + + colorscaleDefaults( + traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'} + ); + } + + coerce('line.smoothing'); + + handleLabelDefaults(coerce, layout, lineColor, opts); +}; + +},{"../../components/colorscale/defaults":165,"./label_defaults":427}],434:[function(_dereq_,module,exports){ +'use strict'; + +var scatterAttrs = _dereq_('../scatter/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = extendFlat({ + z: { + valType: 'data_array', + editType: 'calc', + }, + x: extendFlat({}, scatterAttrs.x, {impliedEdits: {xtype: 'array'}}), + x0: extendFlat({}, scatterAttrs.x0, {impliedEdits: {xtype: 'scaled'}}), + dx: extendFlat({}, scatterAttrs.dx, {impliedEdits: {xtype: 'scaled'}}), + y: extendFlat({}, scatterAttrs.y, {impliedEdits: {ytype: 'array'}}), + y0: extendFlat({}, scatterAttrs.y0, {impliedEdits: {ytype: 'scaled'}}), + dy: extendFlat({}, scatterAttrs.dy, {impliedEdits: {ytype: 'scaled'}}), + + xperiod: extendFlat({}, scatterAttrs.xperiod, {impliedEdits: {xtype: 'scaled'}}), + yperiod: extendFlat({}, scatterAttrs.yperiod, {impliedEdits: {ytype: 'scaled'}}), + xperiod0: extendFlat({}, scatterAttrs.xperiod0, {impliedEdits: {xtype: 'scaled'}}), + yperiod0: extendFlat({}, scatterAttrs.yperiod0, {impliedEdits: {ytype: 'scaled'}}), + xperiodalignment: extendFlat({}, scatterAttrs.xperiodalignment, {impliedEdits: {xtype: 'scaled'}}), + yperiodalignment: extendFlat({}, scatterAttrs.yperiodalignment, {impliedEdits: {ytype: 'scaled'}}), + + text: { + valType: 'data_array', + editType: 'calc', + }, + hovertext: { + valType: 'data_array', + editType: 'calc', + }, + transpose: { + valType: 'boolean', + dflt: false, + editType: 'calc', + }, + xtype: { + valType: 'enumerated', + values: ['array', 'scaled'], + editType: 'calc+clearAxisTypes', + }, + ytype: { + valType: 'enumerated', + values: ['array', 'scaled'], + editType: 'calc+clearAxisTypes', + }, + zsmooth: { + valType: 'enumerated', + values: ['fast', 'best', false], + dflt: false, + editType: 'calc', + }, + hoverongaps: { + valType: 'boolean', + dflt: true, + editType: 'none', + }, + connectgaps: { + valType: 'boolean', + editType: 'calc', + }, + xgap: { + valType: 'number', + dflt: 0, + min: 0, + editType: 'plot', + }, + ygap: { + valType: 'number', + dflt: 0, + min: 0, + editType: 'plot', + }, + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + zhoverformat: axisHoverFormat('z', 1), + + hovertemplate: hovertemplateAttrs(), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) +}, { + transforms: undefined +}, + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) +); + +},{"../../components/colorscale/attributes":162,"../../lib/extend":279,"../../plots/attributes":327,"../../plots/cartesian/axis_format_attributes":334,"../../plots/template_attributes":368,"../scatter/attributes":494}],435:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var alignPeriod = _dereq_('../../plots/cartesian/align_period'); + +var histogram2dCalc = _dereq_('../histogram2d/calc'); +var colorscaleCalc = _dereq_('../../components/colorscale/calc'); +var convertColumnData = _dereq_('./convert_column_xyz'); +var clean2dArray = _dereq_('./clean_2d_array'); +var interp2d = _dereq_('./interp2d'); +var findEmpties = _dereq_('./find_empties'); +var makeBoundArray = _dereq_('./make_bound_array'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +module.exports = function calc(gd, trace) { + // prepare the raw data + // run makeCalcdata on x and y even for heatmaps, in case of category mappings + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var isContour = Registry.traceIs(trace, 'contour'); + var isHist = Registry.traceIs(trace, 'histogram'); + var isGL2D = Registry.traceIs(trace, 'gl2d'); + var zsmooth = isContour ? 'best' : trace.zsmooth; + var x, x0, dx, origX; + var y, y0, dy, origY; + var z, i, binned; + + // cancel minimum tick spacings (only applies to bars and boxes) + xa._minDtick = 0; + ya._minDtick = 0; + + if(isHist) { + binned = histogram2dCalc(gd, trace); + origX = binned.orig_x; + x = binned.x; + x0 = binned.x0; + dx = binned.dx; + + origY = binned.orig_y; + y = binned.y; + y0 = binned.y0; + dy = binned.dy; + + z = binned.z; + } else { + var zIn = trace.z; + if(Lib.isArray1D(zIn)) { + convertColumnData(trace, xa, ya, 'x', 'y', ['z']); + x = trace._x; + y = trace._y; + zIn = trace._z; + } else { + origX = trace.x ? xa.makeCalcdata(trace, 'x') : []; + origY = trace.y ? ya.makeCalcdata(trace, 'y') : []; + x = alignPeriod(trace, xa, 'x', origX); + y = alignPeriod(trace, ya, 'y', origY); + trace._x = x; + trace._y = y; + } + + x0 = trace.x0; + dx = trace.dx; + y0 = trace.y0; + dy = trace.dy; + + z = clean2dArray(zIn, trace, xa, ya); + } + + if(xa.rangebreaks || ya.rangebreaks) { + z = dropZonBreaks(x, y, z); + + if(!isHist) { + x = skipBreaks(x); + y = skipBreaks(y); + + trace._x = x; + trace._y = y; + } + } + + if(!isHist && (isContour || trace.connectgaps)) { + trace._emptypoints = findEmpties(z); + interp2d(z, trace._emptypoints); + } + + function noZsmooth(msg) { + zsmooth = trace._input.zsmooth = trace.zsmooth = false; + Lib.warn('cannot use zsmooth: "fast": ' + msg); + } + + // check whether we really can smooth (ie all boxes are about the same size) + if(zsmooth === 'fast') { + if(xa.type === 'log' || ya.type === 'log') { + noZsmooth('log axis found'); + } else if(!isHist) { + if(x.length) { + var avgdx = (x[x.length - 1] - x[0]) / (x.length - 1); + var maxErrX = Math.abs(avgdx / 100); + for(i = 0; i < x.length - 1; i++) { + if(Math.abs(x[i + 1] - x[i] - avgdx) > maxErrX) { + noZsmooth('x scale is not linear'); + break; + } + } + } + if(y.length && zsmooth === 'fast') { + var avgdy = (y[y.length - 1] - y[0]) / (y.length - 1); + var maxErrY = Math.abs(avgdy / 100); + for(i = 0; i < y.length - 1; i++) { + if(Math.abs(y[i + 1] - y[i] - avgdy) > maxErrY) { + noZsmooth('y scale is not linear'); + break; + } + } + } + } + } + + // create arrays of brick boundaries, to be used by autorange and heatmap.plot + var xlen = Lib.maxRowLength(z); + var xIn = trace.xtype === 'scaled' ? '' : x; + var xArray = makeBoundArray(trace, xIn, x0, dx, xlen, xa); + var yIn = trace.ytype === 'scaled' ? '' : y; + var yArray = makeBoundArray(trace, yIn, y0, dy, z.length, ya); + + // handled in gl2d convert step + if(!isGL2D) { + trace._extremes[xa._id] = Axes.findExtremes(xa, xArray); + trace._extremes[ya._id] = Axes.findExtremes(ya, yArray); + } + + var cd0 = { + x: xArray, + y: yArray, + z: z, + text: trace._text || trace.text, + hovertext: trace._hovertext || trace.hovertext + }; + + if(trace.xperiodalignment && origX) { + cd0.orig_x = origX; + } + if(trace.yperiodalignment && origY) { + cd0.orig_y = origY; + } + + if(xIn && xIn.length === xArray.length - 1) cd0.xCenter = xIn; + if(yIn && yIn.length === yArray.length - 1) cd0.yCenter = yIn; + + if(isHist) { + cd0.xRanges = binned.xRanges; + cd0.yRanges = binned.yRanges; + cd0.pts = binned.pts; + } + + if(!isContour) { + colorscaleCalc(gd, trace, {vals: z, cLetter: 'z'}); + } + + if(isContour && trace.contours && trace.contours.coloring === 'heatmap') { + var dummyTrace = { + type: trace.type === 'contour' ? 'heatmap' : 'histogram2d', + xcalendar: trace.xcalendar, + ycalendar: trace.ycalendar + }; + cd0.xfill = makeBoundArray(dummyTrace, xIn, x0, dx, xlen, xa); + cd0.yfill = makeBoundArray(dummyTrace, yIn, y0, dy, z.length, ya); + } + + return [cd0]; +}; + +function skipBreaks(a) { + var b = []; + var len = a.length; + for(var i = 0; i < len; i++) { + var v = a[i]; + if(v !== BADNUM) b.push(v); + } + return b; +} + +function dropZonBreaks(x, y, z) { + var newZ = []; + var k = -1; + for(var i = 0; i < z.length; i++) { + if(y[i] === BADNUM) continue; + k++; + newZ[k] = []; + for(var j = 0; j < z[i].length; j++) { + if(x[j] === BADNUM) continue; + + newZ[k].push(z[i][j]); + } + } + return newZ; +} + +},{"../../components/colorscale/calc":163,"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/align_period":328,"../../plots/cartesian/axes":331,"../../registry":373,"../histogram2d/calc":463,"./clean_2d_array":436,"./convert_column_xyz":438,"./find_empties":440,"./interp2d":443,"./make_bound_array":444}],436:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +module.exports = function clean2dArray(zOld, trace, xa, ya) { + var rowlen, collen, getCollen, old2new, i, j; + + function cleanZvalue(v) { + if(!isNumeric(v)) return undefined; + return +v; + } + + if(trace && trace.transpose) { + rowlen = 0; + for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length); + if(rowlen === 0) return false; + getCollen = function(zOld) { return zOld.length; }; + old2new = function(zOld, i, j) { return (zOld[j] || [])[i]; }; + } else { + rowlen = zOld.length; + getCollen = function(zOld, i) { return zOld[i].length; }; + old2new = function(zOld, i, j) { return (zOld[i] || [])[j]; }; + } + + var padOld2new = function(zOld, i, j) { + if(i === BADNUM || j === BADNUM) return BADNUM; + return old2new(zOld, i, j); + }; + + function axisMapping(ax) { + if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' && + ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) { + var axLetter = ax._id.charAt(0); + var axMapping = {}; + var traceCategories = trace['_' + axLetter + 'CategoryMap'] || trace[axLetter]; + for(i = 0; i < traceCategories.length; i++) { + axMapping[traceCategories[i]] = i; + } + return function(i) { + var ind = axMapping[ax._categories[i]]; + return ind + 1 ? ind : BADNUM; + }; + } else { + return Lib.identity; + } + } + + var xMap = axisMapping(xa); + var yMap = axisMapping(ya); + + if(ya && ya.type === 'category') rowlen = ya._categories.length; + var zNew = new Array(rowlen); + + for(i = 0; i < rowlen; i++) { + if(xa && xa.type === 'category') { + collen = xa._categories.length; + } else { + collen = getCollen(zOld, i); + } + zNew[i] = new Array(collen); + for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(padOld2new(zOld, yMap(i), xMap(j))); + } + + return zNew; +}; + +},{"../../constants/numerical":265,"../../lib":285,"fast-isnumeric":31}],437:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + min: 'zmin', + max: 'zmax' +}; + +},{}],438:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; +var alignPeriod = _dereq_('../../plots/cartesian/align_period'); + +module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, arrayVarNames) { + var colLen = trace._length; + var col1 = ax1.makeCalcdata(trace, var1Name); + var col2 = ax2.makeCalcdata(trace, var2Name); + col1 = alignPeriod(trace, ax1, var1Name, col1); + col2 = alignPeriod(trace, ax2, var2Name, col2); + + var textCol = trace.text; + var hasColumnText = (textCol !== undefined && Lib.isArray1D(textCol)); + var hoverTextCol = trace.hovertext; + var hasColumnHoverText = (hoverTextCol !== undefined && Lib.isArray1D(hoverTextCol)); + var i, j; + + var col1dv = Lib.distinctVals(col1); + var col1vals = col1dv.vals; + var col2dv = Lib.distinctVals(col2); + var col2vals = col2dv.vals; + var newArrays = []; + var text; + var hovertext; + + var nI = col2vals.length; + var nJ = col1vals.length; + + for(i = 0; i < arrayVarNames.length; i++) { + newArrays[i] = Lib.init2dArray(nI, nJ); + } + + if(hasColumnText) { + text = Lib.init2dArray(nI, nJ); + } + if(hasColumnHoverText) { + hovertext = Lib.init2dArray(nI, nJ); + } + + var after2before = Lib.init2dArray(nI, nJ); + + for(i = 0; i < colLen; i++) { + if(col1[i] !== BADNUM && col2[i] !== BADNUM) { + var i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals); + var i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals); + + for(j = 0; j < arrayVarNames.length; j++) { + var arrayVarName = arrayVarNames[j]; + var arrayVar = trace[arrayVarName]; + var newArray = newArrays[j]; + newArray[i2][i1] = arrayVar[i]; + after2before[i2][i1] = i; + } + + if(hasColumnText) text[i2][i1] = textCol[i]; + if(hasColumnHoverText) hovertext[i2][i1] = hoverTextCol[i]; + } + } + + trace['_' + var1Name] = col1vals; + trace['_' + var2Name] = col2vals; + for(j = 0; j < arrayVarNames.length; j++) { + trace['_' + arrayVarNames[j]] = newArrays[j]; + } + if(hasColumnText) trace._text = text; + if(hasColumnHoverText) trace._hovertext = hovertext; + + if(ax1 && ax1.type === 'category') { + trace['_' + var1Name + 'CategoryMap'] = col1vals.map(function(v) { return ax1._categories[v];}); + } + + if(ax2 && ax2.type === 'category') { + trace['_' + var2Name + 'CategoryMap'] = col2vals.map(function(v) { return ax2._categories[v];}); + } + + trace._after2before = after2before; +}; + +},{"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/align_period":328}],439:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var handleXYZDefaults = _dereq_('./xyz_defaults'); +var handlePeriodDefaults = _dereq_('../scatter/period_defaults'); +var handleStyleDefaults = _dereq_('./style_defaults'); +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var validData = handleXYZDefaults(traceIn, traceOut, coerce, layout); + if(!validData) { + traceOut.visible = false; + return; + } + + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('xhoverformat'); + coerce('yhoverformat'); + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + + handleStyleDefaults(traceIn, traceOut, coerce, layout); + + coerce('hoverongaps'); + coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false)); + + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); +}; + +},{"../../components/colorscale/defaults":165,"../../lib":285,"../scatter/period_defaults":514,"./attributes":434,"./style_defaults":447,"./xyz_defaults":448}],440:[function(_dereq_,module,exports){ +'use strict'; + +var maxRowLength = _dereq_('../../lib').maxRowLength; + +/* Return a list of empty points in 2D array z + * each empty point z[i][j] gives an array [i, j, neighborCount] + * neighborCount is the count of 4 nearest neighbors that DO exist + * this is to give us an order of points to evaluate for interpolation. + * if no neighbors exist, we iteratively look for neighbors that HAVE + * neighbors, and add a fractional neighborCount + */ +module.exports = function findEmpties(z) { + var empties = []; + var neighborHash = {}; + var noNeighborList = []; + var nextRow = z[0]; + var row = []; + var blank = [0, 0, 0]; + var rowLength = maxRowLength(z); + var prevRow; + var i; + var j; + var thisPt; + var p; + var neighborCount; + var newNeighborHash; + var foundNewNeighbors; + + for(i = 0; i < z.length; i++) { + prevRow = row; + row = nextRow; + nextRow = z[i + 1] || []; + for(j = 0; j < rowLength; j++) { + if(row[j] === undefined) { + neighborCount = (row[j - 1] !== undefined ? 1 : 0) + + (row[j + 1] !== undefined ? 1 : 0) + + (prevRow[j] !== undefined ? 1 : 0) + + (nextRow[j] !== undefined ? 1 : 0); + + if(neighborCount) { + // for this purpose, don't count off-the-edge points + // as undefined neighbors + if(i === 0) neighborCount++; + if(j === 0) neighborCount++; + if(i === z.length - 1) neighborCount++; + if(j === row.length - 1) neighborCount++; + + // if all neighbors that could exist do, we don't + // need this for finding farther neighbors + if(neighborCount < 4) { + neighborHash[[i, j]] = [i, j, neighborCount]; + } + + empties.push([i, j, neighborCount]); + } else noNeighborList.push([i, j]); + } + } + } + + while(noNeighborList.length) { + newNeighborHash = {}; + foundNewNeighbors = false; + + // look for cells that now have neighbors but didn't before + for(p = noNeighborList.length - 1; p >= 0; p--) { + thisPt = noNeighborList[p]; + i = thisPt[0]; + j = thisPt[1]; + + neighborCount = ((neighborHash[[i - 1, j]] || blank)[2] + + (neighborHash[[i + 1, j]] || blank)[2] + + (neighborHash[[i, j - 1]] || blank)[2] + + (neighborHash[[i, j + 1]] || blank)[2]) / 20; + + if(neighborCount) { + newNeighborHash[thisPt] = [i, j, neighborCount]; + noNeighborList.splice(p, 1); + foundNewNeighbors = true; + } + } + + if(!foundNewNeighbors) { + throw 'findEmpties iterated with no new neighbors'; + } + + // put these new cells into the main neighbor list + for(thisPt in newNeighborHash) { + neighborHash[thisPt] = newNeighborHash[thisPt]; + empties.push(newNeighborHash[thisPt]); + } + } + + // sort the full list in descending order of neighbor count + return empties.sort(function(a, b) { return b[2] - a[2]; }); +}; + +},{"../../lib":285}],441:[function(_dereq_,module,exports){ +'use strict'; + +var Fx = _dereq_('../../components/fx'); +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var extractOpts = _dereq_('../../components/colorscale').extractOpts; + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) { + if(!opts) opts = {}; + var isContour = opts.isContour; + + var cd0 = pointData.cd[0]; + var trace = cd0.trace; + var xa = pointData.xa; + var ya = pointData.ya; + var x = cd0.x; + var y = cd0.y; + var z = cd0.z; + var xc = cd0.xCenter; + var yc = cd0.yCenter; + var zmask = cd0.zmask; + var zhoverformat = trace.zhoverformat; + var x2 = x; + var y2 = y; + + var xl, yl, nx, ny; + + if(pointData.index !== false) { + try { + nx = Math.round(pointData.index[1]); + ny = Math.round(pointData.index[0]); + } catch(e) { + Lib.error('Error hovering on heatmap, ' + + 'pointNumber must be [row,col], found:', pointData.index); + return; + } + if(nx < 0 || nx >= z[0].length || ny < 0 || ny > z.length) { + return; + } + } else if(Fx.inbox(xval - x[0], xval - x[x.length - 1], 0) > 0 || + Fx.inbox(yval - y[0], yval - y[y.length - 1], 0) > 0) { + return; + } else { + if(isContour) { + var i2; + x2 = [2 * x[0] - x[1]]; + + for(i2 = 1; i2 < x.length; i2++) { + x2.push((x[i2] + x[i2 - 1]) / 2); + } + x2.push([2 * x[x.length - 1] - x[x.length - 2]]); + + y2 = [2 * y[0] - y[1]]; + for(i2 = 1; i2 < y.length; i2++) { + y2.push((y[i2] + y[i2 - 1]) / 2); + } + y2.push([2 * y[y.length - 1] - y[y.length - 2]]); + } + nx = Math.max(0, Math.min(x2.length - 2, Lib.findBin(xval, x2))); + ny = Math.max(0, Math.min(y2.length - 2, Lib.findBin(yval, y2))); + } + + var x0 = xa.c2p(x[nx]); + var x1 = xa.c2p(x[nx + 1]); + var y0 = ya.c2p(y[ny]); + var y1 = ya.c2p(y[ny + 1]); + + var _x, _y; + if(isContour) { + _x = cd0.orig_x || x; + _y = cd0.orig_y || y; + + x1 = x0; + xl = _x[nx]; + y1 = y0; + yl = _y[ny]; + } else { + _x = cd0.orig_x || xc || x; + _y = cd0.orig_y || yc || y; + + xl = xc ? _x[nx] : ((_x[nx] + _x[nx + 1]) / 2); + yl = yc ? _y[ny] : ((_y[ny] + _y[ny + 1]) / 2); + + if(xa && xa.type === 'category') xl = x[nx]; + if(ya && ya.type === 'category') yl = y[ny]; + + if(trace.zsmooth) { + x0 = x1 = xa.c2p(xl); + y0 = y1 = ya.c2p(yl); + } + } + + var zVal = z[ny][nx]; + if(zmask && !zmask[ny][nx]) zVal = undefined; + + if(zVal === undefined && !trace.hoverongaps) return; + + var text; + if(Array.isArray(cd0.hovertext) && Array.isArray(cd0.hovertext[ny])) { + text = cd0.hovertext[ny][nx]; + } else if(Array.isArray(cd0.text) && Array.isArray(cd0.text[ny])) { + text = cd0.text[ny][nx]; + } + + // dummy axis for formatting the z value + var cOpts = extractOpts(trace); + var dummyAx = { + type: 'linear', + range: [cOpts.min, cOpts.max], + hoverformat: zhoverformat, + _separators: xa._separators, + _numFormat: xa._numFormat + }; + var zLabel = Axes.tickText(dummyAx, zVal, 'hover').text; + + return [Lib.extendFlat(pointData, { + index: trace._after2before ? trace._after2before[ny][nx] : [ny, nx], + // never let a 2D override 1D type as closest point + distance: pointData.maxHoverDistance, + spikeDistance: pointData.maxSpikeDistance, + x0: x0, + x1: x1, + y0: y0, + y1: y1, + xLabelVal: xl, + yLabelVal: yl, + zLabelVal: zVal, + zLabel: zLabel, + text: text + })]; +}; + +},{"../../components/colorscale":167,"../../components/fx":195,"../../lib":285,"../../plots/cartesian/axes":331}],442:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + calc: _dereq_('./calc'), + plot: _dereq_('./plot'), + colorbar: _dereq_('./colorbar'), + style: _dereq_('./style'), + hoverPoints: _dereq_('./hover'), + + moduleType: 'trace', + name: 'heatmap', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', '2dMap', 'showLegend'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"./attributes":434,"./calc":435,"./colorbar":437,"./defaults":439,"./hover":441,"./plot":445,"./style":446}],443:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var INTERPTHRESHOLD = 1e-2; +var NEIGHBORSHIFTS = [[-1, 0], [1, 0], [0, -1], [0, 1]]; + +function correctionOvershoot(maxFractionalChange) { + // start with less overshoot, until we know it's converging, + // then ramp up the overshoot for faster convergence + return 0.5 - 0.25 * Math.min(1, maxFractionalChange * 0.5); +} + +/* + * interp2d: Fill in missing data from a 2D array using an iterative + * poisson equation solver with zero-derivative BC at edges. + * Amazingly, this just amounts to repeatedly averaging all the existing + * nearest neighbors, at least if we don't take x/y scaling into account, + * which is the right approach here where x and y may not even have the + * same units. + * + * @param {array of arrays} z + * The 2D array to fill in. Will be mutated here. Assumed to already be + * cleaned, so all entries are numbers except gaps, which are `undefined`. + * @param {array of arrays} emptyPoints + * Each entry [i, j, neighborCount] for empty points z[i][j] and the number + * of neighbors that are *not* missing. Assumed to be sorted from most to + * least neighbors, as produced by heatmap/find_empties. + */ +module.exports = function interp2d(z, emptyPoints) { + var maxFractionalChange = 1; + var i; + + // one pass to fill in a starting value for all the empties + iterateInterp2d(z, emptyPoints); + + // we're don't need to iterate lone empties - remove them + for(i = 0; i < emptyPoints.length; i++) { + if(emptyPoints[i][2] < 4) break; + } + // but don't remove these points from the original array, + // we'll use them for masking, so make a copy. + emptyPoints = emptyPoints.slice(i); + + for(i = 0; i < 100 && maxFractionalChange > INTERPTHRESHOLD; i++) { + maxFractionalChange = iterateInterp2d(z, emptyPoints, + correctionOvershoot(maxFractionalChange)); + } + if(maxFractionalChange > INTERPTHRESHOLD) { + Lib.log('interp2d didn\'t converge quickly', maxFractionalChange); + } + + return z; +}; + +function iterateInterp2d(z, emptyPoints, overshoot) { + var maxFractionalChange = 0; + var thisPt; + var i; + var j; + var p; + var q; + var neighborShift; + var neighborRow; + var neighborVal; + var neighborCount; + var neighborSum; + var initialVal; + var minNeighbor; + var maxNeighbor; + + for(p = 0; p < emptyPoints.length; p++) { + thisPt = emptyPoints[p]; + i = thisPt[0]; + j = thisPt[1]; + initialVal = z[i][j]; + neighborSum = 0; + neighborCount = 0; + + for(q = 0; q < 4; q++) { + neighborShift = NEIGHBORSHIFTS[q]; + neighborRow = z[i + neighborShift[0]]; + if(!neighborRow) continue; + neighborVal = neighborRow[j + neighborShift[1]]; + if(neighborVal !== undefined) { + if(neighborSum === 0) { + minNeighbor = maxNeighbor = neighborVal; + } else { + minNeighbor = Math.min(minNeighbor, neighborVal); + maxNeighbor = Math.max(maxNeighbor, neighborVal); + } + neighborCount++; + neighborSum += neighborVal; + } + } + + if(neighborCount === 0) { + throw 'iterateInterp2d order is wrong: no defined neighbors'; + } + + // this is the laplace equation interpolation: + // each point is just the average of its neighbors + // note that this ignores differential x/y scaling + // which I think is the right approach, since we + // don't know what that scaling means + z[i][j] = neighborSum / neighborCount; + + if(initialVal === undefined) { + if(neighborCount < 4) maxFractionalChange = 1; + } else { + // we can make large empty regions converge faster + // if we overshoot the change vs the previous value + z[i][j] = (1 + overshoot) * z[i][j] - overshoot * initialVal; + + if(maxNeighbor > minNeighbor) { + maxFractionalChange = Math.max(maxFractionalChange, + Math.abs(z[i][j] - initialVal) / (maxNeighbor - minNeighbor)); + } + } + } + + return maxFractionalChange; +} + +},{"../../lib":285}],444:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; + +module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, ax) { + var arrayOut = []; + var isContour = Registry.traceIs(trace, 'contour'); + var isHist = Registry.traceIs(trace, 'histogram'); + var isGL2D = Registry.traceIs(trace, 'gl2d'); + var v0; + var dv; + var i; + + var isArrayOfTwoItemsOrMore = isArrayOrTypedArray(arrayIn) && arrayIn.length > 1; + + if(isArrayOfTwoItemsOrMore && !isHist && (ax.type !== 'category')) { + var len = arrayIn.length; + + // given vals are brick centers + // hopefully length === numbricks, but use this method even if too few are supplied + // and extend it linearly based on the last two points + if(len <= numbricks) { + // contour plots only want the centers + if(isContour || isGL2D) arrayOut = arrayIn.slice(0, numbricks); + else if(numbricks === 1) { + arrayOut = [arrayIn[0] - 0.5, arrayIn[0] + 0.5]; + } else { + arrayOut = [1.5 * arrayIn[0] - 0.5 * arrayIn[1]]; + + for(i = 1; i < len; i++) { + arrayOut.push((arrayIn[i - 1] + arrayIn[i]) * 0.5); + } + + arrayOut.push(1.5 * arrayIn[len - 1] - 0.5 * arrayIn[len - 2]); + } + + if(len < numbricks) { + var lastPt = arrayOut[arrayOut.length - 1]; + var delta = lastPt - arrayOut[arrayOut.length - 2]; + + for(i = len; i < numbricks; i++) { + lastPt += delta; + arrayOut.push(lastPt); + } + } + } else { + // hopefully length === numbricks+1, but do something regardless: + // given vals are brick boundaries + return isContour ? + arrayIn.slice(0, numbricks) : // we must be strict for contours + arrayIn.slice(0, numbricks + 1); + } + } else { + var calendar = trace[ax._id.charAt(0) + 'calendar']; + + if(isHist) { + v0 = ax.r2c(v0In, 0, calendar); + } else { + if(isArrayOrTypedArray(arrayIn) && arrayIn.length === 1) { + v0 = arrayIn[0]; + } else if(v0In === undefined) { + v0 = 0; + } else { + var fn = ax.type === 'log' ? ax.d2c : ax.r2c; + v0 = fn(v0In, 0, calendar); + } + } + + dv = dvIn || 1; + + for(i = (isContour || isGL2D) ? 0 : -0.5; i < numbricks; i++) { + arrayOut.push(v0 + dv * i); + } + } + + return arrayOut; +}; + +},{"../../lib":285,"../../registry":373}],445:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var tinycolor = _dereq_('tinycolor2'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var makeColorScaleFuncFromTrace = _dereq_('../../components/colorscale').makeColorScaleFuncFromTrace; +var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces'); + +module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + Lib.makeTraceGroups(heatmapLayer, cdheatmaps, 'hm').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + + var z = cd0.z; + var x = cd0.x; + var y = cd0.y; + var xc = cd0.xCenter; + var yc = cd0.yCenter; + var isContour = Registry.traceIs(trace, 'contour'); + var zsmooth = isContour ? 'best' : trace.zsmooth; + + // get z dims + var m = z.length; + var n = Lib.maxRowLength(z); + var xrev = false; + var yrev = false; + + var left, right, temp, top, bottom, i; + + // TODO: if there are multiple overlapping categorical heatmaps, + // or if we allow category sorting, then the categories may not be + // sequential... may need to reorder and/or expand z + + // Get edges of png in pixels (xa.c2p() maps axes coordinates to pixel coordinates) + // figure out if either axis is reversed (y is usually reversed, in pixel coords) + // also clip the image to maximum 50% outside the visible plot area + // bigger image lets you pan more naturally, but slows performance. + // TODO: use low-resolution images outside the visible plot for panning + // these while loops find the first and last brick bounds that are defined + // (in case of log of a negative) + i = 0; + while(left === undefined && i < x.length - 1) { + left = xa.c2p(x[i]); + i++; + } + i = x.length - 1; + while(right === undefined && i > 0) { + right = xa.c2p(x[i]); + i--; + } + + if(right < left) { + temp = right; + right = left; + left = temp; + xrev = true; + } + + i = 0; + while(top === undefined && i < y.length - 1) { + top = ya.c2p(y[i]); + i++; + } + i = y.length - 1; + while(bottom === undefined && i > 0) { + bottom = ya.c2p(y[i]); + i--; + } + + if(bottom < top) { + temp = top; + top = bottom; + bottom = temp; + yrev = true; + } + + // for contours with heatmap fill, we generate the boundaries based on + // brick centers but then use the brick edges for drawing the bricks + if(isContour) { + xc = x; + yc = y; + x = cd0.xfill; + y = cd0.yfill; + } + + // make an image that goes at most half a screen off either side, to keep + // time reasonable when you zoom in. if zsmooth is true/fast, don't worry + // about this, because zooming doesn't increase number of pixels + // if zsmooth is best, don't include anything off screen because it takes too long + if(zsmooth !== 'fast') { + var extra = zsmooth === 'best' ? 0 : 0.5; + left = Math.max(-extra * xa._length, left); + right = Math.min((1 + extra) * xa._length, right); + top = Math.max(-extra * ya._length, top); + bottom = Math.min((1 + extra) * ya._length, bottom); + } + + var imageWidth = Math.round(right - left); + var imageHeight = Math.round(bottom - top); + + // setup image nodes + + // if image is entirely off-screen, don't even draw it + var isOffScreen = (imageWidth <= 0 || imageHeight <= 0); + + if(isOffScreen) { + var noImage = plotGroup.selectAll('image').data([]); + noImage.exit().remove(); + return; + } + + // generate image data + + var canvasW, canvasH; + if(zsmooth === 'fast') { + canvasW = n; + canvasH = m; + } else { + canvasW = imageWidth; + canvasH = imageHeight; + } + + var canvas = document.createElement('canvas'); + canvas.width = canvasW; + canvas.height = canvasH; + var context = canvas.getContext('2d'); + + var sclFunc = makeColorScaleFuncFromTrace(trace, {noNumericCheck: true, returnArray: true}); + + // map brick boundaries to image pixels + var xpx, + ypx; + if(zsmooth === 'fast') { + xpx = xrev ? + function(index) { return n - 1 - index; } : + Lib.identity; + ypx = yrev ? + function(index) { return m - 1 - index; } : + Lib.identity; + } else { + xpx = function(index) { + return Lib.constrain(Math.round(xa.c2p(x[index]) - left), + 0, imageWidth); + }; + ypx = function(index) { + return Lib.constrain(Math.round(ya.c2p(y[index]) - top), + 0, imageHeight); + }; + } + + // build the pixel map brick-by-brick + // cruise through z-matrix row-by-row + // build a brick at each z-matrix value + var yi = ypx(0); + var yb = [yi, yi]; + var xbi = xrev ? 0 : 1; + var ybi = yrev ? 0 : 1; + // for collecting an average luminosity of the heatmap + var pixcount = 0; + var rcount = 0; + var gcount = 0; + var bcount = 0; + + var xb, j, xi, v, row, c; + + function setColor(v, pixsize) { + if(v !== undefined) { + var c = sclFunc(v); + c[0] = Math.round(c[0]); + c[1] = Math.round(c[1]); + c[2] = Math.round(c[2]); + + pixcount += pixsize; + rcount += c[0] * pixsize; + gcount += c[1] * pixsize; + bcount += c[2] * pixsize; + return c; + } + return [0, 0, 0, 0]; + } + + function interpColor(r0, r1, xinterp, yinterp) { + var z00 = r0[xinterp.bin0]; + if(z00 === undefined) return setColor(undefined, 1); + + var z01 = r0[xinterp.bin1]; + var z10 = r1[xinterp.bin0]; + var z11 = r1[xinterp.bin1]; + var dx = (z01 - z00) || 0; + var dy = (z10 - z00) || 0; + var dxy; + + // the bilinear interpolation term needs different calculations + // for all the different permutations of missing data + // among the neighbors of the main point, to ensure + // continuity across brick boundaries. + if(z01 === undefined) { + if(z11 === undefined) dxy = 0; + else if(z10 === undefined) dxy = 2 * (z11 - z00); + else dxy = (2 * z11 - z10 - z00) * 2 / 3; + } else if(z11 === undefined) { + if(z10 === undefined) dxy = 0; + else dxy = (2 * z00 - z01 - z10) * 2 / 3; + } else if(z10 === undefined) dxy = (2 * z11 - z01 - z00) * 2 / 3; + else dxy = (z11 + z00 - z01 - z10); + + return setColor(z00 + xinterp.frac * dx + yinterp.frac * (dy + xinterp.frac * dxy)); + } + + if(zsmooth) { // best or fast, works fastest with imageData + var pxIndex = 0; + var pixels; + + try { + pixels = new Uint8Array(imageWidth * imageHeight * 4); + } catch(e) { + pixels = new Array(imageWidth * imageHeight * 4); + } + + if(zsmooth === 'best') { + var xForPx = xc || x; + var yForPx = yc || y; + var xPixArray = new Array(xForPx.length); + var yPixArray = new Array(yForPx.length); + var xinterpArray = new Array(imageWidth); + var findInterpX = xc ? findInterpFromCenters : findInterp; + var findInterpY = yc ? findInterpFromCenters : findInterp; + var yinterp, r0, r1; + + // first make arrays of x and y pixel locations of brick boundaries + for(i = 0; i < xForPx.length; i++) xPixArray[i] = Math.round(xa.c2p(xForPx[i]) - left); + for(i = 0; i < yForPx.length; i++) yPixArray[i] = Math.round(ya.c2p(yForPx[i]) - top); + + // then make arrays of interpolations + // (bin0=closest, bin1=next, frac=fractional dist.) + for(i = 0; i < imageWidth; i++) xinterpArray[i] = findInterpX(i, xPixArray); + + // now do the interpolations and fill the png + for(j = 0; j < imageHeight; j++) { + yinterp = findInterpY(j, yPixArray); + r0 = z[yinterp.bin0]; + r1 = z[yinterp.bin1]; + for(i = 0; i < imageWidth; i++, pxIndex += 4) { + c = interpColor(r0, r1, xinterpArray[i], yinterp); + putColor(pixels, pxIndex, c); + } + } + } else { // zsmooth = fast + for(j = 0; j < m; j++) { + row = z[j]; + yb = ypx(j); + for(i = 0; i < imageWidth; i++) { + c = setColor(row[i], 1); + pxIndex = (yb * imageWidth + xpx(i)) * 4; + putColor(pixels, pxIndex, c); + } + } + } + + var imageData = context.createImageData(imageWidth, imageHeight); + try { + imageData.data.set(pixels); + } catch(e) { + var pxArray = imageData.data; + var dlen = pxArray.length; + for(j = 0; j < dlen; j ++) { + pxArray[j] = pixels[j]; + } + } + + context.putImageData(imageData, 0, 0); + } else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect + // gaps do not need to be exact integers, but if they *are* we will get + // cleaner edges by rounding at least one edge + var xGap = trace.xgap; + var yGap = trace.ygap; + var xGapLeft = Math.floor(xGap / 2); + var yGapTop = Math.floor(yGap / 2); + + for(j = 0; j < m; j++) { + row = z[j]; + yb.reverse(); + yb[ybi] = ypx(j + 1); + if(yb[0] === yb[1] || yb[0] === undefined || yb[1] === undefined) { + continue; + } + xi = xpx(0); + xb = [xi, xi]; + for(i = 0; i < n; i++) { + // build one color brick! + xb.reverse(); + xb[xbi] = xpx(i + 1); + if(xb[0] === xb[1] || xb[0] === undefined || xb[1] === undefined) { + continue; + } + v = row[i]; + c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0])); + context.fillStyle = 'rgba(' + c.join(',') + ')'; + + context.fillRect(xb[0] + xGapLeft, yb[0] + yGapTop, + xb[1] - xb[0] - xGap, yb[1] - yb[0] - yGap); + } + } + } + + rcount = Math.round(rcount / pixcount); + gcount = Math.round(gcount / pixcount); + bcount = Math.round(bcount / pixcount); + var avgColor = tinycolor('rgb(' + rcount + ',' + gcount + ',' + bcount + ')'); + + gd._hmpixcount = (gd._hmpixcount||0) + pixcount; + gd._hmlumcount = (gd._hmlumcount||0) + pixcount * avgColor.getLuminance(); + + var image3 = plotGroup.selectAll('image') + .data(cd); + + image3.enter().append('svg:image').attr({ + xmlns: xmlnsNamespaces.svg, + preserveAspectRatio: 'none' + }); + + image3.attr({ + height: imageHeight, + width: imageWidth, + x: left, + y: top, + 'xlink:href': canvas.toDataURL('image/png') + }); + }); +}; + +// get interpolated bin value. Returns {bin0:closest bin, frac:fractional dist to next, bin1:next bin} +function findInterp(pixel, pixArray) { + var maxBin = pixArray.length - 2; + var bin = Lib.constrain(Lib.findBin(pixel, pixArray), 0, maxBin); + var pix0 = pixArray[bin]; + var pix1 = pixArray[bin + 1]; + var interp = Lib.constrain(bin + (pixel - pix0) / (pix1 - pix0) - 0.5, 0, maxBin); + var bin0 = Math.round(interp); + var frac = Math.abs(interp - bin0); + + if(!interp || interp === maxBin || !frac) { + return { + bin0: bin0, + bin1: bin0, + frac: 0 + }; + } + return { + bin0: bin0, + frac: frac, + bin1: Math.round(bin0 + frac / (interp - bin0)) + }; +} + +function findInterpFromCenters(pixel, centerPixArray) { + var maxBin = centerPixArray.length - 1; + var bin = Lib.constrain(Lib.findBin(pixel, centerPixArray), 0, maxBin); + var pix0 = centerPixArray[bin]; + var pix1 = centerPixArray[bin + 1]; + var frac = ((pixel - pix0) / (pix1 - pix0)) || 0; + if(frac <= 0) { + return { + bin0: bin, + bin1: bin, + frac: 0 + }; + } + if(frac < 0.5) { + return { + bin0: bin, + bin1: bin + 1, + frac: frac + }; + } + return { + bin0: bin + 1, + bin1: bin, + frac: 1 - frac + }; +} + +function putColor(pixels, pxIndex, c) { + pixels[pxIndex] = c[0]; + pixels[pxIndex + 1] = c[1]; + pixels[pxIndex + 2] = c[2]; + pixels[pxIndex + 3] = Math.round(c[3] * 255); +} + +},{"../../components/colorscale":167,"../../constants/xmlns_namespaces":266,"../../lib":285,"../../registry":373,"@plotly/d3":20,"tinycolor2":119}],446:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +module.exports = function style(gd) { + d3.select(gd).selectAll('.hm image') + .style('opacity', function(d) { + return d.trace.opacity; + }); +}; + +},{"@plotly/d3":20}],447:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function handleStyleDefaults(traceIn, traceOut, coerce) { + var zsmooth = coerce('zsmooth'); + if(zsmooth === false) { + // ensure that xgap and ygap are coerced only when zsmooth allows them to have an effect. + coerce('xgap'); + coerce('ygap'); + } + + coerce('zhoverformat'); +}; + +},{}],448:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); + +var Registry = _dereq_('../../registry'); + +module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, xName, yName) { + var z = coerce('z'); + xName = xName || 'x'; + yName = yName || 'y'; + var x, y; + + if(z === undefined || !z.length) return 0; + + if(Lib.isArray1D(traceIn.z)) { + x = coerce(xName); + y = coerce(yName); + + var xlen = Lib.minRowLength(x); + var ylen = Lib.minRowLength(y); + + // column z must be accompanied by xName and yName arrays + if(xlen === 0 || ylen === 0) return 0; + + traceOut._length = Math.min(xlen, ylen, z.length); + } else { + x = coordDefaults(xName, coerce); + y = coordDefaults(yName, coerce); + + // TODO put z validation elsewhere + if(!isValidZ(z)) return 0; + + coerce('transpose'); + + traceOut._length = null; + } + + if(traceIn.type === 'heatmapgl') return true; // skip calendars until we handle them in those traces + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, [xName, yName], layout); + + return true; +}; + +function coordDefaults(coordStr, coerce) { + var coord = coerce(coordStr); + var coordType = coord ? coerce(coordStr + 'type', 'array') : 'scaled'; + + if(coordType === 'scaled') { + coerce(coordStr + '0'); + coerce('d' + coordStr); + } + + return coord; +} + +function isValidZ(z) { + var allRowsAreArrays = true; + var oneRowIsFilled = false; + var hasOneNumber = false; + var zi; + + /* + * Without this step: + * + * hasOneNumber = false breaks contour but not heatmap + * allRowsAreArrays = false breaks contour but not heatmap + * oneRowIsFilled = false breaks both + */ + + for(var i = 0; i < z.length; i++) { + zi = z[i]; + if(!Lib.isArrayOrTypedArray(zi)) { + allRowsAreArrays = false; + break; + } + if(zi.length > 0) oneRowIsFilled = true; + for(var j = 0; j < zi.length; j++) { + if(isNumeric(zi[j])) { + hasOneNumber = true; + break; + } + } + } + + return (allRowsAreArrays && oneRowIsFilled && hasOneNumber); +} + +},{"../../lib":285,"../../registry":373,"fast-isnumeric":31}],449:[function(_dereq_,module,exports){ +'use strict'; + +var barAttrs = _dereq_('../bar/attributes'); +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var makeBinAttrs = _dereq_('./bin_attributes'); +var constants = _dereq_('./constants'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = { + x: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + y: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + }, + + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + + text: extendFlat({}, barAttrs.text, { + }), + hovertext: extendFlat({}, barAttrs.hovertext, { + }), + orientation: barAttrs.orientation, + + histfunc: { + valType: 'enumerated', + values: ['count', 'sum', 'avg', 'min', 'max'], + dflt: 'count', + editType: 'calc', + }, + histnorm: { + valType: 'enumerated', + values: ['', 'percent', 'probability', 'density', 'probability density'], + dflt: '', + editType: 'calc', + }, + + cumulative: { + enabled: { + valType: 'boolean', + dflt: false, + editType: 'calc', + }, + + direction: { + valType: 'enumerated', + values: ['increasing', 'decreasing'], + dflt: 'increasing', + editType: 'calc', + }, + + currentbin: { + valType: 'enumerated', + values: ['include', 'exclude', 'half'], + dflt: 'include', + editType: 'calc', + }, + editType: 'calc' + }, + nbinsx: { + valType: 'integer', + min: 0, + dflt: 0, + editType: 'calc', + }, + xbins: makeBinAttrs('x', true), + + nbinsy: { + valType: 'integer', + min: 0, + dflt: 0, + editType: 'calc', + }, + ybins: makeBinAttrs('y', true), + autobinx: { + valType: 'boolean', + dflt: null, + editType: 'calc', + }, + autobiny: { + valType: 'boolean', + dflt: null, + editType: 'calc', + }, + + bingroup: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + + marker: barAttrs.marker, + + offsetgroup: barAttrs.offsetgroup, + alignmentgroup: barAttrs.alignmentgroup, + + selected: barAttrs.selected, + unselected: barAttrs.unselected, + + _deprecated: { + bardir: barAttrs._deprecated.bardir + } +}; + +},{"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../../plots/template_attributes":368,"../bar/attributes":383,"./bin_attributes":451,"./constants":455}],450:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = function doAvg(size, counts) { + var nMax = size.length; + var total = 0; + for(var i = 0; i < nMax; i++) { + if(counts[i]) { + size[i] /= counts[i]; + total += size[i]; + } else size[i] = null; + } + return total; +}; + +},{}],451:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function makeBinAttrs(axLetter, match) { + return { + start: { + valType: 'any', // for date axes + editType: 'calc', + }, + end: { + valType: 'any', // for date axes + editType: 'calc', + }, + size: { + valType: 'any', // for date axes + editType: 'calc', + }, + editType: 'calc' + }; +}; + +},{}],452:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + + +module.exports = { + count: function(n, i, size) { + size[n]++; + return 1; + }, + + sum: function(n, i, size, counterData) { + var v = counterData[i]; + if(isNumeric(v)) { + v = Number(v); + size[n] += v; + return v; + } + return 0; + }, + + avg: function(n, i, size, counterData, counts) { + var v = counterData[i]; + if(isNumeric(v)) { + v = Number(v); + size[n] += v; + counts[n]++; + } + return 0; + }, + + min: function(n, i, size, counterData) { + var v = counterData[i]; + if(isNumeric(v)) { + v = Number(v); + if(!isNumeric(size[n])) { + size[n] = v; + return v; + } else if(size[n] > v) { + var delta = v - size[n]; + size[n] = v; + return delta; + } + } + return 0; + }, + + max: function(n, i, size, counterData) { + var v = counterData[i]; + if(isNumeric(v)) { + v = Number(v); + if(!isNumeric(size[n])) { + size[n] = v; + return v; + } else if(size[n] < v) { + var delta = v - size[n]; + size[n] = v; + return delta; + } + } + return 0; + } +}; + +},{"fast-isnumeric":31}],453:[function(_dereq_,module,exports){ +'use strict'; + +var numConstants = _dereq_('../../constants/numerical'); +var oneYear = numConstants.ONEAVGYEAR; +var oneMonth = numConstants.ONEAVGMONTH; +var oneDay = numConstants.ONEDAY; +var oneHour = numConstants.ONEHOUR; +var oneMin = numConstants.ONEMIN; +var oneSec = numConstants.ONESEC; +var tickIncrement = _dereq_('../../plots/cartesian/axes').tickIncrement; + + +/* + * make a function that will find rounded bin edges + * @param {number} leftGap: how far from the left edge of any bin is the closest data value? + * @param {number} rightGap: how far from the right edge of any bin is the closest data value? + * @param {Array[number]} binEdges: the actual edge values used in binning + * @param {object} pa: the position axis + * @param {string} calendar: the data calendar + * + * @return {function(v, isRightEdge)}: + * find the start (isRightEdge is falsy) or end (truthy) label value for a bin edge `v` + */ +module.exports = function getBinSpanLabelRound(leftGap, rightGap, binEdges, pa, calendar) { + // the rounding digit is the largest digit that changes in *all* of 4 regions: + // - inside the rightGap before binEdges[0] (shifted 10% to the left) + // - inside the leftGap after binEdges[0] (expanded by 10% of rightGap on each end) + // - same for binEdges[1] + var dv0 = -1.1 * rightGap; + var dv1 = -0.1 * rightGap; + var dv2 = leftGap - dv1; + var edge0 = binEdges[0]; + var edge1 = binEdges[1]; + var leftDigit = Math.min( + biggestDigitChanged(edge0 + dv1, edge0 + dv2, pa, calendar), + biggestDigitChanged(edge1 + dv1, edge1 + dv2, pa, calendar) + ); + var rightDigit = Math.min( + biggestDigitChanged(edge0 + dv0, edge0 + dv1, pa, calendar), + biggestDigitChanged(edge1 + dv0, edge1 + dv1, pa, calendar) + ); + + // normally we try to make the label for the right edge different from + // the left edge label, so it's unambiguous which bin gets data on the edge. + // but if this results in more than 3 extra digits (or for dates, more than + // 2 fields ie hr&min or min&sec, which is 3600x), it'll be more clutter than + // useful so keep the label cleaner instead + var digit, disambiguateEdges; + if(leftDigit > rightDigit && rightDigit < Math.abs(edge1 - edge0) / 4000) { + digit = leftDigit; + disambiguateEdges = false; + } else { + digit = Math.min(leftDigit, rightDigit); + disambiguateEdges = true; + } + + if(pa.type === 'date' && digit > oneDay) { + var dashExclude = (digit === oneYear) ? 1 : 6; + var increment = (digit === oneYear) ? 'M12' : 'M1'; + + return function(v, isRightEdge) { + var dateStr = pa.c2d(v, oneYear, calendar); + var dashPos = dateStr.indexOf('-', dashExclude); + if(dashPos > 0) dateStr = dateStr.substr(0, dashPos); + var roundedV = pa.d2c(dateStr, 0, calendar); + + if(roundedV < v) { + var nextV = tickIncrement(roundedV, increment, false, calendar); + if((roundedV + nextV) / 2 < v + leftGap) roundedV = nextV; + } + + if(isRightEdge && disambiguateEdges) { + return tickIncrement(roundedV, increment, true, calendar); + } + + return roundedV; + }; + } + + return function(v, isRightEdge) { + var roundedV = digit * Math.round(v / digit); + // if we rounded down and we could round up and still be < leftGap + // (or what leftGap values round to), do that + if(roundedV + (digit / 10) < v && roundedV + (digit * 0.9) < v + leftGap) { + roundedV += digit; + } + // finally for the right edge back off one digit - but only if we can do that + // and not clip off any data that's potentially in the bin + if(isRightEdge && disambiguateEdges) { + roundedV -= digit; + } + return roundedV; + }; +}; + +/* + * Find the largest digit that changes within a (calcdata) region [v1, v2] + * if dates, "digit" means date/time part when it's bigger than a second + * returns the unit value to round to this digit, eg 0.01 to round to hundredths, or + * 100 to round to hundreds. returns oneMonth or oneYear for month or year rounding, + * so that Math.min will work, rather than 'M1' and 'M12' + */ +function biggestDigitChanged(v1, v2, pa, calendar) { + // are we crossing zero? can't say anything. + // in principle this doesn't apply to dates but turns out this doesn't matter. + if(v1 * v2 <= 0) return Infinity; + + var dv = Math.abs(v2 - v1); + var isDate = pa.type === 'date'; + var digit = biggestGuaranteedDigitChanged(dv, isDate); + // see if a larger digit also changed + for(var i = 0; i < 10; i++) { + // numbers: next digit needs to be >10x but <100x then gets rounded down. + // dates: next digit can be as much as 60x (then rounded down) + var nextDigit = biggestGuaranteedDigitChanged(digit * 80, isDate); + // if we get to years, the chain stops + if(digit === nextDigit) break; + if(didDigitChange(nextDigit, v1, v2, isDate, pa, calendar)) digit = nextDigit; + else break; + } + return digit; +} + +/* + * Find the largest digit that *definitely* changes in a region [v, v + dv] for any v + * for nonuniform date regions (months/years) pick the largest + */ +function biggestGuaranteedDigitChanged(dv, isDate) { + if(isDate && dv > oneSec) { + // this is supposed to be the biggest *guaranteed* change + // so compare to the longest month and year across any calendar, + // and we'll iterate back up later + // note: does not support rounding larger than one year. We could add + // that if anyone wants it, but seems unusual and not strictly necessary. + if(dv > oneDay) { + if(dv > oneYear * 1.1) return oneYear; + if(dv > oneMonth * 1.1) return oneMonth; + return oneDay; + } + + if(dv > oneHour) return oneHour; + if(dv > oneMin) return oneMin; + return oneSec; + } + return Math.pow(10, Math.floor(Math.log(dv) / Math.LN10)); +} + +function didDigitChange(digit, v1, v2, isDate, pa, calendar) { + if(isDate && digit > oneDay) { + var dateParts1 = dateParts(v1, pa, calendar); + var dateParts2 = dateParts(v2, pa, calendar); + var parti = (digit === oneYear) ? 0 : 1; + return dateParts1[parti] !== dateParts2[parti]; + } + return Math.floor(v2 / digit) - Math.floor(v1 / digit) > 0.1; +} + +function dateParts(v, pa, calendar) { + var parts = pa.c2d(v, oneYear, calendar).split('-'); + if(parts[0] === '') { + parts.unshift(); + parts[0] = '-' + parts[0]; + } + return parts; +} + +},{"../../constants/numerical":265,"../../plots/cartesian/axes":331}],454:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +var arraysToCalcdata = _dereq_('../bar/arrays_to_calcdata'); +var binFunctions = _dereq_('./bin_functions'); +var normFunctions = _dereq_('./norm_functions'); +var doAvg = _dereq_('./average'); +var getBinSpanLabelRound = _dereq_('./bin_label_vals'); + +function calc(gd, trace) { + var pos = []; + var size = []; + var pa = Axes.getFromId(gd, trace.orientation === 'h' ? trace.yaxis : trace.xaxis); + var mainData = trace.orientation === 'h' ? 'y' : 'x'; + var counterData = {x: 'y', y: 'x'}[mainData]; + var calendar = trace[mainData + 'calendar']; + var cumulativeSpec = trace.cumulative; + var i; + + var binsAndPos = calcAllAutoBins(gd, trace, pa, mainData); + var binSpec = binsAndPos[0]; + var pos0 = binsAndPos[1]; + + var nonuniformBins = typeof binSpec.size === 'string'; + var binEdges = []; + var bins = nonuniformBins ? binEdges : binSpec; + // make the empty bin array + var inc = []; + var counts = []; + var inputPoints = []; + var total = 0; + var norm = trace.histnorm; + var func = trace.histfunc; + var densityNorm = norm.indexOf('density') !== -1; + var i2, binEnd, n; + + if(cumulativeSpec.enabled && densityNorm) { + // we treat "cumulative" like it means "integral" if you use a density norm, + // which in the end means it's the same as without "density" + norm = norm.replace(/ ?density$/, ''); + densityNorm = false; + } + + var extremeFunc = func === 'max' || func === 'min'; + var sizeInit = extremeFunc ? null : 0; + var binFunc = binFunctions.count; + var normFunc = normFunctions[norm]; + var isAvg = false; + var pr2c = function(v) { return pa.r2c(v, 0, calendar); }; + var rawCounterData; + + if(Lib.isArrayOrTypedArray(trace[counterData]) && func !== 'count') { + rawCounterData = trace[counterData]; + isAvg = func === 'avg'; + binFunc = binFunctions[func]; + } + + // create the bins (and any extra arrays needed) + // assume more than 1e6 bins is an error, so we don't crash the browser + i = pr2c(binSpec.start); + + // decrease end a little in case of rounding errors + binEnd = pr2c(binSpec.end) + (i - Axes.tickIncrement(i, binSpec.size, false, calendar)) / 1e6; + + while(i < binEnd && pos.length < 1e6) { + i2 = Axes.tickIncrement(i, binSpec.size, false, calendar); + pos.push((i + i2) / 2); + size.push(sizeInit); + inputPoints.push([]); + // nonuniform bins (like months) we need to search, + // rather than straight calculate the bin we're in + binEdges.push(i); + // nonuniform bins also need nonuniform normalization factors + if(densityNorm) inc.push(1 / (i2 - i)); + if(isAvg) counts.push(0); + // break to avoid infinite loops + if(i2 <= i) break; + i = i2; + } + binEdges.push(i); + + // for date axes we need bin bounds to be calcdata. For nonuniform bins + // we already have this, but uniform with start/end/size they're still strings. + if(!nonuniformBins && pa.type === 'date') { + bins = { + start: pr2c(bins.start), + end: pr2c(bins.end), + size: bins.size + }; + } + + // stash left and right gaps by group + if(!gd._fullLayout._roundFnOpts) gd._fullLayout._roundFnOpts = {}; + var groupName = trace['_' + mainData + 'bingroup']; + var roundFnOpts = {leftGap: Infinity, rightGap: Infinity}; + if(groupName) { + if(!gd._fullLayout._roundFnOpts[groupName]) gd._fullLayout._roundFnOpts[groupName] = roundFnOpts; + roundFnOpts = gd._fullLayout._roundFnOpts[groupName]; + } + + // bin the data + // and make histogram-specific pt-number-to-cd-index map object + var nMax = size.length; + var uniqueValsPerBin = true; + var leftGap = roundFnOpts.leftGap; + var rightGap = roundFnOpts.rightGap; + var ptNumber2cdIndex = {}; + for(i = 0; i < pos0.length; i++) { + var posi = pos0[i]; + n = Lib.findBin(posi, bins); + if(n >= 0 && n < nMax) { + total += binFunc(n, i, size, rawCounterData, counts); + if(uniqueValsPerBin && inputPoints[n].length && posi !== pos0[inputPoints[n][0]]) { + uniqueValsPerBin = false; + } + inputPoints[n].push(i); + ptNumber2cdIndex[i] = n; + + leftGap = Math.min(leftGap, posi - binEdges[n]); + rightGap = Math.min(rightGap, binEdges[n + 1] - posi); + } + } + roundFnOpts.leftGap = leftGap; + roundFnOpts.rightGap = rightGap; + + var roundFn; + if(!uniqueValsPerBin) { + roundFn = function(v, isRightEdge) { + return function() { + var roundFnOpts = gd._fullLayout._roundFnOpts[groupName]; + return getBinSpanLabelRound( + roundFnOpts.leftGap, + roundFnOpts.rightGap, + binEdges, pa, calendar + )(v, isRightEdge); + }; + }; + } + + // average and/or normalize the data, if needed + if(isAvg) total = doAvg(size, counts); + if(normFunc) normFunc(size, total, inc); + + // after all normalization etc, now we can accumulate if desired + if(cumulativeSpec.enabled) cdf(size, cumulativeSpec.direction, cumulativeSpec.currentbin); + + var seriesLen = Math.min(pos.length, size.length); + var cd = []; + var firstNonzero = 0; + var lastNonzero = seriesLen - 1; + + // look for empty bins at the ends to remove, so autoscale omits them + for(i = 0; i < seriesLen; i++) { + if(size[i]) { + firstNonzero = i; + break; + } + } + for(i = seriesLen - 1; i >= firstNonzero; i--) { + if(size[i]) { + lastNonzero = i; + break; + } + } + + // create the "calculated data" to plot + for(i = firstNonzero; i <= lastNonzero; i++) { + if((isNumeric(pos[i]) && isNumeric(size[i]))) { + var cdi = { + p: pos[i], + s: size[i], + b: 0 + }; + + // setup hover and event data fields, + // N.B. pts and "hover" positions ph0/ph1 don't seem to make much sense + // for cumulative distributions + if(!cumulativeSpec.enabled) { + cdi.pts = inputPoints[i]; + if(uniqueValsPerBin) { + cdi.ph0 = cdi.ph1 = (inputPoints[i].length) ? pos0[inputPoints[i][0]] : pos[i]; + } else { + // Defer evaluation of ph(0|1) in crossTraceCalc + trace._computePh = true; + cdi.ph0 = roundFn(binEdges[i]); + cdi.ph1 = roundFn(binEdges[i + 1], true); + } + } + cd.push(cdi); + } + } + + if(cd.length === 1) { + // when we collapse to a single bin, calcdata no longer describes bin size + // so we need to explicitly specify it + cd[0].width1 = Axes.tickIncrement(cd[0].p, binSpec.size, false, calendar) - cd[0].p; + } + + arraysToCalcdata(cd, trace); + + if(Lib.isArrayOrTypedArray(trace.selectedpoints)) { + Lib.tagSelected(cd, trace, ptNumber2cdIndex); + } + + return cd; +} + +/* + * calcAllAutoBins: we want all histograms inside the same bingroup + * (see logic in Histogram.crossTraceDefaults) to share bin specs + * + * If the user has explicitly specified differing + * bin specs, there's nothing we can do, but if possible we will try to use the + * smallest bins of any of the auto values for all histograms inside the same + * bingroup. + */ +function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) { + var binAttr = mainData + 'bins'; + var fullLayout = gd._fullLayout; + var groupName = trace['_' + mainData + 'bingroup']; + var binOpts = fullLayout._histogramBinOpts[groupName]; + var isOverlay = fullLayout.barmode === 'overlay'; + var i, traces, tracei, calendar, pos0, autoVals, cumulativeSpec; + + var r2c = function(v) { return pa.r2c(v, 0, calendar); }; + var c2r = function(v) { return pa.c2r(v, 0, calendar); }; + + var cleanBound = pa.type === 'date' ? + function(v) { return (v || v === 0) ? Lib.cleanDate(v, null, calendar) : null; } : + function(v) { return isNumeric(v) ? Number(v) : null; }; + + function setBound(attr, bins, newBins) { + if(bins[attr + 'Found']) { + bins[attr] = cleanBound(bins[attr]); + if(bins[attr] === null) bins[attr] = newBins[attr]; + } else { + autoVals[attr] = bins[attr] = newBins[attr]; + Lib.nestedProperty(traces[0], binAttr + '.' + attr).set(newBins[attr]); + } + } + + // all but the first trace in this group has already been marked finished + // clear this flag, so next time we run calc we will run autobin again + if(trace['_' + mainData + 'autoBinFinished']) { + delete trace['_' + mainData + 'autoBinFinished']; + } else { + traces = binOpts.traces; + var allPos = []; + + // Note: we're including `legendonly` traces here for autobin purposes, + // so that showing & hiding from the legend won't affect bins. + // But this complicates things a bit since those traces don't `calc`, + // hence `isFirstVisible`. + var isFirstVisible = true; + var has2dMap = false; + var hasHist2dContour = false; + for(i = 0; i < traces.length; i++) { + tracei = traces[i]; + + if(tracei.visible) { + var mainDatai = binOpts.dirs[i]; + pos0 = tracei['_' + mainDatai + 'pos0'] = pa.makeCalcdata(tracei, mainDatai); + + allPos = Lib.concat(allPos, pos0); + delete tracei['_' + mainData + 'autoBinFinished']; + + if(trace.visible === true) { + if(isFirstVisible) { + isFirstVisible = false; + } else { + delete tracei._autoBin; + tracei['_' + mainData + 'autoBinFinished'] = 1; + } + if(Registry.traceIs(tracei, '2dMap')) { + has2dMap = true; + } + if(tracei.type === 'histogram2dcontour') { + hasHist2dContour = true; + } + } + } + } + + calendar = traces[0][mainData + 'calendar']; + var newBinSpec = Axes.autoBin(allPos, pa, binOpts.nbins, has2dMap, calendar, binOpts.sizeFound && binOpts.size); + + var autoBin = traces[0]._autoBin = {}; + autoVals = autoBin[binOpts.dirs[0]] = {}; + + if(hasHist2dContour) { + // the "true" 2nd argument reverses the tick direction (which we can't + // just do with a minus sign because of month bins) + if(!binOpts.size) { + newBinSpec.start = c2r(Axes.tickIncrement( + r2c(newBinSpec.start), newBinSpec.size, true, calendar)); + } + if(binOpts.end === undefined) { + newBinSpec.end = c2r(Axes.tickIncrement( + r2c(newBinSpec.end), newBinSpec.size, false, calendar)); + } + } + + // Edge case: single-valued histogram overlaying others + // Use them all together to calculate the bin size for the single-valued one + if(isOverlay && !Registry.traceIs(trace, '2dMap') && newBinSpec._dataSpan === 0 && + pa.type !== 'category' && pa.type !== 'multicategory') { + // Several single-valued histograms! Stop infinite recursion, + // just return an extra flag that tells handleSingleValueOverlays + // to sort out this trace too + if(_overlayEdgeCase) return [newBinSpec, pos0, true]; + + newBinSpec = handleSingleValueOverlays(gd, trace, pa, mainData, binAttr); + } + + // adjust for CDF edge cases + cumulativeSpec = tracei.cumulative || {}; + if(cumulativeSpec.enabled && (cumulativeSpec.currentbin !== 'include')) { + if(cumulativeSpec.direction === 'decreasing') { + newBinSpec.start = c2r(Axes.tickIncrement( + r2c(newBinSpec.start), newBinSpec.size, true, calendar)); + } else { + newBinSpec.end = c2r(Axes.tickIncrement( + r2c(newBinSpec.end), newBinSpec.size, false, calendar)); + } + } + + binOpts.size = newBinSpec.size; + if(!binOpts.sizeFound) { + autoVals.size = newBinSpec.size; + Lib.nestedProperty(traces[0], binAttr + '.size').set(newBinSpec.size); + } + + setBound('start', binOpts, newBinSpec); + setBound('end', binOpts, newBinSpec); + } + + pos0 = trace['_' + mainData + 'pos0']; + delete trace['_' + mainData + 'pos0']; + + // Each trace can specify its own start/end, or if omitted + // we ensure they're beyond the bounds of this trace's data, + // and we need to make sure start is aligned with the main start + var traceInputBins = trace._input[binAttr] || {}; + var traceBinOptsCalc = Lib.extendFlat({}, binOpts); + var mainStart = binOpts.start; + var startIn = pa.r2l(traceInputBins.start); + var hasStart = startIn !== undefined; + if((binOpts.startFound || hasStart) && startIn !== pa.r2l(mainStart)) { + // We have an explicit start to reconcile across traces + // if this trace has an explicit start, shift it down to a bin edge + // if another trace had an explicit start, shift it down to a + // bin edge past our data + var traceStart = hasStart ? + startIn : + Lib.aggNums(Math.min, null, pos0); + + var dummyAx = { + type: (pa.type === 'category' || pa.type === 'multicategory') ? 'linear' : pa.type, + r2l: pa.r2l, + dtick: binOpts.size, + tick0: mainStart, + calendar: calendar, + range: ([traceStart, Axes.tickIncrement(traceStart, binOpts.size, false, calendar)]).map(pa.l2r) + }; + var newStart = Axes.tickFirst(dummyAx); + if(newStart > pa.r2l(traceStart)) { + newStart = Axes.tickIncrement(newStart, binOpts.size, true, calendar); + } + traceBinOptsCalc.start = pa.l2r(newStart); + if(!hasStart) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.start); + } + + var mainEnd = binOpts.end; + var endIn = pa.r2l(traceInputBins.end); + var hasEnd = endIn !== undefined; + if((binOpts.endFound || hasEnd) && endIn !== pa.r2l(mainEnd)) { + // Reconciling an explicit end is easier, as it doesn't need to + // match bin edges + var traceEnd = hasEnd ? + endIn : + Lib.aggNums(Math.max, null, pos0); + + traceBinOptsCalc.end = pa.l2r(traceEnd); + if(!hasEnd) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.end); + } + + // Backward compatibility for one-time autobinning. + // autobin: true is handled in cleanData, but autobin: false + // needs to be here where we have determined the values. + var autoBinAttr = 'autobin' + mainData; + if(trace._input[autoBinAttr] === false) { + trace._input[binAttr] = Lib.extendFlat({}, trace[binAttr] || {}); + delete trace._input[autoBinAttr]; + delete trace[autoBinAttr]; + } + + return [traceBinOptsCalc, pos0]; +} + +/* + * Adjust single-value histograms in overlay mode to make as good a + * guess as we can at autobin values the user would like. + * + * Returns the binSpec for the trace that sparked all this + */ +function handleSingleValueOverlays(gd, trace, pa, mainData, binAttr) { + var fullLayout = gd._fullLayout; + var overlaidTraceGroup = getConnectedHistograms(gd, trace); + var pastThisTrace = false; + var minSize = Infinity; + var singleValuedTraces = [trace]; + var i, tracei, binOpts; + + // first collect all the: + // - min bin size from all multi-valued traces + // - single-valued traces + for(i = 0; i < overlaidTraceGroup.length; i++) { + tracei = overlaidTraceGroup[i]; + + if(tracei === trace) { + pastThisTrace = true; + } else if(!pastThisTrace) { + // This trace has already had its autobins calculated, so either: + // - it is part of a bingroup + // - it is NOT a single-valued trace + binOpts = fullLayout._histogramBinOpts[tracei['_' + mainData + 'bingroup']]; + minSize = Math.min(minSize, binOpts.size || tracei[binAttr].size); + } else { + var resulti = calcAllAutoBins(gd, tracei, pa, mainData, true); + var binSpeci = resulti[0]; + var isSingleValued = resulti[2]; + + // so we can use this result when we get to tracei in the normal + // course of events, mark it as done and put _pos0 back + tracei['_' + mainData + 'autoBinFinished'] = 1; + tracei['_' + mainData + 'pos0'] = resulti[1]; + + if(isSingleValued) { + singleValuedTraces.push(tracei); + } else { + minSize = Math.min(minSize, binSpeci.size); + } + } + } + + // find the real data values for each single-valued trace + // hunt through pos0 for the first valid value + var dataVals = new Array(singleValuedTraces.length); + for(i = 0; i < singleValuedTraces.length; i++) { + var pos0 = singleValuedTraces[i]['_' + mainData + 'pos0']; + for(var j = 0; j < pos0.length; j++) { + if(pos0[j] !== undefined) { + dataVals[i] = pos0[j]; + break; + } + } + } + + // are ALL traces are single-valued? use the min difference between + // all of their values (which defaults to 1 if there's still only one) + if(!isFinite(minSize)) { + minSize = Lib.distinctVals(dataVals).minDiff; + } + + // now apply the min size we found to all single-valued traces + for(i = 0; i < singleValuedTraces.length; i++) { + tracei = singleValuedTraces[i]; + var calendar = tracei[mainData + 'calendar']; + + var newBins = { + start: pa.c2r(dataVals[i] - minSize / 2, 0, calendar), + end: pa.c2r(dataVals[i] + minSize / 2, 0, calendar), + size: minSize + }; + + tracei._input[binAttr] = tracei[binAttr] = newBins; + + binOpts = fullLayout._histogramBinOpts[tracei['_' + mainData + 'bingroup']]; + if(binOpts) Lib.extendFlat(binOpts, newBins); + } + + return trace[binAttr]; +} + +/* + * Return an array of histograms that share axes and orientation. + * + * Only considers histograms. In principle we could include bars in a + * similar way to how we do manually binned histograms, though this + * would have tons of edge cases and value judgments to make. + */ +function getConnectedHistograms(gd, trace) { + var xid = trace.xaxis; + var yid = trace.yaxis; + var orientation = trace.orientation; + + var out = []; + var fullData = gd._fullData; + for(var i = 0; i < fullData.length; i++) { + var tracei = fullData[i]; + if(tracei.type === 'histogram' && + tracei.visible === true && + tracei.orientation === orientation && + tracei.xaxis === xid && tracei.yaxis === yid + ) { + out.push(tracei); + } + } + + return out; +} + +function cdf(size, direction, currentBin) { + var i, vi, prevSum; + + function firstHalfPoint(i) { + prevSum = size[i]; + size[i] /= 2; + } + + function nextHalfPoint(i) { + vi = size[i]; + size[i] = prevSum + vi / 2; + prevSum += vi; + } + + if(currentBin === 'half') { + if(direction === 'increasing') { + firstHalfPoint(0); + for(i = 1; i < size.length; i++) { + nextHalfPoint(i); + } + } else { + firstHalfPoint(size.length - 1); + for(i = size.length - 2; i >= 0; i--) { + nextHalfPoint(i); + } + } + } else if(direction === 'increasing') { + for(i = 1; i < size.length; i++) { + size[i] += size[i - 1]; + } + + // 'exclude' is identical to 'include' just shifted one bin over + if(currentBin === 'exclude') { + size.unshift(0); + size.pop(); + } + } else { + for(i = size.length - 2; i >= 0; i--) { + size[i] += size[i + 1]; + } + + if(currentBin === 'exclude') { + size.push(0); + size.shift(); + } + } +} + +module.exports = { + calc: calc, + calcAllAutoBins: calcAllAutoBins +}; + +},{"../../lib":285,"../../plots/cartesian/axes":331,"../../registry":373,"../bar/arrays_to_calcdata":382,"./average":450,"./bin_functions":452,"./bin_label_vals":453,"./norm_functions":461,"fast-isnumeric":31}],455:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + eventDataKeys: ['binNumber'] +}; + +},{}],456:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); + +var traceIs = _dereq_('../../registry').traceIs; +var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults; + +var nestedProperty = Lib.nestedProperty; +var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup; + +var BINATTRS = [ + {aStr: {x: 'xbins.start', y: 'ybins.start'}, name: 'start'}, + {aStr: {x: 'xbins.end', y: 'ybins.end'}, name: 'end'}, + {aStr: {x: 'xbins.size', y: 'ybins.size'}, name: 'size'}, + {aStr: {x: 'nbinsx', y: 'nbinsy'}, name: 'nbins'} +]; + +var BINDIRECTIONS = ['x', 'y']; + +// handle bin attrs and relink auto-determined values so fullData is complete +module.exports = function crossTraceDefaults(fullData, fullLayout) { + var allBinOpts = fullLayout._histogramBinOpts = {}; + var histTraces = []; + var mustMatchTracesLookup = {}; + var otherTracesList = []; + + var traceOut, traces, groupName, binDir; + var i, j, k; + + function coerce(attr, dflt) { + return Lib.coerce(traceOut._input, traceOut, traceOut._module.attributes, attr, dflt); + } + + function orientation2binDir(traceOut) { + return traceOut.orientation === 'v' ? 'x' : 'y'; + } + + function getAxisType(traceOut, binDir) { + var ax = axisIds.getFromTrace({_fullLayout: fullLayout}, traceOut, binDir); + return ax.type; + } + + function fillBinOpts(traceOut, groupName, binDir) { + // N.B. group traces that don't have a bingroup with themselves + var fallbackGroupName = traceOut.uid + '__' + binDir; + if(!groupName) groupName = fallbackGroupName; + + var axType = getAxisType(traceOut, binDir); + var calendar = traceOut[binDir + 'calendar'] || ''; + var binOpts = allBinOpts[groupName]; + var needsNewItem = true; + + if(binOpts) { + if(axType === binOpts.axType && calendar === binOpts.calendar) { + needsNewItem = false; + binOpts.traces.push(traceOut); + binOpts.dirs.push(binDir); + } else { + groupName = fallbackGroupName; + + if(axType !== binOpts.axType) { + Lib.warn([ + 'Attempted to group the bins of trace', traceOut.index, + 'set on a', 'type:' + axType, 'axis', + 'with bins on', 'type:' + binOpts.axType, 'axis.' + ].join(' ')); + } + if(calendar !== binOpts.calendar) { + // prohibit bingroup for traces using different calendar, + // there's probably a way to make this work, but skip for now + Lib.warn([ + 'Attempted to group the bins of trace', traceOut.index, + 'set with a', calendar, 'calendar', + 'with bins', + (binOpts.calendar ? 'on a ' + binOpts.calendar + ' calendar' : 'w/o a set calendar') + ].join(' ')); + } + } + } + + if(needsNewItem) { + allBinOpts[groupName] = { + traces: [traceOut], + dirs: [binDir], + axType: axType, + calendar: traceOut[binDir + 'calendar'] || '' + }; + } + traceOut['_' + binDir + 'bingroup'] = groupName; + } + + for(i = 0; i < fullData.length; i++) { + traceOut = fullData[i]; + + if(traceIs(traceOut, 'histogram')) { + histTraces.push(traceOut); + + // TODO: this shouldn't be relinked as it's only used within calc + // https://github.com/plotly/plotly.js/issues/749 + delete traceOut._xautoBinFinished; + delete traceOut._yautoBinFinished; + + // N.B. need to coerce *alignmentgroup* before *bingroup*, as traces + // in same alignmentgroup "have to match" + if(!traceIs(traceOut, '2dMap')) { + handleGroupingDefaults(traceOut._input, traceOut, fullLayout, coerce); + } + } + } + + var alignmentOpts = fullLayout._alignmentOpts || {}; + + // Look for traces that "have to match", that is: + // - 1d histogram traces on the same subplot with same orientation under barmode:stack, + // - 1d histogram traces on the same subplot with same orientation under barmode:group + // - 1d histogram traces on the same position axis with the same orientation + // and the same *alignmentgroup* (coerced under barmode:group) + // - Once `stackgroup` gets implemented (see https://github.com/plotly/plotly.js/issues/3614), + // traces within the same stackgroup will also "have to match" + for(i = 0; i < histTraces.length; i++) { + traceOut = histTraces[i]; + groupName = ''; + + if(!traceIs(traceOut, '2dMap')) { + binDir = orientation2binDir(traceOut); + + if(fullLayout.barmode === 'group' && traceOut.alignmentgroup) { + var pa = traceOut[binDir + 'axis']; + var aGroupId = getAxisGroup(fullLayout, pa) + traceOut.orientation; + if((alignmentOpts[aGroupId] || {})[traceOut.alignmentgroup]) { + groupName = aGroupId; + } + } + + if(!groupName && fullLayout.barmode !== 'overlay') { + groupName = ( + getAxisGroup(fullLayout, traceOut.xaxis) + + getAxisGroup(fullLayout, traceOut.yaxis) + + orientation2binDir(traceOut) + ); + } + } + + if(groupName) { + if(!mustMatchTracesLookup[groupName]) { + mustMatchTracesLookup[groupName] = []; + } + mustMatchTracesLookup[groupName].push(traceOut); + } else { + otherTracesList.push(traceOut); + } + } + + // Setup binOpts for traces that have to match, + // if the traces have a valid bingroup, use that + // if not use axis+binDir groupName + for(groupName in mustMatchTracesLookup) { + traces = mustMatchTracesLookup[groupName]; + + // no need to 'force' anything when a single + // trace is detected as "must match" + if(traces.length === 1) { + otherTracesList.push(traces[0]); + continue; + } + + var binGroupFound = false; + if(traces.length) { + traceOut = traces[0]; + binGroupFound = coerce('bingroup'); + } + + groupName = binGroupFound || groupName; + + for(i = 0; i < traces.length; i++) { + traceOut = traces[i]; + var bingroupIn = traceOut._input.bingroup; + if(bingroupIn && bingroupIn !== groupName) { + Lib.warn([ + 'Trace', traceOut.index, 'must match', + 'within bingroup', groupName + '.', + 'Ignoring its bingroup:', bingroupIn, 'setting.' + ].join(' ')); + } + traceOut.bingroup = groupName; + + // N.B. no need to worry about 2dMap case + // (where both bin direction are set in each trace) + // as 2dMap trace never "have to match" + fillBinOpts(traceOut, groupName, orientation2binDir(traceOut)); + } + } + + // setup binOpts for traces that can but don't have to match, + // notice that these traces can be matched with traces that have to match + for(i = 0; i < otherTracesList.length; i++) { + traceOut = otherTracesList[i]; + + var binGroup = coerce('bingroup'); + + if(traceIs(traceOut, '2dMap')) { + for(k = 0; k < 2; k++) { + binDir = BINDIRECTIONS[k]; + var binGroupInDir = coerce(binDir + 'bingroup', + binGroup ? binGroup + '__' + binDir : null + ); + fillBinOpts(traceOut, binGroupInDir, binDir); + } + } else { + fillBinOpts(traceOut, binGroup, orientation2binDir(traceOut)); + } + } + + // coerce bin attrs! + for(groupName in allBinOpts) { + var binOpts = allBinOpts[groupName]; + traces = binOpts.traces; + + for(j = 0; j < BINATTRS.length; j++) { + var attrSpec = BINATTRS[j]; + var attr = attrSpec.name; + var aStr; + var autoVals; + + // nbins(x|y) is moot if we have a size. This depends on + // nbins coming after size in binAttrs. + if(attr === 'nbins' && binOpts.sizeFound) continue; + + for(i = 0; i < traces.length; i++) { + traceOut = traces[i]; + binDir = binOpts.dirs[i]; + aStr = attrSpec.aStr[binDir]; + + if(nestedProperty(traceOut._input, aStr).get() !== undefined) { + binOpts[attr] = coerce(aStr); + binOpts[attr + 'Found'] = true; + break; + } + + autoVals = (traceOut._autoBin || {})[binDir] || {}; + if(autoVals[attr]) { + // if this is the *first* autoval + nestedProperty(traceOut, aStr).set(autoVals[attr]); + } + } + + // start and end we need to coerce anyway, after having collected the + // first of each into binOpts, in case a trace wants to restrict its + // data to a certain range + if(attr === 'start' || attr === 'end') { + for(; i < traces.length; i++) { + traceOut = traces[i]; + if(traceOut['_' + binDir + 'bingroup']) { + autoVals = (traceOut._autoBin || {})[binDir] || {}; + coerce(aStr, autoVals[attr]); + } + } + } + + if(attr === 'nbins' && !binOpts.sizeFound && !binOpts.nbinsFound) { + traceOut = traces[0]; + binOpts[attr] = coerce(aStr); + } + } + } +}; + +},{"../../lib":285,"../../plots/cartesian/axis_ids":335,"../../plots/cartesian/constraints":339,"../../registry":373,"../bar/defaults":387}],457:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../../components/color'); + +var handleStyleDefaults = _dereq_('../bar/style_defaults'); +var attributes = _dereq_('./attributes'); + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var x = coerce('x'); + var y = coerce('y'); + + var cumulative = coerce('cumulative.enabled'); + if(cumulative) { + coerce('cumulative.direction'); + coerce('cumulative.currentbin'); + } + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + coerce('xhoverformat'); + coerce('yhoverformat'); + + var orientation = coerce('orientation', (y && !x) ? 'h' : 'v'); + var sampleLetter = orientation === 'v' ? 'x' : 'y'; + var aggLetter = orientation === 'v' ? 'y' : 'x'; + + var len = (x && y) ? + Math.min(Lib.minRowLength(x) && Lib.minRowLength(y)) : + Lib.minRowLength(traceOut[sampleLetter] || []); + + if(!len) { + traceOut.visible = false; + return; + } + + traceOut._length = len; + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + + var hasAggregationData = traceOut[aggLetter]; + if(hasAggregationData) coerce('histfunc'); + coerce('histnorm'); + + // Note: bin defaults are now handled in Histogram.crossTraceDefaults + // autobin(x|y) are only included here to appease Plotly.validate + coerce('autobin' + sampleLetter); + + handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); + + var lineColor = (traceOut.marker.line || {}).color; + + // override defaultColor for error bars with defaultLine + var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults'); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'}); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'}); +}; + +},{"../../components/color":155,"../../lib":285,"../../registry":373,"../bar/style_defaults":398,"./attributes":449}],458:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function eventData(out, pt, trace, cd, pointNumber) { + // standard cartesian event data + out.x = 'xVal' in pt ? pt.xVal : pt.x; + out.y = 'yVal' in pt ? pt.yVal : pt.y; + + // for 2d histograms + if('zLabelVal' in pt) out.z = pt.zLabelVal; + + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + // specific to histogram - CDFs do not have pts (yet?) + if(!(trace.cumulative || {}).enabled) { + var pts = Array.isArray(pointNumber) ? + cd[0].pts[pointNumber[0]][pointNumber[1]] : + cd[pointNumber].pts; + + out.pointNumbers = pts; + out.binNumber = out.pointNumber; + delete out.pointNumber; + delete out.pointIndex; + + var pointIndices; + if(trace._indexToPoints) { + pointIndices = []; + for(var i = 0; i < pts.length; i++) { + pointIndices = pointIndices.concat(trace._indexToPoints[pts[i]]); + } + } else { + pointIndices = pts; + } + + out.pointIndices = pointIndices; + } + + return out; +}; + +},{}],459:[function(_dereq_,module,exports){ +'use strict'; + +var barHover = _dereq_('../bar/hover').hoverPoints; +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) { + var pts = barHover(pointData, xval, yval, hovermode, opts); + + if(!pts) return; + + pointData = pts[0]; + var di = pointData.cd[pointData.index]; + var trace = pointData.cd[0].trace; + + if(!trace.cumulative.enabled) { + var posLetter = trace.orientation === 'h' ? 'y' : 'x'; + + pointData[posLetter + 'Label'] = hoverLabelText(pointData[posLetter + 'a'], [di.ph0, di.ph1], trace[posLetter + 'hoverformat']); + } + + return pts; +}; + +},{"../../plots/cartesian/axes":331,"../bar/hover":390}],460:[function(_dereq_,module,exports){ +'use strict'; + +/** + * Histogram has its own attribute, defaults and calc steps, + * but uses bar's plot to display + * and bar's crossTraceCalc (formerly known as setPositions) for stacking and grouping + */ + +/** + * histogram errorBarsOK is debatable, but it's put in for backward compat. + * there are use cases for it - sqrt for a simple histogram works right now, + * constant and % work but they're not so meaningful. I guess it could be cool + * to allow quadrature combination of errors in summed histograms... + */ + +module.exports = { + attributes: _dereq_('./attributes'), + layoutAttributes: _dereq_('../bar/layout_attributes'), + supplyDefaults: _dereq_('./defaults'), + crossTraceDefaults: _dereq_('./cross_trace_defaults'), + supplyLayoutDefaults: _dereq_('../bar/layout_defaults'), + calc: _dereq_('./calc').calc, + crossTraceCalc: _dereq_('../bar/cross_trace_calc').crossTraceCalc, + plot: _dereq_('../bar/plot').plot, + layerName: 'barlayer', + style: _dereq_('../bar/style').style, + styleOnSelect: _dereq_('../bar/style').styleOnSelect, + colorbar: _dereq_('../scatter/marker_colorbar'), + hoverPoints: _dereq_('./hover'), + selectPoints: _dereq_('../bar/select'), + eventData: _dereq_('./event_data'), + + moduleType: 'trace', + name: 'histogram', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['bar-like', 'cartesian', 'svg', 'bar', 'histogram', 'oriented', 'errorBarsOK', 'showLegend'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"../bar/cross_trace_calc":386,"../bar/layout_attributes":392,"../bar/layout_defaults":393,"../bar/plot":394,"../bar/select":395,"../bar/style":397,"../scatter/marker_colorbar":512,"./attributes":449,"./calc":454,"./cross_trace_defaults":456,"./defaults":457,"./event_data":458,"./hover":459}],461:[function(_dereq_,module,exports){ +'use strict'; + + +module.exports = { + percent: function(size, total) { + var nMax = size.length; + var norm = 100 / total; + for(var n = 0; n < nMax; n++) size[n] *= norm; + }, + probability: function(size, total) { + var nMax = size.length; + for(var n = 0; n < nMax; n++) size[n] /= total; + }, + density: function(size, total, inc, yinc) { + var nMax = size.length; + yinc = yinc || 1; + for(var n = 0; n < nMax; n++) size[n] *= inc[n] * yinc; + }, + 'probability density': function(size, total, inc, yinc) { + var nMax = size.length; + if(yinc) total /= yinc; + for(var n = 0; n < nMax; n++) size[n] *= inc[n] / total; + } +}; + +},{}],462:[function(_dereq_,module,exports){ +'use strict'; + +var histogramAttrs = _dereq_('../histogram/attributes'); +var makeBinAttrs = _dereq_('../histogram/bin_attributes'); +var heatmapAttrs = _dereq_('../heatmap/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = extendFlat( + { + x: histogramAttrs.x, + y: histogramAttrs.y, + + z: { + valType: 'data_array', + editType: 'calc', + }, + marker: { + color: { + valType: 'data_array', + editType: 'calc', + }, + editType: 'calc' + }, + + histnorm: histogramAttrs.histnorm, + histfunc: histogramAttrs.histfunc, + nbinsx: histogramAttrs.nbinsx, + xbins: makeBinAttrs('x'), + nbinsy: histogramAttrs.nbinsy, + ybins: makeBinAttrs('y'), + autobinx: histogramAttrs.autobinx, + autobiny: histogramAttrs.autobiny, + + bingroup: extendFlat({}, histogramAttrs.bingroup, { + }), + xbingroup: extendFlat({}, histogramAttrs.bingroup, { + }), + ybingroup: extendFlat({}, histogramAttrs.bingroup, { + }), + + xgap: heatmapAttrs.xgap, + ygap: heatmapAttrs.ygap, + zsmooth: heatmapAttrs.zsmooth, + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + zhoverformat: axisHoverFormat('z', 1), + hovertemplate: hovertemplateAttrs({}, {keys: 'z'}), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) + }, + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) +); + +},{"../../components/colorscale/attributes":162,"../../lib/extend":279,"../../plots/attributes":327,"../../plots/cartesian/axis_format_attributes":334,"../../plots/template_attributes":368,"../heatmap/attributes":434,"../histogram/attributes":449,"../histogram/bin_attributes":451}],463:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +var binFunctions = _dereq_('../histogram/bin_functions'); +var normFunctions = _dereq_('../histogram/norm_functions'); +var doAvg = _dereq_('../histogram/average'); +var getBinSpanLabelRound = _dereq_('../histogram/bin_label_vals'); +var calcAllAutoBins = _dereq_('../histogram/calc').calcAllAutoBins; + +module.exports = function calc(gd, trace) { + var xa = Axes.getFromId(gd, trace.xaxis); + var ya = Axes.getFromId(gd, trace.yaxis); + + var xcalendar = trace.xcalendar; + var ycalendar = trace.ycalendar; + var xr2c = function(v) { return xa.r2c(v, 0, xcalendar); }; + var yr2c = function(v) { return ya.r2c(v, 0, ycalendar); }; + var xc2r = function(v) { return xa.c2r(v, 0, xcalendar); }; + var yc2r = function(v) { return ya.c2r(v, 0, ycalendar); }; + + var i, j, n, m; + + // calculate the bins + var xBinsAndPos = calcAllAutoBins(gd, trace, xa, 'x'); + var xBinSpec = xBinsAndPos[0]; + var xPos0 = xBinsAndPos[1]; + var yBinsAndPos = calcAllAutoBins(gd, trace, ya, 'y'); + var yBinSpec = yBinsAndPos[0]; + var yPos0 = yBinsAndPos[1]; + + var serieslen = trace._length; + if(xPos0.length > serieslen) xPos0.splice(serieslen, xPos0.length - serieslen); + if(yPos0.length > serieslen) yPos0.splice(serieslen, yPos0.length - serieslen); + + // make the empty bin array & scale the map + var z = []; + var onecol = []; + var zerocol = []; + var nonuniformBinsX = typeof xBinSpec.size === 'string'; + var nonuniformBinsY = typeof yBinSpec.size === 'string'; + var xEdges = []; + var yEdges = []; + var xbins = nonuniformBinsX ? xEdges : xBinSpec; + var ybins = nonuniformBinsY ? yEdges : yBinSpec; + var total = 0; + var counts = []; + var inputPoints = []; + var norm = trace.histnorm; + var func = trace.histfunc; + var densitynorm = norm.indexOf('density') !== -1; + var extremefunc = func === 'max' || func === 'min'; + var sizeinit = extremefunc ? null : 0; + var binfunc = binFunctions.count; + var normfunc = normFunctions[norm]; + var doavg = false; + var xinc = []; + var yinc = []; + + // set a binning function other than count? + // for binning functions: check first for 'z', + // then 'mc' in case we had a colored scatter plot + // and want to transfer these colors to the 2D histo + // TODO: axe this, make it the responsibility of the app changing type? or an impliedEdit? + var rawCounterData = ('z' in trace) ? + trace.z : + (('marker' in trace && Array.isArray(trace.marker.color)) ? + trace.marker.color : ''); + if(rawCounterData && func !== 'count') { + doavg = func === 'avg'; + binfunc = binFunctions[func]; + } + + // decrease end a little in case of rounding errors + var xBinSize = xBinSpec.size; + var xBinStart = xr2c(xBinSpec.start); + var xBinEnd = xr2c(xBinSpec.end) + + (xBinStart - Axes.tickIncrement(xBinStart, xBinSize, false, xcalendar)) / 1e6; + + for(i = xBinStart; i < xBinEnd; i = Axes.tickIncrement(i, xBinSize, false, xcalendar)) { + onecol.push(sizeinit); + xEdges.push(i); + if(doavg) zerocol.push(0); + } + xEdges.push(i); + + var nx = onecol.length; + var dx = (i - xBinStart) / nx; + var x0 = xc2r(xBinStart + dx / 2); + + var yBinSize = yBinSpec.size; + var yBinStart = yr2c(yBinSpec.start); + var yBinEnd = yr2c(yBinSpec.end) + + (yBinStart - Axes.tickIncrement(yBinStart, yBinSize, false, ycalendar)) / 1e6; + + for(i = yBinStart; i < yBinEnd; i = Axes.tickIncrement(i, yBinSize, false, ycalendar)) { + z.push(onecol.slice()); + yEdges.push(i); + var ipCol = new Array(nx); + for(j = 0; j < nx; j++) ipCol[j] = []; + inputPoints.push(ipCol); + if(doavg) counts.push(zerocol.slice()); + } + yEdges.push(i); + + var ny = z.length; + var dy = (i - yBinStart) / ny; + var y0 = yc2r(yBinStart + dy / 2); + + if(densitynorm) { + xinc = makeIncrements(onecol.length, xbins, dx, nonuniformBinsX); + yinc = makeIncrements(z.length, ybins, dy, nonuniformBinsY); + } + + // for date axes we need bin bounds to be calcdata. For nonuniform bins + // we already have this, but uniform with start/end/size they're still strings. + if(!nonuniformBinsX && xa.type === 'date') xbins = binsToCalc(xr2c, xbins); + if(!nonuniformBinsY && ya.type === 'date') ybins = binsToCalc(yr2c, ybins); + + // put data into bins + var uniqueValsPerX = true; + var uniqueValsPerY = true; + var xVals = new Array(nx); + var yVals = new Array(ny); + var xGapLow = Infinity; + var xGapHigh = Infinity; + var yGapLow = Infinity; + var yGapHigh = Infinity; + for(i = 0; i < serieslen; i++) { + var xi = xPos0[i]; + var yi = yPos0[i]; + n = Lib.findBin(xi, xbins); + m = Lib.findBin(yi, ybins); + if(n >= 0 && n < nx && m >= 0 && m < ny) { + total += binfunc(n, i, z[m], rawCounterData, counts[m]); + inputPoints[m][n].push(i); + + if(uniqueValsPerX) { + if(xVals[n] === undefined) xVals[n] = xi; + else if(xVals[n] !== xi) uniqueValsPerX = false; + } + if(uniqueValsPerY) { + if(yVals[m] === undefined) yVals[m] = yi; + else if(yVals[m] !== yi) uniqueValsPerY = false; + } + + xGapLow = Math.min(xGapLow, xi - xEdges[n]); + xGapHigh = Math.min(xGapHigh, xEdges[n + 1] - xi); + yGapLow = Math.min(yGapLow, yi - yEdges[m]); + yGapHigh = Math.min(yGapHigh, yEdges[m + 1] - yi); + } + } + // normalize, if needed + if(doavg) { + for(m = 0; m < ny; m++) total += doAvg(z[m], counts[m]); + } + if(normfunc) { + for(m = 0; m < ny; m++) normfunc(z[m], total, xinc, yinc[m]); + } + + return { + x: xPos0, + xRanges: getRanges(xEdges, uniqueValsPerX && xVals, xGapLow, xGapHigh, xa, xcalendar), + x0: x0, + dx: dx, + y: yPos0, + yRanges: getRanges(yEdges, uniqueValsPerY && yVals, yGapLow, yGapHigh, ya, ycalendar), + y0: y0, + dy: dy, + z: z, + pts: inputPoints + }; +}; + +function makeIncrements(len, bins, dv, nonuniform) { + var out = new Array(len); + var i; + if(nonuniform) { + for(i = 0; i < len; i++) out[i] = 1 / (bins[i + 1] - bins[i]); + } else { + var inc = 1 / dv; + for(i = 0; i < len; i++) out[i] = inc; + } + return out; +} + +function binsToCalc(r2c, bins) { + return { + start: r2c(bins.start), + end: r2c(bins.end), + size: bins.size + }; +} + +function getRanges(edges, uniqueVals, gapLow, gapHigh, ax, calendar) { + var i; + var len = edges.length - 1; + var out = new Array(len); + var roundFn = getBinSpanLabelRound(gapLow, gapHigh, edges, ax, calendar); + + for(i = 0; i < len; i++) { + var v = (uniqueVals || [])[i]; + out[i] = v === undefined ? + [roundFn(edges[i]), roundFn(edges[i + 1], true)] : + [v, v]; + } + return out; +} + +},{"../../lib":285,"../../plots/cartesian/axes":331,"../histogram/average":450,"../histogram/bin_functions":452,"../histogram/bin_label_vals":453,"../histogram/calc":454,"../histogram/norm_functions":461}],464:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var handleSampleDefaults = _dereq_('./sample_defaults'); +var handleStyleDefaults = _dereq_('../heatmap/style_defaults'); +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + handleSampleDefaults(traceIn, traceOut, coerce, layout); + if(traceOut.visible === false) return; + + handleStyleDefaults(traceIn, traceOut, coerce, layout); + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); + coerce('hovertemplate'); + coerce('xhoverformat'); + coerce('yhoverformat'); +}; + +},{"../../components/colorscale/defaults":165,"../../lib":285,"../heatmap/style_defaults":447,"./attributes":462,"./sample_defaults":467}],465:[function(_dereq_,module,exports){ +'use strict'; + +var heatmapHover = _dereq_('../heatmap/hover'); +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) { + var pts = heatmapHover(pointData, xval, yval, hovermode, opts); + + if(!pts) return; + + pointData = pts[0]; + var indices = pointData.index; + var ny = indices[0]; + var nx = indices[1]; + var cd0 = pointData.cd[0]; + var trace = cd0.trace; + var xRange = cd0.xRanges[nx]; + var yRange = cd0.yRanges[ny]; + + pointData.xLabel = hoverLabelText(pointData.xa, [xRange[0], xRange[1]], trace.xhoverformat); + pointData.yLabel = hoverLabelText(pointData.ya, [yRange[0], yRange[1]], trace.yhoverformat); + + return pts; +}; + +},{"../../plots/cartesian/axes":331,"../heatmap/hover":441}],466:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + crossTraceDefaults: _dereq_('../histogram/cross_trace_defaults'), + calc: _dereq_('../heatmap/calc'), + plot: _dereq_('../heatmap/plot'), + layerName: 'heatmaplayer', + colorbar: _dereq_('../heatmap/colorbar'), + style: _dereq_('../heatmap/style'), + hoverPoints: _dereq_('./hover'), + eventData: _dereq_('../histogram/event_data'), + + moduleType: 'trace', + name: 'histogram2d', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', '2dMap', 'histogram', 'showLegend'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"../heatmap/calc":435,"../heatmap/colorbar":437,"../heatmap/plot":445,"../heatmap/style":446,"../histogram/cross_trace_defaults":456,"../histogram/event_data":458,"./attributes":462,"./defaults":464,"./hover":465}],467:[function(_dereq_,module,exports){ +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); + +module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout) { + var x = coerce('x'); + var y = coerce('y'); + var xlen = Lib.minRowLength(x); + var ylen = Lib.minRowLength(y); + + // we could try to accept x0 and dx, etc... + // but that's a pretty weird use case. + // for now require both x and y explicitly specified. + if(!xlen || !ylen) { + traceOut.visible = false; + return; + } + + traceOut._length = Math.min(xlen, ylen); + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + + // if marker.color is an array, we can use it in aggregation instead of z + var hasAggregationData = coerce('z') || coerce('marker.color'); + + if(hasAggregationData) coerce('histfunc'); + coerce('histnorm'); + + // Note: bin defaults are now handled in Histogram2D.crossTraceDefaults + // autobin(x|y) are only included here to appease Plotly.validate + coerce('autobinx'); + coerce('autobiny'); +}; + +},{"../../lib":285,"../../registry":373}],468:[function(_dereq_,module,exports){ +'use strict'; + +var histogram2dAttrs = _dereq_('../histogram2d/attributes'); +var contourAttrs = _dereq_('../contour/attributes'); +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = extendFlat({ + x: histogram2dAttrs.x, + y: histogram2dAttrs.y, + z: histogram2dAttrs.z, + marker: histogram2dAttrs.marker, + + histnorm: histogram2dAttrs.histnorm, + histfunc: histogram2dAttrs.histfunc, + nbinsx: histogram2dAttrs.nbinsx, + xbins: histogram2dAttrs.xbins, + nbinsy: histogram2dAttrs.nbinsy, + ybins: histogram2dAttrs.ybins, + autobinx: histogram2dAttrs.autobinx, + autobiny: histogram2dAttrs.autobiny, + + bingroup: histogram2dAttrs.bingroup, + xbingroup: histogram2dAttrs.xbingroup, + ybingroup: histogram2dAttrs.ybingroup, + + autocontour: contourAttrs.autocontour, + ncontours: contourAttrs.ncontours, + contours: contourAttrs.contours, + line: { + color: contourAttrs.line.color, + width: extendFlat({}, contourAttrs.line.width, { + dflt: 0.5, + }), + dash: contourAttrs.line.dash, + smoothing: contourAttrs.line.smoothing, + editType: 'plot' + }, + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + zhoverformat: axisHoverFormat('z', 1), + hovertemplate: histogram2dAttrs.hovertemplate +}, + colorScaleAttrs('', { + cLetter: 'z', + editTypeOverride: 'calc' + }) +); + +},{"../../components/colorscale/attributes":162,"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../contour/attributes":412,"../histogram2d/attributes":462}],469:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var handleSampleDefaults = _dereq_('../histogram2d/sample_defaults'); +var handleContoursDefaults = _dereq_('../contour/contours_defaults'); +var handleStyleDefaults = _dereq_('../contour/style_defaults'); +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + function coerce2(attr) { + return Lib.coerce2(traceIn, traceOut, attributes, attr); + } + + handleSampleDefaults(traceIn, traceOut, coerce, layout); + if(traceOut.visible === false) return; + + handleContoursDefaults(traceIn, traceOut, coerce, coerce2); + handleStyleDefaults(traceIn, traceOut, coerce, layout); + coerce('hovertemplate'); + coerce('xhoverformat'); + coerce('yhoverformat'); +}; + +},{"../../lib":285,"../contour/contours_defaults":419,"../contour/style_defaults":433,"../histogram2d/sample_defaults":467,"./attributes":468}],470:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + crossTraceDefaults: _dereq_('../histogram/cross_trace_defaults'), + calc: _dereq_('../contour/calc'), + plot: _dereq_('../contour/plot').plot, + layerName: 'contourlayer', + style: _dereq_('../contour/style'), + colorbar: _dereq_('../contour/colorbar'), + hoverPoints: _dereq_('../contour/hover'), + + moduleType: 'trace', + name: 'histogram2dcontour', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', '2dMap', 'contour', 'histogram', 'showLegend'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"../contour/calc":413,"../contour/colorbar":415,"../contour/hover":425,"../contour/plot":430,"../contour/style":432,"../histogram/cross_trace_defaults":456,"./attributes":468,"./defaults":469}],471:[function(_dereq_,module,exports){ +'use strict'; + +var baseAttrs = _dereq_('../../plots/attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var colormodel = _dereq_('./constants').colormodel; + +var cm = ['rgb', 'rgba', 'rgba256', 'hsl', 'hsla']; +var zminDesc = []; +var zmaxDesc = []; +for(var i = 0; i < cm.length; i++) { + var cr = colormodel[cm[i]]; + zminDesc.push('For the `' + cm[i] + '` colormodel, it is [' + (cr.zminDflt || cr.min).join(', ') + '].'); + zmaxDesc.push('For the `' + cm[i] + '` colormodel, it is [' + (cr.zmaxDflt || cr.max).join(', ') + '].'); +} + +module.exports = extendFlat({ + source: { + valType: 'string', + editType: 'calc', + }, + z: { + valType: 'data_array', + editType: 'calc', + }, + colormodel: { + valType: 'enumerated', + values: cm, + editType: 'calc', + }, + zsmooth: { + valType: 'enumerated', + values: ['fast', false], + dflt: false, + editType: 'plot', + }, + zmin: { + valType: 'info_array', + items: [ + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'} + ], + editType: 'calc', + }, + zmax: { + valType: 'info_array', + items: [ + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'} + ], + editType: 'calc', + }, + x0: { + valType: 'any', + dflt: 0, + editType: 'calc+clearAxisTypes', + }, + y0: { + valType: 'any', + dflt: 0, + editType: 'calc+clearAxisTypes', + }, + dx: { + valType: 'number', + dflt: 1, + editType: 'calc', + }, + dy: { + valType: 'number', + dflt: 1, + editType: 'calc', + }, + text: { + valType: 'data_array', + editType: 'plot', + }, + hovertext: { + valType: 'data_array', + editType: 'plot', + }, + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { + flags: ['x', 'y', 'z', 'color', 'name', 'text'], + dflt: 'x+y+z+text+name' + }), + hovertemplate: hovertemplateAttrs({}, { + keys: ['z', 'color', 'colormodel'] + }), + + transforms: undefined +}); + +},{"../../lib/extend":279,"../../plots/attributes":327,"../../plots/template_attributes":368,"./constants":473}],472:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var constants = _dereq_('./constants'); +var isNumeric = _dereq_('fast-isnumeric'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var maxRowLength = _dereq_('../../lib').maxRowLength; +var getImageSize = _dereq_('./helpers').getImageSize; + +module.exports = function calc(gd, trace) { + var h; + var w; + if(trace._hasZ) { + h = trace.z.length; + w = maxRowLength(trace.z); + } else if(trace._hasSource) { + var size = getImageSize(trace.source); + h = size.height; + w = size.width; + } + + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + + var x0 = xa.d2c(trace.x0) - trace.dx / 2; + var y0 = ya.d2c(trace.y0) - trace.dy / 2; + + // Set axis range + var i; + var xrange = [x0, x0 + w * trace.dx]; + var yrange = [y0, y0 + h * trace.dy]; + if(xa && xa.type === 'log') for(i = 0; i < w; i++) xrange.push(x0 + i * trace.dx); + if(ya && ya.type === 'log') for(i = 0; i < h; i++) yrange.push(y0 + i * trace.dy); + trace._extremes[xa._id] = Axes.findExtremes(xa, xrange); + trace._extremes[ya._id] = Axes.findExtremes(ya, yrange); + trace._scaler = makeScaler(trace); + + var cd0 = { + x0: x0, + y0: y0, + z: trace.z, + w: w, + h: h + }; + return [cd0]; +}; + +function scale(zero, ratio, min, max) { + return function(c) { + return Lib.constrain((c - zero) * ratio, min, max); + }; +} + +function constrain(min, max) { + return function(c) { return Lib.constrain(c, min, max);}; +} + +// Generate a function to scale color components according to zmin/zmax and the colormodel +function makeScaler(trace) { + var cr = constants.colormodel[trace.colormodel]; + var colormodel = (cr.colormodel || trace.colormodel); + var n = colormodel.length; + + trace._sArray = []; + // Loop over all color components + for(var k = 0; k < n; k++) { + if(cr.min[k] !== trace.zmin[k] || cr.max[k] !== trace.zmax[k]) { + trace._sArray.push(scale( + trace.zmin[k], + (cr.max[k] - cr.min[k]) / (trace.zmax[k] - trace.zmin[k]), + cr.min[k], + cr.max[k] + )); + } else { + trace._sArray.push(constrain(cr.min[k], cr.max[k])); + } + } + + return function(pixel) { + var c = pixel.slice(0, n); + for(var k = 0; k < n; k++) { + var ck = c[k]; + if(!isNumeric(ck)) return false; + c[k] = trace._sArray[k](ck); + } + return c; + }; +} + +},{"../../lib":285,"../../plots/cartesian/axes":331,"./constants":473,"./helpers":476,"fast-isnumeric":31}],473:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + colormodel: { + // min and max define the numerical range accepted in CSS + // If z(min|max)Dflt are not defined, z(min|max) will default to min/max + rgb: { + min: [0, 0, 0], + max: [255, 255, 255], + fmt: function(c) {return c.slice(0, 3);}, + suffix: ['', '', ''] + }, + rgba: { + min: [0, 0, 0, 0], + max: [255, 255, 255, 1], + fmt: function(c) {return c.slice(0, 4);}, + suffix: ['', '', '', ''] + }, + rgba256: { + colormodel: 'rgba', // because rgba256 is not an accept colormodel in CSS + zminDflt: [0, 0, 0, 0], + zmaxDflt: [255, 255, 255, 255], + min: [0, 0, 0, 0], + max: [255, 255, 255, 1], + fmt: function(c) {return c.slice(0, 4);}, + suffix: ['', '', '', ''] + }, + hsl: { + min: [0, 0, 0], + max: [360, 100, 100], + fmt: function(c) { + var p = c.slice(0, 3); + p[1] = p[1] + '%'; + p[2] = p[2] + '%'; + return p; + }, + suffix: ['°', '%', '%'] + }, + hsla: { + min: [0, 0, 0, 0], + max: [360, 100, 100, 1], + fmt: function(c) { + var p = c.slice(0, 4); + p[1] = p[1] + '%'; + p[2] = p[2] + '%'; + return p; + }, + suffix: ['°', '%', '%', ''] + } + }, + // For pixelated image rendering + // http://phrogz.net/tmp/canvas_image_zoom.html + // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering + pixelatedStyle: [ + 'image-rendering: optimizeSpeed', + 'image-rendering: -moz-crisp-edges', + 'image-rendering: -o-crisp-edges', + 'image-rendering: -webkit-optimize-contrast', + 'image-rendering: optimize-contrast', + 'image-rendering: crisp-edges', + 'image-rendering: pixelated', + '' + ].join('; ') +}; + +},{}],474:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var attributes = _dereq_('./attributes'); +var constants = _dereq_('./constants'); +var dataUri = _dereq_('../../snapshot/helpers').IMAGE_URL_PREFIX; + +module.exports = function supplyDefaults(traceIn, traceOut) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + coerce('source'); + // sanitize source to only allow for data URI representing images + if(traceOut.source && !traceOut.source.match(dataUri)) delete traceOut.source; + traceOut._hasSource = !!traceOut.source; + + var z = coerce('z'); + traceOut._hasZ = !(z === undefined || !z.length || !z[0] || !z[0].length); + if(!traceOut._hasZ && !traceOut._hasSource) { + traceOut.visible = false; + return; + } + + coerce('x0'); + coerce('y0'); + coerce('dx'); + coerce('dy'); + + var cm; + if(traceOut._hasZ) { + coerce('colormodel', 'rgb'); + cm = constants.colormodel[traceOut.colormodel]; + coerce('zmin', (cm.zminDflt || cm.min)); + coerce('zmax', (cm.zmaxDflt || cm.max)); + } else if(traceOut._hasSource) { + traceOut.colormodel = 'rgba256'; + cm = constants.colormodel[traceOut.colormodel]; + traceOut.zmin = cm.zminDflt; + traceOut.zmax = cm.zmaxDflt; + } + + coerce('zsmooth'); + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + + traceOut._length = null; +}; + +},{"../../lib":285,"../../snapshot/helpers":377,"./attributes":471,"./constants":473}],475:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function eventData(out, pt) { + if('xVal' in pt) out.x = pt.xVal; + if('yVal' in pt) out.y = pt.yVal; + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + out.color = pt.color; + out.colormodel = pt.trace.colormodel; + if(!out.z) out.z = pt.color; + return out; +}; + +},{}],476:[function(_dereq_,module,exports){ +'use strict'; + +var probeSync = _dereq_('probe-image-size/sync'); +var dataUri = _dereq_('../../snapshot/helpers').IMAGE_URL_PREFIX; +var Buffer = _dereq_('buffer/').Buffer; // note: the trailing slash is important! + +exports.getImageSize = function(src) { + var data = src.replace(dataUri, ''); + var buff = new Buffer(data, 'base64'); + return probeSync(buff); +}; + +},{"../../snapshot/helpers":377,"buffer/":28,"probe-image-size/sync":95}],477:[function(_dereq_,module,exports){ +'use strict'; + +var Fx = _dereq_('../../components/fx'); +var Lib = _dereq_('../../lib'); +var constants = _dereq_('./constants'); + +module.exports = function hoverPoints(pointData, xval, yval) { + var cd0 = pointData.cd[0]; + var trace = cd0.trace; + var xa = pointData.xa; + var ya = pointData.ya; + + // Return early if not on image + if(Fx.inbox(xval - cd0.x0, xval - (cd0.x0 + cd0.w * trace.dx), 0) > 0 || + Fx.inbox(yval - cd0.y0, yval - (cd0.y0 + cd0.h * trace.dy), 0) > 0) { + return; + } + + // Find nearest pixel's index + var nx = Math.floor((xval - cd0.x0) / trace.dx); + var ny = Math.floor(Math.abs(yval - cd0.y0) / trace.dy); + + var pixel; + if(trace._hasZ) { + pixel = cd0.z[ny][nx]; + } else if(trace._hasSource) { + pixel = trace._canvas.el.getContext('2d').getImageData(nx, ny, 1, 1).data; + } + + // return early if pixel is undefined + if(!pixel) return; + + var hoverinfo = cd0.hi || trace.hoverinfo; + var fmtColor; + if(hoverinfo) { + var parts = hoverinfo.split('+'); + if(parts.indexOf('all') !== -1) parts = ['color']; + if(parts.indexOf('color') !== -1) fmtColor = true; + } + + var cr = constants.colormodel[trace.colormodel]; + var colormodel = cr.colormodel || trace.colormodel; + var dims = colormodel.length; + var c = trace._scaler(pixel); + var s = cr.suffix; + + var colorstring = []; + if(trace.hovertemplate || fmtColor) { + colorstring.push('[' + [c[0] + s[0], c[1] + s[1], c[2] + s[2]].join(', ')); + if(dims === 4) colorstring.push(', ' + c[3] + s[3]); + colorstring.push(']'); + colorstring = colorstring.join(''); + pointData.extraText = colormodel.toUpperCase() + ': ' + colorstring; + } + + var text; + if(Array.isArray(trace.hovertext) && Array.isArray(trace.hovertext[ny])) { + text = trace.hovertext[ny][nx]; + } else if(Array.isArray(trace.text) && Array.isArray(trace.text[ny])) { + text = trace.text[ny][nx]; + } + + // TODO: for color model with 3 dims, display something useful for hovertemplate `%{color[3]}` + var py = ya.c2p(cd0.y0 + (ny + 0.5) * trace.dy); + var xVal = cd0.x0 + (nx + 0.5) * trace.dx; + var yVal = cd0.y0 + (ny + 0.5) * trace.dy; + var zLabel = '[' + pixel.slice(0, trace.colormodel.length).join(', ') + ']'; + return [Lib.extendFlat(pointData, { + index: [ny, nx], + x0: xa.c2p(cd0.x0 + nx * trace.dx), + x1: xa.c2p(cd0.x0 + (nx + 1) * trace.dx), + y0: py, + y1: py, + color: c, + xVal: xVal, + xLabelVal: xVal, + yVal: yVal, + yLabelVal: yVal, + zLabelVal: zLabel, + text: text, + hovertemplateLabels: { + 'zLabel': zLabel, + 'colorLabel': colorstring, + 'color[0]Label': c[0] + s[0], + 'color[1]Label': c[1] + s[1], + 'color[2]Label': c[2] + s[2], + 'color[3]Label': c[3] + s[3] + } + })]; +}; + +},{"../../components/fx":195,"../../lib":285,"./constants":473}],478:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + calc: _dereq_('./calc'), + plot: _dereq_('./plot'), + style: _dereq_('./style'), + hoverPoints: _dereq_('./hover'), + eventData: _dereq_('./event_data'), + + moduleType: 'trace', + name: 'image', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', '2dMap', 'noSortingByValue'], + animatable: false, + meta: { + } +}; + +},{"../../plots/cartesian":345,"./attributes":471,"./calc":472,"./defaults":474,"./event_data":475,"./hover":477,"./plot":479,"./style":480}],479:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Lib = _dereq_('../../lib'); +var strTranslate = Lib.strTranslate; +var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces'); +var constants = _dereq_('./constants'); + +var unsupportedBrowsers = Lib.isIOS() || Lib.isSafari() || Lib.isIE(); + +module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var supportsPixelatedImage = !(unsupportedBrowsers || gd._context._exportedPlot); + + Lib.makeTraceGroups(imageLayer, cdimage, 'im').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + var realImage = ( + ((trace.zsmooth === 'fast') || (trace.zsmooth === false && supportsPixelatedImage)) && + !trace._hasZ && trace._hasSource && xa.type === 'linear' && ya.type === 'linear' + ); + trace._realImage = realImage; + + var z = cd0.z; + var x0 = cd0.x0; + var y0 = cd0.y0; + var w = cd0.w; + var h = cd0.h; + var dx = trace.dx; + var dy = trace.dy; + + var left, right, temp, top, bottom, i; + // in case of log of a negative + i = 0; + while(left === undefined && i < w) { + left = xa.c2p(x0 + i * dx); + i++; + } + i = w; + while(right === undefined && i > 0) { + right = xa.c2p(x0 + i * dx); + i--; + } + i = 0; + while(top === undefined && i < h) { + top = ya.c2p(y0 + i * dy); + i++; + } + i = h; + while(bottom === undefined && i > 0) { + bottom = ya.c2p(y0 + i * dy); + i--; + } + + if(right < left) { + temp = right; + right = left; + left = temp; + } + + if(bottom < top) { + temp = top; + top = bottom; + bottom = temp; + } + + // Reduce image size when zoomed in to save memory + if(!realImage) { + var extra = 0.5; // half the axis size + left = Math.max(-extra * xa._length, left); + right = Math.min((1 + extra) * xa._length, right); + top = Math.max(-extra * ya._length, top); + bottom = Math.min((1 + extra) * ya._length, bottom); + } + + var imageWidth = Math.round(right - left); + var imageHeight = Math.round(bottom - top); + + // if image is entirely off-screen, don't even draw it + var isOffScreen = (imageWidth <= 0 || imageHeight <= 0); + if(isOffScreen) { + var noImage = plotGroup.selectAll('image').data([]); + noImage.exit().remove(); + return; + } + + // Create a new canvas and draw magnified pixels on it + function drawMagnifiedPixelsOnCanvas(readPixel) { + var canvas = document.createElement('canvas'); + canvas.width = imageWidth; + canvas.height = imageHeight; + var context = canvas.getContext('2d'); + + var ipx = function(i) {return Lib.constrain(Math.round(xa.c2p(x0 + i * dx) - left), 0, imageWidth);}; + var jpx = function(j) {return Lib.constrain(Math.round(ya.c2p(y0 + j * dy) - top), 0, imageHeight);}; + + var cr = constants.colormodel[trace.colormodel]; + var colormodel = (cr.colormodel || trace.colormodel); + var fmt = cr.fmt; + var c; + for(i = 0; i < cd0.w; i++) { + var ipx0 = ipx(i); var ipx1 = ipx(i + 1); + if(ipx1 === ipx0 || isNaN(ipx1) || isNaN(ipx0)) continue; + for(var j = 0; j < cd0.h; j++) { + var jpx0 = jpx(j); var jpx1 = jpx(j + 1); + if(jpx1 === jpx0 || isNaN(jpx1) || isNaN(jpx0) || !readPixel(i, j)) continue; + c = trace._scaler(readPixel(i, j)); + if(c) { + context.fillStyle = colormodel + '(' + fmt(c).join(',') + ')'; + } else { + // Return a transparent pixel + context.fillStyle = 'rgba(0,0,0,0)'; + } + context.fillRect(ipx0, jpx0, ipx1 - ipx0, jpx1 - jpx0); + } + } + + return canvas; + } + + var image3 = plotGroup.selectAll('image') + .data([cd]); + + image3.enter().append('svg:image').attr({ + xmlns: xmlnsNamespaces.svg, + preserveAspectRatio: 'none' + }); + + image3.exit().remove(); + + var style = (trace.zsmooth === false) ? constants.pixelatedStyle : ''; + + if(realImage) { + var xRange = Lib.simpleMap(xa.range, xa.r2l); + var yRange = Lib.simpleMap(ya.range, ya.r2l); + + var flipX = xRange[1] < xRange[0]; + var flipY = yRange[1] > yRange[0]; + if(flipX || flipY) { + var tx = left + imageWidth / 2; + var ty = top + imageHeight / 2; + style += 'transform:' + + strTranslate(tx + 'px', ty + 'px') + + 'scale(' + (flipX ? -1 : 1) + ',' + (flipY ? -1 : 1) + ')' + + strTranslate(-tx + 'px', -ty + 'px') + ';'; + } + } + image3.attr('style', style); + + var p = new Promise(function(resolve) { + if(trace._hasZ) { + resolve(); + } else if(trace._hasSource) { + // Check if canvas already exists and has the right data + if( + trace._canvas && + trace._canvas.el.width === w && + trace._canvas.el.height === h && + trace._canvas.source === trace.source + ) { + resolve(); + } else { + // Create a canvas and transfer image onto it to access pixel information + var canvas = document.createElement('canvas'); + canvas.width = w; + canvas.height = h; + var context = canvas.getContext('2d'); + + trace._image = trace._image || new Image(); + var image = trace._image; + image.onload = function() { + context.drawImage(image, 0, 0); + trace._canvas = { + el: canvas, + source: trace.source + }; + resolve(); + }; + image.setAttribute('src', trace.source); + } + } + }) + .then(function() { + var href, canvas; + if(trace._hasZ) { + canvas = drawMagnifiedPixelsOnCanvas(function(i, j) {return z[j][i];}); + href = canvas.toDataURL('image/png'); + } else if(trace._hasSource) { + if(realImage) { + href = trace.source; + } else { + var context = trace._canvas.el.getContext('2d'); + var data = context.getImageData(0, 0, w, h).data; + canvas = drawMagnifiedPixelsOnCanvas(function(i, j) { + var index = 4 * (j * w + i); + return [ + data[index], + data[index + 1], + data[index + 2], + data[index + 3] + ]; + }); + href = canvas.toDataURL('image/png'); + } + } + + image3.attr({ + 'xlink:href': href, + height: imageHeight, + width: imageWidth, + x: left, + y: top + }); + }); + + gd._promises.push(p); + }); +}; + +},{"../../constants/xmlns_namespaces":266,"../../lib":285,"./constants":473,"@plotly/d3":20}],480:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +module.exports = function style(gd) { + d3.select(gd).selectAll('.im image') + .style('opacity', function(d) { + return d[0].trace.opacity; + }); +}; + +},{"@plotly/d3":20}],481:[function(_dereq_,module,exports){ +'use strict'; + +var baseAttrs = _dereq_('../../plots/attributes'); +var domainAttrs = _dereq_('../../plots/domain').attributes; +var fontAttrs = _dereq_('../../plots/font_attributes'); +var colorAttrs = _dereq_('../../components/color/attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var textFontAttrs = fontAttrs({ + editType: 'plot', + arrayOk: true, + colorEditType: 'plot', +}); + +module.exports = { + labels: { + valType: 'data_array', + editType: 'calc', + }, + // equivalent of x0 and dx, if label is missing + label0: { + valType: 'number', + dflt: 0, + editType: 'calc', + }, + dlabel: { + valType: 'number', + dflt: 1, + editType: 'calc', + }, + + values: { + valType: 'data_array', + editType: 'calc', + }, + + marker: { + colors: { + valType: 'data_array', // TODO 'color_array' ? + editType: 'calc', + }, + + line: { + color: { + valType: 'color', + dflt: colorAttrs.defaultLine, + arrayOk: true, + editType: 'style', + }, + width: { + valType: 'number', + min: 0, + dflt: 0, + arrayOk: true, + editType: 'style', + }, + editType: 'calc' + }, + editType: 'calc' + }, + + text: { + valType: 'data_array', + editType: 'plot', + }, + hovertext: { + valType: 'string', + dflt: '', + arrayOk: true, + editType: 'style', + }, + +// 'see eg:' +// 'https://www.e-education.psu.edu/natureofgeoinfo/sites/www.e-education.psu.edu.natureofgeoinfo/files/image/hisp_pies.gif', +// '(this example involves a map too - may someday be a whole trace type', +// 'of its own. but the point is the size of the whole pie is important.)' + scalegroup: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + + // labels (legend is handled by plots.attributes.showlegend and layout.hiddenlabels) + textinfo: { + valType: 'flaglist', + flags: ['label', 'text', 'value', 'percent'], + extras: ['none'], + editType: 'calc', + }, + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { + flags: ['label', 'text', 'value', 'percent', 'name'] + }), + hovertemplate: hovertemplateAttrs({}, { + keys: ['label', 'color', 'value', 'percent', 'text'] + }), + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: ['label', 'color', 'value', 'percent', 'text'] + }), + textposition: { + valType: 'enumerated', + values: ['inside', 'outside', 'auto', 'none'], + dflt: 'auto', + arrayOk: true, + editType: 'plot', + }, + textfont: extendFlat({}, textFontAttrs, { + }), + insidetextorientation: { + valType: 'enumerated', + values: ['horizontal', 'radial', 'tangential', 'auto'], + dflt: 'auto', + editType: 'plot', + }, + insidetextfont: extendFlat({}, textFontAttrs, { + }), + outsidetextfont: extendFlat({}, textFontAttrs, { + }), + automargin: { + valType: 'boolean', + dflt: false, + editType: 'plot', + }, + + title: { + text: { + valType: 'string', + dflt: '', + editType: 'plot', + }, + font: extendFlat({}, textFontAttrs, { + }), + position: { + valType: 'enumerated', + values: [ + 'top left', 'top center', 'top right', + 'middle center', + 'bottom left', 'bottom center', 'bottom right' + ], + editType: 'plot', + }, + + editType: 'plot' + }, + + // position and shape + domain: domainAttrs({name: 'pie', trace: true, editType: 'calc'}), + + hole: { + valType: 'number', + min: 0, + max: 1, + dflt: 0, + editType: 'calc', + }, + + // ordering and direction + sort: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + direction: { + /** + * there are two common conventions, both of which place the first + * (largest, if sorted) slice with its left edge at 12 o'clock but + * succeeding slices follow either cw or ccw from there. + * + * see http://visage.co/data-visualization-101-pie-charts/ + */ + valType: 'enumerated', + values: ['clockwise', 'counterclockwise'], + dflt: 'counterclockwise', + editType: 'calc', + }, + rotation: { + valType: 'number', + min: -360, + max: 360, + dflt: 0, + editType: 'calc', + }, + + pull: { + valType: 'number', + min: 0, + max: 1, + dflt: 0, + arrayOk: true, + editType: 'calc', + }, + + _deprecated: { + title: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + titlefont: extendFlat({}, textFontAttrs, { + }), + titleposition: { + valType: 'enumerated', + values: [ + 'top left', 'top center', 'top right', + 'middle center', + 'bottom left', 'bottom center', 'bottom right' + ], + editType: 'calc', + } + } +}; + +},{"../../components/color/attributes":154,"../../lib/extend":279,"../../plots/attributes":327,"../../plots/domain":359,"../../plots/font_attributes":360,"../../plots/template_attributes":368}],482:[function(_dereq_,module,exports){ +'use strict'; + +var plots = _dereq_('../../plots/plots'); + +exports.name = 'pie'; + +exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { + plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback); +}; + +exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout); +}; + +},{"../../plots/plots":366}],483:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); + +var Color = _dereq_('../../components/color'); + +var extendedColorWayList = {}; + +function calc(gd, trace) { + var cd = []; + + var fullLayout = gd._fullLayout; + var hiddenLabels = fullLayout.hiddenlabels || []; + + var labels = trace.labels; + var colors = trace.marker.colors || []; + var vals = trace.values; + var len = trace._length; + var hasValues = trace._hasValues && len; + + var i, pt; + + if(trace.dlabel) { + labels = new Array(len); + for(i = 0; i < len; i++) { + labels[i] = String(trace.label0 + i * trace.dlabel); + } + } + + var allThisTraceLabels = {}; + var pullColor = makePullColorFn(fullLayout['_' + trace.type + 'colormap']); + var vTotal = 0; + var isAggregated = false; + + for(i = 0; i < len; i++) { + var v, label, hidden; + if(hasValues) { + v = vals[i]; + if(!isNumeric(v)) continue; + v = +v; + if(v < 0) continue; + } else v = 1; + + label = labels[i]; + if(label === undefined || label === '') label = i; + label = String(label); + + var thisLabelIndex = allThisTraceLabels[label]; + if(thisLabelIndex === undefined) { + allThisTraceLabels[label] = cd.length; + + hidden = hiddenLabels.indexOf(label) !== -1; + + if(!hidden) vTotal += v; + + cd.push({ + v: v, + label: label, + color: pullColor(colors[i], label), + i: i, + pts: [i], + hidden: hidden + }); + } else { + isAggregated = true; + + pt = cd[thisLabelIndex]; + pt.v += v; + pt.pts.push(i); + if(!pt.hidden) vTotal += v; + + if(pt.color === false && colors[i]) { + pt.color = pullColor(colors[i], label); + } + } + } + + var shouldSort = (trace.type === 'funnelarea') ? isAggregated : trace.sort; + if(shouldSort) cd.sort(function(a, b) { return b.v - a.v; }); + + // include the sum of all values in the first point + if(cd[0]) cd[0].vTotal = vTotal; + + return cd; +} + +function makePullColorFn(colorMap) { + return function pullColor(color, id) { + if(!color) return false; + + color = tinycolor(color); + if(!color.isValid()) return false; + + color = Color.addOpacity(color, color.getAlpha()); + if(!colorMap[id]) colorMap[id] = color; + + return color; + }; +} + +/* + * `calc` filled in (and collated) explicit colors. + * Now we need to propagate these explicit colors to other traces, + * and fill in default colors. + * This is done after sorting, so we pick defaults + * in the order slices will be displayed + */ +function crossTraceCalc(gd, plotinfo) { // TODO: should we name the second argument opts? + var desiredType = (plotinfo || {}).type; + if(!desiredType) desiredType = 'pie'; + + var fullLayout = gd._fullLayout; + var calcdata = gd.calcdata; + var colorWay = fullLayout[desiredType + 'colorway']; + var colorMap = fullLayout['_' + desiredType + 'colormap']; + + if(fullLayout['extend' + desiredType + 'colors']) { + colorWay = generateExtendedColors(colorWay, extendedColorWayList); + } + var dfltColorCount = 0; + + for(var i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var traceType = cd[0].trace.type; + if(traceType !== desiredType) continue; + + for(var j = 0; j < cd.length; j++) { + var pt = cd[j]; + if(pt.color === false) { + // have we seen this label and assigned a color to it in a previous trace? + if(colorMap[pt.label]) { + pt.color = colorMap[pt.label]; + } else { + colorMap[pt.label] = pt.color = colorWay[dfltColorCount % colorWay.length]; + dfltColorCount++; + } + } + } + } +} + +/** + * pick a default color from the main default set, augmented by + * itself lighter then darker before repeating + */ +function generateExtendedColors(colorList, extendedColorWays) { + var i; + var colorString = JSON.stringify(colorList); + var colors = extendedColorWays[colorString]; + if(!colors) { + colors = colorList.slice(); + + for(i = 0; i < colorList.length; i++) { + colors.push(tinycolor(colorList[i]).lighten(20).toHexString()); + } + + for(i = 0; i < colorList.length; i++) { + colors.push(tinycolor(colorList[i]).darken(20).toHexString()); + } + extendedColorWays[colorString] = colors; + } + + return colors; +} + +module.exports = { + calc: calc, + crossTraceCalc: crossTraceCalc, + + makePullColorFn: makePullColorFn, + generateExtendedColors: generateExtendedColors +}; + +},{"../../components/color":155,"fast-isnumeric":31,"tinycolor2":119}],484:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); +var attributes = _dereq_('./attributes'); +var handleDomainDefaults = _dereq_('../../plots/domain').defaults; +var handleText = _dereq_('../bar/defaults').handleText; + +function handleLabelsAndValues(labels, values) { + var hasLabels = Array.isArray(labels); + var hasValues = Lib.isArrayOrTypedArray(values); + var len = Math.min( + hasLabels ? labels.length : Infinity, + hasValues ? values.length : Infinity + ); + + if(!isFinite(len)) len = 0; + + if(len && hasValues) { + var hasPositive; + for(var i = 0; i < len; i++) { + var v = values[i]; + if(isNumeric(v) && v > 0) { + hasPositive = true; + break; + } + } + if(!hasPositive) len = 0; + } + + return { + hasLabels: hasLabels, + hasValues: hasValues, + len: len + }; +} + +function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var labels = coerce('labels'); + var values = coerce('values'); + + var res = handleLabelsAndValues(labels, values); + var len = res.len; + traceOut._hasLabels = res.hasLabels; + traceOut._hasValues = res.hasValues; + + if(!traceOut._hasLabels && + traceOut._hasValues + ) { + coerce('label0'); + coerce('dlabel'); + } + + if(!len) { + traceOut.visible = false; + return; + } + traceOut._length = len; + + var lineWidth = coerce('marker.line.width'); + if(lineWidth) coerce('marker.line.color'); + + coerce('marker.colors'); + + coerce('scalegroup'); + // TODO: hole needs to be coerced to the same value within a scaleegroup + + var textData = coerce('text'); + var textTemplate = coerce('texttemplate'); + var textInfo; + if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent'); + + coerce('hovertext'); + coerce('hovertemplate'); + + if(textTemplate || (textInfo && textInfo !== 'none')) { + var textposition = coerce('textposition'); + handleText(traceIn, traceOut, layout, coerce, textposition, { + moduleHasSelected: false, + moduleHasUnselected: false, + moduleHasConstrain: false, + moduleHasCliponaxis: false, + moduleHasTextangle: false, + moduleHasInsideanchor: false + }); + + var hasBoth = Array.isArray(textposition) || textposition === 'auto'; + var hasOutside = hasBoth || textposition === 'outside'; + if(hasOutside) { + coerce('automargin'); + } + + if(textposition === 'inside' || textposition === 'auto' || Array.isArray(textposition)) { + coerce('insidetextorientation'); + } + } + + handleDomainDefaults(traceOut, layout, coerce); + + var hole = coerce('hole'); + var title = coerce('title.text'); + if(title) { + var titlePosition = coerce('title.position', hole ? 'middle center' : 'top center'); + if(!hole && titlePosition === 'middle center') traceOut.title.position = 'top center'; + Lib.coerceFont(coerce, 'title.font', layout.font); + } + + coerce('sort'); + coerce('direction'); + coerce('rotation'); + coerce('pull'); +} + +module.exports = { + handleLabelsAndValues: handleLabelsAndValues, + supplyDefaults: supplyDefaults +}; + +},{"../../lib":285,"../../plots/domain":359,"../bar/defaults":387,"./attributes":481,"fast-isnumeric":31}],485:[function(_dereq_,module,exports){ +'use strict'; + +var appendArrayMultiPointValues = _dereq_('../../components/fx/helpers').appendArrayMultiPointValues; + +// Note: like other eventData routines, this creates the data for hover/unhover/click events +// but it has a different API and goes through a totally different pathway. +// So to ensure it doesn't get misused, it's not attached to the Pie module. +module.exports = function eventData(pt, trace) { + var out = { + curveNumber: trace.index, + pointNumbers: pt.pts, + data: trace._input, + fullData: trace, + label: pt.label, + color: pt.color, + value: pt.v, + percent: pt.percent, + text: pt.text, + + // pt.v (and pt.i below) for backward compatibility + v: pt.v + }; + + // Only include pointNumber if it's unambiguous + if(pt.pts.length === 1) out.pointNumber = out.i = pt.pts[0]; + + // Add extra data arrays to the output + // notice that this is the multi-point version ('s' on the end!) + // so added data will be arrays matching the pointNumbers array. + appendArrayMultiPointValues(out, trace, pt.pts); + + // don't include obsolete fields in new funnelarea traces + if(trace.type === 'funnelarea') { + delete out.v; + delete out.i; + } + + return out; +}; + +},{"../../components/fx/helpers":191}],486:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +function format(vRounded) { + return ( + vRounded.indexOf('e') !== -1 ? vRounded.replace(/[.]?0+e/, 'e') : + vRounded.indexOf('.') !== -1 ? vRounded.replace(/[.]?0+$/, '') : + vRounded + ); +} + +exports.formatPiePercent = function formatPiePercent(v, separators) { + var vRounded = format((v * 100).toPrecision(3)); + return Lib.numSeparate(vRounded, separators) + '%'; +}; + +exports.formatPieValue = function formatPieValue(v, separators) { + var vRounded = format(v.toPrecision(10)); + return Lib.numSeparate(vRounded, separators); +}; + +exports.getFirstFilled = function getFirstFilled(array, indices) { + if(!Array.isArray(array)) return; + for(var i = 0; i < indices.length; i++) { + var v = array[indices[i]]; + if(v || v === 0 || v === '') return v; + } +}; + +exports.castOption = function castOption(item, indices) { + if(Array.isArray(item)) return exports.getFirstFilled(item, indices); + else if(item) return item; +}; + +exports.getRotationAngle = function(rotation) { + return (rotation === 'auto' ? 0 : rotation) * Math.PI / 180; +}; + +},{"../../lib":285}],487:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults').supplyDefaults, + supplyLayoutDefaults: _dereq_('./layout_defaults'), + layoutAttributes: _dereq_('./layout_attributes'), + + calc: _dereq_('./calc').calc, + crossTraceCalc: _dereq_('./calc').crossTraceCalc, + + plot: _dereq_('./plot').plot, + style: _dereq_('./style'), + styleOne: _dereq_('./style_one'), + + moduleType: 'trace', + name: 'pie', + basePlotModule: _dereq_('./base_plot'), + categories: ['pie-like', 'pie', 'showLegend'], + meta: { + } +}; + +},{"./attributes":481,"./base_plot":482,"./calc":483,"./defaults":484,"./layout_attributes":488,"./layout_defaults":489,"./plot":490,"./style":491,"./style_one":492}],488:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + hiddenlabels: { + valType: 'data_array', + editType: 'calc', + }, + piecolorway: { + valType: 'colorlist', + editType: 'calc', + }, + extendpiecolors: { + valType: 'boolean', + dflt: true, + editType: 'calc', + } +}; + +},{}],489:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var layoutAttributes = _dereq_('./layout_attributes'); + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + + coerce('hiddenlabels'); + coerce('piecolorway', layoutOut.colorway); + coerce('extendpiecolors'); +}; + +},{"../../lib":285,"./layout_attributes":488}],490:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Plots = _dereq_('../../plots/plots'); +var Fx = _dereq_('../../components/fx'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Lib = _dereq_('../../lib'); +var strScale = Lib.strScale; +var strTranslate = Lib.strTranslate; +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var uniformText = _dereq_('../bar/uniform_text'); +var recordMinTextSize = uniformText.recordMinTextSize; +var clearMinTextSize = uniformText.clearMinTextSize; +var TEXTPAD = _dereq_('../bar/constants').TEXTPAD; + +var helpers = _dereq_('./helpers'); +var eventData = _dereq_('./event_data'); +var isValidTextValue = _dereq_('../../lib').isValidTextValue; + +function plot(gd, cdModule) { + var fullLayout = gd._fullLayout; + var gs = fullLayout._size; + + clearMinTextSize('pie', fullLayout); + + prerenderTitles(cdModule, gd); + layoutAreas(cdModule, gs); + + var plotGroups = Lib.makeTraceGroups(fullLayout._pielayer, cdModule, 'trace').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + + setCoords(cd); + + // TODO: miter might look better but can sometimes cause problems + // maybe miter with a small-ish stroke-miterlimit? + plotGroup.attr('stroke-linejoin', 'round'); + + plotGroup.each(function() { + var slices = d3.select(this).selectAll('g.slice').data(cd); + + slices.enter().append('g') + .classed('slice', true); + slices.exit().remove(); + + var quadrants = [ + [[], []], // y<0: x<0, x>=0 + [[], []] // y>=0: x<0, x>=0 + ]; + var hasOutsideText = false; + + slices.each(function(pt, i) { + if(pt.hidden) { + d3.select(this).selectAll('path,g').remove(); + return; + } + + // to have consistent event data compared to other traces + pt.pointNumber = pt.i; + pt.curveNumber = trace.index; + + quadrants[pt.pxmid[1] < 0 ? 0 : 1][pt.pxmid[0] < 0 ? 0 : 1].push(pt); + + var cx = cd0.cx; + var cy = cd0.cy; + var sliceTop = d3.select(this); + var slicePath = sliceTop.selectAll('path.surface').data([pt]); + + slicePath.enter().append('path') + .classed('surface', true) + .style({'pointer-events': 'all'}); + + sliceTop.call(attachFxHandlers, gd, cd); + + if(trace.pull) { + var pull = +helpers.castOption(trace.pull, pt.pts) || 0; + if(pull > 0) { + cx += pull * pt.pxmid[0]; + cy += pull * pt.pxmid[1]; + } + } + + pt.cxFinal = cx; + pt.cyFinal = cy; + + function arc(start, finish, cw, scale) { + var dx = scale * (finish[0] - start[0]); + var dy = scale * (finish[1] - start[1]); + + return 'a' + + (scale * cd0.r) + ',' + (scale * cd0.r) + ' 0 ' + + pt.largeArc + (cw ? ' 1 ' : ' 0 ') + dx + ',' + dy; + } + + var hole = trace.hole; + if(pt.v === cd0.vTotal) { // 100% fails bcs arc start and end are identical + var outerCircle = 'M' + (cx + pt.px0[0]) + ',' + (cy + pt.px0[1]) + + arc(pt.px0, pt.pxmid, true, 1) + + arc(pt.pxmid, pt.px0, true, 1) + 'Z'; + if(hole) { + slicePath.attr('d', + 'M' + (cx + hole * pt.px0[0]) + ',' + (cy + hole * pt.px0[1]) + + arc(pt.px0, pt.pxmid, false, hole) + + arc(pt.pxmid, pt.px0, false, hole) + + 'Z' + outerCircle); + } else slicePath.attr('d', outerCircle); + } else { + var outerArc = arc(pt.px0, pt.px1, true, 1); + + if(hole) { + var rim = 1 - hole; + slicePath.attr('d', + 'M' + (cx + hole * pt.px1[0]) + ',' + (cy + hole * pt.px1[1]) + + arc(pt.px1, pt.px0, false, hole) + + 'l' + (rim * pt.px0[0]) + ',' + (rim * pt.px0[1]) + + outerArc + + 'Z'); + } else { + slicePath.attr('d', + 'M' + cx + ',' + cy + + 'l' + pt.px0[0] + ',' + pt.px0[1] + + outerArc + + 'Z'); + } + } + + // add text + formatSliceLabel(gd, pt, cd0); + var textPosition = helpers.castOption(trace.textposition, pt.pts); + var sliceTextGroup = sliceTop.selectAll('g.slicetext') + .data(pt.text && (textPosition !== 'none') ? [0] : []); + + sliceTextGroup.enter().append('g') + .classed('slicetext', true); + sliceTextGroup.exit().remove(); + + sliceTextGroup.each(function() { + var sliceText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) { + // prohibit tex interpretation until we can handle + // tex and regular text together + s.attr('data-notex', 1); + }); + + var font = Lib.ensureUniformFontSize(gd, textPosition === 'outside' ? + determineOutsideTextFont(trace, pt, fullLayout.font) : + determineInsideTextFont(trace, pt, fullLayout.font) + ); + + sliceText.text(pt.text) + .attr({ + 'class': 'slicetext', + transform: '', + 'text-anchor': 'middle' + }) + .call(Drawing.font, font) + .call(svgTextUtils.convertToTspans, gd); + + // position the text relative to the slice + var textBB = Drawing.bBox(sliceText.node()); + var transform; + + if(textPosition === 'outside') { + transform = transformOutsideText(textBB, pt); + } else { + transform = transformInsideText(textBB, pt, cd0); + if(textPosition === 'auto' && transform.scale < 1) { + var newFont = Lib.ensureUniformFontSize(gd, trace.outsidetextfont); + + sliceText.call(Drawing.font, newFont); + textBB = Drawing.bBox(sliceText.node()); + + transform = transformOutsideText(textBB, pt); + } + } + + var textPosAngle = transform.textPosAngle; + var textXY = textPosAngle === undefined ? pt.pxmid : getCoords(cd0.r, textPosAngle); + transform.targetX = cx + textXY[0] * transform.rCenter + (transform.x || 0); + transform.targetY = cy + textXY[1] * transform.rCenter + (transform.y || 0); + computeTransform(transform, textBB); + + // save some stuff to use later ensure no labels overlap + if(transform.outside) { + var targetY = transform.targetY; + pt.yLabelMin = targetY - textBB.height / 2; + pt.yLabelMid = targetY; + pt.yLabelMax = targetY + textBB.height / 2; + pt.labelExtraX = 0; + pt.labelExtraY = 0; + hasOutsideText = true; + } + + transform.fontSize = font.size; + recordMinTextSize(trace.type, transform, fullLayout); + cd[i].transform = transform; + + sliceText.attr('transform', Lib.getTextTransform(transform)); + }); + }); + + // add the title + var titleTextGroup = d3.select(this).selectAll('g.titletext') + .data(trace.title.text ? [0] : []); + + titleTextGroup.enter().append('g') + .classed('titletext', true); + titleTextGroup.exit().remove(); + + titleTextGroup.each(function() { + var titleText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) { + // prohibit tex interpretation as above + s.attr('data-notex', 1); + }); + + var txt = trace.title.text; + if(trace._meta) { + txt = Lib.templateString(txt, trace._meta); + } + + titleText.text(txt) + .attr({ + 'class': 'titletext', + transform: '', + 'text-anchor': 'middle', + }) + .call(Drawing.font, trace.title.font) + .call(svgTextUtils.convertToTspans, gd); + + var transform; + + if(trace.title.position === 'middle center') { + transform = positionTitleInside(cd0); + } else { + transform = positionTitleOutside(cd0, gs); + } + + titleText.attr('transform', + strTranslate(transform.x, transform.y) + + strScale(Math.min(1, transform.scale)) + + strTranslate(transform.tx, transform.ty)); + }); + + // now make sure no labels overlap (at least within one pie) + if(hasOutsideText) scootLabels(quadrants, trace); + + plotTextLines(slices, trace); + + if(hasOutsideText && trace.automargin) { + // TODO if we ever want to improve perf, + // we could reuse the textBB computed above together + // with the sliceText transform info + var traceBbox = Drawing.bBox(plotGroup.node()); + + var domain = trace.domain; + var vpw = gs.w * (domain.x[1] - domain.x[0]); + var vph = gs.h * (domain.y[1] - domain.y[0]); + var xgap = (0.5 * vpw - cd0.r) / gs.w; + var ygap = (0.5 * vph - cd0.r) / gs.h; + + Plots.autoMargin(gd, 'pie.' + trace.uid + '.automargin', { + xl: domain.x[0] - xgap, + xr: domain.x[1] + xgap, + yb: domain.y[0] - ygap, + yt: domain.y[1] + ygap, + l: Math.max(cd0.cx - cd0.r - traceBbox.left, 0), + r: Math.max(traceBbox.right - (cd0.cx + cd0.r), 0), + b: Math.max(traceBbox.bottom - (cd0.cy + cd0.r), 0), + t: Math.max(cd0.cy - cd0.r - traceBbox.top, 0), + pad: 5 + }); + } + }); + }); + + // This is for a bug in Chrome (as of 2015-07-22, and does not affect FF) + // if insidetextfont and outsidetextfont are different sizes, sometimes the size + // of an "em" gets taken from the wrong element at first so lines are + // spaced wrong. You just have to tell it to try again later and it gets fixed. + // I have no idea why we haven't seen this in other contexts. Also, sometimes + // it gets the initial draw correct but on redraw it gets confused. + setTimeout(function() { + plotGroups.selectAll('tspan').each(function() { + var s = d3.select(this); + if(s.attr('dy')) s.attr('dy', s.attr('dy')); + }); + }, 0); +} + +// TODO add support for transition +function plotTextLines(slices, trace) { + slices.each(function(pt) { + var sliceTop = d3.select(this); + + if(!pt.labelExtraX && !pt.labelExtraY) { + sliceTop.select('path.textline').remove(); + return; + } + + // first move the text to its new location + var sliceText = sliceTop.select('g.slicetext text'); + + pt.transform.targetX += pt.labelExtraX; + pt.transform.targetY += pt.labelExtraY; + + sliceText.attr('transform', Lib.getTextTransform(pt.transform)); + + // then add a line to the new location + var lineStartX = pt.cxFinal + pt.pxmid[0]; + var lineStartY = pt.cyFinal + pt.pxmid[1]; + var textLinePath = 'M' + lineStartX + ',' + lineStartY; + var finalX = (pt.yLabelMax - pt.yLabelMin) * (pt.pxmid[0] < 0 ? -1 : 1) / 4; + + if(pt.labelExtraX) { + var yFromX = pt.labelExtraX * pt.pxmid[1] / pt.pxmid[0]; + var yNet = pt.yLabelMid + pt.labelExtraY - (pt.cyFinal + pt.pxmid[1]); + + if(Math.abs(yFromX) > Math.abs(yNet)) { + textLinePath += + 'l' + (yNet * pt.pxmid[0] / pt.pxmid[1]) + ',' + yNet + + 'H' + (lineStartX + pt.labelExtraX + finalX); + } else { + textLinePath += 'l' + pt.labelExtraX + ',' + yFromX + + 'v' + (yNet - yFromX) + + 'h' + finalX; + } + } else { + textLinePath += + 'V' + (pt.yLabelMid + pt.labelExtraY) + + 'h' + finalX; + } + + Lib.ensureSingle(sliceTop, 'path', 'textline') + .call(Color.stroke, trace.outsidetextfont.color) + .attr({ + 'stroke-width': Math.min(2, trace.outsidetextfont.size / 8), + d: textLinePath, + fill: 'none' + }); + }); +} + +function attachFxHandlers(sliceTop, gd, cd) { + var cd0 = cd[0]; + var trace = cd0.trace; + var cx = cd0.cx; + var cy = cd0.cy; + + // hover state vars + // have we drawn a hover label, so it should be cleared later + if(!('_hasHoverLabel' in trace)) trace._hasHoverLabel = false; + // have we emitted a hover event, so later an unhover event should be emitted + // note that click events do not depend on this - you can still get them + // with hovermode: false or if you were earlier dragging, then clicked + // in the same slice that you moused up in + if(!('_hasHoverEvent' in trace)) trace._hasHoverEvent = false; + + sliceTop.on('mouseover', function(pt) { + // in case fullLayout or fullData has changed without a replot + var fullLayout2 = gd._fullLayout; + var trace2 = gd._fullData[trace.index]; + + if(gd._dragging || fullLayout2.hovermode === false) return; + + var hoverinfo = trace2.hoverinfo; + if(Array.isArray(hoverinfo)) { + // super hacky: we need to pull out the *first* hoverinfo from + // pt.pts, then put it back into an array in a dummy trace + // and call castHoverinfo on that. + // TODO: do we want to have Fx.castHoverinfo somehow handle this? + // it already takes an array for index, for 2D, so this seems tricky. + hoverinfo = Fx.castHoverinfo({ + hoverinfo: [helpers.castOption(hoverinfo, pt.pts)], + _module: trace._module + }, fullLayout2, 0); + } + + if(hoverinfo === 'all') hoverinfo = 'label+text+value+percent+name'; + + // in case we dragged over the pie from another subplot, + // or if hover is turned off + if(trace2.hovertemplate || (hoverinfo !== 'none' && hoverinfo !== 'skip' && hoverinfo)) { + var rInscribed = pt.rInscribed || 0; + var hoverCenterX = cx + pt.pxmid[0] * (1 - rInscribed); + var hoverCenterY = cy + pt.pxmid[1] * (1 - rInscribed); + var separators = fullLayout2.separators; + var text = []; + + if(hoverinfo && hoverinfo.indexOf('label') !== -1) text.push(pt.label); + pt.text = helpers.castOption(trace2.hovertext || trace2.text, pt.pts); + if(hoverinfo && hoverinfo.indexOf('text') !== -1) { + var tx = pt.text; + if(Lib.isValidTextValue(tx)) text.push(tx); + } + pt.value = pt.v; + pt.valueLabel = helpers.formatPieValue(pt.v, separators); + if(hoverinfo && hoverinfo.indexOf('value') !== -1) text.push(pt.valueLabel); + pt.percent = pt.v / cd0.vTotal; + pt.percentLabel = helpers.formatPiePercent(pt.percent, separators); + if(hoverinfo && hoverinfo.indexOf('percent') !== -1) text.push(pt.percentLabel); + + var hoverLabel = trace2.hoverlabel; + var hoverFont = hoverLabel.font; + + Fx.loneHover({ + trace: trace, + x0: hoverCenterX - rInscribed * cd0.r, + x1: hoverCenterX + rInscribed * cd0.r, + y: hoverCenterY, + text: text.join('
'), + name: (trace2.hovertemplate || hoverinfo.indexOf('name') !== -1) ? trace2.name : undefined, + idealAlign: pt.pxmid[0] < 0 ? 'left' : 'right', + color: helpers.castOption(hoverLabel.bgcolor, pt.pts) || pt.color, + borderColor: helpers.castOption(hoverLabel.bordercolor, pt.pts), + fontFamily: helpers.castOption(hoverFont.family, pt.pts), + fontSize: helpers.castOption(hoverFont.size, pt.pts), + fontColor: helpers.castOption(hoverFont.color, pt.pts), + nameLength: helpers.castOption(hoverLabel.namelength, pt.pts), + textAlign: helpers.castOption(hoverLabel.align, pt.pts), + hovertemplate: helpers.castOption(trace2.hovertemplate, pt.pts), + hovertemplateLabels: pt, + eventData: [eventData(pt, trace2)] + }, { + container: fullLayout2._hoverlayer.node(), + outerContainer: fullLayout2._paper.node(), + gd: gd + }); + + trace._hasHoverLabel = true; + } + + trace._hasHoverEvent = true; + gd.emit('plotly_hover', { + points: [eventData(pt, trace2)], + event: d3.event + }); + }); + + sliceTop.on('mouseout', function(evt) { + var fullLayout2 = gd._fullLayout; + var trace2 = gd._fullData[trace.index]; + var pt = d3.select(this).datum(); + + if(trace._hasHoverEvent) { + evt.originalEvent = d3.event; + gd.emit('plotly_unhover', { + points: [eventData(pt, trace2)], + event: d3.event + }); + trace._hasHoverEvent = false; + } + + if(trace._hasHoverLabel) { + Fx.loneUnhover(fullLayout2._hoverlayer.node()); + trace._hasHoverLabel = false; + } + }); + + sliceTop.on('click', function(pt) { + // TODO: this does not support right-click. If we want to support it, we + // would likely need to change pie to use dragElement instead of straight + // mapbox event binding. Or perhaps better, make a simple wrapper with the + // right mousedown, mousemove, and mouseup handlers just for a left/right click + // mapbox would use this too. + var fullLayout2 = gd._fullLayout; + var trace2 = gd._fullData[trace.index]; + + if(gd._dragging || fullLayout2.hovermode === false) return; + + gd._hoverdata = [eventData(pt, trace2)]; + Fx.click(gd, d3.event); + }); +} + +function determineOutsideTextFont(trace, pt, layoutFont) { + var color = + helpers.castOption(trace.outsidetextfont.color, pt.pts) || + helpers.castOption(trace.textfont.color, pt.pts) || + layoutFont.color; + + var family = + helpers.castOption(trace.outsidetextfont.family, pt.pts) || + helpers.castOption(trace.textfont.family, pt.pts) || + layoutFont.family; + + var size = + helpers.castOption(trace.outsidetextfont.size, pt.pts) || + helpers.castOption(trace.textfont.size, pt.pts) || + layoutFont.size; + + return { + color: color, + family: family, + size: size + }; +} + +function determineInsideTextFont(trace, pt, layoutFont) { + var customColor = helpers.castOption(trace.insidetextfont.color, pt.pts); + if(!customColor && trace._input.textfont) { + // Why not simply using trace.textfont? Because if not set, it + // defaults to layout.font which has a default color. But if + // textfont.color and insidetextfont.color don't supply a value, + // a contrasting color shall be used. + customColor = helpers.castOption(trace._input.textfont.color, pt.pts); + } + + var family = + helpers.castOption(trace.insidetextfont.family, pt.pts) || + helpers.castOption(trace.textfont.family, pt.pts) || + layoutFont.family; + + var size = + helpers.castOption(trace.insidetextfont.size, pt.pts) || + helpers.castOption(trace.textfont.size, pt.pts) || + layoutFont.size; + + return { + color: customColor || Color.contrast(pt.color), + family: family, + size: size + }; +} + +function prerenderTitles(cdModule, gd) { + var cd0, trace; + + // Determine the width and height of the title for each pie. + for(var i = 0; i < cdModule.length; i++) { + cd0 = cdModule[i][0]; + trace = cd0.trace; + + if(trace.title.text) { + var txt = trace.title.text; + if(trace._meta) { + txt = Lib.templateString(txt, trace._meta); + } + + var dummyTitle = Drawing.tester.append('text') + .attr('data-notex', 1) + .text(txt) + .call(Drawing.font, trace.title.font) + .call(svgTextUtils.convertToTspans, gd); + var bBox = Drawing.bBox(dummyTitle.node(), true); + cd0.titleBox = { + width: bBox.width, + height: bBox.height, + }; + dummyTitle.remove(); + } + } +} + +function transformInsideText(textBB, pt, cd0) { + var r = cd0.r || pt.rpx1; + var rInscribed = pt.rInscribed; + + var isEmpty = pt.startangle === pt.stopangle; + if(isEmpty) { + return { + rCenter: 1 - rInscribed, + scale: 0, + rotate: 0, + textPosAngle: 0 + }; + } + + var ring = pt.ring; + var isCircle = (ring === 1) && (Math.abs(pt.startangle - pt.stopangle) === Math.PI * 2); + + var halfAngle = pt.halfangle; + var midAngle = pt.midangle; + + var orientation = cd0.trace.insidetextorientation; + var isHorizontal = orientation === 'horizontal'; + var isTangential = orientation === 'tangential'; + var isRadial = orientation === 'radial'; + var isAuto = orientation === 'auto'; + + var allTransforms = []; + var newT; + + if(!isAuto) { + // max size if text is placed (horizontally) at the top or bottom of the arc + + var considerCrossing = function(angle, key) { + if(isCrossing(pt, angle)) { + var dStart = Math.abs(angle - pt.startangle); + var dStop = Math.abs(angle - pt.stopangle); + + var closestEdge = dStart < dStop ? dStart : dStop; + + if(key === 'tan') { + newT = calcTanTransform(textBB, r, ring, closestEdge, 0); + } else { // case of 'rad' + newT = calcRadTransform(textBB, r, ring, closestEdge, Math.PI / 2); + } + newT.textPosAngle = angle; + + allTransforms.push(newT); + } + }; + + // to cover all cases with trace.rotation added + var i; + if(isHorizontal || isTangential) { + // top + for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * i, 'tan'); + // bottom + for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1), 'tan'); + } + if(isHorizontal || isRadial) { + // left + for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1.5), 'rad'); + // right + for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 0.5), 'rad'); + } + } + + if(isCircle || isAuto || isHorizontal) { + // max size text can be inserted inside without rotating it + // this inscribes the text rectangle in a circle, which is then inscribed + // in the slice, so it will be an underestimate, which some day we may want + // to improve so this case can get more use + var textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height); + + newT = { + scale: rInscribed * r * 2 / textDiameter, + + // and the center position and rotation in this case + rCenter: 1 - rInscribed, + rotate: 0 + }; + + newT.textPosAngle = (pt.startangle + pt.stopangle) / 2; + if(newT.scale >= 1) return newT; + + allTransforms.push(newT); + } + + if(isAuto || isRadial) { + newT = calcRadTransform(textBB, r, ring, halfAngle, midAngle); + newT.textPosAngle = (pt.startangle + pt.stopangle) / 2; + allTransforms.push(newT); + } + + if(isAuto || isTangential) { + newT = calcTanTransform(textBB, r, ring, halfAngle, midAngle); + newT.textPosAngle = (pt.startangle + pt.stopangle) / 2; + allTransforms.push(newT); + } + + var id = 0; + var maxScale = 0; + for(var k = 0; k < allTransforms.length; k++) { + var s = allTransforms[k].scale; + if(maxScale < s) { + maxScale = s; + id = k; + } + + if(!isAuto && maxScale >= 1) { + // respect test order for non-auto options + break; + } + } + return allTransforms[id]; +} + +function isCrossing(pt, angle) { + var start = pt.startangle; + var stop = pt.stopangle; + return ( + (start > angle && angle > stop) || + (start < angle && angle < stop) + ); +} + +function calcRadTransform(textBB, r, ring, halfAngle, midAngle) { + r = Math.max(0, r - 2 * TEXTPAD); + + // max size if text is rotated radially + var a = textBB.width / textBB.height; + var s = calcMaxHalfSize(a, halfAngle, r, ring); + return { + scale: s * 2 / textBB.height, + rCenter: calcRCenter(a, s / r), + rotate: calcRotate(midAngle) + }; +} + +function calcTanTransform(textBB, r, ring, halfAngle, midAngle) { + r = Math.max(0, r - 2 * TEXTPAD); + + // max size if text is rotated tangentially + var a = textBB.height / textBB.width; + var s = calcMaxHalfSize(a, halfAngle, r, ring); + return { + scale: s * 2 / textBB.width, + rCenter: calcRCenter(a, s / r), + rotate: calcRotate(midAngle + Math.PI / 2) + }; +} + +function calcRCenter(a, b) { + return Math.cos(b) - a * b; +} + +function calcRotate(t) { + return (180 / Math.PI * t + 720) % 180 - 90; +} + +function calcMaxHalfSize(a, halfAngle, r, ring) { + var q = a + 1 / (2 * Math.tan(halfAngle)); + return r * Math.min( + 1 / (Math.sqrt(q * q + 0.5) + q), + ring / (Math.sqrt(a * a + ring / 2) + a) + ); +} + +function getInscribedRadiusFraction(pt, cd0) { + if(pt.v === cd0.vTotal && !cd0.trace.hole) return 1;// special case of 100% with no hole + + return Math.min(1 / (1 + 1 / Math.sin(pt.halfangle)), pt.ring / 2); +} + +function transformOutsideText(textBB, pt) { + var x = pt.pxmid[0]; + var y = pt.pxmid[1]; + var dx = textBB.width / 2; + var dy = textBB.height / 2; + + if(x < 0) dx *= -1; + if(y < 0) dy *= -1; + + return { + scale: 1, + rCenter: 1, + rotate: 0, + x: dx + Math.abs(dy) * (dx > 0 ? 1 : -1) / 2, + y: dy / (1 + x * x / (y * y)), + outside: true + }; +} + +function positionTitleInside(cd0) { + var textDiameter = + Math.sqrt(cd0.titleBox.width * cd0.titleBox.width + cd0.titleBox.height * cd0.titleBox.height); + return { + x: cd0.cx, + y: cd0.cy, + scale: cd0.trace.hole * cd0.r * 2 / textDiameter, + tx: 0, + ty: - cd0.titleBox.height / 2 + cd0.trace.title.font.size + }; +} + +function positionTitleOutside(cd0, plotSize) { + var scaleX = 1; + var scaleY = 1; + var maxPull; + + var trace = cd0.trace; + // position of the baseline point of the text box in the plot, before scaling. + // we anchored the text in the middle, so the baseline is on the bottom middle + // of the first line of text. + var topMiddle = { + x: cd0.cx, + y: cd0.cy + }; + // relative translation of the text box after scaling + var translate = { + tx: 0, + ty: 0 + }; + + // we reason below as if the baseline is the top middle point of the text box. + // so we must add the font size to approximate the y-coord. of the top. + // note that this correction must happen after scaling. + translate.ty += trace.title.font.size; + maxPull = getMaxPull(trace); + + if(trace.title.position.indexOf('top') !== -1) { + topMiddle.y -= (1 + maxPull) * cd0.r; + translate.ty -= cd0.titleBox.height; + } else if(trace.title.position.indexOf('bottom') !== -1) { + topMiddle.y += (1 + maxPull) * cd0.r; + } + + var rx = applyAspectRatio(cd0.r, cd0.trace.aspectratio); + + var maxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]) / 2; + if(trace.title.position.indexOf('left') !== -1) { + // we start the text at the left edge of the pie + maxWidth = maxWidth + rx; + topMiddle.x -= (1 + maxPull) * rx; + translate.tx += cd0.titleBox.width / 2; + } else if(trace.title.position.indexOf('center') !== -1) { + maxWidth *= 2; + } else if(trace.title.position.indexOf('right') !== -1) { + maxWidth = maxWidth + rx; + topMiddle.x += (1 + maxPull) * rx; + translate.tx -= cd0.titleBox.width / 2; + } + scaleX = maxWidth / cd0.titleBox.width; + scaleY = getTitleSpace(cd0, plotSize) / cd0.titleBox.height; + return { + x: topMiddle.x, + y: topMiddle.y, + scale: Math.min(scaleX, scaleY), + tx: translate.tx, + ty: translate.ty + }; +} + +function applyAspectRatio(x, aspectratio) { + return x / ((aspectratio === undefined) ? 1 : aspectratio); +} + +function getTitleSpace(cd0, plotSize) { + var trace = cd0.trace; + var pieBoxHeight = plotSize.h * (trace.domain.y[1] - trace.domain.y[0]); + // use at most half of the plot for the title + return Math.min(cd0.titleBox.height, pieBoxHeight / 2); +} + +function getMaxPull(trace) { + var maxPull = trace.pull; + if(!maxPull) return 0; + + var j; + if(Array.isArray(maxPull)) { + maxPull = 0; + for(j = 0; j < trace.pull.length; j++) { + if(trace.pull[j] > maxPull) maxPull = trace.pull[j]; + } + } + return maxPull; +} + +function scootLabels(quadrants, trace) { + var xHalf, yHalf, equatorFirst, farthestX, farthestY, + xDiffSign, yDiffSign, thisQuad, oppositeQuad, + wholeSide, i, thisQuadOutside, firstOppositeOutsidePt; + + function topFirst(a, b) { return a.pxmid[1] - b.pxmid[1]; } + function bottomFirst(a, b) { return b.pxmid[1] - a.pxmid[1]; } + + function scootOneLabel(thisPt, prevPt) { + if(!prevPt) prevPt = {}; + + var prevOuterY = prevPt.labelExtraY + (yHalf ? prevPt.yLabelMax : prevPt.yLabelMin); + var thisInnerY = yHalf ? thisPt.yLabelMin : thisPt.yLabelMax; + var thisOuterY = yHalf ? thisPt.yLabelMax : thisPt.yLabelMin; + var thisSliceOuterY = thisPt.cyFinal + farthestY(thisPt.px0[1], thisPt.px1[1]); + var newExtraY = prevOuterY - thisInnerY; + + var xBuffer, i, otherPt, otherOuterY, otherOuterX, newExtraX; + + // make sure this label doesn't overlap other labels + // this *only* has us move these labels vertically + if(newExtraY * yDiffSign > 0) thisPt.labelExtraY = newExtraY; + + // make sure this label doesn't overlap any slices + if(!Array.isArray(trace.pull)) return; // this can only happen with array pulls + + for(i = 0; i < wholeSide.length; i++) { + otherPt = wholeSide[i]; + + // overlap can only happen if the other point is pulled more than this one + if(otherPt === thisPt || ( + (helpers.castOption(trace.pull, thisPt.pts) || 0) >= + (helpers.castOption(trace.pull, otherPt.pts) || 0)) + ) { + continue; + } + + if((thisPt.pxmid[1] - otherPt.pxmid[1]) * yDiffSign > 0) { + // closer to the equator - by construction all of these happen first + // move the text vertically to get away from these slices + otherOuterY = otherPt.cyFinal + farthestY(otherPt.px0[1], otherPt.px1[1]); + newExtraY = otherOuterY - thisInnerY - thisPt.labelExtraY; + + if(newExtraY * yDiffSign > 0) thisPt.labelExtraY += newExtraY; + } else if((thisOuterY + thisPt.labelExtraY - thisSliceOuterY) * yDiffSign > 0) { + // farther from the equator - happens after we've done all the + // vertical moving we're going to do + // move horizontally to get away from these more polar slices + + // if we're moving horz. based on a slice that's several slices away from this one + // then we need some extra space for the lines to labels between them + xBuffer = 3 * xDiffSign * Math.abs(i - wholeSide.indexOf(thisPt)); + + otherOuterX = otherPt.cxFinal + farthestX(otherPt.px0[0], otherPt.px1[0]); + newExtraX = otherOuterX + xBuffer - (thisPt.cxFinal + thisPt.pxmid[0]) - thisPt.labelExtraX; + + if(newExtraX * xDiffSign > 0) thisPt.labelExtraX += newExtraX; + } + } + } + + for(yHalf = 0; yHalf < 2; yHalf++) { + equatorFirst = yHalf ? topFirst : bottomFirst; + farthestY = yHalf ? Math.max : Math.min; + yDiffSign = yHalf ? 1 : -1; + + for(xHalf = 0; xHalf < 2; xHalf++) { + farthestX = xHalf ? Math.max : Math.min; + xDiffSign = xHalf ? 1 : -1; + + // first sort the array + // note this is a copy of cd, so cd itself doesn't get sorted + // but we can still modify points in place. + thisQuad = quadrants[yHalf][xHalf]; + thisQuad.sort(equatorFirst); + + oppositeQuad = quadrants[1 - yHalf][xHalf]; + wholeSide = oppositeQuad.concat(thisQuad); + + thisQuadOutside = []; + for(i = 0; i < thisQuad.length; i++) { + if(thisQuad[i].yLabelMid !== undefined) thisQuadOutside.push(thisQuad[i]); + } + + firstOppositeOutsidePt = false; + for(i = 0; yHalf && i < oppositeQuad.length; i++) { + if(oppositeQuad[i].yLabelMid !== undefined) { + firstOppositeOutsidePt = oppositeQuad[i]; + break; + } + } + + // each needs to avoid the previous + for(i = 0; i < thisQuadOutside.length; i++) { + var prevPt = i && thisQuadOutside[i - 1]; + // bottom half needs to avoid the first label of the top half + // top half we still need to call scootOneLabel on the first slice + // so we can avoid other slices, but we don't pass a prevPt + if(firstOppositeOutsidePt && !i) prevPt = firstOppositeOutsidePt; + scootOneLabel(thisQuadOutside[i], prevPt); + } + } + } +} + +function layoutAreas(cdModule, plotSize) { + var scaleGroups = []; + + // figure out the center and maximum radius + for(var i = 0; i < cdModule.length; i++) { + var cd0 = cdModule[i][0]; + var trace = cd0.trace; + + var domain = trace.domain; + var width = plotSize.w * (domain.x[1] - domain.x[0]); + var height = plotSize.h * (domain.y[1] - domain.y[0]); + // leave some space for the title, if it will be displayed outside + if(trace.title.text && trace.title.position !== 'middle center') { + height -= getTitleSpace(cd0, plotSize); + } + + var rx = width / 2; + var ry = height / 2; + if(trace.type === 'funnelarea' && !trace.scalegroup) { + ry /= trace.aspectratio; + } + + cd0.r = Math.min(rx, ry) / (1 + getMaxPull(trace)); + + cd0.cx = plotSize.l + plotSize.w * (trace.domain.x[1] + trace.domain.x[0]) / 2; + cd0.cy = plotSize.t + plotSize.h * (1 - trace.domain.y[0]) - height / 2; + if(trace.title.text && trace.title.position.indexOf('bottom') !== -1) { + cd0.cy -= getTitleSpace(cd0, plotSize); + } + + if(trace.scalegroup && scaleGroups.indexOf(trace.scalegroup) === -1) { + scaleGroups.push(trace.scalegroup); + } + } + + groupScale(cdModule, scaleGroups); +} + +function groupScale(cdModule, scaleGroups) { + var cd0, i, trace; + + // scale those that are grouped + for(var k = 0; k < scaleGroups.length; k++) { + var min = Infinity; + var g = scaleGroups[k]; + + for(i = 0; i < cdModule.length; i++) { + cd0 = cdModule[i][0]; + trace = cd0.trace; + + if(trace.scalegroup === g) { + var area; + if(trace.type === 'pie') { + area = cd0.r * cd0.r; + } else if(trace.type === 'funnelarea') { + var rx, ry; + + if(trace.aspectratio > 1) { + rx = cd0.r; + ry = rx / trace.aspectratio; + } else { + ry = cd0.r; + rx = ry * trace.aspectratio; + } + + rx *= (1 + trace.baseratio) / 2; + + area = rx * ry; + } + + min = Math.min(min, area / cd0.vTotal); + } + } + + for(i = 0; i < cdModule.length; i++) { + cd0 = cdModule[i][0]; + trace = cd0.trace; + if(trace.scalegroup === g) { + var v = min * cd0.vTotal; + if(trace.type === 'funnelarea') { + v /= (1 + trace.baseratio) / 2; + v /= trace.aspectratio; + } + + cd0.r = Math.sqrt(v); + } + } + } +} + +function setCoords(cd) { + var cd0 = cd[0]; + var r = cd0.r; + var trace = cd0.trace; + var currentAngle = helpers.getRotationAngle(trace.rotation); + var angleFactor = 2 * Math.PI / cd0.vTotal; + var firstPt = 'px0'; + var lastPt = 'px1'; + + var i, cdi, currentCoords; + + if(trace.direction === 'counterclockwise') { + for(i = 0; i < cd.length; i++) { + if(!cd[i].hidden) break; // find the first non-hidden slice + } + if(i === cd.length) return; // all slices hidden + + currentAngle += angleFactor * cd[i].v; + angleFactor *= -1; + firstPt = 'px1'; + lastPt = 'px0'; + } + + currentCoords = getCoords(r, currentAngle); + + for(i = 0; i < cd.length; i++) { + cdi = cd[i]; + if(cdi.hidden) continue; + + cdi[firstPt] = currentCoords; + + cdi.startangle = currentAngle; + currentAngle += angleFactor * cdi.v / 2; + cdi.pxmid = getCoords(r, currentAngle); + cdi.midangle = currentAngle; + currentAngle += angleFactor * cdi.v / 2; + currentCoords = getCoords(r, currentAngle); + cdi.stopangle = currentAngle; + + cdi[lastPt] = currentCoords; + + cdi.largeArc = (cdi.v > cd0.vTotal / 2) ? 1 : 0; + + cdi.halfangle = Math.PI * Math.min(cdi.v / cd0.vTotal, 0.5); + cdi.ring = 1 - trace.hole; + cdi.rInscribed = getInscribedRadiusFraction(cdi, cd0); + } +} + +function getCoords(r, angle) { + return [r * Math.sin(angle), -r * Math.cos(angle)]; +} + +function formatSliceLabel(gd, pt, cd0) { + var fullLayout = gd._fullLayout; + var trace = cd0.trace; + // look for textemplate + var texttemplate = trace.texttemplate; + + // now insert text + var textinfo = trace.textinfo; + if(!texttemplate && textinfo && textinfo !== 'none') { + var parts = textinfo.split('+'); + var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; }; + var hasLabel = hasFlag('label'); + var hasText = hasFlag('text'); + var hasValue = hasFlag('value'); + var hasPercent = hasFlag('percent'); + + var separators = fullLayout.separators; + var text; + + text = hasLabel ? [pt.label] : []; + if(hasText) { + var tx = helpers.getFirstFilled(trace.text, pt.pts); + if(isValidTextValue(tx)) text.push(tx); + } + if(hasValue) text.push(helpers.formatPieValue(pt.v, separators)); + if(hasPercent) text.push(helpers.formatPiePercent(pt.v / cd0.vTotal, separators)); + pt.text = text.join('
'); + } + + function makeTemplateVariables(pt) { + return { + label: pt.label, + value: pt.v, + valueLabel: helpers.formatPieValue(pt.v, fullLayout.separators), + percent: pt.v / cd0.vTotal, + percentLabel: helpers.formatPiePercent(pt.v / cd0.vTotal, fullLayout.separators), + color: pt.color, + text: pt.text, + customdata: Lib.castOption(trace, pt.i, 'customdata') + }; + } + + if(texttemplate) { + var txt = Lib.castOption(trace, pt.i, 'texttemplate'); + if(!txt) { + pt.text = ''; + } else { + var obj = makeTemplateVariables(pt); + var ptTx = helpers.getFirstFilled(trace.text, pt.pts); + if(isValidTextValue(ptTx) || ptTx === '') obj.text = ptTx; + pt.text = Lib.texttemplateString(txt, obj, gd._fullLayout._d3locale, obj, trace._meta || {}); + } + } +} + +function computeTransform( + transform, // inout + textBB // in +) { + var a = transform.rotate * Math.PI / 180; + var cosA = Math.cos(a); + var sinA = Math.sin(a); + var midX = (textBB.left + textBB.right) / 2; + var midY = (textBB.top + textBB.bottom) / 2; + transform.textX = midX * cosA - midY * sinA; + transform.textY = midX * sinA + midY * cosA; + transform.noCenter = true; +} + +module.exports = { + plot: plot, + formatSliceLabel: formatSliceLabel, + transformInsideText: transformInsideText, + determineInsideTextFont: determineInsideTextFont, + positionTitleOutside: positionTitleOutside, + prerenderTitles: prerenderTitles, + layoutAreas: layoutAreas, + attachFxHandlers: attachFxHandlers, + computeTransform: computeTransform +}; + +},{"../../components/color":155,"../../components/drawing":177,"../../components/fx":195,"../../lib":285,"../../lib/svg_text_utils":307,"../../plots/plots":366,"../bar/constants":385,"../bar/uniform_text":399,"./event_data":485,"./helpers":486,"@plotly/d3":20}],491:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var styleOne = _dereq_('./style_one'); +var resizeText = _dereq_('../bar/uniform_text').resizeText; + +module.exports = function style(gd) { + var s = gd._fullLayout._pielayer.selectAll('.trace'); + resizeText(gd, s, 'pie'); + + s.each(function(cd) { + var cd0 = cd[0]; + var trace = cd0.trace; + var traceSelection = d3.select(this); + + traceSelection.style({opacity: trace.opacity}); + + traceSelection.selectAll('path.surface').each(function(pt) { + d3.select(this).call(styleOne, pt, trace); + }); + }); +}; + +},{"../bar/uniform_text":399,"./style_one":492,"@plotly/d3":20}],492:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); +var castOption = _dereq_('./helpers').castOption; + +module.exports = function styleOne(s, pt, trace) { + var line = trace.marker.line; + var lineColor = castOption(line.color, pt.pts) || Color.defaultLine; + var lineWidth = castOption(line.width, pt.pts) || 0; + + s.style('stroke-width', lineWidth) + .call(Color.fill, pt.color) + .call(Color.stroke, lineColor); +}; + +},{"../../components/color":155,"./helpers":486}],493:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + + +// arrayOk attributes, merge them into calcdata array +module.exports = function arraysToCalcdata(cd, trace) { + // so each point knows which index it originally came from + for(var i = 0; i < cd.length; i++) cd[i].i = i; + + Lib.mergeArray(trace.text, cd, 'tx'); + Lib.mergeArray(trace.texttemplate, cd, 'txt'); + Lib.mergeArray(trace.hovertext, cd, 'htx'); + Lib.mergeArray(trace.customdata, cd, 'data'); + Lib.mergeArray(trace.textposition, cd, 'tp'); + if(trace.textfont) { + Lib.mergeArrayCastPositive(trace.textfont.size, cd, 'ts'); + Lib.mergeArray(trace.textfont.color, cd, 'tc'); + Lib.mergeArray(trace.textfont.family, cd, 'tf'); + } + + var marker = trace.marker; + if(marker) { + Lib.mergeArrayCastPositive(marker.size, cd, 'ms'); + Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo'); + Lib.mergeArray(marker.symbol, cd, 'mx'); + Lib.mergeArray(marker.color, cd, 'mc'); + + var markerLine = marker.line; + if(marker.line) { + Lib.mergeArray(markerLine.color, cd, 'mlc'); + Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw'); + } + + var markerGradient = marker.gradient; + if(markerGradient && markerGradient.type !== 'none') { + Lib.mergeArray(markerGradient.type, cd, 'mgt'); + Lib.mergeArray(markerGradient.color, cd, 'mgc'); + } + } +}; + +},{"../../lib":285}],494:[function(_dereq_,module,exports){ +'use strict'; + +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var fontAttrs = _dereq_('../../plots/font_attributes'); +var dash = _dereq_('../../components/drawing/attributes').dash; + +var Drawing = _dereq_('../../components/drawing'); +var constants = _dereq_('./constants'); + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +function axisPeriod(axis) { + return { + valType: 'any', + dflt: 0, + editType: 'calc', + }; +} + +function axisPeriod0(axis) { + return { + valType: 'any', + editType: 'calc', + }; +} + +function axisPeriodAlignment(axis) { + return { + valType: 'enumerated', + values: [ + 'start', 'middle', 'end' + ], + dflt: 'middle', + editType: 'calc', + }; +} + +module.exports = { + x: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + anim: true, + }, + x0: { + valType: 'any', + dflt: 0, + editType: 'calc+clearAxisTypes', + anim: true, + }, + dx: { + valType: 'number', + dflt: 1, + editType: 'calc', + anim: true, + }, + y: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + anim: true, + }, + y0: { + valType: 'any', + dflt: 0, + editType: 'calc+clearAxisTypes', + anim: true, + }, + dy: { + valType: 'number', + dflt: 1, + editType: 'calc', + anim: true, + }, + + xperiod: axisPeriod('x'), + yperiod: axisPeriod('y'), + xperiod0: axisPeriod0('x0'), + yperiod0: axisPeriod0('y0'), + xperiodalignment: axisPeriodAlignment('x'), + yperiodalignment: axisPeriodAlignment('y'), + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + + stackgroup: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + orientation: { + valType: 'enumerated', + values: ['v', 'h'], + editType: 'calc', + }, + groupnorm: { + valType: 'enumerated', + values: ['', 'fraction', 'percent'], + dflt: '', + editType: 'calc', + }, + stackgaps: { + valType: 'enumerated', + values: ['infer zero', 'interpolate'], + dflt: 'infer zero', + editType: 'calc', + }, + + text: { + valType: 'string', + dflt: '', + arrayOk: true, + editType: 'calc', + }, + + texttemplate: texttemplateAttrs({}, { + + }), + hovertext: { + valType: 'string', + dflt: '', + arrayOk: true, + editType: 'style', + }, + mode: { + valType: 'flaglist', + flags: ['lines', 'markers', 'text'], + extras: ['none'], + editType: 'calc', + }, + hoveron: { + valType: 'flaglist', + flags: ['points', 'fills'], + editType: 'style', + }, + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + + line: { + color: { + valType: 'color', + editType: 'style', + anim: true, + }, + width: { + valType: 'number', + min: 0, + dflt: 2, + editType: 'style', + anim: true, + }, + shape: { + valType: 'enumerated', + values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'], + dflt: 'linear', + editType: 'plot', + }, + smoothing: { + valType: 'number', + min: 0, + max: 1.3, + dflt: 1, + editType: 'plot', + }, + dash: extendFlat({}, dash, {editType: 'style'}), + simplify: { + valType: 'boolean', + dflt: true, + editType: 'plot', + }, + editType: 'plot' + }, + + connectgaps: { + valType: 'boolean', + dflt: false, + editType: 'calc', + }, + cliponaxis: { + valType: 'boolean', + dflt: true, + editType: 'plot', + }, + + fill: { + valType: 'enumerated', + values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'], + editType: 'calc', + }, + fillcolor: { + valType: 'color', + editType: 'style', + anim: true, + }, + marker: extendFlat({ + symbol: { + valType: 'enumerated', + values: Drawing.symbolList, + dflt: 'circle', + arrayOk: true, + editType: 'style', + }, + opacity: { + valType: 'number', + min: 0, + max: 1, + arrayOk: true, + editType: 'style', + anim: true, + }, + size: { + valType: 'number', + min: 0, + dflt: 6, + arrayOk: true, + editType: 'calc', + anim: true, + }, + maxdisplayed: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'plot', + }, + sizeref: { + valType: 'number', + dflt: 1, + editType: 'calc', + }, + sizemin: { + valType: 'number', + min: 0, + dflt: 0, + editType: 'calc', + }, + sizemode: { + valType: 'enumerated', + values: ['diameter', 'area'], + dflt: 'diameter', + editType: 'calc', + }, + + line: extendFlat({ + width: { + valType: 'number', + min: 0, + arrayOk: true, + editType: 'style', + anim: true, + }, + editType: 'calc' + }, + colorScaleAttrs('marker.line', {anim: true}) + ), + gradient: { + type: { + valType: 'enumerated', + values: ['radial', 'horizontal', 'vertical', 'none'], + arrayOk: true, + dflt: 'none', + editType: 'calc', + }, + color: { + valType: 'color', + arrayOk: true, + editType: 'calc', + }, + editType: 'calc' + }, + editType: 'calc' + }, + colorScaleAttrs('marker', {anim: true}) + ), + selected: { + marker: { + opacity: { + valType: 'number', + min: 0, + max: 1, + editType: 'style', + }, + color: { + valType: 'color', + editType: 'style', + }, + size: { + valType: 'number', + min: 0, + editType: 'style', + }, + editType: 'style' + }, + textfont: { + color: { + valType: 'color', + editType: 'style', + }, + editType: 'style' + }, + editType: 'style' + }, + unselected: { + marker: { + opacity: { + valType: 'number', + min: 0, + max: 1, + editType: 'style', + }, + color: { + valType: 'color', + editType: 'style', + }, + size: { + valType: 'number', + min: 0, + editType: 'style', + }, + editType: 'style' + }, + textfont: { + color: { + valType: 'color', + editType: 'style', + }, + editType: 'style' + }, + editType: 'style' + }, + + textposition: { + valType: 'enumerated', + values: [ + 'top left', 'top center', 'top right', + 'middle left', 'middle center', 'middle right', + 'bottom left', 'bottom center', 'bottom right' + ], + dflt: 'middle center', + arrayOk: true, + editType: 'calc', + }, + textfont: fontAttrs({ + editType: 'calc', + colorEditType: 'style', + arrayOk: true, + }), +}; + +},{"../../components/colorscale/attributes":162,"../../components/drawing":177,"../../components/drawing/attributes":176,"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../../plots/font_attributes":360,"../../plots/template_attributes":368,"./constants":498}],495:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); + +var Axes = _dereq_('../../plots/cartesian/axes'); +var alignPeriod = _dereq_('../../plots/cartesian/align_period'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +var subTypes = _dereq_('./subtypes'); +var calcColorscale = _dereq_('./colorscale_calc'); +var arraysToCalcdata = _dereq_('./arrays_to_calcdata'); +var calcSelection = _dereq_('./calc_selection'); + +function calc(gd, trace) { + var fullLayout = gd._fullLayout; + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + var origX = xa.makeCalcdata(trace, 'x'); + var origY = ya.makeCalcdata(trace, 'y'); + var x = alignPeriod(trace, xa, 'x', origX); + var y = alignPeriod(trace, ya, 'y', origY); + + var serieslen = trace._length; + var cd = new Array(serieslen); + var ids = trace.ids; + var stackGroupOpts = getStackOpts(trace, fullLayout, xa, ya); + var interpolateGaps = false; + var isV, i, j, k, interpolate, vali; + + setFirstScatter(fullLayout, trace); + + var xAttr = 'x'; + var yAttr = 'y'; + var posAttr; + if(stackGroupOpts) { + Lib.pushUnique(stackGroupOpts.traceIndices, trace._expandedIndex); + isV = stackGroupOpts.orientation === 'v'; + + // size, like we use for bar + if(isV) { + yAttr = 's'; + posAttr = 'x'; + } else { + xAttr = 's'; + posAttr = 'y'; + } + interpolate = stackGroupOpts.stackgaps === 'interpolate'; + } else { + var ppad = calcMarkerSize(trace, serieslen); + calcAxisExpansion(gd, trace, xa, ya, x, y, ppad); + } + + var hasPeriodX = !!trace.xperiodalignment; + var hasPeriodY = !!trace.yperiodalignment; + + for(i = 0; i < serieslen; i++) { + var cdi = cd[i] = {}; + var xValid = isNumeric(x[i]); + var yValid = isNumeric(y[i]); + if(xValid && yValid) { + cdi[xAttr] = x[i]; + cdi[yAttr] = y[i]; + + if(hasPeriodX) { + cdi.orig_x = origX[i]; // used by hover + } + if(hasPeriodY) { + cdi.orig_y = origY[i]; // used by hover + } + } else if(stackGroupOpts && (isV ? xValid : yValid)) { + // if we're stacking we need to hold on to all valid positions + // even with invalid sizes + + cdi[posAttr] = isV ? x[i] : y[i]; + cdi.gap = true; + if(interpolate) { + cdi.s = BADNUM; + interpolateGaps = true; + } else { + cdi.s = 0; + } + } else { + cdi[xAttr] = cdi[yAttr] = BADNUM; + } + + if(ids) { + cdi.id = String(ids[i]); + } + } + + arraysToCalcdata(cd, trace); + calcColorscale(gd, trace); + calcSelection(cd, trace); + + if(stackGroupOpts) { + // remove bad positions and sort + // note that original indices get added to cd in arraysToCalcdata + i = 0; + while(i < cd.length) { + if(cd[i][posAttr] === BADNUM) { + cd.splice(i, 1); + } else i++; + } + + Lib.sort(cd, function(a, b) { + return (a[posAttr] - b[posAttr]) || (a.i - b.i); + }); + + if(interpolateGaps) { + // first fill the beginning with constant from the first point + i = 0; + while(i < cd.length - 1 && cd[i].gap) { + i++; + } + vali = cd[i].s; + if(!vali) vali = cd[i].s = 0; // in case of no data AT ALL in this trace - use 0 + for(j = 0; j < i; j++) { + cd[j].s = vali; + } + // then fill the end with constant from the last point + k = cd.length - 1; + while(k > i && cd[k].gap) { + k--; + } + vali = cd[k].s; + for(j = cd.length - 1; j > k; j--) { + cd[j].s = vali; + } + // now interpolate internal gaps linearly + while(i < k) { + i++; + if(cd[i].gap) { + j = i + 1; + while(cd[j].gap) { + j++; + } + var pos0 = cd[i - 1][posAttr]; + var size0 = cd[i - 1].s; + var m = (cd[j].s - size0) / (cd[j][posAttr] - pos0); + while(i < j) { + cd[i].s = size0 + (cd[i][posAttr] - pos0) * m; + i++; + } + } + } + } + } + + return cd; +} + +function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) { + var serieslen = trace._length; + var fullLayout = gd._fullLayout; + var xId = xa._id; + var yId = ya._id; + var firstScatter = fullLayout._firstScatter[firstScatterGroup(trace)] === trace.uid; + var stackOrientation = (getStackOpts(trace, fullLayout, xa, ya) || {}).orientation; + var fill = trace.fill; + + // cancel minimum tick spacings (only applies to bars and boxes) + xa._minDtick = 0; + ya._minDtick = 0; + + // check whether bounds should be tight, padded, extended to zero... + // most cases both should be padded on both ends, so start with that. + var xOptions = {padded: true}; + var yOptions = {padded: true}; + + if(ppad) { + xOptions.ppad = yOptions.ppad = ppad; + } + + // TODO: text size + + var openEnded = serieslen < 2 || (x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]); + + if(openEnded && ( + (fill === 'tozerox') || + ((fill === 'tonextx') && (firstScatter || stackOrientation === 'h')) + )) { + // include zero (tight) and extremes (padded) if fill to zero + // (unless the shape is closed, then it's just filling the shape regardless) + + xOptions.tozero = true; + } else if(!(trace.error_y || {}).visible && ( + // if no error bars, markers or text, or fill to y=0 remove x padding + + (fill === 'tonexty' || fill === 'tozeroy') || + (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace)) + )) { + xOptions.padded = false; + xOptions.ppad = 0; + } + + if(openEnded && ( + (fill === 'tozeroy') || + ((fill === 'tonexty') && (firstScatter || stackOrientation === 'v')) + )) { + // now check for y - rather different logic, though still mostly padded both ends + // include zero (tight) and extremes (padded) if fill to zero + // (unless the shape is closed, then it's just filling the shape regardless) + + yOptions.tozero = true; + } else if(fill === 'tonextx' || fill === 'tozerox') { + // tight y: any x fill + + yOptions.padded = false; + } + + // N.B. asymmetric splom traces call this with blank {} xa or ya + if(xId) trace._extremes[xId] = Axes.findExtremes(xa, x, xOptions); + if(yId) trace._extremes[yId] = Axes.findExtremes(ya, y, yOptions); +} + +function calcMarkerSize(trace, serieslen) { + if(!subTypes.hasMarkers(trace)) return; + + // Treat size like x or y arrays --- Run d2c + // this needs to go before ppad computation + var marker = trace.marker; + var sizeref = 1.6 * (trace.marker.sizeref || 1); + var markerTrans; + + if(trace.marker.sizemode === 'area') { + markerTrans = function(v) { + return Math.max(Math.sqrt((v || 0) / sizeref), 3); + }; + } else { + markerTrans = function(v) { + return Math.max((v || 0) / sizeref, 3); + }; + } + + if(Lib.isArrayOrTypedArray(marker.size)) { + // I tried auto-type but category and dates dont make much sense. + var ax = {type: 'linear'}; + Axes.setConvert(ax); + + var s = ax.makeCalcdata(trace.marker, 'size'); + + var sizeOut = new Array(serieslen); + for(var i = 0; i < serieslen; i++) { + sizeOut[i] = markerTrans(s[i]); + } + return sizeOut; + } else { + return markerTrans(marker.size); + } +} + +/** + * mark the first scatter trace for each subplot + * note that scatter and scattergl each get their own first trace + * note also that I'm doing this during calc rather than supplyDefaults + * so I don't need to worry about transforms, but if we ever do + * per-trace calc this will get confused. + */ +function setFirstScatter(fullLayout, trace) { + var group = firstScatterGroup(trace); + var firstScatter = fullLayout._firstScatter; + if(!firstScatter[group]) firstScatter[group] = trace.uid; +} + +function firstScatterGroup(trace) { + var stackGroup = trace.stackgroup; + return trace.xaxis + trace.yaxis + trace.type + + (stackGroup ? '-' + stackGroup : ''); +} + +function getStackOpts(trace, fullLayout, xa, ya) { + var stackGroup = trace.stackgroup; + if(!stackGroup) return; + var stackOpts = fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup]; + var stackAx = stackOpts.orientation === 'v' ? ya : xa; + // Allow stacking only on numeric axes + // calc is a little late to be figuring this out, but during supplyDefaults + // we don't know the axis type yet + if(stackAx.type === 'linear' || stackAx.type === 'log') return stackOpts; +} + +module.exports = { + calc: calc, + calcMarkerSize: calcMarkerSize, + calcAxisExpansion: calcAxisExpansion, + setFirstScatter: setFirstScatter, + getStackOpts: getStackOpts +}; + +},{"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/align_period":328,"../../plots/cartesian/axes":331,"./arrays_to_calcdata":493,"./calc_selection":496,"./colorscale_calc":497,"./subtypes":519,"fast-isnumeric":31}],496:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +module.exports = function calcSelection(cd, trace) { + if(Lib.isArrayOrTypedArray(trace.selectedpoints)) { + Lib.tagSelected(cd, trace); + } +}; + +},{"../../lib":285}],497:[function(_dereq_,module,exports){ +'use strict'; + +var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale; +var calcColorscale = _dereq_('../../components/colorscale/calc'); + +var subTypes = _dereq_('./subtypes'); + +module.exports = function calcMarkerColorscale(gd, trace) { + if(subTypes.hasLines(trace) && hasColorscale(trace, 'line')) { + calcColorscale(gd, trace, { + vals: trace.line.color, + containerStr: 'line', + cLetter: 'c' + }); + } + + if(subTypes.hasMarkers(trace)) { + if(hasColorscale(trace, 'marker')) { + calcColorscale(gd, trace, { + vals: trace.marker.color, + containerStr: 'marker', + cLetter: 'c' + }); + } + if(hasColorscale(trace, 'marker.line')) { + calcColorscale(gd, trace, { + vals: trace.marker.line.color, + containerStr: 'marker.line', + cLetter: 'c' + }); + } + } +}; + +},{"../../components/colorscale/calc":163,"../../components/colorscale/helpers":166,"./subtypes":519}],498:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + PTS_LINESONLY: 20, + + // fixed parameters of clustering and clipping algorithms + + // fraction of clustering tolerance "so close we don't even consider it a new point" + minTolerance: 0.2, + // how fast does clustering tolerance increase as you get away from the visible region + toleranceGrowth: 10, + + // number of viewport sizes away from the visible region + // at which we clip all lines to the perimeter + maxScreensAway: 20, + + eventDataKeys: [] +}; + +},{}],499:[function(_dereq_,module,exports){ +'use strict'; + +var calc = _dereq_('./calc'); + +/* + * Scatter stacking & normalization calculations + * runs per subplot, and can handle multiple stacking groups + */ + +module.exports = function crossTraceCalc(gd, plotinfo) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var subplot = xa._id + ya._id; + + var subplotStackOpts = gd._fullLayout._scatterStackOpts[subplot]; + if(!subplotStackOpts) return; + + var calcTraces = gd.calcdata; + + var i, j, k, i2, cd, cd0, posj, sumj, norm; + var groupOpts, interpolate, groupnorm, posAttr, valAttr; + var hasAnyBlanks; + + for(var stackGroup in subplotStackOpts) { + groupOpts = subplotStackOpts[stackGroup]; + var indices = groupOpts.traceIndices; + + // can get here with no indices if the stack axis is non-numeric + if(!indices.length) continue; + + interpolate = groupOpts.stackgaps === 'interpolate'; + groupnorm = groupOpts.groupnorm; + if(groupOpts.orientation === 'v') { + posAttr = 'x'; + valAttr = 'y'; + } else { + posAttr = 'y'; + valAttr = 'x'; + } + hasAnyBlanks = new Array(indices.length); + for(i = 0; i < hasAnyBlanks.length; i++) { + hasAnyBlanks[i] = false; + } + + // Collect the complete set of all positions across ALL traces. + // Start with the first trace, then interleave items from later traces + // as needed. + // Fill in mising items as we go. + cd0 = calcTraces[indices[0]]; + var allPositions = new Array(cd0.length); + for(i = 0; i < cd0.length; i++) { + allPositions[i] = cd0[i][posAttr]; + } + + for(i = 1; i < indices.length; i++) { + cd = calcTraces[indices[i]]; + + for(j = k = 0; j < cd.length; j++) { + posj = cd[j][posAttr]; + for(; posj > allPositions[k] && k < allPositions.length; k++) { + // the current trace is missing a position from some previous trace(s) + insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr); + j++; + } + if(posj !== allPositions[k]) { + // previous trace(s) are missing a position from the current trace + for(i2 = 0; i2 < i; i2++) { + insertBlank(calcTraces[indices[i2]], k, posj, i2, hasAnyBlanks, interpolate, posAttr); + } + allPositions.splice(k, 0, posj); + } + k++; + } + for(; k < allPositions.length; k++) { + insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr); + j++; + } + } + + var serieslen = allPositions.length; + + // stack (and normalize)! + for(j = 0; j < cd0.length; j++) { + sumj = cd0[j][valAttr] = cd0[j].s; + for(i = 1; i < indices.length; i++) { + cd = calcTraces[indices[i]]; + cd[0].trace._rawLength = cd[0].trace._length; + cd[0].trace._length = serieslen; + sumj += cd[j].s; + cd[j][valAttr] = sumj; + } + + if(groupnorm) { + norm = ((groupnorm === 'fraction') ? sumj : (sumj / 100)) || 1; + for(i = 0; i < indices.length; i++) { + var cdj = calcTraces[indices[i]][j]; + cdj[valAttr] /= norm; + cdj.sNorm = cdj.s / norm; + } + } + } + + // autorange + for(i = 0; i < indices.length; i++) { + cd = calcTraces[indices[i]]; + var trace = cd[0].trace; + var ppad = calc.calcMarkerSize(trace, trace._rawLength); + var arrayPad = Array.isArray(ppad); + if((ppad && hasAnyBlanks[i]) || arrayPad) { + var ppadRaw = ppad; + ppad = new Array(serieslen); + for(j = 0; j < serieslen; j++) { + ppad[j] = cd[j].gap ? 0 : (arrayPad ? ppadRaw[cd[j].i] : ppadRaw); + } + } + var x = new Array(serieslen); + var y = new Array(serieslen); + for(j = 0; j < serieslen; j++) { + x[j] = cd[j].x; + y[j] = cd[j].y; + } + calc.calcAxisExpansion(gd, trace, xa, ya, x, y, ppad); + + // while we're here (in a loop over all traces in the stack) + // record the orientation, so hover can find it easily + cd[0].t.orientation = groupOpts.orientation; + } + } +}; + +function insertBlank(calcTrace, index, position, traceIndex, hasAnyBlanks, interpolate, posAttr) { + hasAnyBlanks[traceIndex] = true; + var newEntry = { + i: null, + gap: true, + s: 0 + }; + newEntry[posAttr] = position; + calcTrace.splice(index, 0, newEntry); + // Even if we're not interpolating, if one trace has multiple + // values at the same position and this trace only has one value there, + // we just duplicate that one value rather than insert a zero. + // We also make it look like a real point - because it's ambiguous which + // one really is the real one! + if(index && position === calcTrace[index - 1][posAttr]) { + var prevEntry = calcTrace[index - 1]; + newEntry.s = prevEntry.s; + // TODO is it going to cause any problems to have multiple + // calcdata points with the same index? + newEntry.i = prevEntry.i; + newEntry.gap = prevEntry.gap; + } else if(interpolate) { + newEntry.s = getInterp(calcTrace, index, position, posAttr); + } + if(!index) { + // t and trace need to stay on the first cd entry + calcTrace[0].t = calcTrace[1].t; + calcTrace[0].trace = calcTrace[1].trace; + delete calcTrace[1].t; + delete calcTrace[1].trace; + } +} + +function getInterp(calcTrace, index, position, posAttr) { + var pt0 = calcTrace[index - 1]; + var pt1 = calcTrace[index + 1]; + if(!pt1) return pt0.s; + if(!pt0) return pt1.s; + return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]); +} + +},{"./calc":495}],500:[function(_dereq_,module,exports){ +'use strict'; + + +// remove opacity for any trace that has a fill or is filled to +module.exports = function crossTraceDefaults(fullData) { + for(var i = 0; i < fullData.length; i++) { + var tracei = fullData[i]; + if(tracei.type !== 'scatter') continue; + + var filli = tracei.fill; + if(filli === 'none' || filli === 'toself') continue; + + tracei.opacity = undefined; + + if(filli === 'tonexty' || filli === 'tonextx') { + for(var j = i - 1; j >= 0; j--) { + var tracej = fullData[j]; + + if((tracej.type === 'scatter') && + (tracej.xaxis === tracei.xaxis) && + (tracej.yaxis === tracei.yaxis)) { + tracej.opacity = undefined; + break; + } + } + } + } +}; + +},{}],501:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); + +var attributes = _dereq_('./attributes'); +var constants = _dereq_('./constants'); +var subTypes = _dereq_('./subtypes'); +var handleXYDefaults = _dereq_('./xy_defaults'); +var handlePeriodDefaults = _dereq_('./period_defaults'); +var handleStackDefaults = _dereq_('./stack_defaults'); +var handleMarkerDefaults = _dereq_('./marker_defaults'); +var handleLineDefaults = _dereq_('./line_defaults'); +var handleLineShapeDefaults = _dereq_('./line_shape_defaults'); +var handleTextDefaults = _dereq_('./text_defaults'); +var handleFillColorDefaults = _dereq_('./fillcolor_defaults'); + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var len = handleXYDefaults(traceIn, traceOut, layout, coerce); + if(!len) traceOut.visible = false; + + if(!traceOut.visible) return; + + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('xhoverformat'); + coerce('yhoverformat'); + + var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce); + + var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ? + 'lines+markers' : 'lines'; + coerce('text'); + coerce('hovertext'); + coerce('mode', defaultMode); + + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineShapeDefaults(traceIn, traceOut, coerce); + coerce('connectgaps'); + coerce('line.simplify'); + } + + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + + if(subTypes.hasText(traceOut)) { + coerce('texttemplate'); + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); + coerce('marker.maxdisplayed'); + dfltHoverOn.push('points'); + } + + // It's possible for this default to be changed by a later trace. + // We handle that case in some hacky code inside handleStackDefaults. + coerce('fill', stackGroupOpts ? stackGroupOpts.fillDflt : 'none'); + if(traceOut.fill !== 'none') { + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce); + } + + var lineColor = (traceOut.line || {}).color; + var markerColor = (traceOut.marker || {}).color; + + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + coerce('hoveron', dfltHoverOn.join('+') || 'points'); + if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults'); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'y'}); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'x', inherit: 'y'}); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +}; + +},{"../../lib":285,"../../registry":373,"./attributes":494,"./constants":498,"./fillcolor_defaults":502,"./line_defaults":507,"./line_shape_defaults":509,"./marker_defaults":513,"./period_defaults":514,"./stack_defaults":517,"./subtypes":519,"./text_defaults":520,"./xy_defaults":521}],502:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; + +module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coerce) { + var inheritColorFromMarker = false; + + if(traceOut.marker) { + // don't try to inherit a color array + var markerColor = traceOut.marker.color; + var markerLineColor = (traceOut.marker.line || {}).color; + + if(markerColor && !isArrayOrTypedArray(markerColor)) { + inheritColorFromMarker = markerColor; + } else if(markerLineColor && !isArrayOrTypedArray(markerLineColor)) { + inheritColorFromMarker = markerLineColor; + } + } + + coerce('fillcolor', Color.addOpacity( + (traceOut.line || {}).color || + inheritColorFromMarker || + defaultColor, 0.5 + )); +}; + +},{"../../components/color":155,"../../lib":285}],503:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function formatLabels(cdi, trace, fullLayout) { + var labels = {}; + + var mockGd = {_fullLayout: fullLayout}; + var xa = Axes.getFromTrace(mockGd, trace, 'x'); + var ya = Axes.getFromTrace(mockGd, trace, 'y'); + + labels.xLabel = Axes.tickText(xa, xa.c2l(cdi.x), true).text; + labels.yLabel = Axes.tickText(ya, ya.c2l(cdi.y), true).text; + + return labels; +}; + +},{"../../plots/cartesian/axes":331}],504:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); +var subtypes = _dereq_('./subtypes'); + + +module.exports = function getTraceColor(trace, di) { + var lc, tc; + + // TODO: text modes + + if(trace.mode === 'lines') { + lc = trace.line.color; + return (lc && Color.opacity(lc)) ? + lc : trace.fillcolor; + } else if(trace.mode === 'none') { + return trace.fill ? trace.fillcolor : ''; + } else { + var mc = di.mcc || (trace.marker || {}).color; + var mlc = di.mlcc || ((trace.marker || {}).line || {}).color; + + tc = (mc && Color.opacity(mc)) ? mc : + (mlc && Color.opacity(mlc) && + (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : ''; + + if(tc) { + // make sure the points aren't TOO transparent + if(Color.opacity(tc) < 0.3) { + return Color.addOpacity(tc, 0.3); + } else return tc; + } else { + lc = (trace.line || {}).color; + return (lc && Color.opacity(lc) && + subtypes.hasLines(trace) && trace.line.width) ? + lc : trace.fillcolor; + } + } +}; + +},{"../../components/color":155,"./subtypes":519}],505:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Fx = _dereq_('../../components/fx'); +var Registry = _dereq_('../../registry'); +var getTraceColor = _dereq_('./get_trace_color'); +var Color = _dereq_('../../components/color'); +var fillText = Lib.fillText; + +module.exports = function hoverPoints(pointData, xval, yval, hovermode) { + var cd = pointData.cd; + var trace = cd[0].trace; + var xa = pointData.xa; + var ya = pointData.ya; + var xpx = xa.c2p(xval); + var ypx = ya.c2p(yval); + var pt = [xpx, ypx]; + var hoveron = trace.hoveron || ''; + var minRad = (trace.mode.indexOf('markers') !== -1) ? 3 : 0.5; + + // look for points to hover on first, then take fills only if we + // didn't find a point + if(hoveron.indexOf('points') !== -1) { + var dx = function(di) { + // dx and dy are used in compare modes - here we want to always + // prioritize the closest data point, at least as long as markers are + // the same size or nonexistent, but still try to prioritize small markers too. + var rad = Math.max(3, di.mrc || 0); + var kink = 1 - 1 / rad; + var dxRaw = Math.abs(xa.c2p(di.x) - xpx); + if(di.orig_x !== undefined) dxRaw += xa.c2p(di.orig_x) - xa.c2p(di.x); + return (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink); + }; + var dy = function(di) { + var rad = Math.max(3, di.mrc || 0); + var kink = 1 - 1 / rad; + var dyRaw = Math.abs(ya.c2p(di.y) - ypx); + if(di.orig_y !== undefined) dyRaw += ya.c2p(di.orig_y) - ya.c2p(di.y); + return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink); + }; + var dxy = function(di) { + // scatter points: d.mrc is the calculated marker radius + // adjust the distance so if you're inside the marker it + // always will show up regardless of point size, but + // prioritize smaller points + var rad = Math.max(minRad, di.mrc || 0); + var dx = xa.c2p(di.x) - xpx; + var dy = ya.c2p(di.y) - ypx; + return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad); + }; + var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); + + Fx.getClosest(cd, distfn, pointData); + + // skip the rest (for this trace) if we didn't find a close point + if(pointData.index !== false) { + // the closest data point + var di = cd[pointData.index]; + var xc = xa.c2p(di.x, true); + var yc = ya.c2p(di.y, true); + var rad = di.mrc || 1; + + // now we're done using the whole `calcdata` array, replace the + // index with the original index (in case of inserted point from + // stacked area) + pointData.index = di.i; + + var orientation = cd[0].t.orientation; + // TODO: for scatter and bar, option to show (sub)totals and + // raw data? Currently stacked and/or normalized bars just show + // the normalized individual sizes, so that's what I'm doing here + // for now. + var sizeVal = orientation && (di.sNorm || di.s); + var xLabelVal = (orientation === 'h') ? sizeVal : di.orig_x !== undefined ? di.orig_x : di.x; + var yLabelVal = (orientation === 'v') ? sizeVal : di.orig_y !== undefined ? di.orig_y : di.y; + + Lib.extendFlat(pointData, { + color: getTraceColor(trace, di), + + x0: xc - rad, + x1: xc + rad, + xLabelVal: xLabelVal, + + y0: yc - rad, + y1: yc + rad, + yLabelVal: yLabelVal, + + spikeDistance: dxy(di), + hovertemplate: trace.hovertemplate + }); + + fillText(di, trace, pointData); + Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData); + + return [pointData]; + } + } + + // even if hoveron is 'fills', only use it if we have polygons too + if(hoveron.indexOf('fills') !== -1 && trace._polygons) { + var polygons = trace._polygons; + var polygonsIn = []; + var inside = false; + var xmin = Infinity; + var xmax = -Infinity; + var ymin = Infinity; + var ymax = -Infinity; + + var i, j, polygon, pts, xCross, x0, x1, y0, y1; + + for(i = 0; i < polygons.length; i++) { + polygon = polygons[i]; + // TODO: this is not going to work right for curved edges, it will + // act as though they're straight. That's probably going to need + // the elements themselves to capture the events. Worth it? + if(polygon.contains(pt)) { + inside = !inside; + // TODO: need better than just the overall bounding box + polygonsIn.push(polygon); + ymin = Math.min(ymin, polygon.ymin); + ymax = Math.max(ymax, polygon.ymax); + } + } + + if(inside) { + // constrain ymin/max to the visible plot, so the label goes + // at the middle of the piece you can see + ymin = Math.max(ymin, 0); + ymax = Math.min(ymax, ya._length); + + // find the overall left-most and right-most points of the + // polygon(s) we're inside at their combined vertical midpoint. + // This is where we will draw the hover label. + // Note that this might not be the vertical midpoint of the + // whole trace, if it's disjoint. + var yAvg = (ymin + ymax) / 2; + for(i = 0; i < polygonsIn.length; i++) { + pts = polygonsIn[i].pts; + for(j = 1; j < pts.length; j++) { + y0 = pts[j - 1][1]; + y1 = pts[j][1]; + if((y0 > yAvg) !== (y1 >= yAvg)) { + x0 = pts[j - 1][0]; + x1 = pts[j][0]; + if(y1 - y0) { + xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0); + xmin = Math.min(xmin, xCross); + xmax = Math.max(xmax, xCross); + } + } + } + } + + // constrain xmin/max to the visible plot now too + xmin = Math.max(xmin, 0); + xmax = Math.min(xmax, xa._length); + + // get only fill or line color for the hover color + var color = Color.defaultLine; + if(Color.opacity(trace.fillcolor)) color = trace.fillcolor; + else if(Color.opacity((trace.line || {}).color)) { + color = trace.line.color; + } + + Lib.extendFlat(pointData, { + // never let a 2D override 1D type as closest point + // also: no spikeDistance, it's not allowed for fills + distance: pointData.maxHoverDistance, + x0: xmin, + x1: xmax, + y0: yAvg, + y1: yAvg, + color: color, + hovertemplate: false + }); + + delete pointData.index; + + if(trace.text && !Array.isArray(trace.text)) { + pointData.text = String(trace.text); + } else pointData.text = trace.name; + + return [pointData]; + } + } +}; + +},{"../../components/color":155,"../../components/fx":195,"../../lib":285,"../../registry":373,"./get_trace_color":504}],506:[function(_dereq_,module,exports){ +'use strict'; + +var subtypes = _dereq_('./subtypes'); + +module.exports = { + hasLines: subtypes.hasLines, + hasMarkers: subtypes.hasMarkers, + hasText: subtypes.hasText, + isBubble: subtypes.isBubble, + + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + crossTraceDefaults: _dereq_('./cross_trace_defaults'), + calc: _dereq_('./calc').calc, + crossTraceCalc: _dereq_('./cross_trace_calc'), + arraysToCalcdata: _dereq_('./arrays_to_calcdata'), + plot: _dereq_('./plot'), + colorbar: _dereq_('./marker_colorbar'), + formatLabels: _dereq_('./format_labels'), + style: _dereq_('./style').style, + styleOnSelect: _dereq_('./style').styleOnSelect, + hoverPoints: _dereq_('./hover'), + selectPoints: _dereq_('./select'), + animatable: true, + + moduleType: 'trace', + name: 'scatter', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: [ + 'cartesian', 'svg', 'symbols', 'errorBarsOK', 'showLegend', 'scatter-like', + 'zoomScale' + ], + meta: { + } +}; + +},{"../../plots/cartesian":345,"./arrays_to_calcdata":493,"./attributes":494,"./calc":495,"./cross_trace_calc":499,"./cross_trace_defaults":500,"./defaults":501,"./format_labels":503,"./hover":505,"./marker_colorbar":512,"./plot":515,"./select":516,"./style":518,"./subtypes":519}],507:[function(_dereq_,module,exports){ +'use strict'; + +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; +var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale; +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); + +module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) { + var markerColor = (traceIn.marker || {}).color; + + coerce('line.color', defaultColor); + + if(hasColorscale(traceIn, 'line')) { + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'line.', cLetter: 'c'}); + } else { + var lineColorDflt = (isArrayOrTypedArray(markerColor) ? false : markerColor) || defaultColor; + coerce('line.color', lineColorDflt); + } + + coerce('line.width'); + if(!(opts || {}).noDash) coerce('line.dash'); +}; + +},{"../../components/colorscale/defaults":165,"../../components/colorscale/helpers":166,"../../lib":285}],508:[function(_dereq_,module,exports){ +'use strict'; + +var numConstants = _dereq_('../../constants/numerical'); +var BADNUM = numConstants.BADNUM; +var LOG_CLIP = numConstants.LOG_CLIP; +var LOG_CLIP_PLUS = LOG_CLIP + 0.5; +var LOG_CLIP_MINUS = LOG_CLIP - 0.5; +var Lib = _dereq_('../../lib'); +var segmentsIntersect = Lib.segmentsIntersect; +var constrain = Lib.constrain; +var constants = _dereq_('./constants'); + + +module.exports = function linePoints(d, opts) { + var xa = opts.xaxis; + var ya = opts.yaxis; + var xLog = xa.type === 'log'; + var yLog = ya.type === 'log'; + var xLen = xa._length; + var yLen = ya._length; + var connectGaps = opts.connectGaps; + var baseTolerance = opts.baseTolerance; + var shape = opts.shape; + var linear = shape === 'linear'; + var fill = opts.fill && opts.fill !== 'none'; + var segments = []; + var minTolerance = constants.minTolerance; + var len = d.length; + var pts = new Array(len); + var pti = 0; + + var i; + + // pt variables are pixel coordinates [x,y] of one point + // these four are the outputs of clustering on a line + var clusterStartPt, clusterEndPt, clusterHighPt, clusterLowPt; + + // "this" is the next point we're considering adding to the cluster + var thisPt; + + // did we encounter the high point first, then a low point, or vice versa? + var clusterHighFirst; + + // the first two points in the cluster determine its unit vector + // so the second is always in the "High" direction + var clusterUnitVector; + + // the pixel delta from clusterStartPt + var thisVector; + + // val variables are (signed) pixel distances along the cluster vector + var clusterRefDist, clusterHighVal, clusterLowVal, thisVal; + + // deviation variables are (signed) pixel distances normal to the cluster vector + var clusterMinDeviation, clusterMaxDeviation, thisDeviation; + + // turn one calcdata point into pixel coordinates + function getPt(index) { + var di = d[index]; + if(!di) return false; + var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x); + var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y); + + // if non-positive log values, set them VERY far off-screen + // so the line looks essentially straight from the previous point. + if(x === BADNUM) { + if(xLog) x = xa.c2p(di.x, true); + if(x === BADNUM) return false; + // If BOTH were bad log values, make the line follow a constant + // exponent rather than a constant slope + if(yLog && y === BADNUM) { + x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) / + (ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS))); + } + x *= 1000; + } + if(y === BADNUM) { + if(yLog) y = ya.c2p(di.y, true); + if(y === BADNUM) return false; + y *= 1000; + } + return [x, y]; + } + + function crossesViewport(xFrac0, yFrac0, xFrac1, yFrac1) { + var dx = xFrac1 - xFrac0; + var dy = yFrac1 - yFrac0; + var dx0 = 0.5 - xFrac0; + var dy0 = 0.5 - yFrac0; + var norm2 = dx * dx + dy * dy; + var dot = dx * dx0 + dy * dy0; + if(dot > 0 && dot < norm2) { + var cross = dx0 * dy - dy0 * dx; + if(cross * cross < norm2) return true; + } + } + + var latestXFrac, latestYFrac; + // if we're off-screen, increase tolerance over baseTolerance + function getTolerance(pt, nextPt) { + var xFrac = pt[0] / xLen; + var yFrac = pt[1] / yLen; + var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1); + if(offScreenFraction && (latestXFrac !== undefined) && + crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac) + ) { + offScreenFraction = 0; + } + if(offScreenFraction && nextPt && + crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen) + ) { + offScreenFraction = 0; + } + + return (1 + constants.toleranceGrowth * offScreenFraction) * baseTolerance; + } + + function ptDist(pt1, pt2) { + var dx = pt1[0] - pt2[0]; + var dy = pt1[1] - pt2[1]; + return Math.sqrt(dx * dx + dy * dy); + } + + // last bit of filtering: clip paths that are VERY far off-screen + // so we don't get near the browser's hard limit (+/- 2^29 px in Chrome and FF) + + var maxScreensAway = constants.maxScreensAway; + + // find the intersections between the segment from pt1 to pt2 + // and the large rectangle maxScreensAway around the viewport + // if one of pt1 and pt2 is inside and the other outside, there + // will be only one intersection. + // if both are outside there will be 0 or 2 intersections + // (or 1 if it's right at a corner - we'll treat that like 0) + // returns an array of intersection pts + var xEdge0 = -xLen * maxScreensAway; + var xEdge1 = xLen * (1 + maxScreensAway); + var yEdge0 = -yLen * maxScreensAway; + var yEdge1 = yLen * (1 + maxScreensAway); + var edges = [ + [xEdge0, yEdge0, xEdge1, yEdge0], + [xEdge1, yEdge0, xEdge1, yEdge1], + [xEdge1, yEdge1, xEdge0, yEdge1], + [xEdge0, yEdge1, xEdge0, yEdge0] + ]; + var xEdge, yEdge, lastXEdge, lastYEdge, lastFarPt, edgePt; + + // for linear line shape, edge intersections should be linearly interpolated + // spline uses this too, which isn't precisely correct but is actually pretty + // good, because Catmull-Rom weights far-away points less in creating the curvature + function getLinearEdgeIntersections(pt1, pt2) { + var out = []; + var ptCount = 0; + for(var i = 0; i < 4; i++) { + var edge = edges[i]; + var ptInt = segmentsIntersect( + pt1[0], pt1[1], pt2[0], pt2[1], + edge[0], edge[1], edge[2], edge[3] + ); + if(ptInt && (!ptCount || + Math.abs(ptInt.x - out[0][0]) > 1 || + Math.abs(ptInt.y - out[0][1]) > 1 + )) { + ptInt = [ptInt.x, ptInt.y]; + // if we have 2 intersections, make sure the closest one to pt1 comes first + if(ptCount && ptDist(ptInt, pt1) < ptDist(out[0], pt1)) out.unshift(ptInt); + else out.push(ptInt); + ptCount++; + } + } + return out; + } + + function onlyConstrainedPoint(pt) { + if(pt[0] < xEdge0 || pt[0] > xEdge1 || pt[1] < yEdge0 || pt[1] > yEdge1) { + return [constrain(pt[0], xEdge0, xEdge1), constrain(pt[1], yEdge0, yEdge1)]; + } + } + + function sameEdge(pt1, pt2) { + if(pt1[0] === pt2[0] && (pt1[0] === xEdge0 || pt1[0] === xEdge1)) return true; + if(pt1[1] === pt2[1] && (pt1[1] === yEdge0 || pt1[1] === yEdge1)) return true; + } + + // for line shapes hv and vh, movement in the two dimensions is decoupled, + // so all we need to do is constrain each dimension independently + function getHVEdgeIntersections(pt1, pt2) { + var out = []; + var ptInt1 = onlyConstrainedPoint(pt1); + var ptInt2 = onlyConstrainedPoint(pt2); + if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out; + + if(ptInt1) out.push(ptInt1); + if(ptInt2) out.push(ptInt2); + return out; + } + + // hvh and vhv we sometimes have to move one of the intersection points + // out BEYOND the clipping rect, by a maximum of a factor of 2, so that + // the midpoint line is drawn in the right place + function getABAEdgeIntersections(dim, limit0, limit1) { + return function(pt1, pt2) { + var ptInt1 = onlyConstrainedPoint(pt1); + var ptInt2 = onlyConstrainedPoint(pt2); + + var out = []; + if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out; + + if(ptInt1) out.push(ptInt1); + if(ptInt2) out.push(ptInt2); + + var midShift = 2 * Lib.constrain((pt1[dim] + pt2[dim]) / 2, limit0, limit1) - + ((ptInt1 || pt1)[dim] + (ptInt2 || pt2)[dim]); + if(midShift) { + var ptToAlter; + if(ptInt1 && ptInt2) { + ptToAlter = (midShift > 0 === ptInt1[dim] > ptInt2[dim]) ? ptInt1 : ptInt2; + } else ptToAlter = ptInt1 || ptInt2; + + ptToAlter[dim] += midShift; + } + + return out; + }; + } + + var getEdgeIntersections; + if(shape === 'linear' || shape === 'spline') { + getEdgeIntersections = getLinearEdgeIntersections; + } else if(shape === 'hv' || shape === 'vh') { + getEdgeIntersections = getHVEdgeIntersections; + } else if(shape === 'hvh') getEdgeIntersections = getABAEdgeIntersections(0, xEdge0, xEdge1); + else if(shape === 'vhv') getEdgeIntersections = getABAEdgeIntersections(1, yEdge0, yEdge1); + + // a segment pt1->pt2 entirely outside the nearby region: + // find the corner it gets closest to touching + function getClosestCorner(pt1, pt2) { + var dx = pt2[0] - pt1[0]; + var m = (pt2[1] - pt1[1]) / dx; + var b = (pt1[1] * pt2[0] - pt2[1] * pt1[0]) / dx; + + if(b > 0) return [m > 0 ? xEdge0 : xEdge1, yEdge1]; + else return [m > 0 ? xEdge1 : xEdge0, yEdge0]; + } + + function updateEdge(pt) { + var x = pt[0]; + var y = pt[1]; + var xSame = x === pts[pti - 1][0]; + var ySame = y === pts[pti - 1][1]; + // duplicate point? + if(xSame && ySame) return; + if(pti > 1) { + // backtracking along an edge? + var xSame2 = x === pts[pti - 2][0]; + var ySame2 = y === pts[pti - 2][1]; + if(xSame && (x === xEdge0 || x === xEdge1) && xSame2) { + if(ySame2) pti--; // backtracking exactly - drop prev pt and don't add + else pts[pti - 1] = pt; // not exact: replace the prev pt + } else if(ySame && (y === yEdge0 || y === yEdge1) && ySame2) { + if(xSame2) pti--; + else pts[pti - 1] = pt; + } else pts[pti++] = pt; + } else pts[pti++] = pt; + } + + function updateEdgesForReentry(pt) { + // if we're outside the nearby region and going back in, + // we may need to loop around a corner point + if(pts[pti - 1][0] !== pt[0] && pts[pti - 1][1] !== pt[1]) { + updateEdge([lastXEdge, lastYEdge]); + } + updateEdge(pt); + lastFarPt = null; + lastXEdge = lastYEdge = 0; + } + + function addPt(pt) { + latestXFrac = pt[0] / xLen; + latestYFrac = pt[1] / yLen; + // Are we more than maxScreensAway off-screen any direction? + // if so, clip to this box, but in such a way that on-screen + // drawing is unchanged + xEdge = (pt[0] < xEdge0) ? xEdge0 : (pt[0] > xEdge1) ? xEdge1 : 0; + yEdge = (pt[1] < yEdge0) ? yEdge0 : (pt[1] > yEdge1) ? yEdge1 : 0; + if(xEdge || yEdge) { + if(!pti) { + // to get fills right - if first point is far, push it toward the + // screen in whichever direction(s) are far + + pts[pti++] = [xEdge || pt[0], yEdge || pt[1]]; + } else if(lastFarPt) { + // both this point and the last are outside the nearby region + // check if we're crossing the nearby region + var intersections = getEdgeIntersections(lastFarPt, pt); + if(intersections.length > 1) { + updateEdgesForReentry(intersections[0]); + pts[pti++] = intersections[1]; + } + } else { + // we're leaving the nearby region - add the point where we left it + + edgePt = getEdgeIntersections(pts[pti - 1], pt)[0]; + pts[pti++] = edgePt; + } + + var lastPt = pts[pti - 1]; + if(xEdge && yEdge && (lastPt[0] !== xEdge || lastPt[1] !== yEdge)) { + // we've gone out beyond a new corner: add the corner too + // so that the next point will take the right winding + if(lastFarPt) { + if(lastXEdge !== xEdge && lastYEdge !== yEdge) { + if(lastXEdge && lastYEdge) { + // we've gone around to an opposite corner - we + // need to add the correct extra corner + // in order to get the right winding + updateEdge(getClosestCorner(lastFarPt, pt)); + } else { + // we're coming from a far edge - the extra corner + // we need is determined uniquely by the sectors + updateEdge([lastXEdge || xEdge, lastYEdge || yEdge]); + } + } else if(lastXEdge && lastYEdge) { + updateEdge([lastXEdge, lastYEdge]); + } + } + updateEdge([xEdge, yEdge]); + } else if((lastXEdge - xEdge) && (lastYEdge - yEdge)) { + // we're coming from an edge or far corner to an edge - again the + // extra corner we need is uniquely determined by the sectors + updateEdge([xEdge || lastXEdge, yEdge || lastYEdge]); + } + lastFarPt = pt; + lastXEdge = xEdge; + lastYEdge = yEdge; + } else { + if(lastFarPt) { + // this point is in range but the previous wasn't: add its entry pt first + updateEdgesForReentry(getEdgeIntersections(lastFarPt, pt)[0]); + } + + pts[pti++] = pt; + } + } + + // loop over ALL points in this trace + for(i = 0; i < len; i++) { + clusterStartPt = getPt(i); + if(!clusterStartPt) continue; + + pti = 0; + lastFarPt = null; + addPt(clusterStartPt); + + // loop over one segment of the trace + for(i++; i < len; i++) { + clusterHighPt = getPt(i); + if(!clusterHighPt) { + if(connectGaps) continue; + else break; + } + + // can't decimate if nonlinear line shape + // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again + // but spline would be verrry awkward to decimate + if(!linear || !opts.simplify) { + addPt(clusterHighPt); + continue; + } + + var nextPt = getPt(i + 1); + + clusterRefDist = ptDist(clusterHighPt, clusterStartPt); + + // #3147 - always include the very first and last points for fills + if(!(fill && (pti === 0 || pti === len - 1)) && + clusterRefDist < getTolerance(clusterHighPt, nextPt) * minTolerance) continue; + + clusterUnitVector = [ + (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist, + (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist + ]; + + clusterLowPt = clusterStartPt; + clusterHighVal = clusterRefDist; + clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0; + clusterHighFirst = false; + clusterEndPt = clusterHighPt; + + // loop over one cluster of points that collapse onto one line + for(i++; i < d.length; i++) { + thisPt = nextPt; + nextPt = getPt(i + 1); + if(!thisPt) { + if(connectGaps) continue; + else break; + } + thisVector = [ + thisPt[0] - clusterStartPt[0], + thisPt[1] - clusterStartPt[1] + ]; + // cross product (or dot with normal to the cluster vector) + thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0]; + clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation); + clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation); + + if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt, nextPt)) break; + + clusterEndPt = thisPt; + thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1]; + + if(thisVal > clusterHighVal) { + clusterHighVal = thisVal; + clusterHighPt = thisPt; + clusterHighFirst = false; + } else if(thisVal < clusterLowVal) { + clusterLowVal = thisVal; + clusterLowPt = thisPt; + clusterHighFirst = true; + } + } + + // insert this cluster into pts + // we've already inserted the start pt, now check if we have high and low pts + if(clusterHighFirst) { + addPt(clusterHighPt); + if(clusterEndPt !== clusterLowPt) addPt(clusterLowPt); + } else { + if(clusterLowPt !== clusterStartPt) addPt(clusterLowPt); + if(clusterEndPt !== clusterHighPt) addPt(clusterHighPt); + } + // and finally insert the end pt + addPt(clusterEndPt); + + // have we reached the end of this segment? + if(i >= d.length || !thisPt) break; + + // otherwise we have an out-of-cluster point to insert as next clusterStartPt + addPt(thisPt); + clusterStartPt = thisPt; + } + + // to get fills right - repeat what we did at the start + if(lastFarPt) updateEdge([lastXEdge || lastFarPt[0], lastYEdge || lastFarPt[1]]); + + segments.push(pts.slice(0, pti)); + } + + return segments; +}; + +},{"../../constants/numerical":265,"../../lib":285,"./constants":498}],509:[function(_dereq_,module,exports){ +'use strict'; + + +// common to 'scatter' and 'scatterternary' +module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) { + var shape = coerce('line.shape'); + if(shape === 'spline') coerce('line.smoothing'); +}; + +},{}],510:[function(_dereq_,module,exports){ +'use strict'; + +var LINKEDFILLS = {tonextx: 1, tonexty: 1, tonext: 1}; + +module.exports = function linkTraces(gd, plotinfo, cdscatter) { + var trace, i, group, prevtrace, groupIndex; + + // first sort traces to keep stacks & filled-together groups together + var groupIndices = {}; + var needsSort = false; + var prevGroupIndex = -1; + var nextGroupIndex = 0; + var prevUnstackedGroupIndex = -1; + for(i = 0; i < cdscatter.length; i++) { + trace = cdscatter[i][0].trace; + group = trace.stackgroup || ''; + if(group) { + if(group in groupIndices) { + groupIndex = groupIndices[group]; + } else { + groupIndex = groupIndices[group] = nextGroupIndex; + nextGroupIndex++; + } + } else if(trace.fill in LINKEDFILLS && prevUnstackedGroupIndex >= 0) { + groupIndex = prevUnstackedGroupIndex; + } else { + groupIndex = prevUnstackedGroupIndex = nextGroupIndex; + nextGroupIndex++; + } + + if(groupIndex < prevGroupIndex) needsSort = true; + trace._groupIndex = prevGroupIndex = groupIndex; + } + + var cdscatterSorted = cdscatter.slice(); + if(needsSort) { + cdscatterSorted.sort(function(a, b) { + var traceA = a[0].trace; + var traceB = b[0].trace; + return (traceA._groupIndex - traceB._groupIndex) || + (traceA.index - traceB.index); + }); + } + + // now link traces to each other + var prevtraces = {}; + for(i = 0; i < cdscatterSorted.length; i++) { + trace = cdscatterSorted[i][0].trace; + group = trace.stackgroup || ''; + + // Note: The check which ensures all cdscatter here are for the same axis and + // are either cartesian or scatterternary has been removed. This code assumes + // the passed scattertraces have been filtered to the proper plot types and + // the proper subplots. + if(trace.visible === true) { + trace._nexttrace = null; + + if(trace.fill in LINKEDFILLS) { + prevtrace = prevtraces[group]; + trace._prevtrace = prevtrace || null; + + if(prevtrace) { + prevtrace._nexttrace = trace; + } + } + + trace._ownfill = (trace.fill && ( + trace.fill.substr(0, 6) === 'tozero' || + trace.fill === 'toself' || + (trace.fill.substr(0, 2) === 'to' && !trace._prevtrace) + )); + + prevtraces[group] = trace; + } else { + trace._prevtrace = trace._nexttrace = trace._ownfill = null; + } + } + + return cdscatterSorted; +}; + +},{}],511:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + + +// used in the drawing step for 'scatter' and 'scattegeo' and +// in the convert step for 'scatter3d' +module.exports = function makeBubbleSizeFn(trace) { + var marker = trace.marker; + var sizeRef = marker.sizeref || 1; + var sizeMin = marker.sizemin || 0; + + // for bubble charts, allow scaling the provided value linearly + // and by area or diameter. + // Note this only applies to the array-value sizes + + var baseFn = (marker.sizemode === 'area') ? + function(v) { return Math.sqrt(v / sizeRef); } : + function(v) { return v / sizeRef; }; + + // TODO add support for position/negative bubbles? + // TODO add 'sizeoffset' attribute? + return function(v) { + var baseSize = baseFn(v / 2); + + // don't show non-numeric and negative sizes + return (isNumeric(baseSize) && (baseSize > 0)) ? + Math.max(baseSize, sizeMin) : + 0; + }; +}; + +},{"fast-isnumeric":31}],512:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + container: 'marker', + min: 'cmin', + max: 'cmax' +}; + +},{}],513:[function(_dereq_,module,exports){ +'use strict'; + +var Color = _dereq_('../../components/color'); +var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale; +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); + +var subTypes = _dereq_('./subtypes'); + +/* + * opts: object of flags to control features not all marker users support + * noLine: caller does not support marker lines + * gradient: caller supports gradients + * noSelect: caller does not support selected/unselected attribute containers + */ +module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) { + var isBubble = subTypes.isBubble(traceIn); + var lineColor = (traceIn.line || {}).color; + var defaultMLC; + + opts = opts || {}; + + // marker.color inherit from line.color (even if line.color is an array) + if(lineColor) defaultColor = lineColor; + + coerce('marker.symbol'); + coerce('marker.opacity', isBubble ? 0.7 : 1); + coerce('marker.size'); + + coerce('marker.color', defaultColor); + if(hasColorscale(traceIn, 'marker')) { + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'}); + } + + if(!opts.noSelect) { + coerce('selected.marker.color'); + coerce('unselected.marker.color'); + coerce('selected.marker.size'); + coerce('unselected.marker.size'); + } + + if(!opts.noLine) { + // if there's a line with a different color than the marker, use + // that line color as the default marker line color + // (except when it's an array) + // mostly this is for transparent markers to behave nicely + if(lineColor && !Array.isArray(lineColor) && (traceOut.marker.color !== lineColor)) { + defaultMLC = lineColor; + } else if(isBubble) defaultMLC = Color.background; + else defaultMLC = Color.defaultLine; + + coerce('marker.line.color', defaultMLC); + if(hasColorscale(traceIn, 'marker.line')) { + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'}); + } + + coerce('marker.line.width', isBubble ? 1 : 0); + } + + if(isBubble) { + coerce('marker.sizeref'); + coerce('marker.sizemin'); + coerce('marker.sizemode'); + } + + if(opts.gradient) { + var gradientType = coerce('marker.gradient.type'); + if(gradientType !== 'none') { + coerce('marker.gradient.color'); + } + } +}; + +},{"../../components/color":155,"../../components/colorscale/defaults":165,"../../components/colorscale/helpers":166,"./subtypes":519}],514:[function(_dereq_,module,exports){ +'use strict'; + +var dateTick0 = _dereq_('../../lib').dateTick0; +var numConstants = _dereq_('../../constants/numerical'); +var ONEWEEK = numConstants.ONEWEEK; + +function getPeriod0Dflt(period, calendar) { + if(period % ONEWEEK === 0) { + return dateTick0(calendar, 1); // Sunday + } + return dateTick0(calendar, 0); +} + +module.exports = function handlePeriodDefaults(traceIn, traceOut, layout, coerce, opts) { + if(!opts) { + opts = { + x: true, + y: true + }; + } + + if(opts.x) { + var xperiod = coerce('xperiod'); + if(xperiod) { + coerce('xperiod0', getPeriod0Dflt(xperiod, traceOut.xcalendar)); + coerce('xperiodalignment'); + } + } + + if(opts.y) { + var yperiod = coerce('yperiod'); + if(yperiod) { + coerce('yperiod0', getPeriod0Dflt(yperiod, traceOut.ycalendar)); + coerce('yperiodalignment'); + } + } +}; + +},{"../../constants/numerical":265,"../../lib":285}],515:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var ensureSingle = Lib.ensureSingle; +var identity = Lib.identity; +var Drawing = _dereq_('../../components/drawing'); + +var subTypes = _dereq_('./subtypes'); +var linePoints = _dereq_('./line_points'); +var linkTraces = _dereq_('./link_traces'); +var polygonTester = _dereq_('../../lib/polygon').tester; + +module.exports = function plot(gd, plotinfo, cdscatter, scatterLayer, transitionOpts, makeOnCompleteCallback) { + var join, onComplete; + + // If transition config is provided, then it is only a partial replot and traces not + // updated are removed. + var isFullReplot = !transitionOpts; + var hasTransition = !!transitionOpts && transitionOpts.duration > 0; + + // Link traces so the z-order of fill layers is correct + var cdscatterSorted = linkTraces(gd, plotinfo, cdscatter); + + join = scatterLayer.selectAll('g.trace') + .data(cdscatterSorted, function(d) { return d[0].trace.uid; }); + + // Append new traces: + join.enter().append('g') + .attr('class', function(d) { + return 'trace scatter trace' + d[0].trace.uid; + }) + .style('stroke-miterlimit', 2); + join.order(); + + createFills(gd, join, plotinfo); + + if(hasTransition) { + if(makeOnCompleteCallback) { + // If it was passed a callback to register completion, make a callback. If + // this is created, then it must be executed on completion, otherwise the + // pos-transition redraw will not execute: + onComplete = makeOnCompleteCallback(); + } + + var transition = d3.transition() + .duration(transitionOpts.duration) + .ease(transitionOpts.easing) + .each('end', function() { + onComplete && onComplete(); + }) + .each('interrupt', function() { + onComplete && onComplete(); + }); + + transition.each(function() { + // Must run the selection again since otherwise enters/updates get grouped together + // and these get executed out of order. Except we need them in order! + scatterLayer.selectAll('g.trace').each(function(d, i) { + plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts); + }); + }); + } else { + join.each(function(d, i) { + plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts); + }); + } + + if(isFullReplot) { + join.exit().remove(); + } + + // remove paths that didn't get used + scatterLayer.selectAll('path:not([d])').remove(); +}; + +function createFills(gd, traceJoin, plotinfo) { + traceJoin.each(function(d) { + var fills = ensureSingle(d3.select(this), 'g', 'fills'); + Drawing.setClipUrl(fills, plotinfo.layerClipId, gd); + + var trace = d[0].trace; + + var fillData = []; + if(trace._ownfill) fillData.push('_ownFill'); + if(trace._nexttrace) fillData.push('_nextFill'); + + var fillJoin = fills.selectAll('g').data(fillData, identity); + + fillJoin.enter().append('g'); + + fillJoin.exit() + .each(function(d) { trace[d] = null; }) + .remove(); + + fillJoin.order().each(function(d) { + // make a path element inside the fill group, just so + // we can give it its own data later on and the group can + // keep its simple '_*Fill' data + trace[d] = ensureSingle(d3.select(this), 'path', 'js-fill'); + }); + }); +} + +function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transitionOpts) { + var i; + + // Since this has been reorganized and we're executing this on individual traces, + // we need to pass it the full list of cdscatter as well as this trace's index (idx) + // since it does an internal n^2 loop over comparisons with other traces: + selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll); + + var hasTransition = !!transitionOpts && transitionOpts.duration > 0; + + function transition(selection) { + return hasTransition ? selection.transition() : selection; + } + + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var trace = cdscatter[0].trace; + var line = trace.line; + var tr = d3.select(element); + + var errorBarGroup = ensureSingle(tr, 'g', 'errorbars'); + var lines = ensureSingle(tr, 'g', 'lines'); + var points = ensureSingle(tr, 'g', 'points'); + var text = ensureSingle(tr, 'g', 'text'); + + // error bars are at the bottom + Registry.getComponentMethod('errorbars', 'plot')(gd, errorBarGroup, plotinfo, transitionOpts); + + if(trace.visible !== true) return; + + transition(tr).style('opacity', trace.opacity); + + // BUILD LINES AND FILLS + var ownFillEl3, tonext; + var ownFillDir = trace.fill.charAt(trace.fill.length - 1); + if(ownFillDir !== 'x' && ownFillDir !== 'y') ownFillDir = ''; + + // store node for tweaking by selectPoints + cdscatter[0][plotinfo.isRangePlot ? 'nodeRangePlot3' : 'node3'] = tr; + + var prevRevpath = ''; + var prevPolygons = []; + var prevtrace = trace._prevtrace; + + if(prevtrace) { + prevRevpath = prevtrace._prevRevpath || ''; + tonext = prevtrace._nextFill; + prevPolygons = prevtrace._polygons; + } + + var thispath; + var thisrevpath; + // fullpath is all paths for this curve, joined together straight + // across gaps, for filling + var fullpath = ''; + // revpath is fullpath reversed, for fill-to-next + var revpath = ''; + // functions for converting a point array to a path + var pathfn, revpathbase, revpathfn; + // variables used before and after the data join + var pt0, lastSegment, pt1, thisPolygons; + + // initialize line join data / method + var segments = []; + var makeUpdate = Lib.noop; + + ownFillEl3 = trace._ownFill; + + if(subTypes.hasLines(trace) || trace.fill !== 'none') { + if(tonext) { + // This tells .style which trace to use for fill information: + tonext.datum(cdscatter); + } + + if(['hv', 'vh', 'hvh', 'vhv'].indexOf(line.shape) !== -1) { + pathfn = Drawing.steps(line.shape); + revpathbase = Drawing.steps( + line.shape.split('').reverse().join('') + ); + } else if(line.shape === 'spline') { + pathfn = revpathbase = function(pts) { + var pLast = pts[pts.length - 1]; + if(pts.length > 1 && pts[0][0] === pLast[0] && pts[0][1] === pLast[1]) { + // identical start and end points: treat it as a + // closed curve so we don't get a kink + return Drawing.smoothclosed(pts.slice(1), line.smoothing); + } else { + return Drawing.smoothopen(pts, line.smoothing); + } + }; + } else { + pathfn = revpathbase = function(pts) { + return 'M' + pts.join('L'); + }; + } + + revpathfn = function(pts) { + // note: this is destructive (reverses pts in place) so can't use pts after this + return revpathbase(pts.reverse()); + }; + + segments = linePoints(cdscatter, { + xaxis: xa, + yaxis: ya, + connectGaps: trace.connectgaps, + baseTolerance: Math.max(line.width || 1, 3) / 4, + shape: line.shape, + simplify: line.simplify, + fill: trace.fill + }); + + // since we already have the pixel segments here, use them to make + // polygons for hover on fill + // TODO: can we skip this if hoveron!=fills? That would mean we + // need to redraw when you change hoveron... + thisPolygons = trace._polygons = new Array(segments.length); + for(i = 0; i < segments.length; i++) { + trace._polygons[i] = polygonTester(segments[i]); + } + + if(segments.length) { + pt0 = segments[0][0]; + lastSegment = segments[segments.length - 1]; + pt1 = lastSegment[lastSegment.length - 1]; + } + + makeUpdate = function(isEnter) { + return function(pts) { + thispath = pathfn(pts); + thisrevpath = revpathfn(pts); + if(!fullpath) { + fullpath = thispath; + revpath = thisrevpath; + } else if(ownFillDir) { + fullpath += 'L' + thispath.substr(1); + revpath = thisrevpath + ('L' + revpath.substr(1)); + } else { + fullpath += 'Z' + thispath; + revpath = thisrevpath + 'Z' + revpath; + } + + if(subTypes.hasLines(trace) && pts.length > 1) { + var el = d3.select(this); + + // This makes the coloring work correctly: + el.datum(cdscatter); + + if(isEnter) { + transition(el.style('opacity', 0) + .attr('d', thispath) + .call(Drawing.lineGroupStyle)) + .style('opacity', 1); + } else { + var sel = transition(el); + sel.attr('d', thispath); + Drawing.singleLineStyle(cdscatter, sel); + } + } + }; + }; + } + + var lineJoin = lines.selectAll('.js-line').data(segments); + + transition(lineJoin.exit()) + .style('opacity', 0) + .remove(); + + lineJoin.each(makeUpdate(false)); + + lineJoin.enter().append('path') + .classed('js-line', true) + .style('vector-effect', 'non-scaling-stroke') + .call(Drawing.lineGroupStyle) + .each(makeUpdate(true)); + + Drawing.setClipUrl(lineJoin, plotinfo.layerClipId, gd); + + function clearFill(selection) { + transition(selection).attr('d', 'M0,0Z'); + } + + if(segments.length) { + if(ownFillEl3) { + ownFillEl3.datum(cdscatter); + if(pt0 && pt1) { + if(ownFillDir) { + if(ownFillDir === 'y') { + pt0[1] = pt1[1] = ya.c2p(0, true); + } else if(ownFillDir === 'x') { + pt0[0] = pt1[0] = xa.c2p(0, true); + } + + // fill to zero: full trace path, plus extension of + // the endpoints to the appropriate axis + // For the sake of animations, wrap the points around so that + // the points on the axes are the first two points. Otherwise + // animations get a little crazy if the number of points changes. + transition(ownFillEl3).attr('d', 'M' + pt1 + 'L' + pt0 + 'L' + fullpath.substr(1)) + .call(Drawing.singleFillStyle); + } else { + // fill to self: just join the path to itself + transition(ownFillEl3).attr('d', fullpath + 'Z') + .call(Drawing.singleFillStyle); + } + } + } else if(tonext) { + if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) { + // fill to next: full trace path, plus the previous path reversed + if(trace.fill === 'tonext') { + // tonext: for use by concentric shapes, like manually constructed + // contours, we just add the two paths closed on themselves. + // This makes strange results if one path is *not* entirely + // inside the other, but then that is a strange usage. + transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z') + .call(Drawing.singleFillStyle); + } else { + // tonextx/y: for now just connect endpoints with lines. This is + // the correct behavior if the endpoints are at the same value of + // y/x, but if they *aren't*, we should ideally do more complicated + // things depending on whether the new endpoint projects onto the + // existing curve or off the end of it + transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z') + .call(Drawing.singleFillStyle); + } + trace._polygons = trace._polygons.concat(prevPolygons); + } else { + clearFill(tonext); + trace._polygons = null; + } + } + trace._prevRevpath = revpath; + trace._prevPolygons = thisPolygons; + } else { + if(ownFillEl3) clearFill(ownFillEl3); + else if(tonext) clearFill(tonext); + trace._polygons = trace._prevRevpath = trace._prevPolygons = null; + } + + + function visFilter(d) { + return d.filter(function(v) { return !v.gap && v.vis; }); + } + + function visFilterWithGaps(d) { + return d.filter(function(v) { return v.vis; }); + } + + function gapFilter(d) { + return d.filter(function(v) { return !v.gap; }); + } + + function keyFunc(d) { + return d.id; + } + + // Returns a function if the trace is keyed, otherwise returns undefined + function getKeyFunc(trace) { + if(trace.ids) { + return keyFunc; + } + } + + function hideFilter() { + return false; + } + + function makePoints(points, text, cdscatter) { + var join, selection, hasNode; + + var trace = cdscatter[0].trace; + var showMarkers = subTypes.hasMarkers(trace); + var showText = subTypes.hasText(trace); + + var keyFunc = getKeyFunc(trace); + var markerFilter = hideFilter; + var textFilter = hideFilter; + + if(showMarkers || showText) { + var showFilter = identity; + // if we're stacking, "infer zero" gap mode gets markers in the + // gap points - because we've inferred a zero there - but other + // modes (currently "interpolate", later "interrupt" hopefully) + // we don't draw generated markers + var stackGroup = trace.stackgroup; + var isInferZero = stackGroup && ( + gd._fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup].stackgaps === 'infer zero'); + if(trace.marker.maxdisplayed || trace._needsCull) { + showFilter = isInferZero ? visFilterWithGaps : visFilter; + } else if(stackGroup && !isInferZero) { + showFilter = gapFilter; + } + + if(showMarkers) markerFilter = showFilter; + if(showText) textFilter = showFilter; + } + + // marker points + + selection = points.selectAll('path.point'); + + join = selection.data(markerFilter, keyFunc); + + var enter = join.enter().append('path') + .classed('point', true); + + if(hasTransition) { + enter + .call(Drawing.pointStyle, trace, gd) + .call(Drawing.translatePoints, xa, ya) + .style('opacity', 0) + .transition() + .style('opacity', 1); + } + + join.order(); + + var styleFns; + if(showMarkers) { + styleFns = Drawing.makePointStyleFns(trace); + } + + join.each(function(d) { + var el = d3.select(this); + var sel = transition(el); + hasNode = Drawing.translatePoint(d, sel, xa, ya); + + if(hasNode) { + Drawing.singlePointStyle(d, sel, trace, styleFns, gd); + + if(plotinfo.layerClipId) { + Drawing.hideOutsideRangePoint(d, sel, xa, ya, trace.xcalendar, trace.ycalendar); + } + + if(trace.customdata) { + el.classed('plotly-customdata', d.data !== null && d.data !== undefined); + } + } else { + sel.remove(); + } + }); + + if(hasTransition) { + join.exit().transition() + .style('opacity', 0) + .remove(); + } else { + join.exit().remove(); + } + + // text points + selection = text.selectAll('g'); + join = selection.data(textFilter, keyFunc); + + // each text needs to go in its own 'g' in case + // it gets converted to mathjax + join.enter().append('g').classed('textpoint', true).append('text'); + + join.order(); + + join.each(function(d) { + var g = d3.select(this); + var sel = transition(g.select('text')); + hasNode = Drawing.translatePoint(d, sel, xa, ya); + + if(hasNode) { + if(plotinfo.layerClipId) { + Drawing.hideOutsideRangePoint(d, g, xa, ya, trace.xcalendar, trace.ycalendar); + } + } else { + g.remove(); + } + }); + + join.selectAll('text') + .call(Drawing.textPointStyle, trace, gd) + .each(function(d) { + // This just *has* to be totally custom because of SVG text positioning :( + // It's obviously copied from translatePoint; we just can't use that + var x = xa.c2p(d.x); + var y = ya.c2p(d.y); + + d3.select(this).selectAll('tspan.line').each(function() { + transition(d3.select(this)).attr({x: x, y: y}); + }); + }); + + join.exit().remove(); + } + + points.datum(cdscatter); + text.datum(cdscatter); + makePoints(points, text, cdscatter); + + // lastly, clip points groups of `cliponaxis !== false` traces + // on `plotinfo._hasClipOnAxisFalse === true` subplots + var hasClipOnAxisFalse = trace.cliponaxis === false; + var clipUrl = hasClipOnAxisFalse ? null : plotinfo.layerClipId; + Drawing.setClipUrl(points, clipUrl, gd); + Drawing.setClipUrl(text, clipUrl, gd); +} + +function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var xr = d3.extent(Lib.simpleMap(xa.range, xa.r2c)); + var yr = d3.extent(Lib.simpleMap(ya.range, ya.r2c)); + + var trace = cdscatter[0].trace; + if(!subTypes.hasMarkers(trace)) return; + // if marker.maxdisplayed is used, select a maximum of + // mnum markers to show, from the set that are in the viewport + var mnum = trace.marker.maxdisplayed; + + // TODO: remove some as we get away from the viewport? + if(mnum === 0) return; + + var cd = cdscatter.filter(function(v) { + return v.x >= xr[0] && v.x <= xr[1] && v.y >= yr[0] && v.y <= yr[1]; + }); + var inc = Math.ceil(cd.length / mnum); + var tnum = 0; + cdscatterAll.forEach(function(cdj, j) { + var tracei = cdj[0].trace; + if(subTypes.hasMarkers(tracei) && + tracei.marker.maxdisplayed > 0 && j < idx) { + tnum++; + } + }); + + // if multiple traces use maxdisplayed, stagger which markers we + // display this formula offsets successive traces by 1/3 of the + // increment, adding an extra small amount after each triplet so + // it's not quite periodic + var i0 = Math.round(tnum * inc / 3 + Math.floor(tnum / 3) * inc / 7.1); + + // for error bars: save in cd which markers to show + // so we don't have to repeat this + cdscatter.forEach(function(v) { delete v.vis; }); + cd.forEach(function(v, i) { + if(Math.round((i + i0) % inc) === 0) v.vis = true; + }); +} + +},{"../../components/drawing":177,"../../lib":285,"../../lib/polygon":297,"../../registry":373,"./line_points":508,"./link_traces":510,"./subtypes":519,"@plotly/d3":20}],516:[function(_dereq_,module,exports){ +'use strict'; + +var subtypes = _dereq_('./subtypes'); + +module.exports = function selectPoints(searchInfo, selectionTester) { + var cd = searchInfo.cd; + var xa = searchInfo.xaxis; + var ya = searchInfo.yaxis; + var selection = []; + var trace = cd[0].trace; + var i; + var di; + var x; + var y; + + var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace)); + if(hasOnlyLines) return []; + + if(selectionTester === false) { // clear selection + for(i = 0; i < cd.length; i++) { + cd[i].selected = 0; + } + } else { + for(i = 0; i < cd.length; i++) { + di = cd[i]; + x = xa.c2p(di.x); + y = ya.c2p(di.y); + + if((di.i !== null) && selectionTester.contains([x, y], false, i, searchInfo)) { + selection.push({ + pointNumber: di.i, + x: xa.c2d(di.x), + y: ya.c2d(di.y) + }); + di.selected = 1; + } else { + di.selected = 0; + } + } + } + + return selection; +}; + +},{"./subtypes":519}],517:[function(_dereq_,module,exports){ +'use strict'; + +var perStackAttrs = ['orientation', 'groupnorm', 'stackgaps']; + +module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) { + var stackOpts = layout._scatterStackOpts; + + var stackGroup = coerce('stackgroup'); + if(stackGroup) { + // use independent stacking options per subplot + var subplot = traceOut.xaxis + traceOut.yaxis; + var subplotStackOpts = stackOpts[subplot]; + if(!subplotStackOpts) subplotStackOpts = stackOpts[subplot] = {}; + + var groupOpts = subplotStackOpts[stackGroup]; + var firstTrace = false; + if(groupOpts) { + groupOpts.traces.push(traceOut); + } else { + groupOpts = subplotStackOpts[stackGroup] = { + // keep track of trace indices for use during stacking calculations + // this will be filled in during `calc` and used during `crossTraceCalc` + // so it's OK if we don't recreate it during a non-calc edit + traceIndices: [], + // Hold on to the whole set of prior traces + // First one is most important, so we can clear defaults + // there if we find explicit values only in later traces. + // We're only going to *use* the values stored in groupOpts, + // but for the editor and validate we want things self-consistent + // The full set of traces is used only to fix `fill` default if + // we find `orientation: 'h'` beyond the first trace + traces: [traceOut] + }; + firstTrace = true; + } + // TODO: how is this going to work with groupby transforms? + // in principle it should be OK I guess, as long as explicit group styles + // don't override explicit base-trace styles? + + var dflts = { + orientation: (traceOut.x && !traceOut.y) ? 'h' : 'v' + }; + + for(var i = 0; i < perStackAttrs.length; i++) { + var attr = perStackAttrs[i]; + var attrFound = attr + 'Found'; + if(!groupOpts[attrFound]) { + var traceHasAttr = traceIn[attr] !== undefined; + var isOrientation = attr === 'orientation'; + if(traceHasAttr || firstTrace) { + groupOpts[attr] = coerce(attr, dflts[attr]); + + if(isOrientation) { + groupOpts.fillDflt = groupOpts[attr] === 'h' ? + 'tonextx' : 'tonexty'; + } + + if(traceHasAttr) { + // Note: this will show a value here even if it's invalid + // in which case it will revert to default. + groupOpts[attrFound] = true; + + // Note: only one trace in the stack will get a _fullData + // entry for a given stack-wide attribute. If no traces + // (or the first trace) specify that attribute, the + // first trace will get it. If the first trace does NOT + // specify it but some later trace does, then it gets + // removed from the first trace and only included in the + // one that specified it. This is mostly important for + // editors (that want to see the full values to know + // what settings are available) and Plotly.react diffing. + // Editors may want to use fullLayout._scatterStackOpts + // directly and make these settings available from all + // traces in the stack... then set the new value into + // the first trace, and clear all later traces. + if(!firstTrace) { + delete groupOpts.traces[0][attr]; + + // orientation can affect default fill of previous traces + if(isOrientation) { + for(var j = 0; j < groupOpts.traces.length - 1; j++) { + var trace2 = groupOpts.traces[j]; + if(trace2._input.fill !== trace2.fill) { + trace2.fill = groupOpts.fillDflt; + } + } + } + } + } + } + } + } + return groupOpts; + } +}; + +},{}],518:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Drawing = _dereq_('../../components/drawing'); +var Registry = _dereq_('../../registry'); + +function style(gd) { + var s = d3.select(gd).selectAll('g.trace.scatter'); + + s.style('opacity', function(d) { + return d[0].trace.opacity; + }); + + s.selectAll('g.points').each(function(d) { + var sel = d3.select(this); + var trace = d.trace || d[0].trace; + stylePoints(sel, trace, gd); + }); + + s.selectAll('g.text').each(function(d) { + var sel = d3.select(this); + var trace = d.trace || d[0].trace; + styleText(sel, trace, gd); + }); + + s.selectAll('g.trace path.js-line') + .call(Drawing.lineGroupStyle); + + s.selectAll('g.trace path.js-fill') + .call(Drawing.fillGroupStyle); + + Registry.getComponentMethod('errorbars', 'style')(s); +} + +function stylePoints(sel, trace, gd) { + Drawing.pointStyle(sel.selectAll('path.point'), trace, gd); +} + +function styleText(sel, trace, gd) { + Drawing.textPointStyle(sel.selectAll('text'), trace, gd); +} + +function styleOnSelect(gd, cd, sel) { + var trace = cd[0].trace; + + if(trace.selectedpoints) { + Drawing.selectedPointStyle(sel.selectAll('path.point'), trace); + Drawing.selectedTextStyle(sel.selectAll('text'), trace); + } else { + stylePoints(sel, trace, gd); + styleText(sel, trace, gd); + } +} + +module.exports = { + style: style, + stylePoints: stylePoints, + styleText: styleText, + styleOnSelect: styleOnSelect +}; + +},{"../../components/drawing":177,"../../registry":373,"@plotly/d3":20}],519:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +module.exports = { + hasLines: function(trace) { + return trace.visible && trace.mode && + trace.mode.indexOf('lines') !== -1; + }, + + hasMarkers: function(trace) { + return trace.visible && ( + (trace.mode && trace.mode.indexOf('markers') !== -1) || + // until splom implements 'mode' + trace.type === 'splom' + ); + }, + + hasText: function(trace) { + return trace.visible && trace.mode && + trace.mode.indexOf('text') !== -1; + }, + + isBubble: function(trace) { + return Lib.isPlainObject(trace.marker) && + Lib.isArrayOrTypedArray(trace.marker.size); + } +}; + +},{"../../lib":285}],520:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +/* + * opts: object of flags to control features not all text users support + * noSelect: caller does not support selected/unselected attribute containers + */ +module.exports = function(traceIn, traceOut, layout, coerce, opts) { + opts = opts || {}; + + coerce('textposition'); + Lib.coerceFont(coerce, 'textfont', layout.font); + + if(!opts.noSelect) { + coerce('selected.textfont.color'); + coerce('unselected.textfont.color'); + } +}; + +},{"../../lib":285}],521:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); + +module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { + var x = coerce('x'); + var y = coerce('y'); + var len; + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + + if(x) { + var xlen = Lib.minRowLength(x); + if(y) { + len = Math.min(xlen, Lib.minRowLength(y)); + } else { + len = xlen; + coerce('y0'); + coerce('dy'); + } + } else { + if(!y) return 0; + + len = Lib.minRowLength(y); + coerce('x0'); + coerce('dx'); + } + + traceOut._length = len; + + return len; +}; + +},{"../../lib":285,"../../registry":373}],522:[function(_dereq_,module,exports){ +'use strict'; + +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; +var scatterAttrs = _dereq_('../scatter/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var dash = _dereq_('../../components/drawing/attributes').dash; + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var scatterMarkerAttrs = scatterAttrs.marker; +var scatterLineAttrs = scatterAttrs.line; +var scatterMarkerLineAttrs = scatterMarkerAttrs.line; + +module.exports = { + a: { + valType: 'data_array', + editType: 'calc', + }, + b: { + valType: 'data_array', + editType: 'calc', + }, + c: { + valType: 'data_array', + editType: 'calc', + }, + sum: { + valType: 'number', + dflt: 0, + min: 0, + editType: 'calc', + }, + mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}), + text: extendFlat({}, scatterAttrs.text, { + }), + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: ['a', 'b', 'c', 'text'] + }), + hovertext: extendFlat({}, scatterAttrs.hovertext, { + }), + line: { + color: scatterLineAttrs.color, + width: scatterLineAttrs.width, + dash: dash, + shape: extendFlat({}, scatterLineAttrs.shape, + {values: ['linear', 'spline']}), + smoothing: scatterLineAttrs.smoothing, + editType: 'calc' + }, + connectgaps: scatterAttrs.connectgaps, + cliponaxis: scatterAttrs.cliponaxis, + fill: extendFlat({}, scatterAttrs.fill, { + values: ['none', 'toself', 'tonext'], + dflt: 'none', + }), + fillcolor: scatterAttrs.fillcolor, + marker: extendFlat({ + symbol: scatterMarkerAttrs.symbol, + opacity: scatterMarkerAttrs.opacity, + maxdisplayed: scatterMarkerAttrs.maxdisplayed, + size: scatterMarkerAttrs.size, + sizeref: scatterMarkerAttrs.sizeref, + sizemin: scatterMarkerAttrs.sizemin, + sizemode: scatterMarkerAttrs.sizemode, + line: extendFlat({ + width: scatterMarkerLineAttrs.width, + editType: 'calc' + }, + colorScaleAttrs('marker.line') + ), + gradient: scatterMarkerAttrs.gradient, + editType: 'calc' + }, + colorScaleAttrs('marker') + ), + + textfont: scatterAttrs.textfont, + textposition: scatterAttrs.textposition, + + selected: scatterAttrs.selected, + unselected: scatterAttrs.unselected, + + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { + flags: ['a', 'b', 'c', 'text', 'name'] + }), + hoveron: scatterAttrs.hoveron, + hovertemplate: hovertemplateAttrs(), +}; + +},{"../../components/colorscale/attributes":162,"../../components/drawing/attributes":176,"../../lib/extend":279,"../../plots/attributes":327,"../../plots/template_attributes":368,"../scatter/attributes":494}],523:[function(_dereq_,module,exports){ +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var calcColorscale = _dereq_('../scatter/colorscale_calc'); +var arraysToCalcdata = _dereq_('../scatter/arrays_to_calcdata'); +var calcSelection = _dereq_('../scatter/calc_selection'); +var calcMarkerSize = _dereq_('../scatter/calc').calcMarkerSize; + +var dataArrays = ['a', 'b', 'c']; +var arraysToFill = {a: ['b', 'c'], b: ['a', 'c'], c: ['a', 'b']}; + +module.exports = function calc(gd, trace) { + var ternary = gd._fullLayout[trace.subplot]; + var displaySum = ternary.sum; + var normSum = trace.sum || displaySum; + var arrays = {a: trace.a, b: trace.b, c: trace.c}; + + var i, j, dataArray, newArray, fillArray1, fillArray2; + + // fill in one missing component + for(i = 0; i < dataArrays.length; i++) { + dataArray = dataArrays[i]; + if(arrays[dataArray]) continue; + + fillArray1 = arrays[arraysToFill[dataArray][0]]; + fillArray2 = arrays[arraysToFill[dataArray][1]]; + newArray = new Array(fillArray1.length); + for(j = 0; j < fillArray1.length; j++) { + newArray[j] = normSum - fillArray1[j] - fillArray2[j]; + } + arrays[dataArray] = newArray; + } + + // make the calcdata array + var serieslen = trace._length; + var cd = new Array(serieslen); + var a, b, c, norm, x, y; + for(i = 0; i < serieslen; i++) { + a = arrays.a[i]; + b = arrays.b[i]; + c = arrays.c[i]; + if(isNumeric(a) && isNumeric(b) && isNumeric(c)) { + a = +a; + b = +b; + c = +c; + norm = displaySum / (a + b + c); + if(norm !== 1) { + a *= norm; + b *= norm; + c *= norm; + } + // map a, b, c onto x and y where the full scale of y + // is [0, sum], and x is [-sum, sum] + // TODO: this makes `a` always the top, `b` the bottom left, + // and `c` the bottom right. Do we want options to rearrange + // these? + y = a; + x = c - b; + cd[i] = {x: x, y: y, a: a, b: b, c: c}; + } else cd[i] = {x: false, y: false}; + } + + calcMarkerSize(trace, serieslen); + calcColorscale(gd, trace); + arraysToCalcdata(cd, trace); + calcSelection(cd, trace); + + return cd; +}; + +},{"../scatter/arrays_to_calcdata":493,"../scatter/calc":495,"../scatter/calc_selection":496,"../scatter/colorscale_calc":497,"fast-isnumeric":31}],524:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +var constants = _dereq_('../scatter/constants'); +var subTypes = _dereq_('../scatter/subtypes'); +var handleMarkerDefaults = _dereq_('../scatter/marker_defaults'); +var handleLineDefaults = _dereq_('../scatter/line_defaults'); +var handleLineShapeDefaults = _dereq_('../scatter/line_shape_defaults'); +var handleTextDefaults = _dereq_('../scatter/text_defaults'); +var handleFillColorDefaults = _dereq_('../scatter/fillcolor_defaults'); + +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var a = coerce('a'); + var b = coerce('b'); + var c = coerce('c'); + var len; + + // allow any one array to be missing, len is the minimum length of those + // present. Note that after coerce data_array's are either Arrays (which + // are truthy even if empty) or undefined. As in scatter, an empty array + // is different from undefined, because it can signify that this data is + // not known yet but expected in the future + if(a) { + len = a.length; + if(b) { + len = Math.min(len, b.length); + if(c) len = Math.min(len, c.length); + } else if(c) len = Math.min(len, c.length); + else len = 0; + } else if(b && c) { + len = Math.min(b.length, c.length); + } + + if(!len) { + traceOut.visible = false; + return; + } + + traceOut._length = len; + + coerce('sum'); + + coerce('text'); + coerce('hovertext'); + if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + + var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; + coerce('mode', defaultMode); + + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineShapeDefaults(traceIn, traceOut, coerce); + coerce('connectgaps'); + } + + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + + if(subTypes.hasText(traceOut)) { + coerce('texttemplate'); + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); + coerce('marker.maxdisplayed'); + dfltHoverOn.push('points'); + } + + coerce('fill'); + if(traceOut.fill !== 'none') { + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce); + } + + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + coerce('hoveron', dfltHoverOn.join('+') || 'points'); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +}; + +},{"../../lib":285,"../scatter/constants":498,"../scatter/fillcolor_defaults":502,"../scatter/line_defaults":507,"../scatter/line_shape_defaults":509,"../scatter/marker_defaults":513,"../scatter/subtypes":519,"../scatter/text_defaults":520,"./attributes":522}],525:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function eventData(out, pt, trace, cd, pointNumber) { + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + if(cd[pointNumber]) { + var cdi = cd[pointNumber]; + + // N.B. These are the normalized coordinates. + out.a = cdi.a; + out.b = cdi.b; + out.c = cdi.c; + } else { + // for fill-hover only + out.a = pt.a; + out.b = pt.b; + out.c = pt.c; + } + + return out; +}; + +},{}],526:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function formatLabels(cdi, trace, fullLayout) { + var labels = {}; + + var subplot = fullLayout[trace.subplot]._subplot; + labels.aLabel = Axes.tickText(subplot.aaxis, cdi.a, true).text; + labels.bLabel = Axes.tickText(subplot.baxis, cdi.b, true).text; + labels.cLabel = Axes.tickText(subplot.caxis, cdi.c, true).text; + + return labels; +}; + +},{"../../plots/cartesian/axes":331}],527:[function(_dereq_,module,exports){ +'use strict'; + +var scatterHover = _dereq_('../scatter/hover'); + +module.exports = function hoverPoints(pointData, xval, yval, hovermode) { + var scatterPointData = scatterHover(pointData, xval, yval, hovermode); + if(!scatterPointData || scatterPointData[0].index === false) return; + + var newPointData = scatterPointData[0]; + + // if hovering on a fill, we don't show any point data so the label is + // unchanged from what scatter gives us - except that it needs to + // be constrained to the trianglular plot area, not just the rectangular + // area defined by the synthetic x and y axes + // TODO: in some cases the vertical middle of the shape is not within + // the triangular viewport at all, so the label can become disconnected + // from the shape entirely. But calculating what portion of the shape + // is actually visible, as constrained by the diagonal axis lines, is not + // so easy and anyway we lost the information we would have needed to do + // this inside scatterHover. + if(newPointData.index === undefined) { + var yFracUp = 1 - (newPointData.y0 / pointData.ya._length); + var xLen = pointData.xa._length; + var xMin = xLen * yFracUp / 2; + var xMax = xLen - xMin; + newPointData.x0 = Math.max(Math.min(newPointData.x0, xMax), xMin); + newPointData.x1 = Math.max(Math.min(newPointData.x1, xMax), xMin); + return scatterPointData; + } + + var cdi = newPointData.cd[newPointData.index]; + var trace = newPointData.trace; + var subplot = newPointData.subplot; + + newPointData.a = cdi.a; + newPointData.b = cdi.b; + newPointData.c = cdi.c; + + newPointData.xLabelVal = undefined; + newPointData.yLabelVal = undefined; + + var fullLayout = {}; + fullLayout[trace.subplot] = {_subplot: subplot}; + var labels = trace._module.formatLabels(cdi, trace, fullLayout); + newPointData.aLabel = labels.aLabel; + newPointData.bLabel = labels.bLabel; + newPointData.cLabel = labels.cLabel; + + var hoverinfo = cdi.hi || trace.hoverinfo; + var text = []; + function textPart(ax, val) { + text.push(ax._hovertitle + ': ' + val); + } + if(!trace.hovertemplate) { + var parts = hoverinfo.split('+'); + if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c']; + if(parts.indexOf('a') !== -1) textPart(subplot.aaxis, newPointData.aLabel); + if(parts.indexOf('b') !== -1) textPart(subplot.baxis, newPointData.bLabel); + if(parts.indexOf('c') !== -1) textPart(subplot.caxis, newPointData.cLabel); + } + newPointData.extraText = text.join('
'); + newPointData.hovertemplate = trace.hovertemplate; + return scatterPointData; +}; + +},{"../scatter/hover":505}],528:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + colorbar: _dereq_('../scatter/marker_colorbar'), + formatLabels: _dereq_('./format_labels'), + calc: _dereq_('./calc'), + plot: _dereq_('./plot'), + style: _dereq_('../scatter/style').style, + styleOnSelect: _dereq_('../scatter/style').styleOnSelect, + hoverPoints: _dereq_('./hover'), + selectPoints: _dereq_('../scatter/select'), + eventData: _dereq_('./event_data'), + + moduleType: 'trace', + name: 'scatterternary', + basePlotModule: _dereq_('../../plots/ternary'), + categories: ['ternary', 'symbols', 'showLegend', 'scatter-like'], + meta: { + } +}; + +},{"../../plots/ternary":369,"../scatter/marker_colorbar":512,"../scatter/select":516,"../scatter/style":518,"./attributes":522,"./calc":523,"./defaults":524,"./event_data":525,"./format_labels":526,"./hover":527,"./plot":529}],529:[function(_dereq_,module,exports){ +'use strict'; + +var scatterPlot = _dereq_('../scatter/plot'); + +module.exports = function plot(gd, ternary, moduleCalcData) { + var plotContainer = ternary.plotContainer; + + // remove all nodes inside the scatter layer + plotContainer.select('.scatterlayer').selectAll('*').remove(); + + // mimic cartesian plotinfo + var plotinfo = { + xaxis: ternary.xaxis, + yaxis: ternary.yaxis, + plot: plotContainer, + layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null + }; + + var scatterLayer = ternary.layers.frontplot.select('g.scatterlayer'); + + scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer); +}; + +},{"../scatter/plot":515}],530:[function(_dereq_,module,exports){ +'use strict'; + +var boxAttrs = _dereq_('../box/attributes'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat; + +module.exports = { + y: boxAttrs.y, + x: boxAttrs.x, + x0: boxAttrs.x0, + y0: boxAttrs.y0, + + xhoverformat: axisHoverFormat('x'), + yhoverformat: axisHoverFormat('y'), + + name: extendFlat({}, boxAttrs.name, { + }), + orientation: extendFlat({}, boxAttrs.orientation, { + }), + + bandwidth: { + valType: 'number', + min: 0, + editType: 'calc', + }, + + scalegroup: { + valType: 'string', + dflt: '', + editType: 'calc', + }, + scalemode: { + valType: 'enumerated', + values: ['width', 'count'], + dflt: 'width', + editType: 'calc', + }, + + spanmode: { + valType: 'enumerated', + values: ['soft', 'hard', 'manual'], + dflt: 'soft', + editType: 'calc', + }, + span: { + valType: 'info_array', + items: [ + {valType: 'any', editType: 'calc'}, + {valType: 'any', editType: 'calc'} + ], + editType: 'calc', + }, + + line: { + color: { + valType: 'color', + editType: 'style', + }, + width: { + valType: 'number', + min: 0, + dflt: 2, + editType: 'style', + }, + editType: 'plot' + }, + fillcolor: boxAttrs.fillcolor, + + points: extendFlat({}, boxAttrs.boxpoints, { + }), + jitter: extendFlat({}, boxAttrs.jitter, { + }), + pointpos: extendFlat({}, boxAttrs.pointpos, { + }), + + width: extendFlat({}, boxAttrs.width, { + }), + + marker: boxAttrs.marker, + text: boxAttrs.text, + hovertext: boxAttrs.hovertext, + hovertemplate: boxAttrs.hovertemplate, + + box: { + visible: { + valType: 'boolean', + dflt: false, + editType: 'plot', + }, + width: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.25, + editType: 'plot', + }, + fillcolor: { + valType: 'color', + editType: 'style', + }, + line: { + color: { + valType: 'color', + editType: 'style', + }, + width: { + valType: 'number', + min: 0, + editType: 'style', + }, + editType: 'style' + }, + editType: 'plot' + }, + + meanline: { + visible: { + valType: 'boolean', + dflt: false, + editType: 'plot', + }, + color: { + valType: 'color', + editType: 'style', + }, + width: { + valType: 'number', + min: 0, + editType: 'style', + }, + editType: 'plot' + }, + + side: { + valType: 'enumerated', + values: ['both', 'positive', 'negative'], + dflt: 'both', + editType: 'calc', + }, + + offsetgroup: boxAttrs.offsetgroup, + alignmentgroup: boxAttrs.alignmentgroup, + + selected: boxAttrs.selected, + unselected: boxAttrs.unselected, + + hoveron: { + valType: 'flaglist', + flags: ['violins', 'points', 'kde'], + dflt: 'violins+points+kde', + extras: ['all'], + editType: 'style', + } +}; + +},{"../../lib/extend":279,"../../plots/cartesian/axis_format_attributes":334,"../box/attributes":400}],531:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var boxCalc = _dereq_('../box/calc'); +var helpers = _dereq_('./helpers'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +module.exports = function calc(gd, trace) { + var cd = boxCalc(gd, trace); + + if(cd[0].t.empty) return cd; + + var fullLayout = gd._fullLayout; + var valAxis = Axes.getFromId( + gd, + trace[trace.orientation === 'h' ? 'xaxis' : 'yaxis'] + ); + + var spanMin = Infinity; + var spanMax = -Infinity; + var maxKDE = 0; + var maxCount = 0; + + for(var i = 0; i < cd.length; i++) { + var cdi = cd[i]; + var vals = cdi.pts.map(helpers.extractVal); + + var bandwidth = cdi.bandwidth = calcBandwidth(trace, cdi, vals); + var span = cdi.span = calcSpan(trace, cdi, valAxis, bandwidth); + + if(cdi.min === cdi.max && bandwidth === 0) { + // if span is zero and bandwidth is zero, we want a violin with zero width + span = cdi.span = [cdi.min, cdi.max]; + cdi.density = [{v: 1, t: span[0]}]; + cdi.bandwidth = bandwidth; + maxKDE = Math.max(maxKDE, 1); + } else { + // step that well covers the bandwidth and is multiple of span distance + var dist = span[1] - span[0]; + var n = Math.ceil(dist / (bandwidth / 3)); + var step = dist / n; + + if(!isFinite(step) || !isFinite(n)) { + Lib.error('Something went wrong with computing the violin span'); + cd[0].t.empty = true; + return cd; + } + + var kde = helpers.makeKDE(cdi, trace, vals); + cdi.density = new Array(n); + + for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) { + var v = kde(t); + cdi.density[k] = {v: v, t: t}; + maxKDE = Math.max(maxKDE, v); + } + } + + maxCount = Math.max(maxCount, vals.length); + spanMin = Math.min(spanMin, span[0]); + spanMax = Math.max(spanMax, span[1]); + } + + var extremes = Axes.findExtremes(valAxis, [spanMin, spanMax], {padded: true}); + trace._extremes[valAxis._id] = extremes; + + if(trace.width) { + cd[0].t.maxKDE = maxKDE; + } else { + var violinScaleGroupStats = fullLayout._violinScaleGroupStats; + var scaleGroup = trace.scalegroup; + var groupStats = violinScaleGroupStats[scaleGroup]; + + if(groupStats) { + groupStats.maxKDE = Math.max(groupStats.maxKDE, maxKDE); + groupStats.maxCount = Math.max(groupStats.maxCount, maxCount); + } else { + violinScaleGroupStats[scaleGroup] = { + maxKDE: maxKDE, + maxCount: maxCount + }; + } + } + + cd[0].t.labels.kde = Lib._(gd, 'kde:'); + + return cd; +}; + +// Default to Silveman's rule of thumb +// - https://stats.stackexchange.com/a/6671 +// - https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator +// - https://github.com/statsmodels/statsmodels/blob/master/statsmodels/nonparametric/bandwidths.py +function silvermanRule(len, ssd, iqr) { + var a = Math.min(ssd, iqr / 1.349); + return 1.059 * a * Math.pow(len, -0.2); +} + +function calcBandwidth(trace, cdi, vals) { + var span = cdi.max - cdi.min; + + // If span is zero + if(!span) { + if(trace.bandwidth) { + return trace.bandwidth; + } else { + // if span is zero and no bandwidth is specified + // it returns zero bandwidth which is a special case + return 0; + } + } + + // Limit how small the bandwidth can be. + // + // Silverman's rule of thumb can be "very" small + // when IQR does a poor job at describing the spread + // of the distribution. + // We also want to limit custom bandwidths + // to not blow up kde computations. + + if(trace.bandwidth) { + return Math.max(trace.bandwidth, span / 1e4); + } else { + var len = vals.length; + var ssd = Lib.stdev(vals, len - 1, cdi.mean); + return Math.max( + silvermanRule(len, ssd, cdi.q3 - cdi.q1), + span / 100 + ); + } +} + +function calcSpan(trace, cdi, valAxis, bandwidth) { + var spanmode = trace.spanmode; + var spanIn = trace.span || []; + var spanTight = [cdi.min, cdi.max]; + var spanLoose = [cdi.min - 2 * bandwidth, cdi.max + 2 * bandwidth]; + var spanOut; + + function calcSpanItem(index) { + var s = spanIn[index]; + var sc = valAxis.type === 'multicategory' ? + valAxis.r2c(s) : + valAxis.d2c(s, 0, trace[cdi.valLetter + 'calendar']); + return sc === BADNUM ? spanLoose[index] : sc; + } + + if(spanmode === 'soft') { + spanOut = spanLoose; + } else if(spanmode === 'hard') { + spanOut = spanTight; + } else { + spanOut = [calcSpanItem(0), calcSpanItem(1)]; + } + + // to reuse the equal-range-item block + var dummyAx = { + type: 'linear', + range: spanOut + }; + Axes.setConvert(dummyAx); + dummyAx.cleanRange(); + + return spanOut; +} + +},{"../../constants/numerical":265,"../../lib":285,"../../plots/cartesian/axes":331,"../box/calc":401,"./helpers":534}],532:[function(_dereq_,module,exports){ +'use strict'; + +var setPositionOffset = _dereq_('../box/cross_trace_calc').setPositionOffset; +var orientations = ['v', 'h']; + +module.exports = function crossTraceCalc(gd, plotinfo) { + var calcdata = gd.calcdata; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + for(var i = 0; i < orientations.length; i++) { + var orientation = orientations[i]; + var posAxis = orientation === 'h' ? ya : xa; + var violinList = []; + + for(var j = 0; j < calcdata.length; j++) { + var cd = calcdata[j]; + var t = cd[0].t; + var trace = cd[0].trace; + + if(trace.visible === true && trace.type === 'violin' && + !t.empty && + trace.orientation === orientation && + trace.xaxis === xa._id && + trace.yaxis === ya._id + ) { + violinList.push(j); + } + } + + setPositionOffset('violin', gd, violinList, posAxis); + } +}; + +},{"../box/cross_trace_calc":402}],533:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../../components/color'); + +var boxDefaults = _dereq_('../box/defaults'); +var attributes = _dereq_('./attributes'); + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + function coerce2(attr, dflt) { + return Lib.coerce2(traceIn, traceOut, attributes, attr, dflt); + } + + boxDefaults.handleSampleDefaults(traceIn, traceOut, coerce, layout); + if(traceOut.visible === false) return; + + coerce('bandwidth'); + coerce('side'); + + var width = coerce('width'); + if(!width) { + coerce('scalegroup', traceOut.name); + coerce('scalemode'); + } + + var span = coerce('span'); + var spanmodeDflt; + if(Array.isArray(span)) spanmodeDflt = 'manual'; + coerce('spanmode', spanmodeDflt); + + var lineColor = coerce('line.color', (traceIn.marker || {}).color || defaultColor); + var lineWidth = coerce('line.width'); + var fillColor = coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5)); + + boxDefaults.handlePointsDefaults(traceIn, traceOut, coerce, {prefix: ''}); + + var boxWidth = coerce2('box.width'); + var boxFillColor = coerce2('box.fillcolor', fillColor); + var boxLineColor = coerce2('box.line.color', lineColor); + var boxLineWidth = coerce2('box.line.width', lineWidth); + var boxVisible = coerce('box.visible', Boolean(boxWidth || boxFillColor || boxLineColor || boxLineWidth)); + if(!boxVisible) traceOut.box = {visible: false}; + + var meanLineColor = coerce2('meanline.color', lineColor); + var meanLineWidth = coerce2('meanline.width', lineWidth); + var meanLineVisible = coerce('meanline.visible', Boolean(meanLineColor || meanLineWidth)); + if(!meanLineVisible) traceOut.meanline = {visible: false}; +}; + +},{"../../components/color":155,"../../lib":285,"../box/defaults":403,"./attributes":530}],534:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); + +// Maybe add kernels more down the road, +// but note that the default `spanmode: 'soft'` bounds might have +// to become kernel-dependent +var kernels = { + gaussian: function(v) { + return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * v * v); + } +}; + +exports.makeKDE = function(calcItem, trace, vals) { + var len = vals.length; + var kernel = kernels.gaussian; + var bandwidth = calcItem.bandwidth; + var factor = 1 / (len * bandwidth); + + // don't use Lib.aggNums to skip isNumeric checks + return function(x) { + var sum = 0; + for(var i = 0; i < len; i++) { + sum += kernel((x - vals[i]) / bandwidth); + } + return factor * sum; + }; +}; + +exports.getPositionOnKdePath = function(calcItem, trace, valuePx) { + var posLetter, valLetter; + + if(trace.orientation === 'h') { + posLetter = 'y'; + valLetter = 'x'; + } else { + posLetter = 'x'; + valLetter = 'y'; + } + + var pointOnPath = Lib.findPointOnPath( + calcItem.path, + valuePx, + valLetter, + {pathLength: calcItem.pathLength} + ); + + var posCenterPx = calcItem.posCenterPx; + var posOnPath0 = pointOnPath[posLetter]; + var posOnPath1 = trace.side === 'both' ? + 2 * posCenterPx - posOnPath0 : + posCenterPx; + + return [posOnPath0, posOnPath1]; +}; + +exports.getKdeValue = function(calcItem, trace, valueDist) { + var vals = calcItem.pts.map(exports.extractVal); + var kde = exports.makeKDE(calcItem, trace, vals); + return kde(valueDist) / calcItem.posDensityScale; +}; + +exports.extractVal = function(o) { return o.v; }; + +},{"../../lib":285}],535:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var boxHoverPoints = _dereq_('../box/hover'); +var helpers = _dereq_('./helpers'); + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) { + if(!opts) opts = {}; + var hoverLayer = opts.hoverLayer; + + var cd = pointData.cd; + var trace = cd[0].trace; + var hoveron = trace.hoveron; + var hasHoveronViolins = hoveron.indexOf('violins') !== -1; + var hasHoveronKDE = hoveron.indexOf('kde') !== -1; + var closeData = []; + var closePtData; + var violinLineAttrs; + + if(hasHoveronViolins || hasHoveronKDE) { + var closeBoxData = boxHoverPoints.hoverOnBoxes(pointData, xval, yval, hovermode); + + if(hasHoveronKDE && closeBoxData.length > 0) { + var xa = pointData.xa; + var ya = pointData.ya; + var pLetter, vLetter, pAxis, vAxis, vVal; + + if(trace.orientation === 'h') { + vVal = xval; + pLetter = 'y'; + pAxis = ya; + vLetter = 'x'; + vAxis = xa; + } else { + vVal = yval; + pLetter = 'x'; + pAxis = xa; + vLetter = 'y'; + vAxis = ya; + } + + var di = cd[pointData.index]; + + if(vVal >= di.span[0] && vVal <= di.span[1]) { + var kdePointData = Lib.extendFlat({}, pointData); + var vValPx = vAxis.c2p(vVal, true); + var kdeVal = helpers.getKdeValue(di, trace, vVal); + var pOnPath = helpers.getPositionOnKdePath(di, trace, vValPx); + var paOffset = pAxis._offset; + var paLength = pAxis._length; + + kdePointData[pLetter + '0'] = pOnPath[0]; + kdePointData[pLetter + '1'] = pOnPath[1]; + kdePointData[vLetter + '0'] = kdePointData[vLetter + '1'] = vValPx; + kdePointData[vLetter + 'Label'] = vLetter + ': ' + Axes.hoverLabelText(vAxis, vVal, trace[vLetter + 'hoverformat']) + ', ' + cd[0].t.labels.kde + ' ' + kdeVal.toFixed(3); + + // move the spike to the KDE point + kdePointData.spikeDistance = closeBoxData[0].spikeDistance; + var spikePosAttr = pLetter + 'Spike'; + kdePointData[spikePosAttr] = closeBoxData[0][spikePosAttr]; + closeBoxData[0].spikeDistance = undefined; + closeBoxData[0][spikePosAttr] = undefined; + + // no hovertemplate support yet + kdePointData.hovertemplate = false; + + closeData.push(kdePointData); + + violinLineAttrs = {stroke: pointData.color}; + violinLineAttrs[pLetter + '1'] = Lib.constrain(paOffset + pOnPath[0], paOffset, paOffset + paLength); + violinLineAttrs[pLetter + '2'] = Lib.constrain(paOffset + pOnPath[1], paOffset, paOffset + paLength); + violinLineAttrs[vLetter + '1'] = violinLineAttrs[vLetter + '2'] = vAxis._offset + vValPx; + } + } + + if(hasHoveronViolins) { + closeData = closeData.concat(closeBoxData); + } + } + + if(hoveron.indexOf('points') !== -1) { + closePtData = boxHoverPoints.hoverOnPoints(pointData, xval, yval); + } + + // update violin line (if any) + var violinLine = hoverLayer.selectAll('.violinline-' + trace.uid) + .data(violinLineAttrs ? [0] : []); + violinLine.enter().append('line') + .classed('violinline-' + trace.uid, true) + .attr('stroke-width', 1.5); + violinLine.exit().remove(); + violinLine.attr(violinLineAttrs); + + // same combine logic as box hoverPoints + if(hovermode === 'closest') { + if(closePtData) return [closePtData]; + return closeData; + } + if(closePtData) { + closeData.push(closePtData); + return closeData; + } + return closeData; +}; + +},{"../../lib":285,"../../plots/cartesian/axes":331,"../box/hover":405,"./helpers":534}],536:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + layoutAttributes: _dereq_('./layout_attributes'), + supplyDefaults: _dereq_('./defaults'), + crossTraceDefaults: _dereq_('../box/defaults').crossTraceDefaults, + supplyLayoutDefaults: _dereq_('./layout_defaults'), + calc: _dereq_('./calc'), + crossTraceCalc: _dereq_('./cross_trace_calc'), + plot: _dereq_('./plot'), + style: _dereq_('./style'), + styleOnSelect: _dereq_('../scatter/style').styleOnSelect, + hoverPoints: _dereq_('./hover'), + selectPoints: _dereq_('../box/select'), + + moduleType: 'trace', + name: 'violin', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'violinLayout', 'zoomScale'], + meta: { + } +}; + +},{"../../plots/cartesian":345,"../box/defaults":403,"../box/select":410,"../scatter/style":518,"./attributes":530,"./calc":531,"./cross_trace_calc":532,"./defaults":533,"./hover":535,"./layout_attributes":537,"./layout_defaults":538,"./plot":539,"./style":540}],537:[function(_dereq_,module,exports){ +'use strict'; + +var boxLayoutAttrs = _dereq_('../box/layout_attributes'); +var extendFlat = _dereq_('../../lib').extendFlat; + +module.exports = { + violinmode: extendFlat({}, boxLayoutAttrs.boxmode, { + }), + violingap: extendFlat({}, boxLayoutAttrs.boxgap, { + }), + violingroupgap: extendFlat({}, boxLayoutAttrs.boxgroupgap, { + }) +}; + +},{"../../lib":285,"../box/layout_attributes":407}],538:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../../lib'); +var layoutAttributes = _dereq_('./layout_attributes'); +var boxLayoutDefaults = _dereq_('../box/layout_defaults'); + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + boxLayoutDefaults._supply(layoutIn, layoutOut, fullData, coerce, 'violin'); +}; + +},{"../../lib":285,"../box/layout_defaults":408,"./layout_attributes":537}],539:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); + +var boxPlot = _dereq_('../box/plot'); +var linePoints = _dereq_('../scatter/line_points'); +var helpers = _dereq_('./helpers'); + +module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { + var fullLayout = gd._fullLayout; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + function makePath(pts) { + var segments = linePoints(pts, { + xaxis: xa, + yaxis: ya, + connectGaps: true, + baseTolerance: 0.75, + shape: 'spline', + simplify: true, + linearized: true + }); + return Drawing.smoothopen(segments[0], 1); + } + + Lib.makeTraceGroups(violinLayer, cdViolins, 'trace violins').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var t = cd0.t; + var trace = cd0.trace; + + if(trace.visible !== true || t.empty) { + plotGroup.remove(); + return; + } + + var bPos = t.bPos; + var bdPos = t.bdPos; + var valAxis = plotinfo[t.valLetter + 'axis']; + var posAxis = plotinfo[t.posLetter + 'axis']; + var hasBothSides = trace.side === 'both'; + var hasPositiveSide = hasBothSides || trace.side === 'positive'; + var hasNegativeSide = hasBothSides || trace.side === 'negative'; + + var violins = plotGroup.selectAll('path.violin').data(Lib.identity); + + violins.enter().append('path') + .style('vector-effect', 'non-scaling-stroke') + .attr('class', 'violin'); + + violins.exit().remove(); + + violins.each(function(d) { + var pathSel = d3.select(this); + var density = d.density; + var len = density.length; + var posCenter = posAxis.c2l(d.pos + bPos, true); + var posCenterPx = posAxis.l2p(posCenter); + + var scale; + if(trace.width) { + scale = t.maxKDE / bdPos; + } else { + var groupStats = fullLayout._violinScaleGroupStats[trace.scalegroup]; + scale = trace.scalemode === 'count' ? + (groupStats.maxKDE / bdPos) * (groupStats.maxCount / d.pts.length) : + groupStats.maxKDE / bdPos; + } + + var pathPos, pathNeg, path; + var i, k, pts, pt; + + if(hasPositiveSide) { + pts = new Array(len); + for(i = 0; i < len; i++) { + pt = pts[i] = {}; + pt[t.posLetter] = posCenter + (density[i].v / scale); + pt[t.valLetter] = valAxis.c2l(density[i].t, true); + } + pathPos = makePath(pts); + } + + if(hasNegativeSide) { + pts = new Array(len); + for(k = 0, i = len - 1; k < len; k++, i--) { + pt = pts[k] = {}; + pt[t.posLetter] = posCenter - (density[i].v / scale); + pt[t.valLetter] = valAxis.c2l(density[i].t, true); + } + pathNeg = makePath(pts); + } + + if(hasBothSides) { + path = pathPos + 'L' + pathNeg.substr(1) + 'Z'; + } else { + var startPt = [posCenterPx, valAxis.c2p(density[0].t)]; + var endPt = [posCenterPx, valAxis.c2p(density[len - 1].t)]; + + if(trace.orientation === 'h') { + startPt.reverse(); + endPt.reverse(); + } + + if(hasPositiveSide) { + path = 'M' + startPt + 'L' + pathPos.substr(1) + 'L' + endPt; + } else { + path = 'M' + endPt + 'L' + pathNeg.substr(1) + 'L' + startPt; + } + } + pathSel.attr('d', path); + + // save a few things used in getPositionOnKdePath, getKdeValue + // on hover and for meanline draw block below + d.posCenterPx = posCenterPx; + d.posDensityScale = scale * bdPos; + d.path = pathSel.node(); + d.pathLength = d.path.getTotalLength() / (hasBothSides ? 2 : 1); + }); + + var boxAttrs = trace.box; + var boxWidth = boxAttrs.width; + var boxLineWidth = (boxAttrs.line || {}).width; + var bdPosScaled; + var bPosPxOffset; + + if(hasBothSides) { + bdPosScaled = bdPos * boxWidth; + bPosPxOffset = 0; + } else if(hasPositiveSide) { + bdPosScaled = [0, bdPos * boxWidth / 2]; + bPosPxOffset = boxLineWidth * {x: 1, y: -1}[t.posLetter]; + } else { + bdPosScaled = [bdPos * boxWidth / 2, 0]; + bPosPxOffset = boxLineWidth * {x: -1, y: 1}[t.posLetter]; + } + + // inner box + boxPlot.plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, { + bPos: bPos, + bdPos: bdPosScaled, + bPosPxOffset: bPosPxOffset + }); + + // meanline insider box + boxPlot.plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, { + bPos: bPos, + bdPos: bdPosScaled, + bPosPxOffset: bPosPxOffset + }); + + var fn; + if(!trace.box.visible && trace.meanline.visible) { + fn = Lib.identity; + } + + // N.B. use different class name than boxPlot.plotBoxMean, + // to avoid selectAll conflict + var meanPaths = plotGroup.selectAll('path.meanline').data(fn || []); + meanPaths.enter().append('path') + .attr('class', 'meanline') + .style('fill', 'none') + .style('vector-effect', 'non-scaling-stroke'); + meanPaths.exit().remove(); + meanPaths.each(function(d) { + var v = valAxis.c2p(d.mean, true); + var p = helpers.getPositionOnKdePath(d, trace, v); + + d3.select(this).attr('d', + trace.orientation === 'h' ? + 'M' + v + ',' + p[0] + 'V' + p[1] : + 'M' + p[0] + ',' + v + 'H' + p[1] + ); + }); + + boxPlot.plotPoints(plotGroup, {x: xa, y: ya}, trace, t); + }); +}; + +},{"../../components/drawing":177,"../../lib":285,"../box/plot":409,"../scatter/line_points":508,"./helpers":534,"@plotly/d3":20}],540:[function(_dereq_,module,exports){ +'use strict'; + +var d3 = _dereq_('@plotly/d3'); +var Color = _dereq_('../../components/color'); +var stylePoints = _dereq_('../scatter/style').stylePoints; + +module.exports = function style(gd) { + var s = d3.select(gd).selectAll('g.trace.violins'); + + s.style('opacity', function(d) { return d[0].trace.opacity; }); + + s.each(function(d) { + var trace = d[0].trace; + var sel = d3.select(this); + var box = trace.box || {}; + var boxLine = box.line || {}; + var meanline = trace.meanline || {}; + var meanLineWidth = meanline.width; + + sel.selectAll('path.violin') + .style('stroke-width', trace.line.width + 'px') + .call(Color.stroke, trace.line.color) + .call(Color.fill, trace.fillcolor); + + sel.selectAll('path.box') + .style('stroke-width', boxLine.width + 'px') + .call(Color.stroke, boxLine.color) + .call(Color.fill, box.fillcolor); + + var meanLineStyle = { + 'stroke-width': meanLineWidth + 'px', + 'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px' + }; + + sel.selectAll('path.mean') + .style(meanLineStyle) + .call(Color.stroke, meanline.color); + + sel.selectAll('path.meanline') + .style(meanLineStyle) + .call(Color.stroke, meanline.color); + + stylePoints(sel, trace, gd); + }); +}; + +},{"../../components/color":155,"../scatter/style":518,"@plotly/d3":20}],541:[function(_dereq_,module,exports){ +'use strict'; + +var Axes = _dereq_('../plots/cartesian/axes'); +var Lib = _dereq_('../lib'); +var PlotSchema = _dereq_('../plot_api/plot_schema'); +var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction; +var BADNUM = _dereq_('../constants/numerical').BADNUM; + +exports.moduleType = 'transform'; + +exports.name = 'aggregate'; + +var attrs = exports.attributes = { + enabled: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + groups: { + // TODO: groupby should support string or array grouping this way too + // currently groupby only allows a grouping array + valType: 'string', + strict: true, + noBlank: true, + arrayOk: true, + dflt: 'x', + editType: 'calc', + }, + aggregations: { + _isLinkedToArray: 'aggregation', + target: { + valType: 'string', + editType: 'calc', + }, + func: { + valType: 'enumerated', + values: ['count', 'sum', 'avg', 'median', 'mode', 'rms', 'stddev', 'min', 'max', 'first', 'last', 'change', 'range'], + dflt: 'first', + editType: 'calc', + }, + funcmode: { + valType: 'enumerated', + values: ['sample', 'population'], + dflt: 'sample', + editType: 'calc', + }, + enabled: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + editType: 'calc' + }, + editType: 'calc' +}; + +var aggAttrs = attrs.aggregations; + +/** + * Supply transform attributes defaults + * + * @param {object} transformIn + * object linked to trace.transforms[i] with 'func' set to exports.name + * @param {object} traceOut + * the _fullData trace this transform applies to + * @param {object} layout + * the plot's (not-so-full) layout + * @param {object} traceIn + * the input data trace this transform applies to + * + * @return {object} transformOut + * copy of transformIn that contains attribute defaults + */ +exports.supplyDefaults = function(transformIn, traceOut) { + var transformOut = {}; + var i; + + function coerce(attr, dflt) { + return Lib.coerce(transformIn, transformOut, attrs, attr, dflt); + } + + var enabled = coerce('enabled'); + + if(!enabled) return transformOut; + + /* + * Normally _arrayAttrs is calculated during doCalc, but that comes later. + * Anyway this can change due to *count* aggregations (see below) so it's not + * necessarily the same set. + * + * For performance we turn it into an object of truthy values + * we'll use 1 for arrays we haven't aggregated yet, 0 for finished arrays, + * as distinct from undefined which means this array isn't present in the input + * missing arrays can still be aggregate outputs for *count* aggregations. + */ + var arrayAttrArray = PlotSchema.findArrayAttributes(traceOut); + var arrayAttrs = {}; + for(i = 0; i < arrayAttrArray.length; i++) arrayAttrs[arrayAttrArray[i]] = 1; + + var groups = coerce('groups'); + + if(!Array.isArray(groups)) { + if(!arrayAttrs[groups]) { + transformOut.enabled = false; + return transformOut; + } + arrayAttrs[groups] = 0; + } + + var aggregationsIn = transformIn.aggregations || []; + var aggregationsOut = transformOut.aggregations = new Array(aggregationsIn.length); + var aggregationOut; + + function coercei(attr, dflt) { + return Lib.coerce(aggregationsIn[i], aggregationOut, aggAttrs, attr, dflt); + } + + for(i = 0; i < aggregationsIn.length; i++) { + aggregationOut = {_index: i}; + var target = coercei('target'); + var func = coercei('func'); + var enabledi = coercei('enabled'); + + // add this aggregation to the output only if it's the first instance + // of a valid target attribute - or an unused target attribute with "count" + if(enabledi && target && (arrayAttrs[target] || (func === 'count' && arrayAttrs[target] === undefined))) { + if(func === 'stddev') coercei('funcmode'); + + arrayAttrs[target] = 0; + aggregationsOut[i] = aggregationOut; + } else aggregationsOut[i] = {enabled: false, _index: i}; + } + + // any array attributes we haven't yet covered, fill them with the default aggregation + for(i = 0; i < arrayAttrArray.length; i++) { + if(arrayAttrs[arrayAttrArray[i]]) { + aggregationsOut.push({ + target: arrayAttrArray[i], + func: aggAttrs.func.dflt, + enabled: true, + _index: -1 + }); + } + } + + return transformOut; +}; + + +exports.calcTransform = function(gd, trace, opts) { + if(!opts.enabled) return; + + var groups = opts.groups; + + var groupArray = Lib.getTargetArray(trace, {target: groups}); + if(!groupArray) return; + + var i, vi, groupIndex, newGrouping; + + var groupIndices = {}; + var indexToPoints = {}; + var groupings = []; + + var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts); + + var len = groupArray.length; + if(trace._length) len = Math.min(len, trace._length); + + for(i = 0; i < len; i++) { + vi = groupArray[i]; + groupIndex = groupIndices[vi]; + if(groupIndex === undefined) { + groupIndices[vi] = groupings.length; + newGrouping = [i]; + groupings.push(newGrouping); + indexToPoints[groupIndices[vi]] = originalPointsAccessor(i); + } else { + groupings[groupIndex].push(i); + indexToPoints[groupIndices[vi]] = (indexToPoints[groupIndices[vi]] || []).concat(originalPointsAccessor(i)); + } + } + + opts._indexToPoints = indexToPoints; + + var aggregations = opts.aggregations; + + for(i = 0; i < aggregations.length; i++) { + aggregateOneArray(gd, trace, groupings, aggregations[i]); + } + + if(typeof groups === 'string') { + aggregateOneArray(gd, trace, groupings, { + target: groups, + func: 'first', + enabled: true + }); + } + + trace._length = groupings.length; +}; + +function aggregateOneArray(gd, trace, groupings, aggregation) { + if(!aggregation.enabled) return; + + var attr = aggregation.target; + var targetNP = Lib.nestedProperty(trace, attr); + var arrayIn = targetNP.get(); + var conversions = Axes.getDataConversions(gd, trace, attr, arrayIn); + var func = getAggregateFunction(aggregation, conversions); + + var arrayOut = new Array(groupings.length); + for(var i = 0; i < groupings.length; i++) { + arrayOut[i] = func(arrayIn, groupings[i]); + } + targetNP.set(arrayOut); + + if(aggregation.func === 'count') { + // count does not depend on an input array, so it's likely not part of _arrayAttrs yet + // but after this transform it most definitely *is* an array attribute. + Lib.pushUnique(trace._arrayAttrs, attr); + } +} + +function getAggregateFunction(opts, conversions) { + var func = opts.func; + var d2c = conversions.d2c; + var c2d = conversions.c2d; + + switch(func) { + // count, first, and last don't depend on anything about the data + // point back to pure functions for performance + case 'count': + return count; + case 'first': + return first; + case 'last': + return last; + + case 'sum': + // This will produce output in all cases even though it's nonsensical + // for date or category data. + return function(array, indices) { + var total = 0; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) total += vi; + } + return c2d(total); + }; + + case 'avg': + // Generally meaningless for category data but it still does something. + return function(array, indices) { + var total = 0; + var cnt = 0; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) { + total += vi; + cnt++; + } + } + return cnt ? c2d(total / cnt) : BADNUM; + }; + + case 'min': + return function(array, indices) { + var out = Infinity; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) out = Math.min(out, vi); + } + return (out === Infinity) ? BADNUM : c2d(out); + }; + + case 'max': + return function(array, indices) { + var out = -Infinity; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) out = Math.max(out, vi); + } + return (out === -Infinity) ? BADNUM : c2d(out); + }; + + case 'range': + return function(array, indices) { + var min = Infinity; + var max = -Infinity; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) { + min = Math.min(min, vi); + max = Math.max(max, vi); + } + } + return (max === -Infinity || min === Infinity) ? BADNUM : c2d(max - min); + }; + + case 'change': + return function(array, indices) { + var first = d2c(array[indices[0]]); + var last = d2c(array[indices[indices.length - 1]]); + return (first === BADNUM || last === BADNUM) ? BADNUM : c2d(last - first); + }; + + case 'median': + return function(array, indices) { + var sortCalc = []; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) sortCalc.push(vi); + } + if(!sortCalc.length) return BADNUM; + sortCalc.sort(Lib.sorterAsc); + var mid = (sortCalc.length - 1) / 2; + return c2d((sortCalc[Math.floor(mid)] + sortCalc[Math.ceil(mid)]) / 2); + }; + + case 'mode': + return function(array, indices) { + var counts = {}; + var maxCnt = 0; + var out = BADNUM; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) { + var counti = counts[vi] = (counts[vi] || 0) + 1; + if(counti > maxCnt) { + maxCnt = counti; + out = vi; + } + } + } + return maxCnt ? c2d(out) : BADNUM; + }; + + case 'rms': + return function(array, indices) { + var total = 0; + var cnt = 0; + for(var i = 0; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) { + total += vi * vi; + cnt++; + } + } + return cnt ? c2d(Math.sqrt(total / cnt)) : BADNUM; + }; + + case 'stddev': + return function(array, indices) { + // balance numerical stability with performance: + // so that we call d2c once per element but don't need to + // store them, reference all to the first element + var total = 0; + var total2 = 0; + var cnt = 1; + var v0 = BADNUM; + var i; + for(i = 0; i < indices.length && v0 === BADNUM; i++) { + v0 = d2c(array[indices[i]]); + } + if(v0 === BADNUM) return BADNUM; + + for(; i < indices.length; i++) { + var vi = d2c(array[indices[i]]); + if(vi !== BADNUM) { + var dv = vi - v0; + total += dv; + total2 += dv * dv; + cnt++; + } + } + + // This is population std dev, if we want sample std dev + // we would need (...) / (cnt - 1) + // Also note there's no c2d here - that means for dates the result + // is a number of milliseconds, and for categories it's a number + // of category differences, which is not generically meaningful but + // as in other cases we don't forbid it. + var norm = (opts.funcmode === 'sample') ? (cnt - 1) : cnt; + // this is debatable: should a count of 1 return sample stddev of + // 0 or undefined? + if(!norm) return 0; + return Math.sqrt((total2 - (total * total / cnt)) / norm); + }; + } +} + +function count(array, indices) { + return indices.length; +} + +function first(array, indices) { + return array[indices[0]]; +} + +function last(array, indices) { + return array[indices[indices.length - 1]]; +} + +},{"../constants/numerical":265,"../lib":285,"../plot_api/plot_schema":319,"../plots/cartesian/axes":331,"./helpers":544}],542:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var Registry = _dereq_('../registry'); +var Axes = _dereq_('../plots/cartesian/axes'); +var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction; + +var filterOps = _dereq_('../constants/filter_ops'); +var COMPARISON_OPS = filterOps.COMPARISON_OPS; +var INTERVAL_OPS = filterOps.INTERVAL_OPS; +var SET_OPS = filterOps.SET_OPS; + +exports.moduleType = 'transform'; + +exports.name = 'filter'; + +exports.attributes = { + enabled: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + target: { + valType: 'string', + strict: true, + noBlank: true, + arrayOk: true, + dflt: 'x', + editType: 'calc', + }, + operation: { + valType: 'enumerated', + values: [] + .concat(COMPARISON_OPS) + .concat(INTERVAL_OPS) + .concat(SET_OPS), + dflt: '=', + editType: 'calc', + }, + value: { + valType: 'any', + dflt: 0, + editType: 'calc', + }, + preservegaps: { + valType: 'boolean', + dflt: false, + editType: 'calc', + }, + editType: 'calc' +}; + +exports.supplyDefaults = function(transformIn) { + var transformOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt); + } + + var enabled = coerce('enabled'); + + if(enabled) { + var target = coerce('target'); + + if(Lib.isArrayOrTypedArray(target) && target.length === 0) { + transformOut.enabled = false; + return transformOut; + } + + coerce('preservegaps'); + coerce('operation'); + coerce('value'); + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(transformIn, transformOut, 'valuecalendar', null); + handleCalendarDefaults(transformIn, transformOut, 'targetcalendar', null); + } + + return transformOut; +}; + +exports.calcTransform = function(gd, trace, opts) { + if(!opts.enabled) return; + + var targetArray = Lib.getTargetArray(trace, opts); + if(!targetArray) return; + + var target = opts.target; + + var len = targetArray.length; + if(trace._length) len = Math.min(len, trace._length); + + var targetCalendar = opts.targetcalendar; + var arrayAttrs = trace._arrayAttrs; + var preservegaps = opts.preservegaps; + + // even if you provide targetcalendar, if target is a string and there + // is a calendar attribute matching target it will get used instead. + if(typeof target === 'string') { + var attrTargetCalendar = Lib.nestedProperty(trace, target + 'calendar').get(); + if(attrTargetCalendar) targetCalendar = attrTargetCalendar; + } + + var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray); + var filterFunc = getFilterFunc(opts, d2c, targetCalendar); + var originalArrays = {}; + var indexToPoints = {}; + var index = 0; + + function forAllAttrs(fn, index) { + for(var j = 0; j < arrayAttrs.length; j++) { + var np = Lib.nestedProperty(trace, arrayAttrs[j]); + fn(np, index); + } + } + + var initFn; + var fillFn; + if(preservegaps) { + initFn = function(np) { + originalArrays[np.astr] = Lib.extendDeep([], np.get()); + np.set(new Array(len)); + }; + fillFn = function(np, index) { + var val = originalArrays[np.astr][index]; + np.get()[index] = val; + }; + } else { + initFn = function(np) { + originalArrays[np.astr] = Lib.extendDeep([], np.get()); + np.set([]); + }; + fillFn = function(np, index) { + var val = originalArrays[np.astr][index]; + np.get().push(val); + }; + } + + // copy all original array attribute values, and clear arrays in trace + forAllAttrs(initFn); + + var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts); + + // loop through filter array, fill trace arrays if passed + for(var i = 0; i < len; i++) { + var passed = filterFunc(targetArray[i]); + if(passed) { + forAllAttrs(fillFn, i); + indexToPoints[index++] = originalPointsAccessor(i); + } else if(preservegaps) index++; + } + + opts._indexToPoints = indexToPoints; + trace._length = index; +}; + +function getFilterFunc(opts, d2c, targetCalendar) { + var operation = opts.operation; + var value = opts.value; + var hasArrayValue = Array.isArray(value); + + function isOperationIn(array) { + return array.indexOf(operation) !== -1; + } + + var d2cValue = function(v) { return d2c(v, 0, opts.valuecalendar); }; + var d2cTarget = function(v) { return d2c(v, 0, targetCalendar); }; + + var coercedValue; + + if(isOperationIn(COMPARISON_OPS)) { + coercedValue = hasArrayValue ? d2cValue(value[0]) : d2cValue(value); + } else if(isOperationIn(INTERVAL_OPS)) { + coercedValue = hasArrayValue ? + [d2cValue(value[0]), d2cValue(value[1])] : + [d2cValue(value), d2cValue(value)]; + } else if(isOperationIn(SET_OPS)) { + coercedValue = hasArrayValue ? value.map(d2cValue) : [d2cValue(value)]; + } + + switch(operation) { + case '=': + return function(v) { return d2cTarget(v) === coercedValue; }; + + case '!=': + return function(v) { return d2cTarget(v) !== coercedValue; }; + + case '<': + return function(v) { return d2cTarget(v) < coercedValue; }; + + case '<=': + return function(v) { return d2cTarget(v) <= coercedValue; }; + + case '>': + return function(v) { return d2cTarget(v) > coercedValue; }; + + case '>=': + return function(v) { return d2cTarget(v) >= coercedValue; }; + + case '[]': + return function(v) { + var cv = d2cTarget(v); + return cv >= coercedValue[0] && cv <= coercedValue[1]; + }; + + case '()': + return function(v) { + var cv = d2cTarget(v); + return cv > coercedValue[0] && cv < coercedValue[1]; + }; + + case '[)': + return function(v) { + var cv = d2cTarget(v); + return cv >= coercedValue[0] && cv < coercedValue[1]; + }; + + case '(]': + return function(v) { + var cv = d2cTarget(v); + return cv > coercedValue[0] && cv <= coercedValue[1]; + }; + + case '][': + return function(v) { + var cv = d2cTarget(v); + return cv <= coercedValue[0] || cv >= coercedValue[1]; + }; + + case ')(': + return function(v) { + var cv = d2cTarget(v); + return cv < coercedValue[0] || cv > coercedValue[1]; + }; + + case '](': + return function(v) { + var cv = d2cTarget(v); + return cv <= coercedValue[0] || cv > coercedValue[1]; + }; + + case ')[': + return function(v) { + var cv = d2cTarget(v); + return cv < coercedValue[0] || cv >= coercedValue[1]; + }; + + case '{}': + return function(v) { + return coercedValue.indexOf(d2cTarget(v)) !== -1; + }; + + case '}{': + return function(v) { + return coercedValue.indexOf(d2cTarget(v)) === -1; + }; + } +} + +},{"../constants/filter_ops":263,"../lib":285,"../plots/cartesian/axes":331,"../registry":373,"./helpers":544}],543:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var PlotSchema = _dereq_('../plot_api/plot_schema'); +var Plots = _dereq_('../plots/plots'); +var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction; + +exports.moduleType = 'transform'; + +exports.name = 'groupby'; + +exports.attributes = { + enabled: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + groups: { + valType: 'data_array', + dflt: [], + editType: 'calc', + }, + nameformat: { + valType: 'string', + editType: 'calc', + }, + styles: { + _isLinkedToArray: 'style', + target: { + valType: 'string', + editType: 'calc', + }, + value: { + valType: 'any', + dflt: {}, + editType: 'calc', + _compareAsJSON: true + }, + editType: 'calc' + }, + editType: 'calc' +}; + +/** + * Supply transform attributes defaults + * + * @param {object} transformIn + * object linked to trace.transforms[i] with 'type' set to exports.name + * @param {object} traceOut + * the _fullData trace this transform applies to + * @param {object} layout + * the plot's (not-so-full) layout + * @param {object} traceIn + * the input data trace this transform applies to + * + * @return {object} transformOut + * copy of transformIn that contains attribute defaults + */ +exports.supplyDefaults = function(transformIn, traceOut, layout) { + var i; + var transformOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt); + } + + var enabled = coerce('enabled'); + + if(!enabled) return transformOut; + + coerce('groups'); + coerce('nameformat', layout._dataLength > 1 ? '%{group} (%{trace})' : '%{group}'); + + var styleIn = transformIn.styles; + var styleOut = transformOut.styles = []; + + if(styleIn) { + for(i = 0; i < styleIn.length; i++) { + var thisStyle = styleOut[i] = {}; + Lib.coerce(styleIn[i], styleOut[i], exports.attributes.styles, 'target'); + var value = Lib.coerce(styleIn[i], styleOut[i], exports.attributes.styles, 'value'); + + // so that you can edit value in place and have Plotly.react notice it, or + // rebuild it every time and have Plotly.react NOT think it changed: + // use _compareAsJSON to say we should diff the _JSON_value + if(Lib.isPlainObject(value)) thisStyle.value = Lib.extendDeep({}, value); + else if(value) delete thisStyle.value; + } + } + + return transformOut; +}; + + +/** + * Apply transform !!! + * + * @param {array} data + * array of transformed traces (is [fullTrace] upon first transform) + * + * @param {object} state + * state object which includes: + * - transform {object} full transform attributes + * - fullTrace {object} full trace object which is being transformed + * - fullData {array} full pre-transform(s) data array + * - layout {object} the plot's (not-so-full) layout + * + * @return {object} newData + * array of transformed traces + */ +exports.transform = function(data, state) { + var newTraces, i, j; + var newData = []; + + for(i = 0; i < data.length; i++) { + newTraces = transformOne(data[i], state); + + for(j = 0; j < newTraces.length; j++) { + newData.push(newTraces[j]); + } + } + + return newData; +}; + +function transformOne(trace, state) { + var i, j, k, attr, srcArray, groupName, newTrace, transforms, arrayLookup; + var groupNameObj; + + var opts = state.transform; + var transformIndex = state.transformIndex; + var groups = trace.transforms[transformIndex].groups; + var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts); + + if(!(Lib.isArrayOrTypedArray(groups)) || groups.length === 0) { + return [trace]; + } + + var groupNames = Lib.filterUnique(groups); + var newData = new Array(groupNames.length); + var len = groups.length; + + var arrayAttrs = PlotSchema.findArrayAttributes(trace); + + var styles = opts.styles || []; + var styleLookup = {}; + for(i = 0; i < styles.length; i++) { + styleLookup[styles[i].target] = styles[i].value; + } + + if(opts.styles) { + groupNameObj = Lib.keyedContainer(opts, 'styles', 'target', 'value.name'); + } + + // An index to map group name --> expanded trace index + var indexLookup = {}; + var indexCnts = {}; + + for(i = 0; i < groupNames.length; i++) { + groupName = groupNames[i]; + indexLookup[groupName] = i; + indexCnts[groupName] = 0; + + // Start with a deep extend that just copies array references. + newTrace = newData[i] = Lib.extendDeepNoArrays({}, trace); + newTrace._group = groupName; + newTrace.transforms[transformIndex]._indexToPoints = {}; + + var suppliedName = null; + if(groupNameObj) { + suppliedName = groupNameObj.get(groupName); + } + + if(suppliedName || suppliedName === '') { + newTrace.name = suppliedName; + } else { + newTrace.name = Lib.templateString(opts.nameformat, { + trace: trace.name, + group: groupName + }); + } + + // In order for groups to apply correctly to other transform data (e.g. + // a filter transform), we have to break the connection and clone the + // transforms so that each group writes grouped values into a different + // destination. This function does not break the array reference + // connection between the split transforms it creates. That's handled in + // initialize, which creates a new empty array for each arrayAttr. + transforms = newTrace.transforms; + newTrace.transforms = []; + for(j = 0; j < transforms.length; j++) { + newTrace.transforms[j] = Lib.extendDeepNoArrays({}, transforms[j]); + } + + // Initialize empty arrays for the arrayAttrs, to be split in the next step + for(j = 0; j < arrayAttrs.length; j++) { + Lib.nestedProperty(newTrace, arrayAttrs[j]).set([]); + } + } + + // For each array attribute including those nested inside this and other + // transforms (small note that we technically only need to do this for + // transforms that have not yet been applied): + for(k = 0; k < arrayAttrs.length; k++) { + attr = arrayAttrs[k]; + + // Cache all the arrays to which we'll push: + for(j = 0, arrayLookup = []; j < groupNames.length; j++) { + arrayLookup[j] = Lib.nestedProperty(newData[j], attr).get(); + } + + // Get the input data: + srcArray = Lib.nestedProperty(trace, attr).get(); + + // Send each data point to the appropriate expanded trace: + for(j = 0; j < len; j++) { + // Map group data --> trace index --> array and push data onto it + arrayLookup[indexLookup[groups[j]]].push(srcArray[j]); + } + } + + for(j = 0; j < len; j++) { + newTrace = newData[indexLookup[groups[j]]]; + + var indexToPoints = newTrace.transforms[transformIndex]._indexToPoints; + indexToPoints[indexCnts[groups[j]]] = originalPointsAccessor(j); + indexCnts[groups[j]]++; + } + + for(i = 0; i < groupNames.length; i++) { + groupName = groupNames[i]; + newTrace = newData[i]; + + Plots.clearExpandedTraceDefaultColors(newTrace); + + // there's no need to coerce styleLookup[groupName] here + // as another round of supplyDefaults is done on the transformed traces + newTrace = Lib.extendDeepNoArrays(newTrace, styleLookup[groupName] || {}); + } + + return newData; +} + +},{"../lib":285,"../plot_api/plot_schema":319,"../plots/plots":366,"./helpers":544}],544:[function(_dereq_,module,exports){ +'use strict'; + +exports.pointsAccessorFunction = function(transforms, opts) { + var tr; + var prevIndexToPoints; + for(var i = 0; i < transforms.length; i++) { + tr = transforms[i]; + if(tr === opts) break; + if(!tr._indexToPoints || tr.enabled === false) continue; + prevIndexToPoints = tr._indexToPoints; + } + var originalPointsAccessor = prevIndexToPoints ? + function(i) {return prevIndexToPoints[i];} : + function(i) {return [i];}; + return originalPointsAccessor; +}; + +},{}],545:[function(_dereq_,module,exports){ +'use strict'; + +var Lib = _dereq_('../lib'); +var Axes = _dereq_('../plots/cartesian/axes'); +var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction; + +var BADNUM = _dereq_('../constants/numerical').BADNUM; + +exports.moduleType = 'transform'; + +exports.name = 'sort'; + +exports.attributes = { + enabled: { + valType: 'boolean', + dflt: true, + editType: 'calc', + }, + target: { + valType: 'string', + strict: true, + noBlank: true, + arrayOk: true, + dflt: 'x', + editType: 'calc', + }, + order: { + valType: 'enumerated', + values: ['ascending', 'descending'], + dflt: 'ascending', + editType: 'calc', + }, + editType: 'calc' +}; + +exports.supplyDefaults = function(transformIn) { + var transformOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt); + } + + var enabled = coerce('enabled'); + + if(enabled) { + coerce('target'); + coerce('order'); + } + + return transformOut; +}; + +exports.calcTransform = function(gd, trace, opts) { + if(!opts.enabled) return; + + var targetArray = Lib.getTargetArray(trace, opts); + if(!targetArray) return; + + var target = opts.target; + + var len = targetArray.length; + if(trace._length) len = Math.min(len, trace._length); + + var arrayAttrs = trace._arrayAttrs; + var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray); + var indices = getIndices(opts, targetArray, d2c, len); + var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts); + var indexToPoints = {}; + var i, j; + + for(i = 0; i < arrayAttrs.length; i++) { + var np = Lib.nestedProperty(trace, arrayAttrs[i]); + var arrayOld = np.get(); + var arrayNew = new Array(len); + + for(j = 0; j < len; j++) { + arrayNew[j] = arrayOld[indices[j]]; + } + + np.set(arrayNew); + } + + for(j = 0; j < len; j++) { + indexToPoints[j] = originalPointsAccessor(indices[j]); + } + + opts._indexToPoints = indexToPoints; + trace._length = len; +}; + +function getIndices(opts, targetArray, d2c, len) { + var sortedArray = new Array(len); + var indices = new Array(len); + var i; + + for(i = 0; i < len; i++) { + sortedArray[i] = {v: targetArray[i], i: i}; + } + + sortedArray.sort(getSortFunc(opts, d2c)); + + for(i = 0; i < len; i++) { + indices[i] = sortedArray[i].i; + } + + return indices; +} + +function getSortFunc(opts, d2c) { + switch(opts.order) { + case 'ascending': + return function(a, b) { + var ac = d2c(a.v); + var bc = d2c(b.v); + if(ac === BADNUM) { + return 1; + } + if(bc === BADNUM) { + return -1; + } + return ac - bc; + }; + case 'descending': + return function(a, b) { + var ac = d2c(a.v); + var bc = d2c(b.v); + if(ac === BADNUM) { + return 1; + } + if(bc === BADNUM) { + return -1; + } + return bc - ac; + }; + } +} + +},{"../constants/numerical":265,"../lib":285,"../plots/cartesian/axes":331,"./helpers":544}],546:[function(_dereq_,module,exports){ +'use strict'; + +// package version injected by `npm run preprocess` +exports.version = '2.2.0'; + +},{}]},{},[15])(15) +}); + +var locale={moduleType:"locale",name:"de",dictionary:{Autoscale:"Automatische Skalierung","Box Select":"Rechteckauswahl","Click to enter Colorscale title":"Klicken, um den Farbskalatitel einzugeben","Click to enter Component A title":"Klicken, um den Titel der Komponente A einzugeben","Click to enter Component B title":"Klicken, um den Titel der Komponente B einzugeben","Click to enter Component C title":"Klicken, um den Titel der Komponente C einzugeben","Click to enter Plot title":"Klicken, um den Titel des Graphen einzugeben","Click to enter X axis title":"Klicken, um den Titel der X-Achse einzugeben","Click to enter Y axis title":"Klicken, um den Titel der Y-Achse einzugeben","Compare data on hover":"\xdcber die Daten fahren, um sie zu vergleichen","Double-click on legend to isolate one trace":"Daten isolieren durch Doppelklick in der Legende","Double-click to zoom back out":"Herauszoomen durch Doppelklick","Download plot as a png":"Graphen als PNG herunterladen","Download plot":"Graphen herunterladen","Edit in Chart Studio":"Im Chart Studio bearbeiten","IE only supports svg. Changing format to svg.":"IE unterst\xfctzt nur SVG-Dateien. Format wird zu SVG gewechselt.","Lasso Select":"Lassoauswahl","Orbital rotation":"Orbitalrotation",Pan:"Verschieben","Produced with Plotly":"Erstellt mit Plotly",Reset:"Zur\xfccksetzen","Reset axes":"Achsen zur\xfccksetzen","Reset camera to default":"Kamera auf Standard zur\xfccksetzen","Reset camera to last save":"Kamera auf letzte Speicherung zur\xfccksetzen","Reset view":"Ansicht zur\xfccksetzen","Reset views":"Ansichten zur\xfccksetzen","Show closest data on hover":"Zeige n\xe4heste Daten beim \xdcberfahren","Snapshot succeeded":"Snapshot erfolgreich","Sorry, there was a problem downloading your snapshot!":"Es gab ein Problem beim Herunterladen des Snapshots","Taking snapshot - this may take a few seconds":"Erstelle einen Snapshot - dies kann einige Sekunden dauern",Zoom:"Zoom","Zoom in":"Hineinzoomen","Zoom out":"Herauszoomen","close:":"Schluss:",trace:"Datenspur","lat:":"Lat.:","lon:":"Lon.:","q1:":"q1:","q3:":"q3:","source:":"Quelle:","target:":"Ziel:","lower fence:":"Untere Schranke:","upper fence:":"Obere Schranke:","max:":"Max.:","mean \xb1 \u03c3:":"Mittelwert \xb1 \u03c3:","mean:":"Mittelwert:","median:":"Median:","min:":"Min.:","Turntable rotation":"Drehscheibenorbit","Toggle Spike Lines":"Bezugslinien an-/abschalten","open:":"Er\xf6ffnung:","high:":"H\xf6chstkurs:","low:":"Tiefstkurs:","Toggle show closest data on hover":"Anzeige der n\xe4hesten Daten an-/abschalten","incoming flow count:":"Anzahl eingehender Verbindungen:","outgoing flow count:":"Anzahl ausgehender Verbindungen:","kde:":"Dichte:"},format:{days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],shortDays:["So","Mo","Di","Mi","Do","Fr","Sa"],months:["Januar","Februar","M\xe4rz","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],shortMonths:["Jan","Feb","M\xe4r","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],date:"%d.%m.%Y",decimal:",",thousands:"."}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"es",dictionary:{Autoscale:"Autoescalar","Box Select":"Seleccionar Caja","Click to enter Colorscale title":"Introducir el t\xedtulo de la Escala de Color","Click to enter Component A title":"Introducir el t\xedtulo del Componente A","Click to enter Component B title":"Introducir el t\xedtulo del Componente B","Click to enter Component C title":"Introducir el t\xedtulo del Componente C","Click to enter Plot title":"Introducir el t\xedtulo de la Gr\xe1fica","Click to enter X axis title":"Introducir el t\xedtulo del eje X","Click to enter Y axis title":"Introducir el t\xedtulo del eje Y","Click to enter radial axis title":"Introducir el t\xedtulo del eje radial","Compare data on hover":"Comparar datos al pasar por encima","Double-click on legend to isolate one trace":"Haga doble-clic en la leyenda para aislar una traza","Double-click to zoom back out":"Haga doble-clic para restaurar la escala","Download plot as a png":"Descargar gr\xe1fica como png","Download plot":"Descargar gr\xe1fica","Edit in Chart Studio":"Editar en Chart Studio","IE only supports svg. Changing format to svg.":"IE solo soporta svg. Cambiando formato a svg.","Lasso Select":"Seleccionar con lazo","Orbital rotation":"Rotaci\xf3n esf\xe9rica",Pan:"Modo Panor\xe1mica","Produced with Plotly":"Hecho con Plotly",Reset:"Reiniciar","Reset axes":"Reiniciar ejes","Reset camera to default":"Restaurar c\xe1mara predeterminada","Reset camera to last save":"Restaurar anterior c\xe1mara","Reset view":"Restaurar vista","Reset views":"Restaurar vistas","Show closest data on hover":"Mostrar el dato m\xe1s cercano al pasar por encima","Snapshot succeeded":"La captura de la instant\xe1nea finaliz\xf3 correctamente","Sorry, there was a problem downloading your snapshot!":"\xa1La descarga de la instant\xe1nea fall\xf3!","Taking snapshot - this may take a few seconds":"Capturando una instant\xe1nea - podr\xeda tardar unos segundos","Toggle Spike Lines":"Mostrar/Ocultar Gu\xedas","Toggle show closest data on hover":"Activar/Desactivar mostrar el dato m\xe1s cercano al pasar por encima","Turntable rotation":"Rotaci\xf3n plana",Zoom:"Modo Ampliar/Reducir","Zoom in":"Ampliar","Zoom out":"Reducir","close:":"cierre:","high:":"alza:","incoming flow count:":"flujo de entrada:","kde:":"edp:","lat:":"lat:","lon:":"lon:","low:":"baja:","lower fence:":"l\xedmite inferior:","max:":"m\xe1x:","mean \xb1 \u03c3:":"media \xb1 \u03c3:","mean:":"media:","median:":"mediana:","min:":"m\xedn:","new text":"nuevo texto","open:":"apertura:","outgoing flow count:":"flujo de salida:","q1:":"q1:","q3:":"q3:","source:":"fuente:","target:":"destino:",trace:"traza","upper fence:":"l\xedmite superior:"},format:{days:["Domingo","Lunes","Martes","Mi\xe9rcoles","Jueves","Viernes","S\xe1bado"],shortDays:["Dom","Lun","Mar","Mi\xe9","Jue","Vie","S\xe1b"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],shortMonths:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],date:"%d/%m/%Y",decimal:",",thousands:" "}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"fi",dictionary:{Autoscale:"Autoskaalaa","Box Select":"Laatikkovalinta","Click to enter Colorscale title":"Klikkaa antaaksesi v\xe4riskaalan otsikko","Click to enter Component A title":"Klikkaa antaaksesi komponentin A otsikko","Click to enter Component B title":"Klikkaa antaaksesi komponentin B otsikko","Click to enter Component C title":"Klikkaa antaaksesi komponentin C otsikko","Click to enter Plot title":"Klikkaa antaaksesi kuvion otsikko","Click to enter X axis title":"Klikkaa antaaksesi x-akselin otsikko","Click to enter Y axis title":"Klikkaa antaaksesi y-akselin otsikko","Click to enter radial axis title":"Klikkaa antaaksesi radiaalisen akselin otsikko","Compare data on hover":"Vertaa dataa kursorilla","Double-click on legend to isolate one trace":"Kaksoisklikkaa selitett\xe4 erist\xe4\xe4ksesi yksi sarja","Double-click to zoom back out":"Kaksoisklikkaa zoomataksesi ulos","Download plot":"Lataa kuvio","Download plot as png":"Lataa kuvio png-muodossa","Edit in Chart Studio":"Muokkaa Chart Studiossa","IE only supports svg. Changing format to svg.":"Formaatiksi vaihdetaan IE:n tukema svg.","Lasso Select":"Lassovalinta","Orbital rotation":"Orbitaalikierto",Pan:"Panorointi","Produced with Plotly":"Tuotettu Plotlyll\xe4",Reset:"Palauta oletusasetukset","Reset axes":"Palauta akselien oletusasetukset","Reset camera to default":"Palauta kameran oletusasetukset","Reset camera to last save":"Palauta kameran viimeksi tallennetut asetukset","Reset view":"Palauta n\xe4kym\xe4n oletusasetukset","Reset views":"Palauta n\xe4kymien oletusasetukset","Show closest data on hover":"N\xe4yt\xe4 kursoria l\xe4hin data","Snapshot succeeded":"Tilannekuvan ottaminen onnistui","Sorry, there was a problem downloading your snapshot!":"Pahoittelut, tilannekuvan lataaminen ep\xe4onnistui!","Taking snapshot - this may take a few seconds":"Otetaan tilannekuvaa - odota hetki","Toggle Spike Lines":"N\xe4yt\xe4 huiput","Toggle show closest data on hover":"N\xe4yt\xe4 kursoria l\xe4hin data","Turntable rotation":"Tasokierto",Zoom:"Zoomaus","Zoom in":"Zoomaa sis\xe4\xe4n","Zoom out":"Zoomaa ulos","close:":"loppu:","high:":"korkein:","incoming flow count:":"saapuva virtaus:","kde:":"ydinestimointi:","lat:":"lat.:","lon:":"lon.:","low:":"matalin:","lower fence:":"alempi raja:","max:":"maks.:","mean \xb1 \u03c3:":"keskiarvo \xb1 \u03c3:","mean:":"keskiarvo:","median:":"mediaani:","min:":"min.:","new text":"uusi teksti","open:":"alku:","outgoing flow count:":"l\xe4htev\xe4 virtaus:","q1:":"q1:","q3:":"q3:","source:":"l\xe4hde:","target:":"kohde:",trace:"sarja","upper fence:":"ylempi raja:"},format:{days:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],shortDays:["su","ma","ti","ke","to","pe","la"],months:["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kes\xe4kuu","hein\xe4kuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],shortMonths:["tammi","helmi","maalis","huhti","touko","kes\xe4","hein\xe4","elo","syys","loka","marras","joulu"],date:"%d.%m.%Y"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"fr",dictionary:{Autoscale:"\xc9chelle automatique","Box Select":"S\xe9lection rectangulaire","Click to enter Colorscale title":"Ajouter un titre \xe0 l'\xe9chelle de couleurs","Click to enter Component A title":"Ajouter un titre \xe0 la composante A","Click to enter Component B title":"Ajouter un titre \xe0 la composante B","Click to enter Component C title":"Ajouter un titre \xe0 la composante C","Click to enter Plot title":"Ajouter un titre au graphique","Click to enter X axis title":"Ajouter un titre \xe0 l'axe des x","Click to enter Y axis title":"Ajouter un titre \xe0 l'axe des y","Click to enter radial axis title":"Ajouter un titre \xe0 l'axe radial","Compare data on hover":"Comparaison entre donn\xe9es en survol","Double-click on legend to isolate one trace":"Double-cliquer sur la l\xe9gende pour isoler une s\xe9rie","Double-click to zoom back out":"Double-cliquer pour d\xe9zoomer","Download plot as a png":"T\xe9l\xe9charger le graphique en fichier PNG","Download plot":"T\xe9l\xe9charger le graphique","Edit in Chart Studio":"\xc9diter le graphique sur Chart Studio","IE only supports svg. Changing format to svg.":"IE ne permet que les conversions en SVG. Conversion en SVG en cours.","Lasso Select":"S\xe9lection lasso","Orbital rotation":"Rotation orbitale",Pan:"Translation","Produced with Plotly":"G\xe9n\xe9r\xe9 avec Plotly",Reset:"R\xe9initialiser","Reset axes":"R\xe9initialiser les axes","Reset camera to default":"R\xe9gler la cam\xe9ra \xe0 sa valeur d\xe9faut","Reset camera to last save":"R\xe9gler la cam\xe9ra \xe0 sa valeur sauvegard\xe9e","Reset view":"R\xe9initialiser","Reset views":"R\xe9initialiser","Show closest data on hover":"Donn\xe9es les plus proches en survol","Snapshot succeeded":"Conversion r\xe9ussie","Sorry, there was a problem downloading your snapshot!":"D\xe9sol\xe9, un probl\xe8me est survenu lors du t\xe9l\xe9chargement de votre graphique","Taking snapshot - this may take a few seconds":"Conversion en cours, ceci peut prendre quelques secondes",Zoom:"Zoom","Zoom in":"Zoom int\xe9rieur","Zoom out":"Zoom ext\xe9rieur","close:":"fermeture :",trace:"s\xe9rie","lat:":"lat. :","lon:":"lon. :","q1:":"q1 :","q3:":"q3 :","source:":"source :","target:":"embouchure :","lower fence:":"cl\xf4ture sup\xe9rieure :","upper fence:":"cl\xf4ture inf\xe9rieure :","max:":"max. :","mean \xb1 \u03c3:":"moyenne \xb1 \u03c3 :","mean:":"moyenne :","median:":"m\xe9diane :","min:":"min. :","new text":"nouveau texte","Turntable rotation":"Rotation planaire","Toggle Spike Lines":"Activer/d\xe9sactiver les pics","open:":"ouverture :","high:":"haut :","low:":"bas :","Toggle show closest data on hover":"Activer/d\xe9sactiver le survol","incoming flow count:":"flux entrant :","outgoing flow count:":"flux sortant :","kde:":"est. par noyau :"},format:{days:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],shortDays:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],months:["Janvier","F\xe9vrier","Mars","Avril","Mai","Juin","Juillet","Ao\xfbt","Septembre","Octobre","Novembre","D\xe9cembre"],shortMonths:["Jan","F\xe9v","Mar","Avr","Mai","Jun","Jul","Ao\xfb","Sep","Oct","Nov","D\xe9c"],date:"%d/%m/%Y",decimal:",",thousands:" ",year:"%Y",month:"%b %Y",dayMonth:"%-d %b",dayMonthYear:"%-d %b %Y"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"it",dictionary:{Autoscale:"Scala automaticamente","Box Select":"Selezione box","Click to enter Colorscale title":"Clicca per inserire un titolo alla scala di colori","Click to enter Component A title":"Clicca per inserire un titolo al componente A","Click to enter Component B title":"Clicca per inserire un titolo al componente B","Click to enter Component C title":"Clicca per inserire un titolo al componente C","Click to enter Plot title":"Clicca per inserire un titolo al grafico","Click to enter X axis title":"Clicca per inserire un titolo all'asse X","Click to enter Y axis title":"Clicca per inserire un titolo all'asse Y","Click to enter radial axis title":"Clicca per inserire un titolo per l' asse radiale","Compare data on hover":"Paragona i dati al passaggio del mouse","Double-click on legend to isolate one trace":"Doppio click per isolare i dati di una traccia","Double-click to zoom back out":"Doppio click per tornare allo zoom iniziale","Download plot as a png":"Scarica il grafico come immagine png","Download plot":"Scarica il grafico","Edit in Chart Studio":"Modifica in Chart Studio","IE only supports svg. Changing format to svg.":"IE supporta solo svg. Modifica formato in svg.","Lasso Select":"Selezione lazo","Orbital rotation":"Rotazione orbitale",Pan:"Sposta","Produced with Plotly":"Creato con Plotly",Reset:"Reset","Reset axes":"Resetta gli assi","Reset camera to default":"Reimposta la camera ai valori predefiniti","Reset camera to last save":"Reimposta la camera all' ultimo salvataggio","Reset view":"Reimposta la vista","Reset views":"Reimposta le viste","Show closest data on hover":"Mostra i dati pi\xf9 vicini al passaggio del mouse","Snapshot succeeded":"Screenshot creato con successo","Sorry, there was a problem downloading your snapshot!":"Si \xe8 verificato un errore durante la creazione dello screenshot","Taking snapshot - this may take a few seconds":"Creazione screenshot - potrebbe richiedere qualche secondo",Zoom:"Zoom","Zoom in":"Ingrandisci","Zoom out":"Rimpicciolisci","close:":"chiudi:",trace:"traccia","lat:":"lat.:","lon:":"lon.:","q1:":"q1:","q3:":"q3:","source:":"sorgente:","target:":"target:","max:":"max.:","mean \xb1 \u03c3:":"media \xb1 \u03c3:","mean:":"media:","median:":"mediana:","min:":"min.:","new text:":"Nuovo testo:","upper fence:":"limite superiore:","lower fence:":"limite inferiore:","Turntable rotation":"Rotazione piattaforma","Toggle Spike Lines":"Abilita linee di identificazione","open:":"apri:","high:":"alto:","kde:":"kde:","low:":"basso:","incoming flow count:":"Flusso in entrata:","outgoing flow count:":"Flusso in uscita:","Toggle show closest data on hover":"Abilita mostra i dati pi\xf9 vicini al passaggio del mouse"},format:{days:["Domenica","Luned\xec","Marted\xec","Mercoled\xec","Gioved\xec","Venerd\xec","Sabato"],shortDays:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],shortMonths:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],date:"%d/%m/%Y",decimal:",",thousands:"."}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"nl",dictionary:{},format:{days:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],shortDays:["zon","maa","din","woe","don","vri","zat"],months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],shortMonths:["jan","feb","maa","apr","mei","jun","jul","aug","sep","okt","nov","dec"],date:"%d-%m-%Y"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"sv",dictionary:{Autoscale:"Autoskala","Box Select":"V\xe4lj rektangel","Click to enter Colorscale title":"Klicka f\xf6r att ange titel p\xe5 f\xe4rgskala","Click to enter Component A title":"Klicka f\xf6r att ange titel p\xe5 komponent A","Click to enter Component B title":"Klicka f\xf6r att ange titel p\xe5 komponent B","Click to enter Component C title":"Klicka f\xf6r att ange titel p\xe5 komponent C","Click to enter Plot title":"Klicka f\xf6r att ange titel p\xe5 diagram","Click to enter X axis title":"Klicka f\xf6r att ange titel p\xe5 x-axel","Click to enter Y axis title":"Klicka f\xf6r att ange titel p\xe5 y-axel","Click to enter radial axis title":"Klicka f\xf6r att ange titel p\xe5 radiell axel","Compare data on hover":"J\xe4mf\xf6r data n\xe4r muspekaren h\xe5lls \xf6ver","Double-click on legend to isolate one trace":"Dubbelklicka p\xe5 f\xf6rklaringen f\xf6r att visa endast en serie","Double-click to zoom back out":"Dubbelklicka f\xf6r att zooma ut igen","Download plot":"Ladda ner diagram","Download plot as a png":"Ladda ner diagram som png","Edit in Chart Studio":"Editera i Chart Studio","IE only supports svg. Changing format to svg.":"IE st\xf6der enbart svg. Byter format till svg.","Lasso Select":"V\xe4lj lasso","Orbital rotation":"Orbital rotation",Pan:"Panorera","Produced with Plotly":"Skapad med Plotly",Reset:"\xc5terst\xe4ll","Reset axes":"\xc5terst\xe4ll axlar","Reset camera to default":"\xc5terst\xe4ll kamera till standard","Reset camera to last save":"\xc5terst\xe4ll kamera till senast sparad","Reset view":"\xc5terst\xe4ll vy","Reset views":"\xc5terst\xe4ll vyer","Show closest data on hover":"Visa n\xe4rmaste v\xe4rde n\xe4r muspekaren h\xe5lls \xf6ver","Snapshot succeeded":"Bild skapad","Sorry, there was a problem downloading your snapshot!":"Tyv\xe4rr gick n\xe5got fel vid nedladdning av bild","Taking snapshot - this may take a few seconds":"Skapar bild - detta kan ta n\xe5gra sekunder","Toggle Spike Lines":"Aktivera/Inaktivera topplinjer","Toggle show closest data on hover":"Aktivera/Inaktivera visa n\xe4rmaste v\xe4rde n\xe4r muspekaren h\xe5lls \xf6ver","Turntable rotation":"Platt rotation",Zoom:"Zooma","Zoom in":"Zooma in","Zoom out":"Zooma ut","close:":"st\xe4ngning:","concentration:":"koncentration:","high:":"h\xf6g:","incoming flow count:":"inkommande fl\xf6de summering:","kde:":"kde:","lat:":"lat:","lon:":"lon:","low:":"l\xe5g:","lower fence:":"undre gr\xe4ns:","max:":"max:","mean \xb1 \u03c3:":"medel \xb1 \u03c3:","mean:":"medel:","median:":"median:","min:":"min:","new text":"ny text","open:":"\xf6ppning:","outgoing flow count:":"utg\xe5ende fl\xf6de summering:","q1:":"q1:","q3:":"q3:","source:":"k\xe4lla:","target:":"m\xe5l:",trace:"serie","upper fence:":"\xf6vre gr\xe4ns:"},format:{days:["S\xf6ndag","M\xe5ndag","Tisdag","Onsdag","Torsdag","Fredag","L\xf6rdag"],shortDays:["S\xf6n","M\xe5n","Tis","Ons","Tor","Fre","L\xf6r"],months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],shortMonths:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],date:"%Y-%m-%d"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); +var locale={moduleType:"locale",name:"tr",dictionary:{},format:{days:["Pazar","Pazartesi","Sal\u0131","\xc7ar\u015famba","Per\u015fembe","Cuma","Cumartesi"],shortDays:["Pz","Pt","Sa","\xc7a","Pe","Cu","Ct"],months:["Ocak","\u015eubat","Mart","Nisan","May\u0131s","Haziran","Temmuz","A\u011fustos","Eyl\xfcl","Ekim","Kas\u0131m","Aral\u0131k"],shortMonths:["Oca","\u015eub","Mar","Nis","May","Haz","Tem","A\u011fu","Eyl","Eki","Kas","Ara"],date:"%d.%m.%Y"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale); \ No newline at end of file diff --git a/static/babybuddy/js/graph.277fd3f06aa0.js.gz b/static/babybuddy/js/graph.277fd3f06aa0.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..5b8a288f1638ee3cd464c988a0f18f536e8051f4 GIT binary patch literal 826662 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≷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
`2^UyDfP8( zOnR*nx|8dp;}N^%e5(;)``QHf!-|JcIAR5kp^*H;Cpv`w;GaVOr1R%0#Q8(+Kq+-s zxl@;w9rA}SErRJI7a)82am!l=-b(P+g0~vH^&nP6&y^uEPk8WdQCy*+o_XRz&zBH< zO^vUp@%1#mP~eFJYJKqX=v;ew{CM-_@#8029zXsIEsr1nLd&C9x;zBcuCP8@-aTXK z`pCRX#y@R&Y+rkM{B-l>@zW<-9zXpHEsvl6y2~TG%I+Z+PiXsSUS<36mq+W9EsvdR zE{}OOn-8t(>_3wc&=IZ;&eZrvOE9}ey0AF1a`MczFc?JLBs!g@vsr@PO2;8O9VADy z=q3Dvoxtrp^vkUUDH}b~e~bUFTBkdn&D5%YLVs4RH5*T&x9WeZmLI_*p0DAbRcjon zvd_!PFJGKkmX;;pvPSr+@sMi~y5t7Pz`Om_@yk76-F$^mTE_;&;+=JM0l-9^n-m2` zN4fwGy^cM2rZbdvCu+m9=jq^<4QZU#XWc8B)$qVKY2Y4wxX5wjn|kD4AGGc(Fo~Op zVkj^=%9;JPu5OEijoyjm=$wXkM6IyC>3a4#de^dC;!QGR6CBt@fIg)uR*^|)sU_bs zqBdKn%E?*j{o52PH`Uj(#sIw1b;G`W&(_$YBzO-O)yOQ@&4v`6JDx1uOqIZSdJdg@ zoycWSqca(2ZVx6^w>0{YOlNL8n8f+~Sjs8DX|2~6Q!62_ctN^kcRHzRGOH8FoYI{lE?5Yw7AuW9MwQawCP@g;Y^GEf$Yd?QdpNM1R zTj~ShR1>RyB9MmTt?%zPO#(1(mC%BH>3W7pgcvJ;yuk15&wY_gK>aU!ko#{F;eTNh zVKI42&4kfk6b6>MB}-u&i%B&L@lH%PGW13y90pML5Or<~e*0z${N#=RqbGL+7(Jg< zfT7dvWaga?_^B(D)g;SpG>huBsUE(ph9T8)j_%-4HxbM5yOXQep?YmyRud7o(JrdD z&Esnuq|z%wJyxoBH&<_m$0y1*bsL*zeEE92JU%Lm)Q7Ji^;oH1D_8F>k580s>ozvc z`11AkczjJe4>XqqCy#G8SMMH=uVwMl6m2w%>hY;u4+W(vu9YLinoJGH4 z$+2@Ki#`mNsuczg!iT|-&lD1f!pcg>tH-kfWo5ERA|-`jWLYI^L%2=#okHA1FaVrS zw0{5gl$m5a03CnB9cx|Q2PK0g(|lP7>6}be@pLp31>v>;lQL)|8ns)5 zBYaKkP6;8B-);NNu>x&n$L#$;wkc7<&E}HYP7=@W0^mW^p8>iM^=FK&1;9orfGnJ< zGsYF#vQi45idtwo6KzqofN~2!)dI>b09A`N8k3fwYT==3;h}1A4OA`is9NMvwFsih z8umB+hef`!_a^7C5Gp-XI;|ga=#zoDv4C^@O zkE2c#%5@f^&LUKYeQKPGI_IL!d8p2k=SZI3LrD!g*RzAw8WAfHyy7cjcr<)E#%+5e zyA|!gQ98|Lbji*ScAP?8QX!E0L|JOtCTuM9NsCa6y8p{Od*L~>r;s-u4aiAEZ$6!Z zSp|kdWs!+7tLKQ((`|x>pe~}P+rpoKoo*X{0wmoI{_GqcupPGH1Ks8wz^*!>w(i7q zoNeEM@v9%5J8C!{o51odUB0Euw{-cI$3?JwTbFO^@@-wd?J*fF-_hkex_n2M?=Zf) zO&rm-8W9h92Y%9!ci|@u`L6GM;tB)ax#EC#uQ=el*BJ1g9q>InVDN6CN9;JY*t_D8 z?_F`ot>zWS+-hB8(BP88PcF?JI`Ewp2VQun+_|3RIx5J+ zhegaf9)rKk$#e{UQ~Ax%r?GPxUD?iMbPYR~(Y5ScMpv|R8C}`VWprD1E;x(VvT_+r zwT%i+L7i#}BbM$=7hvqwp|vYoxzs^88cc85HUV4h&TiX6p&c|+$%&kVuuVpoKfX@T zJf=GfTCx@3rG zl!a=>2!HgL{-8N%1AGZXLrS=U762OxlK$*u>m-3W{EMt*fqYugfQ1^5s9+Tznacq@ ztzyA7BufW9kP^f_~Gz1bsHBrK`&0vCedr&0tZh5hc@0Db~h1=>m~13m+l z1)57LBmM)m<-C+sKLf)Ra2nr6R#?%nY?_8e;e}V2(6y;YU-hq8sd1kvHgasSkzrRGpowe{0Btt((Du4l`VVcrB1|(YB$P zZ6j~U*~l~8Mwu;dwYZZVr?$g-*YvZ>o$Eb}A%h;vXVFqgKZ)6fC=1?mm|Ib!S)Z`f z|6j|;4!aT3syNTVPR^-LP5RTRNmn{GPnEsQn~p8d9(;sit=UA~A1eJ&+vlkHP``(Y zJ=Ewqsyx);q4ExucGA{F<#nOWZnN!Jmw+A(k{v@BXsHV=b=&RGU0B}rk89C!Y26?M zH2Y_cx_;qa5V`k5xi>{rD)*jvwirQMjKJY>F~+H(CnrFPFOHz{dUyKM@105FejcT52RL6{xw-hdtbcx`s{ zDuh*r+;mB^wVFQ9o>qw^~nwfd(24oyw!h?)9?9Q!4LLQ81Us^j3sU6 zB@~AxZRRBuhb3+11r&!RZMyPFPD0b~ONt~k{WhaWLeuYWiX=4sW~EddVL!OZ$BL-qfE7&!QHauPoPu1WoCo`h1@IRQwMm#pbM?ag(Mpy_oJE#eehMb zAB?{CgHhIgu(th9yeH6)1!6wA$tLcTn`~k~xydH}6DZ6AF`(RJ69>voHnE`GWD^ex zv}b{sP;Rnd6<&sA6<)@th!3@MJy+n_Y$|qKrjb|&gPtanX_CRMtbTq|@?Lmo9}mHH zsH;Y?ZP2AeiW7ZV+?l0fcgIu}Y)B!-#s&aU*#L$F6u22})FS$7IbUt(t7E?mOP2p3 z#fkDe&eyKUe9dYyivE5qaXSw z!Rs&M^7S^kp@AaA5w#OtQEXLl%zDJVGB5lXmrjnz^uUNrL0N=2FHScxO8iNDjfkL7 zSg|#CY|VS3W|1`0wl#NcO=gao5X16L$5y>7sdgj!BoC=#9-;dEintv*?myu{sYU7tCEzYdQ% zLXr&OS-zKkeBT=ePS5=Y9mP)JXq}is=pS=F&62?f1-i=Y#cCIRpH9HZVREWAq~c?-|A(B*q zjd_-!*xj3L{C(~3S-nfC8&xWH5@%nHKO|GA2FrQ2m?VmqQ9ljE zYPD6FlI-tmmw#U)0(ILk zAY+NY=b5nucI~p-03+_o>?F;q@}uUA0JT!<^HD!JN=He5J$j@w_9HH+xZqKs7$%!S zr|GCaJ`EJ(jQ!}3%PKzRv?yNR--XAVyDQlOUvmoo?gnV>%_=lAsA-%}p!KAWrh5wC zQ2cMR=>kh&k?{oaO6Ic-8rC;cc&|BIRQ|YBhmA>`W#CAyMwR|{oXqwkGn^`?{!R8Z zo6ZwVy@Y=+5z4>)jS$s{`~7F?3^99|s!e8;Oe-6+B%7(`!8EFd098ftX);t3zDkWt zwMQxPsw50kVDx`Os`?wRL{p%M+AR9N_`+cK?`s9Kypq)+|5C$>5iYZcyK_>Aj*pJA zMD5>c_VQUR8oo?MYMf6NYQ#3EU7|Ad&zERFR-qzFPS~qPb*!fTv~;0YF`RNLt?2%J ztWlMjPtq6$i-jIOH0h%!DI%eMmlION$jfJn=5=?fm8zorCYR{j67`l!*G)k)G`@E< zJSDWHhZry~+I$f8;TM?TX3-Z>g`lj|vHvPUTE&QW4<9}-$tr>1Q`KQW>KjbfD+iqX zFjA5+G+6;t*7nYB72ey57@LBqCPLnDGToYJ}zLm3KVK0TS9j|SuMl+0!a<73?f zzg~Lzb-QW~83e(dsA8_jG&++inA!?AFiE|>rmxfPs5udG7%hxVo=&xVMK$U{Ej~4; zTkP`Q6QgAjmy*pQta`m^xO>oBTL8JaX@K0aK!%}|tC&#U)Xi87d#gV@NaR2kC}L*b zSnSHyYkSkI?Ho#mh6cT*jh(O1dea~bN6$LXKNwDl+R}ze%p&#Ppl^{pMPLmllW71#EP8qE>s)MuOU1 z$pBG^=Pa<1E7-hfPSIY|;e;%vV-uo6Ntp4dA5Z&AChsSW-?C_9K1&B15p@RpUM?bn z_3n9+DCV41Kowg9@hO=;RX;U(0eHDYMd0eIf$6`%9tYM zCXTf|Z67{Xr;I;@rT}SjA6u6Sgk&@yCeyeJPNz{Ol$o&eP!6Gsv+hN{EKM-i5=M0v3pI;-8?bp?7P=Fg9T0mug2YUkoZ{*ol#as zovz~fVCEE|bKVfYzI~>$wkm3UPRD8gqTBtszj-lClH+l;u}MWP;t%m?c09i5#{Kx` zt&8p;?!E6O)9C_!o+s0b-gr2P2N%g8RwucOqhvHndKV|@WHz2$qzSi@PSnXxr;7!Kr&+KB7O)fHcA)3A0-aTwy zWQ$=pjYk)=X)?Z;PkwGxcx_w^($V|s#fNb}K7uw+Xd8>ctN>4nB#I*I$L*Lcw#kKbQp>EJz~+wv;C3MzOghjTbPL!)ojHvDn< zcNSsiF}NjBsf~GT%Bn;qyz)jjia(^sG4Kaay=)lOi`wO|vdM3#wL5)$tadxy5VbRp z8z<_pt=Oz+)h51!H9xJ#P;ujsc2c^(-eCA6>Zqs+6uH!0n&t^w(-5DBj1q=V4;Yt9 zV}J-&3DBBy8=&S;qk!Xr<`5>0T7fjt3E-J~el$xL=no*9dcOQBFG6bgkxfe8B_Zac}@WUVn`ee>1lD-h$^j@GQ_v59 z(YrOQNR=ICkllsmQJ-rC=ab2(wYC-wYnSntc!Y-6S~Bjf;n&)aw$5qiJRFA|g9$(K zJ_anKE?wSl*>+GpDVv=sD8eypuQr}MS>J%4j~?+@J#=aWPH1-gs3AhT(9ABNSB@)N zu3&stew4!8qHXDeRJvaw{Vf4Wh`YZSlp9m$M)$gNj`SE(aC`UxI<5GUz^K9|-sHUcAMPxCi+>jkd|wT^DJ@_esbs7J09rt$zdP3D z5UM^y%Rz|ZV!eXHE*_%mP5sqa_c9wB^@^1m=0C}M@{}{p_eSH%rulvzY&M=XO@o`w z`jeSpHk&>M@V%b-e&&B~KAi(>bJN4WnMv4q(tP@?;?XY?td`aUMZO#UdGjCIXh*87 zC-@c)D?Lty{hnxnH<;Lw69A1M)O@AqduX^4JKprFV_25Kojo8T! z#aD&~`Y{0d2|*j9bmv-U{at5`aRq!404_V|bq<1JmkM#u%8YtNzfIl2z`0W0n!J0F& zCORo1plUg^Khjxr275aZL7~sM+iLwWqYY!g;??Q@i^8r?u7!Z6v>|e7? ztJgMb4NzGt!T-93Md2_Wj8gHNetU2fyoo!}5TDJGB@#LjpIz4m@np5nS-4OxKU`Z@ zNPT+yx;zXAQ58eUL|2n4>6=6&KxYYd^W?Q1QjDAdE<{j+cJ!m9e|;AB`=m*9#z~q2 z+r^;P)1CF@B>{Mh(1~NbZ(vK;Qn|9VyrlF=CL#zF@9Ytzj6ZSF^FB9~y=E}>8R z3Ume-;K`;T!wR2xVR|hb;PnOha)kPB`|a!RV2BK>WZS7=TQ`;Rij3Pz?p$`}u`I5V zww>2|`(>j1!(Y;Kj01lW_Ay#loVHLZq7Tf{ic|1bwwC$uvptRblY=21W>#T#c7O@d z2onK$V1!P8oknQmSz0ae zrF57JVsJcWeD2s6=>#O%2?wcLs$-Dlnj=D^8rTY^ePunb;)HL-G zEU7+(r{H67X6ccuPbA<8V;OiPOJsN2`9fh1hdqT@oF1J=L!5r)#IfWl>|TbTxt)sH zR>WIEL?Zt_ly2y1fe$kPUS*XQz(3M0Y7cE5HT)s?8!nZ` zrHr>Ay^>Th+{>)Bor5hCEbhV*beKasnHRW^c}~*<_ABvFHa})pg9vfW1K5XxEbeme zXbGZUWu9*nbIj7fBTfhJI6kSR(@rOX)xMQUKMUjjbS%=(%OaAPuIG^B(1KPdy+7J( z*+_jM%z1Pd1#H~%l0Xh&PZiL8^fRmywHk@g&q12-HkGXmwDUxF&o&oNM!;b4Kcg-r zOkii3qp76~vTC){t`e?}fySu#pPIom%M-Cqr%g1Y0W-6VdpeGgX9#3%koI_{(Y#V} zLxI3$#Lm+(?JP{)Swe?Pb=o|mdFEIB23bOKLsT4EV*F$#*(N=#kAO?}#$(tm8|7w| z@>$HRR4O@?umQvPY8pbD725>mGqOlh(;`s+BC6{4E3hI zcc)c=BI&ZCLlz_CV-gy`RAC<-oUUQm;$9Cn0^L_4Oo;wA)hR@%ETWY3vh5iT1Dy^p z$Kgl=0;NPcp%z3;wX2`Ujs+ut{V^lp1h@5W6T*@gMs#+H?T<(c#1-;w_$7KBo&Cs( zWxZn9X8m!6)~b@~Nc)}8R+MS_T?3H)>#}OI3Y$^d$)%_(7DB-R6dS#oVY|fMtQk-!i~S9 zF?C$dsn|f(EfGs*vY8GnyIBLeA7WEPmm&_M?$B}%!===BO41xqsNo32EGVP%Wx$!y zuFq{iDIaK9JF!X3K2{I+5RtJiM^{m2DozJW+FW5WXj^PGIJ==4CzpX*LSfg1w@z#^ z%f|!}kvTnm_u>8NDQy<(^T7x;6cqG>8KT!qCkoQ@u*>m)QArZd3LwZz8=t3H;nH~L zI_}|12%sdHNxedrnXr$F(u!%t5X<1lZ~WNh!e1uXQJwPf*YOIn=E{lQ%M!DSHdz(z zQMyaM+VmVfJ6ls3=fN%-XjWrZnyKA}3tU@QzH@a=dQ0ukiRtY|`7_~Lt5rUO6d+;5 zYGu$)U`Q^3t}aG&5cWbG1_2OC00=Zw8V1(BASfON;TJPf#Gn8~idF?m`x<)KPABnX zI^jLLgZdeX{5fdHWVEc2EkMl`92n+(*Q%2ET)D2AZMjv|~*%dgMc-%;9kWuL3fu8+hMGLvZOrj)?ddYq9hyq5F!pDQhU zLVh%D_v6k-rMB8_0c%)Bn^B_vW(Ev@`Ohmv8icpCo2!Rl#f-{2jxknZCq&J zQ#`v;{ov-r+Cyz)d+~5hW^QFm+qbsanv_>WYl}N;SKVeOI`*+4Vfrw4R6d ze-KW?E^dlJG)0{+Dd!A{zU?TTz-j7-CbCD zUuLWL+&xy-O5?ULf+RLUyCS^>M*=*0Bu%}bsN!mYvokXFHrQ2Vg1U8@hMhuj3(0>S zKK%=_+5Opt+scaLnWHX*`CyblO8qyyms>S=XIJPfC0(i1Jf8!)&?#B1Mq?9w85Rl_ zvG#D8*=-ds$@YfJ=!0mC#&5!MO6k= zg4O8|w@j-rHBvm@#wpT^ChsqYMlUc}1xp(0JL=DxbkgpW_KR+qmLZ!R*aJ~LyL-nvp&g5I3J<<6tVsssM%tz&q zOQ#q;tJq%^`xzVUgbXe?Ld{vT%=Ng5kqt)5aVf(b-1P9oicdaQCFr$Zl6Y7yi9d9y z-e|5rezNg&^VuIvx^+uBc^@@Oa<7fO!& zL)emE0}SjI|D2!!&rpBm0CcDff*^*BCpC$-QUmux$wlJZAVY5|%g6#vCIO)eAwrr@POP!1v+ zk&0Ov&8#wh0e6+3uOn;CZ#=>`6g6sDwj%6p@LSjlp2Du96>L<4^H$KP2K`ommv!mi zUt7UrAO!t8p_B_M^UqdLzp*H!QI8rPg*ab^s^O1OZ|`cP8a74oX4Y|-PA>lW>)^cK zIjNM7|Diq~R@UO4g-;zu{XD(sVI$`OYzj;q%9?%^amdO)R)TrmNd~xqNP|oA(vL#A zPC#koP$8v71Yjj{?EHKzg{nx?2K@^?uhnWNpED7@OxR0AN`>kQej~urZ)a4*ew4AS zo#WIIJbqbT!gSc0jQY4q5;|3#NB?Ox&}gLO2SSP^Cc$O;A|H9ThfZJ|OmNST-a(L904QUTT9D z66j353N|tFMmhtItjz{#@f9kIJuF(edh|lUF}?A5~t_T=Kb4bAsVThL@6! z0+6DJO=V7W2$({z%rDY^IFdTvRs&9UUg7Ht?xb+$U^sAo(Ysl1Yy^i81G(j=`?3jJWX>a{QG7x`oRGoUTVVJgI5C&@u zc@2HI+ey;@C3VtjXBzjrZ<3yl!Bku8$L%$=dURL&Ne}lJSaffc#(lbGktrE*hADS2 z#49{>nw&2iyG&F5kOcvqM-^2OLBU=~xoU?|KN8!@jC!e*FcrU#lK(n<|1Mvl7bU-> z+>d4BWG%E2U5KKkSTYDf%BK7WpzjXQcWc@F;xkQRM__t*kpwG5yj!lo_Oq6o6GwSFD*Iig=m9vqf!Z&~TGWZOS@j zakT^Wb@*C&_?0+=0&_dguf>6&6MY@hT?^;yv3_tDv_D1{-wJa8br<9x+?VD*Vepo( zLHP>wuCJpq+Fs@%it?-HO+1554o#7yt<0irD=5K{7h4%jZJoS4pasFK)1XFDK3eSr zReU8}vK7{{eru$q5NsJfaLuw=nyY~+zU5KjD9Ho1t4{_h?u_$rZO5sa>d<-ANB>#_ zHl?)_wPm!Ij>K{bot$RGAi`-dxTZ#f>qK6D)oLL%B~wiw;{u`eRnV;zG%a+CM9fz; z;bwon4PPF7qh4v3L?)Ou4b)dQLj0( zRMV6~L7+>CV89v7T1ivETW7~hLWYD8=9z;r13@MZLRjZh2X!nc2UTP+=hGzIFG~1R zr6xPwId2V8d3ctU*`BPlo<6RwcsmhDdSa4fmP68pNm6?dNPcRPGy4q4dX~*Hv{-3v z)^Yc@P*hZy`FL`+>+SxkciR2|2zBQdKku>UgKAil92WVAuFn_#fQUWgj5r!6=u}A| z)WvMv?QdG{2-lnJy6n z>rAeLPY0zGPqDfY9w0&S3ZmvFh1=H%{|~N(CGnP;tUL!@@&6^Jo{s+(QMgLk;4EuD zxAeTsq1Di|KofUxO?w(PSo~(JHhk<_HRg;Ko3363Bjl< zP~tCuKNg!!^829v6D`gYv31ddQDT5M6cpQ+!;*SMJTB36!h+=EVWRN1144 z<6}2k*)R>%{94`o*VsZ2uQVJ+fkd3ei(um@xIDkcD+*@AAo}*K0}0*k1@BH(tESxx zy<9?)WjTs%w~gAv`|PsptMa|P+eCh<_GU8)y1WG@ta1}R zXS=7P>(f5<7=x9q=7`xn9bcXHHAPLKx-K-5LZev)#?p)#0o{}>m7vaNfIYNrKeA{( zG?2`7L+J5msP-9W7sjo_Tl}9#pRu2x5&mcFx%|R3YW_2ZbRe(g0w{gF6-m)JH9W{5 z->-b6YSp5SW1M>KtPDK6z@cq=(@k`bkd})U^DS&%RJG zYB1N0n((*672itoJ<3|7*~2cs|_{rR~t0z z8#q4c0KJ{Z_~rzrHNCVtrbWL_!@e1&ViYqQvLal~dn8P#=S(Q~oQt=tLt~5N zIxAAHKoQ4()@`r52`sdbwfYjW@Sk;xx66+Xhs2i%>6H|RsSNx7I7HQ9B4KiVLD5RK z9J1Oau+1UCNlz>GlTlPQ@GG>mnKW)rNes$JDYh$-Rptu(bDfvpxKHkcQ+^ln8gH9J zH2`Ww-&KQF9mC0!rYAnHO>V>FY7u?aq}-;dyYAODxeb%+uWXj@5r>CVewAS2??#sY zu{&we@oh*i-9kU%Z@ZD>Q@cQs3`HPBxehQu2lT8V#<{HZYE^VULXIU*eYzUmALBO56n1i#r|0c|0UVJmTcQl(cO$PJM1|2z92$KoZ2z=^6G zf@aj)Zb_vX6|hXOUCmb8S)|&It##vdSz4x8!3+(QgMOb$1Uv`oI#8??tl|?mFd#4< z@Nd(HLFxqd^b9zZ;~oq^(}uq-zhD4xFmi7pqHS78%voe9Z;ROCjJms?0t1AlfUGdW}6`0v{3eRv!oehAVMD1SD>zzD_ z!@8cs7-oX<8j`1Gf1B#HrC!^s*QR=Hsn@Q1+E#dlM_JhHLn^pR&ERjgy5SOGWdjth z%+zaeQ&BXfonl8_s~4fjJ*yXDxQzi5+k_zD*CzbBSNIMTAmmhgEEl?RVYZwp$Z@~K z?NjEjL$#;B?3hs1evE}TB0L~`A;QJd#4z0&zu4mAvah{tGoeLm zy4C_)iX-Q9S11C$DScJ6=fICP#+$~o9)1a`i_u=pns0Azlm+-IH2IRo9k0)pg=8v~ zLsu>>m4)`J(n;FUUz^Ws6eg~=Nqb}{vI^+FbnLZciT!|oE!(K3L_V&ygo5a@J+t z)Q(Srn~Gb`ghUSa4CW4XSESq*%vo6<&?UMs?#tsHd=Cz_R@#2SCKI3Sz+(j~QUDvY z@ujuY-hoJ!gp&{i_v;`0Ix%PLH_OgIBKo81DM%s=trK@CN*WePrW2aiCIT0|KcG6! zBY_5qEe&}brB;r)(nfQ#Es&}Z!yun0{Rkb26O3m?p{KCwfM4vxTS+oF{CtAEgOd~O z4HoSlcd%&Z5K)BuOrNfD&ff_tR0}$bH{mMYdPz^3dl&g=X4K^R(2JlX^QdGQ9NlO7 zk(p}Lb>6cJ|HVA%FQd|!u4_ii1>~!Z?GmE#5O0IIa3fYr$YZCaheL6IsUwFZ z4-k1C(n~?)3nW=ENqP}(TzyWt*G!zTVi$C7!7(QP#u>vqhjeD3=MWy>=5S$*GZ&#u z=~l4IH}%cf=7n!o^p^Zv4@PR|mIspCKDgy2<#Jjmw_*7f$5XiwOvWWTAwlvP*eDG8 zurA6C2~E~f#-Y{sBb3R3q5#DJu-upoccLten_B+!;A{P|Jcf<@*x3Mi3iPJoI0SJK z;lrI-y9ex_oZTtcfePPa4g=!5D=X+lCUe}uAnNv_V09H){4(OX7(#`x7qL1^(~;Cz zr21$Knnedqa=gP~AjTkvphWtyj75B_<|!u|wt?1eD(#;k)p9y_nRjVG8o-1%YGd5* zk|p?IP_I3y>H-Z{pkWGN{s|FQU!u1+LCol!NAWluch0Yu#Xf@D{?nrW0-H|nr6dVH z*ZmTEvy+~?}QyC;9P-Ot}CeV={>I@$fS)eBnuUD7kvAHfLjxS!t}5Ljr(wz@07 z`0MvP2EVI%x#s57!!}>Ly1xgnZTUU>>-XUMTwPTMTKPTutEfF}YuDBj!g#+FjLd(g zy~2UjFf#vPn|t1i4IuB{Ve|;S4b-;*~20KCy8%Z%pJ+W~CPv zG)6S3`0qr7-R>^JC}UZF4+`DlImap_NFNp;!rvI|z%)N|ZwNqZrqHY{B4*^E!Wi?V7i9eOFnxK{zim*unQngkKSTm=9sxe9b zVT5;-r}8MBQQfWTiAXRIdM(Rjax$#jC5u<8g_@j)7xgNMQkn z1+9M(5ruu2)Fz_g32>~0%9too){?-87Y%}U-7z-?vKisG(sWTX1a1V}jqvqKKpP)M zfsTdFVe!Pb17QJ%uXSshPQzi0`#EH_ig^}i5)b)kbjh38smIo)$7Iw8I=Q8)YAD!+ zjVI{U(>Oi1Tk7aa_84u7HzOnM?>v^>#s!)su9hP&sa(y@gKRQvaJ>??qAaudV%ZuF zW;E%TuF`FPmR}+6391&b*-K5a+G?ktbiQ0-&^fPnQpjX_A{1Do6aC21ft_G7MSWRX zH4=eQv84jKoRmR9DjG9L@w=Rd=ShSi9!}Ak0Sbs=y-P;Onl}GvYa!mZw}HR^EJGo4 z{pi{RLm(hkDGfVmoJ{hteaIN*7=hpK5n??uMm5m%GUh##a=mpA%N)%KwB4uabO1AA zHw67Tjl*G}c1c0`@2F{y=p8+n^Sy*FcZ@35Ae%aVd0_|mxePFh6J3HXB3dqVq#CpT zA>U)q&J8?wsIqZ1G-qn|blPd*ln%?la)$o!lhx{KV174FPQ0BTrh7^X>=#iwroucK ziCfjp+rxj1|CHqL_!Yt&{@bky+JL z`d}=+PI(Xbx|6kBRrRE2Tg}#tMmRV#-y>7@iMuyq0OOALMn^P2;N&O4Yo6bZwr06| zTfUL2$}*s!E&C$gP0>9=X~Ug5JpBuL{Te4BzI%s`vB~($>UlCwBYSl4CLVr?x^H57 zJ#G_>dR+hbF#O*dgXbHuqT`@zyPiL=XmFX^syRD`-u5s5zHwj+3KqvM}UKua- ztu~00Qoet};OLYUuQI_u^f9>JkNf?WIg;m#MYyZ2A|LZWjB$~|{!j$IX-dvrOLFaq zVtVGw@@*t3%dz`uQgCZnNj>kF?LIQ2>ObA_`_qX4aB7@Ux8U!3osXvaCrhRnr;urs@ z5N>Q}=F=;FaXKtb{FW8KS3!gLl5jZ-FA~0Jj1cKA1J!2yu)K)^=T;QZD`A4sCKh(o z&PvKAT)~XzpTlVo4yWjaxturVdKUqh*mOI#sZhpo2CQ)mNHB1G+yR9XOp8i4xpafx zmb6{YOn2hr?QWNzDJMKnKlchi;DBWH)LXCrVxdA*C zd5!Jhnx#}ClX***_l6lbg_uC>er|(>X2b>rRk;{^W)-PLj zG`VfyZyod78AYRTJ8c&oD875nfSg)_Gp|qAwD7nH#-<&@MkZv32y)Bhg}kz={wrf` zoK=&gl`lrQMiN`SOhO?zsoQtD3 zbKG9Xct?#WB9eMx_Zc%=el1Rmk zG|HwSkYBOdL{N<+-B8n6xY2e7?J&W*gc$% z$LJe~mWt@YJ91N_TswoJN8@mab~#wc3s!W!QfCKT7Fv#Tlv!V2ny1MN%s{!oBN^sV zw;7ar$eJOlT2vYzz%w@~h9Dqt6KHiu9qvfjY)d68PTKOg6{{4nDy8T66d@HcDW!WH z>Pb&0KJ$d|!Y5hls3`vajgzurM9YHKR;mJbT9-3V$<|j(GG6K;f1DnVB1GNZDc0Hz zK$1Pf#B=aY?eu#vYo>BHGV{+l=VladujQhfF@VG%ItRVG9DWH^2P?}3l7O~0q0Ft{ ze-E-bvr!8)StrR9ng^i^1JBj*Ffl`lWjm$9#wl>?Da*S8T+Epuvmlpk7L;wOvFA9< z7BB{*HZ^_pf%kpn*+v=C1rIlg45H?k>_zavgKlw=`@C98dDv?Jwl9SMvg}N!M6j#l z2vauFQ_JMJ*s@8lY-Ozr-PUBypp#g5ea3dlnY53~_2-NQq*a>Z?vq6=e8 z;3BK$HZi_;Q3NEnhsUE1yM@==TDy>^Zckx10|k2x zwer(B#S2}rCWgRQ1&Oec2-vpcK(P7L2f=qpU**#@fPA-q%kc>L`Q zQpO8^0VmQ>th;5eU~wBxYaU5kS$E6SO9kCk$*ZiqhvQ#PB4$3!lZ77+Odp^i-rEx##%MApY*aV7aUF z_{bO)qyujkQ{ysnAVdF31Odn6aAfyYZ-dT7lSPi_tvA&E zf>gwMv>X*I)7jO&xbDoV&d}l{olr;A9lDY+h&lNB#2WXtB2*Q!n(5c?uOex!3`0>N z3lT?V$w>&cG8ploc#EY3KOjo}5#o=bF)(6b7;zZLv$$t&7n%`P0PPhQ0p;s0E~bN4 zgCD3^&JMAHS!t0`cYtITdaXIdhH>&{rHyl7zL(rCiXP-HIPVKubEd6thst;}y2UNW zp0fC{Q#h$#lVlD6%R3nXtrg>HB6#S2sBR?-Yl>^O#ig4A@euh?nC1ve)RE4-%^f(L>2S;giwPw z8hl9tb$moI?EVsV=rj>j3cjt*`%5E-XzOaAo{>{nl18fk?({Ue0-B~}d;kKp6$)us zoQ(>FIYBBo^W*w8+-OT-K2yEjbfca&KrmLX%*L6pG~xHzCQ7$n)-tmpo@XyRY$fK{xZgaWDjg45>Nm7p$!3tG??%R|Di`!1jj&-5;d$y4zx$IrIaM-@Z9^TkzX6wn^(!r3T!MvU@DuvAa^46pQADo0!w_rQA|o);wsR z`cy2Kxl?W~xAKkC8u_aehry!z(7=)e&b#$ekkl*#B zS)Gwhj@A=7C#y&)oIA@-lgpH*ALQe7gXCZ|O`X0cDzW zThL@;!CYo0T}R_Edtd3If-RjRAg*UO_KJ4CMco4|p;Ap*f9!mhQ2pj?W&i}}A2R@E z9}P3;i}YO$!vfUs;>#?xg{K&Xn-?ItY07Yp322LF$t}JxZbMl5Z+NNs=aYC5{+PuY zNCTfZ#-cHwX>^jegl}{xy{D;aK|__3?ze2=-Ai?4YssN~D+aM) zbJS&G$m@=a4Z`$v8pWz14IJ-}@^flN-H@lYIJUQxrpL>)0n?<@%WlSOd9!8RoY0c} z&B5Yiwm=o;TiykSXhLtvnfKCkQ|=2I$Xq1gt25Tb#Ra=SOPGaN&^2D;SIA!Q17Ta4 zOx+%?aq4qcQD$NFWyPTG@e8ldvb1<4(PZCRF4X33ma~3vgksGAsAh6>JRZzw+6)zXhXUl<4Ei^>do|^nv&uDHP^yfK zV75VaQOdOAF>}Xb(Ad|xNK44ndthxqH*Wzw2W>0g zZsTq~0Pn_KJS60D;gC(r9B9r2H$tmDjVOt6W;_ncV_%&NB$mtEt2X??WybVSAeb|V z>v^X!H*+U6VR{+IMbKb!DvGesap95C>Z4B|F;tP5xx>-OhI1jS85xl?LHWd7k5689 zb!cRonMN@{?^PENpM_oyCuK*>pdsOTv1|005k@exk9u^Ek{?*4 z7BxZQH4hg(MQEB4Y@!*{h@xiC5=Yo8Cpda|bVZMU7!+Pv@dO>ZP3~azW?}Wl%<4^7 zJ%Pb2Zavqeg;w{gHy5eibk)rx1hZ~KQje{yAv0h$5MOG} zMRCrPaFxv)&kZ^S;pwrwq*COAlg`1qkwFOOjj627PVY#3&dMA12Y9p(e z*wkgopn4BfXx`LBGlg{TOlB(=IM(o6=(vpoWrKW<3zsqDrcpG4xAsmRY@RzGnTFFL zQ_&=6ydD;5>DHMq3_`K^bTb(>Bj0GfdMezbJ5S<_Bk zKfrvORwA!AmkoDVNg!*c3a^N=s~1r?#SrIj6sFg?Ti)cj+*N2~UTvnsR7}ROfT?+q znQL*MF_tNZDy1wCh%C3d-rC;CEbcmTCnh&D`OO*Iiw3WnjoUXE`I5~Uo1Es%T~71X zm6)2W2CS1~T_xHH@jJ*rWss>qUS+FOG?7{1vhEe zKIw+H_L;Oxeoq=PoU``%)&`ts$N6${K+{^{#(J*ce_A;Gt!h+l@>Mi$86O-D8GTooGIl#=rjCsQ3-froV z7wXIV)!>j9B(@cjpxJj5cmKo%l9r^y6Kj4TGY=osku%46GDb%^DoAhp+RGMn&^%*@ z;iRP3mwXWkZ_PL=CFeQ(2ZW+Oz#qX&_z!4Bk}e3Has;`CZB5L@giv!wmZSGtfV*2K)2xB;N~<1<)wQ;zm7vvoHrN zFlNO=R-kkga8eewqQEVj^sgDi3=7^3{ljlH5o9De3WI&6kkr>=cL)DW!yW}~*8hS> zL1%FvWnRHSP&-7G9uzXHfY>SDG4F_nV>qg#9Zn$ksO$j`zpiBtcosX%5hD{0RXKO% z;Z?91w9xVAQ8wTBQa{%DW#1VSJ#X{==NKxfd6!yzVL$>b)onaYO9#<}CAOvwUTqxh z?IotfJa7gy@{in%XO7vC)13|Hn#)yq10fRPF40wOcvUaPAjeqM(f2ZeR$ZjkOh4&D zJ;j-%chvR8ndK!f3Tr{dCPkxds*3K?5W5-_#AvngqGTFp^#|2|4met7lKFEUQ)tSQ zb|Ufls6sO4BP)8LtiYL6b2|B;iC6LO?gEFrsewdjaF-hV)#NXH{5beuQ8ap$c^Kz! zVV}IgIJieW2#Wu_cnSXulOXqBeSY1Bse2uz{@o~ik$FblMy6d=m**vxnP0bYC!8C_ z{apy>##i*WQQY6V;C#3KHs+4+d^$Jgj_+OCZG1($_v&xsKK$I6JHB_R+xUuh7wxb0 ztjp75)9jlB%vmQ{70arV>pJ{GTt@iX~Oo zK9PF|QMLNtqUzsPsk(=_segr-`x;V(y!YDkG?j+&`e(axmZZO5j3xiUnW(#aBa|9bq z{}7H-dK4&Aesx3{^gN+E0prq#RH9r7ep?P)vo$<8E#qyQDqh!(!fx2^NAKw+&9`)+ zj_#P?vBeD@$^{3*^Jt8bd(geof?3vW)JEfElHl8ufFXl2Z>_j?Z5e=ET)AeS{G{Uz zT?o3T2~jQvKvWR&*L0CQG+Qi1cagnc)dj$yoQ^wHdD}1OM(Kn}=v^R@++X-E$uGfk zDrj6exI#d869Cw8{OA#%wqc>;U*M|}_}5;7;0FHhU1LUuv{+8Y&2T*Sv7C&n0Y@U^ z_Q(gF3LOJ?^EH>{WyXTb;ZdRd!lSLyd304WxXQIwycPqHr6(pcK)~V&Vuo-!=izu4*m!$Vjw```2KB4`{fa_i*-;tl`9#La@Cka>*m$$& zQ5tVyR)gg1ERBTJ>U!oer=JABb3yqAdNfXg=g))9%B)s)?%oPR^;??T%6jvDeIbg? z=udHOlX7AFi?i2Hf`>2~jXM1|6WjaIWc_3wPO~s>%)z(_TP0w}Nju^wVFSExiUD#v zbiLoaE!2$|T`J^cF0rCX8$nN^!%5ir0#XvD5esGfS4+YQ&0$I%qPNS7TnS2dr353q zt;fH%LpFDfTI7_%oXWTa^=1;5FIX3-OUsTdb5?{*KCT7@)v{}WE#irI zo0=_xF+X_0SZO`mtgd*gL^ZO6kFAoB?ZcyoI^4zDEx72QY*MgZqjoL1gVDb^wWw<^!MCTGFX z!TvtE%0gC|BmZOLKi7Y*S2PgvaS41N_{boDAY@I1Ag+=Op~3{8oXk`~frGK1J$^s{ zqCc6QRTb)0ivbS+G~qhUnG{wELz|{?Zy2KZ3d;Pf3VS8T!%lySe#!ArgmzjnVkt(- z@{0Y!Qnr>(+aba)7ve!e4V|o6WOtJBSmK(Y`taFw|Fi3FU}e|XHs(MZc)N7b&>Is$ z9mDA2baA1Plk*jTRu>abuL^6kf3b;prDLw4fP!8^j_EDoiyk&m(+V$S`7Pcaf zD;2AqAs|!_O9ux(Up2RXu9wKE%cQ=23w#3UR#)Rtl{~*o@$drB?Q(bQz?7;%X>Vuy z_3Q1U?GjHg>Abl^J^)cWPWoX`ZbL&DGA|rfGTpDb$La78zi;RZHBPw(t_^VyuNjg? z`L@LJ!SR}s=@e*QFYjgmEyGdVB8)yVVD5sUo0v9XH%f8mUKpKOMnRZE)FFdn9rOLnW22$dbPI6*NYC;Ttd{&GsdPdxL_5xbGXC5zVG|5P_CRB5 zM$7CO@|CTpyxA-~h-*gKapBZ?#F48z;qVjyl;c|5#aCpwY}LvE8D~;f3P-?Fr?KG= zgwn7O-L6F!`PQfiPOpOgcI_6y{d++^zmJ8J$C&NassGHra`UQ=3J&moE zDVNQcdRvPSVZHe^M0otC5dr_3)zNHDa|Y?w7be{-cm)s@e)9+(r>7wKfN@VTZkb1O zNW>dAJx{sER1Tnra|&|#<7)Hi#xr``5G2Lnv<(7z*}X3|tIlTgiK+AbheKH4&yu2A z1l+H>U9UgkOJWpIE*aqxz}I|;!4G3 zP6ebUmfq~AuoR=Vq|*Xe2SHdjT8J~^<)D>FU zw`7-H$#t%d*Hm2MCMUX7Teqp}K5mh`$LC30;xkT6{nQ5c=d7QE8HkTH#3x*tUBbh@ zs$XZ63UZh#-DC<{yAOI(Km!%RX@Al(773&bMhAwu3j^lYsVnJA3=(!z*4l7mxzM1? z>XaRFe+!JV>4Nb4N+tkMo`^G&VaxB=g(h>BXBLHlMvDtF$ZXatEI7{azi1vuDd1}5 zGJ<*@ngwil7f>48y23Zg@UUj#q8302SpeB*EuZc01_^Mjf9*s7WH6Xlbe31L1sz|3RGHGfBphgs#lh(bdhE@{+kMkynbOf2&}W|b{~0-zSP^Z ztj+rlIvpFBEk+l3_$=3oJR;l8ib6>cwd|t~6@+nqdGx62)qnIt6WC!))R;trq9Ghp zd;wWc6fkG6$44lsvVAJ)H>)a{9+|1f|4Gm!{9##L!}NN_V4xNHam+~S$TYNng{^nRw#a4pj01M;tBrhDn0q|_vqA>d zTGx$v9cSAOWT2wii^iOVuVFfbVTb(^uYkEB$?qrS!j(HfHQQT2rK*z6HrGJ5SvM6o zwz*lw$5MK%6~~jHqf1G)w%gdWa%O|7rHWt}*l(nrnSf!>%T?WAUU9j?*pf^0MpdP6 zlgTpK(7?%YWjZoNm8fWCzP%z2Sm4W=JV>IsLXEzw%SAMAWxko_O0JpZt1OEcMJS`7 zW=e*6rV~AeCYpyDNhe+!O=~kExj^G$qMHA;H8_Ox2b01+8dLV@$F`as!?U4OOVMq0co))&07{Am*RQ+`ci zlCS|k7{uY(Dqj*t%X}J?!;AQA?L3M*_|I9zSoja$2ZIP?5EY{rEy><*ZYf_z?HWcY zh5dRh8Tasetyynu)|-uI^(T)XH=nJ^9q4`y%T5PhI<=@fUF*mHJfE&ja0Fp(!a5u2 zsD@88C#$gB4yMDnLv&diM`vq;(c{{vd!~?z2Y6~nNXbH&!wm!pI>mKy$w>&icrrO# z`v3}h-05F$UxaZVPoNG>xwTGxW23(D^b{I6J=ohlZLZhro2!lHDWLbX_Rn}kcc-&Q zQ;VHw-F7gx%r~sP<8JG}Qd>isB*Rk0I}zXVD#>~9yh>PvP+kV5lP;Q_g`FtaNv@FF z9s;|i@jtD+Wr4HB`wKiyfxH0cOf_gj4U8-fd^H62LM4yuLk4ar2n4zp96!c+fjJZ! z7eTyg5#)`>0-4^2QQ#X~ev09@0TdKxNR5q?EnQxk!YZZdH13wL9!#@`>J(=>gi-|u z<2E9-jWF^U)*n;C+c??d3&hSNwhz}Fmry}_K*FaXkIA+GvrHscAqG^@Ac00WN}v<@ zEJEIR&~@!GzGK2oqm;&z0SfX=gvPZGXofm7o?ZY+@D(iFjdGBbN*-iA1CocCo-yJB zHo3?i-45HCDp{gg7P(C?CGxOB>2_jOwdbi+c(ufWR-MS!*i<#JH&RE=4g)`PnN)_G z{Ax;!gnBjp6`K`_(lq!yh$i9ZVD)A2IUbEi=2s{Aj9smowU%TP31psSX$#L)g3GuI z>QMkf7vo9X1SeH4GBLhjQVvNbsydd!LN_Xm1MT0TP2ov=z!@4>i>=v8)!puUYfEpi zRsXV#Z4X=sq-5b~HZ4|7e8l&Gu*4cvXlilFXwS_JV) zAw)C{SUa0m0R-8N%2sxwZ_cE)1D3P0QP#oh;zm4qkhSM2n-yqmeAnvKgc!abFhwH; zBgbu7Qs!W03#cZdD^%0wQT7%*t-&nUX}jO>n!wfCw?J`dGAVU?Z}S~30Ar8CW`MD3 z2EnOnhP6PAlyPfq~_I3(PjZpxLV3yB2lrkvFV2Gd9#7(H@#}49Tqi7uN>)WozRSf4ADeZniecjeF6)7fr-!LC;O{c6eeOAiD7&8WKT6 zj^5@*riX7aTpfb2E0fafhGY<4{fPSVAuAB9QjeqV6!t&PECy({GuEWAMtXMeGHA;$ zP@~EwmEQlB-!QYonru$6H2=hKX<-1WCH1gkH_3p|4^-eD808ufoaBR%k`KZv(}g)W z9hyZShgTEL!tfAx?ThRwszVQo71aXVyyCWBerd+pP(hHSM`K*wt?WGzub2atRex=l z3pTR29AM@?$lkXw-RBr+g}LoHh1s>9@Fv6Cp-P&O&aL3L8vT+ZCvdz>Y z5MYLr=(N+JHz$5WUz3e0dY3$@q6^1n6yT4)>3~`P>zgQ$Bs7A&v+9c39Yl zbs!$iFqA9Ik5!H}nR`qwaL=LOKNBd)knx03w}f7JoAsa@^_apuO!sL#Tiy-Ww_$VO~>89d`&*H_nr39K4vR##KXXJ-5|H^%PS6HmI* znK|HSU|16+d1_~LHae3s?PqJ|_8Wz@PqMO3?&Go9NCW~4SC&7^^&Fses<8)%kjZjaH`{>5EPsK$%l z`B#vS%x^3f`1_dPp4g+$?4D+m*-EMYTYPcxv2~|oICsEH?n44@C+f)6_}}A+6kRL8 z^C@~3I-TO(orpX|@feF9O_-cV*XRm2?RU{Fo&(|z;Bm}EKhyfc2O+zJKRxFX_#B7*+!N)l|=QW8w z94B-dxQ1zXAlI**PX_%wA#jwkorW}bUp9*vRmdR5KyJK~bPwJ|K^qr$Z^KeK+n9_x zaT>JaKGRuEEJ9xenV8u@MhbbK7k=)ApX<<2AdzLAL@yx0Spf;ovLrCVebM^5e|MtE zjeTOA&n8z|n+-h6e*7n#We2OW|LP=t30=$&&EsJeNR<)0eeih}J;Q7czs3>gRWz8C z_i?DjqBIMDoC)6M;`?gc5@qC?$V0(fV&U)_WC2Wb+L#yCTE6e~NfR7_uEhtLU;+;S2<^C+6t+VIStS@^Xu>vW1^j&6fS-PQm;4b!N$hZ$HCKQ zPvKYN=|-^eY-59q*b`w`XoE=*TnD27cCi;258#Z7*bNaA+L$1#wr`c(wusEGJG5~_ z0@k$oh0jU|qA6BTzi;#ps;^X%vZ+M&4-Jc}vox1@3tNymepu4#F@Nsc$t{QnU|l2F{;}^P#Rsy zVduG*W^D}Qh$oF3J>6YEZ1^XQwglReGnIVTKFL%&f(#^oPgPL95T3sq`p^F~oA9FS znmhk2%yur~+FK5=v{NjNd-;Mj*)#`UXpMd`+!YS!*75Zn);{_fKRqa& zbxbT<1}xy`N4G5B?8=ZEUdL{J!%wj?ctH*>gIMhM)$0iAT6yV2^0{ zS_8nRWmIS6dQs(n96*mwVX;W2<4$zS!F#6TXffV14@;73f58CPt(i}P3KfYYTV^;N z9Pn1Ou;62-8cLfOW<%)r`|sQYCRq|f;sXoRH=Wq!lJ9+4or|x2`DZ%688)w1EjQOi zxhpbu+a`047II?8o>=HcrpET6IFU7_A(6#0wbBgUo@-~zx_EARrelf5 z#;z<@4^iim>dX-u)TfC`uU0z-OTSc32J^w(zQ$(mOy1^~Crsi_QY1{SRv}~fE#%4O zpC(Us7LX^#hVjQQiHjRP>?*hS)4_hA7daC|>FX3KZ&{*O=+MP|5RSh@W6v}E?We;d z+-PH1!PlD#7I0TDGa=J2Fz9Xo9WCkM<$UTZ&q^&l99-7b3M(?tLsLz02n#%92_u;{W% zAMax^`e`)?JDp@qz$Xd!tnutw)0!{j0?y^-W$kjE)}-&p;pjZ>q-$7sEuKtQCz}nt zV1v*9MZpnGk)2w@lBNu!QpXJ?VF!8({e4cO#X&|?FY3x5Z5<8H)<&NaXJ7E40RI)rcc?nQnnCdeL=`4dufs`~LhzV~5leXG;m@@!aly zMVF)umH@ktZAnSk%qHwwkxT`KVQC~${QnG>6cMY zT^lgwH(9tb_MFPiFWm*px4w8g!>sa~x+{6RfyHwK%@|q=-G^N$g`0&fF{oTT`@d%A zy!a7}(-%Ix``%L*e!kj#25CQ!e;)pP)d+tc>Miua53fJ!!ub7ldd1_cV$q?%gNT(4 z)Hiou4J9kZFvwufdP?UUG2F$lTMN5MJF1P&lSz`EC*Q&-`K<8=GN2u*IQ{WFoKJpt{!&VJ%M6X_T%#d1}0Jre`LGDX4+_ zbo~R88YXFH$0Rjv66jZm;kphGidGxMmwK*74r|6IFd^bckK9?Fak1g|V!iNOz+CQF`Ge|vZRbByrpVKvS7z34lJa~Qd16PAe$NyM8 z`MFK<=*3F;_#gk@$)i`5mEU!8$t0K7wiGoEzW?FKP~+z|ODYY*rWMxz+ri$CJMVu& z095}XjxLYeJ15B%z^DlTl~|%g4dK(fgQG*+k9hdq?}Nd2zjxsOqY3=K1OLDJ?)M13 zqpRWxQmo=}7#V%LSlEjuo{Mq@zFX=j8nY}gy z_Pz!i50BYE$-kQ?nH3^%mlewVQ_9PStspqHEOJ;weoGbe1$Er@c@=>HRtQHrS_AcX z)R6l*u(|0Nt|q#pJ~d3m3WZ#`xy#c1z)DU^&zYH^A$R|+$i(bBzB(f_Ov6Sk_%TA! zT$%vb{&kR$Ksp0?dlr!{keZ^+TJSo-avgN?3ft)Y_33E8u@SUE{NfTwQ73WcN5f=_ ziUuN+Ovgbl0hz~_F#y*YH|fZB6rXr;tZ|2*lP#v(7ri44heNNVcpNGsj4V9IK$zf! zsV=dxjod1dStKL%fV1?G;ieYz0}$doqp4!&9GBekXWa{S{oBV7jAg1M_h~YduArRQ z%S|8lt|sHKBM&q1BEa|NQ?u?U@O1A;Ls16E2f1}$ZYAMqNG?z6XKVr9#CB6#S>}Vx zX@ZUYWj6CbFS{|%Y~>;A(bb@DvyD2(GgMR2wpCC|EPx{I*tS8(H{pg>6d`Ao?r zGCb)hu*ht^(~K1H^3d<^A3HS#!Mo4RLUlp7#-B)rfqXALh4@^weWZQ9_P2|L4DRh8 zamU#FlS~$Ou~z&s&MWsncg(z8b&FBt&ED6WQW~Do&%D5)41-Nwz&*yXRTMdz*K^a<4Y#n-%q=)egkYc2GLAxa1d_C=3xIdy~rxY8xRy_;LQwOQ0H-#K$`j|%GF+}kj?S!{{+-<_Fj6l~1#5Nqc30iPjXxRZ9I zqR(l@Qb7oI8L2#rves01$yXR_;6rhC; zYbjc3*bUfBhNqtRwh)HijA=_neLw4-W&-qC9dw~SF?>_tKQY??Tc-M^-xVzFQ>@G-UcL+( zk1It5|0F8?H2a}g$5cn}U%$se9`!LmFx_taOe>oq4-g0RrFBC8v#BLw@jMy^^;)BD zF0Z0JuscphBYD}xk}NYbHryMt*-7)nK2DpPFomS{3q7OSro%blo3nsFJ^90j=f0N> zHj4GjwSS=as=^!14S1+=}AU*AOSTr z>2}%WdvYFOTndgpf&Q*?*m`E1KC7%5PtKDx^85+$c+)tX=?4sz+n)9FzjL%?CAS_| zDp~i`TO{3Mts@#7@U$9ODkx(NhlYzaLe)#cuT*|4x1q>gI6>A6BGCsEXeK=k6dGF^6N*1e3>!4p2hos9eo%<3k! z@o|{pXXje>tga83RJrR=xehB|z(PO3Y=y7AZHfB!<9q1ac`1!ZV;q2%j<-*IahhHa zI~?(X%%1JR2v&&VQh4Ey3iS}uj6yvFDefdQ3*o&BDufQ3vydWZPAWAT_GW@Yk25Zm zc4rq#yId%}m<XS@rkIHUj zQh|Js`ZSY@^n&EgOfnD%QlDj0)9y;^Sv`{qB!JXLCKdY($<0hMb`p~D2Jo}{+yGvd zv$ixEr~XS2{kXj*8PrsE|AHPrJ7kp<yr(goaD1+C!u|r|>2QeMqLg$pZR4s# z4=18W1O>LhH;;zBv=#(A(+MtD_>u&=I9-mz5fn#EL%O=a&*j8Jj-8J=I5Jdj#12qA zIj{>6Z<6^1_HGi<8|?hvBE{Zeys^~-4E7NZ(-?~5OQddGjeiETy+&=rl;}q2m^I*6 zKY~s)o`mohcpU(zHJ+19sKaP-1p9>qpGS77yZtB}+jKW4<%vhG1bJouJ#%^>i67fb zE!^NdOzDPGJBo(Vxo(N<*jc!`*^1G<^tYmbnN^xGSt7he5^`ktv@Fzk0B zz@pG@mT!OsD(X0EI+Oy)BVGiv9A#hV0rta;4)NY}AEwns4AKT&H)(c&IE?R$O5ZvfM#lA=#3&!!GXY zf?U5)7kzF-$%v*I`X>b{wv+BPP3p8WjxIr9lAT9Ugo7gm2erLgFo3-*NVFW0v-oE4 zV4eb&Qcp2@SP5o75@r#_J%f>@Cb4^-~quc|ou)(?zCPEA-hE!++r{h5uV$Z>( z5OWIYm`5L1>JmN!SgHo+$>8NKu&qnRf^Q@VbZ@jQ@H#fLbAJh8P|Kow2n=DcN-bK1 zOgBEGCvH%O$1t0SiZ_uiQpGiQAu9`-JXx)v0ZD@j^RLs$J`_!_4UIw7#x0C}#G2IV z=@_y6-&lCd)jN>4i0~y%b(aQ^8GahQ1JWJEoiAn2NzlTlX{^Bd7g+UmYkGw)hnAIr z3P$b$?ZEcWD+U7#+*PK$O5xq}Am2fU3^x1gjP?joYNc!ihIU2L`Px=4{hLn(a!RrS zOsRnxl1s~qXg2n=wO2!;?!%UgpMY1rJ?W`PXKHQb2z;!fxyu>BqeqH}bXPm8mS+br zo1bm|*a1ALLI2B&we<_&`d8sVO1OeTB5>7Auidlb#jKcJtYw(k-%0lUwV{lUcO<1$ zc=FdD_s|}I`$c{?7ZRqlRIA6rRmjd<_DlU5R>p?ZU>j1lPA-T&{)dPh$MQ6}t8yHl zbD#w^ii z-$l$;wpJ1uJ;eT@6Pux?Pd8311CUMtV*uRVaJMWOHhbze$3GcmsJD;-v+g z9Bji?R(ZZFh9}WHDV131B%|vO<7AWuXm+j!-BflSR_+1-+GH7nb%14#Ar-|cq|#4$ z)e6NU<3)$kC>P}ubh@OI{r)se!?UP3^{}5@a)UWrGZSE0sXRb~UFZlJs7LrB4635o zz65Solnh*ttSdTEVIu>K(}be*9ie+O=*aOeFUG`4zAo z2i{3+chCZRGT@zZwm^k=f#{Ip*=PqtMV}{Kp@sCqb$>^??;5LkEmJK$(H7{avTj0Y z?=pLSj$$BZnWx*bJo83}4a%@DF;$m&k^TxYGXLq`@H3ZZF|Kb0O0E;+%<0%k8sMFjumYZys{PNPOD4VGWEUXWlCoW{f86QXAP$6O!QE;{L4@j@r^iSvBk4I&R%IXH*ulaT=4u3XZ@JGsQ)Xu z1}rcXMW;rO$H%3e_%HSl1 z@j$LKmLS~=!%#;*>;;GK_m6(u{|7>%G^(%Jw0AuT+B{4~~9#|LG`z z5+AqU9sNCczaMPB`+M-e4&J@4275n!__%j?h@jqo3=ZCYcyq7^$p`Ot-+X#~@b3Fy z2dch%e-ykqczbXJaF5YiTPtc8zhlu6w`wxHrh_M@jAKt%ty$6Xqdw}2e&YL}M4SKixX8YhRG1lwtx7*+E zQN{NF=p&UB9Dn>_kCG1FL8V}O_vqmLJEYI<`*%kl;j;?e{dfdbi9T5L$AiPYYOwwB z;1G$k|MC6XYJk**O795{RDHL{fROV306`J>rgwA>_Z&vA_qN{vxI?V3%hw8A91bbc zpu`8+^BnV|3*2&-Dz!cQu?KSj6je6B#Y2!a>|Sk)n>2DoF&|}y4~&7aF8HJNF$%k+ zQmfiyNn7R6hvXA+%D+!dUhNkGE>M01_;-PFDdl}AucxCv8D|!P(WTNpgd_95+@e6- zBYqy74_1fdwm1&ayXeOoVuor>`ulwXFg-z4~ zSRp5oIHiD8!zM+4rsXN1C!oerKe_-pC=Vx3>}uEP105?U$Uy*)$8ood27O+4EU!|b zn%+fF<6?T*+jEw0HoF3?-&9Jxs;3pXgcjZvov0c?UXz+W~T$N?{NCE)9l&{`W3`Z4NB1O-X} zigd8jj?9@79_`3gNFJ76G)XnYaZu{?lQb#?EG>Z*-y^RnkZ5+Q8An=5KaJDFNjFX? z_Ra(006deBpIDJ}R~kJVVpY)_8^_MVUm=OgWP~uAepS2}?cKQhez-?(OzwC}&MbQobJ!uRAJNfQIPCSwR}Lm+ z$|rd2^^Y1Ious9;3}tAi;EurQ)MGj-E88*%0b`!q#k;X`4pyS7fLrDo0`Ej%Nk!~F z4bMT*qr*>@dQ=Ie5TaO{IrA8H)gVaN*jbU!G`C&Rf=Oy3@-ne*;4F(IM=yyQ7h7ShUAT8(;repz2-!U~P~6_oOcrJM6hYuyfuug@>8 zbW$w+xVZGKS{l~fT(GWE$DmrHj)sc+gh{p%;nWP&n*;?s>^65%RjTsCzPqej{T7(? zIW|#OLD@yO3TomRIzO?JpqbYcP(@EqsqpD3WgD{x|NIdXqd_JidK(c-p>ZKa zF0O*wnkqZf1NRwF7J#Fwb~0-3@wW^F&7KW9lx)(!>jgGKX>W#!P^OI(Ds&(4i{zWk z=uk)xG%ykJ!3&(s5C6yMDQv6vPEQ}M>5i07b*D1}Z(&peE+!Jp*0u#^Qo-U*0JJa3 zJQ7lzF4mUSr6z%78>L|>JH%{;VkHhN-IZMH9aqw+eJxJ; z^xhP!=rexyJTO_F^MOg!`GatzoWbMVQ1LZJ$Pnu5YaQ6s%b=O7vx3iE0IRPUXBg^5 zHCPE&DkjMyU|%~&Xy|@W`z3+x1S~+M3RJ10625|WNllGoDR`2*yGLEe02W_`(Q*9* zGpN~IUgM-PZ&I!--iDs18rFi4WLz8zNZ1O^l3fh)h=*>qLGn-7`S1%@(Qx(C;aWH8 zq-#G$?X~|3FTz9e(_8x(k;7OgTKk@S&Q4i=r)%8U8o!>aNIes z2BReP4%jeQCD|&+Q7c=3{^PnEQg8#RINAPK?Tkj`!GRLH?KeltQ=;pd+()^C0 zEIPFgMbqfS;22VtZy}8K2E~_|{_C9_* zJ$U!G?KcOnPv3tyI)z`K_Da=^14fnOb9s}XgeL)%9ITWu+C2Sy6qHubZV?aRJMOYe z09!Mt6fIm>2sE>ssyo?&Nk132?T_D|;xlgrsJSKP|7Y)AyW2Rfbm8y(6)m1*0N5Zz z%8uiZmVGFSvN(}QEs~1jrO`_@fF{`%h(^$WXvU-e{_bnluIg%#lAUBG$5~kx(Op$t z*IoPi?0mFXD&#rJ(ZvD{5g>?cXC1MZu_nZre>9=9@(?Bm3RP^FEfPc$rzI$r6jzVHFidj5INI?hwgca@V2 zQb1cxu3}k-6IZyR3_MYzgU&jxzN6~gs3q#Yd*Sn#E9l@PcY4k7nH+-dAWGi{aJat9 zI^uI~_Y`^ey=O-!c(6M!=EW5! zuVBIcmKGlG!yiXK+}iEEe2U#eCOzyvPgAanER4Jmh1}{9Jb(G@^Z+L9Y;X73vq!so zf5y)5LG#0W^iDOhSIfzwgb5*6SyDOY^&zDET{EldA@Kb2G!Z5 z*}+MQqM}t2r+=M~-qz-w^4l1}MBD&-KikOF-@Ge+^RD>KyW%(Rihq)K1v>L)tc=#n z4>s11$V9r^kZddPbO^M^T|cx7oh}&LIVsUu{Xb7!2pmOhxyw&gQA7hs3713pzA>Hj)4Ze9FqPGa|`H%{|e*eZ>H}7%3{Rdui**Cqc?F}^3?3ccD z(zDwa+)OuciJ&i&Rs#_{Xqy9Z<&p+&y10&OWAi%0R+DVsyUw+|=g1jclc)LQ{Afm+ z7-y8GM3}a(!6Wb7@X%TExk3LT4ky&A*KUJFRs9RC+Nc|K*EitJ&WS;pjz|e~TSysP zKj^Z_>1JKl3y~VT@m3H{89V(H?%h|w=**wz^LNmy?D?yl5^L@eOOq-G z6;oi2=Gc%VKojg19~g_l&zUzbUFFkqww&amcH&x4V?H5ri=H0mc{#zr0oU|4&jdku z6^re_hSQCdM>I|DxSs$!RSYie7-fgU#C`N!v2*di=Ohtkd}8jPw)j-{e!s`GY81;4B z6)NvMcQ_L}hzxU1V2z2h@YcdQi()$94$eMlNb{7XHOK89j>b;bYUZR8hDFC0F7NfShTL?5OCS(<9| z*xH)--oQ;}+qriIXkQZsVK6B8)isK8M!W#2>|%lXXcYU4YfY))WM2t_1?{sfa>Q{*l( zyD1(D?x}Y0MsiflIO7HFzu*acnl?A_Bo4!*# zs+0^OZ!F=DUUdtu*e5TNz(6iWmh_y~rNy|A=sf3|((pxd0XN+7#n#_zv0DV# zF%4)LK{}B+I9kqMLi=Cu3V0M>E(7x{Fv9dA@AtE%D|lLoBF=;w1{e(lFVc1QWe^Qe z`wx0UPguLGrG0vjfp5bfEvpcW1lw<$;&CxtUcAKogl2XyJA|{tRU3oL1%yvMrI%piUjedc%S;1OhQBhU6ukG((m8!5L$^Brzy zkSgWtC;hn6jU88?U8@+1rn_1btmO7+veU|_ICYx0Gn0buy3YbcP7$q0V zO8pM)y-=Xl_KbF-~P5d3K48bFeqA@^`Q)mU9V_M|fEdFXfn1 zQa|R41!sR47UvbQ`8Sa+y2AViS#}6p5Eu$%(hg1r?rRi1OL6utsRB78pqzsF9%^n4 zdZVeahOZY4G52tA0@*L?5pE0jNII+>Q*YwYuo?8tN`OE^iSd%CcC*6o|C4Avr=vU1m|E{6O8Gm+3Eu(J#MQEMFg!^}jq!ySk# z_1HSa)D&dh?f}y#RAmrk=4PD6VMhcvKq#v-~YTre%e<4jSkwVu6`qwN9*nWBKKJ(YV{xujn z{2oMp^13V>1~IHE49e~F{PTy-*C_Unps5#}X(@9)$u9=k%bM>Dq6^rUE)YYpr%XlG zb!IOgCoUL=C|Z+_0Ln>-1?4KN;kae_b$+v^t`q$2=Nv6}PmvDH;~mY_?Dk%>Ui<{l zb7YC_@EIO@@)|lp&^EP-e869Eq{}WUc^u0rPNP1|#I5Bc#Z7w#k%~!!BsE zpl%_D6<%jR0rG#QwvZMf)M@!1R~h%@x|@m1h<%E=rck>@R~ip2cA9WAMdRFsdujR( z78996?CDZg7KZ99vB;&fCJsA^p7K-iEb^3EPMU2Ecd$^(f-f`i9ixdVU<;6@=sd6S z#seZPHz74W5D#EC0S+G%HGZD;?Rq~Om&S0@Jz{hQkALhRs+8x;o{J(SDoti;>)@hK zS$=H3!il#4%QPf9H{ysz)BsgDs4o(mBj5gR(K`1T0D;VU{2iW~^6IJ>mqZ3AZn46| zN0Xc;5N+OB_KJv>n87P@Ho`qjAtQZVFq!D-4F)|hVWaFzl(CY%1<9VysoU4-1M8a5 z@&fNkx(8u=a?J5+l{#|$`hqeltU1D{;WAJYG&qgL*MR8T~aI+KYiR^Rug8?#nwO=DF`j`rl|0d@UJ`m3(??CuQ>7K59qZ$H;I!q-Ir;`>uf{j|+#n}K_iBYPR3S>8 zwn+vCzIRFNGIu+tx$}H;sCYHF8GXD~fS0TVe(f(`XI~Beph1p?yG;Aux@{7>%>-kM zL=9$gLKbFHT;!vhwU{{H!O>P2WXlRRBxm16vk&m+PFpi=z*5|jdmEQ`?&z{&5gJ_D z^~|Zq|KD^z`<494{$*}s_Y&RuALs+1r-&sr6^R>JI zqL|MEBRu>j<2#P}c|K)b4KS_mdE4?G4l-TkBz0td*rKqedM}>!ni*$V+EDlkmi7{k z5-cg1@#^xboM3!(m0ecXJ~)6u6MK!OZ>pv6)SRP~OVJ5K_g2)nCZ+-dSXTKZ7$is` zsELK%(IW54*&f!XYr_%CdsNj3%?Yx}&L(M)A#-;YhQX<2j|H5MOHj`7@KPFi)>{08 zLpFi1^Z46MW0IK$pXbH&rql6QlnM|TOo6*LlF%uwWy2igJkh)3F_jh3yx~5=R4{7O zZQz%zz*C{?vwU)$-_)NWv+g(ZxWv?6l<6K--*rN!G^HniWYI0OR%vM3q? zJEydt?pNpG5U6w-5kVPFK0DE9i1(xp95DJums;`zaaBDyWEPn|TqjKaJf|s+4=Wxu z9x!GV#6LWe$z}-e8l2^O5s*fM3gAOwMBuJ|pPi$EKzQ+ z^rBq4N+MJn(`%bI$_jI+hk`J%AqkaBun4a_&BGoS0pGUw&HY%ALij*LmAXNJoa zg|$^vp!A8l6BMA3XnSJ)Oj$iC+ZvtLp;k-1!tAWv)^^?6??T7A8n~x>p+}I%9$$HP z`KLYxCv~Oi@;U`k%0Z;t842(4TE78uwhOWB`0jPy1Z!^&0gmWHk})gm7pe}96Y{N) zUY)tZDR+f&R4K6z#l#kA0)$+noHVkP?)+a{E62`9?J7C^aEorpsPg2m9f?LXqaau8 zUSCUiHD2*I5D4oGb*yVG?HfU1#pk^mkiJenE(Ia~YuZgc+3c$TFEDBm+qz-9+E|#R zdRx_HvzAZF3B44B6GP8VQ713L%$mzrI-}ig*;_67xtlM!oo54ss*RtXQGle|vMYK* z`F*=R&sfVh===rxvPM-VGhDuPiJcKo3K}wJ4ukIX$7DeTa?CbJyp1q}==YDrS?T%i zU(cStdXBXEr!P18e*s`GoRHz| zDF*>GogKYAJv(}GcD#G|{XSE6ye_1X)TQu!!GRyR3UrkNgO|;!YgYe1AkXo+>iE&! zigNtnXp5gP4uCDjq<>?@ZHcD6E3|QPV(#h2#*TF4zhRSP-+l|z)Ohls@#M?KldqfY zd?k-Q|2!soQ^AKMM#3XF5}sFiF=bOT`I=A4DZ6geFjUkk!-Aq!W<(zTXa!^)CWRGT z=FC)lE!x>9YXZ!sJ4ei>J|U1(sNJvK&{ z7Z(KC=d3T{(MIX*)!jg@Kw7mN-5^0`z;qlW(@YIZESrtv;ka#YkZM@>QGOvW6EX1f zu%bg8tUk&h-PG2kxd1LZAP3(*Om5@ts1#13qa?ilJld#R$L)ZL?(~C3`a$iO7hg%8 z;2+1Cv(?CX{YB61o>zM9 z7DI0V*8mJ%uEs7nnNylCyZbp5=OKM+Db2pqX5{crZAO}CB98d)aTb-eS zPMmEMfNArTWjN9;aG>@Sf?K5RZKk$2X?YZkiu*|b`GCWToGATG{T zUp`gL$p3PipKlGaC+ILykP7-*6KI(p!N+Tt7Kt59u91caQQ-c1zc4Y!_%A;gFy2T` z5q=0@2BYEjsZ~#WXjWwldat^H_r}u%Ur4NF+RaoG`k@6?kWBn`urKErDkd{)$|VwB9P>c4{MLEE$9G2K)%02 z?mb_%D**?E{l3pvPJS`1;M*fr;vgF}#nFp3>&!W1;ijf4=qf;ebjzZzN5v6QY#l~}f|Q86CSzuu7asM*ouocx^jd=}uXwz$x0tITF_oyG({S1=WXIpm z)XdxybgpGcrm|5Jx%wu+uzMaWxKyNb=vT+nj~!cEYrO?x&UFKb_%~@zOl~3_l4pNp z7}A38+%?v{Vy96Zlaks?b(xOugoR6l)j=aE`_S@L;n)Y^12mIV&h{XxNjqjaofat6 zs}Q$hRs~ef z_rc83y=ZeJ_!{Jb1B0F(d1!dY$TXWIcHo%dej;~c&l0J86}z|5qokoRTB1-3o@6q2 zobq6^p*9E)zOeM|dg>4DWW>m)2i91i4iinz{2G~sDFvb<;djf3u}-L%uW2`rtIyD< zC*^$MulbbNwt7c%AK}{9%|d=0vs}P4Fbt1-iB}F`M@zgvX;&OHn5;4hL?ou)fv2|f zi0kY%Zq5zYnE>Fu#geC;71+vKP0UvI^M$;FU_vdi6Rz%9HMQPEown8qwwarbq}e#O zYqgmTet`kQXC^dsKJwse+GN}fi%KGV(a&(-i`%+eZe>3>@&;8TP95X?+R*X5tlzQU zHMv$8sT374-JVmf*)SgpPZJij&c@3*d*#Ze6z(Z&x120)5C5;LFKL((A&cQF)Rx-u^gNESpCA^F4d_E=4LmBpt zq;&;;8l?uCr6;j^4KJ$}2NZXblXAaZv3`01IQ8P{^}T&{S9L=4i~)DEY8Ny>6@yKo z0fD`ymvnOT{Gy3GbAxa`5vx@y;baBSg5k~BqRI)3y%yMc)zq0RL4wO7x&l^4*iG)a zKIo?tu@4sNp(|_ES7STrlCKKX2$c1eNW?%bxGEZfkT#-Uw}v}Du+7on22T3o7cE`| zeTkYNuK@`VaGz<6fntW3e@|}Sdkr0T>?UwW{)mXP+j2C+D&&jw;rwG!gq=}r4s_xs zs|yxZ8Y;#9n((9&7UKi`(EojTC2r7km1HaeqdtMdNto<7+#I_6b#|OK7#l{AB2ns~ z&L|573S53{0m%|3@ts!}b3AlwHe%JuRBkOviQ$AQ4MKIU7O2Tz<2!6h(Uxd7;9~O| z<)(}`h>b`LNa8AMzqcC)oH&+DdfrJ;$qE7!eC^DK99s_lLtF38Qr$y+A)Hben;UiP zV8i+ttDnG=>v5LIV4{oWE8}_VayG!8D9JWZ`xI#4n1zC^bq8OV9*3e&s%oakjr^4B z8FZq=(}W$t5LE1lQHxMmhW#pxt5#9UG?l;4%L$nzkhc!C-0*r+#j{*Cn&FCg=wn9* z+_Yt9Q1T2x0d$U?uI46#Wu518d|nkDAXz9TxG8jvi88@yR{8MF>GafkDkXMh;HNri zQP88{^D00AI|LZKpa`rsdf;J;HL~pAK6n7xob2phb%m~z}+m9h*Qxx z_)UL6j#ee3rm^&2CA=O|EWk&3 z3a<3(OJuN(jpB~M41*%+v~GG9kN7_^e(nUBY9Bs~ZPo-DNCDMI;E?q3$v_~1te$ZM z{6IvjAvF4KClrK}VVBI~V8YIPv~X}GVh9k^^qA4m>sc`>&&yG~Makk&FgHPaj>2d9 z)V-eUd8VQ@vR>2{4{E@$gY)ctIVU??cUnz1*zpTC?3Dy)%CeZ5na?)ZqKWj6bM-JBy)*u7h|*|wY-A;qo0Ddo*=d0 zPkzU#5VGxTImOR4RB|p3p(!L|L+XAz9|ieQgTy+A~u4!2LaQ}|Cw$K$n!{J1-J z>H~goFg>p#sb9->tg0_aF9RJ414DdUI7j~10It55^lO~T4U@GM4U|Y3c?(RJv#yEv z2-JYd%oAojk}#2QzUH1IMUiuggo1Ef#Y6|3YMIWikvo~`k08axUQD-QQ=fe zVw&Z9tiYJ#sl+|!882l|&7^{eH5IiXU--yE3FHl7qA-^;L*O*T4l5nHRh8A^D8%~@ z7$#BU)ApWHkGr^D!FHiSv|H#ecT#Agi}A}@pfB0gAHopUv>fGqJlMnW%JP&z_y|`d%e9PrsuJ)KS(tPd2O5Aft(O!G9 zPBc963lc>Ef`|*wWV|y?d|CX8eW}AhW!QMsQa2706FNZSL(w#!G6yWDholXeMY6y; zU9j`x;1gFbXQ&>nMcke987GIN5>-qUd%E)FWUpxan2BEX*R)Vu7qu!p%g0k_T?8Fa+-oKjXHOd-QMVI$%iDM~W+ zUE^tCPJtjWD@I%mW|H}yx{ns(Z0KMCJ}9BA`W4nNBcHCZr4gWCDn`+iktQUkEv z(Yj%0Ja?<-M9MkogchCv*W5v2Z%$ONv(AahD1u? zV(=s^fSzZpgBnCOC$+YPLm(bXU}yu@0H1Ul(PS*~1?~hfduRj5QiYz#vtx3q>{|VZ zv&2CUnr675&(c^saZBCEy+&aSPieovo#Sv!0ic6@QC#+7JGRI3v!VxEi-da?651A+ z$D61j?|&4)hLNKlN(a4IBuF^ahrq*LlFYNja6{@Cq2&x?d&z!^cDkYqaI+W0OJVE8 zbfICw-lQ2Pn;BJCS11nTb+NkLE#rHEuFbH|OS%G>c$I2JclkA#In$-3lV^o)^O3N} z-fMy9oN5H+X;2r{f?&)RqsxKyRf~}y6Y2;Pz#HzDD5(}L*^b~qNDs?#Mp!V8eojiGG3+8z#mL9YH94Z-dc+kyuPR{|#a!FzP zqR&|^SdDl~1()x`@z_aezBWYjwdz=ft73k=-h8tYUP-GrU?NTu;WI&iCylNkQ!~%V zMu@ydUm`RtA>QGXcq4Df*JKtJ0XuN(ODkuqX&6xa-b1Fd)PutmurpiBCTo&Pch#X1 zhRS=!B;re<5qICj?o=msy=r|##R0YazpE`8aK)P%0YpNoVL~yUwE&P zU)nGo-Zffh*7WS>gafDI;(BvVW5_9>jCf9N3EEppweR4G6R@(@eLv+qq&C6;sw%Yf z7_gU?kgE_iBW6Lj6C1i3~doXc3ifr&M&jpspPUo2sC_$O(z`cyRwH>*`weOfOn!1yAGXW;7{kAu)^=PBcm^t9~S;7DF|( zcsmfvJy8Sl1QSPY=UsY!F0l8BeZ~axI6jJIyWMqz@hb05CePf}5$=^c?i1U>02W}P zmb`p^K`G)CyAuof%5Yc}>Ne1RC*NePN@;^g<$7L!kl(ZuArDMFAOSoiwRW4kXmIFam(dtme}&s97rukPs1#fgha(G?!h zBp~uK_gcZw*|`NQB<@Sxi?p_j)-kc))g+A8IxZuhVsDKV*i?RYRsmSui%nOmT_mH4 zMPzS7z_MJuiNi7tw4xe?i5OPyT|4z39kYOy6Y&<_7cSw#<`=EkfOgvPu{PK9e1;Yu zpl2KP;>OcMi4fqxZVt_4pN2I9?Z@%{vwXg=_yLxzQtaq?F7-G<06s@C@XINXqQ!EG zlW>KGqYC|p5#y>2Z;X0tH(5NRXT&tA@6_EDtxYl{Gl;fYHoV6>+pP|E3YGujRkGHO zgqZ~RyfwVe3ym%Db!VIvBo3$(WtVwNdw>?d7Y$L&(MfFI#z3tJIuSK^2V_rjZDgY% z`j!&<8P5ETCd5Lmu#5YPr!<6=jnH{Ld~L|QyI99qijG>l$CWY7Q!vXxtfV!)CuZO4 zHPG1ZR=Pnh43Te|W_gxon32$>z*28+^x0IH&GHJlcycHr{|H}j$PqAI=1WHmAL`|@%Bh@PyG0md8 z9L8WvzMj76`%7mqEaAj6vlNx;Meb=7!hF++De@SseZ-R|w2|p7OtOVbI-lGy4hkob z3-7m-OKgJ%oEcatw{t?VCQDG&fUWV;fitD0?ofj?eR6X(tR~5Q*SsxWbLl~DlZn1b z@F+rDZ_7j4mObJQ6<6lErT(3jQn-^eRowaw6Jq|c89&-`z5TDv{;>l0v=RTu*(p9@ zwj6%zzu`0yv2TUq{6*9Dgn86|)9oc3_==P;wawDfBHV-*X}jQ3d!LFAx+bJ#`)O9y zY?8|sWFW?9nmInKNlh>?%=MXqlNg+mPg>b$bm{$@F028+)LDKpL8r#*{2cCox^me| z)@8SZ>x$0{$H_c%_&pST7B^a=Bv)*6C{`Wr8F{j*?Dz2MrwuF(Znh!>4U zU4al20q#XuIY}&3CqM{Zh-CCM?E4g%^r;6}42@}`X2<=POWKj?NU_s#PTRzn#J7K} zN|2E{=HtO@GuEwSead9n4M2Z__rSwJ*Fu}1b)AUOlbgsNjH4uSxEo#iQ^a;n`=NX$ zijD|y#-cA6IGaz#Uhr*n477qqz3en$SY1Gj)#8QtE@?~&|E0rI2kqYuEn$my!_rob z6_iU1Av6G+lzPtj+ywNIau#ui1Y&>CI{cQP#CTGCPFag7<`h9qBX*!DOsg+UGS1LB zoZY<$fi=vo?V2 zb}aN&vXJalEbzx<0bF-{`={hvI`8m$Ypdlo{)_J)^w)LjVZ6{6?%n$KgRlGRtS5oL zp$p{{IJgpjeFejcvO8c)U-Z|Pi*f>`zxwc|zqR?r_G{g9U0LS-RfYDAKJv74*quKk z@3L3TZrDHXKdT!;|&lz ziFlvS3!-Si8_h4!XSSlKYhYIq!?zluPKdl}iGhMn2u3a1{LdHH%BhH$$X+rpS~i?I)<9gadAtU8bp2>O_pO}@i)DF^{}wd9*>VV5>XI;B#%0a3g*P8Q9~BgR0t32_$hPSH z4vmzBGo)%C1s$WJ*q)MNvw6li_0OeX^SU7s7dvNy>kw4wqsRa%t^^q>1^vMQKWxiF zo*Ezt-3-8xLazk@10@Z=*z&5r%o#wEXVAgy9V$*{g%or$;YOGbnMqdwBXocJw6MJ^Ug2 z^TFZcezyPD7svZ2C)m`{afT7)2mA2&;BfER%f|fzC8_Uz#K!6~$RdPLup z)(-YhpgpYpeE)dwDLmPIbnxup^oM@-} z>_aCWL(7K;hfj{-Llnq9fDdD1hesJ@G|Nt&?ml})AKZNj!#F;{UhW;e_~H2A`=_Vb z)1zmP_u=8Aedyorqi6g4H5lFAv)zN|w6PxVKHvR*pDG?fL&sE7`uOVUK0P`(gi6`& z-s!>7A4@5as)zgB z5YBwKKu`pJV`DE*_I-~Y@9#c?=1#D_Ek9uRw*}CL8o@6N{EJ6fz3&+re#`g=2=+g& zD6yvlu%w7z2P2rlW`Z~671}${^Ig-ZXX~uHU#Izt0OoQaR|W8rQ}naJTe!p&h$TnU zVob^v2-^jAP`-)1(Qafz%yW*O_nDxI_dV6D83>Wp#+;`Xfg!r5#JXytQSxo2W2p+Miq0uZrP zOy835&97kEfEY%I+rA4LobUtzcDR!a%9Z_nQVw;U1n3V1Wbafz!EllVdc~5#s>TRx zfz+9ChdU1SzuA_yCrfR>{a z)zhSdTWwO2;HhXiJaxQz1*vK|v;A;DM%}0zif~34oB}mQ-w+@w{el~Nh=+xMmOPn= zJJdM1eJ6MZCNbo^McT#7&dZ|x&HAq zXQLs`8lHMJ-_!z64qrYvmxmnt%sWj}{9?a(r(HV_tqEajxh*UwjN9{=+8HS!upKTy zUV^1gyzZybkk=ZoKtxWTYC|0Vb5W7^)0)9C4Ir{e`DTN^V?QHDhDA?);x`!?CIKzfw?$w&j)f`zAsm12ssREh7qK}^aC(}j(^nM1{6 zIx)+EsiKq*#FL#*NOOeRM?8FPTOpVs57%W`U;)PaF!{3^@WrW4#%`oUOSg>P2zId!?F5 zq8bWj)&+-;?kes7^7$hf*hue2MWVG$&6iWnIdD~6Resw$7)BJC$P%9z2+t(B_PI+Z zpaVo06kbG#$=GZLJ}R{WGZ4UWcL`UV4hgjimfE`QBex6!t(C#;>Mpx$+_w77!CGfY zsUcBH!PIgrxD7;b%O3i>ZHLpVondZ(5rYAyn&Cz5 z{I1;oaa!R;J#Y^rS3Gix8e-j=J%>g^pEPjTK*+b28_{M;G`)lEiWgBbB(rayendQ% z)K1|)ZXQxlClbGV+6TUT@MubwNOq-0=6s0p`){$>TRM|(og(S2cQ|AyEJbS}EHxLj zHm}ecQV0;$>-20($t#!g|B8LBpYYkF2lG?QIH;? zHq908d2`GpJCW^M#829rN8PTj#ENy?`h0z7E*NNvk{plKN#ke^^ro|%*$d~1x+ zEa834I0KVOJ>*)yCZ~Fz0*V<-lOKr8kjtq=x43*)qwHdt&&L_u&=;-a2$F^9rVe&l zkbz#_cyiKE!bf`s+HTa@X>0X-f*G{aEy}yFtn<}+rB`rh>=1KLRQjmy95TX=*%hIU$ zQyM?*5}_kIg~#4%h!7m<{QMpXW||jr5nB4EtME4^xwQCIv)I5gjqsU!C~LoE|KH-8 zl7aNzlDZbvK>4IXCfD$G%wDfCdQ<4qT_VnL_5<{r^2lMs9c;KmnTRr%0TfkQ;pgd6 zo5OdnRRxe!D#&S}O0lwbq8LcPSOWXUQ0f655$VD(nt*|Dv&$vLu<kip2OdU}z)EU8L>Zs^ zvL=*dchUj*)y8~$P9KNbJMqk6<37TP(9t#llZ_K37pH+VZdvCxU))<3bR$a*FHRxm@H^NkR<0i#J)bOpse7F}UQDsvYM zOIjy9yQ17d0hm(XaJ!gb&S6vtdKry2Civ<#@uB_f$;B5&B+uvm#5FDntrCQ*V~wDJ z2M(xr$UikoBM2g*`%qPx;pIwE%=2F86CcS!B3AsW0*r<)coD|sH-7|o*+sE?+}kEa z6Dokv8&QDZ3e=QbSAw8MX;0UTdx+rI2T8=3@CB*FSiyy-&9JCijgA;?4x;YELK2!X zmIeE`Bs@r*BN<`XISH~w#O|6%I&LgtE5f7J)@Di<(6xXpcA|w1`tT9X2i6u(w)QXY zVrw_NoSdybyHfOiHm`m&%Kv7R|IH}>n^FFsXq4Y2+ZEZWNlB4UnoK&WKvNvL)_?(_oz3(6_>^#ln(sCkWe~Xqz$T z*a-g;b5cGOU*y@mKv(u{ug^y}x()ZD(?h;7)ti>Da6s8D4yF7rS_)DvQ#%|KW!vGS z(di8yN$e1nJ6g_PLi=CuQl!K~@y{HXXCUSUOBe9LXG>S`wBm+sz_M^mkHq;Ny1xWJav)$wG&rW}Mv7a_Y#ijnI+U`zHf#ZG-FAnh|FAmzq z(=69$G|gmJ#dJlv2wjqdKDH&qzsyn za0W^cL@2gP(*VHO8Ag~A0TAb*3FJ^*tX+&9rhlL&Yj27&fDjL}d-rsN{;@-Ob^EJX zFMfh}68G9CYe#nlB^|{Sq@3DQQY^5!u$H@t%!Pt(i*iAQtiUFFrLt!x?SZ~kLt@^G z$#s5HyC4wp;`194b`b6`$+YM)#x6-DEU&JLF^bOwFR%S_b9NcsKk>B_T56d+g+;CL2N*vDr%kOQXxr5tPpz1RuxHsRtinu&jAz zGqAQhI0nu#6!p=1O`0BS^jg$}g4g533yNRF%|^8@*@2Kg=iqB1K>jjK)m_Q42eaZy+e>^ zH1JM1d+lhxtQR-v1W4#Z=y^4vWZ<=CFbn$b#4Bj3GlUH|zv)PJV+By7lbSnANe7xDb;gl~Z4a3ebs ztxGa6c~rU_uLWn`#9V+F%PiTZkZ_<2=9Rlt5Sa;6MWc%vgUo9N9ZB&rTdL-Wc7g{P z5xajidWRv8lxeo`qtSRq!6?ohNe9_ekxuYyGDTPAc&rZRv?YB@*W%0AI9P9 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�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�V1Q>?r{QeNL}UX1|UJCo1p67tGd`VleS-C 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�odT< z_`yE3CE4~yfdat$j4vMy^A_vW4mlh%C_>64IFpX2zmpxO&u+J|`G&EQNC-6)eqox7 zXTkL6Q#?7HRG-=%DaIR!2D8?TbV|5Nbc7Lh5*pT8z`P7P}Q``IPR$ z$N<)ACZ}A>)Z&KuDLVKmyC-WZVfql-SVIb^?zk0!U7-z}uEO`NvZc@0ehnkK&u9(Z zApS;_gU0URlt6sJ^{j=qxszXz|M7=cvgbH&hcx-UMo4z32p*+P7wbZ6vxxf?2Iq?( z#i%P&ap^ut&!9T^`U9bZ_nPJTvp|Hzp%21odIeLf8M1x@+=YkjjmrY8PUwrrEzF*P|D(k*(4qAtDxWwz(TS~qtA6s|MkA2mTw+$( zOn7M3c%%f=YWcdl{;s+*%Hb7%rCVn zMSwWZY-0+bh#O=~mU%O`Ul4E`1+fDlZ@s8V6?XdGI3BL&@LpnRH1WpXcC0CevHPfM zKg2Y~HLz7$^Lau5Rof?}Q1Xe;^GS$s!WU1dnvuOhz6-jza_!<2G3U zCyEm7%1s6k@K86nDEq`G9juQj-MIQaqE0&4Rn%#mpg z?v1Gvb?2$_=OTZ!|MmTw*CWQ9s?Vvvw3hMXr-KZ^T|J?)S5G7OZ?M%LUVSI~sO>Yk z*(=XxGX6>k-Tja&&;(faTecQF3(Hw6LV)e+*dH}&K0cKFKI9{0Sa2oEul_@*6 z;|K1C`6rIigmp#^sx)CKFAiosf4(H_T-q|El{wiGvuen;O(py1vuC!ctbd?eONSB1 zlPziVGTuL`hC_?etwdKo(bx!bxWkDX8TPW3xM8ZDn=%h`B^=0Rns?nAtbtLt?dy{c z69}St1kDz(@~DGxElSHuO65zl zG_sFQu}+8|0Nd<~`-~0pa!xv94%`qKSDp=`AR!LnK_vI-1S7vienzdQ68siHMgC?7d+kSMfEvJ?4k-Ex#U!Q#SmjS7%Pb6(%LD9?Kou0O9xN}{KF)<;BF*DXH;?6-T~J3^W8t?I^#y0YeoGr~l)Z_7lyen|m9>}~DLN@Y&L6K44Mwh9+?&*jU7>plE6ERgtAU{q=)#&O3+#6JK=Ugjd=ZK zJ;BVqXGeLaQeixz8=HxDp!L=lD}q>Y9^)L+3JHpc&`|lZnNxN0d=_52s>j*5RJw-h zK#9jtu{YN1dP;uHAL!i7AG?81z^s~Sb;;8q`VQp=yew^=P1cdj46DzLMx$;-p9?dO zE!QQFT*(qBOpfW5M%12=9nPi|Tn8_(mU04dVNsRuHyyTmnk~Jl(H+seWj^32P@FTV zu2PUQxcHi?WkuJj!d}^?eVSia-{AVSeQmi&O(qBW#P2)HYD1GU?0UXFUTTeYk~iz8 zoH(nX=y#-@bWPrtdWdSMORnCilK3_{w33Z6m#(wgQsa$BU5G@=uIRX(sz}JbfP=K~ z|Ke2aOROKOUFwZ}@mg1eOYE9TzI?D{ej%^TrOI5#WNJ-Johcpa+;3Ou)~I!M2D=oy z?UlRVpy1t0J#qxNjcT-BJ?a2E+77;CTXl)h;jQpM5)EFglAK<5xd7R2>bOf%eQW;_ z%1(VqPCM44WlCrdVplA2-%t3*2%07@N27!xiv-=F9)9}@`WaO>3qyWaXc6-NxIRB8 z8D?xcxR}}5bzO!Fult#;Pjq}gd zf``@28S1FtR#h?DLqFbeiP2B&R6lJ98S%Uk*ZJbjcfa?#+L5rb7|*a;#S<){QGic) zE$|l)=0vpDbJHHA!fA#0f;I^TpzT9$v7sTCTL}VksHLIvjEl2o7~SbBDgq}tYelxs zz5XR(tk9oG53}LuZL$G|xV`Nj!FE|iqjD@0ndEGNLQQ5z_j;R#X_@^pl`<|v#3 zB#9jkPpEK?)SGSr$cPl6k{4W#h$SG8a7Fwi;+*4d^<4+MI-}OZ6cEZ73r*`xsCw(p zK=genAG%38@{vCXW2>W;uWc;KXcC0h(J>NVPRZATsJt~-RAEkzPvI@|DdoCsd3RI5 zw+c~##npV1gR&8*&xThNdFwou0$O4KohV24s^v@y6qhN9zzjtUwF%IM(!YN5)|89v z&~%oY^(*?^zSv=Kd^)LRT-;WM!?frn1GI2VyKAZ=>Om5Ja&XHFQ-QsR?&9PihC8k0 zzEQcgp`EsDJ)N$W5_uieqg8cURiNV>#Ky9jB(;s3@UNng#BocQ*{^*BRHUy4jShX6 zhw9sdWC}1Z-?{!r;U3ZZgQ#~wC>J^>8!IAhwIChc8pb_|HUK+^!IFiXa;PR?1pqfK z7bMI$EHG0I^SH+tj0|* z`@yyz~tSEj?V9_ny}1gB|OOlc=UoUr~YSoR;7g+_;$oZH|k(axtkD)?(K9kvkXtY1mhE7x3Ipv`Dxa?sLY^ zI2tF-fnVB)qOwV_-N2XcB#ZPZ} z>db70a$AMC8f3Iu?NOj@Kmge=yvW0jyWwXH7ee1T zBZr=-Boa7Q1_DNu#Ukzsp-yH$I55rIUsK_bO+t)rmry!V#0;2;&c&nIxGH)Ir9hwvMGa!3e!sMYJX;@fs1 zWI-jLHO+jT^*-Zl5e_2pugG2z@>hv4rTS5QeD8D^8`CY=nR~#r_15%8=8a^FZ6=M4 zqWSEy)R0d{#J34ShaQwA1kI*=XzRO>4mPwAf#4${1pF>20Ure^;NzkNJOnXdQ}u@d z1AHWKfZqfVkZ1H6jDUz_?}HT(S)A90*?){s`!q`90Ri_U%yz`vk2|C5Ro?6^gX!HB z$Hz6csJj*1LJ|x3nmr-|-x=#BNlL%}j!Sv%t{fsUF={jujSxu#PJUq%UpRed&h+P} zE?7spBND5WM{A`^IwE+h?42v=Ks(s8=}TZu>0He`KZI;c@7yqpVwy&q~aB zh$DA8wnB#KjH7x>_872fjP)L1507_-C9EdZNa+M-0SGP*Qg<}Z_IY}9aThO;vxurW z1`)oQlO(9NM&+|F@25i_Zh$Lv6uYm8*S+<0wXX?UM|)|)-j5#))ehdLD^eyN1%y=1 zet7k=)wo>{v6Og~?uT5|lG+}Sq~g@})(B2j95S-TR<^efV z2u`K$WB@pkJ1byE(f&@J97a3k-0QN>ihj%;3@gl4H?793^m)-MClg9Y47bQ$?(7jP zvY6HXMx(#JJ5Ttck_5J5i~$1DZ)KaBN5-k!6DFzj>h6T^(S&GLd2iMgwf*#f@WyF_s7$x&9QFsmO?6Oc zT7i&cYLE1@gJj215$SF2T9Mwn2dd1>#(Z)K{^{S-HYJZ^Tf{$!cbQoH`myNYFqKt~ zn2~)L$QiabX1VQqcRAFf53{n~k>VcmJXC=nX0_3n`kK)kNz=zeHKa)E;5E;QDchj( z>v6adQ-YgWGfM;2LW2D4v(8-03tY7+iR&y%Dq=oI>k{rCKhZhSEZq&8I&fy_xN7Ti z#^~$hkY5UyUL+k-8P-Rp+OiM&CeNTKSGjtd?_&se?za@>CX7O<(u+Br*ct&W>qf!Z zx#MBD)Y@x$n)==K{l7m+3R@LdsTI-;D(=S4aDqC#zn zz4nA&u{7NdPP6KQHgD-5@lRXLD6Kmumqc`&3%=qs4Y+BdM3F=n=EDT#&K`}>5~4^_J^RHq z)xNYi_#-`ce!tPkXj=Rh#52x}6EG;|{$@bEoO>K9!GsnO0ZkoDZ;58*E7%ZdUcKLEPISxWB?O#CMR?2uM!6^qhXIwM*SF`wNvTYn^^TFCG z!70^IB}rawKgo>IeczoTc$4EyI~xEZGMD=iiJ9-&IovK!n$uizm)~l8)$P~y>^(x5 zIv2&|o_gi(i;pssOX=f$k;p)5x4tMXgTQ7&VuH@yC(T55GlDUzXJ1!XKuQsBe#xSU zY0-km*c<7zn}%cR2FjcJG=OkP?f?!#ZmJ`!GSv&!aV9!n{>C+$$d1HC>#mM7i~lef zj>P9c*>!m$I2!XApBWJ zLQ<nsRDkNaY@aEkPPQQwJ?W3VE&l&~T18&#np$kSs5Ol4P z|EqB>yq?_UE-PW`a-(EXnmL-Bz!%RKW?6OmBdt=z4a4+Bfl%jgy*GN^qP8J0TIpAm zm`w*L6yZn#WI>x^trtbS&H1vk9^4xiMq~-vTv;G4IZhKdos>2$VG32s(&w0G$eA;& zdz-_~E&~bZf^k7{0L$w}g8MjsAb?N2)yq}Z2sWJM4U>Z<9qr#$hqmY?9LYte{By*- z)RW`-iagq$0O#erVS@|htA1Ma{g4I)Vehss*vn>y(+7y5XbIztk{QC(sE>bMK`jA* zd^oX|Q#DXV9yt{wUF!}y+JJ49Tl)@kk``g3+tg2TCMVGx*IG;x<4t5d$pjvDXpedO z&LGX1zJ*~f0ZdG~Ie5Ts%NfWw>_Q#Nj@Vv?gka8n-q`p?v%LtptGSkKwS;m+RA|p_ ziAg45m+=?46(vV(i^2L8xQHS+zY&sB8MVf%SZZ4tKb`1|ME4tGQkxhq~`! z#z$kzMWvc+))ntJ`J(2QIaDq-HnDLYw{uh7=)t@7ckVte=tt)_@74fw9FoXI`Hf4C z>mItO5`+xOBl?GP?hbJt^KY_c&`rYcgt?IUMzgR;3p(*4zifkN4t9e&|7JmXrC-yZ z6shuc-FkeC5-^1qhSAFexK=E(LSvIwo0((V7=d2UY>tYzWUOCeKy~XHH9}3bSopXf zGO5ad$sjc<{tDTNe~^~AFQcaac4b_{q8eR{iXL?UVP3s``)N9IBt9SMLnWWY2^BrD zaQ%e%?3%Tmv*W-m1ZCb8EYqvHEKVdQ$%XZtjKZG9Q!jly>QQH&^n`|<_Kr1WL{`aWF&L6C+0 z!3wLVfDnLkKN|jlCz-`>0tr7K)z& zS`K4)kHeubU`AD(;P#Q~oY>ynb}g^$BD=h5x`%r1(^YmgqrMVs&3QAKG}kr=mvDX- zs|ia@ZY!Q}3y$()02W?Ov&iu9=|GgRiCA6PP!bc0@W3Z|HZcspwK?xz_ zfQ$Fu!ro-rB-5#x8z#JhJ%M34zhI1MLw-LZk8S-PI2fe68RGe|XEdzyvc?^zDI9*l z5VhF!ONL#N^b9gqFm%JAzOtx0}FU1oN9W6m?opr_~sSG6W;e8)l!6C{FhgZ&&5C7CLv2-cc_I{U%uD_@%P0%h&_z z5|3va)_Q2)IoyYk77un(JOybcC>UTW1h_%j()7m9`u7ClE7P2xuOTI`ra*F87esVU zD=euxrEqa*TjhzZ#*Z9`fpy0x%d+{2>GX=1%}++30;p2k%C(o+CFPm zGl$%3MzLTcfwklk?*aHS$U}{fc)e@1@#Myq>2c*6qG39tmGWrPy$u`&g&6);Qo^|x zvt{>PQCE5EN_v-dtXE{vuOlTf)LufG$;F`7#h;!YQHr5c4VGAy_GFhb55AsXQD6^>Jv)P%3Y$Fg&kY^>PNlu%Km zEAF)H*z&ejb9`RahIhh_T8<*;Q<>E_v)9YZw`Q{XXQzhw#lEO#ddKS(>K5nET(04WI><&O>swU?aIMKl|QNKr9jXW4)3Od+v`xncc(W#rq z+DYWvoRD*ny=r{r{rt$oa=HS-TQzGdrx_~lJ+@jStNRzE9FgQW+v%82u{TIXT7pSE zMY4+w1w~Sn`yc8e!#HvU`6UfC|K&1A-+!N?k%a3mwK!9qZZ=X}iBQ@_)J zg#ZT`0agz6cH|@;NjPBWZndZ~icMFmlct$l+(6itDx8~aBQgCk${?$xkL;DK(NTke z47wWTl%g`Gmh;z`&X1REW;%MUorRH6b|{xiukC6%)Od0S9+8_l^YByEb8?S4w* zs*zt+nzk(;OI6c-b#x?%fGF4`zyU~0Y?r?K&3I&H_W@qhXQr4b<)8r6u}`2Ru&nsmH?H^po@#pMoNJQn;ZVTO=xBBu_B&gP_R z92%dri@eM)3%1cf>ecxI!cU^)s*l+w&u6NWdO?>b^e!Qr6>(Ll8RZPv0Nw|zH*4|cR*T@nm3%? z#W-(cCoO~RcQbnpj>}?IY&~K(#OA8F#7kWyZx$Uf@ijWKgoNpWK#ijMALmVzvL_*^ zPx86q#FGhX>LFx^h0y?AMdRclvM?k`SIbtz;sR@Yva|CC*!-*+C%={@$D)->M5c!x zn=(&7qsd}9xr(;0b^v*?qB2zR$c0G*jXaSYWyq3tfeIfu)5erTV7CQk6O4=RX?^vg zp$MA%;Qk&hDt6c!B)cBHw_&$=-_Lfox3$@6^a`wrYKzD2?OXxA-EsAvbO>g|p)?=A zzi0sxFM2CY;ILbeIHvg}uIR`W46+)c(v`kyte0r}uFJ;Zog}4n{2KC{Fs3f{ZsFEou<8?*8O*VVFZxW6L8&|PGym@KxDFr~g}Oqx0XH+D1@)WGbFkWSOd7xKdXTulie z{U?P+Mu;T%NfI6Y|@6oj{;4yXJ~f%IK89JezPuiPWSHX z@VQY3U5VVB=}GVu-zwZjhOdeyom~`@C1{pFn!^i9EnWMplBibqmEw!g5iua3*Ml($ zfDYFSJmr)CZ*=~l4i_yiA3H!httlL4+$|c~_R&b}j^+k_Ge_varup;(I|TscaC(Wl zNq2YUU=PWzA~8Y-tlkEN)zCK>S%W=f-C$iMyGVxlpHRh)3et$GZZ%iZQ;(YMg<=o+ z3C1{tAFO%X>4!HMY=HZ1R~r%@^+=S2)E#u06{$NoBBrau7SN_(X)#ZVI&Ulm`im>V zMXyu1XH}u*k96)2)eIi$`xvAi;udL#bp`B74a8cs>&uARf_zD0?Dqll@(9p^)bz@= z=leLZEp$kxpcMJM?K^kz*e*)+D3T4q?NU5lH=L28y|%qZJ1u_|)-r76Hg12py;m5l zWE+DBR=jUzkFV?|c6YA+UKo1gjhz&6%=i<%)zqfr?bo*H-Xl@PH<@c!)Z=lf=Y8C` z9^IoR(;P>SqaFp{o0~$)H&3FAeI64qb}(YB-%+p6m}=4Hg^VlV2N6Z*iQ z$RSz$Pib>?V%25HsG^ZxHmR^an81;IS!*&w?eV1&CbQk-JMcvJ8U?rX?_xO$Xl43? z4P!N1J`E{C(7p*fH!sk}g7#2p0A(!#MrN8572Tcmfr`#GTaFPk$(FM@D_Y4xM%8TQ z;2)@XgE5Y?RlMOD<(Y$lq`06O4Ddi}=M-tf0-I#*nnrn+$X;eeOcqKm}`;+d2uo5 zpKy(8Vp0}g!~d`msboVTAdip>E1wG3|X%dtoV6nj{>@J#EY4le^em(hoXsTo#EU7|FABboiAYXxQGoF4 zW}K6_j(e1v2Gln$Y62S-*BUPvKNiNKs*DmyDZ(CJe5_8Eia9AxLuyj;_XM;zaI_#u z?$XAS5--?>OCbsEdJpfKPkB*HG(x?ruam2$A3aQ-%A`7WiLAKhlE+v9<@EWpKt zLy+mdC)sX$+eaB{Q3$YzHfRG?K1PhZg}Qu@lOsXrfRPTjU`37}1`q*{3|viu4o^!m zSk9_1h>}fYFe@fCV^_RB`m>(}6dY<(_2K5QJ`1(}=o?*f9$|b4MoR!o!%~Q@&Gf=R zsPt_a(7?5T0cIKLzx>#TGO%WF4&lDIZ`2*O<-h)VJBT%#UvJjGS;!BNvm0Z_ z#8Aq%LEsD5yioF0O?M4Ge+EVBy}tTrZ6CZ2`i9!enK~`U?=gh625UH)pwva8<;`1& zpa=l#<7L5&wbi1w&@61%oVw-!AdN+T<8>LZN+Y-x^uFWm&+T4cRZVVWASCebXnnpT zC(-hhb#@g&%4S|lq?=rcw?WaJ%IVmHw?A*`OaN-yT(*z(m$B-#ACw|-Mxo&6676tV&4 zmQ|5YGu*`J^|rF=x@+o19s9AV;B?+pCKi=%2L*xhtFwDqqjE{N!j9NH3tqq0hG}87 z{0Bk(stYA0j(VFo-E^@E#oy_=U>{1)0((xx^s?;oB%uayWev=L!{}XIEuEx^P7aI8 z2a_Weuk6+i7QVww63Gwp@vc1&A0@?>ueh~h!x31U;nOG?JH^99$&c)dZ~GJ{Bp|f{ zdZEE>Wz+_{HgM=$m%4PATzH3IN=iF*S-+sabiAXZj}is{BPjSJh~3?>Sq#njUCsRy zz0SXc5AnnBX7YpwFF%`@TLTaX3 zOdAO0P+Xobt?bfl@FetoQ~TrKZ69GJFra-aaK+~MbAmx4PYOgkrZ61E&8|Qw#(kYo z2CYaavpBx`@@phL!;_-dgt2x38JxB*O<*mE8VX1eZ|rAtI`xO|?x-8hCqh(3H{u?x7hkm<(TF-_7)3(HbGYdk^YAm=txl2pBW`lsfGqf?L?*7K{ z4FyvP5Ilo5q6X~TRA2X6q$~$NW2YUzrX7}>?8&=eE7o7VQ7qhqWK%YTH{ShOi8Y%P zgVy$IOi^}1iRoeZr*1p40KL^0Zbks25D>-cP@khmI5e#f&2WaMLA~@Mub(@}K6mFR zN?+kR8imvUV;WMcuWMJ4`r0}H-h`1ML=%|@n2HZ$+OgjYifeN}4aH*+wFaJ$ayOj>vbDG^Dd1CU_)7~{ z>f&z3ODelLy$!$o|F_xDhh;qh9ZZB{P+lvDGF^ZMzJTj12n*Y1Sp}^_JnrJ(#k&M! znZuBQ7i`8xQ2I8u?!fp>qR5_86gIgih(EzE@zi3gk)A7=U~JwZzu~|i#uiam{1X1` zLq1dVcQLulZO;SS8cs=oOp3P5Z2i)Z(rJ-^?G9wuS-!ZcGOGCc3nHpVYVxR2!){jC zSRkZBfJHEmJ|0m^ebF}Lpi-}E7UP*nco=L=LR8E5hsQc=BCp*8Y=d?_t~x(=DH@W< z1K~af?SEb?sVLXVWF{IQT8Km_00;u$764?N0Rrmw0QylVPfedv5Ch=+iu0}tm1bM0UpiY=>_#7uy3_=lEjoBvU#$?Rn36w=l`O!Qxm=&vW{*%R~Zsd@JFw4;ZAdObWb zJv=czJTW~yF+DsnJv?dYK`$l6g2G>#4!7_3A5;Zo8`vMnny;(+^RBk8U}T50LD8iN z@`!#*pN4FMo;{kKWY5%0vuC3whqO3-BQ0%4&<~f@2_EllRN3WbA3`=3WmY&; zJ)iyQwBN&2(%Ys$Z-5?feMKjc?{Ar24j1sg^;aB`*wODP#sfZMy9my)Jy!SptS5?% zr|3>jJ{F?eAp(K76`Zbg0Xt>1aUlfemr2Le=czhKfEzwtqc-DHGc8vb$frwis!es8jVsk6 zxH~SJoRNYw_81|yLgWWOI#)z{n8L4;9=J~k#T zrz3ZxtQ{nzNZwWir>NZuGHYr=wJOUm(%RWAo*)!7(6(lpb-*Y#VI5}3?NeP;^Uf~y zisHoY*9EUY$XbF<@U!hn!Om~zMOsICxB@NY?=cO$e~b2(Mdo96B68BtMb-!_JWjwl zJhJ?sp4B() z4m@1pjj7+R9zYTGZ)Vf70gF^K2S+kE8TQrlCPoRc3b8L_2>_SL>!DWwv?adVMqd-} zgT#c0X)*VEg8HVTI2B;E?#&XJg5E+K&4QcQbRw#FY%bi&H%{kbqW+5SnKdn6Or}t? zqwTCtC!iv{N8?a6);waRU90gnSE`lA#Vnlw#2?IHTUZCRBhN*y&bG|D>P$`MZgiJ? zJgH&Rg3&3xs3@&AYA+ywt;`yGY-0BB0zr#(3qv;li}I9NqfW0&d$|St9cZ^uY~s4) zydnb4UF#n!rs$eM^=LJrQvckEJMIpVA&y)8|QG#geZ{e))*fU z#26ep+U3|Z#YK7zjeE<2|KP~c&J9-q3YtLIDhxtb>(m(8A3x6v0E@c^AzLP>l?$NGya>YCDxE^@`; zD$`B&u3!dEz19)UtEv8Mn~VmF-*F_iPU|SpA*EhzJ`S#M$C1cdtz((w)#l?+h3_~9 zU8A*sB`%bTjMPPc^I_OJcN~pZv7Jnc{0>A?hsx+lL(1#gpXL+Q5=0>= z5r$mM2Y8_4rG{2c9l=$CkZ*G#ByiH#0=-oKXX;k#7wmCsqJEK*Yb( zX*zGn>#s5?n9PS$l@Zi0znPPZbkZmMHufb@51zdm=;Crdk9oCVNC<1XKnaP&3M*mR zZ!a@pX}QXk8}_l*dKzdI9kAOqqQlQH2j^_ZZhtG$;eO4xiVp5xxvcK9O)jf716Q=O zynDl@R(r$$s;b%D8#cAt8^*m7YuRcB;%(cC9ePt%V07qCt%@V2TL?5jnFHyy4Uh{1HfW1qt$M2D0m$rLj0-kk}sC>`9GOm2jty`acEj>5D6d-=? zZOKNCDc%elp}+k`ObPkZ79vytQ%L@_g+RY%ir62vz`V}qvKi;M7;%|jqsaj6IPjNk zLYuUoYrCar9qpIT$?7UvQ*zaqz{}IIC|>8lChk- z{0>7!)GO3sos~w3_1*5%KmQraOp6Qk@_x7bY-a}_;Ys$` z+_xTs6;8c<_9Xh&i~@@MHChDC5@+b9ugFG~`(wN*($}f?f0FOR;WL67gJ*v=&zkX-l2zf^-{6@# z+-pTKV0u^2o~p6^C$XW&*)z3}YSAv0c(h$K><YL%{Fr9?{@$E^!9Y9I@moi zs?)PfNjv{@HdMTIIn9SmJAXhIke@Ut$m zOi4f&E2zj>z*BI(QN=hLD6;~cuM&cwbc`#?KZy4~2iJ@mvH8{RTVOl^NWdl@0~2PF z!3IP#j7h-6QxM`&7nrFR^f74YDOX}FOOWn#@9#%1M(mP|?+FdCVcbv<`2gNFOO_z& zp)Q!7K-Ul(1#LjsWpe%{ogOPXIHxqE=n2)FTY(M~U@(EK8sh*`?OCIaEehFXf7Mx~ z$M<+^%fbqRTDVYxDDqpt6zhNf_%`02#t8G8>-F5WU`wDY_BRYlx6{Z*tdReq(wMS721a`~(XK>yZV$(4 zG5sA|SLg0;-ug|^92bje{(Cfz2ZIvBey@es?f-5Qpo`+~HGFFQ_ZT$hM81S-@NU%t5|&|cRDDNRvJdm_0P}NVV~Z8*r8-tCs3}znm81&+*abD5W6U$xyytLa5>ituwfp zsM8X!aYRIUPd|KQo`fUw3bYOE70BRs`eq=SM!%Z*MpxDKzGAU8pxI{v45b2kFR z6_?}HEq(;3nb)O~IA4G-ljIYvK5`9^{ zn5HnIa4i*Uenki{-}*_8s%1TH`wU9T+(gvMiUt(X@5mqHKHG%!F02P>-KbnBm;xMB z)yrwM%)a&OL38je4$7DCF^(3?tf;=VQT9`Lv0N+^&p)m{qPNfHD}Hj?+1!4X{YGuw zmk99Stqwp1N-Z7Im1FbvzTUdLjRj zq;BtCmvox$k0E){arHSb@5B-zR@`k*QNsj-h-0mAbXjkWEb`Rv=84;fy3G`qtHbi` z@6nfc#G;nyEk-`wx1VUclWBF4TG#sKUd(}49Gv3sc9P}g_RmMXSUdM&2U`GZfXOe^ zbECWjw>l9*QK#3_ep8PD8K9ozCN55=A^wcGUG+Hd)VqKXWcA|Y^QjZguU?FJfWAu} z^`I)KnotbJwI-GqbKU7`(R_!Z&9AqeIQ}?Q)$h<&ip1yLZf#LXU(YfW>j)}V(w2=_ zTV}K{ezo1;3A1U_*sk~+zuE2ZE9&U}b`KpLe4CG~l|*GaR_phKvJ%V`Q0#4S;gy&c z7ii@`sHL>Y9VL@2NXpREyw*)ZB{JizsY-h9v#^CrN+Hr&8OucK8eFk;4YfR>rS9w0)pHA7g)Oki@a-gJie2{#miy^GoVGW&U zIv)cS4&G3e5>wlJps+Vx+I>g`)sCjF-sKpX2j!e+Jfnl7v!kD1e0Y0!e0KQazW`I= ztZ0tNAgv1b_H?PY2k-y>`qkO-;o1JjkNf}Rl5mO0l{|_=kl7azuZ55{R&HE`5c1KKys${KfizV`s2~d!;i1ANoecD z9rQ3VykTS%L($i%Fu4Rv;6B)s1;bScAKD7e-0TlqATl}s5vlQ$!{uVWTyR3$r2}k4 zu8e4P(4nOq|ED1^F^qH@wDb-CNw}B%gZ8$4!$&DUFp#A1!y*Cl1KLuk?ADV4b$%pm z6X!T5gX33?;G-z%coqw%UY&VF_kd^7dr?`lH30X+c|RQs!c z(#vv!chs>#>T&@8YHmo7-t%dZXK+budOkdoF1Q=dYK-@6!&*6mUGc5-qmOFkHP-Ht#1TO`?n}?$#7i|>xqB54Mo!QK?|wuFLD~n zA&gn}2b|umz?UE)k!4PoL*G3Jh956$(9Lqj9eAwykVRZ3_TfVn&Z2so|_brAvFIN8@59>T2|6?V8E4$>z2 z0vMxo0eGG!gdYC;PgBKd%Dj%u4ncVJT%x6q7!epsz1J9X49e8e{K+XVpNV6$!cDZ= zd%SJmCevJfECzAYD~Hd9$&NNc!XkGL7S0eL8Tw5}iv46bgMN{Nnz{a**CRz1=!^3( z7|jVF4;PneJHCZ6oEl=FVX`|t(OOY*Xdr-`ve7sEh!Cqj2E1m(HU-QBcn>@GGa8Kn zZm<|5V&AevNH^8F8Z9w}WM{xRcmbmrLe!BS^6BNaA$9C@WXTp&4i`$EN^4AXaFN^0 z^+>&{a|o+Vry3&#elE{^VTerP;8$lQHCf zutz+IV|kW0?#D6<=%QFCa!3tPoti(25<4Jp$h2k{OLzbS#)tt~6^$X0* zlbVq0Modz`f?LrEznm1FaCk$r?!(00ZNtPax0JJJ?>+ZQ10n|l^4V(Oi)MeQ61`qT zF2Zp{Bp}W6d~E}APHMS1(SaNkdGdI#q}R$RH!@_vJkLVj@4tSpmd$F9A=7 zf%;oFZ&e9;d9i_^DL{Xz=@QO z(?x_HHmGXl?4<<#>hy@X$Eq|cr^$I9HYtc0jXqWEZn`S+I<3c7D`z{2Zy`68>On6i z4-k}NYW0OG%!T)KWT`g=fJ>em)m< zM=i^V?a*q#Iqfv-@fREN1`O}~jFn!tk8_`!aq0teJr@#)Ml%cMsACz#G@XT4i$+=t zM=xXVO;ADdt28Jpk--!*R!5Hej5Q=jkyoJ8dZ{UOX!K$tz>PW2aNdUG5#AJtnshao zhz1KVsbT^2-S*pg(8mQiA+EEXJUOMtv|A8@W*fdv?-qp;>L7KL?#6wk2Bmo3K&Bgz zIOy-vQl})tBqO%3k*STIeC~7V5XiTzu4Q{Ra#RUx5CWEgSaNPP_hMbnQQBxg&5!75 z;5tnix2j9oCb=ali5KrQMC1Z+ozeMq(piX+CDA=q@(cmNUoDL6l4cnMte~1FxdaGk zyi+CTik2Y+bqYE&Bn&cQoe#F$WY!sB^e>C%N^WHXt@#=FkAoWP5>(_I!=vY*TvVul z>3bVb(Q~io2ii+BhOIfawF;027xf2_ztX8MS3o{zz{AnyPl)b{)6)P&e&4nNl8E^A zDABbXLr8(dM?1ScsT7{wfp)eJ1B+%3^jI4~_eq{apjl$H?ReQO1{cXo0L3~EChO+M zXdSP#B?-`4D8CM|wpF}=$|s*r2VIpS^u*N^O^_hteN-jYodA%)C;01h)N4t46;E@u zNjfZgR$aTbWr4pAIPs;6!|y-VpX}4{s&CQ&0DR`b*dF z)*p>VgOTndqA~at?=rVWE#fX4B6kR_2#eSdJWKgU6Y_Pz>R{uqW|RVtR5L}XC}dWt z!nRqA`2d9zF8>g_$!}6fj*hLIyN(I+D#Cg7j2O}&jyp^)Q1FtT%cwdq330QiOg4fn zR}zvtt~_~|RSy%mCX9UB5%SLHy$uOsNZ(7|9g8fU|ge>TdU4gBr=!PNr z2v$&4pS3=N4;z~}iWs7;VgvdJ-%h9Fo8|Q+&lqfn_@=9ofX(cD)-!pq*^}G-yl47E zv!(5qphwLKQzBd-)sp*EtsbM>kog|n9l2!DLJc9ezw@VsoqX!vC^3Pu{2Cf1NSbBD zm`zVH34`mt)61rZ49oMuya=*eyVcZ~od+#~SZwNT_4LFyNjn<)nY+UihELqmN~7lRFj5Dwp!*1G`Ee>0&#z*DePaP$951phXPfGqP- zk-)+bMC1ZVgG38IDbfQ<4C@hk>piIP2~(p{ZFKYmlrINZ3?T+}sJX*1ksQrpovH1I zYVts7qd62hsgb?2Dh(X6qY$-B$A@(B-kN)t_dM_?T`pP^FL+hEu4DFO@hSzkF-X=U z7(aTE0gDVCj?@Y*P*#gx(m@)$RFumdlZL<1!Ow6{B6OJr0AR57vmEEqb_n7h;a)f` zbp@7z>RK_%r8Q2Y1DkHF{E0E5|$H^6@7D3}8$msJ{tA?s9TJMf^Z3Ngvd^ zMj{&M+JfshK^GGQ6U=k^KCr8Fng+7TH+gWBLtkcLy`%hXZ0WsabL*@1!&^jAx(yHH zwZJ^}kLz2p<#(JcDF6uy*d#G7z#&V~9#?M@J-1|YPYQ+Ty{#%5!#^VkF~s8bM@(A+KTS{m<5 z@e%Er(NW<D`&0>2Ah3N7 z+?#asxtO!)&UxO_3jm2%=lnI?#8+hh`jRv5o0jt}z6jNG9W+R_LDC^6fi7fnHe(u4 z+)e6yV!9N|lmQ{1*9z>5mtgzGLsy-7qvUX`P9U|MC{kSK%pV;8L!K}}s?fkGe=SMk z2p6j)bHgOgyp_L{)th&DEs14d?x5+9c{4Y)WP-lU*zKH-U8N6+Tw z7*=HEfqRFPue^`I*$+cgo}-FGQ(mO#W<-XC<>&V`V1i6qR9l8(Y>$rN9iZvhz`V9+ z%8yQY)pgJ9W1}Rkt;!W?TW#bw8{w0n-O!NO3|efCoKf~UwY^5$82u<&G0V$_vcRiV zdfH|}43?-%C0h)H*QHNW(RF^>y3nl){&cWzqQ24w7wH5mpVB{1Bb_lJwN>1UnJ~MR zMUl_kZ4(l!MrnNje|*iFFO>_bU=)mvT2k#Cboh+9h4eZXT#k>%_^p|9g<2l z9WR!OyK!1vkxWW2_803VV26jF8Iv1fR52A&=PbO!jC;=M)fPe`uIlEB?rZS2S|k#q z9tCBz7MX{}YWram?ckPlr?lWvw^lg-sfm&v^wEpm=kyUka@UqCW|PjA5><=7z=`#R zA4}*2x%DAGo2%3l36G%uJELmtXdz-=78%>>WvSJT+wAe~>$Op)2A3TzLfgdHN_e%( zbJs8$8{g3NxZT(VRBS9y^B1mSP#Itf`^Lpk7-b<*6Hecz`i?7H$;(zcDh^`fh|PBW zNN&Z~pgV|N`F;a3G%BTxp%#@;172|XtsN6|uq^&J-v)uq?+<>HZVS6IdV)nhscN15 zPc4Wv6OHo&+Q!4AS5}AfbX+Xd(K>JftSA$Z-4YcQ(_))u>}~BLjtX9G)-?CfL>SP*0JL#2eyX3c!DWS-5jtS<)h z2lS}An{0e4mWbPNOHH7}Z1rSm>$knSKO^SB>Lh7QR`3M^DSDi8FF~9mh)ys@xsWHr zKY}NdV$F^i!uxg&1BG8?UK4<9W^dSX_L*OrLeB{6>jn90TV<!1k-M5 z!qQDvgIPo^@h-Z z&y^72Zha@mSd^O^bcC1f&}Cbv_ep+m6mH3`Q`YV8uA_&}+wzGs;Sj<{4&LU%wPSPY z`R(XV8gpDFm-)3^Vy%hyFB2FzNC_OOv_abo@aR0e$r~wHZNAZ}t)bPqEev3<;Xc4Rdp!|bz&a0+@ zpih_#U8Xbim6(~?(eSWI@Mj9fcYqqy9B`uX8#GIZl4w>lC3DmiT8mc2zq9ha?HrZyf?Y1Fl@U3%Tz|j4*b4`D$r@?xH=M9z(lKk3f?fj4WPK> zZ-lcJP7VXF?&CJk9XMD#x^DB@IFuMa)a>+z_qE$do2rWm%jZ1Q z&B=|spIbcJBWlS`^GFzc_{}0z*mIX$r%l+_mSwc}%p){SUF0&^tK}0i;0He-=5#^I zy2Av9u7PH+Q&0BQ!#>XCF^`i>*y1!oZh;s;S|3_73zXw(+EYjG=K8t zbeMecg{Np7aGZEIz1X!7Y>0;l5>lWCH8ybWkLE;ZnvOizDQkI~yK7LOl% z&o$VFF1Ak(laO0lX^H3S45am3~z$e)|k;{WL9(}59mK;=tm&6c_&A%P%8j%Kt zDq~GU90Wmc4m|} zw^0(*G$S!t2~PF0q#FU;aF6IXQN#oYRun{>m(5ZM_y2Wt_Ic?Esg&PQvLt{L_bm2may8F@9k90K`QIBx(b(#@> zFrl`iH9R%W=^FnVJqEAX#{+oz{Ch>EF|#{&4uZxaEysCv2?EvYn|a%ib%p0LG4jwhWk_1~f2EHRdD)YsY4VL|0B3 z=gnWl8y^`KGIh!HT>5e1ze79TZ2<%xe~1q52JKK8gzgPsDh{pC13@x9dyTn#^yxZMTnK70mx`U2m`R3 zEFqsa-ad7V3OfElp;2;_FAUP&x$pgTPJm?vIoMi1y5w_NU8AG_qRvro2YR{2I2Dds zVG?On=v^lae#ub*QOqFD!PyNSyP)NRENEaq)#D?@2ByoamtzX0MpqhIHul|C^6G-H zLzW9zVm30u3h0K^@}ZRJ-5#Ace`d_MaNPNzZMUBYNng zX5<9ioEz<=2<34pL5A^^q3}mnHUFyslQYm=RD|?Z?)DgYg`y@UWsYP7!6_J)H;AE2c52ssDL{rBuf|gjcLR@Fz`xSL z1DCAEhJep@TKTFd3tg?$$W!zE4M?YcOQGHeuJMIpxMkr59CFa25)zuR;xu^;$yuRrQL6d{ zd)gxJ#}z49Ex%LA=xi+X|MCJ z!5O4NMf)O}0x`-GnXaO@Ie0ylV5X@^OyrBIO16_{ zuS9CjAm)#!Xukf_S88!?=*Y63%0wo9Sy<4mSQMpNqDW943k#aM_c~2Nnq}>pzqSIh zHvA2btqG`JVA>qtG0iU*$^VVN)J!t)riQHM5%_P_gVD+DCNDv7%~{NAmKQcRyAJ(2 zH%}-XE{NUfgoJj?42w&H zDzFA#na-1iv}RF6ca ze>S6<7r6%$D#=nxKH_W9%|I`UBL>-%!<4t@i4}@4F3L*#=&_=IoRp}A{q@(Rue(55 zMZeY$PX`t(?Lm;u1p`b51KpG7Pfxy^7O}9@8e<_bK+W5|&@s;LV7yz3>jpAq@M$yG z4smHle+rwE8}z)|xe^u7TNvc&1bEWDxG(r9oCJgax9>5${5e0c{C|tVPRv>)tdZ5MtgkR5!(p>i8 z|U(QRZ6WeeAs?7KyY}&x}dUqm(um+fctQ3 zB-tWGg#Ppr$TLO1BOEclXolu;PiLCJ_|<#G42UA*@Dhon42P2NM$1cvc2>7P*$`qF zqz%nqtVqLG#Be(9)zIDZoea{Uqx*14P3Ol#9v2+ba6}vv5BKl+3N<$a;Y6Yf9&j*W zw2_j);9JnjWD5*fCLcxaj((>nrvcU^Y;@q{jx)HtK1xb)xN}@Eas=G#fVDZ7!q|*CG*hdyN1dpw zk-ggv%qj{C=5at)^Dx9&7O;+s!K%k?q_pkOS@!VnurJ^Pr!Ln|W`wH&M_LOA5Zsh$ zpB3P{t8Qh0&jqDP%0S>Ny_%D}guKw010Z6xH7m_l0$k!;hvCcr3_m;ZXM2jEmq7)e z(qy&0HhdI^(z>wXUeWav#i&#eom@*aq%>&lFvOS;*a>ncFq?n0+5;M9JM9p}63Ere!#k3)3tke1FYuhkILf#CTl5upRP-J>Y*#4#IjSqsMl(!ocwmqa<~VYD!vQ+QCu8{@;%UOnyU z2LBrYM2|9(qSKxR<7+dg`5Xz+@S(+syNI`ZAzQm`SHV>qXy0I*yZVoz%(`f{z^?O4 zD%Q-7iG6$rjiA&@h1DD!YOH%bo6udykEnd@#J@F*mse@chERcSV&rLjK2~M+7kyov zQYrOZv|>2C4GsAm+iH37j{f(*Sr(vx2a|*LjaLDEtXV$$rx;XQWNRmB%nG z(g+~R+_`(5J@2v-zyM=r@>?)p$Z3g>8v_P4P$_ z>?Zq=Po+$jSRLo2q=biR!VeQprlK>}v50H$_2}YouJ1Hn>O}fXBe#FNTz2*^x)SZ9 ztx4!r(Sbv49(D?d5{ST{c1AAO4pbyRasoz#`L&)UYTi7iim|&`GA- zmsk1tvt|nDy9Dn6uH*7j-(QGW3*oCi($xJECLXY|{CydS5qAaj@ReBv!kn4+?L)#k3+#iokSrS}8^&0AS z#p?-O~<^PaG-BCVrzMNq1D!UH_@8i(=+|cHXWwCtGFfwW z)i6RgvMIe)BtJO9cY>hN7->l4ThW8kO3a6Fq&%d3sfu@ePR^Q1`7*s#ZFj_R zPZ{q5Nga>}jU?d+1$gqjsTu#OZAXOrSt(qgg#z{>BR#J6@uYa}O)IFROzOnYSOxKGa)Tf2?xVjnozJM`Da|23}Ko!;N$X`a?UPG6?w z7lhvr3O>dXXOrpKn9G=kUqY0%SHniHKE@zy-OsB!y{25ZEu}LmpvxZ@i)k<%QG9{# zb?J9imcK|l#-ct;4%r;_(D!dzT-^<}`D5_3(e107@}z_-=F18LOcvqD`1xZ~)YMfB z4Xf2@Q7+c?I<_Ts19_?T>NwvI+VNT*aY6fG%k_1z7}dzC{pF(i2v`v7mPVCB%!uA# zBx>>69{uv;+41Xt9G~sKfBDnl$Frm3{g1~9str^Ny}f9mcL%Rty~QYkS&?PayrtOd z_phJ~z?P}rwDd_L{1Hg?PPW!b=4mv5ku|Gwd|2+DhDE=eXD-|hkd&fDE=%APZZbPj0U>-54|GLs**+ROZEGE z^D^wj_@XUaMXok5Xa02J!|Qb0q!@tn(9slFX?Eg6#W%&j9A6wr)TN~Gxrng3c?&9* z=g3V);E}F6hSZ@$$~C(u(`7J12082ko*nq|4U?NxQBG>)M%(>7RR?WWd>P?owQrZ` zy2ut+yGb7=0^cLQS&S&&w*j1JwbD`%qUahXf?8c!gFFx_F;5r14ccvbyHI{rK%+*Y6jQ42rk*~J78MSV}u z{0q*$cWPvK==CQW$C6JqHRv*(@N<1%gq7;|3`_1Q=}LTE{8_*Xl0cd<* z{R!%&0CMm8r*wLWC*$aBdYwE@p1Qmfo)?)`OYab*e($Ci3EzES`Z55@iems#s(-Ek z@8^m73ee)5M+oe9X8@Y!J`dy%n3u;vG~j4nRE|*&ziQqyJ(xa6XVv8;xXN!Mx7y~Z zMGf&`rAP7^E5RROLyv-nZr#B2=&YwS>uZ>GFyYquhI6f9w{^|-E(5>Ql3Bnr+yjaV zK_jg-m1=_ZDoR@Iyk_r+M$>Z$ssx{p0cnFE`vWLy``{39?qwdJ7-9SKcEF$G{v z*N8^*v}qKNL=88HPMoGidW`e7d#-N?BilUkQ1#yQK{>13OmD`u9P@jix3yUcgyVuo zv!bpcpNw3eQD|vTn}VF2FDo>jV$L#jQ>AH9&I!~Uwpo@p9TF6HCf_C2FfJ95M?|J3bNiMjA8!^${VTf81+{H$RA3hu&9UQ+tJ34sv z`h-WQi?_X_(~F}@(EuIGzlR{RS&<6wwFMlA4!;)MCMw}5dL z2cUYdX85M6Ux1dNZ@xQf88p0gzg487jJ1l;HmC@(_3icAy1LBOp0bG;vXI7YmgFj@)o*x?)(?}wvRV{2u$vW>!^9R-@YPeC5Q9}Bem0V zC6W>qi}u(dybok;^gb<3-4u(yB%YRH4xXa^QW8n3^`M|#m_5@Z3tT#q6oXJm;j@X) zv=vQqr=Xg$k}hvjbC-J{(~7{mRm(enmx(5W zZ4qdSK7xlybi0Y6;I(on2S86+>zVuOQ-F-ob^WJMgO1kIl6D4}7IVVOftX~# z2f>RPWIt&VbtsD*qaav&fY@YlDdq>6?8-dp!CzpJmUCJbprX)=1By$)b&sSsgXcKB z@PK`>I>@AGdj=f=3IzQ7A;m9AqY&v7qWH{>w-&Vfk&<`m zX*uVsI$Xij+K(XKk>1{vAe6}&_I;6;za{=A+6PwEuyWg4*|zv^9>GeEU`yBmOSM@|b^Tk_w_W5nYV8>K z-gk_`u>*hVuOe#y-aFuxVkLk(4&m7f{`yJ0#ev0*0j7^Pb)I+ilL}q3=;lhllx~H7 zCo40NoAwTDyJnf;(J$f=t>l_Ma3UM^<4oy`e$+J)xS8NQK>ap{&Px3+bQ>Vs-cVvN z(5OPz3_is`zS8=@BQ3e=LH73iCWKPk6tjKyV;V}&-HD94RA*gzGUkv*l2X1u3aYwF z%)$Ejqu*ZF^Bc&=GEK=tMHldy2wZtul@myMfyO=z7?|bi@rsHJb&xF}EPx&!qTP+p zD>rwn#30!uxf^GwVeZe+iDCposv6#se21xZI>8-v(8C0TRp7}SSr1VG03ifmWhB2KQh9 z3M|U(rPi_|Rw2E@R(epp2LjKK?NgqBCwcR8_r}xry80pMvoX$j4%+!0uT}4~T02Uz z4Xz(19un?2AGz19zTdSOwf7=6Z0B5#SWw8v12kV)q8G0YheAhPwE0HFBE)3A#)T>>;DfQn8qC2VXFIYOj zC4>iR4yS%H79%2n(*FT`6niH4CkwolC(B-#5l*xR{_m;=TcFUGtY^`Kmcw_wsE!l~ zMOyO=uLc`Ut59~@DG}@4ZH`^|&_-ezy28u)iK;Q0<2XL=mMPP6#OIAeR7a#0)||-> zv=k;^sfiCdCvFckWbB)&Bk&_!vwKgs{)1``oi+KPP2&P@$-1+v_{roV?JI5z|2OCj zVqR|E@aeMJ%V31C41?A~m`_*^@`E$?hQQp!sZ4?680XE0=ukW4$fg!}0Apo5KH0d> z$EP|2$2p&#D=al6f1FD?Pw|Otj*ii9JUi7vsSitM)=z&;R!_p`ZRP2|U&BX_{NvL- zELV3vELRTK4v*EH4v*E{ z50BN_!(;UghsVl4JXU;oh&iKEG2F#ovMRR`9-tfeM@qlw79I#!eb2tc4a0qy#6L(_ zWbgU|=_p|?zR$y3Ki7-VQTUkYh!VeGXf+43f+GRAyQC!DR>E~J(FpeFDu@nwKS0d` z@Z?H$L^m9^wu&kZ82AWEO6K#G;}%E3Xn;gY;8RZ{45T;+`zB?tsOVMy#bo&K;Dcv?Ejmj}`T@_OL;#~YWQI@jw7 zvwN6A06obha91#GnBHuwv9H+rmOrNJ#&3P*nLlIHs2O$O+Xyas4QD7I!37rk2mNPG ziGfgF;;dKt8^SVgv${cXAn6`Q1JwvdsDk2t+ty5d<_Xv0&TPxCKnxqzVLfpOr*2xc`H zPKF?gS=UtCq;NAI-LBL!J)dfb@!!UF9!_+cxnJS{8RwvR5v;0s5pm;=Q;%ZJiR$b_VT8v zn*{+Hsm*zAhIpRzF-jgY(`o|HycMJFDSm%JzOdM-LCe!KC2(h;InAC7{uwug&;rMd zo-4I#2J!3?2Z+FVg!DPRYq{+FC9qLos2F*9}dHkUp|(Hs?&vv*4+Sp zLbPf4*t`%nViA^M z&Ni{5-mYlXEtWyvpj9McsS{SX(P$+$sv#S*O!#QyaJH)tsiO4)uWUPeTNeJoQj*## z(MV3Y2V^t!QPmxBq&==pG{>4|U%!9V*66lvwRWWAMxE~Ct3JKHsq;(O0s^)4(0RAF z8{4pMdNgdhsaCp*(-sFkZbzQ28OH4z(u*G*8_{3< zT#5&L8`+Ye=}`s1-C}D47c9A>J;g+P8l*Ns-*CSsk=@0>_-w7c)&+Er(z`7jp3Arc zz4bY+=B-n~N*nCA_$s}_sQWuNuD)Wdac8x%l`HyGm@)*T_1h&M$i{~XXq0>OFX3F0 z6%_FQ5{zivHsw1O z8R_3#REBLGDIk_YL;0>b5@fV?A%;T#1BmIZeT0+Dyo#^F=pM+2n^;V~Pj^%XqVnyTr_72v%o z0sI8#DD_{t!oBx$5VX`EXG2=&ffrtB2uO5ho;kgkrvs1b$d&%gmOO4U;E?-<#S-&^UCxGsut{KpDZ*WJY=bmIVsM39r)-9kQ zo#Q~gEFOz;^+U~w8K@Gz`%Qa)l*zAFtI4Y9RY#zQ&zn6tg3 zg>g>Bb#W>%F3is`@QJ5mmIKw{y|b)>3HHDrlRzz|`da9&@>~33_gLcir@q&hMt%0j z+{`dC6x65NODvJDF)FF;>0yYjacnm;(-AB70hApP8rF|>k-1yvx7k-~kR$HAeilIK z-kOM3w2FvM#|hbS(qeal;?SDDx$%R3+!hqi8|jh`Bi9cHrlDY6@6KY3RZLIZ^L8R1ZP zijtwE8z2R7R57@%H0v3EbER(8O^oq(R+E{k?pn8)DW=p!#R@g0cWM1uXwPfN>QG}W z0q1xO;F;*qUJ#_6v>I(L8$cLB(pNW6`nChK3@FKcx(&pUiDamt&7V|5sI+WyHH;)% zB4VjNRB{lw=m%Wxi4X(LvrmUkPMA*ZRRu5>xOXA zjZO)U&_#MMkbe?$p|VS|Dr4X?2zHs1o=m2rqn8IqN9v}3{QBq^Q;lO_Yq@Q$FT*4& znom`s7>0!`@H&F@kqlC*w%@p_>I^@d9#wL+Sj?N<$B!pU#9Kld-`QgZeWZUzpPI)- z(=7Am@w1&j|81*n(|-%kROep+z62k~b>Gl!y)svCb9wV_!WzlJMX@lkP}MZ8Pml1Kat<1_9v2;4`Tk3?@fJ&OB;`L~gZoMjieH1lYDi>&Sno7$p0XYq27m_NIA zwKqIH^*aphMAw1wm_2ykfki}J3yr!D#&(Ir+QP>-hm=fjq*smZ=T%kaPM*NK+S6Mh z$XZ_mSS!V&kS?Dq!Vx4r<~hC$2@`8+aWgWTL!bvVjZ8;GiTn#k;(HN#*YlGWlsQ5K(D_+`sprg$@rTHs7VdU%SkWVrk-bzGuU zZMWo_+X($64#@1t_WJ+{SfFpu9Ns1~AV!fi9ujq2QieVNlM@#(x_fP5(oJ?9OTcoQ z@x?1wB0)9fOKqKR)2%DdMs4>K=o$rmf&v71JGig71HiOF;Q5FO*QX9aI7 zQNCqzzT*dIrivhIOcXoCGpO5}@X1)ungMTA_m^pr*FUVkF^bv5Zkyh$@%~kD2nWnbXKcgm z3W=KFI)b+k#&CNl*TZ=9Y3D$3pO#lL_{1xdUfEI+C)xwet+}QfRtJ;kyA(<#G-3_# z=8oSC9UNr|?rzJbi(HUDA)?3SgHiI5z6+%*sg{K31s(H;QL^rwbP?$62qXlLHeP)m zmB0SJKFL|i-&7HO|1qvtg3v~}b3OW!v%Fm1y>6RPL*zd20sOz)eex$tgb;%Dt9O6- zi~o+H_P^VG`sAgAtzhCvpFXeI*n`Y+E!l7&peaP!RGb!2cc8e=%V26 zr;gRjViGAl#ut!wz?8DN_CAm@1!5+SP3}pJQD)QUL3SsZXom4X(K?5bb`Lc`mYpRy z<)XMng&Um$jxzC@BgeYoLXt7mzHzUO;p~-38>KiW9Ax971~IaR_DqPb;+9Jsll_3B zXzIRSUmdRq;g^*$bOe(^E&wt303gziOoTT|wiw z!{jS0>uw^}SeshSL;9&b3Rsu#cx$*rzyrg z+ibLu%u)g+6EoRjbfB2c#P)Qy*0Q3Tm~~d2=_)~PG$$=BE%4V{fs@1Q`}J0*|Jjlu zyLw|IkSXe{b=qRv0iq89vNPl7F2F1QnzAucM6PZNLqsqETf;%Mr8e#pJU=X-?to#9 z8jU9MiYw^G#PpNbp!XvOq3fLXxEUwNokO}UssWW%GFd9Nq3AES7>pE^(CE1+`5$-y z-4-u+NQ5s8D82MYih*ot z(itfc`?UkgNFGIk!pv>pzhT7clc)u!Lj(a|ibn7`6COf3y5zq>(qH+By5Y8t$JA`T zSZV&s;X#Oah4UJPBcQy`3&1l2+X(Je_$Zvc5%eDVO!~nAEr%540J8xDYyMx)VI=mjS@u5^IAPN$#IJL&KFs-a~Zx)m^0Dw;bE5Q?!V zGer2(Zd|?}7ga{RDKgw~m8-kCZd&bmPhf#~XjQuB;RwOAV{=y9u3(>11-g z(rC~yR*e);TM`)HA@I%DyM_g&5R%pu7sa#yg1$`D1!!^w+6~JSF7m7NO982p3myO$YWjtB zn)Go%&p4U#IET7DQa;jX$oQ!hObH7k+gT1cj%xKs$-#m_+d)+X9c0+JS+L|GXrv#) z$J1Pu76iDou6VVaXf@Xjd}+j$FBahRJW&UO<*(%H4$3f}c8zdnWfk=;1WSn)QybD1 z+(ezg$f@4Z<`Aul^2T+}H(i=dJjCL1=b9&P^K$5JYWk_3y9)JSxbBdGaI;xnPi&)M zg@6e}Jv({&AXv0aK5(=R1}ZBD!vsaJWy?VGvf-FE*k4K-kL)DMg2f2;;i94hcl@{S zw>M~0LEoM2`?b|ag?bJPmpFSZQ>AMDI6G^41}{xfF+*23(Kub66v-dNpiVu?JtH4%Dw^~2wqW4zXYnKkZDW9AU zJNCnULEKF+i)NxuNMeL+43|-|fxjRG=T2E4#vnMR7jTzs=AgT#y9f&12rdZt#RmOd z3G~$hYz0RDk21#CC>4xOMSD}ZBEEYvv=EDxq-;*7dI7m>8%|urC`bstVEoK76AM9V zcHw~rHy7I$DN1xVwJ@h3c8xs7Kxk6~C0;s$5jY3c2nN=7a|eD3@=C+UIV^Li`Aa&* zFM|I}v5L*DM^x?m8fM%&93>o6s^ zBi3>5?+810_P@tyoc4VBW=7x_qmif<+yFPc<#8e*QcuH>48wOWVEufOpjXvkNcbC< zoOVfrSR&U~ECZRkDgAVyZ(Yuf7I1E1ZUFjG$u*fk7S9H*)_r8A;Em;RRWE(|(=}@- z{mcIj#SwEsbn-Nw5C)qDUpZ4zaZ%d{jSF+6_Vp;*ZajDSq)$&{JZlrF!V86UmeE0% zQcp(Upr9m}{^OIK?Z2Hq`oA87my-uu<8kWy*qHpTDPX4@GXc$49xmsa3RpB$VqRbX%ztZEa3vA0(_r=NW~p|Jp@@(|BlYeigz{~Xutlt<*(gH4fNRf zoWeci4CR`hU$~E@@!jF)P?|&PA6R6)P4b2Z!#0Bngi~(bYt-cqSY|DA(7SM$w2+7u zWN8Hv8R>JL9alKh)~QD}3>f*?ACm(_{1PA#WEzHRCwvd4Pk_v6JS}>QXD1G&%m3Ar zBe8rCAsoFopvc$d$A`kyb8LC!$~j)U@Ulw*0MuaSeFCLVyNYXz39KYrtI8RzqbhPJI8`1r_wE{PQ={dQAWXLFqG)UZ0HSxW|%KnK4`ONPK(lp}^6n|hBP zb(i!$P27vJTSa8I*aN*qy1ua9N_6(P_b;}7$4{bIBOT`r_hC-qtqyYYKRey`FVkYm z=2#930!xA(dsdJ$=cudU0}cRdKhj)Ri8-}8;hxBXMxeNV|DbZ?8_XiI1w`Jt}ncIu#7Ds%OHvJdDYyIA#| zaf?xILWy-X-=I-Vv>h$#)*uYG?+qKgRQg?0^~*yWU?uF@0ODXHbkA?oySCYYz3!BC-zz>Bx^f0Cf2#QP>48BcF!_()UAq-P0!zC7@Gr{sG;Ez2$hY_BQ4 z)ZeE)(MqK3h|9Y1xpsWF#(z+|kyz)!QuovQ8;1>u-O)uEAlWe zz)gIdH%(d>pwR+1{tI_GINnM@EDLQsf9&$HoW7YDUy_*LxaT7ej42AJPTs ztMp&HO=kJ*BCngF_TEG9lV;)en1WrnS;!s_(N>*K`_%Qo_ZqY-jJ@ha(r)j5ucPGP zQY)6xZwpUttxJ-gMf!Uw$uU(MDr3hR-5w@tY$C3RM$$AZXuKcs)V}4zyShwrM6{*p zHI#vcU8mF+*~Ol=ldsyLb6jPncdiPLl74Td2tpMBkM3m&(pe9|P4r)7MLdcA1`84Z zpu3IFnu%#UMF%s8-pEpLHOTU2To)8J&1oQz0$m?Vf9HgLmItLh<(n8EJmT_-s&LSy}0z6^JiSA*WB`( zsy2wT4e$v%go5m|LUpGK`8N&HvyR4BX}!Pb?+j3B)jR7&EMym-P^~vuqu*VpAoEX1 zKStgWLie`Tcmd#)*WLNG#tpk8qlrAoaqY}0-epXWDfiYjZ1i|tjo+@Dr|o5fxe6jW zF%==mcaxw$)K_(o8Jj&!yB+$)Se`}EwBFk-lzcBjF5sqfc-il1({+zb7~4w!3t-jEQRW z41Sc;)o4U4Nc_^tloYb|4eZlfZ)V!1jTMr|ewY-K-r0}=nqNEdiF;r)~J>Q$YV?)I^Hhpd*qs9*vU|G9KbjtWH zUd>Er*Zvu9+BMuc>hqO#IDARHgr?6d?w1vH=F@VWuG>l&0Zq8Qes5ceQNvxnj^=?I zukx_doejsd>;8<_Ff-V-f3E+67Gm5i7VSYg?D8jxObOLaH%gT!iFo+eUcCB|s9d*FGqwOy|iM~n1v9kJe zlGO(h`=UX)CodS5A0j5Wdx$uJb?i&TlP&I-43koFn5tm8IZlH<5Wn)Bh6Wo7HLV)k zjrcHXHXJdiy_`S7-RQXEis8Y=5_pVuzL3jGR0?eKLj1xmh*#-GpY48qN0Yy_Hl>3; zS>O;6pGJtgWsevGA8o$2Mvi7(rhPF>=Y0tJN*8{H5pU{ho14hj2t-g_w@FO_?W?kQ zr*rdtg&K7lR==9toahKY(@gecLJxR90&)%Wd7#kfW<^l70V)4jY7?)BO930f;Ju#v zoe-wUNDvk{!F&j`c*8SqE&UP25);~=QFO*_e>=$`8luSO%i zDc|mz*m;{;KP(FPH2t6q*EX;rN~R!q*>M?J47K-Nj;7JoW_jkmi)}wPE^oTnbcIuj z(!zLkK-WhEiRIY9Lt*}|UK_{44mxEz9^z3oGorm+_pViJ8*q*vk48L70y+-}g@moP z<_Ml}Xm1vroli7mCAPLkDs~8kxwNd+X5a4K6S&VxS`mr%L+ME(bX3HRmMJ#k-(=KXS(t0xiw59#;pmNn2s{eo+-OQ~rzU1q2GWI6v(TM*J6#8a{Pnz#vEn*>!YHQ@YIca$H(K zU=Z>#g}Lq~C%rk@*gnm3{P#M)fTY?zn27oZq_*u(jT*qIuuEM|RbNz-h?oAsj9~FW zs4-js-k5ncE6UyEiBp{tJl(&3Gft=ZU8?W+Q}WsaYsdIAASsqvyxp{1_54Qg#^W@E z7~LQZ0|0Qmp^K^l+{n(x2!rpT!2?<->QW6yZQ9AN30XnaA3q(xdz)m%m)m5Lkv$Y5 zrP+di0fB13u^yA$_n6oM8*r&O*pNkxJfh1d|Djs^UcN5~MWel-JHlIJXi2}xF{bWI zJ`i^0hL4kogGU;P!{osaBIAyju+3Bk z@mXlYcFPQ0ByTxc->g(L2Q!3dIjnyHV|?Ft<7a|-&?n#alIcP<3B%R~d=x`Zpop3( z@=p2@7y!Tm;Pu#}bnAH2#siu%AA+F}J<6m{d8|6|HBp~4*_j5M#BEn~b&VlCui^IF z@BKuD!NZaIWkf5rap%QN&Uh)!C~C{_heGE>ue-KcQLGWh3A9cgf{_m`uuxzzho(jTuJjLA54)#H zJ=a~C6|o$ER@s)|uP<_p(igEM^aqxSPf|?4rAy#%r+_?LWI_2{R&dS`QNelM6LG-g zX`3hGX$5$iiV357lwj8ZDy|vVIU_Oe}(WrpqT}8K%JOhDg7d3cRzM?*&Z(O<0`RM`{!MG@w#T&)Ka9Q!P z0O#*V^E{wc5eHa6{*Di)io&JnsMz<$pc$HgpH%d1Na`1 zwCJVa&=Ut~d^zvO^i_N{9nF`u5=F(w4FSC+_o{P}UN$<0g<52Ca{w8spf%qLbe)%6XJ+fC+a~X}5Q}6%l=ijha=CM{3EVt<6-#)X0R_)={Smjn{Zfna>)JcKs;ZU=*Vj~k@NWOd*JnR}e0%oc z}9V@A?`t%`$}DaHvfzJ zMr&o^)!*D#sNGf1|4PW}d<1^C`=>uzrFuFqKzptlb@kft5OwE&{5>$!H$*tU17`X? zz{>9c-p>9DkRV0+GdM2~Yfz15GDtD<#)72(qOo#Q5d|#}iCLrRhX8T-3MCv5b(A`& z;-RCQ{Jmmcj40M4v;xq>(`Rv_V2YA+k&&(i^)F%c%UHX=n!d~vPQHft3^erdcv&}U z1Sk|@tJ*_WH%lt?Te5|jCdnjDmTBbSl;F-WFJeQC@ z&NSywTWvc#V`C80+Hgf+1)yZ9vG$<(o;lm5?DxgWb-=ktto$-Ft(jNsBGDCZL{ptX z;F`Ly_7wxUF17W%LiprlT1_UozSu%SCX|s7Q@Ml~r2*=`1HNa@K%?7~r-42{*pCop zOp_LSp!L66J#8G?{c=;OYZhuPW*%-trbKn~qjvOjR80${_{as`a+86tzlSI*KJ7}Y zm)CAk0UDuJ#77m~o|c=MXK&Y5z52*oIH#t3=((=3caYuBxH$%at~Tad?@y^qA0H^= z^=f%BQn9H`eY=8JUp@x>5GGU|v66hBJn;prmQ}F)O>>vQUaOZUq_)&+Y}q*csB!yf zEMW~ak9f^#qpRz02374ODf^Ne`~^Xfpc)l;8e0bOrlp-e0<;ehS7)|dAih24u0?A< zWCLRO@Myf5&C;t${LnFsymF0Jxh7DE^JDMpHYc}f1dY^07aZ7z0&F$JDO1$kW6eO2 zCeI5Jr1l1P{r1p}f1T{a(J_OkCE~>jhA%@Iba5||!4xx65OFkeDruyTyQKve*-gk$ znD-Y52UGqgmnj5M;~Bz^Vu;5Mf<2@sD;5~gEZYJHWq?pZj~A@M4AwuBq$O+Q2D~#D zrUaXTx~Wby*>-wC z5Td7+fhyFPtmSJ~G*=OzkaDb>Kzh!}_uLuWFlmGKG==Q`HN7F)ByQs6F!G1PKd=pyup=RDL0qTa+bCoL@vZC695MP~{k9#V@wTB)V)1f2YZ2>|XN zSid2c%S&S%<9dNtKOPr5kZlUm%tDmQt{s8r)K61Tn-RvZ{=vcP=}!GcXNsB&cog=) zF-!S5f=vyr58B13&+Bk%3F)PAvkAkgZZp(?q)JK8GXa6(Ak~r)K=R@k0wQu$p4_OJ z7UNvnOy9fj=;`O6QT5A5x$ zmciNYgJUvLND=u(mB$zMmPp;2AfNCY2p7%hym(mP9XC$(0_dg&YeBo|0_D^S?Zrq~ z8rRzmv_d)vqvdRk0eA5ZHt4J<`%}o3QV4+Wh*5i{J^>XP(q8?5=&LkL_&exOez8$j zsB3tGnEgQFPup@=m&DXceK#!I0Vae}1qXCewu?~mBdc=$cean#!CY?s*$wTA#=Ah^ zZadNHR0B^9RoE8Wn}fxi(PGZjVv5hNPBr=EpYo@dYp{#`4OG?cL?Ify4ziZZ_H~0N zvs2X;Gp4Ra3`Nl#@gu3qYb!DB$(31K4fvwtZYjvQ6JoWS4oI^dBZJvMY?*Z^pq7J) z=>e;>OGE*x^4%lb{JqAWt;%#(jMtA|Zuqq`xgR8H`}n78E|+%HddSEU?g@?9Hk!L~ zsgCW@y*FZeqP1jXBebFi-Jkk5r}|Au{&ytxyO8)@>HE&0n*LcxQXJ$AxRa0q$H18< z=Mp8!s{)<=Cp)K+ID0gG`6y+MPu4Iwn}-QIrd2hk^s_97(+sY!iYXYo@;R!K>va5? z?P^8sH!-g2I&bC`qOnsRR$+A`n_Qpp7cW-H89o?vCH`>bqb}_ZHD-4PWGA6VN^t%Z zxoDr^?Ipar*!M(QJj%GPCn6l5P=(W0A-PKlWWJr(cbmX~J$^E*gS+l5uSW1mF4JDS z*cHspKq{y|L%piq$Dcxt?A`XUuX_cGUBh6+ed3ee;6Ul^SBkUwU0ulhT>?2It0c=8 zd9CEEobAfRBDn^$>2+O|>^%&&9NbQ8h<&{#b5pYYG}uTxNp#aek<)b=$+NjWhCjve z=Imw75VwX-`=k-mHt*dN#N%YKo>f1%)3tW}UD{XE@|wi2)DI$VBhC<_Z}t7!KTjS= zRm1v`deT()K>ME=AZD{w@NY@ojfVn?s{q)bLr*a2?9Vx!oopOJzpED1&BozYqZpU> z=y+mSp^;ed3#sGAKIMn4**Z3mV0ccNrM23pS<5*k-R0I6ql%%Ac29dxu-aTe6dOw) z#e+AH`mX_7y1)}zr{)zPBc6tsmEi21LjUahcFc&XlUwf6c5Z5a?J}%tdu*)UTQgW~ zU^|kN*^gOcwN292jp(3E&&~1zoY!K!&+|o*t9l~GV(lurHS~Zx9r|+^YzXks(ec_g zyXjK2r0LVw+@sT3vqiBrS^jj^OgM^#V``Df?pnZO+9Kdz+U3{WRoF02`id z3$TNsGCk}KxRyq^&<+~xT_Uu!nx#OCN(i?(eZnT)B^DhRk%^_a~&jLo~uzxhA& z4*eu^Hg`Yn;Q^AWtnTTtNvjgc@O?Xe{4C!Yo)9#Hh6{|eE6ZkUDdq@%OWh#%Ml?yo zDzOJ@?6L$>4BMGufY4JIB1PO0FKO}9whX&8EH2}8Z~O*x&*nzKR5uOypE+E%m24O- z6N?*%D@|?d7_X~@7036j7$u7I-D{Qa&uFXWY740#Oj?dKFv`z7LoI_DUoxVOwdyk! zbYJSketUz+ii}2qiF^@ee)r|>+SY%2jnyad*e8_rDGijbRIB%;)kd=xSl$0I7w57#obOU{+CXdY!0b;|uOsj_* zjzXX1ciB~Fvpi#4#zULIdW5T9%_FXI^Db5mOV@DpgCDwC^-aQR7 z_DN$bNC!C9Bix63zCoBt7@jo2EC*H8%VVc%p7VUq8jnx}3Vc^uw(N*3pxep@LNM^C zgkvs_@sE=&gvw3zv?K>8YcG8k*h{lhx3iXJ>jUJdAYU8W>-hUePXK~O(mttA%1Eug zKQV%f>b^3jX#IoU40T0K0(X2rS`^c>UuP?B$ zGk)I%Eh17;+$LlR+WY2>L%1}t0Nna0TFI5a-Sha>QE3ubEt&naa1r33f_Uuftw zI$%0uw9`GbRX2f_7COvQ<3o)(GU!%Qkk4RT+=eM-fgb(Sz)bY@t$o*S1=&UpW{*F^ z!K~(E!ZR=>JjiQYevss=MP>|yGz}uIcolJ=n4>pSf3&J=)3)c)b zANsR$)FAZL%OenGHj{1_!*q|=ON`R>`Cwh9D#8#}NKxx$8V>0OsInMbnQQa$Kz+XM zDr$`Wp3TkF$KdMp;V`bf5s{@@5f0&>J1lyeX2r@>RnGHPbkKQsY)JyQ$d-4hGd$7rZk>{hE%)!Tc@EpdCQ?vTVdOLY^mF9iJNAnIfrbG z8|Fqbcf_T^E71;FjtT zVEjDi1kYu?b5jrBtQn}2A{<6KN5Vtxkd5IXt$;SxJE|$xJ4(1!?+{CjsuuJ$qfcND z=^kUY8`o*)`JFltXab{$E*hNi=q#J+C;}-O!;%hZYrO)0Zal*YaNd;O$v5&_KM`k5 zT*sCX*Y$0UN5z~3I+C5_JOrSGdq1DxK_;;Tsu?);HTs-qMa5tHrsM1%fM#sgAN7+t zVXps1_?e)$VQB=3V_=$LWl^Jpm;(}1n+6%~et*hry@%-jY8-meUtwgE5}sG6^KkB# z)wH3uX`}?oa%)A@-U*7>o$Ly=(~8{Xr)7nK8G`}@^tvrCIuDz1N_JSFMOsxXg^kR5 zRiZ5kLgNA(Ujq3mmj>qmcVUIa$cvZ2i_#^BVMy9}f6{kBAaOUz`9LpDAgD00az?f} z^K*yjv2Ej)S#M!Qi@M&i_fc@k`vNyQBjNU4@B_9~(7ppt z6?Tkuz`75|*vv^FJ|(}C2FwmU94?HyliJ5*J{<`2*Rt); zBG%INXGmjjcFm?%f(<3q-~n$FYse|um#j7E#{D|PFc+!2RmkabQB9YK4}?3p{V5DV zBbkrAf#Gb?OZ5i1$F`QFb8(3Xm+M;_!NMPFL#G-Yh8dKyc3!0qc4?vqBkn`YJBckp zq;wrARw%CV2XS+t6O2t*upxAo#(*<+dUbBks-iZNrstufLemr>GQ^hY=_cNexp`g~ z5rINE5Xu2xGLsysEBxPHQp@~wUEs|*XWN&2ZWXbY=P1pWu<_2<5GgDN6Im7)$O)3i z-iNh>DR-^)eNgTQaq)>&Cp?OQpH7fnrnVbQb;pm$cmvvkQAF^t-tvh44K0bKR#@e3 zNL4Y|&~{@ZDZFR+@u=Le&M&*T4c~qy?|udiKbLp|3JVH9zjrn<=c_D3J;R5)xH8a7$OA1h5*Xh=Hf7QSTurF@A_Rj?3qpqQZ}r z%J_a>%6&o)I^_Pa%}e9+DZ6$B{PcM-LB@KTU*uCy;cL{?cON7(n+DKxnor=IVPu-R zl+zdI1;g`2p9oXLRozYU+1LbuDEV!0{RpSKh&RH#dtI>H`ivG<#mb*vMjvW=eAk+e zH@~F|!FwOi$jBX_0IG6Z9i6+4cA3sU-ZJ)u#JU)05$pCQ%0)UZR#%@HTVs}PY-kPD z{%sqd2O{(>tF^tNb&=h@9wenIC7l;Dz1@w$Nmj+H#wUV!EZ=klw^Todd$62wWW9f| z6{!)Pk?Czhnoy;0K5jYTYJ#9{54;KPb95C*=Vhfbgzayodmp@F24*Fe|28!`2mePD0uRR)8U*gZy za4|g`3TrB<)E5(y8geo7XR%|KWG9-n@SIdwwl)#=iO<63DxDUS z`CF*7%-C?go1`23|H~YiWU|t#lzB+HVNjp@VrrbXtceKb)TSX=Gr?PmdH=Dg6m2jT z)TGVRa_E%z?Z>8?x$Kr|TS_uCMO*X3g(@W_CGiPi+IT(l{a}pU ziqu7j?@YSTQinf>k)L0+!bdAUSszN9V%dTQI7989f=)Nd(`114(({%AR zrUK3VYJ#Bpc$9Q6h08fh$dO_L`H-J~ZI=*?NPx2E4JP_xf$_U2Z@shYzsgq`*gf13KmR+?506)e_T4Cb?8 z@kmo6Cj!ftr=&Z{(B<8B7Bz?_zyVFaP||=yG}V0O>>4RL8H(dUf{haRAm8d7$yL;J zY{URwRrMe-7u4tPsNQ}O-g5uwZh6>LW#hEl&eRI3LPCClN+N2PudD1LxF}^<9yl|qup;1$o{MvCpfF^VpuJo5L)S@8crA zFET6GhwJqxU=qmFfU@`P7c*1*qO|GeL$inc2#yMnE+#hyS;Uw2SJ0-6ydq__3T$DbhmtN9DzMkj7L9A*RvoJV!G=XUc&6bhxCb9w)DgV!WQF5@I1J+Om_O;y+Z< z?k8)*0bR%NNYgRN^JpMlo481`5eum}zA18WS1_5(j0Kq~AC*AOM3*7CDI-a1fSZGV z9)D@-OEX*(E`1%Fq;w;lC0i9-Fqx!a^!0%Yh;&wrVeniB&R^?&I-i@*aFev&SLf-% zW<uxs5{-m7nCIjwZQUb+DH+kjy zssHnV-+y*YR@LI#bd5FN43X$y*57E59g{B=5I*0@-;s^lCQEKDwG6|kZi zYyydd;&?DGpj6*=lq}QIDKX(J5iZG|$_x)HT%DKe$vHHdQz#JUJ!g)@X}-F|$fizw~T@qOfU7Cox1<=QeRC0Yk@U%XL6n#3~c>X873`-8dgJQ-g;CjBjdFG5`pUM*OW{Wv2P7bb&G_B9++B{&II(x7>gRXFPq<^6}sV zAxOMF&t$qbeh$x?W61CIU;uSX4jLmiiE=QmyOW$Q^Yr7MR*G?_tmOfJ>AGGP)CZ5l z4140w*k|^{44!+EZrc2%>t-Y9iId`Oer8u1XF{YzfwU~4P-97a$gP%;ln$x|D2mR& zPv{rp=Xz~5Li4KH-~3Cqfd5~_4exF`nN+tX z1lqbPzyqtDtq3F-byAa!5YR2V;M4_0MTd7R^+0~dOY8?ci`7eNqD^PcWazqbb)aFW z_o+`CZHo&#*FAT4w5i&T`^2Ywv5H4pg-V{9rP~4x487P%I4`cvHjp`4N1pq|su|T( z*4a$kmW+~3I&o_pNh^t`f;>|<3_zK=q3B)?!?c`FOq5NJvP6ml;|>qjYQ-j{8K0l^ z9IMKO0b^#Re{cKjeIj02ZIXFO?C$V7J^^kzrKH(YH<#4&OVe}*q_=2{PH4NBt!J?P z$jYvpEMh<;_7>tiZoe*8^)OlH7FD-fVBJ?8PzC+8cq9 zQVmCsA*?wf>yYG^F*L{w>>_8>FSG;0#{~Qj9g>0JcARxI5Qn`+s*>f#_!61tD3t2Q z3LGK=4{Ww#VeHPvhNnG-7U3EN4*0k?1kUk<*!6X__RQf_UFN2N_tzZyYa;MXSy0=> zE}fNhPv8r+-W|>UQrovg!xGL6mLCOsGUKSwdghqSk)`FGK3`33x6K?|To=p38wu@N z$uTHFFVU&I7nJscbO#x9JX_PtqgYk3+JqoUgK_i*Hu2s*JnyP=&RDD3rZ&UrP)*u*OPpJO`pM&5#^2z@z zhCaN_(7iY;<_brr9+F+ashp(6yx(_6%3OM0mwJgmH@D~8I?0Z&Plz&RJgot1Mjy3$ zQ*Y{fk%_-?;c7-lw$#J0l#h`lu*P0Thk}OqZ355tqlWKwCY907VY%QUx zyp^31BXclENSG9>}P(7%;t?jCux~H zy)9RP2ox@3Vil}i-e9kWR=VnAw;v{$*^;7XYD6R21f~NP671cJKfQbV`~Vj9k;eVH z>rV0T-7?;!%F^vmLR5;HY2YTr))IZX@vr8F`tJ!mss^rwiud_hNhY5`-eajKa7^W1 z5D77*`(#T%pUw6NGF+Jqjj`-J^4Z1#KMRV(0{9JfJi2Q4?ms{gBQ|nm_k$-+NkGXG zY=$;u%Mx|11Q$S>By2IECM$D+2}oRBIE0<8F}X;#a1z^5iHsT>HOFAe`Pr(aFNGUR zoR+IqIcwDl_@O{(dfPEJ{Ei)5m5aOeZ&^&vS2uPq_C~<{>1uqhjg#YV=FHe-=aXId z%PWnG`5eUp)v^9uAeCrgV(WwqLAS&Q8JE>_L+;bls!hMZ$2PRp*;1}5=Jj&$(UfbF zGS12NtvW|*H#8$76>ACC29Qxo2tGFkFA8T7)nTWn+vK}6T(W^}n5@06MKKGL%Af}J zr$M}sW2k;h)G=2>A1y4~*7PU4DO?w|e-5kwmPUc+qdvFV#;={d8w<~1c|N3@-=TjY zs(DOZrkYkCeQ$c0tw&CXt%2ml-_4 zK0l^dW3;BG9f|t*TkN^W(0)U}IN z6T%)mgzEd3MYb)PpecJED|?Owaj5FzR` z#Egy>Kexp%P4TUT?;NY~`Zm$E{d)i@LuKN{(uGfa$~HzpXKq?<;urU8_2hGL5BMxy ziD5_6tcKBAx5_{X7y-L#_7M&3EVsDlnVOIhmoqfMKlkoti=vD_w5x+M`Ou%y2&S7D zD`Y#C_YxEiSwS?TBFiT|%Yjl?a@-{SkRX6q>#-DpGarf2ri@(()uQh#_BhJ#G0x#h zP3KB^b!07a@Q1BriZ{huRZ3-TGILX`DI({AnN4HFuyHw^ri&_nB`ajDY+wOcDw*|( z9(;0wj?BJvu5XOuv~?^f+Pdpl(#_3dk!{sB`|CFvOT*bS`_h~ZmIP=!KB-w}ls6N( zG^dPfMzO}bA_OwFe*Cq$0RNH-Myrk~RbRXOM>7&!G*wzXfslEe5vzj{Zch+-_RQi`#T2fLK@N==ScWNMEa;wT&Fi z6?{5ES0bw@3GC2(!?V6IEmM`D&Q3c9t*eI*FV%(3{0dO9yljnpJ@v^zI^8GI~|HWM_EE%s-AW%KFy} z@ii7XTH9!emVU`TjSonUn~DmDAa35sY~&s5qoVyk#D4!LRGb2}+955RVHwIZ&zE3a zmP@!FQ*=Z@_kZQ6QujpBji5P_9BYZr^ebuyf9Sv-+x7!{;#B&{Plu= zW#0u+jT9KX;%YX_(K8HpVL{jn%lr%ta!PhP$m_numcL->ui%URirxH`rzc9ksGbMd_3%Ep~0&|j(UA}yxq)WS%99Z^TXbvR?}j-uDbNw41=z8sgwNi!hH0xajP5$ zzsfyTGkJTX9HC2Li|O6#e+c z7?A7&xuJ~Xe6#VM8yx?wn<9kR@p?LlMRWqsNyU~Vu$k-(bn^6X17ESH)-;Tx8Sw?h$E|gbV^1q zDL^$I!4w1iF{DFC%NK&C{e`0qJ34l(Bhh5p!t_#O1f#g0|1K9Ehr^nEpdp6qk41Uw-<7!hOj%wI6HYP5Mg6iA4k^~8o9rj7~#^|Pg)sh z+M;ZCZj4Kf{MNTOcF{aAyCo^zRC2-QCc1o6XKjAY5Y%94g};yVE<=NBl&8!~gVk3$ z!)D=)EQdJH8IYZ-#0 z+z!ZCQF81^uj(S-V!L%5ED=n}Gxd|=03M0C?FCePd5?9H@CU_cuaHMhPk~v_ zfci}_R5bfZgcY%5Li|>iMgz}v)D&C92;q6b`F+9@h>n`>sF^Q;0@`1E%)E8IFJ_)x z#E+UN@09rVa$YRVGsTQHsLvu1kIo6bAPJ_pwN$X6-*l0p#=|CHUb#OUY4meW zb9U;3{7?5yfnLuq5Ra$o%kO=3YCHcPcQ%qBMoA=wYOrIBtcReW=qLH3>6e!&b7KLV zp{jbbeDT*c`2t59Haq9HZFsWGF~)`0w#XR0S`0`C(aVBDWRNzpWx$=qDhkiOmceli@Vf|*h|8otu_I8m zYA6d`qNqyuCZecQQn%VX91acfY23OcarC?4gJG`w#g`eQX1)AuH0TE=m=IMx-f3VXvECJV3}tnx#4hR6oY^x%4qR& zBbnLivJ?oU8Ro2DHmWmxKP~e-87~nuvp_%C*?PK4=XtrVfWJ61;FJ{Tid;W(=ih3! zwDoOIEBh8plV4#Scs>M5lb>op9t4};~6DF|nU{{^5o&MUUoF|#1lLDjJ* zUdOy{xn9;f#uJEf;KzCUgM(c%|9$B7;%;}yn7cuR^NbIR=PLn*X*)S6cV2J__I&Jq z4&+pWs~?#ycrP$-9O+h`C4*#SHmQ$9Y9F>{<~R>arh(s^heO)Cnrw;=t+ z8KDc%bEPZ9&q#!fj0gG5vZN?~xy2*^u6#vm%@1&ZrzM5r;Q|V~%W>ltuT;20_oTk) zSlnzh!N2G@S%S}jsq3|E9Y?J^azLo4z@J{L_Pwh%TIi`MgV2xjlhTneMy)(S1 z4-)ys=+U>25K7;`JHm%YkjnvILa{*;DCwi&|5%hw%=#AnmFnP@>XT%;n7o7i|I4bFR?*saAjf<34tT%DJU0Fb67q|=vq;b{#AltqW6Nt@fWDJ&ja!t@j&1Pj`Jev$;`z^EXao7I zLmUcD&9 zZ=p~Cv;6Mo!vjCdWLadCrJKCO5J_;W*xc_9fBZK;H~C97gE{Yc;Wd7| z&DnB+Hacdm4&ERA{_rPksa{pzCJXU-{&rh#n=FL&@b!!LM{l41c=-Bv@d7%BEbZI3 zZ;lS%!Pmos7x6+~mc5vBuGo^1imL`bj1xz2r|=5+QH+H>!Uf;oixsh%C47OtBh7IQ zB^i@YsjR4HQQZirTx3V(lCdHvcb`8*z?3D~!6_p%MD`csat*vDzbVmI7@jMubyk!- zx+2?^fWWSaT@DLO>G5q?U#4<^ky6r8lq z^NvivYT4+snQmpTs3+XoTXtNP&O&ux2^DmLL?5X?Xw#X^nB_Mf*XL8?0Jh=a_rM zc6v7OI!jwPTHA_&xm=s@Hti=zY!aBWl&G~qsS@JE8>=9c3t+(RQ!?c}D>fJ_WLkX8 z;XMwS36#WoYP2YVEIczv_#|;F5fwqgypwp4hOjET*w87)5;F+I&R2jeq}uS@nHorA zYm_ICBQdQg&Lp%5X@033$!1iZQIe7)G|3Qh$iVa9dvQ!9_!5bzP)9^PV7X*bF=S_+ zJu_W+GbaetzhZDMvdkjEQ&ybO)QY^~R~-MZB(C6tP9y&r*42fBYFh|z46|(ic*9o5 zU0TU5O=MS($@fl^@H_jI+n^cH?;>RAdC0`+Mo3C%)-3P@l^4+mCqjqwfjuC>&5g-5 ze9`cy@b!Skr~z8;;}AMD5TY{J13Xmd5HI1I^s;&}oSu0}BmI}dufR6E3G%9=hWErF zz!Fsar7wO%^4Ba~I5+2wdO&V)q=N3zf>&fm1B;}qv`6Pvxh$awTqJIIBj$9xCevLq zN#LR~RmU==Cl;~a59hEoMy)>zJ!s!U^%k>ytkO>Wc@7;`k54*bXbJq+sBz51ATjF_ zDGLFr_$+MO{W3XSv*9{>5-|YJ;O`^~5gD0@!8HL`8QLZaVIf>CzM#4mb%kgTaDt0` zNeU9qomSNv?;+w060(UcvY=uj!#t46)XM~bfbr+86^p7@<8!eo#DfHR&s>Ytxb(zG z+EYWi{nmJOIM0r~UV(-PlVta5y3{e_mbA9(InZ51uTlFrUG6zynB=3`QLKKCT*h(KcTg<&0uRUlHn(R;WHI(!|< zg0u33?Cp`xvx2{z%1zp)(cxF>d&kXpMtJ`yP@f=p8$R7JZUD7wu9+2UF+_rf3JTIG zu-W>wEI-oqq73GZDK38C8)e3t1=wQBvKnYb8+H-L$f7O55d(M&bh={31S4#%>~*=! zClo6|sKvgCkO|d{&+#l7H6p?}oni1Ds!Z)QiR2)Yt2bXJm$Ws>rfD0&<(4wnWi0X$ z8wbc;YV+Z&j*O~?^+c>(sY(PB#{fVqL*!Ns^AdK?axul+BMuThjdzAPDbUR#hUy)k z=`hW7QCE_x?#udDeWQ%<_${%9W<3&4RnN^#m#2-~uTFF03dvS>h88)qdRcva0gw8QDYQ43c6vi2up@D~gBif=mcaL(Zjw{X$(EkMqS! zG&o3*yyz7p_b0wks;;n{{Bk1=GoOJu_5+n$Q{%|c=LvJpAZ~hIzj>EjmdlRO;avx_oOJ5&kd{g__S#f@4)*7Koq=@h%(N1LJ_O>_=%mO&Msn6`814C)2bVJnh@ z9Rpp4B|yjEZc@zB3HqFy9mC_|sj?G0O8)gIIbeSt)cgXE%092*gq8aIR<0(o)Wjf5 zBg+np{$A!YWE$UE6om-qXqiRM+vT&ac}LAI(guY3x;V-`FYJK1RDx@Upy19rdm4+9 zvr*6V;fo!p2a=$W9zuUwQ$3TELB3tZ}f0*w>2B$yN_}& zJFYLBdd0#u&Y>En=GdCX1dpF(dikRjaO+_i=kv;W;u={6{(L4sM~N0?K&j4cgSi9< z7~GdP^BlX=W@*&xl;S4B^;>5AJ3%(dujjy)|5z-?>tYqE0=Wn@YCT0KD8rI*zM|BI zHab2SI%7ob`>1L$Y#s{cbpsv;9RN26$6EeGC;Kdq?SS02nus2!*X8e zt%j1k0;VJtD`pc@$YM?y!8;s1#13H*az&Hmu7P@rM}Z{5_6n_fSLP>U+j}Au1p{&> zxh$luW$C(h>EWT&Q?%lJO93E@Xb}stN>#6$5f`7bx0K-q?Uzrb_X~#MVU6-FFhq z-SIo_6B;JSZwyt5*MRIk6mvBebx|y;a#G8!X<${d-a0D?2S6AHuVPPysx}&p&h5+!(ta=vb5o`P*sV_7ga62YMUN6p)7I(BoPc1*fq>gaadrD_k zDYbZ$I`-It;(mhmMlub+p6_NOHrvmXjI8D|(L(K&YHW;FelQD1A_|f?L5?I4uo`N- z{AO1rmym|dpQ>pW)vulRC;de10eaR$w>3+bAM<6O4IM~qC10xIZ@}|Mdda+|Zyqk) zfh5}(`2CtGyyf8NBUulbf)6h|8y_5EkfI0w_LL1~}?kkuQ{r1Ldn_ZD0*4VkV~CWKIyIVk3Elolm<@uy_&IV6f3dqg|G)C_sF}NwVudyu5NpyHJ>hHsTrf8^x07 z7Zu81y88aKf;AEUpja>iN|CXReG0QSP1*%JG!X(SBGjhKs$*8?m>jRWin_z^Xp3Cf z4<{D4S*` z<}q>e%`Xd#d4}&HUBpUC$-CZ^6+w8@iWNK;*;9umq-}xeYx2-J_su4!9#7NHOoe zqTY1fdRd>|W?2Vy6KkRP6+VVinDNDxQ4x?V+e953JZm`6$dQqWD;Kdu5vcRO4;YsIoJD3ZDb{2mZA zTV&W!Wau6+fp2r0yTNe_{AedMfsBH0mj^StQFLOv&Yz7fxN3sEQCm14ej|oFSSz)6 zi``r;&e8k{&(M;wzBDzI#9isBSIABXqEHUf0x}`)kgmrI~W0S zpdOmz%I-n;sT0CWNXa|xta?uquid+EE$@w8TZ4UG1%ih48me_JwtQ!2t+-!HXvi~8 zm#e%g(s@`%Snb{|&H(L3JjXv2p57u8JYV36Pm6hlu;{y}gi2uxW$6<2nt$`{@Sp(4 zTu0SJI+?(GPXaNJ2l#xAaqcmf+Uit@c3!;@wXF-?+bZ0sK^OVVa3pT3zb2%&9@$~^ zFv?`$wlmL0w#N_a={O@JFZd$;PW)b*rwjTx31ebuAC$G;beF|Bu-6A};|Hr} zK9B5g^(BkR{-oDtSL3|H2rkG32Uy} zqEBrlGSv;LDcGXK#kK~X8zYgA4kzFh(yr}tdYn)$1^rsbG9mA^70E%7UX28`4)=7m z0yZ+gDA<&zVnOtIF*!#wAKceLC?UM~%URA6NCe?jF=IJ^p0V6HOPKnhkpWvTO!HN( zTL_GeONSDnY8F)3!sFHAXCI<{mX~wO@`or#PG(|Ut^;IUdm!mKI$lYH0t(3^pZ)-> zLR-;UPxD#jn}q?BSd^FT;XtU=*|Y?%;a)UO1sw3UZjj_C&{bSG%JA~_aL|c0SIm!i zXLG|3sG~F!zbxvF#oB--sBAYm9`wKO!dnLZ`5OQEzW@D66v5nVHA{)58(j(#!hUD4 zTX>FwSZa36I5`SFn=5PDb$K>snkPeDlHD6Qv~_NR$?$>-z#W54*(FwjoJo z3Go%)M<@hG_(>KG83h3Ba@vIVYRXSwTm`O`1B&?sHUzCv=tnd|9d{^)Q69FeU z$>N)T(5b;W0nJFKvfLgCa@Rc!;|Q-=!v$mNAqEUX&y?_0BkA<~we+&S8CK|=>j1?{Jg2VW*4&l!Fx$(*-C z_K4G|S0#ywmJ;w)#5_Q_Dlv4$y3u@#u!JWPQydX~*s52}EZ(*A0Ba~an zVgp}SwN1Lgv$Sg&UV3^@n~B2>0{o656N-kLnvoXReyRh>s{mElEKX64F?i2!x znxh>*c}DgW#_`HTi=V^4wB?hJ1;>Rag{`quqb2Bj80&N~w$qALm<7sKul()a;i^$r zv%_)Zy$FRRz>i-_G7a4MQcqs%Z>k`|K62Pjr{5 zqiXkAa8T<(r~MeY#*UCqt6K@P8*p|7+C$gD5YC3Mz~gjPOffHd%VZ{`M|5}Y#dg19 zXkZ1Vd0UD-*|*20K2Kb4gv=Sbq-KX+_tCl0J$d+WyOhIqLEbXVzN%%3^Re`UjwPV2%d${%{CIz_Jv0?A0)%UfF-#Zt1P~xFHZuv#E3*kqXLKW z96Z>Je@GHv#`n;R_RG4*C%TxPo#y81{~m;^Ig^kjyTX1Pqk zYfn62)<%sNr_grIy@T8W@9(RkEUH{ti&iAqtzfonkDes4pr&}8i>8OM4$WH`_rVi* z=##~=7&_Nq2k23NmkkBZMt)A*%iwi_DBKK6$S*@FV?6Q}WV%YKy zVykK_2)uZ#+-;5mfWRN-%e^Mb?ukG+e7EA`8nGrllLMkaK$@p#T1!T6pJhHtml;E_ zpT(Y1W^%hs$HlmB#n$ypw2v{jLT#O9r^RGltga-g0pFK)3Q{s}m#;W){f0T{p%d!k zT0Aqw!l(^nU$HKEKQ?E9rqU9rCu*-gfOdz$-gHFnX^`_#EK00*-l$)H|9kK;f~N?R zN0=bw#%iDo4gbp#7>G|U<{?C(-mFN^WzlgqYVSdubIP;t(SW`9D!??heXfxUt0{lx zzK)Wf`8_U16*BB@`s?U@)XdIMe`BnSx{+YtuZ(3pJ2|KHp3N(fs0MdDDjlt;LBAM? zYG`|h6#bx~GUIe&BqB2Fl;5#D@SP}c$DSxA84eQb9O%kt8ZHBm5b270s-;>2NqMNs|l8AY*;_( zxMVW!tY{jC@<4t|$h^2yjg#|WTB3q?^`H*wi|@4dqv6gjO}XgF9}Ro(7pGP(q?MT` z>S}#e*iITfYnP2h#lJgeYd+iwm@%vg2V(U2!``>T_3S*t{~2BTG}8 zc%n6E9w6ir=ctf9!?SN4$13)Rlx2Qn!B#N zh{z+0@<}wH;AG5Fnty@=KxLK~?+`hms3j?zrwVAVp}kygb-weu{m z)vx8kg;YZoZN_U>XqI5lrqU2xD*dU%cvez?xU23OZR1_u5g7p*>52G-gJQv5PvTYN zFts`hatFc8*i6p8%!0d5`ZT9ze5LoSn7`fl+wJ~*6^*C%4{9of8ayWckcWp7Fb5S~FOrCLfjI;&4d9UqHtq;Sqvx* zHo%6SAZ8y1mppJceU4~VnAx+*A;k3ILUn)SkEz?3x?n< z+Aweo3j)lu%}aie>_!Yoi~?0>trDZott60kli#d@?}wG-Wg9b zdxfEYR#cTUdscC&R%|xt8T^{g`;r?bcvHsOaU;da)ThwIOi|#jBcmpEL|x#ADr`_=%d^8 zs28(&Yu9plpbo9$E+o8mAd`Wrydm9h?|gZD_VVAWqgQ`A5p)yPR!uth{kehcnPv3Dr<9{_jC?emDH``tVOlz9`4F2ho?+ zD7kgsqmd2y9q(25n_<06uP96eB3(3XpLJ(cl>pKYzv%{`@`8g~J1U#~uF5AO3jT&HYPcGHz zU)L;Xy>YJ&yI+6igjz84-+oFT#imr+yKzz<-Dy()e0AGd{qu;Yk#q#8`dTleMBmZy zAu!j`s;*R3rRd%pY9neA^60l-BL>a?C+zv%*a~55H}Cn~*4}MUZW`WopvW7< z-6tzA--SX6vVyM0=`xeMW`}8dvbxOEkJY`nuw zERVt#(41NLQ0M^qBGm%NSsz@THzO#q~xB z6F3xT`Sp>$rCy&qfCb0GjZ{MM1M3ytp!8~LKPvmg(LUFcl-fzsS$@!2PNP{^4`tTh z3MPMmK|Bwf)w0MYAz{3$!Y{zmx|clV&V0<6U{U#}`})8|bO5SyBRtt{cLK#f3HAUpz<%5Hc72vOKCAQwx~U>xmE7E06KzHJ*2)sL zHd0quZ=Dm0+!D+Q%VTaDiZ7uFh-NYWfjx)3E5fVfskbE}Mj;~BM>H2wPdLQXWSOJN z%z7u0kpJH2>^KYkGg)Dk-+%*6#as~ z`+AR5WyRziakdI_`<~{>w`kC@TQB8Xg=F>n0-k}HHHOZ z93X*xRUdz#p|vj%<$8!pHB^BvF(87OB$mOmvr#9RYg^npWT7`d5B6bHrVBOSA)Pfg z8qLgP%W_d$)rfYMOm@vK2#DNMMVkdYPCCXhN$3Xu~0*u`21r#Z_7HV`eJI7qDxE04$^1h9-UHv@kK;|@(! z68o`ctm2!sxa7Ux+>}MMi2@$qLbym$EM}xxBy%V<50EJ(#XUV+VEtruH+WOyUck{F@yzt zr%*s_b@0Sk9cb5(xrtn41FCbTZo;K>=5*s{PB-<;O1F9NvYgbE>FgbkvcPs+sTxzH zQpuMs)t92Ox8PRG0k@PEnA8y_XIM2Oq=vxBQXLRe$?GyPS0U|yXUV^>jnO0`iQ-l# zPq~WN0X)cbBo*+P1V5Ti69s8WNCaYDZt*|cwrfw^Ch3Uo7M9lT-s2P3L-|AlMG2?G zIX4>o>Ni7;a2vBNy?N%gMGw&+OqMX7irr~GuBUsedxdL`wZ@bRXU0Rp2-qFpVoo&P ztB8PwZ3>iy2>!RBC~p_}oAn9*O$R)cPq+*MG zjM?zDzGvhBNujBMqzZ$|XG6kt)CczYT~q4->ufF`2l7FBd1avn`)LqZHQ-$jWsVh* z+q^{?d%4E%h;g!Kg-LEg+%9WxkY{(B3qL&B4>rtghZ-5_y^u?(ZLi3KHNFodapRw0 zbWbYgAv+l5Z8uE(B&a0p>V`duGh*m-CyZ4cN-FgRS}X}?46a*>!9hV?7U)YlFQKhv z&cF>6#2rsNhLR!l{qDWMJe`ps@u5V<%r=@>5GRbF9%Z%l1cws4r*4129@@|>*lki9 zq2557KyV0Tf+b(G`Vq)pnC_io(Dgjz5y7F$eJgQ-RHS%Ekh?lA~M&fB8~>CtFbTt{KH zb^7MJHIv;DJVhJd$+*W}q9acFdd~|*P$(Ix5)Y%W`f1X`pS@n>e1|W(ZLdBPt-Yv6 zYd4WJ>c>BL4c5frvHPf&N$#hb`^ZIEWQ4;uO&4|a6hQ(T?=I>!c4}^av_+oP(HVKh zpF(XtyNKb{uQEP?j1OJLP)DQ$FU^~(Y2umlea6Ck*e8*NXcqR##+mNjGM>TV8R1<5 z5uBh^@qSsRnXbhhSzTe)Q*2??A6W@%591YTpsG+j#k zNe+VLVjnqL@to7hTv7+0ubCa=@q6@*#|SwRRxa8#gIWRx^v96`E1RR6O~ov=irHmL zozp9j%&j>D5i!T*+*M`Z2biNH>@&9sBj=Q&D1N%`cse467xkQSmN`cumAGPL2Ts;J zsn~`Kt3(_+^a;{TRa1&$mn;luzqL^<6^SCd86x$I-XAl#U<-28mu6 z$VpwaJO}64{mZnv-?209jSMJq%Q>4A^BRfJr=M4I@ID1B37N((T*KcK&&5madF9@knKQ9_R*F4FkhQL1O`v)K`VnGal9~yy~ptm3VsdD`^tow(qPKT-g z&}kC#1){KJj_)JcpC+1mF{g`8m8wy6876h&te67p=YnF_E@GoE@Pss3f1?$Rdrj~8 z`bEGozesQJLt5oSwBI`3?Iw@m!Gc-o0(GW_aW*8l-v=q`K@ehWw%QyN#}BzzP$!mJTQFU5N^Vj4ks3f@em7ju{PrN{pun-s8Y+8H~**6`LQxh-Qi?f^M zlVuK9fo=6UzBYL^Xnng0cJq5&@9%K29fQG3ha5)RIg)nC{kvZ73_k8Z z2G)h+GltQn%fh{IC(Cqk-myJ3amxk_-^a?Yq`f~J{AI8|G-rhnGe5v!@lXr~0~?UH z;)>{A8!1qUt{#mZde^pyz=^BX6rTXgi9zFa6K8uU@k5ku9Gzk^fn9`(+JZPpYUr+F zQo6Pd{11;jF;eulCWZj+Uf`X&{1oK|p0Jrh$1m%mDSrqehudrnfig;7{fqrXxH9+! zpZd`8e<8S=-3QJiopRXxwuc%*ag@CD%9E7BrJH&6Dls&T#|Jwbp4+6QMx3qXEYHw) zKy79G?_fy9Dh5U+>ws=@uqz*Ti7r0A=X-;PFciuuTs<1_REAEjlYE6&gAdv#r`)nd z7j>=(agKE>5p?70d(FPb4ZhWc%?#;epWIe@mo90F?gi*)V+?MvEK+fu#yv?OLi%$W<4|?=WO-D z?nkXGWBrfVN7g#n(6wP#Gd(wVD`JeOnrdi-%dytvJgtDYd2{y1JQto0UPHp>FpmSz zAd_C*WvsOX`^s=%&#I;P%4}^83H(z0i?&7gx3n|+Qn;vxVg%} zATro3kV|6f40A+QG)C5l$c>XZzHURTr#m^F`_WKtYl;zCyII|qc3G%n+o&QtUmmZc z4tfm{Id4I9b~}55{1q-(ql(~m6%V=Mqxw#W82))vVH`ocskq?`#B616bqKKF9nagV z!`GkH6qM`gr^?MO9liMRvs!|3H?&l<^3|XLpG$UcO=_EQFt>&hQ5$dI3&n3K$*WyV zOthckYbRp)%%)5z*AeapuFc_~Y4pS$jPqjJ_5f?@OWdrNisQ`^P!UBGttw}aKwXOT z$ih(#39i*P76adjBkLGyZHT#r9T%?GAnqYwd)0IiD(4zc_9T{aNv}E3g2^^O;ggRL zbXPv$<(6x9>$Yq-CB=L^U1tGvYwh3lE~(*2c{qg>7DQOznzq{Y|BHj&FOoLxK;eq% zb@au-(0q2)bebk1-06=9%jaMhF??DF!`3Yi70QmwX{NBZ0?;16 z-|i8;gu~IYiTQGgMo35vM0#r~wbLdc0Mj#6erGnQ>lFCm>?-*aY10UC0|zUtM06ub${P| z)je|37qXqGH@Lt$%)Rg7AA1N_I<#ADEj)xqYwKV)djKdO?t$$aAXwb%-!AT(>9AQ! zXL)&jyH`CMzpPAj>h+w&OEj2*k zcAx{K{34`z*r6!oDznTBl!C@W7`T%4#-dO#Lc0MRhO9+GqCuCQqxkDL@aqx(`n@NK zKn)(g%~FIu&Lr~jx`M5Se8CeUATZQ|M2Gnj_=I#RD8C&PEJ*7GVbfPYICAAhW1e1v znJ9dLS!-Kk<-K}77?Ok7laihOGKZ5l&f7cfzx4iIbao~fPBT_~%5G*-lhImv%%Kp8 znyRfK5DM9VUdi&%N*i>4=GRh^zK!^b2sDM6meVL}JL=|BSRUNcwzGnz{aCbq493@~ z?d4YZ-qhAejMA~5Ge2o%os0+~v5_k_9>Q+dm1JRS?T0m_Z;jWD?8X&w8?#n4%K{}!OXxDgqP$nvSBs^SMJ;}_GlaM5UaKEO}!0zQ%354byNND{HoReeZ78sM@FhY zYTX$4&Sob&It3ZeG#!IPEmNZ8NB|;_I(*!m@t(ab(E}hI=SCgTMdk$+6l9&o@|tsF z{{|cy;Wn!J<6JCOPh#&%`HQfWk*JAO);FY~G?X+IKb1M@$LD!eN;Bj$?2`bv+~vSp zQt+4F4p9Mn$U|EgJ@b|YH4E3ZZJ`o;cTj&L{n1#Sp{kBx-}7Z|LA)h+2C{ibf8b?;HWL&Nu3X0A8Il^4q6wkA7=>$dyL>+E zW&o4w=H!WRhTb%N-TYvv-SV*Io1B^ai&S2iqAqQVG0MRZ^#Yy9Q{Tpgp2za6wh(WHy!_s6kHa|_)aPSx6`eEjsjeAJSM3IT2 z&%$@{qFz1l!yTZV-$lN>ViOl8+jKDzzuVo_T?HOb^f( zirvdG7&Nccf#Yrbre3n41ocWBVNvAo^3UWC$W7 zTF-$ODkt;eZ-{$J^P$8<=Jbek$cI``7!N2jVf&nWz8m$8;WLa)D*j?QnA_SY-wa3) zdcz0K@X8PWXSr|;8FSO0jGUl$FM{78m*O&;gNPAssFvDYL=^T7N07MB1_^No?fnhAdsefJk@ z97bfNz733cC-@Gp37owy8kmQsDilPFSR$O9)%nc06kV1}Mwxg3&*cXy>MZe6Ooq|( zoIc*~+za06#xAE1Yck^#0yH0udgvhtJPp=8S<;#qCZJ-$-W-`*6zx6Pf7B0x&WL{}zE^{gmx(*x8}Nk{Aqov}uR~e+{mFhEHpz^2$S>3ZF@``6%LSRi!cidm zcDUu1gZl!9Qmw~hU@!CRe%u9IqdQm&bsWJe=1}To#OHe~qmOlj+kjMKII=bl@F5nF z5Tw8-)9_cJYSHW)++VaUA3|YY&&hWY*59G$?~thl9D|^))u4HyOgp?^@fi;Em|g7Y zU3J6yjbqV)@joOBqzY-b+y#{tnc?AqHJo7lbkESmkbyy{iVN5w?{Tw*nTlTXc#{js z^@b8Cj|Ux}8soH@`8CuycH_tT{M$1|TbQ2Z;Odc?osGnk%inX=%RTvm`= z89anOCNDPK#TA`;gNjZPF0{PN`ipeIhN}y55EQG@CAn>4_6_S2`I;mwqdYT(8crI?35Iolw^=&vW*D5@ne7T zTie-vnE+%QOf{Mhl$%?aioM_7z3SavysH#KgVnm(!GHBt;yalskG~q@b_Z`hCDJ-5 z&d%__Hyh>j5{@}0mXQSMf(H!2UNq|$`24D_HaSLx!my1HH};THo{t<;`-$mq4gcdh z1OTo#9<~d6&^Vns*D_Pacg#}Msa&rboSUrbxTt<#WLZ8Z`;_Oa)iQvRF`q+^EpyFy z7$cCMHnUCkO1FR+%WI$eMWy|7RmBib$Al3ISOc63OJ%B?c~O2?AV%}aL7ph_*RDcENNjw zq+%PUsHh^T3P7mGEnh~G3m?2MG= zC=~vNamg@FMHl>%4{g$3qT3p^L3qs+bBS!LF8+%cmh8eP zC>QOEI5l0MBA6l(F7RokaKoM@$ml!KKh2FIKRAt>Qxex42SB>qyEf%uFu6{5TNV|) zo^#vGCo%8M(s=OiJa}*qSZ{c?kT}e+hN)0A23siS&atg4rjKYDk)ld@o=sWRiS<}_ z>Ne{==lKNqNO&uX<2vw$E%JsE3oGCc>HUpTH{cd&VWer|4xmn~_CGJuWjaefvD4f& zG%<2~By17mCokRCMU2M4AZo%3R+zv%Z}6Ahr$A=KgU%}jtw6hYnh5>Hv_v9~ z2t1nVK_Bw&FuzS>e~Fta#&sKYsIUi27tGy5cn|jQO?cb#yM6`rppSME;H1AfJ3As(VhkQtM#aJ=CDz?U9r+2kYueV8 zK1+I7o~9q3!!7Dpt0Q-pnNC$AGH*jQ())x8%Y?m_AqA?x`` z^!Y5E71Jv};m2}{HpP%S#^3JA9j&9t+@7u0bFefBuf__%9fGqNNvHUpV~|(x*b@5^ z_ggdjKHjjg8)CYRU}Edjp8RFyAS2QAD8!SBqOG&Svaf3*F%j*Lvm&2nm5f*kRcQOc zR;p+RQLerZPiYi2@P_R0w0s?y#?i-5S7ch)O=e`&+D)<%>I%Bas}Wk$b(5n}^7ZvG zJ>UXbgh%IyD#05;Ef?`dU7T;&q)P#pgan>ZPx#7-G&)XIL<=#;pni3EAkN3?5GXj3 zGiknG;NNe~+O7YXw_LC!l@$qm&+`qEnpGvDTv4SSdWs!4)}X|ot9n)QY~H$GinCDq zG-bdBR6G%PhhZua`T{tR1rm*!^$sw+J2~;g;bMLS89KBvCY?4ynvkIaA{dcOj;bSb z39lPWyvil)Lo`V=unb-87m8OVFp%RE@v^u?<#|XWFeQuzEx8Na3dQOQc^8z8S7lPn z7UhymN!m=t$)*o`=0m}XZ3Uw)NKHhX?|1y*wu`(?u%Yi_Zfdl|XeDmq3G+N%wPf9rHvN0E zX)2o*vExMv%K&_AE!Z+H>?J-EkP^xya+G0yFTx&zFKj$fu9sEAgqX2ei&2=r|FjcW zj)y2dGGW0(TLe|Ld0}Erz#4G#qJ2ln9(?$kCDcf)?=4{^mP?9~4xNU_FL1;`tO*Cx zbtZaJTXvKP@Hx8bAbiIc|LQO_zDV-bxL>m}mra8K=FN=2vm6K}>H?x$g|gPfLsYpS;CK|x4(X(H>c*&h5h%RqnDvIi>3MRxeA+lcjZV|- zDt^d#O#1@N^v-yT8^oZxi@Ciw3Zo2okNzG<_@@Xf;8$|BUX%!;Gb?ej=n;l_1nGgW zcBU>s!cDVJy0`nb!@z2A2XMcv%0;UaTMjA!JwU?06feUPm{u&r;HPLk;fyD(`n+7< z2BKC8w4}}$k9HS<pu!IrIa6cUhD;vqH1-cNkBP1UsHE2dyF=mq2J~zq zGJ(@84~4x%)6s%#9cF}2xXi>(be2x$#cG}9eeAgz-X%OZWO&?fj~igyiA!f@0L>Y9 zNCS||yeVEk1#bfsKbROe5{5;?R*cWfVho*lcfPJn+?DklwZ$A5Wo1BT=>_HDxsbO* z6+q69p;Pm5?kKX;2be3ki2)31s=CoQqCf1^w1L*#o%Q6KK=YTLR&TYB z`XWd-yZtA1UVSZ$Cj}3)6YdAvIunbgZP)#rG%jnmzNka*6QZoRQK`8fMy_R>b?9Yn z4F|1AZa>G9YQ3OKjbLwwc#Hco50nVeoIM0`yhFJIZy2o5g4aJl_<`NWQE#u0jk|G^5LP1o@-GDtHa}s)*eDKG||o`;SLz|zkU4q#wYjO z6Z88PyBjQ)=v|r_^i_eQu=#URREZL4D=IM?TUkmzx0%x2VH+VB0ozS$=1OaeG z=-EZvH~2Vg#R7ZYb-_201^@b%Jg4lKhqVfJ_nI1e$P!tD{t8D&0mH%}Le~ZqL_CNx zZT=3Rz%+F92K~oSZU7Xab5mG>I%`%_`f6F3kY;7odaO1sF<_k&D^XdkYB;egb?4R( z!Af8qgD!1VBWnz6{}STvIA#wzUDf-4K^XrYt~~Y#GvkVco>oMtrMr?xOU+lirtIJr zWd|pkmkb}Y#!85+t_l|g;4+;nX@#0aY~5-S;IC#Jjf&L06vcpK0_Z+2*a^{k_iCSa zxxKx{eRN0VyHX$h|2*~oM|f(DY@o8_-LPSRjykZ8!GC$A%*L%EC_@N;30LrdHwRw zY!(UqKrakkoUbo(woNOgt5Y*Zc0(EsBgzJelzcn8Uq6X=v!Zba5#P)PKbV)yLI|Vb zNsV3+6c%|Qn3WuU7er;Jp=BCNrn>DI4J zG~w*!lU*3E+#CL>9Y1)wrm3!d)pVQ7{I~z*f8CZ0Ri4@^ho;KYtyRMCqKPN{4`nfL zw_2T;)GM|=w2a<-dkX8i?bep3!VS;;Tb+9Cuw2ZmMX{tSgY2_ct3@^1*_jlp^ELV@ z&33LRs_2e6JV+~cPN(JR&I|}tzT6=NS^tAVDDuvk^w_}{O)(Dl z1=i%0{92H%??nw-5%cMHKOQvIW!pOKGS*27Je4T}-_qnuDA!!IowW8mq+J1W89V;m zw+$-J5}hz}G)ji|(7Yx@;P?1oi;q`c(~K^ZJ$cYg>&hbZ6|JObe?TW}F_&X9&a|Y( zObr+8x)RnY%!GCIAd*lW^cT0MI>ec zR@%b)eA4InWtWwf%Y0g-2!gU$mcXX0O;}0e#Xa7z_Moj{p2${|Eky>UENDd~9`Dx!(F6{`0Qof$eV* zbb09dF-gzSVWSY}hn5A`XTMSwnYjQ?$khTJnL!th4#iS7ubyFv;ovX5;WtU|X)@gK z@Am6s`g(?c_xpYMJp7uz4hHYchsUx{ao;{y?rNd{XV@xd6(U)*6lU4?T9j@qKsM5L;J zqM8cq*3t0hhgQ7?)rOB2awkw`(vsz9akal)4BMKaC z`)5Kb@w3H*nsjzs>j@>YLt4R*=T>Oj@l_C1BVo!x<&dxXz6w_$|TqJG0EiTxJ z5&<=~$?pREf>{ zqK=9P^xhFHl>t`J8CUcWlut?E-7#$Ci>jEyig6H=;KsYBIU-LQ-LsR_VY#LYXXa6- zahYKh3fo#MX%G9m`NMBupX6sLy6f7+7o7Ow;WzFZUQYP&mHYGqeBsEa6<#`Gm;pD! z_xnH4zWklb%X_3fL}Lti_Sx^EBf^^TE>R7#;|?ZtDGuPHC6JiqWR26Yzc0x^0`iF| z!VJ5Gtk%K;8@04^Z;Qf;I@w8l0MC0G0+Ou?CUbtbFV-ZeA#AZ(I+su@OrCN_9D78} zDx$DelBZQ+z+^OAc;W2c@CM5RdPoSHM1}`!)P)!l6HTL)AjFN=(ZcW@Z>~!f7Q6QP zxj=DvzE?-WBoHOxH!MBG9smi7sRe89A;^&X``BjPtHnWP8t`yGsh{I(F9H|?#uBjJ zd{tCc8b%lwYQpl4i$K@IPRAEoqhr0MZP_PvNy$Z2XLJ$9MZ;_t%H;6E*poeUgA9u3 zICC}+dshy`nUa?176KaA$BRe|hipNevu2q6782EAU4ggg)W!j2^#~lX`;Y#!!NUey zGHbV3

t7{4#vDjKL5MFC%P>ddpmhz>yYO|L6av3Kay5x#E9Y|(*?%BAD zblws79o1HuAR{{zwKgkmP-&Kr&*$?gmC!n0z1l$B;#q9iUOsgLsn7{K)CXk<%A;yqcdlh!yDG@eU=5{_FRz&<0ZCn&^qzRgmf9}pxq{a*J^9X-r{F; zR8udLLV0^!;~gaMSnRFqz8Q(VjxgO>3^dq>Qlr~yp$uEADYz+D35?pSwn^+S!{P`!8!piStX<`8?F}}HUZ?kvp+pu9r4@?vZG2sZHVR4fDEW?u; z@=v>c-&N&s``x$u-tmC`*>1OOCK9r{?*@DAtFOYPf!0*FTyyH>OK-q$pWHmY50`dT!-eP(~{vq91*^l}hDFBl8-d%Y{TAzy0RSgV7vC3zNru-4M#pVimz0I!qvD*?=PE}Iy=+moYle9P8 z&2D$|2$DLiJR?PPDXi#1< ziJrrpw^e)G^O5eto^$lUMR(!rLybLk9i8P!E#%Nh`{e%GMl)Rh0zEaEPa86O0(Hu4 z_<&RMmwmK7lnewe_VCpFQM+j$x{6#r!?|4tN^%Fsvctit)G0EccDQR>>84fRt!|Iv z(>B~OZI^5SZ0#1NJx6C5R&~d)rh?r9d(fPxO7!BUPIJ6g=ZKq1{lgkM_Zk;pZ`{h; z1R8?W1ube$dthk|4F>ow3^ZD zBR)9|HkcM!%8g_)H{C<*74FWFf8`MOL!;xMuN6i$@Z0Y%*3~&C#PE9jtah)UNdCuZ zv3Q$OB=N-(C4_l0HlG;F2lrBe#(E6Rlgj?GL0vXi$s#S5Wbr@?=vC{J9KBwSy41*x z(>P!cY}Bk#%xZ{t!=CYd0M@J&G!;xcpO2gS!E8VB1l-^ zk43g3#p(Oe-|(i^^)g z5i**nH*DT6iHg*wylTjLRnLmcESa^PdC^TisC}&VFn6PA2b=16BYr|y4$2Q3xtm;# z)MQ*M0*g(2;3hsm6IXr{AEHo+g;8GAo3!gjO>%9=A5N}AY}_0A8Z8)#1*?1tJV0qf zZxBW_Jw2JEjni2^{gGn-q?4*^iWwAt)aQ+-X@#EJ)hbV|s?6^3Ww{2%byAja_Gqia zWUXgA|8gJM!SPP^s0X|Kn-`1cfBW~{$uHj@DoTjQkB^?)_&DgQ&%J&=owi$j+#~o^ zdE{as=d^9+r|Zscri2Vx-_XUZe}%v8xhqN928MW*FWa3y;&RQicFPbI-4+c$heX>E z*`zK@x`O+*RUn{8v@29E(r!J{we&60%X_9?>DCe^qgCts2JUuL;vh5CuBE2%jPyF= z94sHn{I>CO?U+BL6&{Sgonh-yKbba=D&MzoxEgWy8#55~ky_;Ci*^Rx0NnxirBtPI zxy{{itc3!0dj(7{wAZ$q&~IlTh1zc??(R^c5(a>MurBTVSoq*LIk^K^clR3D4QyJ9uP zCx~J483GvXoFLJl4r3M^*v@*varkIV7}X+OQ3#e%@*Jrt=3#2O%<*^J%7|jNCa*Sz zk{eHRMsrFy4_%dXZz2>jPDZWz-CFgrisO|fmpi3kt(jx(_AOT;zW=7rq)?!SYaMwM zcH}tS9MUV{Fx%~-b=-;6DN)HVHYL5n2&)4p6ONlik*SY>XV(T7Cfx_ z4?I}TZAa(IPi=M<-;k@}){+7qDyLIn&f8*}${)YV%NYmf2hp`%CdL>&c!njhUouvLvH?$yzVM|AKt>_Ge2R*FW z`@XgIx>!*i7OYEG0n;$)g-Z`t$=3efs=}*VR(M^%z3(X=Pl#i(jxl4PY z!RK|y=dlj(w&6=qpB#e42G&LGu0S@^_G#}inMwG7ZY^@rvW z^|L6g`xSiR7FGNgKFqQ~N3efl)(s$>fHhTE^wUuI?sSLkz~Egp7Yb@I$#I{?_9d-T^o*% zoz15NMeCPj?WeG^0{N@)#%xr@m)o2o)!+!gLt&1~yB4A&T8Tl( zp6K|QO$L`I8`6eLAbqX2L<^7vc`+|rqJ2`DnU$9tL85DBeI1Jic2MRFoP_oXnmspx59h_gG})dN z^8sgT%{g(A$# zN96H3yUj-5sjiBTW%My?@GNCI$eB(R3`+)R=1z%R<^W~Di<`_@A&#tRK;wNbnQTy+ zM!q@1_q{n&%TY@r=BTI9lLrCem3e$A^J2IK%M^Oi!Wvk0%BDH*pUPtdVpqF9w<_t+1jv*oF8 zHI?)`!GuL3sM79;zgvV`0<53}9$k^lgVP-|8m7+nAy8v+z^SdYGg%m0mWV4kRzI(^ zCA)|4y<_PhgWcztx^0Lq5e$l=gm>7$#ybJvzLP;LN@wYuLrPYq_??Jm)OV!u()y*B zg{L+PNl0`0)$&I^1{JA(C!QDt{?fa=w!^XKruz93X+Kb0wira5>5s^vKlj?}d$sFA zXI6CJw)Vqq`DN$jMf;g@ps{1VI=_lxt5fOQt?QFpMu$qA*s9^C`@lVr zy|>$M)g@za*&E&-hw6==mh?KyCX^!N8k>Nf$?bi5`+Q0Ag^^H+#ydLArp8eA0^${G z9n*Z!6|Xr9_!!960&1b5X9&RE_hI2W&Y zUp1OD%gJfPnxJP!`x2N2PJEGG%et+I1r^i#f(suWkpHA@&*`pCqwrAT#5A3F?WofL zjz#%cW;^5jEA@344x-XQtS0W)h?0zo!X6GHv6#gid9&SxsmU9LzG70da9eq&?~MA9 zmKxgWZeP{3RI$bA6CWeCT4K!htaMqsldG62;!Az`qgs*_ntr|gQCm4lp{v%0>9B9- zy!xI_&sXEVJa&ECUB%3#C*VKSfdtK=VG=%lke(^@4<6Nh%&!-hO=S?mPtOA31CO0= znRToYCuYb`u}RRH-2+Be(a(rwSJg7xsgvE)N?u54Yo>mXKm<5IK!1by0|6( z2M#d0>kby!|7zn%e05Te`u69O+L1=%ASxYfwbuTYq>f6`-Pl~p9Bi0B zPrPz+!7BMFIMZJj1=K{R0MwjXLIhalKkT)9A}T^lUz}FadhaU{zIwjuFZqNb5PS|_ zcpk1yzlKT!?1AVad;N(uBG%8cE;h)-`4%&7BjJTPH;GFLYn1<<*^=p!|E`fG-G|PZ zcJ~>mKO&wAsRZojh|K0arDl}viYb;&mUVnDASl^z9Yv7h7FB>x5gb&{tz zg8}4Y{krF8E7LoI-*uC$)CjCbzV9Jk4Z}Ga|3J=4eG=)aB`utBU4hvCkM;avw2KIRv(S$PKm78vQ!lo69z=`ML@Yl5 z;O{DB8E2Lgow4SWaAATC7bgo)gtow}rU@Z`ecJjnw|=-1@#SgPO-bkXYEYdBVliE= z^k84Q8*`l5Y}sy^W%l!QeD392wQdaumg+FYe0~^VVbs^&XP0?7n=YXRz9?=OjNuKy zN#K=>q9qkYL|0z^$ad{kN0^$MM@L*v1HY+uVi5jRXWqs~W98N&9xtb-2W0zc8TI6g12Bi3shbtrnJ+09>NC?(4jtKK*~;2bT|W3uZ3wb%so{HO z9(#*v)f7=FgfiUbx>3cJS(!13G1!%W4q7{g(F4XXN<46aXd#ptJ!lN~>oFon^rMFm zeS~CLXphk>p^L1^Pk#T_@%j-Ek z?=cCUc$9dueHuOCv@lKB_OQoGi`wvFG$R)$r$)zS*6gq+<dOL3IwW(+FYfN#ed^ zbuj{L-D{dJ&sfQJZaq_v>`cYF|Rg4Z-lF-iSX)W`OnwDFUF4kI{*XiZS`mWBWYju7tNwirb z2(*g_>U9ut-;juVG?}SMsVYyRFYm}`?yBg*`}V=%0d`!MwxnpHrXEvZwYFP`|$U@%zVivl+Q#}W3E}eVMG3C&7>xhXh}+slxa%D?ZkNkKO2oj0$xf6Q7+l0( z`X+;GBLR%$&1)T2MziCyQem~rRtS+XGPj|W39fQn2d{aphP}nAyyQVWj0hL54L5%1 zBmsqT)iA{-eOS;d%^ipai7lrSGmdMlUGFQ!OhU-IhLwprsWtD(uE8r3K95>-C(wDZ z9AHE=^YR&phmuCoo9)w{^vM68T^;pf_&giEb~6~HYMm1iW7BIK6Fis|;6yX4&RCY) zokl=5Zjh#yKC>p55g*y6j1Fs&c7vC+%mfP5Kt3D=&vAIM-kONW1oEe*d#bi)Kxg>1 z!!i8vB!H;aHJ>G$)pv{x8mTnZKroRyvEKL*-?61fh!D*QJSM5Oz4c`LiM=q%#Se

gv6AoOC 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�TWJ8 z5TXbsj{vr_7uxWeva$VRXX{nDZ2I(5&2UipOeHG<0`jY>RNAX~+Vp`D;Kv6t4BL)S z@vnSLUJ~f|)fZ((7$F)WR~!v6+8RshAxJEGk0p=`s|fe&AduX4%&uZgu6q6gSULhI zw-07x1+J*kJEEhsCn>v&LBh0w!MN(Q*i|DbV!Z^dlHCVgXK7iUtkP-GY$B3Xp1x9N z*ja$<)ZfsYysk^NWl|)12cbwE&4t)HTPqW2-Ox&!OkhQ{_<3=avw+aGW6`QgazmYf zp06Y%IDV_C*7!PFLjA!5|Kk@<@AqIX9}XD`niei0kJeZZ#Hqx>pkj$_f_YTq53%ag zYMKJIqxKO{D_A|5LBX6{k^%oDhu|=17*r?yP2LQGt3&q>LCcWFLadL3&D-4vO4$tipk=hY1CNI$Z0*%_!$pnbu zf-&Sl^?r*=cf8n2%!SqfG8gI(p*{qs1!a2u4D1Q&awx3XNK=26=d0wREHqr1yVy_( z@s=}332%ALA$vc7briA=fU?Ji0tKN$D83PurE0f&fiOdJfAp$TJP#E;2B&)uLqj7|jZ zO-qiVD?rg`!|$QI$vse8WKMI(V46cy0?T>JUNz}_uKp6Mt+vR~OMW~Q*Qlejlb2*AV~o7IcLa zk%nMLMG?GQj$174W7m;;7daikx3C3I;{)Ch7S|r0hw55gP`8D;z1dxNmDV+U)iI{r zA^^@_%R1qtrCCO9c)Hi>?1En)hn0#NC_Y46I=c`E@~aU{s1pVD!}ym$jT(qvTY;!P zO5>%6>d{o0D~u}bb)CK0;O#^68g!2%qi#07ll8P*rXy_uXo&`-Dv%f?a^jOo`Xno~ z@kGEV9iDnq#tsUG2=Tb|;26AuZ2%SJe&IlbfREd9nX9aIs))tp?_{MnWvLp(L9!0Z zvl4DMv|UfD0&YfUISyPbwi-9X2WSyU_`ck&;?awJhO-}fU!eA(L_ig+wYKtN5ynnG zqIP--F@z_t=UHMH?hokv+q7Et06sv$zk7dOR%*5`mWs>GBxwhS4ki5X<52=`Y{y$E zUe_2C04SV^QJ0l7q(KBS+?+}w9}l@wJ&EDVY&1X~;V!El=_|##MuJ=s0smfy_{)DG zVm>u(yu2KBMA9Kf9A^>a&COvTMWs%>TXV2F%)Dm?^|9Mm%z~yqjT*DNJ`^-OYgW|p zkXSI(T^u$X`RINb^e0WPZrR~YTkKmqz94Jg7|nl{#HJp0- zMT&0|;6lgk_(EN4v~~th#ShPS)VVg+S4h`soP`Xd2MrhX`?=b*Cy0{RH&PhcP1BW; zsMW2^&2}Ei$Ni6H_nezd5gGZwQR`}lFAlR}o&`&KgFx3&S-28j6zB%pXDdh^I^9z< z{1MX-_LEDsRqaTO1#6rxiuqQaDAfP0Z4Iv9-O zw#d{UKy4~67km{#It*G|MN(zxYo9iNLO0yA?ilE~F}hZw=?C3efRb3qX znBJtops$*PDz8^y&V7^b&*s-fwog4c;JS2`-;$S!uIoZ|?|OoDQMJ1EAL|2f^*i|b z$?ub)joh`vpiuUJ&ZjxV`c@RPc17(K%L3d`dV9jj2sjYUN+7C_o?`W>Q7^OCe`GGS z|1AauyC$$$jAQ`_U=#*x6kI;Iz(Yl2o6D8D{@dT$>z0Nv@XsM;^!F>}^}iCj*=XMK zy=wX1R?np6yE-N8A{Dn4MJuf zc_RHOUZtzNI-gt+godAL<6-rNkpNqdVqvuW*2;p$+yVt$Oz9=}BtItPUo3F41o7)t71o z>#(_(ReJj_zx@EoJ2X1v$?#Xg(YS+P(s<89q(3V-QHNiIw?Sd#bq!(NxBy;7#^SS` zs-#D}-o3$0~$b`5#BHP`4mI4B}Ewi+8Rzuy5*?hMZFctLfw*^3B z7T;|yZNA&`J4`3HtBC0AgSuOgIM>U;B)RQ~4t1ZR@f!#>CfHFjW+EhO#?qzW3)s2dnMDOiw1e#bDp+Zkjso)8gE zeFqRUhUI&jSy`ZE z(GBP3r4?s2R}_Cn9>-u5SC9w8KB>$a7Wp3)3NDA7J zCc(pp&=FOkV>?oJ&CC?@IhtO}gRCb;jrg`$X65auST6HQOcv*&$?~!}GidDX7WpM< zcA(d3GRto%mUXgPYbwe35lgTNRT$?4-m0Xq76Ms0#gNvcNtta%paRbk({2WO6tzz; zF@`k*MFz4W(64KvPR;t9p`tPXwE`3&yc=)X)tKN^v>w)}qbhHpUpxSy_=uQ58wbQL z(NdkPxN+cWwYrK(=cU>FP*5+w0fh6h_bF~^z9e*N5Z$({v^_k0QNTPQz@gsM&-B{K z>jZLP$h@&Nu=h5GfBQHs5J?44wOjHn()Hg>R)Je_+rHJhE7X+RE-im=^NOY~{q2i~ z`<>csYG(WLCP6inbL0FOG*6L0%4ksvV7hRTfQAQCfgHO2C<|~if3i&uoN=4a;_k){ zf1lPbkP8Bft_O3m!JMZL=PjL(@K$a^wPt48)e0ZUz*^W~v;n>nwza_7_Qeu$8z%Uc z--g<*-nhJgTtR9uqNm>5*V5D=JQq|{PqY`<57F?Lp|wowO*+5Mhd^@TYX>1c4Iyg$ z7O-2To>3}8*pSR919Y1cIFj4Z6oelo(n|(cA!=o8#y;fM#$q42u`lENp99q5JKazM z=`D5_yc#7zJi4g9!jZ-_B6k@WFih=&iW5@7cBD96;}F2$T(u3PztAz6WH+?kvhA17 zt)tA5CG;&>;~xQ-6mNN&0WLrK)fMbnahcZGib`Ywv{^xRFH&SHd|#Xx>7Ts z5n|D*1FgeSbriItMwgK=yp-0saS8&T8glMLI`qVK5hnRm)Ns}nV!?LFKOj0*RA#L8 zzBoGOj*&58H;gVu$*RZ$YXs`dzGJ@;J3TQGOt(?Bo45N)?Kn|>fT>{WARIl<{Gzf0UF9CWuRbypH=NT;IU!Hf!%8Is=8h^FLVd} zle}9g3ow7!=Bg8`$lzD}kk0&uS-z!Kcbd;#IFjvkwLd$&Az*b@^K`j{gMV46D?&3W zXjuWiP#^hoU&w#CwAD~2q>QNCqCc2)A#Da#5BN{O$(`FMx(ptuhGY#+>^Et*V!xWe zz0rg>(K2caPGc>gV>frvew~?k)ApM>_vJsY^XvQ=FC$V1;D<=Y<@LgE2}BEO&Noz? zIHIhiFoe1~B*ye*cmep=aNU&D%rk#TB=4~EOP4x`s;gXsYpP1mvl2*QSoGU;L#Fs7 zvBxXFPG?3$PoUGKCWgT>v|_Slu=}->SY4FP9U8r`iWY)hvb#?kb{52 ztR>egu|5m+Al!ic7{QTmNcTBo4ANVOZlbD$P-=b;F_2+wXcXZvi++#o_;Yl^i>A~| z_4r?wia2>+l^5!mhwBO&@K`SvrFzO=mbXh-@*tHsfwyiR|4ss&O_)sNQ^CZ$7LN{P zQV;1Horo%{@N?f4Z?WniLZJxT&^n+8+Cvy%jc()sAMAxyM;wSXM_qm z48Mgx60XUy4{B)3+0zECL#L79E+tbCH0YnTI)H*#4sA!2r8BdHdN4$5m z+&Zo!va)zL%cYQ0Q?NOVv*_fZZ#EAM+iE{a{@>RoOv$4}TP+RTB1R8kB>K8gV+&0j z521?D|Lk5@*H@$^SXF{Qv3>h{jh* zk)2VHP{%X{XoQHiM25!cIEj&aLOrOSjt6a|{jfJRYkJ&BM~*SF*=4RJeih49d6}A6 zrpC)OiDjC2nO3n(t2DFT`*(KxT-2jmPqFLWf&;aERjxVxQ#nO1TZ?L4ITmW@pUJWC z5%$->F1^d-+V3J7NPA(mZ`#U>ow`RM`qVFnf-e-xyEZHTtARTrUp z*vXfcOxz&M8e3Z26sd_tqhF;x*hX((zdQf%{<{~iU%meB<@w)Vzx?>U>cr#YNaFY3 zz5dJhA1Rfh?SVVp1R=A_nuF>g${`$seYcx#|3uth=;UPunChvy%XVX7kQC4BOo|Y zyc%+SM49WZVJ=lfk(hnNHc=0H(e&zstdRzz1aRkpWgY7x>tV7XG$ODHq3e^lM2^Wx zrd=bGe?Cmsl`5qAu~Byo%Y3M7=wb?N5BR*H1RSJmXfrPxFjEQtF@~BsCK$3k&%jMG zt=)2HN{UYLnWC0)f^|)*SE^I0Pm?jppo%pFFGp+k3@q9kE1ELnT%XQ=s?G?>?X>x6 za;9rjWyab(%v3}`^~l^9;N;pMjU-2pAvWjc*#IFW_>t>)-_$ZXn4;|x&EsE@3YCU6 zuRu+yNm26CS*nAiSY>P1scA4nUzwkkeQ_ceoSW!eFnbc?N_NGNxGjA#l$4>8%+E<% zXXx6B_lDBK?xgDvD?rbbLAwu!b}Hn|4UU6y-+Se3x1DjyCJ}=_vTEYxI*w%4q*}{M zXDOY)PeI1v`s_wS4BRD2Heg!?n(Vkc3sY`eoM~||DHt`CK$`>zB$(C}%F}W+%hEo* zP)uEu7sXA!M6)1ubXRLt3ksuAe4}p6Gz!~@cNB!O)i0-dzf@+eQbEmZ03^jXii`_? z^mu5F%+Q_~vvY?r;y(gTMx%q94FcXEVN#aKq_`N8To48cTA6%_&OFYW?^k3c7UoJu zL>>VBfjVGYOe)6p0bzL%hF%|xwh&Mi9GQNJJX)!!+vLC=rsEitp(+5zXf23vP|?9t zRqJs}a@CgHhEeDZYuQb`Us^qkx~;XHHnp)=u%-w5_8u?V|pp3 z>+?;j{uk&K@(87Urw-y>=mcGv?3K-4wPv5_?3; zLP9zN5N+xXa2$5XaC_)?O3XxMKUd;&V?RrBb7DVFj(;_pCYZ3TsM9FVxa<>-?7l;y*+hn?#f<}Wqh*{SGdwQbCTVmuCi#f+l==igc(vm0*1{O6XXq>-7V79sJq^Na#4n;)Qbjj~g(huLAjX^y6NWD%L zd($}8OgIm9prs-&6IB(B-+A@8dx`>Ylhqu3Oysa1pN*H-sj0~Zrx*3&_Els12@5SBVijuJ&EVr~J~-Z1+YeY9xU}?- zs0h|MmvvGKK(OyrGkKT1QT1T`e}+rE103?3T$6yX9M4-wEuD23HpNaxJHRh;_iC!q?ek`*!(= z)iq{QOlZ4IoJuLF8SGd!?U!$9zkG!K0)*$H*}7jQv=P4DvR~N6=HoB1Uv@x(|Nmu8 z|AS;rZA`pn-p%C{W!+m@_C=IaUjz)gA0!~kiCYfbZ2c(z-O6__qWttCV8Wf=pnl6y zWi9lz4mvL{sj^9)Q{aonZ z3)E3j*NQPZc>K-b@0^pb4*EO9D?6F}fHMaC;1haBA0-48GJ=52eTPXKXumVp7ht#b z{8OaW7P*S>JOop5w%F{cy_^m1vA0-wVGqM`V6R1>pC5TKo!O_6)T3TIdLIbcJ{Hrf zn#4Yi`Gl7$q&w-F(TE|E>g$*4qEUaMLaWysBHFi}-&2b^a!!&3sP7E~45&{SCqY7! zoCClD&URiwu-4ZYkKL9bfReM3+SK2vgV8tQr>iZ<>D1+IsOix9t0fMMk&7MvT{|da zt^w!nhqFt-BUW03>ei>pX>Sf5gBAP_?1kP0{$~aMa|{1-**mjhHupZ1k~XUIP%8(_ zyA~O>ZNAHD@XAPLAZLKLxi~cGht70h;*m)_auV5V?P#@DH_be+!0sJAc@UE~hdq4& z?jKn(o2dr_tpA;hXp`U@1&;)p&(_)}4O9~9xI>f6^!5e z0w`_x7@-O73s`TB9RB^o$vaS7!$o;frK`)(Iv(?(?}IsGVDNxfca=}!x;8#EXm46q zQ|Dg;-`tZ6z80ZA-8&(waM7O{)J_Z;n`vfw=)6S>{vS<$;DnMKO34dX@DEg^x-riku3cfE-O|G-#RWaAW0`Zw! zHO3BsGmW)L2aXfoPuh7#DToWBa@Hs%wMGo!L8)!ORlc>zMnWz80`G4=pYKyNcgG3> zf(d|pc8s%2?$nnM#%4m`fJHMH(kkO3ubrM}2o)=guc5Ify{PRZViIDyz{n^rfK!Jm zHm0ZFtg)&^&i4nlp1 zd!V`8kbYKrcR|OD*GRM?_HTVrsw?6`R;3+Zwc8q&ru|hXpNVq#iAO1nIB?tkdtC-L z9D2(R6Y`v;i5bd~4Bg%cJ7ree8t>$k&;-zQ78J~zyus6`4Wlyh?-gdueK})_oHi#& z{eB>QYX*J*M!)2PXi+AknJM>W-@r6q{?sQCmUY z1Y_T$$Y5{5Ya8*Z;>gje63pa>a>awCHb7skDx85=oZvAZjA52}$m}goiL6Ksp?9te zocGiKtdE~79El>|WrG;aQVQ*s6Q{G9x*sJ`oDImge-KhqH!b!Pe0%7pL_ZyP6wPZB zD|ik!9^9f_beNpNmo!-@5(}&i)TMXImSfb%vzUbOw090f(rNE(Q~*Zx$*g|^E>qR> zrhlkr29!DbN1oAsXeE)F&JM|zYnE7#3^Z@YVKM@tAH|B7{`BT-%j%0l?p7+Xtk?&= zxQI;;V+Zjoc;=Vo70$6*^#eS1i?o?uatH-+@uw@rim8QwB`lYDY>*4Y@`cfv2eoZ< zs^#d*qwXjD__WQ+jRyF;vFLtJ7~M~m?R5z<+vcV-Bxf)()p(vRwv8_E1#LHpAwcT3%E0Q7T#$ywh?lu^e$Z2lSQC$% zsFvd?Y+MNa0P}m+DktYnjwJ*{BL)(#rsnYV#=SudYRp%XM((i@jeD?7SlzYG_Yy(7 zJK%xd0YD*cS$;_lIy(WD(P3KR23b@FPJ&1G{SR8Ddmg9g1$6QsLzWB@sY9`n3vjqH zybZz+6roidUy1hlReyUg@NsK60`b(AZ8kA(B&YBh)lm#ai*#ilLWnlcDW+>{zh8H) zkt}K71CCC%rCkKr!GK_IRr?u*MkU+yW;19LA(F7KNkErQyh^T_Gfigu5SAbYWXCJ8 zNU4_ToE;RvaiBtpEZ9n;^er6Azkg6fRF2~iJzvz1;WM~^zA2~ay#FYZ(ltG|0dc7A zJja-U9ubVKK|!zv?sDA3h@VXEn;UOipp6Atqa!OdFTnquZb>vEFC^=#Q8FpMnO_mw z=*-Y=RMC#~tOD|}FB{S{wCb!j1_AH$=GZ$)?zGUR-V?T8aC3^fOfEHRBSRmXkq412 z0cc;+&f&~mIMLn%y{TxkbjlQF6khS9`1CeGuD6(&~k#0C?aZK}q0 zg%Z*K101Dc7|d*PszB9sxi=^^CB9OW5_rI_3{;~aGsld#HM7aU)WA6yqxfi~nd41g zeH=LArMG29VFeZWPDs4hH9`%bb2S2_ta9>j2O+!T5rZ2Zvyj;6PUJP*{|B%@0vVlC z6+;TBzv20uqMAvRMx9+^k3p7eljdZtm~X`m(D4 zq#s9Jlx@%2M5HAs;cPY+I;p-b6rJ5KR~T<75{&pA$oA5@WS=5*)k4@@p!JvTt?wbpPt`jMG1zlcU z-95}M@~KKO6RfgbyPzAG3Fi)fg}EkbX0DD!Ep{f;8$Bv`6+2uTY zNBR1}RA@0kyi;tn!KLj$+Ga#n{8Y{oS0ZLUn_1Ea>JXZm9hm2 zY74>}DHEQ#1!86*LwgMjTHaPA#<5oHuzrV0FSII)rbA8qe=EXvn=!iE`Dd0oy4 z!irufctjZFTD(xBfMF zHSgE+rRJiUj)T{cUlP?m`JCd}=@%o_x6Y?x5c3+vRMQ@)GO#*!lQd(b7gG~WJc1uc zzC32kGw!JgO}T)H$`ZyB6>ln6^NRO~^QK~R^1d+QPnw$T@1b~yImTjEU`%8E7i<>5 z48xz+FytKs1Sl)vrh&qUSz642Ko~V8$aTMg9RZ+8Fdd?1ij9SsaL3C!7$4gjS+ z#Lic`^+$c1SKwg^`Y4`cu@7#6x)@la8-It8cW?zWrgq_XpjU+$J7SNy zv!3dCr>%`NM=0jU#P?05Z&KQRr>1qWAI|J1`ax(?wX_5e6vc5*=LHi#W+d;MDiZLI zRk5)?eQ46@6-KD!;%AX^Qh?p6j@2^!d=QK4;FphYzVRB34({nd3=*VkaakXHb~Pe{RLG1Xo8ue>T_5lCgn zP9p@3$GSPw)@9lgYeKPc5P{^L&Xi&?Xu>*{gTa<#3$pi1jY>_vx>i;qP9Nyn=ruz* z7WvL_paZlZNC?s{xhRLR>PWB!bX(CpXkDayZKK!g>5RI($^wpKr{cl9G|+{%tsGEB z#qxSL-oa2Ts*lcEnA_2|Jc#yM)W8?#=N7IgXi2VGi(*go=+xA3CMGmr?=())qFax= znJ~6+Kc02yZZ@@sgX3MS_4(zcN+lEUL(arRz*TCDmsKrjiDduJ{1-!_03%BRqywmq z_#KugX=~#}y2R+|xYhB>1G8BO0gC8(L&8AM2~QTjFzJQS;;pI|s0^}QS(R5H6b5TE zsGRXABp>fjdyyBmn&mcQe8oG5!tc5b;H^aLz2+ zzsc1ClVKqV)fDT8I4)Dj54EYqDJsox)#lP8tyR8AIILwp-;AW)9w}%I%08RP(LsBe z=c{++bLs(9m?G&0@dj21m#}2ZIovyx4d@rRo3EDT?Htfz}4*!#4(tfS%Q-&Y_QHh=yton!o$AF&2XgbL}G`efRq@8(Yl zbhz3$-q>=z&?_5MsU#V+rAQ=^!{|(5_(G$_{`a9~2T_sRrZSSQ{m|t0spct$P02B6 z^l}LSNOEHM*09$$Y!SpjnU|9Zto3q3+Z6Vkt%*ow*sOq$OgkcBjICu0R3p|4=8G6l zqS~MVD8Wrp#*vKUXIB(XzxyQ;?Zu;dqWPm_kF!U3QI;qxe@~;`^u?Z6`gD*$0Bo-Sg?p_Wse;x~*T%y$Dt& zTxV;~JgW&=JZib)H*gx2U~{9erQ9mF@<>0@!u|-j!eY|Qvi=xnk=PcJZev;S^vAQf zN=D~4Jc-j$Rm=&_ApdJQoWbl+9owGIzF%g@0~y?tYu#zqV-!B*m2j;`A{5o9JJoxUgcO$uR>?~cz(<2XILGkg;M2cAgUgoPM> zvSU%Z0wnXyv?WV*Ak~6UOf=_vX&%qp#>KN5d@6lQ>@d-FrTa%TREZq)<)n?j5sMfZ!dF-8CPom z;#FBU`&KkCh_zvpI~%gu#MZVSWBxKW$~ujDXvZ`3nw9LKY8ioYHU0KQATWkEIz*61C!vh~St8_s$ykOF;@(MF zH%ljLRMQ_0MfH*{iwJ9EJ==U+DQ~t_aBK1k(|qK{X%*7tkL((! zE?C1%mJBpd>&SUUE!7F*fH39STd8H$&P#T91L7h#+Y|&FwX-$IpxbJ-==(3}vLjwVlr)ln z7fW;nY;=riu8|F0fCggnVTCNN#S|wEOa|T^NT?V`x3P1N(6_&$#JN>I)gkg?z2v|J zZ~K;QEgO zMfah#dp&Qx?~N7UD5mkr(Sx!dCd3JZnLD&a=LM@@>EO(8{@@5)B6=MsZm zf+$i}3}!Xp20>e^pjf@^z?NN4*@10=SR9Ikx-F|Krpk$0B9~)=X}X>gL1JrHCGr~f zN`Z_3WWK#D=eZ~R#72ik-VSFw&lff9GQ7K?muTs8UFWiTuNDY=#V3cc_j7e)Yj9|^ z(-AgF6JfznI%q0(-_>IeJ+i7Bz3@619(3F52%No|UZg2aC|V&eP!Zy8A1A8bj^4gD zM^?Vz!XikZgy7R`PW+adJ3y=g5-)0tA!1#}VtwKFHuyu>DzRwTE-quKj@b-+YfWVA ztFySV@H*uO2MUbVaB$-pH%`V)d&YGIkQ?iiPA}7igO*A$?dq=QQ?n-xA)!#zi(@S~(hgXx{MdI<7BIt#bYoAn2DI}_=RF&4*4@7TVI=b6&A2KT%sIHf;SODynbcEV%_`@~;?$VQE$ z)_Zcf<88H!G7h=xhS4^XAXF8o0w4hbR!(qgQ()5Sg7_|w5AFp{O9jY4R;zr8@PxFT z_tEC7pN}p^G@JAyB_@`39pjopImF1czXhu=K6g#asdsmqWrx(pTBUXEh>aAZOtxhY zNmT5}leCe3vAij-$SIQrL7;_+MOr6q34wObwWt}{@^pd-UY5;ZHaxk2=GAmEO9sOI z1u$(D7|!i^4rC-;D;y~oiM6zmmL1%XFjw*nO@*}yO1{pg*A2&&x%C7l*hE3T0Tp1D zjsm`Vi5y_Cl+At6RSlu!gsAAa1)0%wl0XWA`J#gIb-Ng%1 zf9_whoM*2jij?{r;M{X(Br=LfRIdRDp+7)QU%??qG<3T;blbjn#FWOrY6Q7p(~bj$ z$uFh%5G;2WyNv-LjIdB6+Cuy(ial@NxIGV?2@KiLtrelr5-YqG-q=RnlRTBqKN{;< z%svXg86E|8!@w);!aQPOu~@s6-n*B4VOQD0PI85vWC~WE;B39!I8-ZOa0_pCElhlI zwI_I1Vy(UgPcTUnlDqC>^?3%dNa;Nb>kC%;z85{jp+h13VjD zJfc@A?SUe$cQc@j9v2u>m3UB|yd*^U>qv@NTe+9vwtM?pZsId-Cnfx*Fr=yaAvuP!_ry*zdMs{yRIY_ur02?$H_C z*5&B*#1$A{C<@uWMA2!}dpaD|Zj(p%Ve5W30(^I`2bc1F=d`^8w#luB>Hd56{`<9S zoBcvuGj{4gqo7+B`nCemRK?<*vbsVB6%FRBpao-57U(ND*?2aS$Ztz_baa2tIyB!4 z7-9atuX(i36Nb;Gua+nLtRIiNRydN zyw#^{^<^<@0C*sV`Rq1u=M82ySJYDbQ2c)0qnEO0E$#sRl!_H}hzCV9{g*gGL0#+K@>J>Fe2{LW9Lw4NPO^N*E*f$$< zI~MZt+|=JeFzJZa*}Ipd6Kvz69s2%xT}-b42OjM`FuV@`%}gE<9b^p{rt?@C%C%p)0Brc2U~h^rKJA{bN(#KnZ6r-k8YbU88iN~R}T2ho6q z*b7mV$wEoZRdu~;%#l*-mtTXDhK*6BI)be%YisHWy8?oc;IRy~6YAQ>>IfaZtRN-^ zOWNfKMjacIytTuAG6G9XL}%V~b1z2bJ>u#m zi-M;XUAavRdxU=_EfHYHjQ?xqyX)HH>herqf1Qh=hVnw&ubEv^v(;QBcDlpHfnb%L zZwPmad{nY0lH3Tc9aY^!y|W)`lgV_+ZoWjyfpTt{j{=F=@_JzpPN3e?Ika(dn8Q3M zl6Q;Ft!zogW%E&>w(UFFpIANe%7+`7FL*34)Bva^uHCo|mivJ|dy1uWrvSYP?O3`1 zxplktnKkd>&M4iTC&miD`X2MV7lrn*JA#ywwWH2M{T;=XD^R;gF#tK*Gpi-_#k*9j zRMM!I(w#!VA-P*Zk|cvTX%v~RX+(ynX@``ai5v-Y#{EFjJO^wCf>v8ygZ;IQGoCiHnkAU$JQ^sb$er06SARu5stuQQ|)cuc}C(3y(zH z?Rtw4WC1x$By8&u+x~*w_tF<^?6$_iAUT)J7sAycs9bshaNZ^R32Gh(tia1D0`BTd zlL^ToBcrFVAfd@Y3H;Cv8k>{GyFiBxt`=3Hn&MR7x6_=S?rIk@O0qfIAD{5e(X%;q zrDgtsGP~rx8gQX#%Qku+yd%^!>|Yy+ zjFRWHD2vj--ZK6Vc^MHKt_Wa9->B;hS_{cg#~>VUIch4c3ZyrwPbJb@PB2WE6GCty zydrf&RElc=Kn|we4VMC>xY|k=lX8t$ll6zto_00RHKk*G0g#zX2(eiUSip|m=AZR| zN8b6?@BkO<{GXsD@QlJ&yn*;i5r#Zndu-@xdyN=k=+$23m!-O2?dO<)LH&0oVkz6P zeb9(>B;u<#%jrq=9RBI6!SJoL7dkwdazFi)L_XsQa{xDEEw*W=9D!6z<<4cO^Il}f zI`E`#kMA2OxX5)~0X+q*O{ey2tLAIZQB4iCJq&^RSb&^={No?vCZS@I5p zB&GmyQ|E-+YL4SWP|ZKo7EK&1(ZFkE8evH{@K9~whiI7q%V4L-0#Z)xq=8_9te!pX zmY9}uo{4Gh3l|C<#KO^e%k-y-=WT(WVQM7FWj+V#p{lfg8xOgBO6P;NPg*tFCK zAdmOa5^^7#0xzqkw%+wY%~TB4^?!i^A27cQmc&kx)9X1KklkD)^J{f?mg-2LFZiV* zMFCMx&+Pf-VpXiC9DDijSbSFn#dq06eVJC|NjWb8mtb0zwVG3~ZUU#C<>a~ovJgCR z@?5p1eihR~UAyXsI#tVjuCAt(!aE|WaDJhL@VJNJ>UDkxy`2e8M4US2`Kb1=%Cs;lZ?x=7avh9kg$ zLvY#TnVBhcMpZQ8MU`I=iH|3vo>maNZ!fSalaq%h*0(vp2P`lRbgat&SPMnEQ&@Pk z2}07)KNZ@ukn*ar9CszB>y|UYt_pCZK5EpQHhqnO)q&gv9M~;~#j~#MfFICEw4XDR52gH|74cEGtFQfKGtmcUs^8V7S^A8M@Tw|2WMGfeHXQE#)|f z$JRunbUFj|G*|P~n@|YKo@JgMFSBTbP|*&`l@g9q9UKQAhc7Em56=)94u6dr`CHvE z(2;iVUfRNL?on1$&E}o}TOK@_zoB-4C~L&&@XN=M%A8%#Ikkl4)O^CYL~(59mOWw4 zX(x=fqN>7U9jY4y=Iid6qrI^kaA*g7FH5MX?2xV+#fa1bhVPfWcOGcGl96Zv?_H42*6$ieZIvUaLT&H>FI56eT1p}J=*yDHV=6W`xAsE9j zBYjivN>A}Nz&p{z>{+kW7cQysb7tRZ0_NTv_+ak)`30QDRo)B&lAVI;V*bp@uRcN; zXDaq9Ou8vZoEeWaD_?+Y3XK_G&^;(7;X?_mUyY?4uudheO=`I83gF$$SWL!d)idUvJYDx3a;&>R^;T zq?6-|S5(_MctNAGM3G3@XnL7e&zt_CYc&ehkYj+NBAW7|N*9kazDas9w?LFn=hbt! ze)AQ*i;vq&3FVDiQgBXvogt;<)f@AUTk9#FA_Tr2B6NS<0~(_rbdS>zmYtJEcF=qY zZF(_hzpmys9nEcaYHqK~Jg8JVpoM@gv8qF8oNZu6d+Kz4CWw34N)!{ut7vX3YOAHq0HYtw^)pr7I4K9nWweIRjyYuP zIeS`8Yjq|QgnJQM-86ZWOjM;Et>8ubE3^aG_qJvrPd{E5 zuKn%BA(ySdankT!QkMC5#3@SYPK0cJkfN0DjtB1D5d)M}$NmKISSK5r7N?B`NG9p3 z*43a_*a#E)-S4FPU|@;Z!=Uv8CA+~q{5)f*v&hhvxa?kJdc9|Em{zv332rUFZ zps70RIMTWY@m)jBY_ZgG z#mLBbqndV%4DtcXAX^cc&A{-%wmrpD7=82%eM+6LZL32;QS}B}g!{Io^EK$ZzAcuo zie*t>=GpO%@@{QC=6zxOJfb&Bjgk|PEP&X+&T^OdWq8ZwGZ$swM`aT;Qd;gDJc7Zqo+>V;k zU6!1vMJlt|TIQ}RFjr@PHeb-6?PIvx`+5721XIU*_pkqF*lGN1dkxoa!2~*}MHc3D(XxzBpSvIuaQj@A6id!jC}Gwc`m1y88h zootL4P*e7MyNwS-kb&5@RY6%^)C6s2`Z_6rmrmA$F;2+>_cq;FC7XC<7u_h1E-d=O zU^se^s4~Fd)L9(osLqzrC{L@8q%_z$L?M}%U7k|O=-z`<3Dfb{W%1AJ+{v!)rRA=8 zgt8ovEHfWB{;u6o3>|u)0)Ilue*-ojR)6KqW(x$jgCyPV9%pc=w?zIhu31|`e$Wa@ zB+#;vS7b_U^VI zN35xfe3@72oB|cT&nvuI-s&sKaeL=2YJR{4M3e}B2#FEAlD}8S@MNCZO+GT8kHeh) zOJLuC$#tP>QIri`WbYsr*Z!aJ9YDu=IPQDxXHj>z%jAI_W{BeR$~h-f2KlvxjOny% z|4EvnA;!^!RZgNpm?4xaxh^<5o9(L7RC$;qJtMR;2b>pYVP>}S3s!0xF!tw1_*T) zMnrvd47nk$DTW`-tb_E+leZ&>ZnJv_wq&t%wx|yXX(zxbb{2bQXN0;m{U}Z6xn8e$ z)DRKM>#|N=u}y^|(gmz5Qw$QPZ4CI{%687$7ZOF(Os9xtX_;7OR)g0Wv*|PC>_%II zchGzi$(+c;Gc!36-!V<_7(i@ufdTn4E9OmJ{f(xs?^ZnUuQcCPnNTdZUPQrtC&zm% zZejS}Z<#KW*DeP&{fNS93q2B#-*vLoXlXtIzdMQ2beV2?*^a>Gm}WL)kIt&6NFsF@ zIFffFx_0imoE`+}**ZVV{PvY$j`@!_9FCX6Z%|rm44KOmUa7v5j((bGe;Rys zrfuLbG*FGx1yfD<)vw0-cpwVi1C>RdA~%vq@4S~hN< zQC|)WlH7S}034v2-(#Zl=^OZD$mIjzk|4qL5>|{Pt^RWDzYd05T$D$94pdB zeN&S*OY3I8sCn99ChyLMPrm+Ud}>a?d6rlCKhKAJ!t(X1tQ3p#d9Mc`u4>wfY13oR z19kNQFGwZ&Jz71m`i8`hydGYT_@e1TG-)r(MOrLP)MQz~L568+>*+=VwZU_=1z+c~=Z1xd{-ADmU{*NawPsZe5U9L0?9!>$ey=p+^ zCogf!sLiIHJylV;LcvyU>X}}a=qp_?ASlS>t1QtlaH>8i|5^9yJIAAT;$P3B*{`Q69mQ6OW+UdVm3P zfe3&PFt>YRtf4$y9DRqHd%g6?yAz-g=&&+IzG+{tXf0giA=V@!r4wwqntr!!Gy_rl z6eNY|a%1XBvkdoWI!DLnnqxS^_9LKj-DBJ`DPTs;=TQ0%QT~~t;@e$NLEQWY8j=Yy zKR1J&+fiEWfgcNiXM}}?h#P2)i$w;Dm!z}VBPmHB=lAG20py{tGfI79!r7=lfVZe}_0~=E{=xsw9{t$2 zF0dAOp3^(2CGH5$uA^J(G?2*8syu%XMooUbJS~8U_J(JcmC`K_h{C__%u?GWI4?6% zDqRydBSnE5+>pLF8-*eRXft>iV9&*sW#|Vz>`wrf0UUU#p7vs?&TfIQAVvfMRXQg{0numF+q2$(|i=Ph2e{`+4Aju)t0v^04;621LrKIr1o(C;%oeYrKPa_Xo{H8`$AZiSSz;jFZnAF%b()*i+0Y$DrEp?7w``4fnPJZ)Q zqlDfKd&1#PVugLR8LNDoyRU%Nb^-F7&V_oIo_@yR-`{**qvG&xn0)@tdHDMr|Ioij z1f{QNw_6PA49XwG+k^wINC-mC872>@z(W(vL!Ets?`bYewfg8rmKC#Ej*c2@qe`vq z6(zA;lIKe@bFQZ)QmzQx4hDHMaASMeU;nfj$R^>gNtu$?q27T?9uoa~YrySiMLtJS zE{th8>P2)>y;0BN1B%sncfFXv#Rv;X%&}cT#Wd5W|J_qnZ`38a7OOfo$#)JsdGghJ zbt-aP*zv!O9?$N+8j1B6d)M0(S*~vBd7ZEW4EF{)LT$@hdM{0^n@Fz(s~_$9Pi`K+ z|1VSAmX8mQ{-~b zd0ncx{|~BUODIlJRa9?l>mnGO-X|Eq761JR{*HyG64PLQs?4tn#SlQ$BZLjo0vO_g zQIeQW6^(`Rke;LNmm>y!(ou*&X&UV5J%w{E1|T&NQ7vn%lG>|bcV3uGWzr#mde$ky1|hU)-c>pw zwr|^ZxybGG$?>wkN#`F?&e5Y)6(E}^F}rz3!9Iy&+Ug(_<3eFt=O>C;UeR0$NN=J1AVkxR_{=GQi z1C0bOye>;eqH+jnxeIZfL6ZGGteOVi!NAbRC&BV(5rRG z5vV{s@W3^8phAHaiRlE`nDgDyj{pE=V>^)0$REQ%y)16VpvDf}3$*NG>_YWZeW=bH zEz2zLyAA+%VNJHUljNJEcTcEY=BUxKRukdI_7+{5We;&{uQ+szBLL861vT694gL8_ zY?Z*P#N@80xDHN-rli}>h+ARTIdO7Q%c5j(UTm`8%TuRhbf?3Xb>_z_gR1if_M5-2 zZ3?cVkqCPF@ksXRyjZDQap#ObwRe=9|MWq-_Y*p9xcs&hV`p4&2v|gkvCNiUKFABZDjQ*WpzO5s-(@x zt@ETm7XzRpg4<&35wc@D(h+7rUSKE~>18Aje03m!PhOs&KpH%rmsvTRk+zHM0rO>@ zlMsz>c3dY}D}J!iQHtqR&0U^esr`|=J)m!teTkXJi5H39W~U4giyn!suZPK^SdNpU zI~GJi@}XbR(GuLts(e{mZm-IS|3o)wcQKgtfmYQ<-BdNJxA*+Zxstv1U6-#gB!&SFj~@YDK`LPD^S z%}1LjI1uKe-4@WRE&YHj(SATe&=2#`<_Y>?K5}2-t#K0MqrbAfst#RiK?#`_nP*j&S_7?-XvCSTZ2Oznm3iRE1KM_S5bR99#!Q}$_nV_Hq=H{S@3#}cTg*PUT7HJMq}H5N zn1*_WNnR1=>>N>&E&EZuF>ipZXen){6<(urq(C~VdYe{PyVbN%IXfysC;8oKLa>3B zs@|wq=P&A*ZLJBOr*n84f{MOG&Zqen@LWKJSPxGgToM!g15Z{D!~*SxXE8Zpx80fM z!oSF5O*}+&id}gq-k`!{)SsDQizUZcuPfDN%=kRk#+tfVIt%_=rK`L;H?ND$<|OQC zWQ03u3lVKti9tL^nltozC$bJac7V4*D#gk~Y6m8YV&za!$v8 zTa4Zc74^cNQsFbH8FLDC8LowUf@&MY>lvLdbwA&s$G3MF0Byo-z}7W0%O38@z(gd4pMN1iXx91g=yk?D2+&~e9S_%ez>OpCYz>*Jb&>Y$~Zjtc~2 zifV0!>ko4W@YBYd?UEwQlhv`R`7*sBJ7w+zSyPYfR>m#(G#E0`E62i;v?brf%{Oh! zH?{cydIM=2n7>Ch4q;>A=J=i=~d77?+Vc zCY^OmcB*6AS;y3^!v(hZrS`dlwW0-Q{#$bzp1rf5X$;)aS?))>>2K6_GuVEi38ym( z2A}Q$Es|C}jOef|84QC=`SA|tNSj=}Q0uF1`A?JcFwv!fYlz<|&P-K;$3}*3CE)gp zhQ;L)U4+VGYW}vU^PznY&FFy~Lie_ByAFze zo^I;oh*s{I7~a@Cj?gS+69xG;TeA&N7}ag8s@pL$(f#& z8uuiDuhQ}rP^10`pr7zi zO*RxY1EXZEA<0BZWKWKqPXxM+sqeLO0->yKpoN|2I_YMj_z?2Sv@lEesYy-uT~M3k zmv*SkOzvl*#y*nR8pCVeIgS&T<*n&$Eh$k)l-v_Bvz!w0rflCzBoTEs($1U7Zm3`r z4ZWD=E&E9CCEtGDh+%szi6kcNxi@&6U)*JZKRY6*7q+`EtSFM?W1Qx(08WTPO`8EL zs_#>E1k^TBJ03MT{jU#yGfZx3f!V1G{hvP`{bnF*({6EaXs|{$SYU56rtIlT%3mjiW|$N&UQfADfe#3mqrdJ@a#$V4F5?_#-J zc?V>p&ijHMP#1sGt}aFa*yAsCa(E)eAlA)V3r(70xtaPxGV}4V5t3-Agiy?-9nyD^ zh2f3YR2Zz0=2bYvzR{;)iZ_5l;T+}wwpD7b~Cr2{r&n%+_!l=6+NIRLPS5?0HfcOs`OAE}>G~aFV+=FER(E;U#*JPA~I&RbxtN`%|`{ZLP%Qz@u+i zG;Qu?xWN!}6I@%!S4_W~+Gh+?wB4IX*Plc#z%uPSu~|(LSVp`8V2RtZy6Q*cw_Y#R zbJJWe)ja}^1Ug8ldLS{5ICyDZfl>!O3Q_g|7d0GyaP%;MO8`8@=2*rlF)PV}5L{01 zu)%|jR7%}5`V`yQlt?r-?1WfoVkDEU#Azh)E+TvWsO|E2lb20tzmD*4a$DZR-#D|< zzOH2ft=RBss|BLyt$M?Jqf_`Ka2+ULnYt15DL!g}Vzv zn~6;A7C9}>VmMg_A`MLO$|cF&Vy61r`fqLa4YBD4Zg(KFs3ro5dV@>p3bOcoQ?@h% z=8_WJY(qQiT;lyU%)b7)bL$MLjbRe(l=lA6-ow2%x!}9K-&>{4CET&;eBLLG0N{{# z^^B|&havK=DSiyWJ9Me9ym&IzajVTk3`4TYoJ`})Se!F>{_2p58to4w(^d6RT z$io=gHE}p=wKrFQpKDehNc4U$1NGqB!{On78XnaRCawo-#?dcE`3Pi%4b!_IV!X%b z#DsqND0L;j=xFV`fxej*l1Br$w2#fPI*yEtkFse&b>pHpG~2~Fvz=EB=40jn5W3-Z z`)f7tJI5XDTL>Ia?+F8sx~(W$Z+YeH@t0kTyj{Cq0Om!MP1|)SmT0+xGF>raniO)8 zpJ?Nu$E&&tQ;yXGAP6cOYNm9K=2B7WB*{zB_y-|&EN%(8`%}kGdqBMti_ca!lM7O* z)6~LmoU7iM^&)fcPg0~{9fb0$_;SF%L7WW-aNRadfSgu?p914=rAN_rCSTr;F%X#S z(9ZPB+nE|e1h<{NXzp31Ru?CEu=Cuelkz7273OyLc7ER6?%q!4+~SK`DtiAWpPTdM zx=6UVf}FQ!MSCMSS$*5=Fd>)+DFVimK(;6F6GyFoO!0?4CAtB1J2rrqZ0_j`*(BZ8 z(|KCM$Sgvn2-4g$!$Qo&V|A0;@^J3^PO%V59XRwSnk0gG@tiE!vjX90>M#g~KzKp_ zdjrZqL@lb&Mt-4pCm+8{_USPJQL8D0>Mg^;0@xYCMu84JjFANF9fDsZcv65H#03~7 z-$SX_y+yLRCcpnhx~h{&ehIj3S}+?_#-dqTM1p~s-=-TVXh4q10AQmC0<{4Dyw2-J z>)1p^ihR9<1_7G}!OY+WhuIM!pUU!?4^W@cmKVo979m0Ilo4)?T>ZhZ{`5_afp?>T z^|l>zh2NtYHIMgcg5Qho2DVTpt(DWg1$HKSK>i2533h0GOH=g1#VVo=r`Gnn?xPkx zP4PKKr2!u1=p_3p7!Txq;9oEpfTqvn3!M1d%O8MPRUN>1_4PlirvJO-AbBz{^o?ZS zTIn0f$!#Td7@N?u4;j&Cn{7?NMKADHL?t!;Jz6V97-mHOb#w-}Nsn#8O-sRzQ*aX& z3`GhHooxj6jvQhtkJ4`Nzda#K9G1P;?zbplV^YUl24IknZIM5Cd z=0bh5d;Au@sGMAepLQSG19IIu<)Eh9BM@g{O;D?$7XmRZrrksQx?YEKT-vpEj~~mE~y5=GGQmyhmwoJx;M@zoG1AK zCtQA@?S>mnvk_=lzrqL7Lc|stLK5D(o_a7*@mazxhmm9;H(@%-PO_y~VEL+*_SRkq zEEUGdX-YO?kJU`zzTAhd4e?Oy)6U<=MA+OS7Mu2@a09vd>)`AIHKD_f+>e6XCbMYw z98|Eqbx(t9a9|@=O7+Do3_bCW#0+qFVayH8>W67MH`XySt*GBOe@95*AJD$zX%c^o zm~3(_ZaR14#@mgXopd-AiUsDy{%vol#JMvJ24fu&&pCkq-@1NA1&O<8y2@LAa(D;HJw>%<~zcx{Wa0 zV1aD|I2GS*gg^;cbGnXMF-1JbYjE+qn6sxJHAcA+`&!XZpb>{OEgZ~^P^Y1QZV15& zaoEusgvB+|Fjt%~0u_=Tdy#iu8pZ6sD`EJsuhmra9HSUx(E|eC^tvf~$s$){Ne%)3 zSFuI7CU&Y2eiKle(JTah9ir{bp37jd%T;|@%+T)a$>B(N*BJ-T{-YQrWBa6N{6lyG zpBzRGnBEgZyF0w2VkikQ6X|k#36?@VP-Kg_(9nX8d!as5;tT0_#>wrTq|n7lOLshD zX>htsupqmIQ;eBLSgd8!D6Y8={M>C?WrI=jcb;WuS1;k=Ruz~?HU+rgHn5y(15W29 zV))^PB}*TOX&=~uiz|BDww$zWOXuO9;vStuw%>|a?5ojuOc;?X5U>bRd`X%QPM@5C@{_!FX6Vuo9?uD6I{Iboq_}E z4R<--)9CSj^#7^jJ>79vOyj$P^uPs;vGX`Y9Xm;wD&MiBL z80-2l0qY~o*!slcI`h#gIvzqmg)7Iwl^j7(6fMR2UWlkAO2tM%@QF!zr1_xeZH!ylq=4 zOPzIf_7O4lxLcA(7MPakL6}|G$4)807fs~|V_i&J29|aXENvNB8Xeg5XAEq=ZDiAJ zBb&C2Y}z@pY0Jo_Er$r02C%~Hvp6*}S8xuz!{wh?xJQ^aW3&zr9J}+rp10g-V-k1E z=7c?pn$hwq*Xf|$jePRuBcI%Jjc}iRF#Yn`n0C*Gmd-x#2^diDDN0dQ ztFG}W7;8y!(z!A9d~E|d)O}a`8Zox;D&Q3`s;}YRb_2F&_|_$3$4tpDsK>F#vL0*0 zp)UnTQdW{l^3S#1!b%X3$>dTOR((BDi_-rFXPsc`w%USpNCl3&c(da!+COqR(lz0n z3Qm}`B}_R%?;L;fOxIT!fe@8VixX1+-1 z;RAd{FdfE)z=gsTNvR42ET$klM zg9^{78)SR0h(r=#T4&DZ)VbcktwZ`Jh5FR>*s47q8Qn%8G)2Mb*s4SJ6Vd6B+-y%E zO)Y6yz6V#}KJk#fF?dU6ipcGCC}I8OnRG4BIEi2XSW-k%&w|KcXCFA0O# zbmbBGt&o68P!o#Cb9_&LGc$j~EEykU?9ZvL`wX!FoYF2Z3=YE)c)+Q^QFC8{XuO7S z5UqG^IMkw=yQp*wutiltpxF>O9UZalSacR36IXd=a;iVSTV{}Pi0L9b@{X^TI_rXh zk1uPe`PI^`8iw|vxGmAls+wMFMYh{vP)d&@NnK$%GA0aZ`L!!+3D66K*LtOeP@ay!SpN`1tBQIxCa)sbMZk0)oXuN%FM1ht&$|TeWkxo#ez)~% z{zBa*xP{J+Z=cZi9w7PuYN={6x5^v-gqP6a(7EOZaTGQG#&$1bR-o(8yiXpnNpj!$ z&S%F@a=8`q-#<7=E+I7Uw1jYLF!`i<|5piSxn%`rzL4p_Kyw*3xp9 zgE*=8_wDO<|9Nuq_TVunurm7gWVI%5@HgMRBhVP~R2^|<cYA0KRRD@$SpW)+0!Y9zzwf-meZ%`C zHq0epOTaey_E{$D1eCFL(e`Eo$%M=^XK>>ZVu z-R=aPcOAD8rhU#Am=;gP!0vEA{`}@|Nc3-hIzE2+);MM=A=mXfmu58loEUKsZ)!Eo z=#2`cc<93hARW)e$ye#c+O9G81;nF3-mQ2)jWCuKhU*~Lnlj9L)VZ@mamPifR}$p`S-kfr%t?S@1zv?+g3=3O#Muf5~Zq%qdMWg|^NzxS!>%4{^ zhg(z{jOhl4k_G5|+FL@{f>wr5lMq0kd>^)A3VKVJP?~ zjUPY8#}9p{;kT;r$zx_Tteg}b3&x>Iyzf>JYdBjr#7npq?Qn2yxwWc2XdHWB>EeKK z@8F*jCP4iHF&7Qd@f?FM!+bKqn2x{o+ge!m!(-xQfI}QK&k#ZIR-$v(h{(D0?Ifag z$FO^-N+C?0w6;b+Qlnp6V-Tq^IP~qvsiLF4v7tZ(={2Xz2`Mfym*(ZdA2xx$j`B1r z@|YUymk@QoSTTvo=oqFg+G__sw)Q+{Ekri~nEPjw}oQ;+_ z+T)PQ4PC?Aa#=s7_+W)91ia_Lf3I7uXu>tuw*}BRA=5c1aPasH=XzUaWZK=$1|0@_hV*_#%dGYWS;1 zHrVecPl}adS0H$yTJ8z@w8MoIrUd%;4!MJ9lU!sNEjn%~3>`Fpwm@qG^s@w76f;3- z{+W(C$(LvgSZ2l!#pQn#A%-F1GJ&ag=*NfQ&)M*4QOwE37&VJ2Myh}C%(kYGw_KAo zGwcA5lPY-~2kA;7h*+Q_`YOd*%yq;SIs3h+Cx{n#vKMviuoQFXbOkf2V2|uqV$Mm0 zf66XHOIA5%$2uJ)uUT!rDI{(k5eBL4)e>-~(}APzrUOA3INFzz#w)s#ipNU6sV4ic z8qNbu!>{=7BBwN;G&Tg0Dfq3gL>Jc6yH2joOnbiNB$5F)jYCNzE62!7za6#ByHLwR zz}6_?+1*es*EfbLhP^&w=Ln?|jHH6Pe&dZsp^p0FK9Gq6W9%ATuQ z+vUD`3;Tq9epFXdxA6-P__#oC0$pcUwIIBrrJTowe+pIdzJwP)fB6uKuvj;|G_l9p zkPm@(w`enDfFOaWqa9zPX<_cEi&_BN*RXvwO{j+*XxM?K4h`$FyiF5;2RNI=s^wP} zp=wR;hwS&y4MQYGY*-n`PgNaPHd6OhO-m?9Y_S}k`7SXLsV|STS~ouZep6Y^4Aigp zuD)J$M)Ye&|BaT)lqS*Yeier^-{0B z;WHn2q_|H@9l8SMMpt&-JK2KwCIc677%WDBQh!>rXo}%KD}7qR``NTupbslu4$QJt5!n_3q$FY`}g6AO-ceMHzZM1ZvU;n9ZZ@~bRz7r%M6o7)e z|B4joNT0)%5vAB8^vt_I57!pf3X1ChHhR2>qh;4%3MR*%C{5F|`v$xfTBVAUWmnYM zb?d~Y4o8Gli}`|vbi(RC)<|~x>fga>2O*jilpR6mkIO+Tz=BD`23h5(sSTs?;f7k# z!ABtbt>};=kb_oiplYKNJO+aJQ?l>xG6$rKk$#*S_>vr{rPrF_4xrj2x1L1PJw2?c zynmWJa)8~eQu4%0agpf>Q(NGFo-yU8xS-y|4K>>F1Hb3H*x%hqFK_I>>b4_$A!nTI zoxG;R@g=#UjcBA6y>C|k)bDNIMG;grC~) zcE}_mB&zjrh{mKpV0xm^i!l+Ege&dbb&(IVH|)1UF<;(N+$S~vtv^nF)DM-8q;o6z z&rS7sj-doF^?(XWLn&}5rVGh$JC02QJ%uRO=Xse-Ftb)Vo@XgAD^BbQ{{Tg2GRX(o z*zDNYDEWe;cA%BCO56yo>7>>4g}tWrV@ENa6w8uCWoe${J+Qr4ai;p(rx^b+Vw{!> z9}%g&ZmsKN8{?HCS)Hx$1wtdW;z zGb}&j0DrnxIvgIMUg6zx9BF2VW<4DV!?m8%`f@ghu$E4(q-La_mKnMkSEc`^mDzK% zf9ra$Y7>WZ&Fb{wP7Wy9=OF15|9+NbGuZcWU{wgH)%tgs9JG0d{tAc8Q#gf2#XOId z9a9cckR(p+XPwDLWb%@=j5t*VU^i$5fXe(jpQr1mHomjl2>}CGvQ^Ma5*s25;xts? zUALka=`4Sp!6`JX9L5;8G};iVr0aUJIMvS1-o1M}Ak7~avt166%}fOjwK+){AgMDh zPN@Bsc6mnwK~=2%ztB{7l6>TSkfb=^1Vf+kALlv(=5=zW7r2M@68@!%vBntCVg_N*|0yBWaxjL^jK0hVYn60 zz!KX>8UsR#k7-#JgFIbmOJ>gaV6e0aO~wB#C>}P~d#49QbzhdKqNEN?GPo_N>BP3_ zH`>I)^{gmi1;`Q?mvgrqfXtJ&>ZgTi=%(|+wWz3*99?J|?ld{r9p)nt#l|4SF5!es zxL(q1WSC&MF(gqqItQE#3#CS$f|MOS=4(35C_6I9afoR5hbN3{_vxR3VL zstZd&>AzK|LE?F~r)o?%0hr)wO;TqD66JC>8|S38ilW9i<++aDip#AKX^UuTCB+!8 z<8epLDP}y!!NRT-Ye~0ZTLO2_87B^zL5F!FDFhKUkpBiFQ6KB5Dp9$C#+-@vL$)q4 z*QHu9TFe}chR1F@6p*nNZ+>plc*;OU6jfd)P)>k0DA zB2C_d#K=?mUYD1%Y(P3;n<<7NDm#j87H~_#jYUJ}NW1446rXj+(*y|Wr=!i>LtV0O zvQvzz0!tK-{rFLfJP50%Qn6YKq+tyI8H-p$4hAgRRNWri4&51WQ9jHD!V7 z!dhpVbz668@=zFDcA;j1&8d1*R%9wtX`6R=zVO&6K51(}J{Fr*d!&8kv`75qtl>gt#77${$%6imp=DHD+*70f%2 zN~Fnb!Bf-E%o1h8JB_#!sD$MoTr^Fq9ZQjZ2Pnn85`)6$Ro#@#9B0Q8s>Z!!cTNL} zQ`R=ZFrU(pNuc+U0@ql!ErFDlKM{&{?x&6 zKHaK+ij9Ze`mB^}qf)5*#t+#-Kx>>!9bR`J?*xu`9$q(o|6TK00wlKTpdj9faYcY^ z0ZGHf$x;fVAmqwAO`n{Rd&;1g&$ALpZb;oN+#Xtf49Ah-B{n|@!aCl86Aj~Cf=@7h z0z%_HSM^R0eSWOTOK80Bs<(D8DtBcQi zo7q3sm#9?XYcg2NoAf5vYDt6|k!UY5Kd?iTOGt@1W|{g$G0xKIwKMbTJ{8P6cCT%4 znoO{|2rbToIVUb_y&($_sh?h?^F?yBSPt_dc{(nZa6lLH59JKUcL4%t8!s0ZiaN?H zvthnRhr{hWXXFj{)UU&t*8#9%rN&uLFpY=`0-VXzJjgz!S zr?8tm_XVj79C~Mc1UFjQUA!z$F{|M@89A#VmdVMf&!Eg9{nA)D*ie6*F;)-_duM3C zlT$BF3@Oy`H3XCSiZ8$aZeU|sfDK@kV8-;TfIu1}%Awt}h)}ei1j>1rN6sNZWD554 zP}6@NB8f6Z=)Zhc->}U8i566*!l+wJ#W=-MzRO_l;Ib?)dCZ{HT`ze7SE#TSn6ri32H6b=DUYVX}a6i^g+4o{AYZM10 z2g1tQ?lx0>NyiKCG#E&fQn-s17@Y}lsK#7FeJ0^uswMI8b)1paOEsZJou$^;(WQEX z%Mp`kPPn~Oq(g=gYyK3qK0>y%m+4N;^rLday`W3TPN$>vz;L@Q5msGtOL5D!^<9QN zCH1jp!J?I7xtNjq%}zFwNmUpLbxANyJDJp|U&xX>)ZzPYc#-r34`L#~J(Xak4|6wQoIToKP6d$tY(Yr}Zb9&;C7nPrQVpLWewW_Of%2(VOb3#Rtc z*n4Gl3BY7|sePGkCzaTOz3J{_X(|8V>Qz2u)%}S9f%inxX7;TQQJC#a4~(xo*8(D+DTLk+_f@yc_RS$hK4fj);+wm^}p)|eeniWj^mSg^!t&X>HMeWlnoYG6u_Uh3MGetL9{a40QV>w&-Lgis;tQ?R{6^YQ^zDhDz+3ZexooiK1k(d;rBeQu zNpjD{8le{F+zVOYiQIAj9Kpjf)!T|5%E}-jNh^cHDoq+OSPjCkopLrt*P-1;%cgl& zMlR2CwrE$ltU;oaAmIgDt6iL$2D@!3tjz_szBeDG&@j7} z(g7%I$yBuy{#U%G8=#gCV`^DFn--XrRqRz}4MTA%U9(WG{JpO8a>`aAS6Ksi0ea|7Z z?La#iIU3WND<(a2z!$o5P*>pga&9v(8v@j~uZwgDW1{9Mw6nU1;Jo2p11^vcXprb* z#tU5<{e(q_{DeE?D<~|GySVoIspBhs$EnbY!7EM2tJs3u<4Kdc11feAowMUefiEIg zBhWi4CKYoh?4EJ4C_7rW3;eOZ`SV~yR_IpjB($QV^m{M++X_16HYmAbrSxyby>0F= z{6GR|uvnoIOf9_E_`nVk`Gx+RM)dD~l`0+lb{8k*OVSR|p5&*SVzxTddjk!#Z$9Fj zoJV{ssD0~m5S}WD-*R6c*YCe4{x{aimY$=dx%NF)abe@N99tP_6GPRi?=96PH=R)G zYKxvkPx+e(4epifUpw(iwu(vgHnBFS6& zzi>^ot|77_s_3=<0v~AQ#8AXjviho*)4>+-%4oHqR!Se~63*W-M~@4l9^u1!W?Edc zaM2uGcA|~rFIVt2lW0{DF^bg_bs`X1YIWuZ4&NBkY8sZc+wrr<`0R?+nsJ)0xm$T4qNbYT8UmnlbG{AS?Ma05Pk`=$OM9B}bF z0s4-tcY6;GzP1H{pDKDIe@V{9U*6K$SU6Hzw^-|ItvzX9<$VtVh7k9X#qdJa&an;v ze&F;eZ}Ev$IbM<+mfwGMG$eIAf}%;DiA_*0pL$D#*I!uDv5X!3{02NmL zup6@S>}+wdob)}@brW+T8VzT;C@{ZakKX^os5TTawU=}sM&b!xWEkhJqL7^Bd&x(P zE7nUIjq6ANS4=t$^3ZgsQe1@~lXO19d?6plz2sp#nbUt4^xr=Hca{z@EO}TnolXmW zelB}tO<5g-M;Y4(Qwmc#P4n?hrgMu?V`!1GSk4D%7ZIQIEheU4%z>iS>g-pQlV3mf zqkd+)G_zfr*)Gj&_m7#`s1JjjQ1f`{7#Q2_AQ|n4Z2hvtfH6(@%Rl57+S9y2fsfEX zb*=ySjs7@w*eP-4(ggBS7}-P%BeXtjsnGD$hnv;sjXaT zogJRr4yeH=fInWhZBy+i zvr+@;SWeWE={_llpzNd$EQoT^M`81$@euvCY=`&*U~m8q{c6m3WIg^OD)NW0MbmRW zb?qj{E>;3b<|>&lu>TMEv@5oRDYKmurNS0lPaCEp^l zHqG!YEkDl(snVB|vpdj0F&&XWWc914$!407pKzOuvhxK;M>Cdcqwxdnd-lCZ ze+G@c99Hk1&UrpYk6I&MLb+?rNxUB&-$^6e-1`s0`OEO$|Jc*Qz0EGH+lv?`a$4l9 zd^t%+*_-e{+m;IjW>+zsx0SUJ8E?i)vBJ%xSqX+2eGx31qBpWkP#O^guB+(Qo6pL< z&qVb|a4KCBxmB2<>kbuj#KRcJ6UE*Vc`VJvx30&xzA3)n>hb-y9X??ZDaO?fDaX?{ z2KCmrtoK`WJwN3gatkt85*%Y#e7_S8{>-d7xwc2ykIAm6jZHfP;5Ca(g-2^PhWTZq zbyz6@v@rA=WxG(;oB@Q;N0^(P+<$&P#0CGRfO`dB9p}?@oHX+Z#a_wgmw4)+Dgk(7 zKOg6dwam>6q}{VrZt?WXxRXQz zeut=BzJN3I!}x8!%ElClU$pKQZSKQCO$U5zUWUoM#1Nt5VgxV0_m#&5u+E;Am^(pHMgBHH#bvg5VsfMKJ3eVoXPA(3 z;hZCybUREhUR6u>?spgjMq9@iYrgemx>%&_#YhI?O823z6m(h-$ zd~Z>l5icRV>n@^M2thO9?uGzep{6L(?#1HI_X zfk@xw#n=1_!DxE$^msEJuWeFhv4zk?>@Cljm6zvPS&T0;bW7&g0Is#^h>Y3KFt}+s zO=rLW7cZxPWCZof&^MH(Pnhr>sXA;?p)3(#X=e&Ur%{n@5RI%eausFIJ~Tx_?}f|J zqR1zl{5TyBa}Ho8tI_!|Se7M{LUE2wfdD&ECgCptZa|U0pS!TQ3oy+!T4^Kc!tcwJ zLvbZf-@H3Y&?**#YH)$q5Q;cxrVZDh0;}Wn|Mc#a3k7}F(X_}&9HSE?XjnD6kWS!$$dE+oq0xXQD{lk zRkN59+YjinBQ2+xS+5VR7xThsQL%Ntsx_55E4I zj_t2L_p!a@>AG_dA1jr6);PhINTFA#$O9D7@Ek}id7;<8^J@vUB2IGIIEQ&(?|G1~ za=7%AaGl<=%3gn5^!FwyS|jegdinJEk4MitlObU85I;_EQXiMVJxocGPiA~kA~j71 zz`C7o=U~74)&7HnZ+39NPu9uvai07D4Y#RN&?nUq?3C>C>zBtnc+wwX2$fIl{+=`| z(tBw+AGcLVIoCjnGz0)bvU>GpFl|Uxbvpg2B*dy#L(^@8+~8D!y#E=-cGH8Btlway zE~8`-=coDD%?WUno~QGi&JIc|Pe+ID8#xJ{6S4%RqYMuSuHJ0Lv?#{tT^OCw!GQ^% z+LWL25RJ^f`m)_{8G$}HioqgV94%m8C)Or7nA8D^>S5+nQaVTL^)CP#n$ArXhSzT|MI z4>+(v0?ns`4EsfB(At@}9aMDl2++zrGV{e0_yj&O_3|<8PK@&Do2HlfC|wlu&ZI=L zq)Lb`aJUiexVn)WO*7^|cJ@GEZdgB*;0Pm0Q`2-%kws^iOB$1}8PeOWa{)w!@_^kN zQ82w9a0-IxYi9rVi{Vj^H zdArpNMQbxf8UD6z3I4sA`B>GPre?m;b38nnzy9M4Ib@8JXXyMXLvPsZ4Z_Oij+4_N zHP%;{HOP7RwUVVm7N6zJO85 z!P$%rTD@2@6f z%2*gV46t&njsJc0=118Gx-Umt!2l=fEol#dsv05$+!7Mivp28Lka<7SEEa#jl|ucx z%K9I2+?S$b9}QNatm?c(h!c?|##`4#LFB_Haw=;_}?0Re$yC z8}_8GTv@n-r1FIRZmO2xr0x4+N>zUtOE3{gg*?b>YX&G5M+i&KUNZSt^tpmI_9TI; z(=ViYLbY2GE^Jl@LEB4?(+}B{Zyj3b?vJ7A577R6v^Oor>2#c4!MyG5XX)U<{#V(T z!*p=a?S47<`r(0;v-J%19Z#kVAFum?OHhA#t|Vc4uzs>UKL-&VpjtA{(Okj&1cKN% z{IU%dTa~eIpnmWM2v|;Xa`ouo%0Go&|9D|y#W_L605=_EdIes-e*D98+)8I}-#vfv z^6!9T?>{W#`epBztNU8w-4iPp=tpo-j*2;u5{Q6Ct)s+1M>?mKU}aDzImT%6Od`Es zaMS(LxTXjnSvu@A?3o&>roXx6LAvo8i$GfIW?r)Nk=iq&i~iEq<@RWDfb3_6B-MRS zHcnqigQLQ!S z=W&6udjUCgB*3-=WEh5CsHU$y&n~q_FH>!!HX0>U(VUU8KmBU0pIa2qR(dDLeBg4V zy99r_WgB$y-nCR-{}64phIW&3mN;K_bU7kLsnVF)f(}@Bsq*0Q)uWwM_OLRSsDC@zXnUq~I>2`I zQt~g45R`EjlLGyKrS7^wSNp+}%Y*%W7v^F=d~oSL^nk9q=-)uC+yks023dHq@9D+| z3)Iu7bA%*%dM9o+4xAu&L`0L3q8?Vaw*uOKLRy5SqiKo=ggAzJCDPKl*qfVzlJIL6 zRdpk-P=La?Ze&@jNl-ZBH)1t+`U&kyEV3#x4`&x$IWdz_r_joU=uJyZ{Ik>qHVg+E zDrc$`11#A4dy=EJkyYHH@Lr%WT$W@%dK;rgA}NarrW92HjnZ+cb7ATTeWsd%7-ED+-j}vf1PEcMm*cRnsK``%#&JE z$>dZy679btqP}evx&3V$olS4oCfq=0w{Daw-=AA~zM>4cD!m%qT!F9M*6S;rKd@crOBNmqy>Jn2>Ex!W*54H#r$Moseb#@%~@e zq`B70LA84SY8k)`tz42d6CVH3o?Xiv=^p#dluT6jqWPH^ZS;-vKb7#N6Rt#tb?Uv|*@5ieTb?BDXDb(_1?XK_#5bRi>YQF|P@ zP~z%+s7%vXuN&UIxm?ey3t}(d*2v7a(;9(rk&jTecw>yhqPARY$0tn36qLMuu?Uc; zI~e{;haLWbopr6b0xJ_mVU+TEj=@iG7Kk{1c!oBKR~cFL6fl8EwUj(%fD!nUY+}PqA zggy|0v#HL$E$lE0R2y3z3^c&OUOUnUCGEpmv0P|_P7iXNog)P`b|3EV@6T2WsQNig zS2r<~^_pCzz`y)!`cOUE_$nrYMke8wT)NB_#~{qY*JiCe&bNG`@t|wbYhsn500a2K zl@i2qZT2?@?g!gAcrGN0G0KsU&1=FC6kq9d8=D%w(p3B6M(c24J0cR|pnBS_Ej-Pc z;CnEth^xrus2AKquEY80@b;>?PU#0}y=paYu3}}YyiM9nYhl#CxqM%XYB2-CG0$og zq`z@CEO3iA+Gd*7UmecO!GDjX1En&$7P-*L(#!V6FTvd-zHPSc+8{B|ifnVNDptCU z{d7C37TrP%siPfq>CN%;vv<#bcuwyZclSoB^rzRae?B|< z=?O)#eDmb_(eb;NZ{I$D){y189v)#p%g4|o==$?7IW`08@|{j`=a^%9ei{9{g;0`+28&46#hzn`L`AR{do5U z{Didxn)@Q1%nlnB_N7Ad!;>C`Ep>{rI#tr6Jps`t-8m9PFdUK_q}9eOon>>8FRPf* z7Y|B*wt!mM=pWW1lA_i*24}N{0b>4OCBjMiA*0)x^2#9=10w4jJvcyk#Wg1VDV`nh z73e@GX8>6-ol&YpWzaSrCk1(Qr05i@{LwE^Wk`9o(K=40VC=6c3u87P;~yEw=-Syw zpZ6HDpIc`!n^qWup`#Zx%4}rQKUmJoVy=wq2W6R%w;k|87Q{)#snJnKD1E%XYERni zLQc7>bqD4&gO*#0Nr2iW!XcbiQ)*VfD#Dh7eE{4DXAU`Ak2=0PK(V6X9Jp}OtAhbu zqTT_#(Q}gQrIyjJLx%&Jv7`BgU0))Sv;K%yPF%=UbfI09Lwf+d-^KfxiY{wIN9KbI zRFb8ASPV>hz)6;;3-Wx>na#$#10AE=u8;Dao}}y>0a(+=aefNhB`4L!0uzEQ(3d0aYYIw$+(42nC4)qQ(OWt%m6wx2 zl;B32k;4+|HA|+!{2Wj>N=!t(SSL*)@(YYg0%sR`bgElrKAPgaELYJpG2DP^DM(%n zOTj`Hmv`_(v(+JT{1FS?$4BA=gJMQW1i3O?Vk|UgO{L^2ljR~^a0;?9CP_ob`zxn$ zX!HjhC3b)V4?yijK}jA(PAY1qCIW>)voUsq0_5>>9e2s?Y*DG02IFgwdst19yoKs2 zH8BblM*&%GM9W2|`{QBJ&yttp@e+Ffq{u#eQK&!+@1St44?HZjoCAvN9S zTp3nZg04pwKsV?aU|0#)S27xt6x;*S`EZO*BdF~}trKGH7(T1kh;<5m>1W!|59$cU z!XbuPYoHt>IWWcuYknj%sNLn1Pj`&a-Ag#j8JEDv3-#$*RL)X)U5Zj8g{ZOeDNbtS z{fz~pgre7=HTgW-Rb4?Qg026WuG)@}#oHhADEY|HVT@Ew>^#l$Mw7e5Uch7$m*I?r zI{Q)|KtK4HuCfLa!aV19LA*l@VL@OC@Lf-f)09yZ8zsKMc|ug64Id$0Fq6x?RFvOS z11FY8d^3ULKcdmbc~i@5YqNaJDCkL9K&j3#2#NbKMF1?1w}C<}`Icq`IJ$9$&K4y0 z*k11Gu3V<$C8q{bZ&)!LP>PRKvjUIYoZbWU62T~T_3Go{?fTS|(ZQ*kkC<^q*XAz7 zG+q0e3f+1EyEHjZCpp@rAkLEC=Mp;RN|U+g z@iyh#%889dDNl;gJRRmhB|pu_|Hpv)0e&bU*A}&GgB-)>Ly!{0z1*NiZ+f$qgoiuDVv;WHTr~*Re;_ zx&L5^YlL7(&+xCM=;a*F$2y{(S(=85jW*Z69XW?8^~JpSkhz`bZ$bAJC=7uAy44W4 zYNM@jeEP2R_JJ^YnhnqCe3IX?S?Nc2gVipzwL;0KDLn$OKxm{WNXcuE1parr!Yc@7 z6d_rsTu=JNxa3S^<#I-3lMgt>9-8N%80ff*V{{AvMslJzMxDwf!HcYptzZ}*l{6j-z2uD!kQ!ubiK*u`UM zIorLu$fmnXJRx>hld;)U^WA*9`)hicmV($(I1+J_OMTer&g+Wq$7*QPG1n77~+-*meN`(J*& z$4&1p3cTyV5weT@L%sl<03>iNbeFr?YLEd-$g{37OnH@|Dv{pIDgTMQc4O3zJv}~J z9_TCUO_U|oaHrPEnDhypdUseX`>@N97&D$#mTmcq-E!``#a()@P!uPyqOq&e(;-Dk zR6{Rh2zeu;%IN~89_<9UBX}L8S}r);7$vb$9d)kZy@%@VdhK|9ZvFzNORIVEAC1#{ zt!Cqw7C*W)n!nurxBqeQ%iWeY7TS%(<<-2fs+NeY52q)vrx|9cq2BJs#`sw}{Yyjj z^Ow6`Dk}Xt;lzvbU-Itux)1sBGkg(*GJ52!d&s-nJNW9Ww)>K5Unt?jK0SRNd^YC~ zVLODXOw{bX`H;v23w{!4%xp$(?gU)(+Ct^!aq(1DXby%dg@)c7v0UauS2H*mp5&(; z1jrS$#XUxSV(%~;I<=i(1C7N>FI*IoRImd4g5hlQG3o8WH`f*gAs7iKZXilBR(%;F+bfJxJnKMIyt1ekjNI zlYd4^usP7&O~DxmOitWAtZ>Fc*qKDv@i3{_Fid2V8HTTm^~|)Ofn20vePxaZbo&Wt zgX+o{5EuUl#lH%iK9?g=F*2*j!)v>R0B2!6!w)@cMEzOADV;)X2Nkn*de>1EWBD6G zW~kC%KsA}y!OZ^)F-S9DWwn(Jb4rBV!*t59jaWgTxT=iYPt*9^Lep&)z^_{_Rlla) zWsCJ#$&}sGe=r3FhcfpV5IZEaVjQb||GVnW z(62C5HP+sD<@LMv5v?^Mzrqk|aN9Gu`xQgK1N>ECje@Kth=SjbquF=x?8g!9JDA`$ zIwG^)I7UbENrE zkQo_(U$+IBV~q5kWz!w9#6u5_zd(Vq3F3B)c;)rPid_;+_a{8>D6_;(J77ktEtun5 zHG{oMrra`vFF)(_l9NV0E%PC&6dRb1NBxO8Z}4AwgF5xMvnt-S_v6RIwdy#xi``GB zgA2^Ta*NL6!G+>iJ{*pz<1k_KMWn|B80#6WzS}eI(}?25N4i8sjZJg1xd}W?d)^V` z9=Vz%%wpR)yG3QSytk}P#5gCl08;zsA~Z~7T|;PGyd^~q#yN;Z$TEVlIh)hMg>y8| z_0#07#(0TixsU_#x_qCWTQTDZek{EapwUki(X9iLqc_n{+l0mWd6_M?JmNoFt4b)k zb)&uvajQQnI#YJ+81dhy7H61^U-dqp-fp0h!8 z{f>9NcilZWaaL_bzGu6(_`ZB6+!Ab-0K=o==yiFG*eEA`IsMcbu^4VVCt=mkJS(Ds z8_Wy+3%9}9JR79m%=FP*CYYHGOt6|Pw_%fUjmf5)_}yOjtAqC4EpElAjr^*8*T41Z zq>ndHg52#rc<`lL#k=QY-o||wBBG!`sBZ`=)Z5(ceaWdLjR=T7A*?vMZ7#^itE}8c z@guZG$gAFxFuz4b1U_TY-^2_~%co}9w$-y3*UgSoUFdG;H>z2;MM<=%2j!`0T^$UU zo-K+qt@kxb8~A-h`;1)$V~RI~IV&|#BYHKpS?~aujPo5GF}cNHr*OH@_3FjEn7kZX zK$VmS1MsY*3&^^*(%Mv7%SvdM*iPX3&_>B;^tO=sT(I!XOx&l5X_h}7KkX%V?kN2- zN-Si=W`E(M?S9M2%AtsXX~|7l-4P;}GL3UA46qmY-kEmLw7@9Wby_ieoz)!`3RpE7 zsJO1ExR(0b8G#CV*I?_EK+W`sI%Ygj^UfVL&ED0wBJ!RZQ+3xijH!Auwi(kZRr9vp zm{##InHOC44W*~UyGYZ^G1*pQ$L``w6fz1+Q-CWgIc6T(Vst>1YeG-eD+rHUTS5t! z;GvAA*ECq9vp|OShsu)|U!DT;KgJ0089$~|hYu5_jOql=n zzz;b%!)IvJt)?1Hy6Gjegc<6uSrJeXe3)lHAC_^{0o&w$lzJ2ukQGG73gyA!Az&qwARlJ(yP+yWKVh zjfgqZ79-Fa)z34Gv3*tAJQi;HMgy{n_4o^;a#xpUlv667bO*!3t;8(Eh62`u_T4Sc zYlQr&Qj!wF0cJAzrds4;0jui>M((+a!Vx7t2oGzNk`72px=re$3N=-k{ zq*}(P1_f>cI)485YgQL&k z4%W#P+Gd8F$rC)7FH$sEM|&l_Z;?_1=|`W$l(tBnH^?r?{Rp&3!A?48<-+D`x(UKDEeIf0i4Dk08Q|dWMKPME6LerG7!Z?1u&X2h zq7#U6GfbdzN>Sk`eu_v*KQGGi826<)77#w4o)>}Ct_xU7_oQ=ExaPu}dEw$W>P0Dl zo;g{tajqnYW{WY1KNs`gg!#Q&_*Zm8;KC{q%66yDpV2@m=Si;Nq$Y-Q+Bpdg82ikO z`9eV%7r*$zfedmDo2sZ-PC-cVYgpVWwdo@grqA+WHRRert_jkBMKTE4S{rUPHoS;j z4|Wo;-&}F7){)0uyDbpyRQLT15BlXIdwUVzNpK$}%Qw@?^hhkgWDqSqka>@gl0JZI-PSc*C- zcx#0BiF40@p6s9UQRR7Gg@*?X)lUJehvYH$2#caKLQhUTcz6z&Ww{!Xp?e_{F-voa{gs7ZOgqWCCH$>C?IcQ{rZKH}a3r`NCIns6fuG_qs`*FOwCOVSf>6&@ltpa^>`%`@fauT(aucrRg{ zrtD`yF?m1)R{OiJ-c3js(&=;@2gV^|0CXlYMZpe}Blxl4%XFDgTndbFLy7pZ#xTLO zHVe<^GD!y*d6!#LV_#w2bqnL3P;~QQHeRHpL!yzV=*P?PEgfV4+DV>J`~}5a$TR3% zMf@0NKq#1NILs7)ffbDJK;+6}rha9h_4Kdx7kAPd*waQr?_(skq9!FlRfn4SX~C24 zhR!@d^-ozT6qnUPaq_))@3WrNCf^Z;Pll_PCqShgXx4oWh^UR4IG`X=0PYlVFEURd z2yEU$%x$Svd0j&P7U%`lgF6d_U@|Kc;i1$=r7&yuTU9qOz5pTe`~AZjh>Ntu=z87A zEs5E+MYLbXq;NX6y&-OaGI~pvQ&l@p>ap?(vlTgAhXFzj_dWdT`5p2W`6ff_N0&~u z)~cQT@J3pD+u2g=x4Q7<4B4G{YeV%2J-B5H;(si?@;9>dDl5I8wmTrI#S@n!DXk^G z6OsnAd1Am%{Wl!w{$nx}gb{o9qS=qx=&$Lx#|fX^Qoc*gek@+2dr&XonB{mc-igL{ zRS%Q#Y2mM$I~Hs4)6-$=)#=gqA?`$(maE@EzybLw6eD+aWt(m^?g0L zyf=zg3aOM*M4q`b8+h)OiIVo(CgKVyU#*l;auSYwiqYtYbn+;n8N0FL#Nxr6AANOq z1Xw&RQAz_@QgULmEyhJh>UOT)xYpxR|KA7`&0$luPxA53t@m0LT~^cNHuM;pm(G@~Sp;2eC$Yw6yHKb{$79~Cp0pI5%1qO5L52Wa3H3{FGh-fGkJqZ|N#JRzh_s@PBNCxoCyM8R*JqijKiv$iU$5~nS~7D1lrN#|Fq!)@LRV9kCs zr!HEF=wPm50ZR&|1XspB#f268i&7oDcX-8n6%n9J-qQ%xQ$LO5eJ8?=HgOt{X>1^H z!4lwF^DL*JWjwFycg3qE!|>t1%~lk_TIa;bv}>ns_FmHLCcC`tN9jx$l|c;xemh>4 zKcq8?xhimkfDGZ(QP_DR^}`0Tnf;d7j_N&)DVdet1Gp+<+&~zJ9hiMOqMvBR`IO-v zCA+2D1mT-63ekZ!A0_k|=9sM)?P$o%M~8@J;_T;BO%7u2_D^Yo z?D+PQ?*6`|f&AP`!Dwr@R+5Ptuc2tR^g;j=RmqA4JI=&YU`9UtQD zj`XTydi$A*9PrKN0ko}ND!BY5Mu>zhg8lK(-3$7?YreEtM5vwINC(X924XIEE!REK zZ$KOSkc9azJu#+Yd18Xc|1UyRi4gxq<%YJVI8T6x4?;)@Tb#n5{3y8{<2Ob79>E?~ zT0z<>vA4{H2XPqTH`0_+a|(*n>e-KYS+J)$M&^ENaF~ta9V*$?Tm600(-y-8uSiU) zsv&wDVAlnef`#(|<_nd6LwBR&Dq>AH>N7Af487<0`h-Z3uKeKn*)WfPR?p=HbRWdKkjlR>8TVbveH`B;f9z~mlUGP#l3oczhzn^*4nBDUred#u?rVDl#AShw*#$oMvILETy>(7nM7 zihk8>L=_)W>oQWImGJ`;FGyQSXJ`-}7 zKy?_KD6)1N=0m*f;$~88J32LkV$RuG%+>=M&8B3ELqgU(qkK&)zIgHE?Y4eYx2=OC z=A^=a{igcrDh^he@Z39>?gIRx8aXbQwo???#u>eDM;eK7BR?K z*=9+uA{VW#tS{;W>YK^9Gj&h`C?5;i83;K=t8_k|7Ba_5S#u+FHz zH(&64>m~i@!KN^rC~`U#NM}g<-Wq+c7mac1B{H$`y=#5A2kNhcDMoXD4fpItKKO9M z4Y5&udpS#oLW5}^QYdc5M73(WqcOCrRL4N`ewY7ATko^qJZDZe9|j@0hZPYOJRv-GXO3n=tGttM#pkwUTeoJ3 zpThG#MatQ0M$N5d6xKPso|X{&O?4>>y)?kME}>Wnj$WI-mF_b?_Je$(9bS$-o|dw zJn;>d^En2(LBy0n3uBJWv&+0#mT-nnqCyL)^D0aSd3LI(hZTX3G+VB4vDu=oV(tAH z2}S#?&WwN{32W;xUdcI1)TrZgDIX!(Gi|DplUF`Pdh5zgGL8pd-GLQV4OhAdY;m+j zkGPMONr8|nFv$NG~e5MmrTe)_g zbkO~ASdjKly_}aSI~aTawWCy1B&_|TgP6j>q~oh}UEZmX5&US-wa2wQvMZH$o|XP~ zGVFg3)9QdzxMtNi(UER)!1xzW`%P{IJ6n6fhbyB7esmM3wpzzF&%+|VIef2*=qB0O z6y;}Iace6I6jV6dPUO1fVeK7$aslcnyQ&w!mt}MK8y`C9{=lZulPsN0Y1@w5}0%vvFJ5G z$9r}!6zYk3r-#p|q%c6oBqUi^B^HAL6@{TnzE8SIkFCCJm3)fYJgT~vbhB?Mgse{4 z33m!@V3uU_c|rDEn!Kt0>wL=2@#BL5^rka77&rG@vQw|qmF`sFGW9(-+#@`iubTPG zvSIm_gJU)?<(5>g^X|K+eT-M#xNHR>+M3Nv4A6_6-sQ2kP$K|ezvGVH7hlEmKGtYi z7f?5H>VG};kW+&smVbik=(bK(I=(xFF-ZHclbBYwccfz8aqm&0V!g-8OHp2TYpJ;E zY^CkmM&q@`)xfNn`U6PS*(zLX=N7k#ZEg~qTCb>j9ls`Rr)@xO6> z9i?{1E9r%DwJ?UQ<%}YcsY7zXw^dzA^}L7Mwy;;Au(xpR!*^uT`MEi3>9|XQdtYVf zHkB&>Qr zTL6c@;Pkjo$_*Vh%zO| z$hQ7kKxo~)?*3o2@M6Mi>+QuEVw7?qTY*$W5tkOQ$vCnaJM$O9s}Sd&+Ia!c1=1c! zPy73D`q1Qn5P`EP=#M+~NYi0Q|2C0-W)V3?1=^>1rwUEk<~?XjH>6Lu>py}jRSc|- z-9zdxUx8{Kq;-u?Grzh0wixDmUqqg|(RN{&Tg#JO_|>@*b{X+KwBM={L}pzN<9&fK zS^@6DP=%KiKxc6$Z#By&2!2|1{5e1<;@SPY-*!u3O@gYq@Es8Mzoo9LGKzyjB~$=T5LnAVgJ=%=DcEF;$?4jM zhVdwKkwQY<_ApN}YyEKzLHbDYg$xGrctxQmbXzAMFV#d{@D!bh#_>kbz`1OQyeHd) ztKeqP%-tDE7@0x^b*PL>ApvEl$vJeMiDH$ z*cCdF!FePqg*Y+NrBPH1#PqL$4<0MyaD~ zv?GBR7@`MrYp*teKqHb-3%U?rWh&3YjRmqH{ztT)nlcG@B3cvcD89-CBs&rpUp(-| zR?P)#W9vsIU!u`7@7{BxZUKfvIUzS5`TgvO(wJeVy(k*isnat_jwv~1z*_ysH6b}~ zAv4n$^63n@RkiHwO3lhF!A$|fuYZcZM7tF^pm2zF2?lgu6i+Z;TH9@*-K=aF|pNMa1_!7G3>4$BjlCKziQ1x8=!d!yvxS6fe z2^sEiuxp8_IH!CsaN)^tL@IJMraQo==>ii?biCLQh#Tn`=TI>)rMKZ20x#g=-28am zHnZ-z%jSG89jjTR%f%5pt1+YM=m|v?f&Wck?m9vF+Xwim0p&TEyP+-J4rMkrbHqN4z-q%f%OUHk*Q37E)m!x&7xRNZ-~U4X%>f7 z$_m9+P|uuWqN4d$Rm+@QEl2T1pB#S)St`|Nu9#;SwnE9QUtB6*5AOCJcE5B0#Nz}M z9&{e9{_X93L|)A`dhR zv!r+iNn-Wabljlq*$aF@gs=GS+M~Y?4JbbJG}b=o+JLT&J_GBeEyl0JNBSfI)(T*4 z0oJJ%Wft3{X~p_{;y+?shfa}pvY~!dXeE3Ut7;T$XVmwLV|Y3++SEMOI-bEyXYQi>NJIL>MX|OUJRJAFz#HJc?=hw#dY~>V(%(rwb%oLi z8Ae4x)0Hp3f`Ibo{0SO;AZKV|GtLB}B+MQA6ZQeGWS;QV#P%xce21O!gqb@|qfx^8 z7p$&+%K4-nr}=_fT-fUXk`J3Lf0Y0H@jWNTma?MbQwAApRBn(~oQ^AMvYA^aM{VL8 z#fw@r3A^2Y<)n==RZUJ7^X;QSI>uNq)Mu&26cmhP?&>w1$}Kxd&Uf`#rno9K0Nc(1 zF&Q(X2u`SudhKv$&l?>+rv1%?X;dRzaNgx%E{AoMJTlw2xexsr=AFs-3ZKpSFA>}L zPAn`V%NOEhw4*|X>6C~;k-)g`uKU%YC%RhcG_>Gp-?b+-h_u~rQEK>h|CUn2i&d?? z&iU$Q#yU5&0wVbHd^|?+y-K#w<;JW=uCg$NxyZ&KgDdkH1EXws@PYajifQqdVis9| z$gRex*i|AnzQ70!Ffg>VyCIx!(YLE?l8YBVBCLR-+sT^#OCXf4J#bWli~3t#3H5sj z`DRM)OG+NDJuC6(4r(JoLt1apD`ZV?=bc?B%4rk=Q5ln9F#F}7LLlKKlDeT6W=Lrj& zrcsqAl}qGPVqX3Yj?DQvzQITG>R#zWSqCJZKO^F2ITAVb&-UIgbgCWA@()>6HK$Pe z1{5e{v?*Q@{Q)1?0F=M!zlSUM|1}i9Lx*=X;=JJvkQ;(-;ffuKjBV5|d|x@=O*iN& z+i)w`l9ebKuE?eEx9o+GXoqW>3D#Kag5YjxI=8Cm+`77RtIE#FZc{&k&aGmd6N^Kd z4y|B5vCZC38eG;m^&dOYF#)#9>wNmQD4TQ^>#e4Wp05^M)J7@kQ0X!Q1nFuE1YAFg zKtPqFsdsVX-utRp)r_*3hFZxz>k3C-$*>(LZFDwk#q`t$gPp8XgUYpF7xO%!$)X&! zdVj;sQ_a53;^0iJ5HgHBU?m^(QgP3{?%YdWPAQ8OI)E_4N(X~%wotDb`FXM~mguE~ z**q7?y<&b3{#Ds%IzF-=vvfXQ>$jVH(clE4Z0VppZAxCPU0qek5FguBS|%XF zjPpT`?jq>-0P>V}q%ytWRN8%}MAdu17+||7*yq~swGW80(tDj8;yu11EV2~yU(r{a zTjD)>!dzv#$uOKJrq*DGuTeVdB*&DhGFAR#$*{;u^oBVfr{x7suS&9o*}P%SeN2-A z`$aVoBRPcS?%I`|$l{uFfD;M9Ik_BhoHuHi9;>o!f^p%p%Y2dc$A0vHdt|?dshiH} zZ53MM?BT0qT#WL82fY{DLdIhvU*(G#*W(bx6^*HYM$|SZYO8l`!heOz?<9x9g zv-cEIMwu;##qLm7!iP)VHYgaiO-O)oj`0CmMcww|&H(*UqyQ$jtHQ})#b~f8v@aog|TOx zM+vIi+~TAyAQ~UR(eAZJ4&CfVW#xiXKM#SC z5M%G5^YrRSx`V@xz)xh^uphj8YH{VeWIq~OCb|EJFnUj!Mq{As|Ea3}ZnS>v`BKew z8J{nfgCf&)sf47zN@Kc+jEklzVZRTB!qGpEcsI$5`7`7FEu^DO;f5`4qu z+e3T`;=m&jL&HbT-No=koDvc% zon_u#wt+I@9E#Q$btowYAX3A!Q1~B&XTg!&y!)AS+n`e67jBXV-!a0K+~ej_vaJxc zS=tkM+5$;R2m&{2cC+lZsafe&uee9akKcmM*oKR3iaolJ$x2yk<0!v4o%(N?Rl#tW zB~n25m1k_y6ArRfTw=d*TEj!SR7wGPW=;3dZ}a12J$6!m;-mek;_RaT?2}9p&$WB9 z`Ku$Gu220Y9Db9c* z-O{0qxLa9Aq9obKeS+lZ?gahxA{FQIz;>t0arM;dLHDcs?q#YT`VRk5?Y~SqKj3{i z84Sp_S-!XlyGQ~ZYuIrFn4af?8dofpnnxt(2|}Nb?8a9p|GUY+zK2f+?wh9viI>tZ zzDVe~QP)7lb+yf%cr)tBA8lL8FA+WYgG-&+?72U0Vln5u3PQ=ZqU1A&KM2AasHZ)& zk>lsi?&mOuez?@oo$UM7)Noa zURzYEWeScV;P8Vf^kQg8Ff2Nwy>iA?INE%cHGEsKi1JWVI~38$Ao;8FY*rsOr%=xul& zw{)_|M)<&n4wZ;)7qyA_=xRaHHT!ae|4?g*ieQip@o(cn{pK$Ocu`!v%0_Ud$6vn{ z#RLsN;EYlvFwNn_N7b}+m!D)fUmx`gt;5i~egpsb@}Xms>k%6o?H-U_Za`4Tdk!j0 zb`b`Dd`UmNO@IYoVSzB|{pM@@U>6oYeM5{6Ndj$t_wiT2RIubglt?RK>In`Qe^5`D z8t~^sV_1y8z9trWa0?b%#Y2^AVb8VuQ-qfpBP|j6n@yyUY&Z^STe~D8g^-qcjepKF zkQ(w!Ljv?Xc|Jzxcm-}gXTyAdZ${}X z-Wg?M+LjmYUb5eoH{o8=y+KPV#r9fI+!)M= z0Q~dbwKj?z_;m*NO@>FeQU1#~rAq6Y+eX!iBKP}WA6*AM1@Ri;guo#k#YM3k51(W| zq%*pf1OPx=XcaeIUy*&dR|<BB(;}PB zpAgso>yDyPIbP*GXa=JL0Mtge(o{kn(g$%%Zc#i(gOE^sqG3cTzvmd1N|FI8IQL;r z_B#hbSSoW?A^s|h*xTMA*T2C)FmCpBdjguG%8L!h@dBAW-SttB*S*wa%shNqjT_AIC~CKoo^ z(dr8I@$*T}3Ye$v%7X{_GH=0uW!8)!BE{xd03S5S8i|DTtChLhtr5FpaA zlORy!5742fPn>6?$L#Vb5kRY}6q;S9qGB9wjc_aND~U;t@nr{LwHmnGcOO|8WF|po z`>v|EJn4pLx*nQth^FhJ(bh0-k54cZImV+snYEMh)SXyZ$SG51v(&NC+;}eq#gIpf zfH%{pWV5zFb<`LiRs}`e5y5iQmPfkhoEdPA!5MpO+ail@U-m2ve%I~0wrS%pY9KI) zb+k&e&b-u&USREr*=fn|!{t0R<1oJ*5Rh6kE{4B=-+P6Fi)MpWHRkX{djc^Em^1sU zi8i?r6zE+)pD$;Nn<1(&nf9k5YPs*Mqn#ib&q|vXJB3Qt6kp85p>>l>mUyByN5eM# zkx$PH41dr1HT9MPq=q)^+fXOc)`~1e6fzde?N(nqg=@Fa(lx$o3_C*Dwa*!M3NM_O z3RUFND64tGs8X(=%(|9MP^6S_f#8z|W3(3@p!yzIU8;K%hnT0snyy<{7unQA$H$~d za1Eo7^Tyi&Qw(vwN*jB@JL{WDhbaPn1@~xxSm;DTctgi^CH*&tE^Eq_xAef;S$fEW zvynE2*$acA&t-6(bzI@$gANlrJx;c6=%aH}R}ep|Y^;(^`svnqlFnn`bnEtv!srh1 zEH7suwEufHC&P~>WBC1d)3~2jG#z5vgSYvL`n7*pVbN%Bj38Z1GmP*bUM6jky)^f; z#Z{I~gG;5`u3uj`06JmnuXnup5htFRrkMANJ6HJJYq=R48~CSGkUb!u8v3CSHk7~Q zD97LO@PyHr(+NvrUUDG(SL!jygJED&cXC_vB=CzPCP4ng?juHXm`{dz4&`MexhauQ@J8 zI=9BCK<|Owaxe!{#WXdzoI2kRqbxP_PT3|yQkq2WRuxqO=}xwV=O5k1hW}esO~1a z*y|o*--RBuXd~{2==JLUMhpukQS5w>4#IFy+$6A^2){#dlkZBM3PXgLEwmkm7sou3 z2tu1ZPT?>sgKX^zQ+*@NsCG;wZCmX}LMwz&-EE-OTj5%FTLDOG(>$;yKo87MS7FDi z{tA~KEmZpi#9k39Arz%@B!kb)$&-UV71=+QKwrB_K0yT&ngt<}a#CQpJ}YFF6=V}# znDb`rO6mU(@3H+GrXQibwcfvBwh^{dvN9GOrtj53`X0607}<|>+H{B#H|5D9V_YcU z^gv{Ssn66TOmorEb-X?=(jtWDPuQP#D~)vgeA=0oik5Eg_7> zA<3?65Q&ReF*8+5MlEh&;30Br)mCTJR4B zzA-c}+sWmijcIu^Jh_jcw1TuH2n7?OUSAICG9B~NA36$fyCht2mcl@ek*|h$+pRl& zSjL8XaAkJmPS&9lNoNseoG5@nr`O%8de|IRyRb48Ra|k!RlK4O>atbRN~S^bDpnp8 zLv+(Xibfd>F9j+22;mUt*?1i#MNSJxZmcsn|8tPuE6LCJ+gzgW4=mVskCFqB4TGK5 zi?u{9&Z@&On<)Nc`aiXxIr%kVVXQ8kcUf7&aK>w$uh~BMLe)t=x>#tp%=nJO;JEgDfA@DEQ|&Y|J^P@U7h#$g@$UgiI5ar6t~02WU$cqibF}lY$!8 z50xov*v{!c;Oar1KG|BmoRb6{!;$jp;kBi$RuUuy)FdJu%t}4(4PgkZt|z3i)Rs>3~+_% z>~~u}*^?WE!xAIM2HpAytrCWUi(*trRW$$_Yi+2pwr#MK(?$`nzKU%G!LeFhm53Zv zYL$=Hw*=~xK1S7|u)B@c4PGVOmdh+;)NFCuY+RB^yqle$qv7sDh)OTIZ%`L8~6va#{6(5M;*k%e+x178hxL5$2+&^PyTaqT|}$b$seL#;{#N*Y_?Qx0B& zj*DZwca{DTqhE^UV)_om=={xZan`10yLfLmc@}a=R7KH4I&o6psLe%OS22c#Yxbk6 z=`UJikqh>LMmM3o2BrFK0=%RH}iMOdjB;&p*auB;#rLy0Hki$129eiVu)yJ z&JT*{SA8lc49=cOQ7*XjPP{W9)hG;|vx4EpOE?Wn^v9hbiUbWT(dlxtsqtxH37RmM zUi@LM&SYZjs?Ng(C>@Ni?+(FK_Lr9P8W0|{;*G1u_R|`@t2`elkw$eJ&m5<_oo(V4bsQl>;>AtQ$JjawAODd_oVmJRZBOb)d@ zrNvsRh-7G}bg5ntReV~QeSC2v?|n2>Z1317HHyKYxEyZ#K{20al;>UrmLTF%^0^x! z0M`xp*>cjfDQm0L)KIiag<=)@(?Gi7wAz1q6voQo8ag5fT!qDDWtU%{S-xda<#1iqzp7EKV^a{f!N$Wc9S%wDv>J?;!)z#b ziw;+7G)d;NwVNEkh09nsiaLMJNVy{_nHNZ}(oR&Oa7C6UPu$;svr2INi5nDVL!2>M ztMUWpqw`1k(eE7=j4v^oHKLJv%Rg@13|7}Q?GaD(8k21WQ4aU;4f~3B+_I!Bq%IRe zL1S={4L&G!+IemR!1xdJmECsr#5V0Vi*i5lbrYK-6CxCN-Cp=tE?+F%AXG+bKY1dq zsQo7xS4`sUOc773)eb6ZGG!$EP!d8J!c<@vif#h!2mz<%k&bd=TKJB2_yQRQ2MeE+ za6L>KG*SOgkL;#$@o)+Ms73hl{9Un7k>dl*3Rw&_fo;HibTMCs#ssnFiKZFGcaOvh z+@k{3?w;sGE2d)>unLR%bx+{mv*=rqOTJvHKs@145DS|duBOz^>jp5MBv<;5W`nF_ z7aLi)U)$!qi$U5g5(-RJ+Y>L}l}ALZTN?)yIQKYBx;ZeaBP+p{K; z*_R*+O&6qS!(J*5Vkez-k_%lfP2K7N;g;+J-BaTw9QPTOc1 zP#_3xL$y-W59o^wCsle)^;)m_e*PB>%C&;K(_m|ub#tDYL1pXn}~TppttAKiGBNW0+9W0JK_x(-I%&=qLM_ zl#(iIq38KzlJIINoxMC2ABmzI+UZk-GN@@{$Mn{xK z%G{Ia50LkpX{+VHJta$W7j96L3?~O-tS zrl!PlKk15iZVdPe$^i~9#RpZSTr-toqswMBpo!w@oUUE>u!Zi! z1+%mAzOL!3wX16R4zdN+5whiu7q$b{y_x<9z;;?F$?uc$^ zv_ESV0DxnfZo{5ISXvYlfA3Qd-L5pr*1>F|CucDw;le^pE!JK zZR_Fxy#4SaSusC4yZ7%bKmFakf&a{3e->YPnR@yUzvO3KBI>Bc>ghlHlAm>ns6!mF z$N#iF_S{qdR*P_3+${gaGTeVA`9%mQCXDW1vW&yR>z9_26l8#bL z+9QK_vv&>}JZQiv2j?mMzZq{giU3_9di z-6+5($p|~!9p_|u8$Hf9?U1cb&iWHG+%@t%x?hj9^3?n9aNtxL{$TzAWBelyn);NJ zfGUXUEk6o!aA0S{Y%f$Ys^o zIc2-+`RJp+Wb~H?{beB?P%Z{J zL4?4Oh*y=n0%3DwEo>=krCcUaJq2t8JEF`(7c0?c!!d2T8S`28`LeptYT5~dvlkX> zf~?Pi7xt|=TNw8T8i&ZTP?3rbJIBcvA0_t(Dghmtb=m&RTK%D+mP1kgz2JAOU6Z*F4dEg?WK_rLn~q^CbcxC3W}A=|0sdBHY~F zd^a~Y`>g3(OBqZYua6?*8Xeo9BE9Q7Srqj3E7Kw@_B20#e^5iFtH627)x6{MROfC+ zv%j0?m&J5hs%nPC+51|$`g$;Xyq=@!!nCDaj-!@DiK6}YGGmWMX`M{+#gLz&`fYOQZgS)Cy20*OY6_;3MrsM zyxR8EzbW+5f4~{O`MlZ&Uvb0Pw)~O4M%p?W#W&Lxcc8T=Me3#l3ow|01@67fA{w|ig^;n4M- z3rZVw1~;0%GN+9(_fq%hJ=Vefn!#pkisQr9DeYz_bj2xcd0MO@TYdCIq}%)!Cn6gw zM$b1k)7N{uKB?3xCHXZSps2AA{C-rERv%bygG~WccujUbohvdEhc%h`G~Vj0l*Q7~ zS5quYIKFk;L>&eJ^qeVIoi~Z-G@WT@r#{O}opCqGv%oXU&*1wgzFCeI#cY%-5?r#T zA&*gsxNS>3v$WN=2fi)ZXQhdy?7-fPew(_cgTVUDTPN-#!=}F6nrlyd*ph2g)mLEK z4CmH73yI_gJaauaVA$1u5a~w{?bF6Dr}M{+W?ft}j?QF*9(cwsJ^#Zk1#68E4Kw@F&f%{JD zu8TJhL|wwXfA}>bsM*ljA|h2<=GlC3@rpcp4G`~9Uc#XzWx@d}-Yso=Al|6RiC3h> z+;~N@hj+U{+T0ep^tQ5TDJ-O=Y*Sc)#OoFroA}Q=4kTnQ3lxUmaI)I4D1%{DKoqh% zM29@(5lF9tor6stSvM-*32_^NWgOs{*nlZSSUm*?K^>r_Ir^7FN6x3kuo6YG6~%jv z_ai=dF7VYa9iR<5b|^Xh*{KqRO*EzO9v6pWA}9ft0D4en9QO_7{`O#x!rrc$aiTQ} z=bC68B3@iaoDOg|x8v8{@&Vt?)w#(_ya3R?K5&)PcEmOgm~_im9)cW@*L`k6FTWoA zGjn0*Eg)!Bwk~Ju2HCW~Uo_i{Yb(Rr804@j8`oBPi1if$rRv9H=MAsrzS#8-D*Ykr zOk{M?_&gUYuX0XQ@U)gttJfAVT}SWw;cFZfABy}^$9KRmyag2adsY$41mS=M4Tq`V z`0B9;6G8Y<$j{VJ;r)B}?j-k;-{>4o2nYcol}hA?<0TQ+lLGB#JAeXTi7DGZQwIG5 zuAA97`IFR_*BUhB9`qM|LFoxqBFho^KWR)6n{Rsh5f;@oSzh3GxLoO^)9Dnp`pI>2 zT;{X4`8hPZPbBz%ug*B$A=)s&(!`rIbDk}1^eFx2!%fK8fAOB#&J&Fttbu~@X*`=u z5mtfHC;53M(kuB>5Ls`hZ}RzhZkTj=Wb=wKQfN%A8Cq-*;)r69k#H2B_w5!#GbPnn zBGM)Xp_SVPU7i0puqmeJv~i31Ac$P{HKqI28WRvEEAhDtlR|{mg04ZpE%Sk5K;E&S z+{48M?j>;K;LT+%H6sRy&q@c8NN3!-ZQ`6UrE~J?@;-8TA9Z=AxR3EuoUyDKSo32) zxH_PNItM}L)z!3(;ggdHs%|t~Y^QHP)=@a)p7MDy{b!6yybLWx61j|+k(M28{+6Z> zmt=Tgx0%#X>AjuG(IoFgdBaOoklP15^;`8XiE~{BmZFAWm+7g{ANrZArqlPeMlAH37#+?_@rY@p>qNkqPaV%URcT zoxwy{CB|}05FfZCIuhWu*`7b>?RYuXo9&nYhvfdr!78r>;z*cAyB8(!X;<1L^PqrB zS<~IWLshqD^C{eXp$}jtFXYt*ex@_;F*9mYf#|BYDFfPZHzEhiIEBb3lGf z74?BT=2+5J|6dBH`m&hA(Mec}&W;XqnS`$R$UgJi7#-O2+!ptQG>J11o z$%X&_XCO*?V!BR#)osJ%bT-e=iWL-t_S$D{_#To?j0ZK~WDA5>l>; zl5jdjiH(0pT&(y=n}k4W6s6nF3LRqqHqcv)(GqHl5h_HUAiKv3e$*~ALF$*bMe6x?C{?Gfr_8 z1-QwVAy`?)a(P~qvlqkjoGz$*s)L*bE4j!<1%+TkllN?rWb;LFRuE*Bj)8^9%47gv zpmt`s9OT0`CHQe{yjQO@jMC-6vcdLey8UqykJOO{LA(r3d#C}6$MLGk@SW&-g@$io z`~!WS7l=oBK$jQUEZ+qg0VX$YA!u%4db<(cB1}}nNUWkTLRa8h#jwCzh31My=As#C zl<5{4HiYur9;s7BBTaoc&YHSZqcn)d%wpC8q3jrqZtSy3WwSA9ye$DE)x?S!uV%F| zZx{}GBdZ;at$w6?~B#O7*manIuYuhoT2BX!zmegWf#ZfVOnir_m=3 z*L+l~dU=vMt{!%bUrh|400JRaOfO$~xN$<>axUi9kgZ})2K?4cP~8CnT)C{Arv4NS zDhj9B7IW7xWwDAe3}3&_ly#ryho%SoP4R0_Z}+7uA7TzI@VS%Gh&DE~UXTXpPo z%1r5LrCpTtZ|Zg)=WW`(#Z6Eqd|uf`5n9hqc&~A(@QaLd8&2ZdndeTORse_;n}uo)c$2USRX3PDUpn`;ZJNiB zZku<*-L7%A7E}E5oK*ItD>5I6rgl4UN#*66Kaf&h!p_qy_`S-l%gwFljN{owXEDr@ zZOcH~umqaU+ZI8@wv4z_rJnOqmpC!&BRy`~DSF54Zbc!M8802TGG(*5p)&3e(}i9| z_+3<^mc3gKACMcWQJrQx+N+4SvkI3^0kEx)VH>-CyFhz3s`(&49t>sVJSl`-%h3d~ z@nFD)#5IxWs*U_eIPUR3t=PS==YJ4X`d-v?e= zs-F1!D;g&HW{2CD4f;lc8_fbq_50H;Rlhe>?PACh{H*~8He@3*t9ooZ*||hZ_ncQx zxoxp(wHduEuibYU`m{}|Oi>)7S~ExvYeDw`%^9!ER}-~l56MB63N&k48Ck+W`ojiNYt{Y357Ec3 zpTE)LSKZT;J=47xkMkoIh-o?;5IR19Kh;HOaOr-Mk*i+P@pvst{}Vo(f5`Ifc9 zh~qN6z*nxV7*I2RW9cgB+2ch0=|GE{KY6+dEvGJ|R-GO|KBJnlEii0-TzP{m8v__K zV7ms4NK7`q)GGpMk^~`Uq?Aj{Bfyzl)ne&5HF`I&|#+k+1oh{)YY*EA+r84YnG$eBNH%TqS4m z$NgUYK)SO4^}JZjFPaBIDYiXsOW)lK^+&kFSj$Wqy2PJwo*F$?d>kJ$P98c0aPDlC z@(omP!ERK9sCBmW!bTsF?WR!ORM~O@MEk%f*MduHP>QQE-E=tB-7AB&(+fXwhxTeByx?qd+Y;AbwM6=NSkZbmldLY)Wo)?q(o(p`;7(FZ zhdp;TsjsRFq9kicqUbyEVmeMaop?bVr=)CDoKMC%YQ|zEnq;Tss!Z!>rWGwy-V}QN zmXbDB4W(pV*VC<$jtD2&IFFAB8>HTLzWmYsMyq8E#M$H<75>NmH!65Ey@KJ$CxiU< z6}TlXXS1(ZBF}{>+$b?tNUS{NzO*fSR2J!4wp{TbuV6?4`#Xajr_+9DzYNbt3;eYA z@FqN9#M!UbLm=`U9X>zQe~+{EX`W=mq5iPT0}Z0xsL1E1XpancEE+2L%_A?D<&0E4 zv3$`)=H_&|nkn^vtbMt`B^2#r*R)=8+`yNO7RB_zV%e(5s|!Dp3vhqS9{*UdPVV+2 z-rce$YWQqg-lVSGJ+HF8ZO0vYdYo6yqxybkm%c_hylG!nS9tTyq8xs;B_JI4Z#C^M zaiU!``nNq-mT0*<%Gtl;Rve)8n4zw_L2*V)jEmgDNKN`j-`{ggmM#0}OZGjr%WYc~ zi_o$zwRY7LtFL#`d?Q%@rhh)VGi)3_Fo$x3U#O)@$;i}snz@iwia zA-)cu$~V_DurI9JR1`J{CwVV!pHo-6xvRxEsOevOotDo{F&fG}amh zJ2&N}Zsl!WeH+%6x4uMu!EUp~u2Z%9xLZN@OF0avV_Sv4ww?XWpOm-Q*0$TQqP@7G z2V=d*J-2ro7HT`dp}c%TL5e7LRB}zX zwX~ML_O077}&rnSkmmYE2- z?YM8CFhzl!)JS*=)#}g{v2XyAv2~QORTX2a4UDbgjEUgVqtUx5Tkn_rvS@y46L3>u z%Z`?KXXHuKZ@Objh)a*0;*UELn15cJzoU4s4oO##5CROYOCJS>Xbm%N#m^P3j{-vl za)lPCh2!!V2VK~T5d)8%`EX&N3PYkPn^;$w29)~qvX@*Eh+T1wi)uE?6M~O2==!C} zq>!Bu#PcFg{tiihw*@iyl+6f-+3$vV3Xw|CH9*O=7{a%PL?Je5F7O-_d{mGrJPy55 z1-4Q#!Z{|fuJxtBv=GSjr=c;U22ra-x9Ul9u~^J{_wQd_UA1Qy>#`WYHLg4@KQ7z( zaCsj#W!RIUoqO$z#dsuGeMwcnoLEFhqm8uo&DxKWV~mbQ4FTeSlcpHLbcg%6u*WgS z;%p&Y$KhA|l28Y|MC}dmCDCZZ$Q6X1(QINYJXoSj-x!F|%KyAR@V_El5~E=so2{s?P@`h_ zxoQ*q{(HU2#EhG;TueThA$AYbA^SL|mpFe<*DR~n`6yx`e;^PRj!@%|u5YG<>0?57 zBm#b3iV^*>ZwlFO_^zC@g#;rLZI#muT1&H@2jXy!5-$hxkYqS(;(nU*RG^W$IqG1^ z%w&Mfa=N-D9DnRb47hYF9LTbG@!SnKJ=A^+Hv*I~`qu}Q^t82>N7H9&BbQ-JICJx$ zrZR<@Hd|CsUEityave@%8b?CD1KBdg|NM%cGhPL1=T|e{ZW}7^IGd#uGmXweFYj9* zQu+0$1)!{YQrcd}$)l!nbMC!;gH4D6d`%=Axf z)fk`g%wrio{Gu80!zCJJlNCS|obWYP{t_$4&T8o%Z6$A;6$Xq;!?=Aqtzw*L z)1}Wie9%3(U&Z)x1LOaSmEVYQd!sOO%V8DUMB_7!yWE--d?CO~q zYXr^WDY=f=X?Mu-)UO0L1r$}2NaUkYkK~K?8sj5kubCI@SmzO03jcMxZGv{o*lYM# zPc!fqOZosM@vM7WwPGiU*$$v*CS^s_B-u`4Kq38>6d>&=%F)?D1xdD2B+Ceik>{*< ziFi8B{#kV*soITsDgHJK4sJSC=N)ro>`L*s&Gr_$r5nq7UCp~PthgDUJRCRTk%wk0 z-e~ZsTCA;NL!mmb=q=H*anW66^GSLfWxDZd0!L*rOvrs5`f;3nf=kYFj)0xnB7t26 z&IOjh@Gp?F;`rYU66hLw59(G>sgf)ue5fjLUlzC@5cO3+hW`^*V%4&}>_n$;c$QZR zXdSz%B4cPE9s)U_a$=pOfq2m5YNFeCe6CTohPOYu8(G`GNn8KeuPm=7XD2qtgHLcz zK>tu&yGd5kTT)Wy=cr_<6pT;qnjMF~v~*$K4r1=4X1n1pEnQ_Zye6U~y{E)sR#T)_X%N!0An%@~*N*=J z-gHr>&c}IqtgkUA#jvlnuW(x)zEA|IR%bfZ9P4H>`<^espBNJzUw_bIC-f)`^@LW3 zATCz8rJ37|y2+UPf#(4Nm*Za<$rp3spQ+G}wMZA{XL+4Z_*$*W%pJlZTi^}*SKi+z z#o{f-c2HMMEJ6+dEE2?1#hVYsDnpESgQ3Yd?zdJysE*k}?{HbIbRdyPhvaYwz5&2yz7A}Nz3wmLl# z$&{KZrY=G{*Ivpz8@Qj{qVe(Jg|>`>9DWg~%(IqLVcu7h`n=ZA%A1ADVl*^5U8Ukh zg)d^*Pxw!oom$r{P^{xK6h}NdF=1}lAu`$%mYbt`sEF2Uv*t@LQJVU(Iie%nYNdl^ zs8;*%r5cE>v=HvuVuj{9QPGu=Yfrw|?Xu>6xZyf?P;;D<%54IDGqZU+?T0Zb4#7O% zCjSKYS==*HcOm@aAMWgKR*E*LCBYSZP?<~`hzJ9U38&&ZeH#im)8y^pQ9_GqnA5X` zl6zs`6pZRcHae>;gIE=_fjn!`{_m~2*;i$xMg$ot7l@VJDxI8>72V#fZ{@dYUJaug znpmlFj4OY%{3vE4ai-^DWu|>M6LD0Psa9@|u^<-2-c7g=`F@Nt5|zbyC=jWf`;}C1 zWsMZQg{rSodv_~!TZBn%3ZIbSefgiSA&T(G`h_yCJscFalG|y4pop#1Kv6nhoRN19 zn^2?f@Fg9239$)6`4K;*Z*U!{zLHc5ZeQnu;%JMKCq{NgWV}JPNRPcfb9$hUN==>P zMpqjJmI8v8fnF5|S@vPo#IW-PB?YM)p!-dh<6guJ!84eAhh7}`U-!F_rh3AC%#l4ZPQAR;`aX zu7H{aGWf0mN~^eR>#%EoE?_M4@eHl~X)a&)4}_&4Y>lrb37R*e1zD0_hyZY?10ox? z9+DMjuxTc%BwaTt4p<9iJB+ZGt~<>Z;mv2`94X5w5(DGORW@1BMFfh%6zl4bAf<5G90UW3)4_nqMgze!I4hQuVuUO#fO1e^FKrK}Y){4+tZ&4wUnl9R*>vCm zwlW2RE|b89PUj!837d8OwxVn&L&zB=-YzElBMgxUF<{%Nchl z8;f||+V|mn8#oVbZ_~ZRH$H1u#G`WYG_0;g9WzjH(|;_M-ch6Jd82P4!^^sKgm`I@=W5riO&%GrPVy@bn3#@o zEi*+T;Ejy>1NYrRsURX zm3x6qs*IDgSz9xrMsu;HP1UjM{1!DF>jFTY>{lvSW{YE|8IZAWHdL<=`=i_MZ+aN5 zygzSSV}DbCU0R!E}$amHqx$B|gnx~+SI;VV2F zlxp;jZ>#1-&%~S81|v1UWa!rN_#+%a*w-IxpR};27TNg+)g`Fi4c3D$b^{oc(YmG} z6`l%H&#Zk3+s$eF@*t7)R?V7D1@%NH`7h;KR~rZ4l5Z`0#H~^v?JDM3D)b%$YGE2 z{vCDk40zVzko{^L_J+trjOxG~z-J?2{eX)e38E8IdJpvgO!eL!Z+(~Z-4KnY7aQ}ck8z9x?W`P zu}hw6d$;sdySew-reK6{*F~ZFFX$4wxn%0Xd8UGm$-Ig|;h#jN+Xk3IidlpCv@8kb z^O^Q6!;0{oTndlo&5~Tp^e@k^OlM?2GS7d^B(6EJO7fIk*G0B=ysw;G&O5yD?sCbr z%U1FP`RjR3TIBljx+oXYiOpc*e99Aa5Jf*2a`%L}J|I-@60TLBN=f{3iWq!@PgmJ| zNNWu$KP^TG(ct6(c{hg>Ca0+IS;;|F^L26$zTYF&j*4l10h6LGhPNJ@O<}yrKTi_} zfxHR6)rJx~h+(@{#2O21122oRI34lT6VK3S0KddUh_TmHXm!K7+DH|E>9mic02N&p8pLUv_ z?`MlxIdXM)B#ViaZ49|DrdN}9bV2A?|1*EBbN2F1#1uo1Q^lTTSdq(4<$)bbtUu2U z#$>Ep(WX_(7Dsj4U~iO~{CLqIy&eg&-b6CZ-$BbsCkwtSvAb7biFGY?PH66A`Ot>T zEGF0;Us#7&H-=m8Bg;KWSrVkm26^o zJS6Y+_NRPZreTc=okvkqvEhN{6u-p41#TsfP48dvOyxy!wg|F4(T^R>18Ws~$)Zi~ zHlfHT<^~_VxCdEON$GB<{Sc->%53hCl@(`i`W1!cnF$}|E5byj zw)ut)tZZ6m1C#a2Y7)!af)x?5w5@75^*_r^KqlDTz~4@IV^llOE@_D9(R{a@;n{!! zn*cyCaw>eDjh5v>@D+rCrMr>l!}DATsId)>lDR1`9F39O z^NCM;6s-7QzswuKl|W4(yUdt(*o?YKFJfP*X8~luKM#V^sMA%dB6OFBebl%7P4e?> z6MvNj`ZaIg5XtL?16VNP*Hzm=6;(Ln#3LRDtzdXxBeUrbp{|87hYcbB49Ui}EEQ0z z5qFc>EZjcPINY$cqpEEx7v;!f)=V`XMk4=YHy?&y_5DMLDfg2r96)#0`Ltnk0pg&SsVY_ZgX0tj!Kx&De5w$I8Dz>53+>9hG77f^c zit}^mC%Dut7qoHBR=ZI9*Wf%jJGMcG;70xOsCIm?MZ1rih$A{ey(Y@okrQldrmiDX zbG%_{?wYCjOV!lGB6fqhxm!Os$7*iU!mbLZDH|A^2 z&l@ox$}!tc(XpPQl`X?o{jz(bIl6(b>-rO9<1F7$h36l~r-HTA@ilDfP9(f;k3zrys_WYq@;o-zV=DK!4zhu{B# z(^!d|N_4ufWt2P7Dcwu{T=neF7KY!^`|$>2?bwP=RWdQ|x#ab9}JQ8IbmHCP;QjKMh|5n8~8h8~>nQ#QRiKncX zb+J5w!g;17lyvXyA~r43jq!cGptmx*kslCu?WH+p=K|+XOZmvOj{)c!pI3kb>I&oV zRwh1gfFb3@9Co5A>z+_o53OILWc(3{K<$(h4O(oFzfIU?I6e+J!hsz)%LjW6%C1vh zB+p^mBx+a>w&)=NN@W5ld$?2v{K*78cJQAtR4x_(_jvrb0>{T?FO&`OCOu@NC{u!) z2dfR=<76i)`bJCU)P1ChB&NDQ3ek_Q+q*Mem3P-|`rM#FiGGbDL?QHnp~Xif{iC9z zU7p;IzV;jva8Du>pKM`vrM8XsB)28;*|AQos_xqFB)^>)&_(99`;89X|# z#msSVw^t%z<*dsRu_A(F&o#I4M~D%D^qTMb8=%3Tvb&@YE=r~B%Tioy(aNI?)ueY^ zFGL)`Kf2mQ(=)VDywHY=e^rnHFIdXHO7>kvNpxlinrgdY`1pQ$^_mJu|LA%KumJ`0 zvx(}D{bq&VDvkGZUMqSvt>ut=X)B4{&TZlB3ZB)e)+vaISCzyoIiIv|59Hvb-_~D* zGtW$1TgZt-$)+5l1FlCZtM~nm^sc0>f;XT;`*PVLCzW76*P$Y-i1HsO?qf>L#79x} zoBAkl$$*<9`BBn8>qtEJPN&g}KLz4L-@h?H5NrdDRi{B-Wa{T|6@xr98UL5fxb`xx z8x_zVDkF^CcU*au-Y4wMgp46?2)Gb0=CZLBTmt)9*-goji^9P6Fb?REmaHN}(2Whj z_RNjk<&G@1Xe~6i;*5FN+n$Gut$7F)K3nuqbdq+zK6mRI_E2Z<>yOgSEnZQMDSA}=4V5f7F_U~0}sj#;{V?&$*lm5$>uGKb@UgSapwFSjkXj z>TkqaacY}k<#Tl{d&J`Vowz@bzATEbjaH1MlxX7U~=te_Q5QWcRYaf2{01>~% zJ~fm4Qpbv6yfqQbX4|U-&;`y!mt^0pZex!kPt)dJ-f6VRq$id*L{E)wGiFs2b6`O}ebES~9 zuP%F=o?RNslt@N?mFgx`zRNVJ3*wxlp7*nx{OBW~RoNyc`^|3kvIq>p5=F6mKq8)Gpk9~FF270o9ayBacKjQs{$7ttf0e!5vPy4Eg%}z$)nQ4ij|R?3FF+0D6kBgjq*(8n({7Rm!i1x3`wx)fsrNeS zS#36TbfS1pSZvmvLhj_jUVLz;@=1M*$8_2_DH289qq^Jbh)1^Oy6Rr0i&kDDNE*0c z3xBl4d3tb{t>?DpH~%!HZIb6hb&VN}iovJd#dMdqYIEzECdK&#{Tr?>;P#f058hQ)GCgeQ z$&rF4!HW)DjZ2JqFkP8&qd3m!C#5_X25@(pjxEQg5pCmJyJ%Z(33pBWCIDLT+lo95 z#EK!9KWskcQpEV(?Cg!TPqk*MwWPvanJR=G>Zzbi608-$+7e*6TM1o44(DpB(x^ZD zF9QYga#d@ucDA~uZ5vwD9Kk7Vf=%#GY5hfm$LpCbr}@NMU1D63HT{=Jqzr2SB}|6) zKsfA2!6L!~pG{5GZxKlv-8W$_J!eb-=>Tv*kH54}N$;paVfo|>0vxnGDhIel%byyN z*v{pAi~eZ(x|178CAKxdjHTa+!cGw=x(s%xA~{VEanRp&t`DMU}Y$Szg!Wm8rTdk4kh zZ96?@<#^@9+whbyqR$LK(Xsg04gub!F&ZUho~tVG7yvd@?B=oQ zDW?zOlUqT33&O4Xr84>2-!QgbA=~X3j3;E0R+*0`WRlj|mitTqXG3Cc$yf;0(dKLS zA*+MYkh+%!nOFszSN~8{rWyTrh>)Y}mInyV+isaJhtucEe;t;`toW3>A6?_d=jg(Z z%(sPGr%b~-%#XMExf>syyzI$U{*iHEm5?aK*@E3&&9O*WWO`1_49Eyai6IYj_MF`n zi8Bh&A_G{PZvXy!fmlFWVgp}j{Z%e#>YW~kF?OTxgY0CXo0vu;7^r5xT*3AYeVCGC z13S4nz1ND3o-gVL@pbk1Fmwq0iqpWbc{l=feN*Xh`OW(V2i@%!>aZ{Dn@(M)gdJC* zyWZURZ5qC9qi;GvLZ zK&u*S@jN$E&~$5wKW9l-GW?h#)u&+?u`kBmEY=QY+2M~_c3Lru^D*=I@K|0)SY8LB z76RN|O|=*a;*pYj=$USmGm^_Bo6o0LD3_w0$(6!2pq1(jwtV31+Vf6hP-dqfm}p>g z{TIq!Fq50+sPfY~{ic@ZBlF7CD2bGS=1kEx zCxL+Jek~Zq*>~)5MK}t92Lhs(P24s|10yEd=LwrU9#~|Dh!>3ApV^f~y{n|7LDPys z_C=#9C1Z8T9}T!R{j%j2G?oP!Xo*-kV_8m^%q1A-8^ySxJDxf59B>9JQE#T?Kq&@_ z6U^Y1RTkIXP~46y*Gl-aXnC*kI))l0EEWpGh!!U^k~5bRWv`Yz5h=ZdtCVK*v}oZp zUo5b5MCr6v8(wqTrL%$`nKgPZy8Q3Q_7=Z~Bnz<)a(hC8?Ddj78(buJsjRzbAzmiq zY_e8ygG?jQT0%uz)M!iEbC9uP$K;uUE&vqbr@_&J8XU~WK&i=C=H&i_K{M2ffS(ke zSgi?VJK_Wwz}onx>bP572W3~Gro_t{ogtsgv&P1!4$f7BI-Bt%AUZebCECf*!whas zINsRwVR4+%n87#Q#ZDMlom(d!>l}S!fF_Y<2GiM^0%TAO9rO^N66O`{fHczVzz@kZ zzBpDB<`PZgor2ijRE)C;xS~-tJrSpiHm?%|@KS%O0R_5*@Dm$&*pf3mEo91!D%Diq z{J9^|%U;wWT|N6=tYUr<0M6#q;S#|jS+$g*r4{_i*q7(aqP$2lXL7K{Uume$=F_oy z4j3-czajd9;0=zW$oHlnnp@J?5nd)PPXZ~T*j#GL%hSgR-W7!_4LUxb&-F%K4x^9d z&`QG3`jqE0ieE+I0d0?HicAv1SuqE3gF@+Q&zrER6jBF;)kb7C1Y5W{DSAjZ&iKm#PoSKlmQs``KCD^;BMXtb*6^UTr(7-kZXlE3*a9Afnr+;2N<}I zKlA`ucIo!@Eg4%I#@5KudTYkSh2f87Hk@ZrL7wvEKuY_l$;Vq7Z$Bn}cR2K)= zT$Jm;N6~6b`0B_mT4;BU;s`2ws)A6vIL1+Qlm+iV9GN8+s6D@u(j9eA82lVPb_L-t za_(z<(kOCKK*dlnu4w4gB>MFjVXrra=rMM^Cabv-$z8_ut){|gLrOFion(`{eR-c+ z4eYwb*I7rJpw&CDix*#K0|K7&kfi2$S39jc?__l{h=idb@o5fs(b;q|r1u)W7Q)%J zy1!1yQiLKUa_A|0?jcnV*(h4VfCw%|y_(yeCB>7bCjzYz`Vo_vATAMa>T8fmGm0u-8ilk=*;aTbWgzi8?2_L^mJ z77uQuottHz&P+^-_DYL<$}k{KwU$(xmoUIy=4rYrE0W5z%6ed)hk^Qx?rcUu0VJTk zSJ-iI{p@qLs>`5$;g{jl+-JZ*M$m_5wJDM?5$MSuBCeZy59~R=oOCC@{&^G2A;%s9 zQhvUHO_2gJBCC%dzJ{$2cZ@Y9>Qw>la38QC_|rH!C!U14d=oH{M<5ApYZw;K9WxiR zb+a{Fv<$^{z=)frWg;;|#kV!dtQ1C9! zx+AD~S3RmD(FLLkoWrrL(s-WWkI&QT_yam(G!!n{ILF@kQ^Y~GTZG3YEb9jkBUXL* z31QbM6)+HC1v7jFr(~$>b#uG~2_rtbK~WtV2|;o=vd{ zaQ$AW;%T{Z%5_U~bDquO%rFduD2c+9cf@ZDC*Jj0=(P85P`L^O@gL5CV{`b^Eo~0B zhriooYv`XvE=XMDFp}1SC%nn0Ooryq{FkwRW-(m*E*W!cH(-#894_OQkRpzD1SkIX zkuW5emz7)CtxMH|W4JC=UdCCB&AUwua?Nvq7vk|4IuXbHQ_Y=B=Jycjq;0=rvhHGY z1+h}4D|FL8=y6P z+x3bwgm#FZZ@EJZ_vK3dXFPL8>-$57U)pA4cs|drP;p#mT|8tODwA&5Dy}03!CZsP zQ4BPIIT|{S^0L&wk))hu^D@_%{7tI&UtS#@y?FoZ@L%5^LER4zIDQSi_~ZrJQ*y8y zGJ`DCO@1(14s((F!@Kt{o<7ITpxxRK1mwe*o{()YU1D6;GPyg(c(fzcci-u_OJqt!N4~TvI5lg+G1|3jh5tg`{(+O}-HMT$ zO=k1KrB$ozuWr;ZvE)2@3rDFGmFhQ8dAj<^6)L1v@)LSH*fWdTVp1kOqpoEyCH>rm zn?9T!ySRl9jVF?cCnnpAzq>{)NKlR%-Z53%i6efU>PUPl(b?I!G?CctS7`kFVyeps zHF??Mf-Fe$k=l8mjz(z%KetKbXyY|9O+9pVGCKcG2I~9AKRgo1t+zxDsttWlaF=j$ zEsDh&`uo^CBoiBL3dEK++u3Xew`}I3Vc$Yod670Qfy?Q9H$OW=Edrc3lj&r)g!>8{ z>$@OPOh4r^7fL8;V=&6fvS9@&S?#J=K&^HwmTAu6HT0~!hkAQg(qZFGr`7pe!;7dl zQW55Ca8NQpSB65TfnktXryY@Lv^Pt2QM+o)o2mf4sDd!U-HHNKRHg350tQ@ww_!ZN z7!|-BSq)R@?7gHHt0*kreI$%7^?-@1P6Zdl#JvRDlj6djqty~|-K*#~+NU9SYZdPWuyS#~RgyCM3dzhGxfxx*C}!3mn~;-H;f8A*8=w#c>Yv8s5ydC)p%`^h zPK{MAHu;Zz>?!!eyiV({yGd7KpGtPg_f^KtrdW{)iN?HJw%A7028DJ)jOFu-Y*u1B z%Zur9G)(ZBkM0d1+e}%Sg+I$ho(-`>DD(*h41>$ns2CKBmJ`Vha$j9!3oMXa*(TE~ z=>DFhA)`kfmJ)sChV9WAhf%S%YuM{()iYURt3-)Qd=Oj z$UL<*-Qq;wP?=;y1EX>u#=seKC{>5^K=UW6`;mOZYYK}#TeY>Ubtg3Db|#7}{|Ph= zP5OJPdm;;BxiNpXqWsHv{?Q~@P@_q_rjT+u3C|I7uCRh-;9=w5x71lF^{M**8gCk~ z6vn86X*GD?ALIVt_+9_%!oRrGd4Z$lWJMu=bY&?{A*zf~ezu@kKtTzq5#*w9YmMKq zAm~L@#)S-b&G+R*CUlctytqd2xyc+ZrZXc`Mj2&JV1p!b5NFiJMmgg-eiuCgdFI9W z#l~Jx^(?De+%exr6Svp3B_KnLq6>X}d;Zo4nxbXlO+$?D{c2>+@Xt4Q&gXL-(7zAaT;x4Em;3`Mb9&&04fvzZMnpXPDDBU6q=Rnx(_MMAl_pp5NS+ zsn*MvcF5B0kWRcq?B~(#JEVF;z1h0fD^<*t-@SG1lI$Cj|Gaf=R%y$!`dhfJU9zP| zc7t_YsSr}ysSS!CQq7XqX6315>EqhdH$;7@U2RQ7siEXG7KeOfxD8$lw!nEl%d-X9 zd9&Ns?i>U+JWA*-GC9vD_&__I8|5*G?L&4Qc~6w7G{`I_tTv|3(hBtRd;!vAg5lgU zn8%kn!3j)qvMFzo4}o+dK>!+tj!~g)7i-iDB&f4tS8tOsYy}g1iF;-8kLBy zz%L@I7m->cP3dWl0#@T)T>*Se6;MwM(OlDy1xvfye4LHoMu2yUSO4=p-&6|qx&r^} zBYfCyuP~533XqxT2$?uK5w?W6 zgV*wI9HZIqMjOWx;Yn-ZojoLQOTQ5d+^O`q=vT9M{YBv9H(-g@Vn;3knC}HlcJaK~ zJgCh+8xt}KSy^}b?aO-VqAT5?EvKkm!J2n&RCBh{HFv4zIv94^JHOLljGJ)Q37-oG zseK7OikJVUuFrB0zgwJE55dkZtlUqgqw#l()B?>qboSaF_~=e{D!cq+zDVU3b1z8? z)LRxE+|YL;GO|v)9x)~1+N!4s69mZ_lZb`6*kW)f6JK<6{Ju6FS z`Gj8p+HST?FUmRe@E-Q8&k}kAG;1SCqw262jRdH=TjI2)gUV@T;EL^~{d`$QQ^v&}&kw+bLjgAKR9{{oNu+dSV_UoFX< z7L`ub*URl3?*IEyUOvh*%z*zYpZZvE1Vrq6H#g-geOdt@=j1X*P3EH>Bm;-=wI27q zJ1Mu*fBSd05j-w?^!%TD3R2HTpcl0fsN6ShXAkh-PyMxT+~eTA3WqrQwpEUzv~=w* zhw&^I@hm%TmRs4$ebFPC|GX8dJ;i}KY*cAH$DLM(G{Vp~{%x_g=?ow&<&P0 z#BLC2F)L<~ympgKr5-{vI;2~0AdfW($@S-*itAUk^S>sRe*T07u%)ZN!A;3!0|F9L zlUFMd|TgfdD>2K;}V)s81VbMhcfe4|Ia-&o7 zK8RcR-<~*J&xcL-J;+GsxqY)KwQ;Nte$;Z`x6I3v0j$v|d16KKj1BjJBJ1TU5;)f z%A)93t46qB_>#}4g`8PbTThNGNyaL@IUoyR4M2}n8DYl+S9@_2#&Ut|stfl<_LNbT zDxPdqs%zX_p=iT7!020*^TP4r;qBF`O^wcW_o&T{Mtj%vw= zS$RQ#H;^SoPY#o>$?KTSJiN6X9!T4*cE4|$5r8Frt&a2xD;B-p(<(zs{6PIPTUagN z`Ea8xi38$Lb)eK@l+6}Z`X$4%3vT1iPPuRB=4gs{v1V0;SlhNQYQs!1GDbBFhV%25 zcrEVLDRNcXyQOe)k!Oo>HhZdYOQlGmlJ2{n>?AC3fT&m|9Xl$_gx8;!1mL($hIQz+Y9VQIfSJQS?x~(_i|tQ;tuxc8FA#Y*d_2FdSN(F_Y|+T$O1ZWa3cRFl-(r zeiHCO5eoCYrNe1Y%GtEc{etv&)v5)dkGM_2Z;Jm&^4cb^1WIks-=@;WN+SZT>*>~- z?sGleXNp4Ybc4pFi**I33G(UWJe!p6T2BNIvHEx&__ma_ z$Ve_a5?RvHOPev=SxAH=fs&+2-8(?#;9Uz`M;xJQHUq>WTPbPXswyOKG&j|oF^zv0 zbF|T8mF;zuN0=P{#W}=z`IT7oA)&q{+q9>EDD_3t6!$F(Y|k4V{z*0k%I!O^n6jMh z78CYjdPU_|Gac@2Rr_0g=lZo0R{QO$r9rqwy;LU(tUW8&dUy5u$w5Q>!jdq`ps1@E zRK`bXRV(psG&zqpveCH7v=x_$Ud8Pr)jL14CbSo#i!icn#--eT7WZ{%PEYV|>hDs- zZblpb=r*&N7^r4Eo2;Wf-MK|oTc72P*#6-hcews~PUv$ITRh3fzkz7YRps=hxw%7B zr`f!$n5CqPy5VNKVNPRZ4JbK?!E>;Ty=f3;|>iWV`Q>vali zO%4Df_(Imtf_WTAQk6ZA?LoS}Ll9=?#n8+PC6(T3)@ot=+xy*K_lLb|t&B_g;}4cH zydU4~?ftMX9>3Mi8$SQ|b8STLKpTR1ybs(g-sK-Ce&`TIsLHw9d!TZM8XHLa`2lH_ z_HU_GdS-fYOI3-1SW}!LG;M`CieYS(A}U`zQ_`sm!Uzc`T5dHukje1fXz3Bc$CDX+ z)p%^yY!J1qBK?IsOWc9C|ByIAp z8!Rv(l?q!T$1p!uA8f8mTE0_eQ%XR?hYPBpjT?lu8x-4q+=zmv6W$lF(asgmeL+M& z)kk_R8E$;2xY8*F1ZV}v?|3~8w4=Zo%!zrLV$r+I1lqX`2)3o`g-j~Sm&K%5ort!K8 zWN9$a%S~Udd%i2kUoX~4ihEp>yca)1tr*x8okHmEZvTmw#V_G)``0$)#T9yr8(B7A zzbPgLIc9oB&UP@YdkIDS#vQM8uy<5J^nZU>?Kn}#)!$*K9>6}D6{Fxi%#$55fd0q&uOfWaHGc-j53REi|ffS}mG1#-}2ZGIE zt{!z<#73B|L2vlMnp906r^V#yX!MQ@(uY%+ANhpO+7bGRt&=M}j7>Mv0SRT7`DEd{ ziej~Xe^=NR(B_<}m9^4SAS5|eB9K5eoiA;4=ZX7%#horAyQRzXFWd6`i!)V(TTHO^Bh$eq?+wbMN3yu)e{LTf z_bWQ#(s8$Tnm{MB`z5-qP%(RQX5YFP7!oMS9|vU8nNK$#o6EibVgu4Y{{PH?biD!L z?)*<*;Fa={6Pcp>@IO*p!Z))|Yzq1&o4INYOgW(pCu0s)u$=~^LVeImrn7}}C)whj zPrj7W%vRj#>+Z+K;D)W7gA2b$#ch{ep>-2~7UeUg_T;yb>!#Er#MB4AM&Jgz3Rc&{ zR&au^Kj-soh@M^808&THK@OjnIS;dYcM^r4jfxp5LQ~FV$K3Zo_tSkQK9jF4-piSi z5FXT@*~-142i-yMSF$f`!*tAZ-S^Htp2^g}2P$;84t_^Vp4d@j6HGrGpi+t3*`nXb z2tb1_4p4|SvrQajI4ia(75{W_j}*6b19tdT@5Zhg$a}I4G(i4?>7dk=Rof1d>b~)r z0)yXCND}lR(;=rg@E88~Cc2GKi{Wkv`1vQ=vu+W~t)eZ6pfw%mBBI3tZh`q|#`v>i zbX~#0FA+}{MlTy-P^`tpSm(YXH2~f6Py>n=HKms_!MZ85Brl#{508!x-+VZF`u?bw z9D~sLb1UiY!~b_4w2~jY`|4r#=Op6+4*#WF|9E?F}+jsl6m^;i`UOz!D@K>>;=|tYEj60P?CAw z=KYnsOs$MuEo|##K^2?KhGz~Y5A^ud)&?navbLfJ8vWB=fe_cDk%Id}U^eJdip*(| z8_sh?_iiy@HIttrAoYBRahInsW~0&c3OZvn1!i#R;zt-={N%R#)!S$9Up)N)?e2DL z!(k6E#>;W6o9Xze=xZulWont}wXhw94NvYtyYH#qu#CKU^|lAwkYxd`E4Ket-$65| z2$1PAgg87O<%TcZVp|AvHYS~Ff)S~){i2Ge|K>K+jaOt_Fq@Wzied#TTOTR5pu=}Z zuim`+8%<;<3QkVAr@SK~3OfR@&F0e;!bRDb!Y`ix<;AnZ4@ZvKS|mF%L{DST182}4 z4Loh~Jbn9@*ISijqDi04r(?Y%@6JXb@D1fi@U9GJ!>@<$x!i{>H-s|uhZ^-l#c)zX z1HEOQ8EKW6d>@{^dGY$i?=N1zeERI@@I4QKC{#~2!(_T~ay5O7`0bgn3*BuR%s1kK zns}O@AqG6m*sCdG>thhegu2VJVlu4RH-XggSTejZLuzb|i z7TE~p)UdLn7w?}Qy*hjgbv%sR7;h1)l4JF}MVp_(c_Kf{%EfL`PB_IrsFP0^MO4-S z*!*~fkR+ne%1i0?Ua4@{9kNH6zAd%wCnlMC>DmGG3;hhhn=B1r`*uH|8@P8ZuaiZ?f6j@Kl)8puMtweRZawFYz$%Y~#_iPFbix;A} zOR5Y+uC=aof1@aZ*8JEc+VF>gwid@O(x1nMKnH{L`-F5qrekz3h}Uy{Ef-bcCGnAS z3pFiuv)0y z9!=*ijo<*9=K$viKBC=|X3w(v{r7sErNSxM)i(&n&9pKNLzej+grDPAn8!bZj|LxuiEtU$JCAt7j2C zgx*Hdru)L7Fk=xfAvoiAyG>U>dY}KXEa<)r0vy=_;kkA-Rfm)aq!P?(Vx^&PZk`wS zcPy(QydR<=xm}T&y`E{Ttv|-Uyi2P!p{~_S33l#y5eG5y11RB+pokB5vnX% zjMmIQhAHNo%lP$qIm=N2{ka!9H_J*?@ljymut78u+m!c9$nNGo4PR4l*rieD?XZl{ zz@d(#)(yyEnmW||gH0eY&JxiV98`v7^DUM}rx}=6Z8^&WEWa&QMbMKs7|BZblc3N| zFgvJ^+}Z=V+Je;+98snCW9zOfh2HHcbE(?~?Y^Z{xUGPUpbCiDUasMixWcYPGuu+% zB=c24F#n1Tb|ORym4SiZ@sdtgoNc&KMN7Cdp;lW=+Hpqi5xFG-l&4zG#=e zPLu_)mGrFL4eaa{npS#Zr56tZ%KMNnkjXN|?`pNtqhE{!zH5@lisID-)=!3!!YtWi zL-k7y)h~wXQ4JNyaCExr&L$1-WJ*^!)aRhGrM$>yIpS<^?<$l{RB7Wg9(3X>j53xD zvl-%0;FX*VZ}oWSIne%Cw(ocWwdw4(a%GF%66BeEk?|7NCJyep)~A(_%CXqt@8Y2M zV|Y7N2opuSyC{k$NxGx{WPd4N1gyN07)e415Z?s?gya>iew~&+aoEKzC+WE@@L!4I zUfExR6=%yj_W5eLoWd#5_heKji$~w=SGVKCi=(gA1fE#!aW+#DB6jC-lo8i%)tOJ% zv{2qt5}s&qM8JMB%IHkXDdOG&!|9QcI7g&I?YW9xxO0Cu>9Un(KA$Fv4#h?G2Ewvu z^XVnR*0y0e!q$bj-cZ08fl-Ia>6)Whm>EqLcDySs2MZ2mqBk$j81!}gZs0Kz)7vDBo z8c19ki-yb4l0g|tOG}6-y}u*)I63t2h;Oy7I}g4%BEr8*P4A2hp)W&=Tpx&+N2jAB z+w-^Y?Yfq-FdDX1TR_GdtkdG{m6A=R@KPbA{QOn%Ce0-hC;v7|A^vwSq5=9~921Bl z^_|eAy9B4aAUB1Oo=`q4^|&ZeWM=Brt<8E#AV#(;&v4oE{3EfWJF(STuK z9O-=lo6Gdk=(xHFl^7jg?)pLR5Z?+*|}IsUj`ifWeIz0!j5`v zmUYcT7y`?o+)E}BT>Lnj6f-qPbP=i{_~M}r>FIzjmxBv^)ic;OL>2o z&-1|o8hbA*@5y$yff_U1yO+Ge=V+9`KVdLlermiMwD~S^1ouOPV!=zzCB4(n^E33R z#3$Qndn=!o;D}K;x3i*y|LHm!>W(ZUo6iM!B>d85RB7`-Z~L6BTbu2`*!(C<~`QXl+y}9%tt+) zk$JTe=*gX4q8DP}f<(yIFMHgH;dv-Dn)mm?t9MboNN#O<3*cQLBl;$lvY zMfdLC*t_S$;4sWVdFt?F{Rw7xgp+SIqo>SCM! zF%;I9E)PQCDHlTFDG_i#^RPo^g~awSUELG3&+~ki9`}*fj)izes?FdNb91YxIkhQa zZmj}A$EKibO!r${W2}MBzx}VjMa^@pm-=e`*|E~GdEXIgVnuch@4?WD6(xkmE3S=@ zy+%|QKwE5Oc1o?tOu)PGoOEdqvRSASm)gZWt&IiZT>N878P`sO?FRJh_SOAZZ;Q~2 zB07H#PDyl;YAnnAw<^Zb6MNO|QGK;F?QK(4GYI?zn+d74y$QM5kf~|nn|%jmNY;#2 zLPzp4zTPhL4ll839b-aUb0~HDfnyya!8jXU+S*HV5)m}i&tT9*D-sx6Gn&X)nhp7a zgnmhSL9yP=Qw1SbL!+tS^{pqqSG)k+Y_vH389zvLpIgJ8&rPApkrm5P`dTJzRXE%6 zO=25e=z&*;vsY|AF|R%685?z|VuQWgd+^{V2Oi1WKkxP)b`d|xg-z-rN>psO#Izq( zn!6QZ%Fl!|;%s$@G_p?z&)zpUc$79$arq{j&HRWWvJqoW-`_ocm zxKW+rs6kYK^R%4JXF0kxj>U_4AP03aiy_V zxE-iJpREiv1+VI8SkiH}hHd*2 z9Sh2m!bYRJPBu~H1ZCh$5&QiW`lgN#(sOj?2KJjC#G$!5RInqNaqsZ)Y@c~veD)q4 zEexVvm9FWjKvZ-VX4A7S^YuWF(f=W^8njBFqba)P2$v_=9`OwVhca7G=7wWMUE{~D zjlZHQy~@)uatz>s^;6(uN=Mag-~#X8yOZ2YV0Ny_S(Lr8F!Cu+(TRCov09vDR4f)F z@}0x?HUj&(m%w0aKeAC)nzu!y(i14j=kqCsmA-es?u_z1d)+&&d`eArJTLb9NjLw& zegAs!EzfWA#0t-C{Jh~LM&C9Ktt8%RR*UJVm8{{v&M7=ucsJ_T(}BQP6?b#-_ec9Y ztRyymE}dcAYjWI!MzdW?qLrtacIZQGB>NQg6f*x5&wu&{V?Vb$N{hVagte1!=N6b= zA&!?5iBJR=^l`VMK?QX;5lEEQVfi5i(I^9lAVsx^>DZ)!n_I&fjVOZOfbI`M%g*|t z!_$v&FKB6x6IM>JZ&i35Prc#WwBbGPa4gr`iivY@8BjeTXZqX;$Cb8 zsD)34J=rPIA{O2*(v#KPFFY+>^frgQtI4}Wj-_i;FXO{kD3@^P2{eA#MG=Z!;rHi5 zdVqegxKO7ks^IA?7s1^Z&!HsK5V?GZXqSX+>}fXkglsxotJHm+y021Y!^=PBixdjF zho$XcCirXJ3~h|e(2j6aaU!3Mvh%8)1&BT@&=djwXd~PkO&)2W!t2LOvt!O7_$RUG z!y?Bgr}KP4)+_ik9T&2G$fwHH5)sN4=TibG%m$y}$VF%0i)?fT$xs?S`4@a}&;g`a z;GXwlSsg>ouSnE=x3aB2-ZjjTA(;5t?f09q5!}qr4wM|B%&7!iU4EKamDqjkuh8_; zRnM53Lo!%Q(vas$J40&}2D6#!?M7}M2fSV2dpiR5;r@O4V8#!{|d7}m$h zUa<3wmGI3cSX(;xACHUvaVonLcYE2NxUf1HqYfj~_5D|ya0WS5dG7XD+otf-`6!2` zRnJXtua?u))~f*>+zL^VX6R$`A#HXqQLKTvJM?Y_JvceWdJ0xRKnBvGbDu-McW27W zbpbO0*Gh{P2%#Y_VA-=bE?xwY$R4Wt`60SKY#Rl8kb8d5R9(Q2(5_J3SxY2F$imZ}mxcms70a$IJ@4y`It@w!{L7&Kx zOKr2*o>3nR$N+ag^rl)BcbxsGdWBmveNP^-3-%_Gi8}&x4}O$5Q?3)6W!-*6Vj?RH zGl zG!KIU-Lgmj-ESq6{A#v9_VF?S|74fvEwVpAdU=;fKDNx|zOa9q+r&Mly_Y<@$OiVsgiL%q>fLgX_*^V?)Szxbrk>EZj7Gjq{o$P|0umZ8?g zz1TeW-F0AY%ob@Oj0Ld)MeU{;Roq$H++ELrY8pM?UdQvcw*%F2jVE?Tl-zeX57|jU2oWy@E~SA92H$ z4L!TRbPx0^K66$anZWD>;Fme&6^chvk6D_UGl!-~RLrYk#6 zWpyNvO9pTPG)ebyAJM;}=(tV+-Dm+_E}&n#@NukAuLPG?HJ7?AcS?4Zbm2R{>&d$p z&R3S%_M46ngI^A+cZ!M)vueK0%)HU36;%rt%x7v10pR123cd*!0G%`namM4OP8j7DT)Q$k7ux8>9L6%f>2(8 z&G~pWTY;oHTlK5d3K8K$3m67%A=z_U&{*j6n*jiJc@I&~P(@D9LHd|L&9c7nS>#Gq zJD*P%>S8~9($vDe7yyE0kQOUu%%ZbI-1lTG2x|h!*dtLL18zY2l2WMx_}7W_E-$8Y zd#z~2!cdU-XSJ^OdQ765`uV!iY*wiT7@sYpxkwHzFw(qy7`_wlUfOHk(&9v*E7~PK zF`@@Y_QZ9a%;)F%FfpEcJ9zP?Fw*ncyb+V!7FVNyn{AQ}Vrz(Gcc@LZK)9spEYQG@ zw3QcitQvR4+&q5c1mKljn=9B%bVNtfhs_%9Dj`LJCOpOleB9kV*u;$-b5vfKp;Ua$ zFh-17U3I)wrc}tW@l4mT1N%EUH>w84+cwmt&+Fb;3$AOAz0OQxs8E{5=gv_xggH(q88$dPCt~hR33PE*8^qr2gg1t$eL2@77cv0OIQ>8i6!!U_!=) z#Rs9%yPh~`avOBOE7d%KTMNztW#)#J=muyk3RRZ5jKB^D zD|21+djg*vUEeZwS>V0`#u?=T3riTYI$ybz*5DELF5N(o70Q+cbymiaT#7LO5SRgs z+pJy~H!8Q=ToCDs@qb2ZP0C^0(WTT{r>KyoaHm7)UZ{1z_zjSSi{rk8PIUC%`pr!H zB&Hpd>nHg+lyC_$DBJTWj>vy=FkjL(Yg@$o6#2SS*D35^92#2P66p|0xiHla}ne_75ig8T`maXbIyox3i^0=Pvvcs<(J%hqdE8?;dU4DMou(yox2uz|Z+D{5U* zuU>)zRLLQF%ke5}5B6qh+Qp0Cs>yFfE9MyOL00k$mDQqi+k^jGb1}$0*Bzx&W#FnN zqKYfBIV5YFN6aWjb@M7(XPZ>VJomv`^?_=Kwuv!cs`~&d;D%AA{y_|vFEGNy69H{6 ze;}*EFN!s^9jvpA$NU?5vb^I37PY>~m%dx;SS*7RQ@ffBrs zLnzwu?9DgZ6;n=pb+V1eNz`&7qF{Xy-rA-9=v-xPS*f@(U;1-*<2d1~*6uYo+pJ!2h` zdR4xWydAkdkPfkRxRgKY;r4Ul$YoD&))D&Hp4$Grc#5jyz_I!-%@S|<9v~6Hy7VQ&8zxuZ~wu= zDsMB6Uftd2lY)BFi)f9?PX`# zxEQT_qLMa$ilN{MBK*+uAe$-Rn(3}!AB%z_E6$|D?6m;qhlBPaE9vyKq1P+`*k{kD zHtthGGwZf!z{7nxvHlq+?`VC0xbFt7xJ@L*FY##%Ll53CP@;K$bU^(!IS)2(5KeJ2 z@Sf(e$X%`&g=KVQ+mRXgZWal?=*8|MTU)`%cHt85ShVezWE7VI0}c{bY1JaMdk^ey zVGmReMw{X*-Z-{m7+2AAkB3SFL+s&noDo>)B)?+#;1vDfErAZk=%?GJ!2Pq3(J7<- zUKOSiBM5d(SHO=0FF9L@*sT(7{_Cl>QZ|G2WU za4u38@tzvvxej83TXEQYFM4E{hSo79b{kCdT@XtRsShywnGtE3!DAluU)1P^cJF%R zt8<_Z)G=OxnMZZt_O8b6&&X&ClLGi35;h}a>r(M>J8(+ZUd6yFR}9=$R7{$DB#2f> zsaz3%5N{dHjgbVt=1NG-Qh+htQQIkwE&M0tU-!v=`yLMcP9k>vZPrJ46%qTKIge}y-#1iHo*!P$#eo|HN6rQsGG&OW~fo+c#Zk$B3qWI@I);s zx*8i(WNm0`OvdkUK);*}b4fW|vep_qpC0kaC7R5`*ztxPF39r+0nHAdANEj+g}cB+ zX_zM%jS=n02~rJF+Aa{{7Pv$PLuW%lt4(8#yT+J}Zr9$%;Z!fo=+)l0F0{dyPw4#C z&!y3~sb;K1cJa7rsruci>B$Kz)zTHVbiyrN6&0?pRkbso&J+J0{t+GUR zmAP4|HrTIkYRNGbA7LNtB#l-B{;Ox7J>6**;V>V6K3*67<5kgzb5i|Dis62%%D{oL zu+G|K(?2cA>JaFyJ8Xk`l{+!N)nq_(4&6UA2j~DrK)Sy(a!#_v z1o6s~;(RhyFVKjKYS3d`LB!hlzG{q?F9veS3rTGt@W)G#wPRCaSuoH&YuARhXeJ3`*T^@E4 zrva!VdT`*F8sU=7vrlTCWiZ(1$oD8;OECmn@))_&R?~@T2L?a-vAd6b-R*{jb6HOD z^8z+0|LN8#uiJTm!_oQaAVj;-gk7S<&c(w;5+U2>53Rb7XRFT6> z9NgP#KFiDVBe;!KKyQKs$##R)Q#3x;P{Jx9}LBKU-Vbw!Gk#q>l3ZZP@L=I2*-Xe)f^Q%XEJHK{j231Yf(@l4PP0ow`R z4r8I_KE3bBTP6NZ&xbCUpFpnSzg}nSd@grlHga*W-F#CQ2iZ-@lK9So14+Pme?fmZ zqZ+3w+{(>MT(g}!@AHG9zlCCLe^HK_!~J#%x9Y)yoKih)++avL)uH2xBOmq+T$r1@ zQb{ZK^ICmgd$rWRA4G5mx5HR;{pxWO%k&CEIT7FT(aHre)C;t%ywFyIr2BX3R@t5@ zRW)@#@Ie@O6lUuKIpw6LXJI_(om6GiI_^f$!HOVqW4?icoK82VsKKxw!xa)6OfZji z_ExmDvNtP~D>ANh1bNFxVL8yYWa|+$R=@6bI!=1AqlY0c z51TYt?4 zpU%l=S}svDdQpTy%=Z$wEoBzT{x$_rZ1bm+A-}adm77v%uc6se-dmMhKsDE%+(Lp+&vg&BMZD^n3fIwVZBw)h*7u*pA{=5%&lca6$W@S=7y?e&f~Z% z2Szr85IF;Dz426~PLq=rKQR8>BU@rPV|k*QL3v`^CT6B7t9Eh1YoH3lgJj%T*L{gMDU(Zf@Raa1;igt_qZ!EZl@W@fO-fAK_am6toLN z%kR^e=jviQk~#69IAynM&0tHr*z#T2=PM^CU=35p<m!pwFF+#1eH|L+YOQ@GL z8op)(N6kt~g}akGZeO~p;Mv#};JxWnjqT=U3-l#oBQ!OKM3plR4xoHoX5E0G{L#ep#tqqn{MYQ%70))7zHCCwHNnfGNdagtXH z`I@Gb(&AG#BnNsQq^BrB0!(KM!Jxp_RM48bi&(`)P=E*otGZZ}#d6@*#qDh<4!gFx zuui~Rr>-y(X}2zI&oAwcMS!*A9*Gd7`WePkDVh}K7j9Fj=dIatAi?Aq)j0k!hBbMLq(Kq%eso z*>tfqJ_r&dgIatZkCUn6SJy?#v~eIX=emS(6bRx+&I^lCK2@lOvTH^Z8Joi_QI-%~ zINhfrh#r`e$)8&GrehcEaa7BUnNdyfY|*eb`ogRQbijZJY1PBg_joO&$8t#Apa$9& z^XrLWX;>9CtVL?P_3melm$$7DSJ85K**EHZ1;^dCJE_EhF~mioTWTlB=H+yL)1eR? zilgkbk|MjHyso5B;@PV6XlDL2riHb*h^jC;G7SVOS`z^)27!htn#V#&dfF0hKzN$L z071jj$_%CJz@q3|GAgK7y2{-~qr){d8G8h8*A|*Wd(?sz=z43|y~i2`lfBa#9=J6; z2-Yyzn*W?NjE}+M!OBTyuh#`1dz^o|2#tC@KDBDw_HCZf7QR9|6)0@*tBIlz%ko0- z!bZ@td|%b8sn|%LzN#8f>Xm)+B!+8hBM7;i9hL);4A|y)!8y&6XuF-zY2POCK*ILv zLoZ~tL@NVBx+QNtzj&J2uKB~dYkdD zMvW;ZM@}X$YDN7xXK;jNa@=1v`p4SHAF^woT;sKE8US^~nr|m+z*WUB5nWvp)D5p-~Sd1?9P@HGf`V4|BZo0K?uFNr}n7od_Bv2==Fd~M#*Q7 zrIch{7(JVD4hzJv;=p-A*BxskNZ_ktt4f7H`gI|@8jO%CWoUi0^kA&EWwXN?wZNJ| zoS^-nj{|)r)e&VL8s{(%#u(`$Tfkc2wT^?K&d`9c93Lh=wu4I;lMj85xKWnQO{?c< zREdGEr}&B-$i&vu==dvEO7Cmfdbk{qOAJkLwTrBPDAuejr*GVe$#K-&_td4%R*kmw zzu-Mk2)$`Q!f#I~UaT))I2=}L3j=DQ*Y z(rl~V!e-j1!~|=wF*B-W>|!ek zE1We>R6u|EaY%@Qq?R2mi(CcXV7io9byd76EK~rkyB^v>9nDLo)Vi3ZT0_5DKn)7`D_w=3Pr##9eREbjuyj>Rmn~+GN(( zwhMS|G1pKjFFU$(J)T)jRj+4V)AOdQSo*xk(~Bvm7IkTk2|jNa{7|dqbJ1_wBWbZ5!BLy*PUR;n|z#FA-cuj(Fc& z0*h(7?a&1P4!%Cfqc_jry?plmrJRqY=mK5ZFdDl=y`|dM*OF)oXME6~n?vU#AYS#h zd!|94g{~E$vZkt7^hZ`JGGpO3fE?CV_|70QJRgdz`I+(Pdlri|b$BVN)V9;@TxNt) zID~o3>%oC`ae`wNT|I1)!aq&h$8|mgyBBua;w%nw8-F}{v!)lef?Qc9$G$A|#~{l` z;VAaSIc&uxB;g32{j(M6+9XpIe6AfytwV~4p(TzLgzVobLQayMdUxFD-44~ubu5)@ zwly|zvyBv)G0XjHDM_q3V*dAl*O1BevQv!@%ISi@F}PGc9>0DUx4- z2pw7^B7ib0Eo?xZG3eQ^;;jSdzZ}@GpKHV75JZc0JI?a zKj8>e1=e=U*=92?5M1+M)X4@R5tLziR5hHii(%&!GV<3P+v$|P`_khhMB|A*lGwz2 zMq8-pH|l|3$+&QnV9S&IztD`ksF;1m`U{Lb4d^5{YhN7I81>BA3~fSl zoIO2s-Jd;V*#h1EeLjlq&CyV@0%FtA#@};a5-9iF2n7TCDBi@%u8ItKaZim6eiyC} z8q2d)?xOGEe+v#jhe@P!p=mtLhw5|b$DkuS7)2yulJL<|HPX>Y6q*9=Ocj6E;A;|z z79=8gwfx8?dYqj_KKOKQM69uP!aB$LYc(B!wpiHKny{=}MuL)t_0$6Pw=M$HSvs$N z>6}7N8e%tZFJcWgh-U6=+HYn`0gvTk(uXK_NRgj9dBG8&s9tSAOR;Arc+@eqAQuh( zi~cJOo$B+e)q>No!fZly%JvwXAkvXr358uw4%dO0;5ZiSvFh3&v6Ba~#S#n`-fWWv zQ#eW!b-wbwD)5U}^G z&`;rK6Vs8@o{3QJUK0hWPycmq0*Q$F1dqih5^^9~xV|68jlzxMaO_RrBi7kV1*x zo?8tHioS7#6scs3VeX1$-aKSPJ@Xq0)r4?UA?4GPMCVG@p0GxkC^Az8)A;c6-Lnrz zZ{NJHq^B}%X}e&N35y#wLj0vL`fwH#ZcnmnaVWKXc&+@gLz!sRm$sPUVGQtdo}o!3 zfuBv$-{ymUShXW#pu8%v=S;7vu2_ALS>P&05y|1h8rs?6#^gpsN2&h44L0e=Xa9Wu z_Wg(C5u#;;l2zEVEYFJUwGYG!KVKy2b?W3s62_TADP)*U4ml=0 z&pf-zr^~{Dm1F(bqD%M0JHmA6G2^}38+pLj;8D`;d?ABPnCXA>BtCx|2ZN9eMTnuy zB=SJ?DbfyHjf{O3WuKI?AL+XI>jO^=3>pRb~2d=M~kwLYJ1e)IU&K| z2qIFOC0ZnwGjcy}N1=Mi7s{>q<#e9kfQK7#9fl>7l33$DdP;`sWT{u^OtAre`VH?6 z)Qir~DhM7&A&%hPUYUA7WLgItQQ?SKa(pF?OTKZ($EQ+oimedwodV!g^# zaW^Q0zQ&F;Nb1C@#(Wa3Y3#K42&kRo6$UeDQrquHz2QHB_nRwq<2(f~qMS56MfhH_ zfraL+iHpl9C2ex`8O# z!5@bm0upWP)~u}_h<&wrIlf7{6fuy-^bJD!=sV}XE8@ZNuj$zUmb zr+OaEmtW{hE3#p*59Cdgm&IV>-LSuBZ@zzRI^;8^|E)a>_NH+bNY*Uxc;+fA%F`mw zGd`A-xI~=$9@X6SYxbz-uB{o-=GpsMXSQnX%5n;nu4{W?=J?(W<;CF^9lOW+_jQ%b z;Hxy0cj=CeIfFf3ckK%CLg!&>dT-pS+g5AmYsgkT^({zytb5~SAGXbJUh!c~-QA!r z*4!D+VG=!@wh(viniiMl{CiVF__){P*!sj&WItE1z!nyLs;@!Y0;2c^aJ8 z_s@QO`TFJ0!ZLw^fQNbP@di4GkxN+V`&9}dapgX~d3p5x58u6g$5+H#$>tWLeD&=4 zhqv!2rk9kc>#HdGRVz*7-jlKzoqQg*4?H2G|qZ|NV9tN&Houc`Cig+3S{6#J+XM2H{hQeN=3%n|DJvNDab8Z+J^wN^{-A}%<)e; zk%Y$xZ47fRcGc=8O+}w}_p(`$kEW9rn!}sYS6=d)(Rs)`4!vA@5Taw5D~9hic+h~U!Gkjo|M`5F96>eG&xYt&n}h#I z@Dgb{UfI)%So9~1ZzAR>0yTT4D4}btxA6i`Bhn^EVs)N|rxbpZy=)G>d>Y3mei99| zi6_#a5|Kzobo`g#@W z-g7rB9h+3u_2fV|$F7Q+^oji0Jvng!jc%>)Yihz{jn%GjVrg|6#_F9${%TIDqCIn| z8lk^jC+y@`14~S;pCmV4#`K#*KN0WR^fon7nN{)MY_Z7XTN6|HVgK=gtsV=ZtSUV2 z)>RO(l2yGgdS0GUo#2tPM}|Ry5$gK$YJy??B@&7lNqi_(q#xlJpD7omA1N813A`V0 z8K1w7WdeHKy=TDfyT_uwXO2p?#~?ctrYgvKrr0vypB3g(vLFtSVBE#iy zMp!rsqoJrNWVU;#Q0^X$;GvdqU&k_&@fdHS3Ft&|y7DZ)^k+;Ba(jlQC^U6U)jKlD zDtx2vZJoa;ZOdU?KUwJygInLYGT!NI!SAF^9pebo4T0*YyfetG@U@P8S9xdP}TyB#>dse(3#{~m7I4^>P)&M z5b9XxglqZZ*{cB9sglqTWh-KMj1Y(u7Yh4PIvGAPYESl5$W|$$Bp^x&#=DOmgL^X1 z8imoo>F5b`VR%B{k_uH_jE>^mKj~~&wiU8+rNu%*R8I!oGldfsC+7kx&X6W$0bgBk z7iZbxI@5{22!$$q!MAnIs(dux8-k< zM39?S!S`r?(DdJnZ*z*sDKG7Gx|7h@^^HdrwkZcbqPkxG+m!mEyYE#6 zFon~aW0*{xVdL3$8nx~6V@+1VC*49-0I*t6ji@!F7?>|$M6Acgypd|ICNC!E8}!tk zfk`Y}l%t6@drs&qrN<8FiKmsLy+HSD_Z(6<4p5l*x@m_-mfT;oOo~&<5o#y_Ul_7= zwR{_~Hod;!J*-^@-zsKOXYw>b3Gjt)cDYp!V2o07vA5x(p3UTIx$ z6Koay2%xM>{J!ZG^+KDw%(d(V%!&E-5IvDGTH+r0J$m`Q5HnBxH@9#JD?X4fO|ss% zCR`hJo{S^-g9tY>P zCH$-DB<8R-5!Aq7P58EimEM~cRYv)01}of-`3a@td{p+`5Ig*(I$Ng2T|J^LcA^U^ ztI-FYwqonGZfzGwg5ng;06 zb+RLO{p`81N0S-suw9QMGMcPasmmH)5t@5c2&C6vIw`WDH6TU)Dk?>OeD8enM!IkK z|1DD~?hkc+OJFJDew49AU<}kg-SAV{Y#rIe;AkZcIDS-)Ci;pgwg@|w%^{Hq_Fv9c5XCj<(28OjVHit4#H+lV zvZDv?y>EG=GCetb2sP&aIyg9Jc#=&~i=H@DOP%VvDtM$Cl~_0aXgIq^5P5}|0{X8> zslspz7DikPjC>c>svwxawsG`!e2tC+XvEOI%U;nlGRO5dV2$SZ;dpd2K9n}5xLvHnIF>hpVI zDAf&S|7@|Chqy@>?L$4Et7GI;UC8YmYhhW|Y7=nyKR{J#gRyQ@t>wij`}LkmaGd~> zMvwTg$i_2>MAi5yt_LF0c4%&82PilMIr6A>qOM#DtKY;RodH^>}$=quQ=V?JPx8NTQ))xzZ@<+CV zj?i=hTiUzrIgz=sK8Ses`VjWI+xD^k2q{Q$K&%d_jP<~M80Qo3x9Es+&ihB@zpJAa zZG(vm6_8rZ#;Rek)cSg*sdux!-VH@}(+J6Jf!HPe2>|yapzb1Hv-zBrq*4imm?S%)wwFP1CC`6F6!nO7{sO<`RS^>u?>OCdP52g7PLXhX>X_>WnG^#3(b1|`s|yprWOn` zUs@|=QIRY)wKcA(Olh!T92yw}3E#+a)E~xo&tAOw`;W1e(iWxr^fcvajTF1YwbzO%M z))^jIGt-99fVy@+uoHW2BYV#MX5YW=y;#@mV#FuNnr|}g0h1Lw)Tf%9NczUClPihf z0__CcBe=flJiqv-SfzFXwhQbMN`4VNQR>RGTs{3mVK57~{1G0nA;C875IY^pYb1>8uWqME;;rq-amtbEQ0S1_<1U9URSP2emxMMMec{8sE#-dUN64{y$H|8(iO?Cq8FKAVkoK)rE~s` z`+l<~;N#7aFaY+9Ibr>#V;**2G*!J;#Hz|in&u>~Qgu?)|2HTY8K~%E^_HImDidc~ zEEm}ErJV|_GZ;nC+6p{6?(*76Q9u6^OP5Uz1o(zZG zlPFh*R))scs!l!9hE}uGZGH*bm_eUF(pspPr zLHR!75a~;2&PYWzZ;~ESs3}6arT0aiLBJo*r?Y=eC*M(FQ{gOyoNmH2<5jd^wx{@% z&rI9MPKe?%RPNn<)%}_3QNfg<>F~3faK-P@yLeT1B|O89UTEIP_^g_)Xx_9V4gJNp zPx)l}FMbo6&c@y|MfO@qI@d|WsC%Ui60cucpdUZkfw7EK7$!M|&7)9A% z_mie9XdTw&8hJUN|I0-4BVyixpZ6s%FJOpecw@0``rJ1_A!|P8|xNpD>&GNVm_N_Gg zR&Kj*zl8f%Zo6;gefF(%`&PPrE8V{RvgN*&|0DYr=6%)KK8ZD=_`9KaVow<)oJ69ki@iW{a1pjv!-J4xMOGBZzbm=puN}%J6nR zvu8;PJgkOl#{lrYO8!RlSJw6?A_JA{`zz6(;RTE{tbWhU+Oooy;yHpbG1MXjgA{Kd zn3~x8wpz@yaaJ6I1;~ddhCid1PM0-7L-5K}{UR)Yat#2GRiy+VgPINxJR@hs456xi z$erNRO%5e1F0@4Fw$M+o~^7z4{Or_P;fss1s@2Ir2KQDQiD{C{pT+^PE3Mx|sBr4OQvgD5SC z@@TS-Eu{Y5;UX7;d*cs>Uac~$RmN&@ylY)!W?aZUV()p?{nM9QIhM4Y{0_%N93|g$ zqCze^!cnb}Z;@R65?IP=?9~X3q&X0_HiY((Icrn&0hSiQB;x=cF+dR;K(O3F+<_J5 z3>0gC*$+YRhp?XgAfq*Sn|jVBbphUTb6Srq?XGVIvknJVW6vEcs3(VhtQo;wc=4;w zf(QZpprl?{fmo=XY!Z#%KZTS7bfJg1uI9#xVn~S`r!Pk)Qd272*t+82u!> z{#P&oNNM)EwFAGYi$Q?v4=Uq{7%C21K9QLkKJD%v%Ixz0I{tJLq)U#(qP{^SBGd23 zD)j^Gb^NieZ=d4lK6woRSGB8JSjsg;i*4c+kc=A2i^nc#K}A6Ou;2Ugi}tqeEkf!J z4aZko$1gDI1NHH5Je*J*|FHi#4KxsatIv*Jj-sz@1OhGBhUF+lD=^$EK0LOj zq(YjF0@jv%c~5OgUcBLDl%jA@?|XTn7kFEB$)7J$_e3XeN5fEdA}-w=ins}x{nEb| zhzHWHnI};Rt@OnAM6S>YSs=pQ;fpk{(y6 z;k^dw3T@JrB#^YW%0WABoI=i}YAN2NGq_CW&lb&t)=7=XhpjuA62xmBG&_t`)-w)V z+C4wF=R7P{GWx^PUF{|fU8z*?8Yh+)IW|>JD#>T$%?-L)7dw!bPLKLHXs8Yzp7eZO z%APQjO-b6eC2iHY%N zw>5c&ea^mG?znK!PbqOvY3wu#JPIaxP}wJ^-+Mzh8uyx?BW~D=N37z)7&W(|(X47l z6Ka<a?LLV$r7vKCim(zDdUvern$1zlf0iIi2HI==bwF6WY%3 zPO9=Q-TZD;Cu#cfVs?*u)S_3{q9jO~}m!6TXdi;!tY9tDy7BM@Y` z*~a0O78oOdeXh>a&x$B*igh&=Ar&HLDYQYx!ouI(T0p(v7&t(^3Lu56p0PmO=i-9} za%<*V9%+~WSPhZ*fArA3(%1vy-HAG)!48uVH!c1oUl@jIa@1BmRs?3=QECPG+BSm; zq1e+QK{?UwcEcj9bgqc0-dUzrB3suiwp@%UV(%?h8_VNlJ?uF@>iJ(|^k&}G z`NRU-HRlgDIQjWGhqX5w8zDyXUKLW$Lh3~z^)#dkZ7JU0_#ycirz~lSMFSuy*&h7N z+D4W_md$F~n3Kumgg&IP4ovBB-a9$YyC-5)p1jV|$KsHPHu&tYk6F^Kl|@!eOjdPt@wUTK72&9*Yy~{z-JRZfnqn)aX1Bh_PnL zDk_~P74)fEs%Mos$L=QbIJS^gUQHm|F&x2TDL&DeWPYwr4y#HA^>*{XdUqUHZ_U7Z zn+Dd~FfhHS0pgTD?@vH)DLW+!_v^oa!WHi5|NP}&acA^Zd2<@~U}ws!#v9()obxnl zAb3veez)Y@UupK|IpI57c)#Dk-E&>IA}<(V%;jG9sa=KHd)ORfLC=#vGWuE-mC!<;ADkTRbny%$UxBsUbUFCARUDv8$a zB^UWsMnPq{@-djYw#}INe7c-0PAGz({K8SW5P}OoROg>lc1UDDwYfGq!s&~*Nw&&4 zJRwDu$jL+=y(UZQcRC+(C_p`QL=uF=3Fb`jnqb}8bVR8-JAi{|h*QxKb{6=>)p!=H&ysHmHO}_-!oB1-q0;<#77Aq`Z=kyNEwh<-Tv7L= zVxkn_&t`C7FhD*u3Zv6I*gEc=L{M6#;qE;fsz!vr@6~X((vafYA5;VIZA7EN={e>l zyu;-QHoTZo)}2{9YPlafjV@e!>^^uVIhE$sbA-3MAsvXByh9Xh1iB^~@YQpIiCV}1n;`&}lVhdrvl2rY3m{b`v!)*~a zbl+4uuU2d2E$6Gh&gmb9_KN&}JKI|3!vVNVqqRy^V!$bS?r5yc`v7b|1)HwPjN!KimHK)I80eS-rJLm;fc;Idf>s7r}J=5vME z8ZYq=pE<%26XOKbhT~!d-8ZPiHypU%g!n&JD7vk#!%3T;gMOo(1Op4EAq8}myhv(M zzDW-77^=U%WiHo{9r#$<;xhTx($n$W#uapF{S;rV)X2`Jt0wNAb}{6ONIyIe#5TzM zZ>dwS4-2E@W+lO}Kgt-4s?@c{Qc`+DMD>}yz&jgf^|+TjqN&g%x+fwKFO= z&ZsmqD)o%Y?PpZV8O0nP%Wy_jIPTWN()Xa9n5O?yEb^S6TTBYgU0UO?-ygZ(iURl>KGi_~!Q-x4X zaD=*(e`;P7?eL{gfbdj5EoEs*zRlu1@#uw7_6qhgr!+|s5rD=iYL!%rrc-lIW1|{u zF}6Wvgy>2&eX2*5^c7p9|J>zoyllZCr6HEoj>gID_|IMb=C#}2fRRV!uh2#tOdbv| zSR~yYYYZx}3+UX*12zi77traF$7s}AV`qBvb2=iqo*IXn0KDXhP*CH4K!2I?mT4jU z0c;a039X|Z2wN*wskAIYkJuO~IPBx6)hbBh>I}6=ud=3`u2aOkr&7ty)jf4pxNExH z)LWz@%YujuMS{sxs1;vw!=V5jJ*r7;K|42P1e^P2=OMV9gi-&Qu1*5iR1;lJ zl7X(uX@M9|>0*jZMKITOzeT0Va{NkzK^^;ZM_5hMQKTTxGI1fSC`{hMHXS?!Ny11J zDcJdJ%ddx$tMlM)o|iI$5G`yl6CHTSs`pKsh}+NhwQum7$1NL#n&*2z|?%eqv)=POn6 z_4YO$GTqbXy+6-F+UZ66b!H4Y(xPXs;8k|E5jtFAIvRKtID{P+VrE_;MV@SlK#OPa zqxGWN@kcun=xzHIV-Hg-cyX_izKA-)Brk^!eo6O8qnt@P#wX(-%pVa;@+URUAE{M; zgWSQbgRu)jm{eDcXsuTl_@_67sS$qLF!v^c>o{AaCeG(=aMFVQ)WP5k%n>EGc6fZu z8JhJ<;sfcrg`WE{B24lkLF)~FN6jk@HV#-)Q95yYb^u9$yNg0CUiRBEWy7WE2yX~2%8eqD z_S9bHSJWc;?m4mqLW@^)xcRejdZPf3)LTS4rjL*=q+^IE3r+z&LvWKJqU?5K zjhFK0uE;?Af9fJUAVYT(r%wt}pY!rLlBS)cfTE zLH_1bSW-~hT~5Ah!aR=E0wDklg?qf@1o3)(C}Puck}qh+`1Zd+Rg9D0d0nUHI&jM^ z8*9-2KB!*p$|KN=khdHnOL0(Om{AO}*&?~12$ei}6#r^z-(NdxhfdZdKWW9R?)N2`olGGAis~Tf zgnO1N#x4bmiiPLVS%5-_tyNnX<0G~3kIdQi*5WK9@woayhj~0LkiEo$o{BsjdF|D+Zq$N@a?0-Gj?~>lH(zrZXMrz)(bvl> zDV6m#iuyoF@3>Jjb~dg$@%%-^bp1dHp;}++zcHuZcuv0oO{jmW;$3fpRArwIJpL^Z z9*d2$MqnNYHP@;a$pg_W7QjV9<`obP$XLtoqZ{c%wtuk6noQKEC1ty$xN(g*L2UXb zepQZa#0F*?sFng1pW708O~)Zg+K3^* zXIdezNXu&o5c@D?sFUttH=TZw#ldPlYE^79iRW@ z87{P({_Py%s7QJQ;VKZRy4Ely9v72{Xt-=#9pJyiEBqI{7{ZEzPg+URi)z1JhW6_J z@p%QV1a&yPsCZo0PmdPaxOhANkv;SxE8W!VMk*Hlw^7G_3k>1ZoN=g7FNtv4 zCiu<+U)2@{#?x{2y#9QnP&iyn5^|OY!_3iP2V_d)q58||EQ1+YnTQybV&B1xF*Bub z`8Zo#PKPYHduOAW~iq|2g+3)L2^2T{Ruy4c$coHp@p|v zGEDLtF_=~rznYZrobQzKGF+zYOC zZZtZOxGow#TR9wbzOYvhUnog)KqA&sV-jkY@*{_^#iy!)h~#Oz^>G+uJ!+#013PD% zh+W%Re$ukC$1RMteX#yia0s-;#X+Q?SGC6uH=K@9{VB3nN*%;X`tIkv+Rh97aa z`C&F97f20bZyk)@x_#gcki%WOAG$w9%hCuAx7ajpvXP#?-ALN3M;yD|wwB{0#S~}* zRGfO`()kcV^L_{|imqHR>sS~nkr^g_qT-i0=^M(-7`5>m%@*`J-s~L!!r-D+V|?Z# zZ4&_21!M?i%#NnbC~VZ5vJ$=bE67jXR{rRjY`DrebcuMA4>}Lf8$9W zmHWfThNNBOlX9SQrrT=vW6vu4{=77B6e`N*>3gLng#=k9KJ=FM-fMtxFRSrrQcy6C zSvtsyb%DL8>gmMRT_2Z=qH1Fq^U3*C9nGQ3C#L^;otD{L(&cQvt($Plu+V_p6qa1L zV`RLTC(Y{n`E)renz9SO^Ty>EcJPXmca8&tVMwB)BLam>^0-rEqinDs_X=@xow2$w zQD0-G?k9lq?a6D5(Q6FS*IS^kw?h9T?9Tl%oOzUewop?a=HtQx2>I$wCBz_!oY&=a zu=<}p!p+352@IR32=czjlTBG6FU{z zZ%dYFbvhlcqqSl4jHPypi`j?Lay0`#bK}kE`%G)L$)BCn%E>HWskbU=YV$@&k7kOh zfp^MaszBzGbisM!sHXhblCaP7i=|p2WC2o>&2niNLSqN?r9vtl}0vXB$di~2!< zbW>eU0$C*M4apS~PLgUDpw?y2seBHb1NDm-P(g1BEwf4aDnK?xCP(B*f;wECm zXn86WLBX&$C~iyDxHL>e6cPa3%2{LQA;*(TQTMGk1TVo+FdCN0MuzikRjRG)t{cSO z-eHZ@oEv{b6RC*H70!SMKdKx?yF{zQ*n*&G;1V~K5w!dLM`F+Sn~K9ln)c4$%!^^2dRn zH_xQ%v)MhWY5Hu&j=IHRdxeR?(m+iYQyB6r*tX>|9RURugL$SVIX$;81u!M$fcli_ ztVISZ=3~l0S4<}P>Afd9Rb38w zVKzu9Ii3PWc4ca?Xp~Qov8pikJ@j7EL%HMYJ4DBzg-oBS`Di)BN|Y>1z)ba|O_w>F zc@-p=98JYrAX4T%s7-QaJwic9$&_eJlQG){t}TX*tZrkEK|CTuag$kvP+uIsSp_O7 zX{d24XiZgHvAbC9wG{1ewQKHaO|P;w*KI}5B@w?kkpIYsM8gU{MV{DRIA`1BhfL0# z+vsua=^^{{*+Q`peEKKTVOHz}2ZoE(aRC)KM<5^s4&kF=P7evJanaQo2`Kot=jZSF z7=d4RbNc29dmhR$<6A2f%UA%h^E8Wb109HV$zmWi}!>RIKjxzkB44$fjyz?fBDe zaC`8GhO$($^rPbtAYftxXR9EO)ogXgGoe7>X~er<70kLE2jna4LJg3VpY*%x`=L52 z{Yu+--T^&Gyu5epa(v$|qvx2XUFobY9|Olp$H$kpeta< z-0Y3h#XMgjh7#(lf2y!keJx;_4m%m!$hV^)`3I5hvz3>vgbFCzS(&$0l|Xd0I?L@H zAiAL&HqGsU4a+S>Jiu0<=;i=o-}%9@uG~4xCkIvQNUG+a z4LwAAvTPB@EsFHit@IRXa;aXIM$}z5BCgl1)QiF66xZl|53_B7oIHqBqm0Vve0{Z5 z8O)6(T*+C^mKDA!pkl@6W0GYGyFGHlTXKbBClZa%xD>b+GX(~ICuFXsP>%E$3L&K9 zsb-{|%ZA2M6m^MAJcuXFCdG1|#jH8<$r8vd9n7aiQK4Nr)#K6KtWZR~Zl#`D(5jwM z8g|fmh_yjL2!hBPRs(_SHc?sEbzA&M!Prk8*Xc(IeYpOf`MU z8M?C`g(k;<7Q2;E1V=o)>ZrbJ&`SrwTUAFx1WPFWZ_QyDt5~*LbrLZbu7IcnIF)H< zyf!2rp9;3oEVSz%Q8h2ZOF)nB>WBgZJ&E07Cb@%IR-*;!2PLCaCkSHpcQuo*SU)eA z)JGnB`Y8E4B(Unj0#)GO9dyOcrYbY3d&XCyRNj_pQwY1coG2&4B~c@?H`|Lz z*&_Xvk$%eBehjlC`w4_p{&PGDm&kjSi&(2B=!_>i#6laXxVujfdACi`sh@}t)d;xI zX>FRJkfZ2j6z7#^D9&M1QSf%1Yvu)`{2_-=^ru7HPfm)&tO=28O;}|q!Kur5L(D4o z;|=wfreZisG1OSY`2o52l=UQ>t=SE0c57;`U*ERH_=*Hy8|fD8%Nlao?2jXx4{Jfq zmik3xEN;i3xUg1TklYOxe_E)4{dUF)c=I;x>3Ho<&b7nU$C||@E*k9h$JTvTf75zf z(gD%4v9{fHt7@O_rW)2GDdXOSfTx+aK-ql$-?}eVhQ`t!l#hl)j9SQfWJ<(Sv`zvO zr#vit5=?j066)#Pg}h)=$h?t0$QmnJwrbanDyA$or*FuqER*`9paHTk3{NC_8rQD7 zS!cDu!6I{b9&pMLgUj$X;p5)7gfhWJ&87=BK7ko6`3GsE(g?+gHt({*qGCD=^Ad%f zVcTMCSI!V8K|LFUZZ5L9Hsoj1n=*Nn&a+3*t6l?jUt|@S96n&K)ud--La!moqD*uJ z@)1|XmD9QUp7Oe~1=C86u1W=$vPv1P0w}MAZtZ+DRr^k`tAYCr%nwd0#`Q~Af9GBp zY#sJ0&pO)PrrC;>9+zkI*b;IJ%!Fl(wKI}hDXLlh#d!(bCuax1pSxRR95BbV>Z%&Y zH!G4=1_Dl7NJ37C`Bt&KZUJ&$?>=CquQ)qw3QEWQLA3U2?ecPs;}tJ4;pUnpHu3N5 zny$<;uj0$R+J2eWFLivm>A-T@ifF35%FA`tPMSQXJsvXNpk}GR&{=8K%-dis;0{VC zq~JE@7iVb`dCmr%kJEh8Y_uC>dfsZ(q!o}*fPwT{?%GS*)+ct?+`@M4YIzgEzuzMO ztv4C`tG?dFN5PHT3b>_8n!oK^8La?)5vhN%rv6!^{@I%PUTlsyFHn}fINWXa1 zXX{yCMb{_$S?^n8r6`3@{mXfF-sl_M@?r|Ay_PCte03_n{QBuY}Z!9j(0<_ zn|#8`u8vMERV2TITk4xwe`g4*2vqRWkHkD16ON?%VXn=O z-|<~OVVX>md~lKynT>K|zwES;GRAG#0g+-1TIM&(DekDLb*!MCc%|K-l;Wt$2pyUo z^#{Tgc?@dU=!M*p3849{-TY$1!h-v_#N+%(&I8%y6Xy!8N3ut4Th77sfi8f}iXQBf zCuWV+#|HouNdy>J72*WYf7{{rZtOdqkK|w{Q0d<8M0&~QKrDC*shm#p}w)TAbb>pYBS|>QG`ouKXA4{oc zQpblHee)z1wY=_#)SQqz91zgnYdRglQZE^1l9U<^;QA_JwnvjXgZhUBqVA&+`2JUE0He{ z56cdH7*AS|4B20(qnjLhPq^{i^Vf-%(`JYZSfxOJ%rgylsD1k(UpMf`_ov_}Z;B$J2#o0@$*r9t2@<{P{h$r_>YnE^FSH?_Jf4vA?B#{d?Yxb9;~hLDH}3nLQuel! z6)0@Qvf&@^EXY$}R&=FP*7LvW#c8Z`oTE~gl(wX7hztbnMC4q#sWRygTs9CB8P(N~ z6ppty*@`>}QTZINlSG#MzESL>AyF=7PMmZ z$SXLVyDZE4Xm}Zw-Ga)RWl%G81EU;XH=JLZ?OD({1z`2Eisi-6sPeBSHa9X?o0fT= zP736h^qA<#N=%l<7sF+dHwVc1musWDi&4Y&N**p`vHmHaSwHf^#RTTd#`r3Scj6RA zivV4?;4@t5xo#~ol3V{$GAq{a^N$pdrD=l(AfI!CL6G_I3z4CmnARxt*rh*~xBMhb zGA*a<2XgdxnSQ-T`ta)0L1pfunW};&rcWPe&2{akVQt7ouq&81-HHa{J2Z%388#e3 z5TLMqx?G}z2;hU*b1@{rnLk+H^PUQQQc#>y8ANF`QaC}Osq7hRi=~?sDyThH6^;tT zYR-nfcP<~6;_W2UDVi#!H#Vm1Bc8>h*$x(glU{Fz!(d@OXu zTmGl~3*hpAy__<^hAJk*yx1y{9c+1DR_Lqmy$C>6ZF>I=BBBA6|2+dLV*@JPfQaN;E~ctl%@S3w1&;vi z+m?gS9h84b2RmA9d;vvhya$jZ-d(sWM)6+$euR*qN5OUdDJ#{Y+XuY^uV~7^^?x%h zO}MqGkka|5Y%ZP%{-8^z=jTPX&{K1-i2JG+c$8mE;By<+^b0glmaPI6+Ir>_gIi7_ zRoq7nMo}a)h1}AGk5p9yh4e&2QLn>=(X>jAqEY1O-@dNT#T6s@ke%yuze$9KQ?9vRb&%fw$<|e(7_(E}szc{!DJ-kLdIGjGax@f|etj!6;y__bK zA5%zxQ0&?2QRuGef}-JT{ZfQkL+e!k7r?boYs=TCv76 zAbcs06lef~C+Gd5`=<)pJ&c#GUT2F1qN|NzMrkHoQA$zjm-F&S2aGQoc&| zOkaoh8bl->^6mCpldk@Mx;5YY$8L?K%i^NIFYcRrQo(s1m3#$u4!XZ|WaMDeN%l)d z!JH+C&f7OHpZ)y(FE5|Hv(Cv!t-fk4JB8jrZ9gQse#0=O$3^;L{P9b&%-Wh&xFR>9xZ9#~CMezBNk$k(!^} z4k#8QnWAWGL~=He83#F3*ew}KexJ_3;#?=^WZ|T9z)|!qw4*?blak=*q%+2jkuqD7 z0N`m1IK``qL+4Od_XE$yS=`CVEgHVW^!l8GXRuf*Qk5XHXaeEshENNtiW8mP(tw(t zeN-IIZ8A%_^$hVpu$JIDWJTsEZ>{o?tJ46@CzFoS5SjMQTq!J9yu*Ys&em<8QzqJg znVj{sDe7P$Ab~1daFj-pho)lR7nkbyh|*w-OoeuTlV7iX-H=Z4B3mdD`TZ=NP`c%J zWUQo*K-+Ft+b(HaEsO}To$GI1??Suq$9~`bqZ|3qjWTSgdP9f zcK%wcUsA?evAl(pN3!(nIH4gj#! zux!83igUirp@2)tN3rS_PWMD4J%O3wUboX#+iL*HoG7p`+&ic(?&*-6=zb5?T_Ls? zz{T+W!mcco28#grg0@0fA_(KV3v3ou;M6?BCijI<=-qbWe)NK~(B257D}i*QKnR9V zOVhI4cen>~xZQgW_dpJ}TQl4p>$^K2lYiNDM(%zKodc8a0q!ZzN`UTM{0|^Qd7&J} zrN)#~lM)>-C+S!Xdx>OJoCFMa{sIw!2?ObTFKF74nLf!UjUpNsWa^OX$ieDXPLjtu zA|qYYB55KbgJuoYy|?uYyq>~W*=VSMWXa|HGqo>3P3oV|ifqt!34R}cd2q05wJ~s= zS{IN8qb1mJ528ZL=gSc$i7=XC(Ts@)f68)xs$JH5xp23EYE9~^i_xwE#N~@6pX^xTURW4hT*BUFxz5ut@Rx^rLwc2 znWe&b2D{h5gok<=nDhw=>S0=3zGM+SteN%L6aYc4)lhxsZih42VZH&6q~Z6KTESs(k!`X;Ly!-r*Ap^1L<+;{a->Ohq^!r+Vxg0g ze`7Ymi;}LeE+ug^q=F`L8N_1RYI8V=bg>gVUc2Vv?k5OLguMoOtP~@~)Yq->EChDe zwv}Fsn1qJHn@s4Hj68;C>EIK^8;08|_q<0>7uh89vLVZ3I3=9{4aQCsY#$)NM9_1H zn(Eb*4&eDj$Mc%({DGrtC%m_2!qHQw1*FWky)uOV8d@1{>6Dzj2`TH4gd$8aC&!>m zu6ic`rB+Bk*bfQFLr&*OANMI5^$ffP8Fv#Yp>qll1&}7{>7Gu805sQrY8cjXqDI3U z268p%gT}<)J5rQ@?shQ;N?`v%?cahlJiFVvH;w6r%xoRckpO7|i1^B6b>>o||kw zeTuW$SSYrjw9&II0fwM#if0^93JzX79j`l`%|=8l($;YpbzTi)KWJ)>Q&3GE^VDB~ zbx?f5H`4t0XmXCkoux>#NZFGtm7^N$C0AW9x#J5c0O*YhXoo|e+9I}s*6RDEVuUA) zEE{^OHY98ba2h_vm4@I;9WOfH1`=n+;AVB2tL{NXZv`@&IDN(6lRG3s z)j_ac-U|UwQZ*+W`l4-8(&liTna(_;bVq#IA+$o{LaHW5(X@5dN!O>H#}b_Smfd{Y z!hy}K?Px^Hr?KMgHmL3qxg6w+Y*apyGJ@10x;8yO!>bb(btnXX1*rHjnNAd*f?k-l zStjjolTb&oPixppW{8v}1`x#qo6ojBko0EI-NAEl#q{<2Y!Dn+lT{jtJEw&W8^SLT z^_V=muzi}zd!gCYRe_trJhKvx>4X|GW+aCNphg|c5ldsXuN}UU*BMwboj04GTt#X? zRXz_hQ}+q=(7Q^5S8sd$9QtdJ5(BD6hRW9@1jfmuTs2uX z23t$Hb?i&d(&G`*1P!vHNatn8JIq&6ekLfTCqz9xovy`@`s-m`6MlgzZRm4U*~WvY zi*{(h{y?cMToe30j8*(GpLqS|lZs}Nk6Glx5tg4#ubW+kSVVLd`g1hBP@RCPr=PFi zD3Y(`o6iRrVz>o%h~9+_?B*i-NzoxiSF=;}qgj!!9L8Jq{8qiStw8VOwft!i587ZY zJHBITenKIWUH|%P-SYE_U-c~Quijf(&~*ihwd{0*9dI*?FSMCev4I)x6thvj0H5m| z1gV{g#M1D*GN=efK9Hu$Ffgu7qF^Cjh5itkKNVG6Tq}wlc&>g?Z{Wk`JgjW`{4cg0 z*e1E*_GuEIt+HZUHID9_J-=VN&y<+>7K6<(+U6+Q^LOP_t( zIb5|6eBfy4ph;V6?MjB!GCC3#Wp^{l|0sK-Aczq{SqFVv2dYt$Y-`n!j#O4H4KtKg zKOtkwlB(Sv8^B@?l~lF1^B_g7ZD6gN-_J<9$U}6WWMLTA{n=>rY`K`K z-7DOpG0HJR@WW^5?i?Ub?VvX7z%T8di0&PIh7K`m#b_XQDZ}mX?vlqC8*6%98-b_X z{4<6V0DkJK_YH8%iCAfTHl8h5#YlHc#2y(0y|BSa@G+mvmW!V#i+WHJSmT*XXLrfi z(~A%ioSDQG113^Oe6kzOd;>8^C zy0Dl}!Vr|PnWbHRc)1+Ph#`?RC92`X+hfXKlG)UDz!pvL8eo=K_oYJ#Lzs{`J#ShR zieM0`vTpVzH_r4o%#>3Q43yj2YWxCAv%JvL67 z2$78LO>MA3_{Puj8&vhIi+ko_zGJt7>{Q?>5jRB}%zLdM7$q4)Qw4MXM4>>?T*u4! zMa-=g7w|zKgvPRh7WUDE_be1qJ-GCcmy{|}EFy@nO}YnrxWH!5v3c^K(!<0f%GVEw z#A7rFQNjx5{DujU^K`HL|zPQi)?oRi)r%3lHkNp!JXulFZsdB z?YMU<9jT!VOO98fb0(_C#MGuxuXF|X)#2MG3Is7*v2Cn$VN#^90@nutv4LAq5t+99 zAV)fu`$P$H9&{e8YWs0GiO`B#5w!rNTh9(BMIXyv)TksThkp?u+L2s(6<{1H-P%t(;oT^7J zPvN7#N%Ae?xTtG9(S9=3^uyZGMbj3ZiLy_|SQGh*V6n2oNHpwTsN>0rc}V$L&53T} zS$WkI!*vqVmq)fR5;MUM=^6~6@+Q$69PE_#=n2df-1+c~S;%sbX0zvP(IzLc28Tbt{9U%D_&FREPMMLUEh`uhKv?845S{Eu;$Hl z0wuUF{1~OO!1YJbJ9ZfT%vs$V*HXoQWx_P%<6EQV~&}VvmRI zEav;D{S(7`7fdAIBIwMm*S9}Iq?3Q*0X;;f`0d8xFWq)SiSW4(EoU;MTksIe35m2f z?qG}{cr6a@vQj{H0o#>mc5ptw9u>pWyQmkD1t4n#i zJ@_^-;L|F(k`uX-m>rFTn2+W56>Ie(J4ap&SROI*L?U~|p#HntBAEy@-4uHbs*@-X z7OI{?b-r9=KV8oZg)#}xEd%^k<#n zc+YMqwuPk%85E92iq|8%fi!`s(kSD2^{DRc7I# zDKuQ97w>fq2fSS&iQ{qUhQj70(6#g_8B97^5OrXgxas>zR9zdCw!v_Ku- zh-XXPRy#QbMjxdm?(6fk@YIiI965ke#(P4Gq9Ii`h8C|7=YL!nqP}UeUy}TGMZU}9 zbU*6_-tx@+#%Jk#JRNFLm7cqpO|F-z(EsM{77`zK(55}YyAhI?l5hG$x`#n;zNWP* zjkb2p;VJT+RzXJ`A)Z!pDtlctub}3+GVxrWa=>}Ogk1}|rjRrOIdPC0KcRgTwDd_< zfUOI%gP^A&vw71@nutfj!izTYU*b#$q!`MMQ4MG56&f`v)TFILq08+$xwa=&xrJ== zgd=&hd_a0U{JWZx%Ma|R507oU4J3n&Ckkt1!=L8USvFs23=aKs+ul?)^$zY80JJEP zng_B~Pb>q|kkPHeEu4Cjz_WTXFZc!%#xw%uvy75Y&Oc>H$vDZ2OU<Ics@!m zh-D$eQZm1W+5eJpl*NIThn`1kmKwE1kxb$ z0tRdP{#R_-uil^(g{A8LnisEdXnxM=_4~EsyGVJ375Y<$mhBCHBso+dw9}<{?t8=M zI;d-1c1sOzMrc58F<95HPkUVX09x#DrxiKooL%EdVus`*CAeQhAZkb{#6V!43| zjT6S)V52N;XPzTlJC-}uo<*G{L~U-9zwzr8p~2-<$+x>JF@B24$t$zPPk58-xGIu5 z$wRR&;aTPQM8vvVu~U`lc24!|>Jw~kzbZWZdt^Y zspscvyVeUJLW~=tsyEc3lN%d)53l51MzKX^wa_LEFWp+ZFz0}9{hc+`BBo34mAeXQ z+g{ZP6BTAPCB0{=fGH!20>T~nF$~nN`;jBIM@%SqBq5+a>k7vn@_^KUTSy`&T=j5= zwA1uMY{kQ*2Lp=v#ge>Jb#6m8Edm$nqH-opHbr7~rcuex`4MRXhEvuRr|@rH7V43> zfOp6sU9wdM+u)OWsUpMi)+}Wst7}S{}b0Z9MTmY{K>JUV$?LlQBweKg{TB{i% z%8y2kCv{_XM$sA7|05G1J$H5Z73-A4&6h~+9(nS?cBZc-AA7`L4jp5eXg&zE#1uSk zA>04G&`AV9BB}-%8-7e9)*fU%U_2WA&Zd*hx4AIfnYvOB;&{-ZK~he-V*rMEomLr9L;n{D}xjB{KYHwe#J>GL=@hboo_A-eZj3t z#JbkPvstSV;xl1_@3tb&G<9kw&0D*0#Q(V%u8uhR4@F+`*|6NPh3O|(sJzv!8qRwo zw7YTJXxosiwuKl!^;V+MpiOL8DnqWBwD7a_5#@eeZQY6FFd}3t^B3G=1D9;78m9gw zV*fC%fz;Cpyo&_R`__mIp3abkLNmE=GJ)&NrQTxuva_t5YF9{Y+BQ8G7_GsPT-rOH z+n$xZlah(x@r{BVDgyngQX*6UcFc~Vs@NH)bMl$la$#)nSwBKrn`6DTClagJR$AN$ zmQ1r)VL*JY5I9l1*B9O%QT!H`)A)HbVU};+8#VnATKibR3>%21=>tifSY>_F>yQap zH7(PgUR4P}cW;N>3bZNz$XwDhEj8CmPr7NaoB1d+-j_Plknhebo+}b1g^=0_d0xS& zo&rLUzQ)Az3Sgy^DN`aE+;BT^o?WMj5;mg@^UJw2I`cN(thZhIr&U`I_cw9bPRaC+Y3M}h~v~g68+EA|~q-vffjgdMPic5kLl56_( ziQ4nN-uoaRJ&=Fj!@_h%oCtqG67_5TuYR_Miq@Hr zDW4-q1J<&3b^_UrR(Lja7m_AA^i&dtVi4_4;yFj-1V)sAU{cH{LOcg9N3g~EkJpS; z@iq0w;lA=gI}!T(XUwTdR@hfe8yP}E+EqiIsmpUQoeuHpK1wdsdKcu-rp^BBMjDm3 z)y!X0^aQx0bhO_7oruL_+%S!#LG!<)8=0{}iewd+6s}Zg#s2^cE7am!X}ozk@*&wJ zd{4@=Ckf?JoJ`1`!rCv0&SipbMUi|^6QK50_cnEo-!mWMLftr5JUc(`o|IG6!AVuq zo!i$)xS)^%5LPqLAn{seA}HyY1IKE4542CHL!^fhlL+#Uql9`w6j2&zZmR26r^T@v zoN5zan`5QjS}h@*tewcnUu|EM?c!EH@G3@RMjrwK*E-(}iBHn!y>PO#A?Zf59HeYN zgtnJ(3|Wd-1xpcO4->EX=IsY-;p8oLs0ZK1tfE%;;Y>9G6_-1f){<6#@@XDmHVxe% zy9&=76IPIM1qJTR*Ew+K;Vq8Xp9z0%z|v5yH@C096ewdTD)&wzCYDo!-`M8Ih8nfj zXgZ*gK9kzpg)O8*U<1XIuy~y;H4%eU;WzAcN;+B2(u>@+r^NRoj?K=WrM{|dYju=n z9l85VFSI$@_0t*p`~|S^*X1a1M-Cm8^L0A zz0{~CU#RD%6weq+*0CQ?e7Zh%GR%6gtBr26S&~55Ttw}BeV#?|4DYPMiwu=E63 zG>D#*53_k80CW`PD}qVdu(4W&gUo9jIge~^^Km{>XXiR4YurorJsN^bnJg(7D!5Tk zy9aAu!r_{Y&^!sdQBT=&*D0)M_{_L=!!+V5)NZNu>K!hMX>FtI$=#4yURXU?b1b7CVMrSMVF z?AsN{+A=EecRjwLW4N-?sd((=Ul~OMuaT#^;e{p z9*GgK!Ei)>aGk3eoL{S_9qbM&UUhdK;f~MjZnvs;TrzVWY}!Oj^rX%}z~;;aZ>wH_ z33MBefFxlG`n?6 zbJrPFf|}TpN~A-BsBY^s3M#gnlsbd9{q6bo1Rts?P9OGrUu_P3XeyZag6iYn9=pV) zm3V+iVA?~bQtbqdJf#OFp{b4Php#>J)rI84XUcx+#{g8Hzf=RMNdor8pOLJS7u8FS zi^S1tcQm3$q zaoUQ+d^8M|0uXOwlB#)H49zk5A8nwu|p5nddaeSjiZ99v?hsH)d#R?AhZRuBq=CG_J6k(KNSS_L9r973(|S4 z9-<$i4CK<+)9EKVWwJ4uEa-9tM|zFetMqoca9(KEwT+}%FY>u3!T;D!K83X3V4BSb znf91Mj&CnW2UDKx9zgX>mzpJQhhv~3z3?t8bUP97I7Qxz~^zp&&pS+nJE5v?I;mg(jyjj>N586b-ZzFF9Qi{}LbH zd2SDz7++;?0}VC#-m~MCmRb?h%)U5mienW5*5+wkWTUG<4SlZ8Iz+?Yhi;fy@TKB& zc9X^_Neko|v!SD0OeyKdSpgav!>>LNKev8!){BJRvW!Gk^hH9@A2b?%TPGZGH#-^; zhF^?qSiH%uj~3b3>U*Lhk%|=4%vTS89@qMqG@dPxzkb2?e)=MqsFjexl9iyq zQxy8ET@SCwkVGd*@jj7R75PfiFx3~}gvOfIbi=ssi3h=-u!huWL?NAYJ}Ne>*+?3# zjR6r1rEkvEgDuaLMjx~J^fHG(pT!ZRiC|2!EI3Q0$-5V~(enLDNS3Xjo~37_QsEvg zP16B|)kDn*XDFG$r+lVUQe6K>Z9n_4G^Cr4!zHimh}EfktE1@M-F6^8&RAk-#*-LaGL%ph!7io4&b- z)wnJ$z3k>Up*B*MFM$V;hIsLO={O!OhwkKq?(0Q12|rC&q3E+B`#h3lfslSy3=AFd zS$PC1rU-9eyzTS7EG{9t2GwwuY0?`uGCI~Y>7^(iuuiZw2t&z$ieD^dMSp+)LRDFU zcpMYEG%EE!osY$SUKEOY-~Zd^U+>hpToN_83Y5=>>#LWQ>d_!Y#x9)cT4dGDFD4Mg z(^ft*S#uwmZGJ?yeSw*>%TvYCw>=oiLoNMho4yQdU~qVtIz6qa)DbizNM_ zAD$;VT1Q{;$b(PqzXrCmr`Za$mkrx_8ibt~fC;g?wnpVtHhCY1u|bI>%Wuico|b`% ze&u9dIT&L=QvfJD&K<7l<1f3Px7u!;dafD=xTUmxa-ar^$sD@~0d5qSYBP3pl|AKw zq(d7GA8+2_vS}3mWGzctwP2}>8pDr-)eYgQ`ny^eDPWey`5GcTBlZn+Z3#ad4ILjv zADR*s%u+--k8#lP6sO*GKQ=uWbJOFRS)J68nsS#6u+3CS1$jC!6ga=K_77ylp4JAg zCGM(LyMlJgoCGD=y=U`sUGsIPuNnjNa4RY<8t>VxXjCBOMSG5zQ*CG?WM1)|-2|YP zd5vxW~Hq^^X$ANwdvXF;W z0_dWL=s2Wv47pm=2Hjh~XKg{MEg?daoc1&R`s+DQQF)hq5 zVA)!vvIXPb_z>sH;hAO*R1FK=(4>c5cp)L>$W}G&jxHusMC&6HYty>V&a!%5ZQ}kH z@HeT-W$M}K+vtYaW%c>QI9K9sZE=ZYylB?k)r8Ydt{_gSCvyM%nbbcWVs_F4m%8$}PL}gxM8@tPpE%96JrNgq&AQ_Pxh(%GteREpnjVOoB9*zjWMDx*vh=|bmv)$`#OzGmEzQ;HB?`mE$iv~q z=3Jv#F@X$nc1L_fR(y|W7+2{i+Nx#`%;LkjHt7D295m-Rnsa)i%#W0z@s;8PuG6xu z?~1x=xH#I$kDX#Gf!fJq1%}QkWh#VktuYBV3E5IjXmB!XiUh%iN1(<+faFX=K^+Ea z9T1#j?q&I|TvIlV!KS%yuz88?4zA~2Zszv4Q@-2lH3#cL=KUGR)Ui$W%C(b?Cuh4D zeA^()2ho^#54>P{(eOLl@nUzwE>&swT`djSk`2~|wUvYs80vk!-YU^pticxXZ)C6c zw|m;_ZJ%#>lV#uY>1aBy)__&jbOJ{O^{1*~?fJBq^r9SPYi#ohl%F~aJ#9s3Erf!E z$@V*SQ*X#sEaA4~D?`A(IgF{hq*=9-k!RQShm4^7Znm*?A{Pxo@UI%egS!+3WkY71 z5y8t`75C8uEE6fqU7?&uq^ac=rPLx)&jmhf_){`<7ujlIip)34{;{TDJR4E&KfiEA zrAQZ;RvMN;S&a*esV=@M?!L*v16LH}=8`n%S8@Z26xxy3itWz=;eWkh8J*#ai%pGO z&TEk%4kK-@FeXLUx;P)!EfwcY{rhfu{hA)8=8EE#R6duA4eWbq7`$Tp3d#A=gF*Q{ zt#hFoP>h##t}~%1jjx4w8r(Bw1b&#Om^I^y=UNFb(1i`@NbXJ=)BKn%E~mq$RoWLY zqhS!9?WWnqIP}Zo-Ri(QNa0m z2Zfqs>d@=u)*KjRjx-r^WbC?)e8KD(NFpO$;dMT7uGm!PJ5}Yq_YiG5(bVirpJ@LL zGv}lRa%^qg)YTN}kB%5?JNX@y3t_F>zYaJqR6bD$D__{IeEKS+ARhL+UsyUSB);PG zl21jbvai1)4-WhIKJ0(~`PW29ef4KJTUY|Y`dc(vg}SiuUy$d9(&4cCg~6fBU;z&e z6H6V61X?@B{|^K~W2NZv2JZH3fwWNStNMTlZ0fH=TNNZiqYSH-wrc5T1@1}zRD-G- zH%={h&j15(H-r zt1B!(-^B<*%u|bUriEnyD&F(j3pvIHcyl^-PJeW-jS0r*FzQo}&M+?&uUVo78gZeY3o&V8QL?$Bl0QwT#{w$J zpG@k?7CotC-Nq3hhphADtZ(~v`0jnPG}hby%hnQSYTMNL(aGOnN{dKL>JdICGSdr# zh#(MbLr>H@R*C~hpl-fEgtXu0910!!50K@eYZ%rSI%~a}3@r@_rYMD;8v1`x6_de< zqATMOE~d#iOA%)Ad^zeQM>+;yzRrNEpSK{I*Po^K^m0%4+E9RkL{~ zX#psxMzI`^y=WPo4oA7Db8mG7UB6B&#F|ECAf835^U35Ir`4uD+3oe?#8ld4xx#08 z)v5TDF579?lzaLA*n9K!HjZRp@P9r5J7O!YS6N-yr``^vZb-)ZtL>n$1Vaz zvP5i+0!frKe5ZMad9sNmm&mOEQj)vRozv%7L{;UUk(m*Z5x-!|uUKZMkwLxWIxTC0 z7qE*9XKIIt!-8egbY>?D7lR8W%#3d#1O73iUCe)FzfMR%kLt>oIP-p7?T2un_*bNS zJz)`VMGl=zbrz+DW3=p-nJVBHU~}7MQSV46(qkx&F=r^%$*S5!wS=iuljy5XYeWI3 zIC@KwCNLc(WZGtDg_iO@p%2P4*4pj!>tk5pD2V?;`T8qPbW0-y-dq;!2A_apN)SfzND?D?Oi;Ae$GtQOxquwo)j7`}VxijMf|F}+Gq&E5XP-MxrNiLbcT z4<0_Chsc`8kJzPc`2wpG)Pb)^W>qI9aE)|g?%v|WOzoaC)QwEYCsmawqOSYg%j6gG zaFswD?sqsA5wBxsC)?%{LR_~Av;Pxd|FJmug^WL;QcMoBdrzql#cQYyhup#l>i6@T zxn482pUUQ|b_|P2m23kAs0UUR!_?oPIha0s%6~vu-Mz(lL00-jg*XjviILN-Z++3` z1MACKs1O`B+lbqA!l9aLDKI_SyQRQnwl6Z<@Pr@p(Yr-r@bU9PK^v#DA<{>f^{mJj z<9rs)%AX?j;g%lDVK&P!3#w2^W_m$P`kDY6%+Wi|?s+8%xl!M}G&W5qatjUfbGfvE zzC}%aqEKJ^S7BQJqK{On22d_~O}WkAp(!?DOpX<}bc|SG2zc4|2&gB#KM{ z$9`ZOHBtvWZth2fGUFM+6~l6&(hvt`zh2v)d<^-e(?Ip3?jb9yZLZp@K9*QEb3-~5 z;$|L->rDB_=CmJyAKn$oosF;`*;i>4Kb;dk6Xceoj%+mt-wlS{ zoESZu2#P^t0u{gDx+D1kZ{KaVpqZZrtnZ!>_}lZ{{_dkaaap6b z;k*64$KSZNFb)sD_qfrTCOEcGtMtIl5;=z;)uShrDm;3DrB)@x7y-@9r_Xz2$qBCO z(fzLkorR%Z)>YVQ4RJYnp)tl9`4)pjrYpled39D7#Z2cNPM=?%rnxJIP1A`_ivBN)N~OZ5L`01(1M8Mjmm6Ho4`!Xb@Uwt?(=eJ6DkKkVWrcUx>l)e z;XXo5&%6<328rr0(Q>{w5FKh?LLaooc8P&&2JK6N8eZDKK$$?2svGjxGJhSG~ktCi^h8*8I3H=Z4&AXXviM6yrEJZ2K zb;I%v1ES0CyW)GjZtZnWW+)vutE#6UfKT$~ za+=d2>qYEYvJ%Om2m4Am%`vDO-q{E&J&tsEmQQ%fo=LM1KQ$t-o)v?0WxkI|kZs(s zKu!}FtU=3|@L#F$iOhQz!%XQ?=@!9>zKPdO9J*vuoluL=E06h0`T%Qp??)CjHXIxp z;C{$yqRSgVxlj={su^OtEApp20XTMunMr81XqWb~w^} zz2}~q9DrgOM#?oQR2)KNXWC0QK(U*W!{E8;Fn*jsOK95?w_*Vl&F8LZorwZz*_2MU zo$VHn_Fc#(lb5YpREze>(j`(=xuOa?S$rO`m%H9{ghdPQ;{3UEBk|ERcl6oif;6)c zjGx(VMyTyoHA_x_HX!`gMqq8zXB?8rQp^YOI>@8h5#Pj&+u5G1rnWRt$7!GzG=MB= zyH3$SZD_zv=hHylOary0f!d#-dIb&CE)CSHX`rzRO9LNyiqwQu#1=^p(mFu|bllE< zAjZW;{+Y-Sq@IH(%p&`AQZ|gH;~H^^!&_B{>7sPUhd-y%H0U;+iLg!*A=)o~4C$}d z^jACdCqmTK#O)&e)sp^dNq^W-%Q48&R!1owBxbUzwzC?#%vW`Rrn^3C)d_BPyplee zLTXh=?HBSs2z!agESg$#G!yN8hwQD2ZOwrxHCTBsdoh(frWJNLRJM&Acc~9fvV~Ep z8Q4lk+}|3ml?yCdKluzMoIy(&Ie19E8-^dnKkue!QP9RcTMei^(eC)X zC;B}^%B#cjYAUZ*<>^kN?F{(`YCl7J6hS@zy1-yU)th4ehvPLGe10E%UIwAH%mO1? z3_sWM==xhedo{g^Mq0#gKbNOx(T{aQ#yXYJ54@xWsAyc3C7o+1as{!KYTE9CPZNcW zjv>yzM{k1ComaZK@Y2+!oh#{8Oo0q=*6G5K{9eZ9-5B510ip>>Ae&D$1JeeP5la7f zFh1;hd|X#;Pw>ez4;4Ecd_0&OdOMS7qf0k2%T<(|$#rtV!CQszvwaT(9l9y%C>bW% z9f}##35hj=>3iMS()Wp$5~V_){g&GIFz@M>nq+qFtfLiKEy|t-V=hF4ov5-9mjRYe! z5-)(DPq4ce!wvUDb>n}Gc^&gofWNqa%?cn@Y?~?Hr}S%GtQI-fDOsmgWh#S#rLh>0 z#Y^i@=NgqZ6LB;18+vDhJb79y_-C7Es_RoZ9wzwE%`7?C+07;IEQ;TO8bx(OXu;6E zx~<&?)nS9`ScCX9;s#kdvD2PQMottD4XR~>pany5&G@mOeY8(AG%XXm(T0MYMJ1<^ z+&$KUv}B5@xRgwV)L?T|E_)tXr5y2#`<6WX+>s;19v)CEM`GFqHVd&{DivY|2%nz} zh4^;&tQH!myu-`=8`NeEUK(Rx3I2fC13Gb369Q6IvHnnwurW2nMAAi^`HB)ocf*a* zXX0U)S=(?zv?=N;eB$b6r;40TH8o*)b{~#gOq6vFrNTsfr~Nf&oFw!Y6NZ2c?qu3Q zgH)rPcI`(2j8mM&C35h#)5^KjVI|D`Ub@GTrx`g=jpva>66EI{zO#)(0@&NIzbLxOrce78kIqVh~YY36}g>l36*FqM!Uc)%TTKVoiiuM+45c4n7ty7lp|CRj8WUs!uZ&~Sj1X`c6eOI-m(m8oa@v1+_8{)xm zCv-szebgYC_DeHjJn0_NG1Yk!=E42P&KLHtdGH`|rs3;HJ`3C6855rf;}a7(5amhu zK>E!*dPqz6-;0;kbj;n39g~)6YePku94t6Skb^|#M4DRY)=hMD93b@c91M!t0=>qnqzq8#iE8X%K6YZuJ+`;2jbblU|y6OxOhqB))@@rs2r4w zQLQl}ilM!cpx%LKd8htLSVdOT$ks{166wDNcoBn5I(7vm`bo3NRF~!zo^Vo1@;uHh zY7snEiy|N59skE>C?rjLA6p%q5B(_XZ3%Yk--M2|_v4?6kF@l=pw0h7aiS6HhIX|l zpX(U4H*EBA+8ahZj}XRodNGe>RG*@mzJTA)SN41QN^V8G0ZA7THR{@*sE&M6>fw)( zPfAy;U4eq{{6X<75l0-I&UM9!xZ%iz))6Og!qLg38;)|p342UR>W!!mj#G~6f1N0& zw&g@zaC9oy2`A!!<0aHCIM&%ENNN0AHa!2DXjetjx*=(8lbV*QrPOzpTTgsZqo|%L zFQIv_IqhgFHxWX3^&KEcaw~IkU>e@Kt?K)#9HKjp=b;j2qjX*G5|TYuc6txHQOBcU zUY*67fbWI!oqT^_5MxQrN1@2KK5QheNKct{{f5@{8M=YLknbxLEJXQzqsg|ud$}Rb*lQes@8oHYN|^0$q^!MtD66+I&_EWO|Amm48C!a z1iJJ4_k+zMrZo@s2~ThndA#%gPhFoG@kU*rPSuF~qY|w^|8eHev@5C@wA8Q`bdSwo zG=Dc$$qquASZ(`!}Qp8Hzq`|j_A>kwb>-9X>xuRvd; zN7y%ycW;fGcw=;puLXZ`E9uh6&v;8Ua*t~b*M^naG^*nRwCFl$%}C3REzr zR98A(hHPo^B?|mN-WC5+9F<^eY%MqI&5xV-ag$PRNIlz{5H+Su#;Q5+T3}C)*5QT& z`@?>{@DXwugU*fXjxYD!5-X+w_{FkEz(w+%$>Y7R1lMA!N-}7iuB?8=Gp-!Sba=3B zmDPm5+ztVTf$Ns$sy5Nuo=jsfSzeCw(?a=VN}GvdG%M!FT*@@JgisOAd{?f{qjz+c zSN}_@W6q4n|HlA%@QQrvmR{_d-bdHW+$sAlaa+DUG9xWG-B)>-I}=_TXBr0atU$PA zw=wjAwA*1Er`51A$WD3}_p8xkD3criiM?<0-f(ZAda>N;WLqj1d*S-Nk}`EIQ`ekY z1U@I#wFs=|*`hD!%9!k6iRdmnzz$y_{T3O3?s;KavMdh0$WQFjI?SYp+=C(x)g*K|)jq(CDLbc&Milo!)*@ z-`eE2LS-g0*A1D;$0U-ObnR;Y#K5sXfG@T!{u6OOs63YqtkLvRGyZ=EN^` zKjwsC^R82Syw-uNgv*CD^0#6_qDz+B&2TYQ@H1>>VQ*5b2K*ks^> zVyQh%U8f&sYC}0&oi#!gaG=5ym>1HxR4Nrv!WA!IfAMpQ9cto8^=5~5D3W|mgdaH! z(W~inMv;^d6h0V}4G9}7;!{t8_|zD?j>P?gbM`w_WUAg6`P~0ZZvyvG*K(rc< zG^uL|VKm#e(yh$251rK8P6Ny?t4ivGMVZ|3PgIQa#R)7Oi*FL?r66cioh?2Ys@h*C zvvOGkILyf1&BQw@Ts<7n$;n5p*QywfpF3mr}|yG6O;QGi#N z&F`k@<3Mg9xAO_|SWq%eWTpzAA9e4#tRG|}TjR&~zuf&pT(5(mS!mh?8xC$f%$C#M zqneVB__SAMeQY-M!`rvAgqJ7TN8CvNZ`=w=H%j** z>Ziv(iY@7B4t5CgZO2;;q>wn+d-$-M>3_TwCu50jjvKn3SWA}oVGDP2RTn4ZMRI^w z>HObe42%~Qsg3PjtI1t-Q@e4iAFR{r#+L6rd=$kIR)nI}#b~x!400qil?!#EIKyZ! z$OfO{*zicisCh{YO^=IKt%idWPU?S+AwH`{H>c(MkKohp4*#>V$q~5cc{ann(xL7! zuGYaW?uGPcGi&{&6-%u+z*&gRq8kG)B;*j|j$*ne`F zS4WiR=;L%c?wu{hBbldyOvB=`7-6va-gx>EHuJnUou6(Olda$1ZV#t}YWp9>@iwgg zZ7OHWt=qKhE#CrL)o{y8-bYWYatu=S+gagU1%xTLpe0*_d0EZ2$nm#1@pS#9SH*LrqCH&KY88uj;i z8P-}&@>z8@T?j4I%U_=V?Wd#Pe|vTG=C_|-y!_*{f`0$`rbyBZhE+^j?SmkV`J`N+ zMvA{6gGAQm96w6|LFL)+zrBRE%;rS}G84*=L@rfeL!?T z`Q6F^#F#Yg1cpq(n_*zxKlEa>k3(axK=)|^t6PmaU)wv;*T;$akLeb@6U&e`<#x~*e`)R&n@308l&uAhFbR+r}r2%z! z?N9ROMYT`486l$VDwhy{i$vFu(4_70ZsIQF;nxj*tS21f2`~Ln!b=q)`0qOA52mX9 zRJ95dEK_|(s9_z#b)X`E&lWn)xAR5QBYgIiV0QnZc*sszWKmQi_Uq$Ew>*|HoKx|( zo;bHg^m2>Y1-ygUuIkC)%N(m%!uP;~x@Z8$^ky4!@V9*hVTIR*s5wAw(g@EaVCAbb z6co!bb_%HO&c=r7V9&d5vN5UV1F6y+vKJY=?ZJ6Bf@#o4R@Z{yl6@DnMd93VvnU-! zv8#$l4|Gz^ck7XCW4^%tG_j&o&vEf}??%V{ZvPu{HGOoQ)S z1H9nqoo|m%kUJUm@-_Eg()Zt9ME%B-SmYRi6#>(xiwvC-aW3@Ae6j46ljG^KjZH>X zECL|xa1Y?I&Ch7|1wZ5^RR<)@PWD~40|%QUjhPZ_TOwUvi4tpFqP)y9C5D`&kM#MX zoD6}6b71Ti%<;D{DC)3Sx+oFkeR?vQPUmg6gQ#fsZo3TTgvdlc^W)0xi^yC{hJ_lk z>avy+sagJr2oe@&V!g{)U4Tl%!f3A;v8U_=kI{B(Zz!&z>6n=y3Tq7#Kf2KLaMAmN zI`_zhD}sH=+94RQ4_0G4cJ{^;6zwro2hU1-H)6gCwE8joJD4#uX@YprIWIhufFd72 zM>kt8CMG4cp!OgS7(n}uDdj*}VjMPQRX(>Svu>;dZYplCKmS%~<)xC0%{{K-*GlqI z!MX2LcMk%dpQ~i;iR8P}%TP9?WEpxeKrv-As|c55 zz`7v>alwZ@e%L!a6qceMY%+BlCgedUQU#>PZROxT7j*y7;mrNihpqT$J*+A*H$|qk zn=Rf#=L*wDOvPgQ`wWi5AK*l7lc{T;8)i!lh-X0vKdP58?0dr^cT(v1CIxOP4m9?H zTNp_e+k0YVH&(p5&Pa6|Zft^+siM1#iJ!og?-QAKb*OQW^~VJ{C!)9g$;F6rx#alI zzkH%%VDhj3X*cGY?KcK{Gkg5BVYA;vXUa}!qM$L0H6jqFUk2%yE{3s&2TB$%qNQjQ zA6^*V^`F1+wLtHiP`6x50@Sw%TRFjyi0fe7-}>1Bo&_)+9u%k@BkD7BS)TYd?QWQs z$7i<_&w=I=GZ`*$*L~Lhv!V_d&_4Yu-qENKaiZmTG^x;wu$twAA_^9w7Tq=_ zJ(`%bH$h!!uHnbG`AI%6<(rp3wVP#W93L{> z#2;+bJs1+@c3Eb9h2D`toI#zM^;^DRtUuQL(K5f>k?M`UE+R>ZbnUTTLN88-AZ)RG zh4zP>02h)L0SW#oKW8YSzad0L)wb;xoq^?uUTgW_tZlDahk?igMN=uzsNwxyRg8+k zqK$DVyyzvYRuIaB6$%A|Xlqou__%hEd336{ zBgA9O=S#eyHEW6Tek(Lb<@cuWj;FEi9m#&0-y%|hxrafC+#RCjj^O~k;0`;6*KoYA7yRRuukjqOtAzUoq=zJR$CyQ=2N8@VX6@fEBuzC3Bc)D0j$K6asAVByk z8)#}YJ;gtHHX-Dw%fhmSHYy!iuAY^nqK)-<7f&$!WJc^;25?f%6dH@-;nEKsvAG$u zA;apg{OYNLvHCq75wXCWc8vyXZKWCz^dAN_-u<&Mtj5fW7{>6~$P;Vy{>-nOCv(m2 z>%?m`!R^14u6YcE$TQ&p;Ve~SZ{s1m;HN5-0S?#D+V zStV&jhZnHFu*C;?HC$mhKD6Utvpuv!#*jbbVmzJKvbA^2_7O9^By9kaQSaeC8G_^^ z6-*DWRSeL*H#!WEmoM8}oc|t@@R5#wL{kwTZ-cDY-i5iQ%pET`CGEsUFRZl|`EW84 zBb%sI=H|4W==hT(1Pi)n=C&Uvge#fwQEZ?+1d6^)EvX$v z7-4wBldWnnFA$4xIGwZ>#A9c}d483Z3%Abd^~uRwN=w1CY^%S0N?LEHRrt=;Rf+sD zYMD&+nPd)nb;ds*joON1XGLxMzz?aSG6NS?F4~>m94Pg&X#0dBROjI`_yK5{?C+WALmuzPYpES-3(B!ZNtP zxE#F!wdbc1$9-#Yf|l1MrR=^^#AD|qn(RyyjF_nXuqn*bQA}aV=IhbDphg@pZYeM0 zN^2A8IzI?XjT}sx*2!GY_J!Oe{2D*FHGWv5MtKx`;G;k-6k8z=3e^}6%uN;A?x^0z zT|$;{GG4HP8h%8|cVBt#NX&uxVKuxNL>3PIVAv}CYFED^P1b%a0u-lu`jkt+T>zx7 zU+HzndEDuAh_=efMG=;yjrv7gLFNxRv|!k1K?vAz_ya_+uV3xzSBZwJ#(&U_|KK$K zgWGugNRM~S8pc439~wJE>n^x+@C!(`Y>?`+M&v`W>!ciw$RN_ux1%PsjY?D?6evgQ zNLfYNX~nCI`hCd%+56L+opNXS9Cjw&5O35{GSq4Bbm{++?b%pW+;1rUAzgK8_PR{3 zTbX%<4>WaQ4cAQp#k4gy?S9Rt_Wc#dl~b-dy9UEUxrK5$y#k}m!O?|iu&`I55~;7x zJk;*A-I*cPMc-F;^dOh3#UxX|`DO7Ov5ptNp_3cB$xkOmOF$+@n&Tg&uJ5aw$&}#R z$)1Ug(zV^CD8&0NpWVbonotC!?!j=&#V`2`j}LM%!BAek`Ce?ptm8Rrle5-2!@YJ? z&M;1%2}OY6d36AR{b+IwR%ZeWfJjoulS6m8aJ(qQY^e=-u~aeyo{yH-GJd#0`kG|} zom}x@(eyljn@>qM%I@|3mI!$3rnMTj_5!aoS~IleTh!`jCy}wmj&KBk!(D!sZSBJJ z@6txpH6Px)A3AHbWJ@$k#i#X|vJQGPpN`9_uo@~@ zS<}&FK(lE}2fM#eMxQ-h&)Pw9tQ1il*m}*as`#w1cB=(5uTTUBH78oY{U>&WM@QQ+ zr|`8^(L`u-p*T_42(H%7quEl(bhch5WuE-hcxUp9AZO|fP0YZ`b5M#2LPRjJ=UKi& zFf)oK&!F~Ir7cG$j9UoAljLS@DF-Cvfvd(;MjSZXPq*iW!hknlr4s3!N7(srLD7!i z?xgFEWlr!*B>YV$qiH^rQo%Yr6pC!z6s3(uSD{3!!gcYqp#~0g+E*jjZA_;Ty&B2c z&}h1K4V2xGlgFChh3H$V4UST*b^PkV$#~vV0o_`YYspI!%j% z%|aSiBKF{biazYFnUJGJuAg}CCW-GKCet1!7I|U_ZcfrGMJ35bg_G4^e-AM0)P zd6kFjj1Hl`QKY0j9>tJo?|>wT{y@%mEN z$anjDj~=Zy_vqfWH{g(juGXLIe1)s^CNDSSc-yDuuGQYF!aFI?_R4YQ%d|bt-I_wL z%LU?!nWhhe>nL7&cqgm8B2+GSCiD_(M#gQk}|H^M&LSr<}~%KqZ8iW?Gy z_45bLiC*KISN8+nGFFMPvQ;L2>b(~vI^F`=idfVWVYwO8>geVu+`}LR53Ah@ddhC= z6;u@)zww6*uv|5Qabcz)kP1UwkJ6p#>MpHw3wn!V!J9~ zw@#(*`KeFJ5u|3G@vN+7N3#;a2Fnv@64p*8S+K)XyaZstL_L zlTFG<6+`KkGt;~&L3{ap$l4=<+s9_;m~qE7T@zG-UFab&ze7=O&rop|Day^`qyWux1HbzzD>AC-1>D>X#z z;TJ)kHT4-eYKiv2Xc zr>5)mPVn_dMUD)+r-f@~4FYnAC{%6WO&cm4h?3yIGNeWIw#YsIClVr;ju}cyvTnXZeh^(k%Y=rKH^cqY@#vD8)0i-EbCsepO7*;-0&h!IIkSLGlo-n(a~ z?;bKV^MddY7gtk+IUO_nEV^w#&Z^5nje+&KIx7cf89^Kn8+206tA#A8J%X*Fs1_tu zR%E5$$;h*gy$X@TqI+hIhg~IQYU(%0H-YASQVVuWm)0pR$7o*YF zT3V~wfJlRmh6p@`ep5r|81c`QFL929BD+)F?GF>m{@*8xKfY0CwFb52-Tv;w2i?z( z-oVEXe0b>P@|3I?(qU)4zz?KrzHOKXDmtYhrGH5uud!_Kz(jjEJTb*(*7#8-sd>3)JPUvD)5MpQNzd!27*f7}ePLY+Sy{m@;6wyEiKE6qgAb?dk{ zM4k%j*4xJeIm;{Z#vQO)o}61Z1%G z>(+T1j><^^@^X>4(1zU^RBN0!x6nkkxDFB=ezQhQz=a%(I55XBv|Cx;yWF={C*+Pi z7+nlmc&{$Tw2NR^t=;euTMKrW+K;yzvu+ntrAQImT4aQK={Np$PD zoYP|Qd^9`D+nrc7(A$G@c&Jdb)RXq25q0Siw5V(mqVOK(`E`1gT@}C}K#s)}5hZ6i5*SW1EC(+O0XdUyMX~8v1XhA| zVvRZ?hNuB|V!V7B0;ibeCm`mL+6VD<;0IPdD+lK~XN6f5m61kFt9B^h#&Mz1rV#D! z-nL*SO%U-PG+U{`C6ML~k`=EI>N1KuMCApXn!_xgpOT3)0-smUER<#o#kwgWnI zGcrdN%T6qnWjJLXWgL^pShWrV;5lyasfsc6lav*zwocKF3K0(cI8D#Vh~e#iK;0Pg zcuw#o>2|2bFBah05j}K5hb=OY9cn@c!g|b7f#k?3A+ih(Sy&crqtV;YwDLndl#o6^ zKZJ~Ac&;UUHqOwI{tA~sF(JlGad5b#^QtOOk@3sb7RPP`7-t9lxGu}-Ma2X1EM`t@ zb~0!TGhZnKT5+U?nQmxd92OwpjCBP-O9vj^{Kb4x!=5l^b+bvhh?O?zS}BFQHEq|l z?d9&4X*AUts*=pNsz5;gi&Na6=>DE|$wgI+PO=ed$_k1A4jD)~g?$o83Ik@$ihM!V zT5_*!T6wt0!lhS9>Adu|k+&L`@qfO3{cDdkq~%F%%j-mh>7a%KQI^HD>4nav1|GQvKIWaX^(F;2Drj&)4v8MHPuHPK-;q z=X?sMt8afsrl)Uz+OH$jT#S_3!i$@_Dws}JpSwS>1UGUn1^&DcJE4YJ2j1WpGH}49 zUL-mRzi=Y9%qhWIi`8FCb0+>s035TBF^PGOBs9xdCH%WmkH>@HsA7fxZKSXb->cRc zpPg1D^b4z}TkaTR?IRZl=%(>?+!mU^C$8Ea?Xxwql5UW?Pry&?DBH4eflT^NG`YQV zkv()SD1xun@Kl6rV$qlkR#6c^V*YWV@{nGDi9X0ug9HI8j>~h=vVYVbdSO@@ zo#dTHg?5w4crr5iOM^{M#3BmlnD1UuD5$r(J}S0q9ct7l)uuLdL@WsX+aI?Z#=t=l zPDQ9N?a>_DYDVf(l1u42g?If(g~aImoL6tj z;|dKVJli}c)$fyPt#m(V6r>f&MkR1=2+G!+4tttUPCdBVAlXYYgUly`GgO#U%DA_k zI(@$<=#U7_=+PqR9d~YU4#_lg-!JVo8&cA?%SA^sYYSNa4oPJXMFb`VYiZf)q#*pl z%9P=4LpShK!vLh36E%`;rLF>FTx&Z;upK9g#YpZM_~`5vXcHsX3qJSc(#281m%)O= zmv15~j!H0VuFEZW<{P`<_8}}~JUuVv!Ike;f@AI$)3N7>TQMpTMftXP%VeOzP+Ko? z)owICY3cE#r%p5=7%F9hkZ;;H(Ep$<%KZ1Jo5y!)ficQ{^y9DNc;N2yil}07%N!}y zz}qKboUtdnsc_a$9^O(mWGp3GPrpFLCBcguuBa(mk_d0rP{=;{-uy}ttVZQ|p)~ad zr96XE8jeIVu0d5FLwT_03MrXQyoT+I3I838P)9{2R9HBAy_5^Wamlk`iE*S*WB&Zj zOHvu0PN_Jk=kyG5x$y31REvZ&5Xr7!Tfws4PLZ)FCe_8fz(R+R84({(iv>Cv!uSol z@V=!YG%ptU;LI{3Z^}aL@x8UexXl(3I}=vE3-B7VImBB@#>x4D*MV~08sl=>Vt+26{R-&^cEuKgYvOlUc6T}^!09y0heIGo zs!rccscI=EDXK(`gP5WiPQWI}4GWoL4lReNy#1*q$2`Y>#j<~pc7jMg$=q^J9-fh^&FaibdrH{TsXkRPnGr&>%@;3zB zv}PizwN@{r6WHidg^4VM${+n%$L`xc=p8=W*(|ogl=OdWQyjoZL0Z9d zZH*kcSFi!T;bks2JpWt!vYS=KET88Kp++Z#fvSL<)@hj9m)SiGJB~W#H*oHkxCGkw zJ8sNDWH#VLe9MIvW^mnlRJ?F$n`xRxL>_H4eYr`KFP*0D?%bxWZY$yRd4a|aFZdE& zw9@_3rls6P)EB&-7U4nfxSTVk>vSA-K}4UY%4xcqM7183cAyd;l!va3)Fm?Rm$a~V z>`x>tH6WM!!Zg|#%>tYAdZsu`VV$Ypwo4FiV?A{{im9xXM0`BGqPR60r-%yB`SBr` znco!G`5eVQY38fhQGbQ&_focb24mCDY>s=ZC>HEE630QFZwB^LPVQ9=DxM!FYP<%b`fx*mY4NkPlgsuNBRQ9$rr(DUL||_~Uoc>lG}``BC?_!F+8iccBz^Tf z*c(ga+f`j|^%dOoWYFa&-+T>*iSHQPMeklZMpk_@l2c^sX>%c{m*iisKmSrCQe-#i zO8Roe=#T`d1u6|oGA&U}Prvk%d-TxiG{ikmtVW#ZZ@voj7AAPJmRHb?qB!5cJp_~g zIM1)xVkDhm$Z2j6i|Z|3Mc6o8^mv0nUDf3&YLtIpl%pz+0tG3KP<}qTz%;t%i%E&h zXyWQzeLd3ZN4|y|Oy|R2$_eICfx|^9z2Lio49^1$W^K#c_#tX=8%*9Zjd_*>SPJH>gD% zI@h5^t{-jucH$FSW83; zgReogDJkBYVgLfd-aui{fef~p+>wnLStzsQU|=Oo;VE0D={Pa&FwqcFApzR{sRz|h z@P>m?UR4ER@l%;yVjetC16^JcE?>9)Ul%#-upCn`bpAa!IP7Kzhwu-~K>aO7p8Xbs z&VToGCz+O#LV{G<2u{7d;8MG%Z(1+Ab(B+f}}h@$?c+q?j6Q@V9E+sCzcXfpxz=?wWwtpPtAy>pIS7p#e9n|S}-0t z>~O8?WV5haz}}47d@J%|`7_;~Ax;1vld+se`HOdSO(AUXJ31MvBVXu;L;*%Ije4e? zWZW1kD`8(|?m3p|o<%9|WrEn!$jLxBksP~+>&9-0tW#Vz%NON@ql7Qx>#8;h>c{{a zekGp|+Q3`QVUt2D!RidA14W3UAW0-Hh(fF)tP}T$&n& z^;#RPunb@-#fSaG0l|t&>QG= zaw>pvE9O6x^8v>f`h~^`shnwbhaBb103tE#54(q*hLJ0cq=lm1lXtSxVWB%!?9~;J+T?AG2tY`mKbdkLt4d`*_S4c-4(>KdlTZ= zz1_@?sA}waoMN3XtD9ryX(d5*ZW$x(c7s2#TQ3gt8e?Jx!(=pr@YTm)O&&ZS4#@~2 z7U*xBFW%xvk?|3Gj-uV!a3{fKVmZP}EXgLhCYI(NKh5YudM&NTxK|}jvMMRgP!8GD zg{nqlv$oS#VQUI!F3wj!J4iFXraf!Prx6)dA*e>=)L0#jGHWbDqlg|IU8Qefo1?cS z-XgX=!9oDP$_N5gr#TkkY(70Jk1@(pvCN2>p8;FNcaImqE{xF25_KMI=hjpdmVo@m zyg0oWDRKkh@U%eTFum6G8b`>w$rLiRy2((J*QwoKhYWBA>UY*MqHQ7R#|W~=Sp(99 zHG7t9Bie?}$K|9DQa@QN8dDdkbz@USVG@xnj!k^mDJ@!7YA_7h&~nnts*`L_D(;B0 zlhF!62~Vt$#vC>zUVtzcVzxnXTWso#Rj^%YD;>D0p)Vv}Dn&hwT{laQpv@t*=sg06 zoG;!@-&M1GQYkfQDwLTqb49BuBKkSTX+-ZM?SOzomZV$E^8NQfxw_TI-%poqJBgF& zuxNK0Bq%e|Y1SxB_D%bPDVSvw{w>x(o12a#d)uQvT7_K~qK4GT*4Rt7oIvYkU3QP< zzHd=}XeN1zrd?dUSZiD1SS&93aEfNabs8aBRdYj!wSDATw6oq)z4biDS(*e}XzeRKRq+jREGYyR__lof7i*F=$L2}%KnQ#NXvroT~s^rBKJh1 z`47Z$AXjZ)E=kq2W(Ds_OJgh8U891!sRGo~0=N23r?&aJihLUOwhHyXc-@gzi;I&J z+|cmC18*Pa+5~zs)qZ|xdOgZ#$xX;Kcp7&P31--gNH9wKO=_B&IO1PQf~`|!Y^l!b zaE;M`u8}f^wv2Vs=SxxN<_+tlVf+cLf&Z{hrYqCFZ%!LrR>U1*8`i2F{1dh3senN} zzKx9Q=DfRLVZhtX1PMB~l~MW~9+U)E5^hl|`5$I2O?u!gaT|xZ!LN}O5j$o5FmM46 zs--kM{qcS^xGti%Qb=mNd85<`S&8K&HIl|Xs6i%jtG?lVooz`}7V^t(l!N@lD`g;O z;J$+VvqJVkN0DRb$i`>MEZ3lEX)XLe@+c{xzzUfs(`gL z(q`CMepsYrhgEWel^Fyjt>y%q<%NW-(A`*5P>AtbavtKs!tI5GRSddyZSZ{NVh(nn zHde83IzL%@FdyBH^UaN{d9p}#lwg_4Jmf|M6>P|B#7eh(lsGtgbtfI(oJ^?N21aAI z>$Crz&RS90Ez{vty{J2DoC%ZZ6~V(~aV9Bi=ZkDqx7uN%3s8m+j2Ub`}|xHyoP_sO1-;77~roDy?S$k1v3u z-ppEprdr*^CbS&&<>){IurKi~V>uUTQGMT#wm$C5af5Q}blQbTOXa51LUVb~$(L<5 zAJJom(uXFu9>v!MaV=iRTohvSP*edZMVGs&<|19rakLy2q(B!`uTjv#?^PY`)nYnJ zmodwS{p{gt)_TMLA^~1C9w)kZ=GpPZDf$fla5hKjv<>{)^d#Hc*?qXR^LT4-uglSB z(65)ga8FKvQ_NnxFpiMu)#(J9q70q}hwja-7ch!DRacn*>DYND6wV2hlHD!FcTw2@ z-(mlsSZKVf`E+s$$8JGzov{Bi9BnlvfEB#AHziRBz{^Cf48qX>Z|va7Qi_g{R6j+qup5ziuyL&IUp zt6uheR82+4%^<3JuycqWRpuj#OAa~b=zvcalfcZ(3wo=QL%ApDCe1CYKpcZYVd&!$ zfi*jog3b*a9-c_EmLPLT;YE9Z2`Zk8wj)5&x z{Fsh(tu7~g&WhZBM^lcPxskdC3mPRZ7c*_Fz9IA^p#pF7si$k%Z%Y3@NdI1%65mwS zAZzG&#>WJNiPV$RexPp;lH-Dj-lCb$9||i}*@11wjnSMe?^TvM$g=BOQY$i5=81gQ zE~?g7=eB4!{|Q#`2SE@^E!mc%s6%hYc4)7;wTlLu$Za4S{@zA2#-x?X?|R_xq+SRyUf0V>N+tN+nIySryC7s z9@J#V98q?1SJ-_}%D5QR`kBgdX4xCZO*OIp5uTDNy)IY_47X6#G@iUf<1TGSw=zA9 ziL_@e)6yib5o_|Q5pVsS8qr7@u|~xkB=*Qa&!u-fR9bxOx zp{E!!A7{Ms+bzTm{24P6rL_oUo;Zb$h`3`DxQKtE5yiM%kVLJTK~~$;49z)a3O`5L z`P-(ctOANe0g*_c9bmVK*&Y_CU1s6zkOl_7=ZJ?5 z45NFjn*4hS|1pQaWg@KTKzLUix@RjU)5DAxi=w8UnI?(Qt~ovzLrw>))(Vn68^7AY zesIAUOq$>vM4{+nR-pII;JofT!5`3|eyReS-y;_Do@fBdUw%;v0g0(-#^_Lp8-(`& zQ(Ot;oz0-}!;A5_=1v3`KNzP1!;GL2-Or{*BK>^P+lc7NP@jN}*E%vw*(Zm z&Zbw&?1~oBGru%waz2_D`LHHIv$D-bv4a7i=;z*#w908Uf|xf(_9V}5%g z2ZM-Meh@Jw6v4`s1z{vo&(T=n5a>G!dUQ8_c}2L_b*{Nxz(CP{GW|e6H>XpKuDVIk znFK|p0W>!$EV|2#>vhVT_bIMN+8GHNsVrEi&nGM>R~N??(?RP3XanQz6p@->iC`Hg zCqcLcwK5hGbJMol8b1>LvirU|FK6!iPK+6zznoNqd3j90FfbkBIrM@ttCvj;jKzaw zDS=x@8){olr?CuU)F659b;_`CH;y0dUeA6AGc8zDJUEPKxXtj%F|(jY3S?ISA2B|w zb{Ff#LL_OM)wDUSnO#VQ$d&w#@XX@2wQDq!1zpq1#O>*lCp&qw$gt$D`*M4+?8t7z zV~TH9-M>`c(K82eOMcW`i5?*jHi_WiTwtRc4hPkenl;;maO!3Th>Cn`wuNU+Ds8ee z8n{Zp*nL)~44`kkTe3MZN7*yU4@yELDo(6cW$R}1(sig;I-Ct7*pBFIh@1;1Vz~nr zX!P_ueRp$_8-Vu4ie%gMvTd%Ft#)#~??$CkmPvZ(UEOYddp!TH6_H#RNhi5qN9K>t z6gzp(^dwODroOdNi^RJd7WwG;s63tYvrs3}q6oz3bC!x_tne38hcZv>@u>>)CEOuH z#71+QKq0MuSgT-4>J}am^nk8jV13T@RmVM+!~YbXvWQ&B>5X*nipD9EAM{_4Yu6RumJj&?9xgb5Ae2JndySkt}E zq0jGqics`qk;7s^AZ)<|9H|YhF6Qd4HmshV(h~V+`K%%9>97y~HDB~61aQ~JPVlJ- zq0&tc5*P~Ha0Up5DxoaM7hp~i3oc!Yl`F$8>eHAm~qxv^RRPC&80kwx+~KdY*ySFoSc zPrNPT`)qo=m?^CQ2t{-$%opOw7Ho3?G>>@))q84T_}GY45*`P6oM{|?8Gy2veUDn)lMUA+RrHT7V2VKNR8%(0mB_ zn_e=uu&O)-;H~dWIn_aV=om^g!R&dK?c{?T(M_-83bEP^;jgMo)c*)JF7Y+0!`}H%t|A@*W6lk4N@V4#Dd~L~0?@)-Gknw9I z)53gpGX0hh6B*|8#Yz19Vlj;+Rk7PlO4tpIsbQtAz=SLNJ15>N7V0r?<9x}dH!x;& zVFgkm#rqiIjim1U2&gva%XSRwg%NkytX!Qw`F!Bo0uX>C>W>j5pJ~cS{xo&n?7XP` zZJid0&O$;S(@yHXYe=SQI({yaphJ3^6ccU|keiHX?$hR z_bcFa_$BIqJe#nkhc#U{_n)`5NZ(OC_2nAYdx%X#dJP9*?kMU$x2m0JRo7k=UUMSS zAkcPAzwGd&c3RqI(KzlHT_`HNQ_)hYF#uzFgniSMk(ILsa|D%ypBboAm&}(2)UvJ; zojGXO#N-=#MlZ&XPTMD%7-18-vz){7F2{5?#03&M#4smck}>|tCkt<>X*WP^gG3t+ z_L)NFP3I&-2S(>VmiZW^9la@6|C(zqju}e?#*mLb+`>KgS#I8HL;DotqMVE6IdUi2 zw$q^QM$8Nh2aX+eEg}%XVd>4JoY>yqoW(QDTy#r|ZJizmw&`(T4j2WHFcrbzINuRB zr`S=!H>}=x<%+PYAlwwzK2QuZ*fm_AScae5?b~!FzQeD5GXDp3h|aO0MGYaAI$!EI z6gzILuXENFoC81OmaY{B=V8PzdPMH$fMg1FpP#jLaySXdI1uf39TXY zdU3pTa(lJhldN+6CZEEA}EV#1VFuj{l(&WiHGuJ}Ok{NBd6CqCDaWxHopMN2e1>|iPP(3H_= zFl+U`*6$lv>TYCN?uJWH=qi_eM$-Uy5L#d1Hs&x$*avh<5Gs0l z_uhR6r`MZ{(MV`V6P|t&!c}Q7F*qf;U~^P$eXE9NAllI=F_~OGKr}u<2^O1xS1B77 z*@i)?ilNP{GBT`_^2rgk!TG9SWR*gd6AE+*{KhWylwryTbI*2hI{Sq&qn@rw92-LQ zPdu@Pu2nEdVpL53^An_a)#D8ZFk{B# zz)L}QP&ls{k=}v=zeHCA551yRw5Tc;zwkrb!D&hWg$bvS=`89KVrZgQqt1&qMpa!$ zuYKo6*S2}5%vVba7uZf+Kn^2(NNHnvP&O5tgiqp8Mo65oD98r6udcHhbA;~YQA z%OxoOAGGH_HJb~AGD#384b9}e!=MS~LFx<^s?hmoUJXo2_ZYI~FE8q}w))wDg{N|40^4!Jj2?Xik{IrgfKddkq>z|o> z5xc@!uq)J&T|oe^!MaA9wj5R3bS#Wc%qoUL84Jh(iS?z3$mDn3_Jxu|a9oI9+e5|1 zKeNVtW*w+z?1*jhgGuJPXX`xPDpa)yM^e-teqhbv2apqN{YGv0!BrbNdnzV8_;3h( zzG-BvFOZ~B6=zsLIbeR*{HK7Vi|PCY@?IEyXAU!2E&i}Sg>%{R$MOWE zZ}i8h?N8(#{x7Jnn@wl5$rbcua$O-W{uWr@E?)5I)QTPMGS~N7yOkYV8BvXw$GT^WKmz zzrR4lJEBAKZ^!wgADmY{{LwWP{NY})TkKQfnrY6+mji8WH3%xZ&oeS=aJ6}FIvLgX z$k_LyGMHfmKTk-iXy=CU?34!5-eal$-m}Y(LpiEpa-q{r`dc(VpVSJc&7o>{&lJHc zjbDT5Bi@2yH?vfDJZHpkjo@LC=WxdTHPPNHA)KO zSz>_;ErOt7#gH#98lqq-b#%WllCRhU&>BtkFt$J0-tz`!pS>MQTzr}p zDXWp&lzMoR0^_^&Ca~%30ueJQJ;ka~o3TQ+{2hGV8Rt=(TbNKg={&EM(k)ySEttk3 z;zZ-@OW!e3oSt2rbW}D&E?B3hQ}V-@LiQwqNDIFdWzeFZO-Ck>crd5S!HBjkzk1$1Bh%;3Y-T9PBpwXJgGCNI6<_~Ad zAT)yR$X2E|CR?GiQ=vv9vZ(%OUQ~ZLqZ40Lf5=6ZhTfCP9X#Ned!j>*e|`%hH~(}` zH_4Anu1oR1z-;xzi9I9#Ho@JWidyhiz!f`S;hIl&_r9 z(Y~so2D7knL5VWZr(%$^_c=ZX7yz!UChV>gyqoDt1M8;cVU-kP;8>)|JFb$%Yk)%T zu{oQ4UgDBv=+NzTgjd5~|4Kb_hHG`NWb($I#m%6TE%wTd2Nd#5c;fcR*TIq^PbxfZ z=sd|UrzKx2piEUxFRFDWTu;!75z=xm^{FJ!OSganJcnXolZvcA5&QkzdS=%Klq!OenOr6AN42DaKu*d=q8SGYCtSi}e^ zK?!)vUka^4N_i55{|HnU&Y&L}HO8s~O)8w&2k|-Ha0yqxO+Rsl>6xA4q z%FdJ^0rF)zz8I4=lp?}gc=<_m)VQZ#LW{a_gNPWIx-NK1@G{8BFR%ENx#`p_O_mBp`VYolkE$hT21$S*@Xf+ z+D;=-V#^~|g}e}xPAJqbHpZN1(;y2pGI3F^D>j+XH2+A&_@o-WVb+3nt!RWD!K0^T?UTQY{|Yjn^x-X z9z%Ihxr%>{*f-1WHCQ)~X^(q3Il+(@wY%cbw@nmtaDKR-RN<}Klvlf7uX4dokpa%` zX^Qi}EubM1o!v2JWjcH?7Gk_=10#b_z0$B7HAP70eWW4ovA;cmxY`)_cdjA7=jn)= zw5!hX>at*(ULb2Y7)=M~ zS7qgHs{#2p5k8^zkW76BOlL9aC~=pCh>4T|(a(hwd9s@8;;AgCRi(}i7D<4twB>>X z(+ z1sEoHZ${kS8p&)2sw0P(KSCQSnFAi-v~_>&!7T~P*kN@`6$QO{EM$5OtMCO|af4ZL zM?`r#1x<0rm+4{E@QyI5)ARWN9qYJ_-%0-f@;+d_>W-JwnDS>5xiyD_5Y-(MuxnMj9L*c$YD;U9Ww!AUn2>lbJH-I52J;=&8Udy4jX_iykZd)^lKE zz-zkaVl@fk>qS6SM1YGpmSD3<_GmgiX8|3=C<);E(Ouw{p0ES^@ziP_m z6d}V?^z;jBAm!A8f}GAGe_MEdUrd<6I(2MoVI3bK%1F&YQg+HpO^VD!kf9+u2GO9 znz+6RpVjrI3C4gvUPeB=;;fA$7o{%`@L3URKa2zrO_!>+VUX8pg#x0B1F>cHr;K(oUpe7atNhNePkTX$jWoPv*C1^oe4ZD=z4{(FH5vLTQ4oDWPDBa_|s z94Tt|iGURVF579{E9RrukG@suU5v}V|AJGAtFgX( z;IDpczkcwB)A{t>58iB`Rq=KA)7@Tg8~z_Z1IIR5RNJ)8-tF%`eAo>?4jCR1K0kgO z`Fyg3FW;Dqqw@G}fA5KW-mWfBkC0@JE)bmYZvXy{DG2Ae;(y`&qet}qIG=BmnIGCK zz-v_+zP^~0*cB=czI^iyeAy;Y>m%IG@Ae;1;%&9f;WL!^KN!G%dw3t(wsU>heelh0 z|G@B>M+oTiucIzgWLHsS=hH?DT^TA26ShF*2Ho!62TD~L+AUi!@pg<&Q3DlB3Yo08 zO>d8gLEfii^8(%86z^#hhP|W^!#eGLWZ77a6V}mo(Nn8H?C2fM%7St2MIdRGJ6guE zR#!-JP1RLuZjEB^#bX`}Q@5z=T%HD>-%_&}wJ;sc*-hHx&VK?Kc~B}39R$Ya$Lpqy zb`U7Eb9FI4p`mCDI!$|s7m=dwq`Q(7Zf`69#5-T+C0&xO&X7BN2gvVid5^U78%| zd-ZPmgUZlG%R7fA*jR=koXPn9d@{YNG>{u=1JGbWM>HALQ1`g7;Vz?gAg*GQ6|tD0 zR8x%1DRoIYFEDqE?Q1QTdaF#Pw9$i9jzwb@6CO{cKU-0S6=75cmS$EoO0z1uQL|RZ zcjS!#+PlU5fEq?B=dy@HkaO9U6nc^^lDO}jZ?Jwo4b9FOPg)_u7w;e5c4E5Bk85^S zZAGGsapd#f`l^xi-6qLsc9 z`z}{J8u*BmD+lM5*mG4OSFNXRdsZ!-iCq`D7b29I@DS zWOscy^SzRoRgk;ib4XpKD$t4CGK1_~suODM7R8476=Q=_Kvp)1Lj4n$?!vca*<29r zNR92n1V>@MVP2gz)~W(?p6aC2I^a+x{v+DJ2a?Jya>7{#arMLYhE z%(6+iFa6+wKHe1bayo3|Pw)0c$_%4-u#EayE1%RNnB(wdv~ZA9ieb4>DUrocNqz5o zQ|r#{s=Z+?6q^u(WvyFVF}WC{?LsjWm69SCBd9t1M~iMkTU~8GiNA2fO`=18v`oUa zx|7htEc?-hBa?ANEL*zx&B3V1=g(mz@MR?u=96;FSW~J6{0F0Zm6Z(s16QGOX)F84uuZeKmV{>+bXL=CkTn?I|bdqxi9y zPq8&6D#_8YbWucFB-5^L+lIX@yXCAaL0>Mx^=h;sb^~Uv{fT1RDlHgiHL~wlY$>U1 z+=;_zi}4!KwqR6NGORSdq6s5sS~`!3m8YMirRaxyXTneZpYXxVDc@?qIQ7HmV*X90?j9=}y>Pwp zwx^B<(Jv8#DOF)`Fez)@AW{%h+uoOKT6LDsigeSWTZX%B;OOQUiMNIJG!tSA&uAmP zJmcSI{BYSijCIEpkm?nREUkFs$J6QJ3|8b97~63UOxSMkeyoYRw`dzmMSUT(5c%Ru zAmU)kt~)YT9T0taQm6ZLJe>kdG-<_}>Q409t@RT*`zS23NikTY50p#ID^Ks!4rzJnq^L@sSpkvr^L9gJPh^~5$g5qyh@xyaPHw7J=OSdnyAAE`C zt+NYLe9A9$K0LZjqdj?NjW)vo8JEm2*7lv-oB}X-^Wx-&ZCJIldjsOtM?LuJeO}Q9 zk7>N|$vsr4(zr*Fzf1glw#n9Sr*#S--YdG(BsrrsMl&^{D+qp#m71b_ZvtW?u#rgT z>2=#JajgMk|H)Q&ZSP_QN1$&K{Bc^;A?vm#5x9h)A{=&|h>FOwDd-~n?TZgl*l1Q(P4rp*7Q%>OjXH2Obn zX43!c+b=X>CJgy5|5oTB>WM+o-7aXH5uMA09kDN>c@=Ds&Z{16UiE_WuDpUX8E>so zb&0~5t{)_^#m>JgemfaZ=R6l#lu@#UX<0ZNc-hL-lm`A z)ncoxCj3QT(KnjmVS?w&qFTpv!&(~96%=1Qt&tHEAMxFlF5vs;KmPjrFEK*{HA35} z6TrQ8dYoFDMPD4#O`>NOeQ~^?`$c1AIvV_7=*%}x(C?3k(sidi141_we?3}EkF-Ui ztV&uM%bMWRQKi4pfIFeZ$S69nTV2c5bBXQ4nIi|B`E>kpXiMuYk)qMmv}H`aD5h0o zrrKnxWhOL;BxJ?Bhh9R)zEU|Uxiw&7LW4{sc1otuBbJnOqGe8{cDd$`8y!1aWl;BVV5lzU!098N9B2eaCf+0qOy(mlQ$jj zkP_`Pe!>Z<@E|f zcxc-=FKWde5(Q8@k(&Byn*0%h#c3Q7eIHl5T2teT+r4IO0%W7$UXCi~eiU{2=M2N%WYBe?eC zT+k0;fg&=pOlSkwX&({v8X>$M!ge1H6_o1`F`E5e*&%ah=IX43J~`eb5fmcY)dY1u z)w2>OE&|6GK(XCyw-d+U-`tE&D4yg5360}uL=zc^*MlBt=eAi}*|B*bPp5X?D=9=y z>G`g0#Fq-vdSoYHgeAOLbGR_U3^# z9)(?&;}>qQxXI|ls8m9;4l6ozvwIfM$xrp#s> zPnSi-mxVZHko`x>YL2Qpcx!4rE8x}DSwWcZ$J2|+u*#~l=>_n{#|XsDArFZ&>8fjd z=26i4sn;_xc`3vqo;ei)-k*swICI9q?9OoOuPC4e2VA9709hg%GkM9r&*TBZnbm!$ zAcb@i6Kr14CXI~B(o^DX?LFfkM=*s>y?g!RYc?FFm1_mo>8zOiL?iK35dF8=9Ub<}IT?&Dh6T-f)?P5+3(_9i7Z+og^)}B#2Zb&^=b1)iDea5NsHiw{G#Q>^ zq!S9Tg*yy#2qnfJ`f#CmH42er($0(1d_KeiIve4%@_0C6juj&BD0kgrKA+A(B30}2 zvaHJEQQ?j;%ZM@RAE`M;K6cyuwfB85%d2`kF7V0;j<69j_ehu(hbbWugtq?U zQ9e0`#faAtIDxq)G~fX`R@2KPegH2I^#uf?=l-NVw3p12X!5mC0(GpII#mp^Ho(`^ zwiur?YSM_Rk;MHby=o0$uRUevMa~BdtDl!N5K8>dwzjsi-!3NEaBr|-WGNUu6H4Y3 zIDz7X!ocC0WvC@uI_p3WXvTS7V6d#+-Xj~mL7)uaSD>C_) z0Mr%*NTkingqA>u<7!(J?DnOKKDn*C(CKFPrJf+6<-TpzVTJMf%y^(KM1>uFJx78V zx|j+WE5ciVFJY2OSX=qUV!B0W02qM>_Q&FMI+2B4sLUABze!>K7opAYCl2qp?IUlWI)z86aX zqNEk{+I9vCX8pXF#dcIXD5g+lOwfY~Pi+g7GbHeQx9{ECKq(Er%Fs&c_+q6B9z=u@ z1DgO|Wn%`b`O|4hKghpNN1PsYfKX@&{NIDgZM7hwc!iFLVKn-+1lAih zpAI;a%pPd2c-3l`u+>$CM}4t?>q%2UV-t>-TvOAB%))Jq0rs_{kOc(3B?7wHp z)7-mWIF(aAS_Oln6;N5K|M`^=MD>@$hO^v%1s zN3UM~`qSIjzrK3k&-Qk@A|U7zm}x|jCttaWA~%mND)@~|0&@H4;XI><5r}f-;%r>m z>S7V%BpL0PaPk!R7kWU0ubiBhZ=nMq6+eg4#bh}^5g3|6lOs{f zH@7^Mh<^-BcWL9_CXrFgDTW*UYlg@%z_NVI%_(p(%9Zuey=;ClsjyF4jO;QIflzlw zXxkw6j~3-@B&?f=N3TfsYC3M_)2e!hyTA|Hf*8xkkSJ0RAp2nbfu$7B4%mFG-H)ol z<_ga+_|mYJ)4Ua;f)5TIhzlyu;l5O}Ii+~YH9ax!SoKI%Xdcgv5T>kS_ZR??gLzIX zcw1eJ;a{AoeOE%o>O6Z|0|(UAcAI2lYb{_E`|^BJe~)UZ+hHL%|D4KP(lc_19_zIvW z%XuqrfeWeh($^z66&h7g7A&NB^23WpM#Q_e@ z&2-c`rIm&K38tUsKR49L#6%IuF_3&Yy-=2M?Wz+GVM~$AXqAw(3ulAZXmYlYi^)4A z)KDZWilUZM*D>=g8ZxvZ@ak_El%R_ggE=)S!4O7^CA!;F#J$J%g5_R%Zh}FK5a8b7 zAsxqj0^^~4>+y^mm^lQc3|*lrE!>n-cHWq^=L?24DN3;H(u_to5$j z3~>N$WKA_YTXgPhB#cSr))(0~kl9lE9c&uvY&Oz-%vrs z+Gq`NTexru8l%@r4=MRbA`@L!Dpoh<{Vl(8tneIOf;-fFnVi^i#D(&QH0@yxKwp|+ zA-|kw*$&dY8Hi`Pod=wT+gFJ*_9VFzmYy>2?~45i)7 z3(&WHCv$4$u|-m68@*sK^6V@_UojFalo=8EF(?7F;}Gu85dLMaQ^u)96~FtWK9>#dI?p14WDB_;!usUCpMm)@%`nj!76& z5Kzxglf}F?noL@?{cLhE65%v~=zTbds;|65l--CfNV>V{URYnMcYJZAJczhgz+CCf z9dkkZ6RI$cm)>N$5Zwiyw2R-SlR|BK&|RbS(j_oh2Wkh295CLFVPkU&uWp=O==vAS zz!E7R=9w2=ac4_*c*6^9d2W2QOmb;ip<)>4YzVE3_=mF5AiJD$!BLaRsw)So%ttD2 z&{lR_406;WlJ#FcJ}yr$kTWM0r!EXD#!S%ck}U|S2zY&G!+1Vn&>`4%O{Xb3yq<#t z{_Z$X1YwauD$v9c%V;TF&mXxmm~4_wQ8v@qTy;@2BO z8CEQPqe~`LqEc;?13McTD_E@vFbV}W!nw-h*}~n*nZ8|kv7bn&SqLYw%Sj9i(&wbg zi+;8x1d-2gFkC_bJzMNFk|d|ujilK$X>i0Ljj@@4GBv&Or=9&$CCSlc@$heH|Ff z0lbDkJUqO^yThLIJ=MrsWP65v#*<9!3mh+J!`}*U5cDUlv7O!sjA08IZQwh)sXiPD-PO~mjC4bnd=q4;mA9_t9xGt3(>Qw%7br`-sxwY$?d+d97 zIQ$(faghE~aaq=Uq6V23I>j1}IFP3REhq}kv`e6*-QkV+4e>w!X12GVd%bz9qf`b3 z81Nxoi0!T#3exjbx*W;y4aNBCnl8r%2@M5d9+NJHH{+H*wxnpeZ_bQ<3&yADMx(cFwd5&yCCHVN7Bvd6%$x; zz1U!yg3fckL^U!fryvudzp$n;T#zY>Q*E`%bvh@H&&d12OvY9wyhCSqBe-1uM+qJQ z&OtGyUV>Mgo}KJAYGg%yt8Tb36sZ~1HgFR9US<=mBrvnC35U}QUF4qQ+OKW}%%~u> zvl|Bwk+wS@zk_O-u#$55;w`le+7^f;eS-XeLn+Q4OEq6HLTXEM?&BNgOq5 zPX?^wafm90Y~QM$W(Ld>nzv(?EE9nZl0AV!x`@+U-0b*WD~w zyRwzKG*7UuC1m+NC!heH@TdMJU(~NX2}O;Pk(U7~`P7(s{!lF6AzmI8gBLc181jl5 z>J5~WjKY|)0_0wN&A2}9HXVba)S5yPI%5wHj$sS8vsg0n`V{5JWna{oCn^HHi zh|^~G#*)@tLhg-@zaTdUi8TB9m!HQ-v-lDVqW4A*4}rMri`4k~h5hP_%SZ!;ZYaCM zT9;Z9+#&Ftn&aOQ@Hyph5y;u<>k+_p<#h(%hIIKmy5r@~Z0#*h0!GMBI94>=9aBCS zt&;x5oq4FxvgtKP=>T;7!T1UXvU9-50u3fxB;(4vtPDwYXwgXU^Isxnwi(HQwB)FM zffq?;EDY0A>{Yj4RteyQX183*6R9a0$4L}AN#GZTEpM;e=DThEwqOH9o;>3_D5mzJQ5^sT7a5KFWY4jw)QPJ&FL~5 zdkN)c4%XB!Qx#bsu(X z9EHu|T4`oF9sjUfjuRM|^d?=e76p6D@Y`y>C~$JWdHIrE|L6Fy46S?oE`a)1ikTm$ zRQY<6qqAi>?o*K@mZ(`7<1Lo-G>u)isvD+!hXtOR0<)1*?v-LE@!$s=Wcx!@VRv9gYZIpjtOx6>d@vqQQEY~h|;J&7Mz@JAZ+dhrQ1L|dr>SX)3Xn0f2%`L`tz1g}|4R{i7`cXEaLDj}_#(`GtUoAP|fnycz z(R@Nyekd|2q2yBRyRN{2)Wr*Sh+1Ec44P26jEKQ^?$ct{2MRBU z=9yQsaDl_=*&5f?Z}p@+GfTmq$g0!oXq#;C??xIBbHS&~ZOL_pY4+U#+Y9_w-j{Q2 zQmNKL*S^4LiH~8g{Bwxi?|o=;a>O!6I2)f~qmk|`v3}EMLOdrLfIJ|b@CR6r{b|VysX*q`RcjJ(?1;u^+w+CVg7i`@xKR9CeDt^Dw=niknF#(wh`*8KI=7|*9{j}{$o zJZ9FyQ^rM5-@5|Ho(*mRvOpUPK57wrfR-58d5a?N1unS$BvB8`pldq(lrZ^D7=oXK zmGT`O3rIq{o#%^wRuPOOS#yF#;H|Q^td?qVAWvkqu2?08?Q!;$-R_iFWP3E59MIXX zU92XI{!EMvv{MUBWQI@4bvaq7fhEKRGKI&RVFxD19-JKHoG5w%RsA8BA3*Hqe994C z?6|?W00BhE(FPA5>hHu#V_g#$?^X#YVmv+S3PxFjgrM@c6v~z0ahukft{ZCFG}g4? zT$^D1;1(hxU5oog!V8V#Xg2AD`mrtF`f!Bp&fGOl9a8FphEy{EBll;bijAsb(^$o3 zNU04~Y+MzamMZFb`Mxk5egr<=rS69LyjR4LytbH+5z35jR;h1;U7?_rXMxPS`|~3; z;Q8#L>hzijr_N2s$j=(o^CrTOI(N+7OdUr0ZMDx=Bz70jM-v&(&#UR?5-#XUxNp25 znfF<~A30sdTT?o62ryJ1g*i`>`JNk-3?FNPWT*&5fHuAo3sj04$8uIKH(?^dmhPuh zH*G~XQBl81i>u8+N}QF$RjWC=QDk7bLi7~=yuP!qtH)I zwfoLMX9#zR57pv52X-aSJ6 z2O3GL?#`FJ2ES)sET8dTu~n}fFmjJLFBonWemGW^$GXRbmx36ebLZ7Mfl6T3EZa!v z-wF3Uz0z3ZEoWW)wyS4=U&%tW0D0ifCdg@ z001Bhj1fm?noYbpMAYMb3Mm0uinap()LJEOZgi#MaR#sfi>G>Bmx>S zeR363_nKeh26F(;>r2QtG)oU?8sLs{l=hhq_?g-dTRgPcEY;;lNE^Z)ZSubzvL7le z#Bm%lhh&z8#%CrnR@1MtKCz~^v*f1m>q)&LCuZ+9mmug8 z2wHp!Y{Bg4TRH$1;Mv1(87n!jt->VMl~`R|U5$WF=3OrO%rL=tbKpQLwtu0cw$u@o ziM^3HO@UexWEg1)rmtjJU+L=@)>QhAr8aZJ{VJKFFN+O0oxxR!1n&0Bk@nvVlp=8u zSq(ytH#V3p`8W_Rv6sewGQ^ksMyDfrieky9;HVrInB`Z+9WGb5url(VJ5|b;jj+>^ z{bcZ-2`k6X>ACic28MK+apGAMPnQ(dbbKnanm-RQRbH#;4 zy7{E9{LZ3u)a2Es5yU-HS}(Kncha!rkx#0Zos#u5L`4Y&Jp$((2wZdz6tj^qm+L2^ z&iXx#K*dcloLpX3e6S70`Z>=RqlBZINi%t+A)1$;6W+2Rl^y8i1{3D_-Kj5X5a?f# z>E(--*6-onh6a-47MCj8}}Ddk=D z;YD$&PCxp^&DsY5guqPh{G+O_UIV_Ep_0!y>8ijQ`k3TgL7Nrl`TXG)?D~{Zw{bbA zAHlLl!k1x!G~6arYWacC^S#A}uT(kBJ$^kD*4}0T!<>ce#?dSMU}G0r2>8iB&RA&p z^~ba&%w6H*f2%$A?*u__e5AKRrSy`gSYw>@mw@ z*(mcq2W}t@zJg|;ylQr_NS$Y=5Z%Z2jS_ zVA*3c?mymf`_5KZPm_-DO#wuVwHOCG2a=YcFff7X$wuzw_ivxRc>XHPW4`;j-oASA zm)9^oGo>D|A(WTrhvz@~nf67dEhc$&aY+hT0#B)dwaa#H(RTq4Kv(8R+GR-p-W@$) z8~@ODbCZ}KkeM4kCUL?kA;1VDrHOBZ_j4O>>U*3-TOyw{}^Wk~WDX?WFrX zfl71R#3z>zQiMg%H#xnJ0`Z)pHR?R{e66oGk0D#cdh4_hZ_E{s|6J$o$zWoUoFFc3Kb#4sbO^U%83Lh{R-@! ze}6Zdh+lsyFW-ruFRBl(maYIvK(@c>_n@%7XY7gKf2oZPSezq=!NC4^J%cGx^;O;rfiz5*3zJ{m9|GQ9TvS4 z&HSmjfNIBE8+f!9i-Uerb8-=;ds1ANY(KDLi`Wt)SS$^#`k|bCYv|ji{E)B3M>NR( z6hjPc2zHc@p9V<1D&B-X+Q(&~CZHQ(lUZY=$!TbT1n~kkdRhyUdge6@jkz5eqC5t- zo?4r|BG*2ZtPZ&7TzWQS0+OpLoMS4k5OK7ZpXz%k7yF(t=a~LC=FhblJ0dTFr~F zt#pd3!)(*ie^Quc_kEM$w^!MHKF057`uEq_p;`gH05%GQtzLGJe1m|sERkGmlAGO0 z_IhpLr_}P{;o)IJP^V4%%x`GsH#=D8MYNh{e$&<_qc_E??3s6&@ZsNz`ez-cr@MZp zZc{Lg(Erl!q5hq=x#ui+z@jn<8|sb8u}S5impCOXH&Srvsq$dCfTfS@Qv)&-rbcHv zbh!>aMu%KjDQX?#&##zz%J6%vAMij7bX4omQc#a3lk?u!Wh_zaCATUXjD^^(RPpfT zf1>Ao{o`-=VWCwms;XmceHYWog?XYED^IMAP6nIOoiF<&7QI@Ab53(a@1duMXW0R5 zvw)ww97E*TL^)JTIb_o_6Aeqe#6q zi0AFZwJKyubFvEuxF$@uj*>hF$eV|Zxq0M0$y=U-97B^s+7Czz_XflE5W{B*C!X_? z%s13EjV~r64o)^#bZRyNfn?YuXRwT6Bf8;m=*+Bcuy$mpr^EfT)`xzV;c$*`he(#(Z9wXup1c2BM~OMTKTZ(_u2U{2?x1NB(G-(p{A_ zv^F_xs+Tk1#bTyfwh!J*PFV$+0m0`BAkc@sJquBe^#(g|en(;pcUKi|;7-x;-Q=>s zdAF~nmv9cfj4(?vQVw>bpIN#RTC$sVvKvEovkO@=RD@c>Zmv`AKwsC`*X=aF&-y{m zVMESgW6q(@dDxiqup#H+8Na~GXZo>&esCpxPd7MSFtQdNB&46&@iH`KjjT-L=DcwYFEbMdJAh3L9MnWK~nE$hP zCdxhlK(-o9Rk8YCPf4xA9SUt+p&=LYo0hf*nv%#E3DQ7p4tz935>Z9 z;r>^25RBCsSF>6x8462}4q)(&l<*wdGeZ9U%6#M=^4A{vJ6O%?cjbl7o-k{p7a(M0 zTj7RWy85>GN`aK2NT!7xoLNte;=Y)W!W~A_eh_4qvA;6M!ax71rS7i{|K~45CcD?cX5-b5UqS-q0iu1Qe2ey5hUx?YjUq*bR-%1%o4 zrKS^rARouoYCbI{eIXIT2Ks}G1u&_Q)4lK%xnlj64Ef5<3-uqkj77ZoPe1>5KCDXP z_-|OA!(Jl)QFH#IUNA94o^~%4RDX#zf<(<+j@&%Z7K*9#g6%p?RCN?)lI>~Op$M6L z{Z3EoHRc8&o68b>iI|$2B?jW3Sz~%%oO8ati0q-p`J3cNi8Shhx zSLM~}isEUi;~1)9{C#9y_oQasT9VT;#Hw24A7*CJv3yD{n9=HZ^39WYd8nn;=L^T7 z{&$$b@v-l>snv9swR4;G6FY83OZU?)ubr3J)@2qoTg}{SoI$GXM@q8xUT(X@oz!HU zDRCP-3xASLe>^+;F`$s?IWx4LA*36_pO5%++y&FF#*kghH`No|c~8RF6 z@#`YVufdz;Ye4Nh%GR0z)u*s^h+Kh)g3h{$vr4LXFShZ-=#YNwy$cc*JdoiJ7mvVW zrX{Xq31`6beEc4EZtuw=fx(1kbh~^Ud)tHlQKD-{vG=^(>l_O9}cz1&cEN39dK3e*^7#ovnbZo{`;5;DBaXA*}| zFl@moPE(3KDG0e+yU$--4yyzti$NK5%Fucx1~z@I2Px2R^yD$p^)($mc~%G|;YN@WY7k`KNK&3cW zi5j*y{z$yxf2I~+FYVu^h6=AbjC98rv3-ms+h}P69n!a|qg`DUARa8M4>^Y~O;OY{ zrG}hTz~nz?r_2O(SM*Y{t>6NuwpUuwvk||CVeDbY{OqD{=rFyi(CXmMxksUT8NRmo zY$HWY^$!fhupD~mH0tN}!VE$Y34gKVCsg_KlzPuZg){}Utz_y*jv0pJq#hNBQ6~*| zA~8#TnUurxO=_N!Gs8nTE%NK+2d_>jYfb*}I5#s>siQpHW&FFCN1E7RB}y`;VF znzxjen-&{C)Jv*YRuPLNw4iYan?Jf-(3~H32k6iGES&mO9Fb@+GiY=TlCy$l}Yog&44Gc0si|6*V*%p zvHV?Di+a~laIzE${DDwqPtLR3BL{fL&h3`oZDhUMpcRC)<#X{_uj$wn9oXj)>RFET zH~o1V;|bRRU-e+7a6x_zn%md}#n3Nv)&Zv|%4H+hg+s4WWoxP{PB!B9#!pUexgA6rA>#7WvO{o2HeUxr6DHif$O7=mix-}^fPuo?OMu2G0VsNL_f7d(B!ESE5U$J5 zmJGRTolCY~v-sW=(Dco~T@P{i7(JeTp>aL5y?pSXxsRfs>_?L9X3$m6*`NUsjz@AV zlA3@-yxRnDN)9E+q!`1Ey3j`Oku^#hi%hYA`(s$7+;;>{-(8=5JnA3cn72)S#|LOC zyz&%h(B0HX--~~c%Sy3WGVh5rR=}%fdzKHz_yv$3YME>hPsp6w^3u*Yf{cd`I=*8G?LD$QNIM@T7OWnn|8YG0{0p#e z<94G;Oe}cRMqeIrv!Yv--nVIV6LJGJE@h-kgo;W(bMp^4fx&b(85AMt(^tMhaL_Yj z8+_mMLrZRRLEHHYc~)cgAxIYFTp{iH)(?Ne#W#~ZcRJ#<~IJE79O2@TXMO( zjy5dyep`|BqK-9riTXO>%2%gsJE zo~X%1NStN++#JGeVEfz*&(eK1Rec?Bz^1(y@k#4|xg~dNhj-_M{>9C-{wtp)Z)v>c z+q<;g93Ho9x^LmOE3_WNsI!D`wlRB%E+dRkR@>xLwbUjX2f$7@LGYH0{DG>StY3gY66bah)2D6p=mPVTU!S>$X zL=diOd@q2OKi#-#g_rpxe7p3BQxg(WCrum)_qm$SiKRuCCAC`gErG0Q&Tf*; z&DombQLui4qs51^F6?s2$#iDFy4W{MlCRqoOq4p7*Q_K>xQzRq)O9C?8+SVmx7v+& zX-q!aEQ&dtIS4MFV?!7WUD^wKX@x?gmdxg%#}`jo=8}eIXL6#9mAWx1Qx~9VsSbxj zs0GTN@1dk^DomLe;w6Ggxi>ocLkjdR2nI~}EnnUNzh#e*J+H1fB!UI)qIGP3ssGLL z#ir91!|T>jy#-QK+t|K5nd)t@)pz@FX{mR;Swob(BCuAE)q^>(6%Sq_Oo81846i1FSh zg_izEU!DteNHS=``BPV5H&h8`^L{uThD2NR#cMYTx&^VenrX%5v+amw4zG{L4(o)JReYG+lPQnr|@INR~;+eSv;!oC*^ zBy}5e=`H>dK?qUOR;ZrJO#v&9hS^i6PlT-OwZ5U?7RB%`B(@Af^8om0@1m-&g4Nyg zHi9qm>F5kmL%&Z=ooSmeN41L<)QgE+#`<7!EYY^YQ~LS@2H*)@%m4CzcAhc`r)ED< zwdIMO;$`|qWjb%jWc-bpnl}pjy0rY*OSwUr!lhq~VNk?mMXtY)dBJ1_108QxMS7ihhnSb5Qt=5qT#M7E+KHe zyl_U4OjV|$B-U>+D>D}%NgwjSQOwR^%(5upJTVzC?EV-bg!8&qa!KupWq52+-_=qz zgm`pGh9pAlhs_Vf{5Dg~Ax#1;oDp?{mWoXE>YQLUJ=_)^LxVaFk${jnf@Q6DBKEtP zDSoL)BPu5tT++qPtRKCvxwlC+E*g<6g{e=p$Qaj|Y>jsMCWGI{#J1de1=VD#4>OH< zCdULS>V|$HeW0dD$xV=V|IlOkY+3!ISX7gMbK)^hF$#r;VJk=lh{vd$`FVqcYzpVAmg0ThF zW;3x>DfFT?1yuL4IWd&vec-aF==UBQZLqz^jNX@`g2$7#TLYX-8KU}i`H$k+tm`d! z2Wo1EmVXMsJyqH`PKli7YC6h@H^F$oZ;(f1NRr{*Zet5{^+XOwzy zwyCktQjF=koFHBa5Sy%*xWrpHr>M0#nCqle>=?d{}`v!4B** zWeN(n=RqGnn~_hB=z|UHjtV<60JdE?*J?atD2?mvz>Hq+M6LNzdqI)?1Wq)2;+2x# z)`1xgd5FUd1K+Ef9n6Z$oPfS~RO`o4u?~n<7Z-K0>^Ok($9pN4w(HQ107HU>1Brx2 zhkA7syf(89ItBX!_?TY*G*PyFJl3HYW4BS))Wr57wqCebgh9uxKHB8!o8{LFV$mDJ zW2Xi1x9Gjm@sQZS(H+=80~EcE`b4<8ypA*mw%QGV+ExFU5BZut&$_yUY-96V>m$4O!G?)*`?-TQBAXpg1{PVNDRa9)c7rG zv#CHyU0f=tS4Kb*L}1!UrAwKer%Qq%)m-8dv36BwY#E)kwkfqCZP4YLh%*p)F*#Vx z%%5(%x>W^w2dxYrZTxhY*CxUN&?5Al*r*4Fde4V_jR3NvxGOHdZp;rao@$m-F$Swc zaCV$fb7hXtQqGi^U20Gs2OFJrO+Z2Mt#p7hJ#(|O4efaY0?!4L5`cJ{LxE86joK_o z)B$HYeK&58v%|p`*~q@ghU66I>h#i8H6Vs>0Swnn=aWK)>jpJ}hFcJGwofsO-ERSR z&8+Q#*T-HA1@5-N)~sPhGp5@~%A9d8zzt$IAi_)P)EbDF--&Z2hFn|}2w$t?2a(5# zi>2KMzwvKi`BKJG${BrEPqkzsLg4tG@RAH;8fjKjB7fgYXY-pQ)}fV!p?u$p0tW z32xXwHWAeKfz*EUuL9e0C_y{kinJ5WSsF^G=KGtC_qd^nnp6T(@ALw7vnc6s#^c#a zQ;ay#ZwtKiNrDZ;!9s?g-%*gn+ufUT{Qj^hM2O9B^4p>ly!`RIzkc`KpTp1t`m<_{ zht#afR+B>ngXqrb?>>a7Vm8U`!w$4Ml$8BXNBmKsDc6?|+lj8B(#IhU&iPugl6V-p{Ecvn7fEO+yNKutW|988YwEzN`wk2k#u7H4+)wy+9f^z`V*IbL`rJWBX_?<06*6{l`+Vv1H+3igUM=PY!QX# z@`ZT!(g6AY*k^A#2Ev_!fkU3AryO$5Yk6|q!B#=njgiOYTTI}9uQ5-i({AUIpTRm# z^2h0%cY`YXPRTbBO2>(PM;oll$%%`Z3IF*y(ACJrllwig?NNJEQSSBzIptBUko7}r z(d~?>c(06^@wO}tCGswiBVx&SU0)%#-qs#F9B8miOJ47?e& zmnN!fui1d}ES|y6iAJ9nx0wS!CYM`10bXDGp7DbbAbP#J;OSSqoOD2h5uxs0%qb~; zE|DUmTEd2O`38!}UdlZSYmGW1w%HXz!=J-HvKX(h+Gtxk+RhFw8BDz17u$JJQ0#B= z&SlULqUmc{9akXre`;8|>AQ)3@^;&Eay)RqyZ%d)-I(2m4I5+!;(M4?eN+5D$~b=4 z?QrlyyINCkf}*1cRAhg3LzBg11?1GeSk0H8t{QQJbsgpE8f#FK`~z0VVr9^+ry8t} z-6quljSI0zXsg!;%#CZZ5TtiKdTkblO@W0bhcrTNw+>gNp{2bz$iCA$41@QGz$C^& z`7@8=R@N8vSnY)*E(JFD%UMBNzLY$7($6AxiR~30l!@_=j3vUSO=g!{+xIEkc9Xcq zyVJ54HEvxl6)Wc>wi_9J@OKIAJ}D;#wjEEKdu?-sVk5;lx~G$~UPsn)Bd>QMN2ZDW z-#%N`?Y2qbsD^?*+p#|KM9Z#XFx)z_nCjH626wl<&b0w zMoszOEN4(Jy{OM6Dh3(jW0wx1i!W^r_#r zQ0&tJowr!6Y6BbWS_{}h;+-}V{dGlVps~{cyil`Cymdm>Md-E$HpC*jx#O4w_Fk2QlI)ydHFP=zpkD@f+yWtI<%_Zg@D4o;aL5KJKlAM9xZ}4N;hu;)_-Sr6cDjYgWu<|H!pyJr zv6CD;nJpW+gvj=iB4%0+nu?lf6LsXwy^xBWJac0NDzMw5tbYO=2LisUt=Iu6`{B5wHX(gF?y&itSmrRW`>D95qwbM4!#63f zSmhMPS4~*u*-vh^lLnQ3eZr(tCIk)vJYRlxBfOk=ltp_bY5g_gKj8DI@WlJF;8xfm zVmL3TStZ*7v+T>}zqoO)7S{!94a6nL21c}YYV22hna`EfXiy3a$P!6irW!X5ByV4v zx^0F9;O=VD;%GYi%W5AZVu>xHDW#6gGzuKbX%j+1O*Jre6od!hIn+rv-|EMO623K6 z7+TpM7qb%=p40>aB^$@{;sVPWXLo*K;=Pgtgs~EFbPw6}>oq%RQEUpn)x5G3XCoHP zN~j893r5_X)L}$ZNo~Zhw#EilvfW?k_Nw*9Gj&`WYZJ>PJlj%UXVx#u*uAI&5bwsd zF``tfR#@AnpAo?XX*DOLPBQym7PzJ`*VA_BywlXItm?wfc!q7ixS~Vc*S|1*2psI$_ZdCW<0)j%dqnhi;Cv2b4IEC4f&&fn)@HmL=4PCGK^`Z>2!`uNwj3U*xre;I7g*h3;izC6%(DGBzT)#nNFi(s8*;ax2Z7 zh!8;cgqd6vrY+CZG4(!KR11lAYROlSi3y)ggi`T+344uh=pT2lr&4##E?d1ofrM4X|&t0=Ry8Q&br#5hvk^AxYGph)sXTL4>@?T=Fgkp zvj8UM5e3N_fXU?0KmAN#+9Pp5 z>?QTK_Hy~bK@MIeXqqKoEq;mQcdR7Zh^0$u0TYW z_kcF01$4nhs@Z3cNDcH@oltQ5CdjI*g7WVrFe%L|0UjVW)hc6%V?%5(aIQu2j2EJ$ z`-0adHZQYY-^?z1ek?TMOBQ5I!srAQot6X8BrPoNzAGnYsgJLC!AW= z){q;9E=KHaI_ZSm?91~p7rqwxJSjhodgAVDTkC$H#EBOjjSw>@rw{g^8uo6{7zCza zGWvs~a@%v!YuSIvmffyBT()_zlCE@(A~K6HJAg-%24?E%Xh?yXaNU#AGgfx39i*le zLeVOAwRmq{;HR5W19VkFTyBEIYS^e(|f z^x|y6I}AMo2=Zi~p=9%p=}4b-b5ZLU?QFlexKMxKt~jG02js7}&z2V+-Ik@f=Rfs` zPEggR4~fS&)2;3AZ|+TTTI36`^R!Yssq_{rv-87Y|M0(Ji{us=z?~r9O^>6P;x?t_ zrfzSEhs-&L4G5~+4=P_z`N(J_n|HFR^Hc|0;V_U&`W}pr=5Pt7**V#QRUKBh-df$^ znTuZjNZkQ;sjl%jARUPkM&TGe;wK;0qhR4<*{o_{KkML;)kPB>jsIj29kti!9NzMa z5)u@yCQXH(S%`r56c90=m8`K*H`5P`eD2~5)CLk>#x-ncjxJEK!A8C=SgX%3lR%|G zd`-?Y$X*)DjV6k&P5MedEtg9;1XiJvq1xi4}@h3Kbx0(1Nj0oB+ z?_i0Wzm4c3grB|Ljy^sUP$4szTgpsg*}zGD#E-kAVr0|>1of!!AgfaE)^jJ8L9+UU z{o0NylT3NQl{ zh1)N#$>#_St}fRzm^EuG=RU`zzshGEBb?(-uExtmBaBK6NUo~|_He;V#6)4BjYqEo zT5ZOA^h&Ll>!!`(i>`Pzz-4(+)Z{R$_8%Z)%@e@=S+%@x($e^lCg!w1J0*j-CK68MhZLF@LU6!hUP_Ug9HPoS9q4*c-Whn zw+yR69&sgnM=ydCLA<9D5DY^JG@mD%0~0NZC~dO)aKGy;@6&3k-;r`f%^oerNyYK1 z$X=6z=S`Yht40wjKZtj@_f!at#7|KiT;+ppHH@+0AYC&iaVj!`c{o;w8@&**{+FWf zUPw&Da{1d<&Wm8>IG+Fea>#@hC{{p0o2Ul8D_<6OMa^{LrXGNk9qRYdO9D>5mw-n3 zDb%E#q;g3WoA;`g>_H&5x{ipifn%jOURwIyJl+hh zV^8nQJb$7joD-HrDZ)kI2vH>Q1no?N7rKlRV~YzshDVWH|EyaPSq0eRy`0ny3P|09 z2LCSSUZ;4c9>r9&Zik^57R33hpC_2L_sPg60T<~z!Pwhk9~MzRR+FN^^0UcO$$@fK zAG95qL|0Hmjdd6gTU_c5TSv$u_+;~Y1@t&FJiI?4LAYG1mB&)$b_45YTe2ZWoXOLa zi@s5KWo~o|7!PTdp7ZGnMH~|hkAU42^Y3T(%Gre~gbUw2?4^vbdML`U@oi6p0aCP( zSD)tl>ZBj%D+1*O>NAmCkdp@Jva>&O!FD}m>yA3>tgDVoIMu5o{2rMGDxRkf?|Xf1 z`+3hA|KH2-ScwyH(@WTp@>454r(71ZVnH5Ix;OF2VJNs12@ft$)dZPsZOlzkib)T zqMMZU9Lk-EGq89|K#WgU(yQLFr+`AksUTDxWGt&vsy4blsD zo}+k@D#jbh+lHwUKDe_6l;CAApKFcx$0MUb2(0%9^w~4wHZEz_{*yGIvJ-v7bwXxD zT1i#**Bw-pzM7JoVUIa?;yUh(um&?^pWtSm%AU z7ZSyGc@Jpzd)ltCzwxIg@OE?DLXmi8q`=fN^asx57xp^#$b7BSqH`o{$`;f6=2xHF zKQidUSTxt4A^6=WDt4Y?m`Y6{waNW!hZ_S+R4*GdXrV`lRPH7e*iAYOQ!S3!mmEQs ztvnMQGKR8ihfTmAocsB%&w1LkLfxpVE9~v2<`q-1Kgm0M-xUIM3ULN9A%i#%ZYn-m zv9WjO_^xb0x;O98^QI5(b%VXN~l+8u16r2AwUk8jLb{leQ3pIQE z-)8d+-&+{quFUB2PQ2*S6wvMYS8}FL%~39YBR|<`-(KEs*V62XHO-^E*K#%^UeNYK zecOtV>RQq--d7WGpAoavf0wL4CXQ$C5!FTZ`seS>Hsu8Zh1#f_BK;ibUDY{h#7P-F zFV!X?P&B-q~kPI3M2L^~{#k zu4D9iyzIh-fZa2H$mdF{f9FVl-)#}PTXW-sAQ$Gb%ATA7j&7cT9V98HRcoxymrq^Zhfk9P`VhJ>S zfN8HGKG^ZdxSZD&aQ5M($q&^Z@{Oz5RPxx22Uo&aU!Hpz1F(2u9#V?cP;+=apcotL zg|^1W@2}|hQ2*W_edE$MNZ)WoILWX`_d*IdGD02~^OVCX&2L~T5#F{%+pPVF9yS&kEQPX zJV#lbll~HG-5)2tYpw60@abSn^U6f|vQ7YEtAtCA9w8G?uY0567hfdec!h9U>id^ApTQ}+H#+{}7$}MY z)a%~p;o%?kH-p{h;pv9<8<@K$C``Em-?MlKI*wLu=B3gK^hDPbS>YbS7LyY^F_b>y?B@R!-Y+ zN9zH<)M2|#Kp5)KqAggA$OIVd7k6YwbN%M?L|xIywYF0&0D?;71Qx2Sp{qyJMny5e>~d6uHHupQr?z@Se&nZn%9clTi?D&H(xwLjwm{D4yWIV;&*_ z#7fqbBASw~c;|0M0w$bOx`{my5TTB72E|^Bj@nM2>O>zm=(Dg$Z#N=3PFH}ftVY0; z2#Cm+zLNk*Xi+cUXzm^a88#8$UM3uhYyuw!I@G5bo!%}n>vm{sgqAB&cTm}Tk_p+~ zL5F8Fri7E#g5&n+NgwCav63=r=J(R(SvBM4ofkSVB!n|f<`)$Ig&cIrbKZUiWB`rd zQ)EaHt}B)@U;bG~9%e~yQ80!Ufz&XyoT9OyrmUDG?6&lNqkfLm5!X*u_or&gR*h^3 z{o!3TG zl<;a^!#qxPm8|bHuBaMber?xlC`IY7WJ5sNFDn8Ss^4Zdu>cNP3ALgdOdz>xPnnsOQq(@j^SoB4m+pSuB zQLeLJu&92q0Rzg56g@LuzX+Z^$~x~lzo=!osWcpwHX=t|=|zI&VK(Dglm86+yo&wr zhi$xxJx~!asMO#~b~ea;i>x+j@YP+76oTFtJGt_R;F#Hxh;M8#(BY3SYU`}iE!FL%%zb z9l~dJXPnwnXH6gp)Y(7f^%HK0<~vmI%zp_?YpToor5j7rHnJ;`qR2_(OQxy%pZ^pK zv`J0vckIlG3;5aRx#px+mqF3uHa8e|@DaS8B5s10$|j(zYKc5<_3YF3@oQR2I(%~H zR$es0c(t|L#?W-<9L4|2wLLU3Y!ixy&Z}}pe6fvUIn$a8JR=flF6fer00l+Yyl$|4 zG{vH{jA(mbh`r*tG9rM9bB+k11OZ6?GGrF@Z3xI08dA@Ii;mMFCs4o{)7k$3&SK*_ z0oYfa(oJWmpx4#sjNsKux|1I_Yc0(g_@a4tV$v6%K1q%Ml0=KGTY~24WwmUBz-q5(4=aT<7I9r?_hM4U}v*UQ&=DJe{;r{7yITD&N?~5spxcEngeGNy(1X)EdIcWN2U4XwzVxv0PwH zq(nI_(TKe@7z}uE@M8kIE-)KWSNW=yn!h~y)JT>mb3$Zq69+xafvx9)y{WV6GrX|? z?SZ&;3!^dXQ5% z?&iXt5V5st$ae9>4sPKUawrfPU zIyU1$5_E|Z|3%A(P97K>&uv)Mb-NLyhbKL-p{*y0$35zg)&EbW=}UW4*{0@S z+)&cM->#X~r_>=Z>;^ut3q1x%ZlH(6nBMM4s?_67uUm)K4b4-gM?bbxM#d&fZGSMM zno?sP)Z?2^g>~^t*;32P*=w^CVUxia9b&sCpMZSWSfdc~bR@Gpj3? zr5APejrRBdeaD+OU~;%kjLIDvK~cl%SJS+ly`+fIp6^j)`oh@>j_&0G_A(QOrt=J$ z5WBK>k_^%&gRmVP{vP|_t}(F5IyD&q`(G2L`nK^8o1#LSmpnEQXX{e)mWSEn^k=G7 zT|<`gUQ}dDt{)EZOMsT&B8ah}wNn`5bmrDYGyEME%`F7(ZSS}L`M0}0-mYa#Nr`g5 zkzpA6$N|)XfH$&`LX&wpC`m*&kzZ7(OEpQo`N(55Q%EYKtOM7sw*>Itaj(}(6`+Hh=1LU3Doc5(QqbsMgf*`1?!t9L(&Hd7du8wsENzS!Vhz5_?g&N==wwxM9o zym~Zq+15mxEo>*2-^de#)K(ouYn|m9H{t}coybPt#muQ)I->u&ZyJ9)-C9Q}0nPo9 z-(D~Ju}}TflFk8{Ey+c{LrSco`@4MHq9^ zqbBp}G_k3LTR^ge=QrS*qwZ+w#;628+oHH=%&F4+%*10FGOM&8Gx3XBr2)F`p1$#$i_|D) zvH0wmtGN;;GMXe!(p=JloaQr=ba%NRn@ccy5wKGDl0{qCN>KxwM{Z)ex;JrkVDIKv zMT)yeebisQ1Hha#v9Q@yv>Nq(Ny+TqX!wVt{=FRpG3X2bApJ0SM3f-@#h2!DC_pNK z{OAimF(KfpTz~i@<%*@&d!x_4{G3Gk<2#DCCmatnDUegPx66tO6TpIzveoa`!qXyIx61R@T8hh( zykTD>z*2KjIOBVH&0C;q2I<7uGF&d&GrxDw_|f%aj>T=V^dWNUV)Sz?mj2mIs|Et? zaj{~(rv*f2B;AIOVRWjW6+F{vb~B+9<28FJa%q+A%H@E0`)lFEs1oU2%NBH5b2A+1 zD(B9rpZxbZby7R!-UyvW+0TCMy8-RP3PS&dlb8M+iocJ9Cm4r^B14y}vaZQK*=)(Z zgl~wOwkF#_n*$GdHn(RrsP6mR*HfF}tVbHaa^~4vx#q)mkBi7C?XYz+a;Pc4WrAbZExEeoy3n}>eF#75w45a_TPxqxCWKa^GFHb3fZb&lQ=fB5e8EYhGHH|7+A%Y?B=7^h#&z+q!-{bAe)eY_z;7cbOAV4wn(-V`WB?A zBp9WvE-z=+ex8molBi0H#)Fq{m~hXsO~Jp~4pr*T*p%R?;&@**H!y#>r<6c^#rqK~)7eVJUoOOOA z9)8R-)*+GWsD!iG&mUY+~d$lZrNa&`EPm8a_t1{Bdrf%D@}Sb#4Lz{XCaMu6P6>V|uqNWIP{V1Z;d(Mopf?ij0R zUpUs(*aSD;hAlC+8>EfxvmY(V5LYYOMt5EBH+$(0c`R`DG<5=kKtc#0$iUaL02Uo5e|^lI6pi(qT9>EJKSEH1z3ngOSl_$i5bk7 z)@m4cI{|lMRtN=<8?2QqQdy`8#932+o`?(^onhlLY;wm!)U8&~J`0VR#s)z~X7WK} ze;j-RFFQDknQ&!LW8_nH`JJeRQ!OUtOfg2`nuYd|{5vTJ;?9oG=lb)x|9PxGj~k4# zHBc7T*S-meECMN+3RUM5^{KCk>eScqNdkyPrJqO-o1?oTKs{L(jYc-(T!|ZXna>Uo z2X2d%9-QX$<%|4W4T_r3(ykn*6qkzgDP@#sAU=FaqbhyyR{zDr!gK~XRjkUPKZ9jC ze$RIAoOO`XV>eH=f%U~6^Jtv6(>QlD&Kqfr+i8p)jj>O|vnC^2A)8Ch$-U9B)?(Zh zaz}FBZKowLDnn8b zJ%VrW{~$YWiKG=FIL?X>i3Uj`9;TpvT&1X3dB&y^7Au84)zw9qgJaR1uV#D`nr3kU zRAWhj54AfoyX$h@fB->CqFLanW*hWtbmWrBlMS8lL0%Zw1 zOmWG)Pj^lN`b8f2-l|NG9*9R%}o8HmbO*;VkP>hnz%Z{GaJtJe8eY09RcpU_E#;Bqi4&3R=h%0|SUUE1;eTO9A4>{&~peAL0sU+Dc zMe*dgaDClXfsa_^HTyfOMFP}OwQ{DFo)Ns*FF1fEX{HHugm4rw>pZ)YYEQxd>F|7V z6AjE3k?UG)1BZ7L!|WiNyPvta90wkH>lcNM_dz+=x&KOJ zGyXtu@s$6*t@?50^88b6hIuhBh|}g57E7-yMa*Tr3I3a-&!Xg#ffV=7=O-j zHU89vilPRO~&&qLWw+?8A zN@NkzLO>L^Jr6|1#Kg8X>b#BSd4lHocADchn&Sk`v7~tuFDHT+)&bpUTKQ)F)%oJ< zPHI7W(`R>y_w{72mB%HX{irx@%j|aa$4_yA)yC=eemrV=zcHrX@}0)Z>|TY8z3r5| zsz+__Y9^B@B>9I&ofF>jZnjk@`NPY_R#2SCoJ%Bye0h3o)62t9b608>D$ zzbEe2QYNdg>ofkpqCNU{&=;nSaV}8<KLXR{2P!;ja%SNob~2X5B~VkyGSbphHIOYaJJ-L1U(qF;IQq8geS~^8 zR?k<((t1kVB=2#WGRz8-S|hWhpd0d4Q#^wB?mv3doZ<5y6S^XN;-~P3M}$2sl}n%F zgBx^wf-=ott@w%x?fxaBN$L%L5b*2Km%r^6`icdW)Vkqi-+Bi{7B*qvvF+ojF@PYHuj4_6-0#Rp zBG&w>yi%7ed*Qb+BfL)Nv)`SpYc}{d9a=jxe=<<n)f8;p!8z`eK8eOUp-J)x7m_TJTs7&!AKgg4Y!796}X5U{xax$ z750*6lwQ?CI--v6>P~%Lty8{e>T|;F>`q=|^oFknp6+?iXuIyB%q#1+dT)C^YyqX_ zgLX5reH&Z;73}$aZ!bGpl=A5XMrPJXYT9GICPu?Ckvp`s_B6B43cz z6e7e5g3b$w`c0BI;#MSle{_ajJ3G&3cWz5vT`bw_dO_#&RX$tg(?pv%S(ZPqs#eT}gC)x>H zc0+1MxVbvIOA|tmMme9t@yu>+;dCa#cr`Nv^@oW(<+GAsOB^E21vxz|MYQHsONAy! z#Cr5#HAt+0@BsrNlOdoBm6sUs&nQ{c>smk_`6}6)!N%utWj-VH|JF~ zE%I3>wMf_=wrQcz68K%0>Q%1#GEBAXaG(V0+c!)s(oerZLr&1h4YpUfrP74+UvkKI z81|;2zkLg$+_&K%`i_^+!a@9hvVQ;VdH8Q**L?n#ca#RsD;7-H2lJYIm1o5zT}To# z0cYRwFy*LUS}1ceqWj9(^J#fG0|-`_(@%)JLv6lh&rq8s?G;)z$Sm;uoR+m*Pu3aq zx~$Zyu6fMJQb+yuev%q2o%I3ddw@)0=%8z4_tL2+eKo9x1U6B#_vW_C4Bo{*ry~Ak z>)z<|LxPGs{=LwSZf8%SZ;7~?CN7E6bMsv>ofpAX@<4wydA@n_?T>%?A%$I{ZJ`dd z?}HMDdjrmWN54n%>(VXv+(v{+53m*o+PGSHf~22veD_KBVxbo86Sf&5i;Bx)`D{^L zJ)bz}x=Z1FPp!~zKoa`cqS|L2QizbzD=9y;gR``zg{;qykaBL z0PyE;zjLO{`rA{A<@du;SQw!3`;(trwUt95F{N0u1cOH`a7q% z6{1}nsqbUtLw3P!P|M>VirLXCx+|r>_N`A>o-xDXh#ypfa(ZOrPMzxZ^-bf>LQk%8 zFewmj7~Wr}AFm+giK;w() zREY@J6rB++Z?emq7rn-l5bdQ-AfRxG|WL;M^ zLQSTEouk!{GM#@GhkX02te;`i6s6kQd;ll#K zT4*uAgLy>;ARO-Nkm=qSX&f(CV2KAPeND9sMqhwYTs;JI5(QO&eo*6K8K;?$^z6+xH|`i(#{p0Da@}i+xWVpSLC*`cvNwQJ9-J zzpX%j>YE)ATg`(D7gvQnz7QbkJN>O|8RMx}t)h6QF6;ArGJ%LET?4e0f}whnbVX1R1D!51w$vnZ4VG|v^fmLjt#6jUq zHA&K^j@p@jqPVij;BS;Nh*w;c*w+IStUy0swC)ngeY-%n@$RiXO$L_ZW{&i~{Zu;D zKC*u`Mb`47KALr+txuRyfuX)7nGe^3QK!M#R~B< zk!?SFw_MKa(ZNAYgGPZmiZvTU4isxX$PfPThcAYH-eRWK_4{(Jt`oVE^)3(a>)_AD zVpdGwYR2=nsFx+%&;5@lZ&C4nb+JzszqN;v$y6=;3NF>busWyusa;S=Piyb*^J#T{ zaFy5m{OZ2Xs@XhW-&3qFPjdHf3I$)e-I(oj<#$k~jC5;+~E0Q_QmG|KCpx zrJQH;s#;{fR_CypofP$0-GYsbQ_mMgZtY{Wr7Hw4x+x>>?Vb%|n5hL(*#JLN>dkmn z13AI1ZEJ;EkuC2z9Do&@2hU*XisM%fg0m^P67-N-jv8d;a}s-?_`KJ0XtBGfIQ)ad zsGewcRxc;THO}Lj{gU?~!P6=)@`Nd;ZI^#@XxPu7^Y>%DZT>zC!vC6{93Dov=h1fE zO$mGojy{$NKBkzA`kq8 zfX>U6F-(p6|9n|ob;&|53Kya!+vl@WX(#JM4_*r-FgeV5y8URQpG_F{&SYO4jk}5^ zj*^KJB=$+RChY^dsM1~(WEJutvslobvy@dZHA8D|9=lQ0eaHZ{7WswRi6zv0JY&ki z&?BgagQ$hB)jkD!DQ%`ev)>el!sa~nX6Ufj_or;wI}7232wMdjHi~Kb_fR7BP49qXb zB-Z3drwg);lK^)25_PoeCe54dMw<@K(@L;{_KQ+UMs5EpgBv0Cn8Cyaby{_iuR zQE5m|Yjx%C)`HqMRTY5;N;zrIy|KABlsm|pREXFaWNZ0fchOr!o2zSv34wb0f@t5Y zt5ht9)Yb(9As^x6t!Ikin~wF;&&=Fuiyn?*)8k}<5*5o{A~-1;e+|Z)=z4=rFLwt1Lgci@DyjjL zHpnNF5W`{M+NmVw!NZ>ESG*kAiC~xI_)%SDFUmEH9dG0~DVlzFj?~Jl)^T^#$7fNG zbY|Ht+ikz>i)cP{S#MJ8ti^?strR2QcT?jR{@H>U*`1|I>RqUceZ$r!T{{wL^1aUT zZ`T3$n`;vr%~jWP+ev*S&Au^vF+haw`BPP&93Nvn(>gw;bTmM=blU)#((x$S;_*=V z4OZjfwLuO@KwgZBA)g$YcIfMSqv6B!#X%mCINFaRFOOrkZ1`R94Aph*hkSEhWNK4q z?@7suXx0n3xqo1EvPG3o`p5|AvYCZyBfXMV1pnd;d->Y>i&}20*+h{d4Fg=8_xNV0 zow>QH{Yf!bS7;y%(^HnN-&_VS^Hq(Tl!(f9oj5hS!|Z@{H}&7usB3w%tn*eJ7qC|dq`Qz$}z)tJ;_KDvVcqrnnFZ6AOdVU@)kIC3F}US!o@4TZYpl*3NeO^ zv|i41uQBa-jC|$3I&G$1VTfoN7}dR6_v0$vr;jMYF}FrO9sYO|S=*=%JyqjXEMBeU z;ufDzWL|FTp@9_OpQmzs z;09F2c-2vyN%!Ss&o||p3(Sd=_Q1&=`D*67g;(OxdY7|zRyF&X4MmNwe>HPH?#A5I zkGJX<=d(5{3VYMI*mzpig+}g??-=y`m4M!mTDO^rSZ78xZo3i%y%t4AdZ7P}y2fzT z{^H@mc{x+*MZF8Ck$f`s2LhO=qAD#-WM9V?a!yeyz*k}|-^S0fNo1MZL(lv%=Xa-T z{g2x3O`5?i$Dyr;Sq8=9ak9wL&UZnwZAci1 z(f(6)(Ya6k8;pljYTZOGx9=%) zw#TaULrFUK3xgu8bDIl6lwA(+>D;|29(G^`qwPe+oK2b^-F{w04;-xfLR?o_HG3g%n z5_14YeE9ye(L{-eC=5IXW=!k5wYvi<^Q|_i#m*GW8x8Z)(<_FusO5<>3bW_>{2D2p zEdwr#pT#Zk(-VfAZ4HjmoLY)TEeQdhf^nZ!zAvE-48AG040;G$qcOhI4WttEWFu*$ z)pIP&nR|tku^ra$U<*9*B+Jx4g#vg$$ad^lnw_(QdU4 zKS8ZAr)bW=&AOJ16c|$p){=ZtO9^k`0VZWcw*=HEjMcd%7j3V>s0)>Z;;6wjwvbKY zO~T2ZjiOrCsy+b7PdB2CtYK=ya0q?FCp6+;h7|Vi90gjJSe{pe#jErs+_p^ zivx=L5-$A58H9zA60pr((`FdZ{kYp35i?tMedt3}{Cvxy2RDTL8|8hUU2T zEpg!7;-=nuL7j?^XOT&eU628!nnx*ETaJavT)UBu)7k@>Z#Q4j2tl78K1|xL{Neur z;uk+zK1pZi#J9f@dc5!VWVA7rSGI*~Onm4RgtW2fMxu;KTSvTo5>kYVN)@_y~Z-G@qgg$M1hs z)fK7Sc1IbrEm0fv^X7LXIc)0N@NidPvExGlEJhq4VTSVX_$WXeJ3f{`V#mZLqD_dD zFGPwg|3?8mb`0NO+sM!P5$ub*LpHsztRkiJOYn-F@S6hQrY45QbDj(l?>7xkuR2b= zha$oRF7x>g_CxhvY>-9iPoVMN=kuDqF8yQ(25+iqv4GkGkUn1okUj(x55tBz-FSk; z@=ATa*rX`40XqVfaH_j<#uB_*GUdd+a|u9Y$8i zk;U<6#^M;b*9LCFUkA3K!No8Y@-5IN(AIrb5k&X^fA}pupV+iXl<1%<%vwKP`ko;S zK+$A9E=svq|AtcU^7g%z46k39RoDc0r|@bxDmWMY;%(5V{D`E|BEd?OYeIAK5Q1 zE^v3~Q|PaK*oCU?w$HkUuq5jxNT1R1j=H!VpMakAe@%u$jl0bS1T@G1 z6XS#*XJV5iog8_{MfMr=N?7Z6nrU}}MTzLtuK-Awc&~|#J52HE(Y$L@LOrW>6F*76 zCnnuSErKQOMeW&rkOiBv{b4WkIp6Bqei}NkCzR*5Q7)@c84*}!;B#jFFZM<^9fGl1 z%E?@%Psl1nBOL_(YRxY2>4DlyE&;M>E=Mp=9~}nQ%X( z{o@EN8{a!`;*sGwAj}3KBDKyPkGW*hzirGllt`&#jqYc|lkB~j074Ea_xHEX!uz|< z!v4RI6a|=3B*j^LIkd4Y(@nBDJ8&g2 zMV%iy1en9aDkn-2@h6H>s8NZgG)-Dm6_0%Dig+QKv~3alzqDkb1sW(H@g+Q&>45|& z_}PVG5(K1#kYpU6I%Po7&xC14NXpo~5mW=hCR(d`T%FndH#;w;)dwFca}dcCzMZI- zQ5q_8BscZ%{nT+c8Hmk?u>j70xOyhSl9gmD5=m7j-&9L*7<})-Skl|++2iae8~LzZ z5h$C3iq~KV)%qmNVcZ~mVBaX4^qA<{yfn1h6DU;^MOa`&^31m+8<`~BgKcNlF!BMI7) zYDhmjymedlxiQUc@#3MDGsLw}Wb8++fT9Q&->5|~oBUPX9Vl9}y=}pl3INnvP$$kl zGVJ}v8bf`Inm*!dKs*j3IUN1QE|0ul>1_f-9K#|w{6=dBe}Fg7e~+G#R&C}-hPGV5 zDmtu3b?ChmZib7QHtR4q^&WNw2NsuUV7a_n{gzxU+zuM%=P|4qmc;CX$kFgw-0XUj($=FK;gjqCg}IwwFJr5 zrYsYJEZbE8*Pi&Qq%9h<=Ii%X3rA<*7@qclj={+Wd%HR2ciuJKY+0bWCJQ|hW?)OA ze*DZohGz{IS1xCA_brh>nQ~sKrqfogXrnq7Tw+c7VsP3`?)ZXz73sA&@M)F*z0vTC zFB)C=_|fw7AXKmxGSA49u|{yq0fq3F>Y=jVsN)APqI-1_-$v6;3%T6nMhoU{Fv zjP}~)!~-+()Ltr0GLOrF(FtqawFwDiwr$0J$nfEn`^#w6oqNMT=R9$XrgdXVb#>?@ z;od8z+cf0tM{9XL%V|l$j)emu&7DMiR=b}1hecCnDJ=S4wI>p#V%n&PO|r?rR*ReB zNMqN|*I`-3MWAA{1Psn!Eubf9=|KD z@&v`V`Ml&mYRX~?@getQJTB_GT0Ao>KmB+;Nat=ryWawJZyh16Nf;C?YQAPQJO?@= z3(3GLF~7WAKyzA{A#Tcc5t3cr@*ZRB-I zI^Q7fJc5NAi+K(YmZ@`{wmWBIEXcCX5O;i8Rg&6xwZMg7y0H|infjsjMfCyDl9m9` z+a(|wFT}8)v+A2^K6@X2CaN!^&pPnLde^XBX7Yg3C5P*6S*xx8zL>nJNZCDd^Q^K8 z>dSm_iA!8l43wys%}ok}`>4g-Og9$`oyTr<2ASk)j?!)nOS-%?+QXb9%ZPOk^p5-Y_tfG3noPGc z%kCt^ma)51`H=NL2){V{Se^f>j?^1h=J^cC8GncAirPfv5yfC{CL2An5wv~>^W6c^ z8L~<4+E5Q+s8A*FvU+Q5%CwkG{7Upd^{6ZWU6G>3wquqSSz6UucUCR;>uMjAdm>M= zExNxbUe4yLWj5KZEksaXFXB-&Jsel_4T0~v;bqj6F~?u6-4w|gI4IP>9h=dv* z;+TpAIS1Y+&V1iHd@>~&7W|yi%jzmCu@KcdG!$nAM4Wt4Vt>NZ5YmU*Gp0(D@0mKm z=i1E??sp$ljp%*3RLc{}xS83Sw5nFicN@($4TTRyXF4UV-;kbNXh3QZ0{RYMVXVY6 zfumhNJEVLFc2mDo-?%{E(93IrjV{$n$u1Vvm2>LT=-CjQYaT0>J<1xj%ojxsjbk}; zo6FF=D(l+H7CgkN2Gwi;5Si9xYTK$ZHMK4^=4R{;9G1G7d^+^i0&^lPp`ko6>C6-@ zY?%Pm5bMY7nZOXiuf|6eb>&a}Y>Hvxae63_8Cc8{`+z@q+rUSf;|#sP2kA3WDd&h> zy5;T}xDBZNCesM*V!Or9igA_hX-DjJIbQ~d+OQ7q6boUgWg0UuuM1vLY1QCY=m!M} z=%!GpX(Gy<8KGaBn&N&?LEoO;%{(z&U8x>KhA)0KSgUWzQ*o4)ffk5GT)WE3Zil~g z0v#0wLFS@!J;~k=> za$@@94$ghL_RJJxqQ~keoy3%T#6a%JZDTuwmlrOi{_2XA&0o*3HoH7L!mLZ>O=Lw5 z%OQJF#3EI#BcvtvoO zU2979*4TY=b{jN?LQ(ixPLd}}%7F5^^lJ9N&fUORzX#%z#-YEV|W~HA*(jN@mZ2Io1P?G2#0^o-S16?OzK?1H*>SKZ!+L^={$*DF zeN~tf&{giOGe4A4a{eoFSCA9$xjfRsAqI!P-IAId@tw#PLpUI@bdM%Mvif4|3Y3tm z?Fv%!(C6DupX_6yn%AJca#l7R%``u+RvKtoZ3;<=B#!JnAHT=dYQ9<&R<2{ciO?kp z_*Zp^=@l|c-Tf))B*@RaE}5T_!u<5wkj#!0alPT}c;py`HpFk}@d~$w`J$Sv#`fk| z&hS!2MeD3kR}D2f)#~z{(YwuR7zQhmH>8L$A!wayVL%-=G^&GS$1L~YeNpmJ+=Q36 zgq3i7FKb{InphLFh)ly!6tSpsmeaIUfNM?KP96Y&CAm-X^Jq`E?*Y)6c5#9*tKn$U zrIMlhLo|nYp^E14`(josNH+^bp<2#UC8W?ic;ippqg-k|An~Z$g!BEK=Y}@&Z250I zacSaoIBrZal?|iDsjQ-1{Gn2zzy!`q)lkFrA}$9UQ8=B*ot5e_Wgd^xG1NbL}a%w5WJCm ziseC9ow~tMSu@wCO|qdo8Jh5#R?s429OpcEvBXyANS1JZ$F3fUjgaU+WQT zt5)-H9#<}R(wdnExlP2q`K`QPx971v<98NpVSYFI|8FQe+)xm3>0fwFaiO5(g=P1N z)z7oPb#9><%co?`V{Mk<#(iE+Az{!{s8G|4QUvOXjg>wWFlm~STB-s?RWK{21puPx z2F*I8SwTj7NlHru^}-`;U-gB&0>=d@Q}o+BAMTpwT^1@OA8lL6!-jwXhb`Pxo33qv z2?>N5L_9bYl1ck+_`mI4dvnw{w*P-Vg-I2aWH$+9fZ3%PW-27WFhdBJ08bPJfJJ}X!DN;O=jq#)Pq3os9ya<{%1R+voJI?hSu{FX*arSOqe9wm6 z2qlV6M>G*RqFb)3#7=3u-qIefii3?r!k4_OB z+g~j6yRGtYxfgBd)v)d9ZRu}bb$hx)ebN+xn~Dzls+Ju{*IVN%mEy$dyNfrFWt9Sg zx>X3ca3)dIo>LC*p0&ZZ-?8UZ#nsm3g0wccxBIYo+ZMvy^1HoH@yhRi``Mz>TFz5% z+;w7Jl<8`=sSJ^`5n#DRu3!A@oPLQs^334e7*PP#qrUrUyoiN zqYl#$*{Qzb>@QxOJbm+a?`0#i{erU|KRs#W(rGty>tZtYo3G%<)$}gY%rL-mooYzgnE0Kr8u#Eb#>SQtltk#r7F|K?<9d4_$`Mwq3Jrn?{jR*na*f!A=dd z`61=rEUkadwuHjR-7jW9-?*FJo>A+LyX%WJ>Uv4De7){Y&Nr;@3;3WwB-TtZyAm4M z>>D5DLz#`^U;}3wXeDawQ;bbk&3M!z@wu!=%eu|bRNG*Bzr}f`VX|Xg-D^#GL(@(x zZ|ou44nS>-I7b_hImQ^S)=r>KcM)MF?Fx<1h(OpkHjpxj1G-S-ATO%+b!ob#lL&SQUoE+GL|zIUk$38&2egGYVcmRZL;m{ zPU8JIP6Ct^?M1-Ex9mA)TiubQ^{lYHdsh6`MpsbJR;__nabv@}$G%|w?X#6OOuk#+ z&0sn2g&|G3jq_-WGulIAPE0TO(CET;FendTE}9201XyR?){mtF!bSZk<~-VnW~MzY z7P|apDti`HdN?${Vf^itQgMHvVm2d-X7w+q_V#Hs+W`odoKae4+kb;h%= z_8HNPY!R!GWbN;z?Ok<1n#~r=-#k?n=*d zfYT>r|HsV8*s-$15&3ePhMs#fx}L7hh}Y>OL9wy1RDn;4y;=OQwAwX`l=3%1_t~t8 z1|NpI(@TJU{r&H&c_=iPgr@RnWnTc7u{vd`S*Z8r`xYuGRl%>p@^{qjrN2Zb&T6g1sI)W+biyw<-RqrCNZltCW#X_Fj;m5x9H|ARy zQoH;Pzt{=Aj}>#{6C>>{AywZ{3k4P{brBu}Z@I_J#!>zH8c?s?IcOE<`yQ;G{;~L8 z?&Q)}sy9iZ#lZ(AaHFHzyM-^cT_H znTpwlsR}#nw58sP^|&?pKGf8ckC$jBYtx436{e!ZTA3gY-RG?vQ@_=N`u8^_{{3>& zH*Wa%%iI0KO=qyxHQ7 z&EKcRZ4F+vy{DUBxrbNo;IpsW!7I;*v(w+D!QVG~__yC&UBA4=n;XARi`yE!au08O z$vaUinwZEY%pSHK+&wY`}c@E+0cA;X+O)(`|z=EUG#+*;#MSbFZ~%1 zWZm2S6XG3y)mVmjwEtHu!#_qWLv!=6eP*N{{l8Ia_X%*=j0jTSKyA$69#wNWqOx&5J6;Zas3uug zz*uLlI!Bl3D~&izVG|(Tw7`AWpEazSHic%h3b_VVH^0y>uRCtsFb9&KT0>KBU$gW^ zg(t>EL>{SYRd}H@on=P>1=u2KB3$ewQUHGgPNCqXf(aWZb+U;Z<|;%pOjM~Rz*yC^ zjnJ#36T1Ctg1OmcEoiuPqp(!n1CV8=Vu6;Q+U^pqOPMj^`(NjcYr+cajMpJBkR#$e z`ZR2_9PS;v(=ELVURZA*HWpisKDXm{z>7ke$vU}}<7nx9X|J{|-MV$FwhfVJ@==Fs z_?tG>sxVI6g>WGPdk3a`ioBgJa6a?$nX$_(b8oh6b09otv@h|K+2*trZ{&x007406 z!2fMqjV@?l)y_TEDpoYFSh+rePxaaK+3g}Z`!5UXv-o+#P1%0~38*5yUqENK3 zz=CXr<3^I2p*at>#NsDN-BNHD6uR`=x{*aNEiO@2WV@D&E?3hDRT`?*J!hmAZZ|K#hd-oPRG@ajcL&6$|8rE5|q0V_|Iy>X^{oH zS=I9F7culqj=R+uwa0jwDnma3a)O+BEMBk(c9EwDZf1y+c&{iN=F*)8Yv$>G!2Gx& zti0lLfRuz5GfnFxc7ElNu)f@pse{}6>av%r-c^+o@C$Sgqt#?HkfWY+_1&RX*3L>+ zCPl)g)7}-KfK36GVU;2p(ycqTB>rrDKxG$M5}C1H6h|bJ(;3EkKc>?aUU?;s+H#zG z(qIiVr^l?#V6F7wgR(<9Lblqo4`5cD;4PQv!KJqp4Dn79rXME>NGvF8#@ex}ltV~D z+K@X|WF&OLGS)Ap=(uHK@gkKWi1!l`oB**$Qim2KQ=T{?kG0CpkHs`AFbUq1rkTZM zqVOb!egLOn!yp}^zPxVIrU$}k20I_csbe3#LQp6o(v>Z&t+#6Qw4(7tnO#cB0p?Sf z7Ma~@1TR1jVHeaVP*P+=W3=e3-leKCB|>#AjSrkIhi-JRgTa)^Oq63BD(Z$}TPlaZ zs{65PxpuDQT6~I{^j79uBEB_P#Km%6RA%j%s-}zV-e4!jVz_i%KAdT0ICSv;2r7q7 zP|n)Eub=s)!|JjW4XcKi4zs)3Citu}lGfQ3LnppFDNeERSfjp@R);jl0DZV7ziBR( zjTCR9FixQCvzM%Ovm%!BoTdSGZ2XP^w#t?h zw>jdp$zGi45Ri2X>FUgVF0k2NV=&1`#{vP>kA%9)<|WBnokP zRh$~MEdeq-bo2?-bXdq}R1?Z*mS{%5^(2NC;lOad7`d^^T6?%5N8!pj)3J+9-*kY# z9v|Qmj@-4@4fViO)4FpvDrS3zv%0^;<+Npg<;MjOSkMq#vEKQNoCM zH*g)dG(WXv&ng*yOZ(^^RHr9Uc}h{}pjY-Orhg&vyYHNe=KD8LG@E0lR-JECO_Ej} zllTOq;$Aa<(Oe4-YiEaCQJbH%S5#DG6}*72A3xTuA@Qw#&3PS^!1zF7p(1&#edsnx zjie&`WVZ1g)D-7B|JdV%r9th-%AIx8#R|S%PnSl1{PvHQHL0r*<#}Y!I(K`D3qxtz zJopm!3182({CB#!6GMpo<>0-MxUnUB7V)q*_jh(YBxVqx_Q0Z zlB%l+<)X{kt7ysQ=W*u3<}_Tn`bRzzp}#73|&p(3WPp#$oHGR zK;;&^BwFE?H*B+s7QTrnw=-{v20JuahR!VLo9A~=~nIb45>Fk?xkKz+;jdr@-Y4c)|EONy-2hYJjVi*Qh>`U4g)XpM(QpK0 zxZ%S4Pc)l)Nlwskmc+=C|5FH8rT_qwZ+E`kxw~%g9saX*y8Ga4b7S|x<~MjYK`1GV z(FgSLhZ6A&);Mi#3n9*6Er2Ir7WfGwSqwhxPDQ5hl)^inh)_vr*l2BQ z?MZy)9m^0Jy*5~TBmHqE;fwP0Qj9}*y&o#REJjACD&oT-r0HcUgQ^E>zhDlOk@Be* zfavs0B*PSHfZ8${dQwH#`A80BOD`yYz!$~r2gfJD>U0(+PHsh@A+ex%3Z|X^(FSsNsLYX%*nDh#!Eec z%=@dr$Iz64k{(FGK zY>{L+1?V$Pe4|hmh}U@gWA7lI&15(s5-Orrza%oUUICE3j^iockrd~+D1HLeo2QuS zqDKTSqc9c$8JN)lr#FMOHCQuaCc@Nv{pvZMh343|p$PP+Sp}{9^?XkQ){X6t(f;5# z%L5fd_7~GiiZ_(7^dyW6mU;1%RY!=Ak>Pmhe6-_XfoO!vD%N0=5hlDqYEM0?G|k>$ zo%`8!=0$np9lw3PjeV+Q1f`#Gp-YtnI8g8sM3GC0!yjflQ0q$pVrR)u=x51Hj*_9m z(3MG?kzyBe=%gZL>$QjmYj2?${#fa`^qMbo3fS_3ZBL=-Q)sFT!_1pP@95O6HxeZ4 z!P>v`1i!+cz^`i}LN7et1J-E5M(_#lT)x1Q4pO-BxdP6AP80b#nt`AXebA#kU1N}n zxvWwMAwNvq;~C9zUnGEw5B06p2|nhPj^R05oJE10YZ&Vr=Sgl;Edf-Z)7B2aw*tyX3`@ z#IWFijp*r*({T~QWJ@)PTR6I@Hxff+D&A|IpW5=1y*7w5nu=4X?L;fnOCnR=ZUNjjAyrGd8V9)vEFi+FL+7_$)n2DvcJb_%m;RdvlvYi3`j3fFP zNA&Z*yZ8(Lh39+pd=H;f=uV%A|4)wJhiUro5yx7|kcnNQISE=!VDkTBzru(5V*vdV zDg(^J&T%xXXPAmBd~qnQs680^7pBW_*L$Dez2{Sl4pjzy3^irdH=MhggH!P~4@%ch zkfwA-{_GQw7%672%}NduIY#+L0oTAF!Wm|aN3fN^LQ^pKFJWYG(4R=yr_vCH6?t>C zeMbzToCuk5#xqHEJ;mko-T8S0s}t)StI-(Kf5K$oAK4^NQr(M~exl@gnW!sHyO!Ku zbWMv6x1)05MSA5m8m1yYo)YU0kthc99WWK(t~g6aa*SJA;sN^{0&awcOX@wCextax zB+SXyl9PbO{eXDNVD00U_JaEmh($U$h1DIwK%g9X80&`v=+#uG!MI+=#0Cz~3MbL& z&wx4b)3KOR+SwNLRFWMSszi{85g8&t1*r^VpF(wG%=@#5a)HUu!ztM!&`#YLdnL^a=(^CmzUae$=fzwCd)Hi%Pjp zyGE{%>*LC}8m@w=17Qs9Jy^RP?A)I2+DSlSq7~L95^pdhn@?w=XV4FCg+F%1BU9c*TJSQj83;Jj6X5wqfrn)r3E&Xg@I0 zPBJX>103mxL3&bq(hqv{q_)2Vadh|Eb6|pU_Z(36Qnb<49(f_Xe=UU=%P72SQb<60 zz`$dxkl`{N7=_aX#oLcTUlTJ%9CHL64>flWl@3Eki2@%rk*C zq}ZyyxEVZweubYg<`(@NZ!DsuV9nE2clnk z4&V4Np>HH)au`Ep@D$U>Px<3p0Lo7QjAE*-hECp~Na-u$dlg(#yugW?cH)sY#W4HS z5*+$j0!#K?_H=ZEis7M%q_eg-smx{5!w^1V7R(-762HQ+Br(-7FN5Fz|}j+un2*!lJnIMiN`Ez%Bi* zinPj%1<3N1gweMJc@bWVLAzXi9_AOc4Z>`JEkju1uncB{5}MJU0-4dD0u&5EcyS=D z5PN>A$a?sk)}Do>s28u=&=jGgHXubWs~C!(gax9Ien5>x#abq>u*afY&SUx!TNp^7 z))i)A2Z$)TDTQ^$y|f&Zp69p(tDuw({^Jq_n_aMmLCd~_y+ik11D71oh~lTb%p>BY zholD}G(jxgk3;(TGkiEbyoCwES&wl5h9Zb-u;hS{{!5bS4;g6?EvCXGisCj#c?!@c z))0lCpg8S6dqV?I7O_Ssv4$x0dIT#h))++r2uZMlBR|Xe2@ph)3K&g4*I^1&U&0h> zH&n}&)-lQ`E-{L4VHD+C80F6G>75RY;{Dk$%DL*qDCtCi3?=Fq<)r}THJQY7nD%sZaubscWU;+I)SmRE_M{KB{l3)p+o{zR zA`LS692IA+<(pcNI)$rkd#8hmp(3Dp~fI_3s|@> zo(>`}%O@D$%MC&3XUQ#El_8L00(%kieS#c>HqazTwL^`QJjOxGbqb1}@_vCgUIS~% zarT5V11=3|Icf#!=)+{=IS7-nEx!won8Fi!*QcxvM(U;KN=+s9F!WF;qX?+r@YjiH zcw$P^A?jp-0?z&R78uY{FXjH?P8{sycXk_&ePEXHM6S;an*1=a)ymGwcP}x9A!N3tr`%Tz&eC38p~{= z+Hi>7Y2#eY@~kHU>DAj#cyS`q*sQ5iZ6oru0wpL7R4JgMJmJ$dJN_wF!4OKu*D+4Zpze@R(us^>{M~ajs-N3uZAjeU=5HzrZe1R z7>O8=0gJp6NI1TYl=CwT|V;9<@r ziA#33Ee9K1G^oLiNz6$aY?vLb1~wLU=QU79nnzKN!4;@q0cGjuOyMaAK2b*jGvlp1 zixZs=m0XzIz=im+iZ;@ms1iA%LKLPnT3t`Add6Pr6uW8jvCkyHr$c4;*R%M{zHFEnrWd;xBg2Iv#KUR^ciaBb~@UMMtLX{;lbr-WDmL# zU**WfGuUN-++8nRN2uV6b$cW);DFj(YVbKqNbdl++837m((Xkj^+KVgpAv6HQiZq8kAza;P ztH=#qi9tt5cY%ESAsK0TGZb>LBsCnw1U{ATW4%?QO_TyGPIeecmulpMmKU!E+&9pb zq#Sbm-gAXkN(mZwIiO5;`F5;=iyz+9>%(sO3|f?kZh@bIAdEyYz!8Xa+8UJx@%Zg? zz&}NrPQpLH3X;^NT(S93i0|4}E0ibZcTyu9axx0F0azQvd5)FPT*I^PhaLb2zoA&c zHzqzuDx3P1+I#wZde^CHp^1od4>^XIQX2#>oK&C>oHUaPiY$XV{ivlP$IEyUqADBu zn6~dM6f(^Woj~TGT*lZXvoKE)ZNM&2UDr-5W6c#BnmkR_r|4G>kO(Yx{Ilhe{WO$< zU;muKs>7ay=Ex}ba7XikoOHbQLPG@)Ze8;jS(LrvR@TH+mI2NGEzg&}f;9w)bd~^n zf+~P`_+N}@!zFP(wOEJgr%W@jJZT~f3@T;6E^x*oO6}Q)^ z{fHNM8ek`T6$OZXU&AF>6aLUIN@(KM)gUn}JJ{Iqr|eQz5gQ{-6-!%Aq)RF@ncSg8 z%vv+eb7smbTeuyBH%2nO9>QfCHSvQ{6x~@&oZaKZ)t_m?8b~rCN5Mmg+_)r zl3OREO~31=2ILLU*=z;`I5#yEGvOA_)xaFq zMr*#WL@!8IBbdcWgao1UbZ!t`ul3iq!ZTRAXqe@xViMrQf@V#p0??0aF<@&J>(Cm- zfg*!A^?3}~IIqx_Y*e7A!dS3DRqZBh%!ay+*EmlNVja(7Hr#4KSNj;ku*F!-p(>&u z@FAm6;p|jPOx4tss@I5zykJmyhK!Gm*3Jp5EVWp3(1G>lK{aZM(d5H9ED4aYJD^wM$De1CHQx&5$Ip~5wJj634fDsV$YK1{V6)Id;@LS?` zp#n4l!TCruDh6kXs@mxBr)Y3731k%yrE!On?HPJi5knoxogFRyH-@XYr&|x-L)KNq;ks&}hK!?c$1D|ZweZoR?69f4GM5JlIpb*`puC_tS5Bo)g=%%!2q10j+xr`hZ z1M$=|L|bt1&p4^3nh?Dtqk_bn=IKmM1jdCJBf&6CJmy+G|J-EoUzmMy{8b=sfh{I^M(*c-%=sMwAn^_dy*T z*9*=U4RGAdvRtS57Uw*#ys5y55^U-o$bQSw36!7UrF1C`%O{}XlLa>=@kY4tusIJ# zP}4bZnz;&KEd(JfiUMD4em+qe*c=&za{@wYV1iKa!%Ny% zoA`!_78IEr(_%tQq@d7@N}9#JVOX!)XdND2$)yd#(s z{MlemmMp=`18TJm<~xykoq@vJPM#WaW?$J z6si;GHHIL0opHz#O^~Qi8T_XLj*onS(Zr|*nGZ##&wg-NfLrsK&4Sy9^XMmWtrAK; zk<)2T@lNpV?m0{$vQU4z!1e0mP(PiJp8OY)G6DRtmzd9;G$zpz9YdM66f^0SN(;w9 dsGsY$D&|VLKCX`I`QzH^{vS64fC%ToH2_>~Uq=7{ literal 0 HcmV?d00001 diff --git a/static/babybuddy/js/vendor.218afb8267f7.js b/static/babybuddy/js/vendor.218afb8267f7.js new file mode 100644 index 00000000..3709bb9f --- /dev/null +++ b/static/babybuddy/js/vendor.218afb8267f7.js @@ -0,0 +1,29240 @@ +/*! + * pulltorefreshjs v0.1.22 + * (c) Rafael Soto + * Released under the MIT License. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.PullToRefresh = factory()); +}(this, function () { 'use strict'; + + var _shared = { + pullStartY: null, + pullMoveY: null, + handlers: [], + styleEl: null, + events: null, + dist: 0, + state: 'pending', + timeout: null, + distResisted: 0, + supportsPassive: false, + supportsPointerEvents: typeof window !== 'undefined' && !!window.PointerEvent + }; + + try { + window.addEventListener('test', null, { + get passive() { + // eslint-disable-line getter-return + _shared.supportsPassive = true; + } + + }); + } catch (e) {// do nothing + } + + function setupDOM(handler) { + if (!handler.ptrElement) { + var ptr = document.createElement('div'); + + if (handler.mainElement !== document.body) { + handler.mainElement.parentNode.insertBefore(ptr, handler.mainElement); + } else { + document.body.insertBefore(ptr, document.body.firstChild); + } + + ptr.classList.add(((handler.classPrefix) + "ptr")); + ptr.innerHTML = handler.getMarkup().replace(/__PREFIX__/g, handler.classPrefix); + handler.ptrElement = ptr; + + if (typeof handler.onInit === 'function') { + handler.onInit(handler); + } // Add the css styles to the style node, and then + // insert it into the dom + + + if (!_shared.styleEl) { + _shared.styleEl = document.createElement('style'); + + _shared.styleEl.setAttribute('id', 'pull-to-refresh-js-style'); + + document.head.appendChild(_shared.styleEl); + } + + _shared.styleEl.textContent = handler.getStyles().replace(/__PREFIX__/g, handler.classPrefix).replace(/\s+/g, ' '); + } + + return handler; + } + + function onReset(handler) { + if (!handler.ptrElement) { return; } + handler.ptrElement.classList.remove(((handler.classPrefix) + "refresh")); + handler.ptrElement.style[handler.cssProp] = '0px'; + setTimeout(function () { + // remove previous ptr-element from DOM + if (handler.ptrElement && handler.ptrElement.parentNode) { + handler.ptrElement.parentNode.removeChild(handler.ptrElement); + handler.ptrElement = null; + } // reset state + + + _shared.state = 'pending'; + }, handler.refreshTimeout); + } + + function update(handler) { + var iconEl = handler.ptrElement.querySelector(("." + (handler.classPrefix) + "icon")); + var textEl = handler.ptrElement.querySelector(("." + (handler.classPrefix) + "text")); + + if (iconEl) { + if (_shared.state === 'refreshing') { + iconEl.innerHTML = handler.iconRefreshing; + } else { + iconEl.innerHTML = handler.iconArrow; + } + } + + if (textEl) { + if (_shared.state === 'releasing') { + textEl.innerHTML = handler.instructionsReleaseToRefresh; + } + + if (_shared.state === 'pulling' || _shared.state === 'pending') { + textEl.innerHTML = handler.instructionsPullToRefresh; + } + + if (_shared.state === 'refreshing') { + textEl.innerHTML = handler.instructionsRefreshing; + } + } + } + + var _ptr = { + setupDOM: setupDOM, + onReset: onReset, + update: update + }; + + var _timeout; + + var screenY = function screenY(event) { + if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) { + return event.screenY; + } + + return event.touches[0].screenY; + }; + + var _setupEvents = (function () { + var _el; + + function _onTouchStart(e) { + // here, we must pick a handler first, and then append their html/css on the DOM + var target = _shared.handlers.filter(function (h) { return h.contains(e.target); })[0]; + + _shared.enable = !!target; + + if (target && _shared.state === 'pending') { + _el = _ptr.setupDOM(target); + + if (target.shouldPullToRefresh()) { + _shared.pullStartY = screenY(e); + } + + clearTimeout(_shared.timeout); + + _ptr.update(target); + } + } + + function _onTouchMove(e) { + if (!(_el && _el.ptrElement && _shared.enable)) { + return; + } + + if (!_shared.pullStartY) { + if (_el.shouldPullToRefresh()) { + _shared.pullStartY = screenY(e); + } + } else { + _shared.pullMoveY = screenY(e); + } + + if (_shared.state === 'refreshing') { + if (e.cancelable && _el.shouldPullToRefresh() && _shared.pullStartY < _shared.pullMoveY) { + e.preventDefault(); + } + + return; + } + + if (_shared.state === 'pending') { + _el.ptrElement.classList.add(((_el.classPrefix) + "pull")); + + _shared.state = 'pulling'; + + _ptr.update(_el); + } + + if (_shared.pullStartY && _shared.pullMoveY) { + _shared.dist = _shared.pullMoveY - _shared.pullStartY; + } + + _shared.distExtra = _shared.dist - _el.distIgnore; + + if (_shared.distExtra > 0) { + if (e.cancelable) { + e.preventDefault(); + } + + _el.ptrElement.style[_el.cssProp] = (_shared.distResisted) + "px"; + _shared.distResisted = _el.resistanceFunction(_shared.distExtra / _el.distThreshold) * Math.min(_el.distMax, _shared.distExtra); + + if (_shared.state === 'pulling' && _shared.distResisted > _el.distThreshold) { + _el.ptrElement.classList.add(((_el.classPrefix) + "release")); + + _shared.state = 'releasing'; + + _ptr.update(_el); + } + + if (_shared.state === 'releasing' && _shared.distResisted < _el.distThreshold) { + _el.ptrElement.classList.remove(((_el.classPrefix) + "release")); + + _shared.state = 'pulling'; + + _ptr.update(_el); + } + } + } + + function _onTouchEnd() { + if (!(_el && _el.ptrElement && _shared.enable)) { + return; + } // wait 1/2 sec before unmounting... + + + clearTimeout(_timeout); + _timeout = setTimeout(function () { + if (_el && _el.ptrElement && _shared.state === 'pending') { + _ptr.onReset(_el); + } + }, 500); + + if (_shared.state === 'releasing' && _shared.distResisted > _el.distThreshold) { + _shared.state = 'refreshing'; + _el.ptrElement.style[_el.cssProp] = (_el.distReload) + "px"; + + _el.ptrElement.classList.add(((_el.classPrefix) + "refresh")); + + _shared.timeout = setTimeout(function () { + var retval = _el.onRefresh(function () { return _ptr.onReset(_el); }); + + if (retval && typeof retval.then === 'function') { + retval.then(function () { return _ptr.onReset(_el); }); + } + + if (!retval && !_el.onRefresh.length) { + _ptr.onReset(_el); + } + }, _el.refreshTimeout); + } else { + if (_shared.state === 'refreshing') { + return; + } + + _el.ptrElement.style[_el.cssProp] = '0px'; + _shared.state = 'pending'; + } + + _ptr.update(_el); + + _el.ptrElement.classList.remove(((_el.classPrefix) + "release")); + + _el.ptrElement.classList.remove(((_el.classPrefix) + "pull")); + + _shared.pullStartY = _shared.pullMoveY = null; + _shared.dist = _shared.distResisted = 0; + } + + function _onScroll() { + if (_el) { + _el.mainElement.classList.toggle(((_el.classPrefix) + "top"), _el.shouldPullToRefresh()); + } + } + + var _passiveSettings = _shared.supportsPassive ? { + passive: _shared.passive || false + } : undefined; + + if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) { + window.addEventListener('pointerup', _onTouchEnd); + window.addEventListener('pointerdown', _onTouchStart); + window.addEventListener('pointermove', _onTouchMove, _passiveSettings); + } else { + window.addEventListener('touchend', _onTouchEnd); + window.addEventListener('touchstart', _onTouchStart); + window.addEventListener('touchmove', _onTouchMove, _passiveSettings); + } + + window.addEventListener('scroll', _onScroll); + return { + onTouchEnd: _onTouchEnd, + onTouchStart: _onTouchStart, + onTouchMove: _onTouchMove, + onScroll: _onScroll, + + destroy: function destroy() { + if (_shared.pointerEventsEnabled && _shared.supportsPointerEvents) { + window.removeEventListener('pointerdown', _onTouchStart); + window.removeEventListener('pointerup', _onTouchEnd); + window.removeEventListener('pointermove', _onTouchMove, _passiveSettings); + } else { + window.removeEventListener('touchstart', _onTouchStart); + window.removeEventListener('touchend', _onTouchEnd); + window.removeEventListener('touchmove', _onTouchMove, _passiveSettings); + } + + window.removeEventListener('scroll', _onScroll); + } + + }; + }); + + var _ptrMarkup = "\n

\n"; + + var _ptrStyles = "\n.__PREFIX__ptr {\n box-shadow: inset 0 -3px 5px rgba(0, 0, 0, 0.12);\n pointer-events: none;\n font-size: 0.85em;\n font-weight: bold;\n top: 0;\n height: 0;\n transition: height 0.3s, min-height 0.3s;\n text-align: center;\n width: 100%;\n overflow: hidden;\n display: flex;\n align-items: flex-end;\n align-content: stretch;\n}\n\n.__PREFIX__box {\n padding: 10px;\n flex-basis: 100%;\n}\n\n.__PREFIX__pull {\n transition: none;\n}\n\n.__PREFIX__text {\n margin-top: .33em;\n color: rgba(0, 0, 0, 0.3);\n}\n\n.__PREFIX__icon {\n color: rgba(0, 0, 0, 0.3);\n transition: transform .3s;\n}\n\n/*\nWhen at the top of the page, disable vertical overscroll so passive touch\nlisteners can take over.\n*/\n.__PREFIX__top {\n touch-action: pan-x pan-down pinch-zoom;\n}\n\n.__PREFIX__release .__PREFIX__icon {\n transform: rotate(180deg);\n}\n"; + + var _defaults = { + distThreshold: 60, + distMax: 80, + distReload: 50, + distIgnore: 0, + mainElement: 'body', + triggerElement: 'body', + ptrElement: '.ptr', + classPrefix: 'ptr--', + cssProp: 'min-height', + iconArrow: '⇣', + iconRefreshing: '…', + instructionsPullToRefresh: 'Pull down to refresh', + instructionsReleaseToRefresh: 'Release to refresh', + instructionsRefreshing: 'Refreshing', + refreshTimeout: 500, + getMarkup: function () { return _ptrMarkup; }, + getStyles: function () { return _ptrStyles; }, + onInit: function () {}, + onRefresh: function () { return location.reload(); }, + resistanceFunction: function (t) { return Math.min(1, t / 2.5); }, + shouldPullToRefresh: function () { return !window.scrollY; } + }; + + var _methods = ['mainElement', 'ptrElement', 'triggerElement']; + var _setupHandler = (function (options) { + var _handler = {}; // merge options with defaults + + Object.keys(_defaults).forEach(function (key) { + _handler[key] = options[key] || _defaults[key]; + }); // normalize timeout value, even if it is zero + + _handler.refreshTimeout = typeof options.refreshTimeout === 'number' ? options.refreshTimeout : _defaults.refreshTimeout; // normalize elements + + _methods.forEach(function (method) { + if (typeof _handler[method] === 'string') { + _handler[method] = document.querySelector(_handler[method]); + } + }); // attach events lazily + + + if (!_shared.events) { + _shared.events = _setupEvents(); + } + + _handler.contains = function (target) { + return _handler.triggerElement.contains(target); + }; + + _handler.destroy = function () { + // stop pending any pending callbacks + clearTimeout(_shared.timeout); // remove handler from shared state + + var offset = _shared.handlers.indexOf(_handler); + + _shared.handlers.splice(offset, 1); + }; + + return _handler; + }); + + var index = { + setPassiveMode: function setPassiveMode(isPassive) { + _shared.passive = isPassive; + }, + + setPointerEventsMode: function setPointerEventsMode(isEnabled) { + _shared.pointerEventsEnabled = isEnabled; + }, + + destroyAll: function destroyAll() { + if (_shared.events) { + _shared.events.destroy(); + + _shared.events = null; + } + + _shared.handlers.forEach(function (h) { + h.destroy(); + }); + }, + + init: function init(options) { + if ( options === void 0 ) options = {}; + + var handler = _setupHandler(options); + + _shared.handlers.push(handler); + + return handler; + }, + + // export utils for testing + _: { + setupHandler: _setupHandler, + setupEvents: _setupEvents, + setupDOM: _ptr.setupDOM, + onReset: _ptr.onReset, + update: _ptr.update + } + }; + + return index; + +})); + +/*! + * jQuery JavaScript Library v3.6.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2021-03-02T17:08Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.6.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.6 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2021-02-16 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +} +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the primary Deferred + primary = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + primary.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( primary.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return primary.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); + } + + return primary.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + + // Support: Chrome 86+ + // In Chrome, if an element having a focusout handler is blurred by + // clicking outside of it, it invokes the handler synchronously. If + // that handler calls `.remove()` on the element, the data is cleared, + // leaving `result` undefined. We need to guard against this. + return result && result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + which: true +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + // Suppress native focus or blur as it's already being fired + // in leverageNative. + _default: function() { + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + // + // Support: Firefox 70+ + // Only Firefox includes border widths + // in computed dimensions. (gh-4529) + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; + tr.style.cssText = "border:1px solid"; + + // Support: Chrome 86+ + // Height set through cssText does not get applied. + // Computed height then comes back as 0. + tr.style.height = "1px"; + trChild.style.height = "9px"; + + // Support: Android 8 Chrome 86+ + // In our bodyBackground.html iframe, + // display for all div elements is set to "inline", + // which causes a problem only in Android 8 Chrome 86. + // Ensuring the div is display: block + // gets around this issue. + trChild.style.display = "block"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + + parseInt( trStyle.borderTopWidth, 10 ) + + parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, parserErrorElem; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) {} + + parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; + if ( !xml || parserErrorElem ) { + jQuery.error( "Invalid XML: " + ( + parserErrorElem ? + jQuery.map( parserErrorElem.childNodes, function( el ) { + return el.textContent; + } ).join( "\n" ) : + data + ) ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ).filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ).map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + +originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script but not if jsonp + if ( !isSuccess && + jQuery.inArray( "script", s.dataTypes ) > -1 && + jQuery.inArray( "json", s.dataTypes ) < 0 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "