From 376bb8659493ff6fed2bf70970521c0324f4300d Mon Sep 17 00:00:00 2001 From: "Christopher C. Wells" Date: Thu, 22 Aug 2019 20:20:14 -0700 Subject: [PATCH] Update node dependencies and static assets. --- package-lock.json | 606 +- package.json | 2 +- static/babybuddy/js/graph.239e93d5162b.js | 84270 ++++++++++++++++ static/babybuddy/js/graph.239e93d5162b.js.gz | Bin 0 -> 651389 bytes .../docs/css/base.3208b6cc4466.css | 359 + .../docs/css/base.3208b6cc4466.css.gz | Bin 0 -> 1604 bytes static/rest_framework/docs/css/base.css | 17 +- static/rest_framework/docs/css/base.css.gz | Bin 1563 -> 1604 bytes .../js/jquery-3.4.1.min.220afd743d9e.js | 2 + .../js/jquery-3.4.1.min.220afd743d9e.js.gz | Bin 0 -> 30638 bytes static/rest_framework/js/jquery-3.4.1.min.js | 2 + .../rest_framework/js/jquery-3.4.1.min.js.gz | Bin 0 -> 30638 bytes static/staticfiles.json | 2 +- 13 files changed, 84868 insertions(+), 392 deletions(-) create mode 100644 static/babybuddy/js/graph.239e93d5162b.js create mode 100644 static/babybuddy/js/graph.239e93d5162b.js.gz create mode 100644 static/rest_framework/docs/css/base.3208b6cc4466.css create mode 100644 static/rest_framework/docs/css/base.3208b6cc4466.css.gz create mode 100644 static/rest_framework/js/jquery-3.4.1.min.220afd743d9e.js create mode 100644 static/rest_framework/js/jquery-3.4.1.min.220afd743d9e.js.gz create mode 100644 static/rest_framework/js/jquery-3.4.1.min.js create mode 100644 static/rest_framework/js/jquery-3.4.1.min.js.gz diff --git a/package-lock.json b/package-lock.json index 653884da..8b424ea5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,10 +32,22 @@ "wgs84": "0.0.0" } }, - "@mapbox/gl-matrix": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/gl-matrix/-/gl-matrix-0.0.1.tgz", - "integrity": "sha1-5RJqq01kw2uBx6l9CuDd3eV3PSs=", + "@mapbox/geojson-rewind": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.4.0.tgz", + "integrity": "sha512-b+1uPWBERW4Pet/969BNu61ZPDyH2ilIxBjJDFzxyS9TyszF9UrTQyYIl/G38clux3rtpAGGFSGTCSF/qR6UjA==", + "dev": true, + "requires": { + "@mapbox/geojson-area": "0.2.2", + "concat-stream": "~1.6.0", + "minimist": "1.2.0", + "sharkdown": "^0.1.0" + } + }, + "@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==", "dev": true }, "@mapbox/jsonlint-lines-primitives": { @@ -45,9 +57,9 @@ "dev": true }, "@mapbox/mapbox-gl-supported": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.4.0.tgz", - "integrity": "sha512-ZD0Io4XK+/vU/4zpANjOtdWfVszAgnaMPsGR6LKsWh4kLIEv9qoobTVmJPPuwuM+ZI2b3BlZ6DYw1XHVmv6YTA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.4.1.tgz", + "integrity": "sha512-yyKza9S6z3ELKuf6w5n6VNUB0Osu6Z93RXPfMHLIlNWohu3KqxewLOq4lMXseYJ92GwkRAxd207Pr/Z98cwmvw==", "dev": true }, "@mapbox/point-geometry": { @@ -56,12 +68,6 @@ "integrity": "sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI=", "dev": true }, - "@mapbox/shelf-pack": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@mapbox/shelf-pack/-/shelf-pack-3.2.0.tgz", - "integrity": "sha512-dyQxe6ukILV6qaEvxoKCIwhblgRjYp1ZGlClo4xvfbmxzFO5LYu7Tnrg2AZrRgN7VsSragsGcNjzUe9kCdKHYQ==", - "dev": true - }, "@mapbox/tiny-sdf": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz", @@ -126,6 +132,53 @@ "d3-shape": "^1.2.0" } }, + "@plotly/d3-sankey-circular": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz", + "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==", + "dev": true, + "requires": { + "d3-array": "^1.2.1", + "d3-collection": "^1.0.4", + "d3-shape": "^1.2.0", + "elementary-circuits-directed-graph": "^1.0.4" + } + }, + "@turf/area": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.0.1.tgz", + "integrity": "sha512-Zv+3N1ep9P5JvR0YOYagLANyapGWQBh8atdeR3bKpWcigVXFsEKNUw03U/5xnh+cKzm7yozHD6MFJkqQv55y0g==", + "dev": true, + "requires": { + "@turf/helpers": "6.x", + "@turf/meta": "6.x" + } + }, + "@turf/centroid": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.0.2.tgz", + "integrity": "sha512-auyDauOtC4eddH7GC3CHFTDu2PKhpSeKCRhwhHhXtJqn2dWCJQNIoCeJRmfXRIbzCWhWvgvQafvvhq8HNvmvWw==", + "dev": true, + "requires": { + "@turf/helpers": "6.x", + "@turf/meta": "6.x" + } + }, + "@turf/helpers": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.4.tgz", + "integrity": "sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g==", + "dev": true + }, + "@turf/meta": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.0.2.tgz", + "integrity": "sha512-VA7HJkx7qF1l3+GNGkDVn2oXy4+QoLP6LktXAaZKjuT1JI0YESat7quUkbCMy4zP9lAUuvS4YMslLyTtr919FA==", + "dev": true, + "requires": { + "@turf/helpers": "6.x" + } + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -868,100 +921,6 @@ } } }, - "brfs": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", - "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", - "dev": true, - "requires": { - "quote-stream": "^1.0.1", - "resolve": "^1.1.5", - "static-module": "^2.2.0", - "through2": "^2.0.0" - }, - "dependencies": { - "buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", - "dev": true - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "object-inspect": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz", - "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==", - "dev": true - }, - "quote-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", - "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", - "dev": true, - "requires": { - "buffer-equal": "0.0.1", - "minimist": "^1.1.3", - "through2": "^2.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "static-module": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/static-module/-/static-module-2.2.5.tgz", - "integrity": "sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==", - "dev": true, - "requires": { - "concat-stream": "~1.6.0", - "convert-source-map": "^1.5.1", - "duplexer2": "~0.1.4", - "escodegen": "~1.9.0", - "falafel": "^2.1.0", - "has": "^1.0.1", - "magic-string": "^0.22.4", - "merge-source-map": "1.0.4", - "object-inspect": "~1.4.0", - "quote-stream": "~1.0.2", - "readable-stream": "~2.3.3", - "shallow-copy": "~0.0.1", - "static-eval": "^2.0.0", - "through2": "~2.0.3" - } - } - } - }, "buble": { "version": "0.19.8", "resolved": "https://registry.npmjs.org/buble/-/buble-0.19.8.tgz", @@ -979,15 +938,15 @@ }, "dependencies": { "acorn": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz", - "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", "dev": true }, "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", "dev": true }, "chalk": { @@ -1001,15 +960,6 @@ "supports-color": "^5.3.0" } }, - "magic-string": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", - "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, "os-homedir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-2.0.0.tgz", @@ -1830,9 +1780,9 @@ "dev": true }, "d3-color": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.8.tgz", - "integrity": "sha512-yeANXzP37PHk0DbSTMNPhnJD+Nn4G//O5E825bR6fAfHH43hobSBpgB9G9oWVl9+XgUaQ4yCnsX1H+l8DoaL9A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.3.0.tgz", + "integrity": "sha512-NHODMBlj59xPAwl2BDiO2Mog6V+PrGRtBfWKqKRrs9MCqlSkIEb0Z/SfY7jW29ReHTDC/j+vwXhnZcXI3+3fbg==", "dev": true }, "d3-dispatch": { @@ -1869,9 +1819,9 @@ } }, "d3-path": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", - "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg==", "dev": true }, "d3-quadtree": { @@ -1880,18 +1830,6 @@ "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==", "dev": true }, - "d3-sankey-circular": { - "version": "0.33.0", - "resolved": "https://registry.npmjs.org/d3-sankey-circular/-/d3-sankey-circular-0.33.0.tgz", - "integrity": "sha512-sn6Nuoc1lgZ5PYrxqJ0G3ZbInHM4ZP+lrg5SMc0AolgenQ9CBERf3gVliQzxeXyo2kSU7QgewUZwjvddEzT/JA==", - "dev": true, - "requires": { - "d3-array": "^1.2.1", - "d3-collection": "^1.0.4", - "d3-shape": "^1.2.0", - "elementary-circuits-directed-graph": "^1.0.4" - } - }, "d3-shape": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", @@ -2379,9 +2317,9 @@ "dev": true }, "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", "dev": true, "requires": { "esprima": "^3.1.3", @@ -2486,6 +2424,12 @@ } } }, + "esm": { + "version": "3.0.84", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.0.84.tgz", + "integrity": "sha512-SzSGoZc17S7P+12R9cg21Bdb7eybX25RnIeRZ80xZs+VZ3kdQKzqTp2k4hZJjR7p9l0186TTXSgrxzlMDBktlw==", + "dev": true + }, "espree": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", @@ -2977,16 +2921,6 @@ "css-font": "^1.0.0" } }, - "font-atlas-sdf": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/font-atlas-sdf/-/font-atlas-sdf-1.3.3.tgz", - "integrity": "sha512-GxUpcdkdoHgC3UrpMuA7JmG1Ty/MY0BhfmV8r7ZSv3bkqBY5vmRIjcj7Pg8iqj20B03vlU6fUhdpyIgEo/Z35w==", - "dev": true, - "requires": { - "optical-properties": "^1.0.0", - "tiny-sdf": "^1.0.2" - } - }, "font-awesome": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", @@ -3725,18 +3659,6 @@ "is-property": "^1.0.0" } }, - "geojson-rewind": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/geojson-rewind/-/geojson-rewind-0.3.1.tgz", - "integrity": "sha1-IiQHl8hHzC8MHTE+SqDJFa+n8p0=", - "dev": true, - "requires": { - "@mapbox/geojson-area": "0.2.2", - "concat-stream": "~1.6.0", - "minimist": "1.2.0", - "sharkdown": "^0.1.0" - } - }, "geojson-vt": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", @@ -3927,6 +3849,12 @@ "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA==", "dev": true }, + "gl-matrix": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.1.0.tgz", + "integrity": "sha512-526NA+3EA+ztAQi0IZpSWiM0fyQXIp7IbRvfJ4wS/TjjQD0uv0fVybXwwqqSOlq33UckivI0yMDlVtboWm3k7A==", + "dev": true + }, "gl-matrix-invert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gl-matrix-invert/-/gl-matrix-invert-1.0.0.tgz", @@ -3977,9 +3905,9 @@ } }, "gl-plot3d": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.2.1.tgz", - "integrity": "sha512-WSzZ9118mUal3uW/9bCPqdrlncmD/T0zWoOY9PiskIOAJ5dxKhzPbY2XkjDs+jGh/ce1yCSEh6LO9aB9SirGow==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.2.2.tgz", + "integrity": "sha512-is8RoDVUEbUM7kJ2qjhKJlfGLECH3ML9pTCW1V7ylUdmUACmcZ4lzJrQr/NIRkHC5WcUNOp3QJKPjBND3ngZ2A==", "dev": true, "requires": { "3d-view": "^2.0.0", @@ -3989,10 +3917,10 @@ "gl-mat4": "^1.2.0", "gl-select-static": "^2.0.4", "gl-shader": "^4.2.1", - "gl-spikes3d": "^1.0.8", + "gl-spikes3d": "^1.0.9", "glslify": "^7.0.0", "has-passive-events": "^1.0.0", - "is-mobile": "^2.0.0", + "is-mobile": "^2.1.0", "mouse-change": "^1.4.0", "mouse-event-offset": "^3.0.2", "mouse-wheel": "^1.2.0", @@ -4024,9 +3952,9 @@ } }, "gl-scatter3d": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.2.1.tgz", - "integrity": "sha512-bGeCkqWOvjE+CM6eJGapTfZcVAjAVdUoi3LjRtvnVmpaTBeMF+HWG5mTa1pWKHu+DNEosH3Yjr52wNenYHMUdg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.2.2.tgz", + "integrity": "sha512-oZh3WQ0bVXnpASpZmYmiEp7eUiD0oU6J4G5C9KUOhUo5d2gucvZEILAtfWmzCT3zsOltoROn4jGuuP2tlLN88Q==", "dev": true, "requires": { "gl-buffer": "^2.0.6", @@ -4663,35 +4591,6 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, - "gray-matter": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-3.1.1.tgz", - "integrity": "sha512-nZ1qjLmayEv0/wt3sHig7I0s3/sJO0dkAaKYQ5YAOApUtYEOonXSFdWvL1khvnZMTvov4UufkqlFsilPnejEXA==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "js-yaml": "^3.10.0", - "kind-of": "^5.0.2", - "strip-bom-string": "^1.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, "grid-index": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", @@ -5423,9 +5322,9 @@ "dev": true }, "is-mobile": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.0.1.tgz", - "integrity": "sha512-JNaoIFM3geBseef71iOu4024vfphwjFQPFGD64cziXbbyHdrF9zLTqxj2VEbrdOS/gkiQ5b3HldPf4eYPu8g/g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.1.0.tgz", + "integrity": "sha512-M5OhlZwh+aTlmRUvDg0Wq3uWVNa+w4DyZ2SjbrS+BhSLu9Po+JXHendC305ZEu+Hh7lywb19Zu4kYXu3L1Oo8A==", "dev": true }, "is-my-ip-valid": { @@ -5733,9 +5632,9 @@ "dev": true }, "kdbush": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz", - "integrity": "sha1-PL0D6d6tnA9vZszblkUOXOzGQOA=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==", "dev": true }, "kind-of": { @@ -5845,9 +5744,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "lodash.capitalize": { @@ -5895,12 +5794,12 @@ } }, "magic-string": { - "version": "0.22.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", - "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", + "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", "dev": true, "requires": { - "vlq": "^0.2.2" + "sourcemap-codec": "^1.4.4" } }, "make-error": { @@ -5969,37 +5868,35 @@ } }, "mapbox-gl": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.45.0.tgz", - "integrity": "sha1-r3HMgk8NflHM1cUF6q5BG8CRDM0=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.1.1.tgz", + "integrity": "sha512-i57kASg8J/U/lJzBePyqTP2ImKUcx8FkHyCjb3ssWYaBBXHUeZ4STGXXfU9u1AQU9170PjDIJLubUUB1vLLSBQ==", "dev": true, "requires": { - "@mapbox/gl-matrix": "^0.0.1", - "@mapbox/jsonlint-lines-primitives": "^2.0.1", - "@mapbox/mapbox-gl-supported": "^1.3.1", + "@mapbox/geojson-rewind": "^0.4.0", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.4.0", "@mapbox/point-geometry": "^0.1.0", - "@mapbox/shelf-pack": "^3.1.0", "@mapbox/tiny-sdf": "^1.1.0", "@mapbox/unitbezier": "^0.0.0", "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.0.0", - "brfs": "^1.4.4", + "@mapbox/whoots-js": "^3.1.0", "csscolorparser": "~1.0.2", - "earcut": "^2.1.3", - "geojson-rewind": "^0.3.0", - "geojson-vt": "^3.1.0", - "gray-matter": "^3.0.8", - "grid-index": "^1.0.0", + "earcut": "^2.1.5", + "esm": "~3.0.84", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.0.0", + "grid-index": "^1.1.0", "minimist": "0.0.8", + "murmurhash-js": "^1.0.0", "pbf": "^3.0.5", - "quickselect": "^1.0.0", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", "rw": "^1.3.3", - "shuffle-seed": "^1.1.6", - "sort-object": "^0.3.2", - "supercluster": "^2.3.0", - "through2": "^2.0.3", - "tinyqueue": "^1.1.0", - "vt-pbf": "^3.0.1" + "supercluster": "^6.0.1", + "tinyqueue": "^2.0.0", + "vt-pbf": "^3.1.1" }, "dependencies": { "minimist": { @@ -6142,15 +6039,6 @@ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, - "merge-source-map": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", - "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, "merge2": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", @@ -6209,9 +6097,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -6789,12 +6677,6 @@ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, - "optical-properties": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/optical-properties/-/optical-properties-1.0.0.tgz", - "integrity": "sha512-XnBQYbIIzDVr7U3L7d3xyAEqp1W+HTkqmw/G4L/Ae/+dq57bT1jqW2uDwV0wCUzO8gsTDIZhGQsGrMb17VSkEA==", - "dev": true - }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -6875,9 +6757,9 @@ } }, "parenthesis": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.6.tgz", - "integrity": "sha512-2fobSoJQTFoIKJ2kXw8QupNtKJ93lNwRgwBxf8YxMNWnWwvMVzqs/baseqWhHP1bRQGf0cv75UtO71nUO5dFuA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.7.tgz", + "integrity": "sha512-iMtu+HCbLXVrpf6Ys/4YKhcFxbux3xK4ZVB9r+a2kMSqeeQWQoDNYlXIsOjwlT2ldYXZ3k5PVeBnYn7fbAo/Bg==", "dev": true }, "parse-filepath": { @@ -7120,26 +7002,27 @@ } }, "plotly.js": { - "version": "1.48.3", - "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.48.3.tgz", - "integrity": "sha512-MzRzengCybw/ObEuAHnXOroIvllbHcVer0MIMzP3GbwTAaNI/3QlZB/kx5C5mJ40xZAol7z08bOAnJmzGSwT/Q==", + "version": "1.49.4", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.49.4.tgz", + "integrity": "sha512-yOcA1GKLY6vsGLWYoGa/jWTPXcHPTyTWwkgAhopNtCgxTVexXzKfnnGQ2SslL//+TkpNX8CpCzC88HQVV4p42Q==", "dev": true, "requires": { "@plotly/d3-sankey": "0.7.2", + "@plotly/d3-sankey-circular": "0.33.1", + "@turf/area": "^6.0.1", + "@turf/centroid": "^6.0.2", "alpha-shape": "^1.0.0", "canvas-fit": "^1.5.0", - "color-normalize": "^1.3.0", + "color-normalize": "^1.5.0", "convex-hull": "^1.0.3", "country-regex": "^1.1.0", "d3": "^3.5.12", "d3-force": "^1.0.6", "d3-hierarchy": "^1.1.8", "d3-interpolate": "1", - "d3-sankey-circular": "0.33.0", "delaunay-triangulate": "^1.1.6", "es6-promise": "^3.0.2", "fast-isnumeric": "^1.1.3", - "font-atlas-sdf": "^1.3.3", "gl-cone3d": "^1.3.1", "gl-contour2d": "^1.1.6", "gl-error3d": "^1.0.15", @@ -7148,18 +7031,18 @@ "gl-mat4": "^1.2.0", "gl-mesh3d": "^2.1.1", "gl-plot2d": "^1.4.2", - "gl-plot3d": "^2.2.1", + "gl-plot3d": "^2.2.2", "gl-pointcloud2d": "^1.0.2", - "gl-scatter3d": "^1.2.1", + "gl-scatter3d": "^1.2.2", "gl-select-box": "^1.0.3", "gl-spikes2d": "^1.0.2", "gl-streamtube3d": "^1.2.1", "gl-surface3d": "^1.4.6", - "gl-text": "^1.1.6", + "gl-text": "^1.1.8", "glslify": "^7.0.0", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", - "mapbox-gl": "0.45.0", + "mapbox-gl": "1.1.1", "matrix-camera-controller": "^2.1.3", "mouse-change": "^1.4.0", "mouse-event-offset": "^3.0.2", @@ -7170,13 +7053,13 @@ "point-cluster": "^3.1.4", "polybooljs": "^1.2.0", "regl": "^1.3.11", - "regl-error2d": "^2.0.7", - "regl-line2d": "3.0.13", - "regl-scatter2d": "^3.1.4", - "regl-splom": "^1.0.6", + "regl-error2d": "^2.0.8", + "regl-line2d": "^3.0.14", + "regl-scatter2d": "^3.1.5", + "regl-splom": "^1.0.7", "right-now": "^1.0.0", "robust-orientation": "^1.1.3", - "sane-topojson": "^3.0.1", + "sane-topojson": "^4.0.0", "strongly-connected-components": "^1.0.1", "superscript-text": "^1.0.0", "svg-path-sdf": "^1.1.3", @@ -7309,6 +7192,12 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "potpack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.1.tgz", + "integrity": "sha512-15vItUAbViaYrmaB/Pbw7z6qX2xENbFSTA7Ii4tgbPtasxm5v6ryKhKtL91tpWovDJzTiZqdwzhcFBCwiMVdVw==", + "dev": true + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -7434,9 +7323,9 @@ } }, "quickselect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", - "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", "dev": true }, "quote-stream": { @@ -7669,13 +7558,13 @@ "dev": true }, "regexpu-core": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", - "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.5.tgz", + "integrity": "sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==", "dev": true, "requires": { "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.0.2", + "regenerate-unicode-properties": "^8.1.0", "regjsgen": "^0.5.0", "regjsparser": "^0.6.0", "unicode-match-property-ecmascript": "^1.0.4", @@ -7720,23 +7609,48 @@ } }, "regl-line2d": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.0.13.tgz", - "integrity": "sha512-bTsuvTw4No25kUKGiXwOm0sLJT9kZ7vAkZOZYyXLxKCMRYIz1TS0j7DfqtC5ammzni8AdSahuTT0x52RU4Izuw==", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.0.14.tgz", + "integrity": "sha512-F5Ru1Bugi6Xkk2JJ4EuzAybuL99CtnAr6VIrJVJdsaFzWmI9GfPFtwbNZROeOrXXX7yElyc0HQsQDJaNpSeWmg==", "dev": true, "requires": { - "array-bounds": "^1.0.0", + "array-bounds": "^1.0.1", "array-normalize": "^1.1.3", - "bubleify": "^1.0.0", - "color-normalize": "^1.0.0", - "earcut": "^2.1.1", - "es6-weak-map": "^2.0.2", - "flatten-vertex-data": "^1.0.0", + "bubleify": "^1.2.0", + "color-normalize": "^1.5.0", + "earcut": "^2.1.5", + "es6-weak-map": "^2.0.3", + "flatten-vertex-data": "^1.0.2", "glslify": "^7.0.0", "object-assign": "^4.1.1", "parse-rect": "^1.2.0", - "pick-by-alias": "^1.1.0", - "to-float32": "^1.0.0" + "pick-by-alias": "^1.2.0", + "to-float32": "^1.0.1" + }, + "dependencies": { + "es5-ext": { + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + } } }, "regl-scatter2d": { @@ -8153,9 +8067,9 @@ "dev": true }, "sane-topojson": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-3.0.1.tgz", - "integrity": "sha512-pntXgkpEHB3tTAO/rJM0aKLauq7uetB5T15NT1DMpDnbK853M9d8IiFD+WmVUDnyvZtfg3hQ5BC98fec8DlwaQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-4.0.0.tgz", + "integrity": "sha512-bJILrpBboQfabG3BNnHI2hZl52pbt80BE09u4WhnrmzuF2JbMKZdl62G5glXskJ46p+gxE2IzOwGj/awR4g8AA==", "dev": true }, "sass-graph": { @@ -8213,12 +8127,6 @@ } } }, - "seedrandom": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", - "integrity": "sha512-9A+PDmgm+2du77B5i0Ip2cxOqqHjgNxnBgglxLcX78A2D6c2rTo61z4jnVABpF4cKeDMDG+cmXXvdnqse2VqMA==", - "dev": true - }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", @@ -8241,9 +8149,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -8294,15 +8202,6 @@ "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=", "dev": true }, - "shuffle-seed": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/shuffle-seed/-/shuffle-seed-1.1.6.tgz", - "integrity": "sha1-UzwSaDurO0+j6HUfxOViFGdEJgs=", - "dev": true, - "requires": { - "seedrandom": "^2.4.2" - } - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -8519,28 +8418,6 @@ } } }, - "sort-asc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz", - "integrity": "sha1-q3md9h/HPqCVbHnEtTHtHp53J+k=", - "dev": true - }, - "sort-desc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.1.1.tgz", - "integrity": "sha1-GYuMDN6wlcRjNBhh45JdTuNZqe4=", - "dev": true - }, - "sort-object": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-0.3.2.tgz", - "integrity": "sha1-mODRme3kDgfGGoRAPGHWw7KQ+eI=", - "dev": true, - "requires": { - "sort-asc": "^0.1.0", - "sort-desc": "^0.1.1" - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -8944,12 +8821,6 @@ "is-utf8": "^0.2.0" } }, - "strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", - "dev": true - }, "strip-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", @@ -8972,12 +8843,12 @@ "dev": true }, "supercluster": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-2.3.0.tgz", - "integrity": "sha1-h6tWCBu+qaHXJN9TUe6ejDry9Is=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-6.0.2.tgz", + "integrity": "sha512-aa0v2HURjBTOpbcknilcfxGDuArM8khklKSmZ/T8ZXL0BuRwb5aRw95lz+2bmWpFvCXDX/+FzqHxmg0TIaJErw==", "dev": true, "requires": { - "kdbush": "^1.0.1" + "kdbush": "^3.0.0" } }, "superscript-text": { @@ -9277,12 +9148,6 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, - "tiny-sdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tiny-sdf/-/tiny-sdf-1.0.2.tgz", - "integrity": "sha1-KOdphcRMTlhMS2fY7N2bM6HKwow=", - "dev": true - }, "tinycolor2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", @@ -9290,9 +9155,9 @@ "dev": true }, "tinyqueue": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-1.2.3.tgz", - "integrity": "sha512-Qz9RgWuO9l8lT+Y9xvbzhPT2efIUIFd69N7eF7tJ9lnQl0iLj1M7peK7IoUGZL9DJHw9XftqLreccfxcQgYLxA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", "dev": true }, "to-absolute-glob": { @@ -9621,38 +9486,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "uniq": { @@ -10035,12 +9877,6 @@ "source-map": "^0.5.1" } }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", - "dev": true - }, "vt-pbf": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.1.tgz", diff --git a/package.json b/package.json index 00dbce82..513f1b85 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "gulp-uglify": "^3.0.2", "jquery": "^3.4.1", "moment": "^2.24.0", - "plotly.js": "^1.48.3", + "plotly.js": "^1.49.4", "popper.js": "^1.15.0", "pump": "^3.0.0", "tempusdominus-bootstrap-4": "^5.1.2", diff --git a/static/babybuddy/js/graph.239e93d5162b.js b/static/babybuddy/js/graph.239e93d5162b.js new file mode 100644 index 00000000..08a7192a --- /dev/null +++ b/static/babybuddy/js/graph.239e93d5162b.js @@ -0,0 +1,84270 @@ +/** +* plotly.js (cartesian) v1.49.4 +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* Licensed under the MIT license +*/ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Plotly = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i:not(.watermark)": "opacity:0;-webkit-transition:opacity 0.3s ease 0s;-moz-transition:opacity 0.3s ease 0s;-ms-transition:opacity 0.3s ease 0s;-o-transition:opacity 0.3s ease 0s;transition:opacity 0.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:white;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:white;", + "X .select-outline-2": "stroke:black;stroke-dasharray:2px 2px;", + Y: "font-family:'Open Sans';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,0.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:0.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":169}],2:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/bar'); + +},{"../src/traces/bar":274}],3:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/box'); + +},{"../src/traces/box":288}],4:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/contour'); + +},{"../src/traces/contour":308}],5:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/core'); + +},{"../src/core":151}],6:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/heatmap'); + +},{"../src/traces/heatmap":324}],7:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/histogram'); + +},{"../src/traces/histogram":342}],8:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/histogram2d'); + +},{"../src/traces/histogram2d":348}],9:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/histogram2dcontour'); + +},{"../src/traces/histogram2dcontour":352}],10:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Plotly = _dereq_('./core'); + +Plotly.register([ + _dereq_('./bar'), + _dereq_('./box'), + _dereq_('./heatmap'), + _dereq_('./histogram'), + _dereq_('./histogram2d'), + _dereq_('./histogram2dcontour'), + _dereq_('./pie'), + _dereq_('./contour'), + _dereq_('./scatterternary'), + _dereq_('./violin') +]); + +module.exports = Plotly; + +},{"./bar":2,"./box":3,"./contour":4,"./core":5,"./heatmap":6,"./histogram":7,"./histogram2d":8,"./histogram2dcontour":9,"./pie":11,"./scatterternary":12,"./violin":13}],11:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/pie'); + +},{"../src/traces/pie":359}],12:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/scatterternary'); + +},{"../src/traces/scatterternary":397}],13:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = _dereq_('../src/traces/violin'); + +},{"../src/traces/violin":405}],14:[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. + +var objectCreate = Object.create || objectCreatePolyfill +var objectKeys = Object.keys || objectKeysPolyfill +var bind = Function.prototype.bind || functionBindPolyfill + +function EventEmitter() { + if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) { + this._events = objectCreate(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +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; + +var hasDefineProperty; +try { + var o = {}; + if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 }); + hasDefineProperty = o.x === 0; +} catch (err) { hasDefineProperty = false } +if (hasDefineProperty) { + Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + // check whether the input is a positive number (whose value is zero or + // greater and not a NaN). + if (typeof arg !== 'number' || arg < 0 || arg !== arg) + throw new TypeError('"defaultMaxListeners" must be a positive number'); + defaultMaxListeners = arg; + } + }); +} else { + EventEmitter.defaultMaxListeners = defaultMaxListeners; +} + +// 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 || isNaN(n)) + throw new TypeError('"n" argument must be a positive number'); + 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); +}; + +// These standalone emit* functions are used to optimize calling of event +// handlers for fast cases because emit() itself often has a variable number of +// arguments and can be deoptimized because of that. These functions always have +// the same number of arguments and thus do not get deoptimized, so the code +// inside them can execute faster. +function emitNone(handler, isFn, self) { + if (isFn) + handler.call(self); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self); + } +} +function emitOne(handler, isFn, self, arg1) { + if (isFn) + handler.call(self, arg1); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1); + } +} +function emitTwo(handler, isFn, self, arg1, arg2) { + if (isFn) + handler.call(self, arg1, arg2); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1, arg2); + } +} +function emitThree(handler, isFn, self, arg1, arg2, arg3) { + if (isFn) + handler.call(self, arg1, arg2, arg3); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1, arg2, arg3); + } +} + +function emitMany(handler, isFn, self, args) { + if (isFn) + handler.apply(self, args); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].apply(self, args); + } +} + +EventEmitter.prototype.emit = function emit(type) { + var er, handler, len, args, i, events; + var doError = (type === 'error'); + + events = this._events; + if (events) + doError = (doError && events.error == null); + else if (!doError) + return false; + + // If there is no 'error' event listener then throw. + if (doError) { + if (arguments.length > 1) + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } else { + // At least give some kind of context to the user + var err = new Error('Unhandled "error" event. (' + er + ')'); + err.context = er; + throw err; + } + return false; + } + + handler = events[type]; + + if (!handler) + return false; + + var isFn = typeof handler === 'function'; + len = arguments.length; + switch (len) { + // fast cases + case 1: + emitNone(handler, isFn, this); + break; + case 2: + emitOne(handler, isFn, this, arguments[1]); + break; + case 3: + emitTwo(handler, isFn, this, arguments[1], arguments[2]); + break; + case 4: + emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); + break; + // slower + default: + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + emitMany(handler, isFn, this, args); + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + var m; + var events; + var existing; + + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + + events = target._events; + if (!events) { + events = target._events = objectCreate(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) { + 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) { + // 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]; + } else { + // If we've already got an array, just append. + if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + } + + // Check for listener leak + if (!existing.warned) { + m = $getMaxListeners(target); + if (m && m > 0 && existing.length > m) { + existing.warned = true; + 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; + if (typeof console === 'object' && console.warn) { + console.warn('%s: %s', w.name, w.message); + } + } + } + } + + 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; + switch (arguments.length) { + case 0: + return this.listener.call(this.target); + case 1: + return this.listener.call(this.target, arguments[0]); + case 2: + return this.listener.call(this.target, arguments[0], arguments[1]); + case 3: + return this.listener.call(this.target, arguments[0], arguments[1], + arguments[2]); + default: + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) + args[i] = arguments[i]; + this.listener.apply(this.target, args); + } + } +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; + var wrapped = bind.call(onceWrapper, state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type, listener) { + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = + function prependOnceListener(type, listener) { + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + 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; + + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + + events = this._events; + if (!events) + return this; + + list = events[type]; + if (!list) + return this; + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) + this._events = objectCreate(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) + this.emit('removeListener', type, originalListener || listener); + } + + return this; + }; + +EventEmitter.prototype.removeAllListeners = + function removeAllListeners(type) { + var listeners, events, i; + + events = this._events; + if (!events) + return this; + + // not listening for removeListener, no need to emit + if (!events.removeListener) { + if (arguments.length === 0) { + this._events = objectCreate(null); + this._eventsCount = 0; + } else if (events[type]) { + if (--this._eventsCount === 0) + this._events = objectCreate(null); + else + delete events[type]; + } + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + var keys = objectKeys(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 = objectCreate(null); + this._eventsCount = 0; + return this; + } + + listeners = events[type]; + + if (typeof listeners === 'function') { + this.removeListener(type, listeners); + } else if (listeners) { + // 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) + return []; + + var evlistener = events[type]; + if (!evlistener) + 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) { + var evlistener = events[type]; + + if (typeof evlistener === 'function') { + return 1; + } else if (evlistener) { + return evlistener.length; + } + } + + return 0; +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; +}; + +// About 1.5x faster than the two-arg version of Array#splice(). +function spliceOne(list, index) { + for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) + list[i] = list[k]; + list.pop(); +} + +function arrayClone(arr, n) { + var copy = new Array(n); + for (var i = 0; i < n; ++i) + copy[i] = arr[i]; + return copy; +} + +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 objectCreatePolyfill(proto) { + var F = function() {}; + F.prototype = proto; + return new F; +} +function objectKeysPolyfill(obj) { + var keys = []; + for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) { + keys.push(k); + } + return k; +} +function functionBindPolyfill(context) { + var fn = this; + return function () { + return fn.apply(context, arguments); + }; +} + +},{}],15:[function(_dereq_,module,exports){ +!function() { + var d3 = { + version: "3.5.17" + }; + 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 (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var status = request.status, result; + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + dispatch.beforesend.call(xhr, request); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + } + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" ? request.response : request.responseText; + } + d3.dsv = function(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); + xhr.row = function(_) { + return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; + }; + return xhr; + } + function response(request) { + return dsv.parse(request.responseText); + } + function typedResponse(f) { + return function(request) { + return dsv.parse(request.responseText, f); + }; + } + dsv.parse = function(text, f) { + var o; + return dsv.parseRows(text, function(row, i) { + if (o) return o(row, i - 1); + var a = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + o = f ? function(row, i) { + return f(a(row), i); + } : a; + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.slice(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.slice(j, I - k); + } + return text.slice(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && (a = f(a, n++)) == null) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + if (Array.isArray(rows[0])) return dsv.formatRows(rows); + var fieldSet = new d3_Set(), fields = []; + rows.forEach(function(row) { + for (var field in row) { + if (!fieldSet.has(field)) { + fields.push(fieldSet.add(field)); + } + } + }); + return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { + return fields.map(function(field) { + return formatValue(row[field]); + }).join(delimiter); + })).join("\n"); + }; + dsv.formatRows = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + }; + d3.csv = d3.dsv(",", "text/csv"); + d3.tsv = d3.dsv(" ", "text/tab-separated-values"); + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { + setTimeout(callback, 17); + }; + d3.timer = function() { + d3_timer.apply(this, arguments); + }; + function d3_timer(callback, delay, then) { + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); + var time = then + delay, timer = { + c: callback, + t: time, + n: null + }; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + return timer; + } + function d3_timer_step() { + var now = d3_timer_mark(), delay = d3_timer_sweep() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + d3_timer_mark(); + d3_timer_sweep(); + }; + function d3_timer_mark() { + var now = Date.now(), timer = d3_timer_queueHead; + while (timer) { + if (now >= timer.t && timer.c(now - timer.t)) timer.c = null; + timer = timer.n; + } + return now; + } + function d3_timer_sweep() { + var t0, t1 = d3_timer_queueHead, time = Infinity; + while (t1) { + if (t1.c) { + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; + } else { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; + } + } + d3_timer_queueTail = t0; + return time; + } + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value = +value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_locale_numberFormat(locale) { + var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { + var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0; + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = locale_grouping[j = (j + 1) % locale_grouping.length]; + } + return t.reverse().join(locale_thousands); + } : d3_identity; + return function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (symbol === "#") prefix = "0" + type.toLowerCase(); + + case "c": + exponent = false; + + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; + if (type == "r" && !precision) type = "g"; + if (precision != null) { + if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); + } + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + var fullSuffix = suffix; + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; + if (scale < 0) { + var unit = d3.formatPrefix(value, precision); + value = unit.scale(value); + fullSuffix = unit.symbol + suffix; + } else { + value *= scale; + } + value = type(value, precision); + var i = value.lastIndexOf("."), before, after; + if (i < 0) { + var j = exponent ? value.lastIndexOf("e") : -1; + if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); + } else { + before = value.substring(0, i); + after = locale_decimal + value.substring(i + 1); + } + if (!zfill && comma) before = formatGroup(before, Infinity); + var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); + negative += prefix; + value = before + after; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; + }; + }; + } + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_time = d3.time = {}, d3_date = Date; + function d3_date_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_date_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_date(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_date(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_date = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_date = Date; + } + }; + } + d3_time.year = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3_time.years = d3_time.year.range; + d3_time.years.utc = d3_time.year.utc.range; + d3_time.day = d3_time_interval(function(date) { + var day = new d3_date(2e3, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3_time.days = d3_time.day.range; + d3_time.days.utc = d3_time.day.utc.range; + d3_time.dayOfYear = function(date) { + var year = d3_time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { + i = 7 - i; + var interval = d3_time[day] = d3_time_interval(function(date) { + (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3_time[day + "s"] = interval.range; + d3_time[day + "s"].utc = interval.utc.range; + d3_time[day + "OfYear"] = function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3_time.week = d3_time.sunday; + d3_time.weeks = d3_time.sunday.range; + d3_time.weeks.utc = d3_time.sunday.utc.range; + d3_time.weekOfYear = d3_time.sundayOfYear; + function d3_locale_timeFormat(locale) { + var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; + function d3_time_format(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.slice(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.slice(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "W" in d ? 1 : 0; + date.setFullYear(d.y, 0, 1); + date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); + } else date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); + return localZ ? date._ : date; + }; + format.toString = function() { + return template; + }; + return format; + } + function d3_time_parse(date, template, string, j) { + var c, p, t, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + t = template.charAt(i++); + p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + d3_time_format.utc = function(template) { + var local = d3_time_format(template); + function format(date) { + try { + d3_date = d3_date_utc; + var utc = new d3_date(); + utc._ = date; + return local(utc); + } finally { + d3_date = Date; + } + } + format.parse = function(string) { + try { + d3_date = d3_date_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_date = Date; + } + }; + format.toString = local.toString; + return format; + }; + d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; + var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); + locale_periods.forEach(function(p, i) { + d3_time_periodLookup.set(p.toLowerCase(), i); + }); + var d3_time_formats = { + a: function(d) { + return locale_shortDays[d.getDay()]; + }, + A: function(d) { + return locale_days[d.getDay()]; + }, + b: function(d) { + return locale_shortMonths[d.getMonth()]; + }, + B: function(d) { + return locale_months[d.getMonth()]; + }, + c: d3_time_format(locale_dateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return locale_periods[+(d.getHours() >= 12)]; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); + }, + x: d3_time_format(locale_date), + X: d3_time_format(locale_time), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + j: d3_time_parseDayOfYear, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + U: d3_time_parseWeekNumberSunday, + w: d3_time_parseWeekdayNumber, + W: d3_time_parseWeekNumberMonday, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear, + Z: d3_time_parseZone, + "%": d3_time_parseLiteralPercent + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + return d3_time_format; + } + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; + function d3_time_formatPad(value, fill, width) { + var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 1)); + return n ? (date.w = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberSunday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.U = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberMonday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.W = +n[0], i + n[0].length) : -1; + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 4)); + return n ? (date.y = +n[0], i + n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; + } + function d3_time_parseZone(date, string, i) { + return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, + i + 5) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.m = n[0] - 1, i + n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.d = +n[0], i + n[0].length) : -1; + } + function d3_time_parseDayOfYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.j = +n[0], i + n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.H = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.M = +n[0], i + n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.S = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.L = +n[0], i + n[0].length) : -1; + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + function d3_time_parseLiteralPercent(date, string, i) { + d3_time_percentRe.lastIndex = 0; + var n = d3_time_percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; + } + function d3_time_formatMulti(formats) { + var n = formats.length, i = -1; + while (++i < n) formats[i][0] = this(formats[i][0]); + return function(date) { + var i = 0, f = formats[i]; + while (!f[1](date)) f = formats[++i]; + return f[0](date); + }; + } + d3.locale = function(locale) { + return { + numberFormat: d3_locale_numberFormat(locale), + timeFormat: d3_locale_timeFormat(locale) + }; + }; + var d3_locale_enUS = d3.locale({ + decimal: ".", + thousands: ",", + grouping: [ 3 ], + currency: [ "$", "" ], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + periods: [ "AM", "PM" ], + days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], + shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] + }); + d3.format = d3_locale_enUS.numberFormat; + d3.geo = {}; + function d3_adder() {} + d3_adder.prototype = { + s: 0, + t: 0, + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } + }; + var d3_adderTemp = new d3_adder(); + function d3_adderSum(a, b, o) { + var x = o.s = a + b, bv = x - a, av = x - bv; + o.t = a - av + (b - bv); + } + d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; + } + function d3_geo_sphericalEqual(a, b) { + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; + } + d3.geo.bounds = function() { + var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; + } + }; + function point(λ, φ) { + ranges.push(range = [ λ0 = λ, λ1 = λ ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + function linePoint(λ, φ) { + var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); + } + p0 = p, λ_ = λ; + } + function lineStart() { + bound.point = linePoint; + } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } + function ringStart() { + d3_geo_area.lineStart(); + } + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } + function angle(λ0, λ1) { + return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; + } + function compareRanges(a, b) { + return a[0] - b[0]; + } + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + d3.geo.stream(feature, bound); + var n = ranges.length; + if (n) { + ranges.sort(compareRanges); + for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; + } + } + ranges = range = null; + return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; + }; + }(); + d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); + var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + if (m < ε2) return [ NaN, NaN ]; + } + return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; + }; + var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; + var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); + } + function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + function d3_geo_centroidRingStart() { + var λ00, φ00, x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_true() { + return true; + } + function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n]; + if (d3_geo_sphericalEqual(p0, p1)) { + listener.lineStart(); + for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); + listener.lineEnd(); + return; + } + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; + subject.push(a); + clip.push(b); + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; + subject.push(a); + clip.push(b); + }); + clip.sort(compare); + d3_geo_clipPolygonLinkCircular(subject); + d3_geo_clipPolygonLinkCircular(clip); + if (!subject.length) return; + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; + } + var start = subject[0], points, point; + while (1) { + var current = start, isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + listener.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, listener); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, listener); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + listener.lineEnd(); + } + } + function d3_geo_clipPolygonLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; + } + function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; + this.e = entry; + this.v = false; + this.n = this.p = null; + } + function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); + if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); + } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; + function pointRing(λ, φ) { + ring.push([ λ, φ ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; + if (clean & 1) { + segment = ringSegments[0]; + var n = segment.length - 1, i = -1, point; + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + }, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + } + }; + } + function d3_geo_clipSort(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); + if (abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfπ; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; + d3_geo_areaRingSum.reset(); + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], m = ring.length; + if (!m) continue; + var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ 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; +}(); +},{}],16:[function(_dereq_,module,exports){ +(function (process,global){ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version 3.3.1 + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.ES6Promise = factory()); +}(this, (function () { 'use strict'; + +function objectOrFunction(x) { + return typeof x === 'function' || typeof x === 'object' && x !== null; +} + +function isFunction(x) { + return typeof x === 'function'; +} + +var _isArray = undefined; +if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; +} else { + _isArray = Array.isArray; +} + +var isArray = _isArray; + +var len = 0; +var vertxNext = undefined; +var customSchedulerFn = undefined; + +var asap = function asap(callback, arg) { + queue[len] = callback; + queue[len + 1] = arg; + len += 2; + if (len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (customSchedulerFn) { + customSchedulerFn(flush); + } else { + scheduleFlush(); + } + } +}; + +function setScheduler(scheduleFn) { + customSchedulerFn = scheduleFn; +} + +function setAsap(asapFn) { + asap = asapFn; +} + +var browserWindow = typeof window !== 'undefined' ? window : undefined; +var browserGlobal = browserWindow || {}; +var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; +var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]'; + +// test for web worker but not in IE10 +var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; + +// node +function useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function () { + return process.nextTick(flush); + }; +} + +// vertx +function useVertxTimer() { + return function () { + vertxNext(flush); + }; +} + +function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function () { + node.data = iterations = ++iterations % 2; + }; +} + +// web worker +function useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + return function () { + return channel.port2.postMessage(0); + }; +} + +function useSetTimeout() { + // Store setTimeout reference so es6-promise will be unaffected by + // other code modifying setTimeout (like sinon.useFakeTimers()) + var globalSetTimeout = setTimeout; + return function () { + return globalSetTimeout(flush, 1); + }; +} + +var queue = new Array(1000); +function flush() { + for (var i = 0; i < len; i += 2) { + var callback = queue[i]; + var arg = queue[i + 1]; + + callback(arg); + + queue[i] = undefined; + queue[i + 1] = undefined; + } + + len = 0; +} + +function attemptVertx() { + try { + var r = _dereq_; + var vertx = r('vertx'); + vertxNext = vertx.runOnLoop || vertx.runOnContext; + return useVertxTimer(); + } catch (e) { + return useSetTimeout(); + } +} + +var scheduleFlush = undefined; +// Decide what async method to use to triggering processing of queued callbacks: +if (isNode) { + scheduleFlush = useNextTick(); +} else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); +} else if (isWorker) { + scheduleFlush = useMessageChannel(); +} else if (browserWindow === undefined && typeof _dereq_ === 'function') { + scheduleFlush = attemptVertx(); +} else { + scheduleFlush = useSetTimeout(); +} + +function then(onFulfillment, onRejection) { + var _arguments = arguments; + + var parent = this; + + var child = new this.constructor(noop); + + if (child[PROMISE_ID] === undefined) { + makePromise(child); + } + + var _state = parent._state; + + if (_state) { + (function () { + var callback = _arguments[_state - 1]; + asap(function () { + return invokeCallback(_state, child, callback, parent._result); + }); + })(); + } else { + subscribe(parent, child, onFulfillment, onRejection); + } + + return child; +} + +/** + `Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + resolve(1); + }); + + promise.then(function(value){ + // value === 1 + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.resolve(1); + + promise.then(function(value){ + // value === 1 + }); + ``` + + @method resolve + @static + @param {Any} value value that the returned promise will be resolved with + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` +*/ +function resolve(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(noop); + _resolve(promise, object); + return promise; +} + +var PROMISE_ID = Math.random().toString(36).substring(16); + +function noop() {} + +var PENDING = void 0; +var FULFILLED = 1; +var REJECTED = 2; + +var GET_THEN_ERROR = new ErrorObject(); + +function selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); +} + +function cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); +} + +function getThen(promise) { + try { + return promise.then; + } catch (error) { + GET_THEN_ERROR.error = error; + return GET_THEN_ERROR; + } +} + +function tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch (e) { + return e; + } +} + +function handleForeignThenable(promise, thenable, then) { + asap(function (promise) { + var sealed = false; + var error = tryThen(then, thenable, function (value) { + if (sealed) { + return; + } + sealed = true; + if (thenable !== value) { + _resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function (reason) { + if (sealed) { + return; + } + sealed = true; + + _reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + _reject(promise, error); + } + }, promise); +} + +function handleOwnThenable(promise, thenable) { + if (thenable._state === FULFILLED) { + fulfill(promise, thenable._result); + } else if (thenable._state === REJECTED) { + _reject(promise, thenable._result); + } else { + subscribe(thenable, undefined, function (value) { + return _resolve(promise, value); + }, function (reason) { + return _reject(promise, reason); + }); + } +} + +function handleMaybeThenable(promise, maybeThenable, then$$) { + if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) { + handleOwnThenable(promise, maybeThenable); + } else { + if (then$$ === GET_THEN_ERROR) { + _reject(promise, GET_THEN_ERROR.error); + } else if (then$$ === undefined) { + fulfill(promise, maybeThenable); + } else if (isFunction(then$$)) { + handleForeignThenable(promise, maybeThenable, then$$); + } else { + fulfill(promise, maybeThenable); + } + } +} + +function _resolve(promise, value) { + if (promise === value) { + _reject(promise, selfFulfillment()); + } else if (objectOrFunction(value)) { + handleMaybeThenable(promise, value, getThen(value)); + } else { + fulfill(promise, value); + } +} + +function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + publish(promise); +} + +function fulfill(promise, value) { + if (promise._state !== PENDING) { + return; + } + + promise._result = value; + promise._state = FULFILLED; + + if (promise._subscribers.length !== 0) { + asap(publish, promise); + } +} + +function _reject(promise, reason) { + if (promise._state !== PENDING) { + return; + } + promise._state = REJECTED; + promise._result = reason; + + asap(publishRejection, promise); +} + +function subscribe(parent, child, onFulfillment, onRejection) { + var _subscribers = parent._subscribers; + var length = _subscribers.length; + + parent._onerror = null; + + _subscribers[length] = child; + _subscribers[length + FULFILLED] = onFulfillment; + _subscribers[length + REJECTED] = onRejection; + + if (length === 0 && parent._state) { + asap(publish, parent); + } +} + +function publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { + return; + } + + var child = undefined, + callback = undefined, + detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; +} + +function ErrorObject() { + this.error = null; +} + +var TRY_CATCH_ERROR = new ErrorObject(); + +function tryCatch(callback, detail) { + try { + return callback(detail); + } catch (e) { + TRY_CATCH_ERROR.error = e; + return TRY_CATCH_ERROR; + } +} + +function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value = undefined, + error = undefined, + succeeded = undefined, + failed = undefined; + + if (hasCallback) { + value = tryCatch(callback, detail); + + if (value === TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value = null; + } else { + succeeded = true; + } + + if (promise === value) { + _reject(promise, cannotReturnOwn()); + return; + } + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== PENDING) { + // noop + } else if (hasCallback && succeeded) { + _resolve(promise, value); + } else if (failed) { + _reject(promise, error); + } else if (settled === FULFILLED) { + fulfill(promise, value); + } else if (settled === REJECTED) { + _reject(promise, value); + } +} + +function initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value) { + _resolve(promise, value); + }, function rejectPromise(reason) { + _reject(promise, reason); + }); + } catch (e) { + _reject(promise, e); + } +} + +var id = 0; +function nextId() { + return id++; +} + +function makePromise(promise) { + promise[PROMISE_ID] = id++; + promise._state = undefined; + promise._result = undefined; + promise._subscribers = []; +} + +function Enumerator(Constructor, input) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(noop); + + if (!this.promise[PROMISE_ID]) { + makePromise(this.promise); + } + + if (isArray(input)) { + this._input = input; + this.length = input.length; + this._remaining = input.length; + + this._result = new Array(this.length); + + if (this.length === 0) { + fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(); + if (this._remaining === 0) { + fulfill(this.promise, this._result); + } + } + } else { + _reject(this.promise, validationError()); + } +} + +function validationError() { + return new Error('Array Methods must be provided an Array'); +}; + +Enumerator.prototype._enumerate = function () { + var length = this.length; + var _input = this._input; + + for (var i = 0; this._state === PENDING && i < length; i++) { + this._eachEntry(_input[i], i); + } +}; + +Enumerator.prototype._eachEntry = function (entry, i) { + var c = this._instanceConstructor; + var resolve$$ = c.resolve; + + if (resolve$$ === resolve) { + var _then = getThen(entry); + + if (_then === then && entry._state !== PENDING) { + this._settledAt(entry._state, i, entry._result); + } else if (typeof _then !== 'function') { + this._remaining--; + this._result[i] = entry; + } else if (c === Promise) { + var promise = new c(noop); + handleMaybeThenable(promise, entry, _then); + this._willSettleAt(promise, i); + } else { + this._willSettleAt(new c(function (resolve$$) { + return resolve$$(entry); + }), i); + } + } else { + this._willSettleAt(resolve$$(entry), i); + } +}; + +Enumerator.prototype._settledAt = function (state, i, value) { + var promise = this.promise; + + if (promise._state === PENDING) { + this._remaining--; + + if (state === REJECTED) { + _reject(promise, value); + } else { + this._result[i] = value; + } + } + + if (this._remaining === 0) { + fulfill(promise, this._result); + } +}; + +Enumerator.prototype._willSettleAt = function (promise, i) { + var enumerator = this; + + subscribe(promise, undefined, function (value) { + return enumerator._settledAt(FULFILLED, i, value); + }, function (reason) { + return enumerator._settledAt(REJECTED, i, reason); + }); +}; + +/** + `Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = resolve(2); + let promise3 = resolve(3); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` + + If any of the `promises` given to `all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = reject(new Error("2")); + let promise3 = reject(new Error("3")); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` + + @method all + @static + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static +*/ +function all(entries) { + return new Enumerator(this, entries).promise; +} + +/** + `Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. + + Example: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 2'); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // result === 'promise 2' because it was resolved before promise1 + // was resolved. + }); + ``` + + `Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error('promise 2')); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === 'promise 2' because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + An example real-world use case is implementing timeouts: + + ```javascript + Promise.race([ajax('foo.json'), timeout(5000)]) + ``` + + @method race + @static + @param {Array} promises array of promises to observe + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. +*/ +function race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + if (!isArray(entries)) { + return new Constructor(function (_, reject) { + return reject(new TypeError('You must pass an array to race.')); + }); + } else { + return new Constructor(function (resolve, reject) { + var length = entries.length; + for (var i = 0; i < length; i++) { + Constructor.resolve(entries[i]).then(resolve, reject); + } + }); + } +} + +/** + `Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @static + @param {Any} reason value that the returned promise will be rejected with. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. +*/ +function reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(noop); + _reject(promise, reason); + return promise; +} + +function needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); +} + +function needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); +} + +/** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + let promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + let xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {function} resolver + Useful for tooling. + @constructor +*/ +function Promise(resolver) { + this[PROMISE_ID] = nextId(); + this._result = this._state = undefined; + this._subscribers = []; + + if (noop !== resolver) { + typeof resolver !== 'function' && needsResolver(); + this instanceof Promise ? initializePromise(this, resolver) : needsNew(); + } +} + +Promise.all = all; +Promise.race = race; +Promise.resolve = resolve; +Promise.reject = reject; +Promise._setScheduler = setScheduler; +Promise._setAsap = setAsap; +Promise._asap = asap; + +Promise.prototype = { + constructor: Promise, + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + let result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + let author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: then, + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + 'catch': function _catch(onRejection) { + return this.then(null, onRejection); + } +}; + +function polyfill() { + var local = undefined; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P) { + var promiseToString = null; + try { + promiseToString = Object.prototype.toString.call(P.resolve()); + } catch (e) { + // silently ignored + } + + if (promiseToString === '[object Promise]' && !P.cast) { + return; + } + } + + local.Promise = Promise; +} + +polyfill(); +// Strange compat.. +Promise.polyfill = polyfill; +Promise.Promise = Promise; + +return Promise; + +}))); + +}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"_process":32}],17:[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":22}],18:[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; +}; +},{}],19:[function(_dereq_,module,exports){ +(function (global){ +'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,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"is-browser":21}],20:[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":21}],21:[function(_dereq_,module,exports){ +module.exports = true; +},{}],22:[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; +} + +},{}],23:[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() + } +} + +},{}],24:[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":25,"./lib/epsilon":26,"./lib/geojson":27,"./lib/intersecter":28,"./lib/segment-chainer":30,"./lib/segment-selector":31}],25:[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; + +},{}],26:[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; + +},{}],27:[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; + +},{}],28:[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":29}],29:[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; + +},{}],30:[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; + +},{}],31:[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; + +},{}],32:[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; }; + +},{}],33:[function(_dereq_,module,exports){ +// TinyColor v1.4.1 +// 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); + +},{}],34:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 + } +]; + +},{}],35:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + + +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', + + } + } +}); + +},{"../../plot_api/plot_template":203,"../../plots/cartesian/constants":219,"../../plots/font_attributes":239,"./arrow_paths":34}],36:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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); + + ann._extremes = {}; + if(xa) calcAxisExpansion(ann, xa); + if(ya) 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":169,"../../plots/cartesian/axes":213,"./draw":41}],37:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 v2 +function clickData2r(d, ax) { + return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d); +} + +},{"../../lib":169,"../../plot_api/plot_template":203,"../../registry":257}],38:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../color":50}],39:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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 v2.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":192,"fast-isnumeric":17}],40:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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'); + + // 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":169,"../../plots/array_container_defaults":209,"../../plots/cartesian/axes":213,"./attributes":35,"./common_defaults":38}],41:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); +var Lib = _dereq_('../../lib'); +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); +} + +/** + * 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; + + /* + * calculate the *primary* pixel position + * which is the arrowhead if there is one, + * otherwise the text anchor point + */ + if(ax) { + // 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 { + if(axLetter === 'x') { + alignPosition = options[axLetter]; + basePx = gs.l + gs.w * alignPosition; + } else { + alignPosition = 1 - options[axLetter]; + 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) { + posPx.tail = ax._offset + ax.r2p(arrowLength); + // tail is data-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; + } + + // We have everything we need for calcAutorange at this point, + // we can safely exit - unless we're currently dragging the plot + if(!gd._dragging && 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: 'translate(' + 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', xa ? + xa.p2r(xa.r2p(options.x) + dx) : + (options.x + (dx / gs.w))); + modifyItem('y', ya ? + ya.p2r(ya.r2p(options.y) + dy) : + (options.y - (dy / gs.h))); + + if(options.axref === options.xref) { + modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx)); + } + + if(options.ayref === options.yref) { + modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy)); + } + + arrowGroup.attr('transform', 'translate(' + 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) { + if(options.axref === options.xref) { + modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx)); + } else { + modifyItem('ax', options.ax + dx); + } + + if(options.ayref === options.yref) { + modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy)); + } else { + modifyItem('ay', options.ay + dy); + } + + drawArrow(dx, dy); + } else if(!subplotId) { + var xUpdate, yUpdate; + if(xa) { + xUpdate = xa.p2r(xa.r2p(options.x) + dx); + } 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) { + yUpdate = ya.p2r(ya.r2p(options.y) + dy); + } 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: 'translate(' + 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":169,"../../lib/setcursor":188,"../../lib/svg_text_utils":190,"../../plot_api/plot_template":203,"../../plots/cartesian/axes":213,"../../plots/plots":245,"../../registry":257,"../color":50,"../dragelement":68,"../drawing":71,"../fx":89,"./draw_arrow_head":42,"d3":15}],42:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); + +var Color = _dereq_('../color'); + +var ARROWPATHS = _dereq_('./arrow_paths'); + +/** + * 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: + 'translate(' + p.x + ',' + p.y + ')' + + (rot ? 'rotate(' + (rot * 180 / Math.PI) + ')' : '') + + 'scale(' + arrowScale + ')' + }) + .style({ + fill: Color.rgb(options.arrowcolor), + 'stroke-width': 0 + }); + } + + if(doStart) drawhead(startHeadStyle, start, startRot, startScale); + if(doEnd) drawhead(headStyle, end, endRot, scale); +}; + +},{"../color":50,"./arrow_paths":34,"d3":15}],43:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":223,"./attributes":35,"./calc_autorange":36,"./click":37,"./convert_coords":39,"./defaults":40,"./draw":41}],44:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":196,"../../plot_api/plot_template":203,"../annotations/attributes":35}],45:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/cartesian/axes":213}],46:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/array_container_defaults":209,"../../plots/cartesian/axes":213,"../annotations/common_defaults":38,"./attributes":44}],47:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":242,"../annotations/draw":41}],48:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../registry":257,"./attributes":44,"./convert":45,"./defaults":46,"./draw":47}],49:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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); + +},{}],50:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var tinycolor = _dereq_('tinycolor2'); +var isNumeric = _dereq_('fast-isnumeric'); + +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') 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 + ')'; +} + +},{"./attributes":49,"fast-isnumeric":17,"tinycolor2":33}],51:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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: ''}), + 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, + 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":164,"../../plot_api/edit_types":196,"../../plots/cartesian/layout_attributes":225,"../../plots/font_attributes":239}],52:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' + } +}; + +},{}],53:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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'); + + handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear'); + + var opts = {outerTicks: false, font: layout.font}; + handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts); + handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts); + + coerce('title.text', layout._dfltTitle.colorbar); + Lib.coerceFont(coerce, 'title.font', layout.font); + coerce('title.side'); +}; + +},{"../../lib":169,"../../plot_api/plot_template":203,"../../plots/cartesian/tick_label_defaults":232,"../../plots/cartesian/tick_mark_defaults":233,"../../plots/cartesian/tick_value_defaults":234,"./attributes":51}],54:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 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; + + var ax = 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', 'translate(' + Math.round(gs.l) + ',' + Math.round(gs.t) + ')'); + + var titleCont = g.select('.' + cn.cbtitleunshift) + .attr('transform', 'translate(-' + 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', 'translate(' + titleTrans + ')'); + ax.setScale(); + } + } + + g.selectAll('.' + cn.cbfills + ',.' + cn.cblines) + .attr('transform', 'translate(0,' + Math.round(gs.h * (1 - ax.domain[1])) + ')'); + + axLayer.attr('transform', 'translate(0,' + Math.round(-gs.t) + ')'); + + var fills = g.select('.' + cn.cbfills) + .selectAll('rect.' + cn.cbfill) + .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 transFn = Axes.makeTransFn(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: transFn + }); + + return Axes.drawLabels(gd, ax, { + vals: vals, + layer: axLayer, + transFn: transFn, + 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 + 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', 'translate(' + (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 + ' ' + 'translate(' + 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, + tickfont: opts.tickfont, + tickangle: opts.tickangle, + tickformat: opts.tickformat, + exponentformat: opts.exponentformat, + 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, + 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":145,"../../lib":169,"../../lib/extend":164,"../../lib/setcursor":188,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_defaults":215,"../../plots/cartesian/layout_attributes":225,"../../plots/cartesian/position_defaults":228,"../../plots/plots":245,"../../registry":257,"../color":50,"../colorscale/helpers":61,"../dragelement":68,"../drawing":71,"../titles":138,"./constants":52,"d3":15,"tinycolor2":33}],55:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = _dereq_('../../lib'); + + +module.exports = function hasColorbar(container) { + return Lib.isPlainObject(container.colorbar); +}; + +},{"../../lib":169}],56:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + moduleType: 'component', + name: 'colorbar', + + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw').draw, + hasColorbar: _dereq_('./has_colorbar') +}; + +},{"./attributes":51,"./defaults":53,"./draw":54,"./has_colorbar":55}],57:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":184,"../colorbar/attributes":51,"./scales.js":65}],58:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./helpers":61,"fast-isnumeric":17}],59:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./helpers":61}],60:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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) colorbarDefaults(containerIn, containerOut, layout); + } +}; + +},{"../../lib":169,"../../registry":257,"../colorbar/defaults":53,"../colorbar/has_colorbar":55,"./scales":65,"fast-isnumeric":17}],61:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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) { + var container = containerStr ? + Lib.nestedProperty(trace, containerStr).get() || {} : + trace; + var color = container.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 v2 + * + * @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":169,"../color":50,"./scales":65,"d3":15,"fast-isnumeric":17,"tinycolor2":33}],62:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 seperate 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":57,"./calc":58,"./cross_trace_defaults":59,"./defaults":60,"./helpers":61,"./layout_attributes":63,"./layout_defaults":64,"./scales":65}],63:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":164,"./attributes":57,"./scales":65}],64:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plot_api/plot_template":203,"./defaults":60,"./layout_attributes":63}],65:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":33}],66:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; +}; + +},{}],67:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169}],68:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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); + } + + 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); + } + + 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":169,"../../plots/cartesian/constants":219,"./align":66,"./cursor":67,"./unhover":69,"has-hover":19,"has-passive-events":20,"mouse-event-offset":23}],69:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 && + 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":162,"../../lib/events":163,"../../lib/throttle":191,"../fx/constants":83}],70:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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', + +}; + +},{}],71:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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 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 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', 'translate(' + 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]; + drawing.symbolList = drawing.symbolList.concat( + [symDef.n, k, symDef.n + 100, k + '-open']); + drawing.symbolNames[symDef.n] = k; + drawing.symbolFuncs[symDef.n] = symDef.f; + if(symDef.needLine) { + drawing.symbolNeedLines[symDef.n] = true; + } + if(symDef.noDot) { + drawing.symbolNoDot[symDef.n] = true; + } else { + drawing.symbolList = drawing.symbolList.concat( + [symDef.n + 200, k + '-dot', symDef.n + 300, k + '-open-dot']); + } + if(symDef.noFill) { + drawing.symbolNoFill[symDef.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(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; } + } + if((v % 100 >= MAXSYMBOL) || v >= 400) { return 0; } + return 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 fullID = 'g' + gd._fullLayout._uid + '-' + gradientID; + + var gradient = gd._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); +}; + +/* + * 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 gradientsGroup = Lib.ensureSingle(gd._fullLayout._defs, 'g', 'gradients'); + gradientsGroup.selectAll('linearGradient,radialGradient').remove(); +}; + + +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', 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(Array.isArray(gradientType)) { + gradientType = gradientType[0]; + if(!gradientInfo[gradientType]) gradientType = 0; + } + + 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 { + 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', 'translate(' + 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; + } + + s.each(function(d) { + var p = d3.select(this); + var text = Lib.extractOption(d, trace, 'tx', 'text'); + + if(!text && text !== 0) { + p.remove(); + return; + } + + 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 'url(\'' + baseUrl + '#' + 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 += ' translate(' + 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 = [ + 'translate(' + x + ',' + y + ')', + 'scale(' + xScale + ',' + yScale + ')', + 'translate(' + (-x) + ',' + (-y) + ')', + ]; + } + + if(existingTransform) { + transforms.push(existingTransform); + } + + el.attr('transform', transforms.join(' ')); + }); +}; + +},{"../../constants/alignment":145,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../lib/svg_text_utils":190,"../../registry":257,"../../traces/scatter/make_bubble_size_func":382,"../../traces/scatter/subtypes":389,"../color":50,"../colorscale":62,"./symbol_defs":72,"d3":15,"fast-isnumeric":17,"tinycolor2":33}],72:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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 + } +}; + +},{"d3":15}],73:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', + + } + } +}; + +},{}],74:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/cartesian/axes":213,"../../registry":257,"./compute_error":75,"fast-isnumeric":17}],75:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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)); + }; + } +} + +},{}],76:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plot_api/plot_template":203,"../../registry":257,"./attributes":73,"fast-isnumeric":17}],77:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plot_api/edit_types":196,"./attributes":73,"./calc":74,"./compute_error":75,"./defaults":76,"./plot":78,"./style":79}],78:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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":389,"../drawing":71,"d3":15,"fast-isnumeric":17}],79:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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":50,"d3":15}],80:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":164,"../../plots/font_attributes":239,"./layout_attributes":90}],81:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../registry":257}],82:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":257,"./hover":86}],83:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' +}; + +},{}],84:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./attributes":80,"./hoverlabel_defaults":87}],85:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); + +// look for either subplot or xaxis and yaxis attributes +// does not handle splom case +exports.getSubplot = function getSubplot(trace) { + return trace.subplot || (trace.xaxis + trace.yaxis) || trace.geo; +}; + +// is trace in given list of subplots? +// does handle splom case +exports.isTraceInSubplots = function isTraceInSubplots(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 flat(subplots, v) { + var out = new Array(subplots.length); + for(var i = 0; i < subplots.length; i++) { + out[i] = v; + } + return out; +}; + +exports.p2c = function p2c(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 getDistanceFunction(mode, dx, dy, dxy) { + if(mode === 'closest') return dxy || exports.quadrature(dx, dy); + return mode === 'x' ? dx : dy; +}; + +exports.getClosest = function getClosest(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 inbox(v0, v1, passVal) { + return (v0 * v1 < 0 || v0 === 0) ? passVal : Infinity; +}; + +exports.quadrature = function quadrature(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 makeEventData(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]; + } +} + +},{"../../lib":169}],86:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); + +var Lib = _dereq_('../../lib'); +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'); + +// 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; + +// 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; + }); + + alignHoverText(hoverLabel, fullOpts.rotateLabels); + + 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]; + + // 'cartesian' case + var plotObj = plots[spId]; + if(plotObj) { + supportsCompare = true; + + // TODO make sure that fullLayout_plots axis refs + // get updated properly so that we don't have + // to use Axes.getFromId in general. + + xaArray[i] = Axes.getFromId(gd, plotObj.xaxis._id); + yaArray[i] = Axes.getFromId(gd, plotObj.yaxis._id); + continue; + } + + // other subplot types + var _subplot = fullLayout[spId]._subplot; + xaArray[i] = _subplot.xaxis; + yaArray[i] = _subplot.yaxis; + } + + var hovermode = evt.hovermode || fullLayout.hovermode; + + if(hovermode && !supportsCompare) hovermode = 'closest'; + + if(['x', 'y', 'closest'].indexOf(hovermode) === -1 || !gd.calcdata || + gd.querySelector('.zoombox') || gd._dragging) { + return dragElement.unhoverRaw(gd, evt); + } + + var hoverdistance = fullLayout.hoverdistance === -1 ? Infinity : fullLayout.hoverdistance; + var spikedistance = fullLayout.spikedistance === -1 ? Infinity : fullLayout.spikedistance; + + // 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; + } + + var dbb = evt.target.getBoundingClientRect(); + + xpx = evt.clientX - dbb.left; + ypx = evt.clientY - dbb.top; + + // 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) + 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; + + // 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 { + 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, 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', 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; + }); + 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; + }); + 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; + } + } + } + } + } + } + } + + 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(spikePoints, spikelineOpts); + } + } + return result; + } + + if(hasCartesian) { + if(spikesChanged(oldspikepoints)) { + createSpikelines(spikePoints, spikelineOpts); + } + } + + hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; }); + + // 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); + + hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout); + + alignHoverText(hoverLabels, rotateLabels); + + // 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 + }); +} + +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 commonAttr = hovermode === 'y' ? 'yLabel' : 'xLabel'; + var t0 = c0[commonAttr]; + var t00 = (String(t0) || '').split(' ')[0]; + 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); + + lpath.style({ + fill: commonBgColor, + stroke: commonStroke + }); + + ltext.text(t0) + .call(Drawing.font, + commonLabelOpts.font.family || fontFamily, + commonLabelOpts.font.size || fontSize, + commonLabelOpts.font.color || contrastColor + ) + .call(svgTextUtils.positionText, 0, 0) + .call(svgTextUtils.convertToTspans, gd); + + label.attr('transform', ''); + + var tbb = ltext.node().getBoundingClientRect(); + if(hovermode === 'x') { + ltext.attr('text-anchor', 'middle') + .call(svgTextUtils.positionText, 0, (xa.side === 'top' ? + (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) : + (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD))); + + var topsign = xa.side === 'top' ? '-' : ''; + 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'); + + label.attr('transform', 'translate(' + + (xa._offset + (c0.x0 + c0.x1) / 2) + ',' + + (ya._offset + (xa.side === 'top' ? 0 : ya._length)) + ')'); + } else { + ltext.attr('text-anchor', ya.side === 'right' ? 'start' : 'end') + .call(svgTextUtils.positionText, + (ya.side === 'right' ? 1 : -1) * (HOVERTEXTPAD + HOVERARROWSIZE), + outerTop - tbb.top - tbb.height / 2); + + var leftsign = ya.side === 'right' ? '' : '-'; + 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'); + + label.attr('transform', 'translate(' + + (xa._offset + (ya.side === 'right' ? xa._length : 0)) + ',' + + (ya._offset + (c0.y0 + c0.y1) / 2) + ')'); + } + // remove the "close but not quite" points + // because of error bars, only take up to a space + hoverData = hoverData.filter(function(d) { + return (d.zLabelVal !== undefined) || + (d[commonAttr] || '').split(' ')[0] === t00; + }); + }); + + // 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 [d.trace.index, d.index, d.x0, d.y0, d.name, d.attr, d.xa, d.ya || ''].join(','); + }); + 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 name = ''; + var text = ''; + + // combine possible non-opaque trace color with bgColor + var color0 = d.bgcolor || d.color; + // 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(d.color) ? d.color : Color.defaultLine, + bgColor + ); + // find a contrasting color for border and text + var contrastColor = d.borderColor || Color.contrast(numsColor); + + // 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); + } + + 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[hovermode + 'Label'] === t0) { + text = d[(hovermode === 'x' ? 'y' : 'x') + '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(text === '' && !d.hovertemplate) { + // if 'name' is also empty, remove entire label + if(name === '') g.remove(); + text = name; + } + + // hovertemplate + var d3locale = fullLayout._d3locale; + var hovertemplate = d.hovertemplate || false; + var hovertemplateLabels = d.hovertemplateLabels || d; + var eventData = d.eventData[0] || {}; + if(hovertemplate) { + text = Lib.hovertemplateString( + hovertemplate, + hovertemplateLabels, + d3locale, + eventData, + 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 ''; + }); + } + + // 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', 'translate(' + htx + ',' + hty + ')' + + (rotateLabels ? 'rotate(' + YANGLE + ')' : '')); + }); + + return hoverLabels; +} + +// 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. Indicentally, +// 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) { + // 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; + + if(anchor === 'middle') { + txx -= d.tx2width / 2; + tx2x += d.txwidth / 2 + HOVERTEXTPAD; + } + if(rotateLabels) { + offsetY *= -YSHIFTY; + offsetX = d.offset * YSHIFTX; + } + + g.select('path').attr('d', anchor === 'middle' ? + // middle aligned: rect centered on data + ('M-' + (d.bx / 2 + d.tx2width / 2) + ',' + (offsetY - d.by / 2) + + 'h' + d.bx + 'v' + d.by + 'h-' + d.bx + 'Z') : + // left or right aligned: side rect with arrow to data + ('M0,0L' + (horzSign * HOVERARROWSIZE + offsetX) + ',' + (HOVERARROWSIZE + offsetY) + + 'v' + (d.by / 2 - HOVERARROWSIZE) + + 'h' + (horzSign * d.bx) + + 'v-' + d.by + + 'H' + (horzSign * HOVERARROWSIZE + offsetX) + + 'V' + (offsetY - HOVERARROWSIZE) + + 'Z')); + + var posX = txx + offsetX; + 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 = anchor === 'middle' ? + -d.bx / 2 - d.tx2width / 2 + HOVERTEXTPAD : + -d.bx - HOVERTEXTPAD; + } else if(textAlign === 'right' && anchor !== 'end') { + tx.attr('text-anchor', 'end'); + posX = anchor === 'middle' ? + d.bx / 2 - d.tx2width / 2 - HOVERTEXTPAD : + d.bx + HOVERTEXTPAD; + } + } + + tx.call(svgTextUtils.positionText, posX, posY); + + if(d.tx2width) { + g.select('text.name') + .call(svgTextUtils.positionText, + tx2x + alignShift * HOVERTEXTPAD + offsetX, + offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD); + g.select('rect') + .call(Drawing.setRect, + tx2x + (alignShift - 1) * d.tx2width / 2 + offsetX, + offsetY - d.by / 2 - 1, + d.tx2width, 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); + d.xVal = d.xa.c2d(d.xLabelVal); + } + if(d.yLabelVal !== undefined) { + d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal); + 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(closestPoints, opts) { + var container = opts.container; + var fullLayout = opts.fullLayout; + 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 yBB = ya._boundingBox; + var xEdge = ((yBB.left + yBB.right) / 2) < hLinePointX ? yBB.right : yBB.left; + 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) { + xBase = ya._counterSpan[0]; + xEndSpike = ya._counterSpan[1]; + } + + // 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 xBB = xa._boundingBox; + var yEdge = ((xBB.top + xBB.bottom) / 2) < vLinePointY ? xBB.bottom : xBB.top; + 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) { + yBase = xa._counterSpan[0]; + yEndSpike = xa._counterSpan[1]; + } + + // 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'] + }); +} + +},{"../../lib":169,"../../lib/events":163,"../../lib/override_cursor":180,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../registry":257,"../color":50,"../dragelement":68,"../drawing":71,"./constants":83,"./helpers":85,"d3":15,"fast-isnumeric":17,"tinycolor2":33}],87:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); + +module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) { + opts = opts || {}; + + 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":169}],88:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK; + +module.exports = function(opts, extra) { + opts = opts || {}; + extra = 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) + '.'; + } + } + + var hovertemplate = { + valType: 'string', + + dflt: '', + editType: opts.editType || 'none', + + }; + + if(opts.arrayOk !== false) { + hovertemplate.arrayOk = true; + } + + return hovertemplate; +}; + +},{"../../constants/docs":146}],89:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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":169,"../dragelement":68,"./attributes":80,"./calc":81,"./click":82,"./constants":83,"./defaults":84,"./helpers":85,"./hover":86,"./layout_attributes":90,"./layout_defaults":91,"./layout_global_defaults":92,"d3":15}],90:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', 'orbit', 'turntable', false], + dflt: 'zoom', + editType: 'modebar', + + }, + hovermode: { + valType: 'enumerated', + + values: ['x', 'y', 'closest', false], + editType: 'modebar', + + }, + hoverdistance: { + valType: 'integer', + min: -1, + dflt: 20, + + editType: 'none', + + }, + spikedistance: { + valType: 'integer', + min: -1, + dflt: 20, + + 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":239,"./constants":83}],91:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); +var layoutAttributes = _dereq_('./layout_attributes'); + +module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { + function coerce(attr, dflt) { + return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); + } + + var clickmode = coerce('clickmode'); + + var dragMode = coerce('dragmode'); + if(dragMode === 'select') coerce('selectdirection'); + + var hovermodeDflt; + if(layoutOut._has('cartesian')) { + if(clickmode.indexOf('select') > -1) { + hovermodeDflt = 'closest'; + } else { + // flag for 'horizontal' plots: + // determines the state of the mode bar 'compare' hovermode button + layoutOut._isHoriz = isHoriz(fullData, layoutOut); + hovermodeDflt = layoutOut._isHoriz ? 'y' : 'x'; + } + } else hovermodeDflt = 'closest'; + + var hoverMode = coerce('hovermode', hovermodeDflt); + if(hoverMode) { + coerce('hoverdistance'); + coerce('spikedistance'); + } + + // 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'; + } +}; + +function isHoriz(fullData, fullLayout) { + var stackOpts = fullLayout._scatterStackOpts || {}; + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + var subplot = trace.xaxis + trace.yaxis; + var subplotStackOpts = stackOpts[subplot] || {}; + var groupOpts = subplotStackOpts[trace.stackgroup] || {}; + + if(trace.orientation !== 'h' && groupOpts.orientation !== 'h') { + return false; + } + } + + return true; +} + +},{"../../lib":169,"./layout_attributes":90}],92:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./hoverlabel_defaults":87,"./layout_attributes":90}],93:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../lib/regex":184,"../../plot_api/plot_template":203,"../../plots/cartesian/constants":219,"../../plots/domain":238}],94:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var cartesianConstants = _dereq_('../../plots/cartesian/constants'); +var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; + + +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' +}); + +},{"../../plot_api/plot_template":203,"../../plots/cartesian/constants":219}],95:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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 v2.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":192,"fast-isnumeric":17}],96:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'); + + 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":169,"../../plots/array_container_defaults":209,"../../plots/cartesian/axes":213,"./attributes":94}],97:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +var Drawing = _dereq_('../drawing'); +var Axes = _dereq_('../../plots/cartesian/axes'); +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 = img.xref + 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.img && this.img.src === d.source) { + return; + } + + thisImage.attr('xmlns', xmlnsNamespaces.svg); + + 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; + + 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 size = fullLayout._size; + var width = xa ? Math.abs(xa.l2p(d.sizex) - xa.l2p(0)) : d.sizex * size.w; + var height = ya ? Math.abs(ya.l2p(d.sizey) - ya.l2p(0)) : 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 = (xa ? xa.r2p(d.x) + xa._offset : d.x * size.w + size.l) + xOffset; + var yPos = (ya ? ya.r2p(d.y) + ya._offset : size.h - d.y * size.h + size.t) + 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 ? xa._id : ''; + var yId = ya ? 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 havd 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":150,"../../plots/cartesian/axes":213,"../drawing":71,"d3":15}],98:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":223,"./attributes":94,"./convert_coords":95,"./defaults":96,"./draw":97}],99:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', + + }, + + 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, + dflt: 1.02, + + editType: 'legend', + + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'left', + + editType: 'legend', + + }, + y: { + valType: 'number', + min: -2, + max: 3, + dflt: 1, + + editType: 'legend', + + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + dflt: 'auto', + + editType: 'legend', + + }, + uirevision: { + valType: 'any', + + editType: 'none', + + }, + valign: { + valType: 'enumerated', + values: ['top', 'middle', 'bottom'], + dflt: 'middle', + + editType: 'legend', + + }, + editType: 'legend' +}; + +},{"../../plots/font_attributes":239,"../color/attributes":49}],100:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + scrollBarWidth: 6, + scrollBarMinHeight: 20, + scrollBarColor: '#808BA4', + scrollBarMargin: 4, + textOffsetX: 40 +}; + +},{}],101:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'; + + var defaultX, defaultY, defaultXAnchor, defaultYAnchor; + + 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) { + 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'); + Lib.coerceFont(coerce, 'font', layoutOut.font); + + coerce('orientation'); + if(containerOut.orientation === 'h') { + var xaxis = layoutIn.xaxis; + if(Registry.getComponentMethod('rangeslider', 'isVisible')(xaxis)) { + defaultX = 0; + defaultXAnchor = 'left'; + defaultY = 1.1; + defaultYAnchor = 'bottom'; + } else { + defaultX = 0; + defaultXAnchor = 'left'; + defaultY = -0.1; + defaultYAnchor = 'top'; + } + } + + coerce('traceorder', defaultOrder); + if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap'); + + coerce('itemsizing'); + + coerce('itemclick'); + coerce('itemdoubleclick'); + + coerce('x', defaultX); + coerce('xanchor', defaultXAnchor); + coerce('y', defaultY); + coerce('yanchor', defaultYAnchor); + coerce('valign'); + Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); +}; + +},{"../../lib":169,"../../plot_api/plot_template":203,"../../plots/layout_attributes":243,"../../registry":257,"./attributes":99,"./helpers":105}],102:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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'); + +module.exports = function draw(gd) { + var fullLayout = gd._fullLayout; + var clipId = 'legend' + fullLayout._uid; + + if(!fullLayout._infolayer || !gd.calcdata) return; + + if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0; + + var opts = fullLayout.legend; + var legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts); + var hiddenSlices = fullLayout.hiddenlabels || []; + + if(!fullLayout.showlegend || !legendData.length) { + fullLayout._infolayer.selectAll('.legend').remove(); + fullLayout._topdefs.select('#' + clipId).remove(); + + Plots.autoMargin(gd, 'legend'); + return; + } + + var maxLength = 0; + for(var i = 0; i < legendData.length; i++) { + for(var j = 0; j < legendData[i].length; j++) { + var item = legendData[i][j][0]; + var trace = item.trace; + var isPieLike = Registry.traceIs(trace, 'pie-like'); + var name = isPieLike ? item.label : trace.name; + maxLength = Math.max(maxLength, name && name.length || 0); + } + } + + var firstRender = false; + var legend = Lib.ensureSingle(fullLayout._infolayer, 'g', 'legend', function(s) { + s.attr('pointer-events', 'all'); + firstRender = true; + }); + + 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, opts.bordercolor) + .call(Color.fill, opts.bgcolor) + .style('stroke-width', opts.borderwidth + 'px'); + + var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox'); + + var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) { + s.attr({ + rx: 20, + ry: 3, + width: 0, + height: 0 + }) + .call(Color.fill, '#808BA4'); + }); + + 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, maxLength); + }) + .call(style, gd) + .each(function() { + d3.select(this) + .call(setupTraceToggle, gd); + }); + + Lib.syncOrAsync([Plots.previousPromises, + function() { + if(firstRender) { + computeLegendDimensions(gd, groups, traces); + expandMargin(gd); + } + + // Position and size the legend + var lxMin = 0; + var lxMax = fullLayout.width; + var lyMin = 0; + var lyMax = fullLayout.height; + + computeLegendDimensions(gd, groups, traces); + + if(opts._height > lyMax) { + // If the legend doesn't fit in the plot area, + // do not expand the vertical margins. + expandHorizontalMargin(gd); + } else { + expandMargin(gd); + } + + // Scroll section must be executed after repositionLegend. + // It requires the legend width, height, x and y to position the scrollbox + // and these values are mutated in repositionLegend. + var gs = fullLayout._size; + var lx = gs.l + gs.w * opts.x; + var ly = gs.t + gs.h * (1 - opts.y); + + if(Lib.isRightAnchor(opts)) { + lx -= opts._width; + } else if(Lib.isCenterAnchor(opts)) { + lx -= opts._width / 2; + } + + if(Lib.isBottomAnchor(opts)) { + ly -= opts._height; + } else if(Lib.isMiddleAnchor(opts)) { + ly -= opts._height / 2; + } + + // Make sure the legend left and right sides are visible + var legendWidth = opts._width; + var legendWidthMax = gs.w; + + if(legendWidth > legendWidthMax) { + lx = gs.l; + legendWidth = legendWidthMax; + } else { + if(lx + legendWidth > lxMax) lx = lxMax - legendWidth; + if(lx < lxMin) lx = lxMin; + legendWidth = Math.min(lxMax - lx, opts._width); + } + + // Make sure the legend top and bottom are visible + // (legends with a scroll bar are not allowed to stretch beyond the extended + // margins) + var legendHeight = opts._height; + var legendHeightMax = gs.h; + + if(legendHeight > legendHeightMax) { + ly = gs.t; + legendHeight = legendHeightMax; + } else { + if(ly + legendHeight > lyMax) ly = lyMax - legendHeight; + if(ly < lyMin) ly = lyMin; + legendHeight = Math.min(lyMax - ly, opts._height); + } + + // Set size and position of all the elements that make up a legend: + // legend, background and border, scroll box and scroll bar + Drawing.setTranslate(legend, lx, ly); + + // to be safe, remove previous listeners + scrollBar.on('.drag', null); + legend.on('wheel', null); + + if(opts._height <= legendHeight || gd._context.staticPlot) { + // if scrollbar should not be shown. + bg.attr({ + width: legendWidth - opts.borderwidth, + height: legendHeight - opts.borderwidth, + x: opts.borderwidth / 2, + y: opts.borderwidth / 2 + }); + + Drawing.setTranslate(scrollBox, 0, 0); + + clipPath.select('rect').attr({ + width: legendWidth - 2 * opts.borderwidth, + height: legendHeight - 2 * opts.borderwidth, + x: opts.borderwidth, + y: opts.borderwidth + }); + + Drawing.setClipUrl(scrollBox, clipId, gd); + + Drawing.setRect(scrollBar, 0, 0, 0, 0); + delete opts._scrollY; + } else { + var scrollBarHeight = Math.max(constants.scrollBarMinHeight, + legendHeight * legendHeight / opts._height); + var scrollBarYMax = legendHeight - + scrollBarHeight - + 2 * constants.scrollBarMargin; + var scrollBoxYMax = opts._height - legendHeight; + var scrollRatio = scrollBarYMax / scrollBoxYMax; + + var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax); + + // increase the background and clip-path width + // by the scrollbar width and margin + bg.attr({ + width: legendWidth - + 2 * opts.borderwidth + + constants.scrollBarWidth + + constants.scrollBarMargin, + height: legendHeight - opts.borderwidth, + x: opts.borderwidth / 2, + y: opts.borderwidth / 2 + }); + + clipPath.select('rect').attr({ + width: legendWidth - + 2 * opts.borderwidth + + constants.scrollBarWidth + + constants.scrollBarMargin, + height: legendHeight - 2 * opts.borderwidth, + x: opts.borderwidth, + y: opts.borderwidth + scrollBoxY + }); + + Drawing.setClipUrl(scrollBox, clipId, gd); + + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + + legend.on('wheel', function() { + scrollBoxY = Lib.constrain( + opts._scrollY + + d3.event.deltaY / scrollBarYMax * scrollBoxYMax, + 0, scrollBoxYMax); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) { + d3.event.preventDefault(); + } + }); + + var eventY0, scrollBoxY0; + + var drag = d3.behavior.drag() + .on('dragstart', function() { + eventY0 = d3.event.sourceEvent.clientY; + scrollBoxY0 = scrollBoxY; + }) + .on('drag', function() { + var e = d3.event.sourceEvent; + if(e.buttons === 2 || e.ctrlKey) return; + + scrollBoxY = Lib.constrain( + (e.clientY - eventY0) / scrollRatio + scrollBoxY0, + 0, scrollBoxYMax); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + }); + + scrollBar.call(drag); + } + + + function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) { + opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; + Drawing.setTranslate(scrollBox, 0, -scrollBoxY); + + Drawing.setRect( + scrollBar, + legendWidth, + constants.scrollBarMargin + scrollBoxY * scrollRatio, + constants.scrollBarWidth, + scrollBarHeight + ); + clipPath.select('rect').attr({ + y: opts.borderwidth + 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, opts.xanchor); + yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor); + }, + doneFn: function() { + if(xf !== undefined && yf !== undefined) { + Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf}); + } + }, + clickFn: function(numClicks, e) { + var clickedTrace = fullLayout._infolayer.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() { + 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, maxLength) { + var legendItem = g.data()[0][0]; + var fullLayout = gd._fullLayout; + var trace = legendItem.trace; + var isPieLike = Registry.traceIs(trace, 'pie-like'); + var traceIndex = trace.index; + var isEditable = gd._context.edits.legendText && !isPieLike; + + var name = isPieLike ? legendItem.label : trace.name; + if(trace._meta) { + name = Lib.templateString(name, trace._meta); + } + + var textEl = Lib.ensureSingle(g, 'text', 'legendtext'); + + textEl.attr('text-anchor', 'start') + .classed('user-select-none', true) + .call(Drawing.font, fullLayout.legend.font) + .text(isEditable ? ensureLength(name, maxLength) : name); + + svgTextUtils.positionText(textEl, constants.textOffsetX, 0); + + function textLayout(s) { + svgTextUtils.convertToTspans(s, gd, function() { + computeTextDimensions(g, gd); + }); + } + + if(isEditable) { + textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) + .call(textLayout) + .on('edit', function(newName) { + this.text(ensureLength(newName, maxLength)) + .call(textLayout); + + 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, traceIndex); + }); + } else { + textLayout(textEl); + } +} + +/* + * 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) { + s.style('cursor', 'pointer') + .attr('pointer-events', 'all') + .call(Color.fill, 'rgba(0,0,0,0)'); + }); + + 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 computeTextDimensions(g, gd) { + var legendItem = g.data()[0][0]; + + if(!legendItem.trace.showlegend) { + g.remove(); + return; + } + + var mathjaxGroup = g.select('g[class*=math-group]'); + var mathjaxNode = mathjaxGroup.node(); + var opts = gd._fullLayout.legend; + var lineHeight = opts.font.size * LINE_SPACING; + var height, width; + + if(mathjaxNode) { + var mathjaxBB = Drawing.bBox(mathjaxNode); + + height = mathjaxBB.height; + width = mathjaxBB.width; + + Drawing.setTranslate(mathjaxGroup, 0, (height / 4)); + } else { + var text = g.select('.legendtext'); + var textLines = svgTextUtils.lineCount(text); + var textNode = text.node(); + + height = lineHeight * textLines; + width = textNode ? Drawing.bBox(textNode).width : 0; + + // approximation to height offset to center the font + // to avoid getBoundingClientRect + var textY = lineHeight * (0.3 + (1 - textLines) / 2); + svgTextUtils.positionText(text, constants.textOffsetX, textY); + } + + legendItem.lineHeight = lineHeight; + legendItem.height = Math.max(height, 16) + 3; + legendItem.width = width; +} + +function computeLegendDimensions(gd, groups, traces) { + var fullLayout = gd._fullLayout; + var opts = fullLayout.legend; + var borderwidth = opts.borderwidth; + var isGrouped = helpers.isGrouped(opts); + + var extraWidth = 0; + + var traceGap = 5; + + opts._width = 0; + opts._height = 0; + + if(helpers.isVertical(opts)) { + if(isGrouped) { + groups.each(function(d, i) { + Drawing.setTranslate(this, 0, i * opts.tracegroupgap); + }); + } + + traces.each(function(d) { + var legendItem = d[0]; + var textHeight = legendItem.height; + var textWidth = legendItem.width; + + Drawing.setTranslate(this, + borderwidth, + (5 + borderwidth + opts._height + textHeight / 2)); + + opts._height += textHeight; + opts._width = Math.max(opts._width, textWidth); + }); + + opts._width += 45 + borderwidth * 2; + opts._height += 10 + borderwidth * 2; + + if(isGrouped) { + opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap; + } + + extraWidth = 40; + } else if(isGrouped) { + var maxHeight = 0; + var maxWidth = 0; + var groupData = groups.data(); + + var maxItems = 0; + + var i; + for(i = 0; i < groupData.length; i++) { + var group = groupData[i]; + var groupWidths = group.map(function(legendItemArray) { + return legendItemArray[0].width; + }); + + var groupWidth = Lib.aggNums(Math.max, null, groupWidths); + var groupHeight = group.reduce(function(a, b) { + return a + b[0].height; + }, 0); + + maxWidth = Math.max(maxWidth, groupWidth); + maxHeight = Math.max(maxHeight, groupHeight); + maxItems = Math.max(maxItems, group.length); + } + + maxWidth += traceGap; + maxWidth += 40; + + var groupXOffsets = [opts._width]; + var groupYOffsets = []; + var rowNum = 0; + for(i = 0; i < groupData.length; i++) { + if(fullLayout._size.w < (borderwidth + opts._width + traceGap + maxWidth)) { + groupXOffsets[groupXOffsets.length - 1] = groupXOffsets[0]; + opts._width = maxWidth; + rowNum++; + } else { + opts._width += maxWidth + borderwidth; + } + + var rowYOffset = (rowNum * maxHeight); + rowYOffset += rowNum > 0 ? opts.tracegroupgap : 0; + + groupYOffsets.push(rowYOffset); + groupXOffsets.push(opts._width); + } + + groups.each(function(d, i) { + Drawing.setTranslate(this, groupXOffsets[i], groupYOffsets[i]); + }); + + groups.each(function() { + var group = d3.select(this); + var groupTraces = group.selectAll('g.traces'); + var groupHeight = 0; + + groupTraces.each(function(d) { + var legendItem = d[0]; + var textHeight = legendItem.height; + + Drawing.setTranslate(this, + 0, + (5 + borderwidth + groupHeight + textHeight / 2)); + + groupHeight += textHeight; + }); + }); + + var maxYLegend = groupYOffsets[groupYOffsets.length - 1] + maxHeight; + opts._height = 10 + (borderwidth * 2) + maxYLegend; + + var maxOffset = Math.max.apply(null, groupXOffsets); + opts._width = maxOffset + maxWidth + 40; + opts._width += borderwidth * 2; + } else { + var rowHeight = 0; + var maxTraceHeight = 0; + var maxTraceWidth = 0; + var offsetX = 0; + var fullTracesWidth = 0; + + // calculate largest width for traces and use for width of all legend items + traces.each(function(d) { + maxTraceWidth = Math.max(40 + d[0].width, maxTraceWidth); + fullTracesWidth += 40 + d[0].width + traceGap; + }); + + // check if legend fits in one row + var oneRowLegend = fullLayout._size.w > borderwidth + fullTracesWidth - traceGap; + + traces.each(function(d) { + var legendItem = d[0]; + var traceWidth = oneRowLegend ? 40 + d[0].width : maxTraceWidth; + + if((borderwidth + offsetX + traceGap + traceWidth) > fullLayout._size.w) { + offsetX = 0; + rowHeight += maxTraceHeight; + opts._height += maxTraceHeight; + // reset for next row + maxTraceHeight = 0; + } + + Drawing.setTranslate(this, + (borderwidth + offsetX), + (5 + borderwidth + legendItem.height / 2) + rowHeight); + + opts._width += traceGap + traceWidth; + + // keep track of tallest trace in group + offsetX += traceGap + traceWidth; + maxTraceHeight = Math.max(legendItem.height, maxTraceHeight); + }); + + if(oneRowLegend) { + opts._height = maxTraceHeight; + } else { + opts._height += maxTraceHeight; + } + + opts._width += borderwidth * 2; + opts._height += 10 + borderwidth * 2; + } + + // make sure we're only getting full pixels + opts._width = Math.ceil(opts._width); + opts._height = Math.ceil(opts._height); + + var isEditable = ( + gd._context.edits.legendText || + gd._context.edits.legendPosition + ); + + traces.each(function(d) { + var legendItem = d[0]; + var bg = d3.select(this).select('.legendtoggle'); + + Drawing.setRect(bg, + 0, + -legendItem.height / 2, + (isEditable ? 0 : opts._width) + extraWidth, + legendItem.height + ); + }); +} + +function expandMargin(gd) { + var fullLayout = gd._fullLayout; + var opts = fullLayout.legend; + + var xanchor = 'left'; + if(Lib.isRightAnchor(opts)) { + xanchor = 'right'; + } else if(Lib.isCenterAnchor(opts)) { + xanchor = 'center'; + } + + var yanchor = 'top'; + if(Lib.isBottomAnchor(opts)) { + yanchor = 'bottom'; + } else if(Lib.isMiddleAnchor(opts)) { + yanchor = 'middle'; + } + + // lastly check if the margin auto-expand has changed + Plots.autoMargin(gd, 'legend', { + x: opts.x, + y: opts.y, + l: opts._width * (FROM_TL[xanchor]), + r: opts._width * (FROM_BR[xanchor]), + b: opts._height * (FROM_BR[yanchor]), + t: opts._height * (FROM_TL[yanchor]) + }); +} + +function expandHorizontalMargin(gd) { + var fullLayout = gd._fullLayout; + var opts = fullLayout.legend; + + var xanchor = 'left'; + if(Lib.isRightAnchor(opts)) { + xanchor = 'right'; + } else if(Lib.isCenterAnchor(opts)) { + xanchor = 'center'; + } + + // lastly check if the margin auto-expand has changed + Plots.autoMargin(gd, 'legend', { + x: opts.x, + y: 0.5, + l: opts._width * (FROM_TL[xanchor]), + r: opts._width * (FROM_BR[xanchor]), + b: 0, + t: 0 + }); +} + +},{"../../constants/alignment":145,"../../lib":169,"../../lib/events":163,"../../lib/svg_text_utils":190,"../../plots/plots":245,"../../registry":257,"../color":50,"../dragelement":68,"../drawing":71,"./constants":100,"./get_legend_data":103,"./handle_click":104,"./helpers":105,"./style":107,"d3":15}],103:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Registry = _dereq_('../../registry'); +var helpers = _dereq_('./helpers'); + +module.exports = function getLegendData(calcdata, opts) { + var lgroupToTraces = {}; + var lgroups = []; + var hasOneNonBlankGroup = false; + var slicesShown = {}; + var lgroupi = 0; + var i, j; + + function addOneItem(legendGroup, legendItem) { + // each '' legend group is treated as a separate group + if(legendGroup === '' || !helpers.isGrouped(opts)) { + var uniqueGroup = '~~i' + lgroupi; // TODO: check this against fullData legendgroups? + + 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(!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; + } + } + } else addOneItem(lgroup, cd0); + } + + // won't draw a legend in this case + if(!lgroups.length) return []; + + // rearrange lgroupToTraces into a d3-friendly array of arrays + var lgroupsLength = lgroups.length; + var ltraces; + var legendData; + + if(hasOneNonBlankGroup && helpers.isGrouped(opts)) { + legendData = new Array(lgroupsLength); + + for(i = 0; i < lgroupsLength; i++) { + ltraces = lgroupToTraces[lgroups[i]]; + legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces; + } + } else { + // collapse all groups into one if all groups are blank + legendData = [new Array(lgroupsLength)]; + + for(i = 0; i < lgroupsLength; i++) { + ltraces = lgroupToTraces[lgroups[i]][0]; + legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces; + } + lgroupsLength = 1; + } + + // needed in repositionLegend + opts._lgroupsLength = lgroupsLength; + return legendData; +}; + +},{"../../registry":257,"./helpers":105}],104:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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]; + 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, otherState; + var isIsolated = true; + for(i = 0; i < fullData.length; i++) { + isClicked = fullData[i] === fullTrace; + if(isClicked) 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; + isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup); + setVisibility(fullData[i], isInGroup ? 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":169,"../../registry":257}],105:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; +}; + +},{}],106:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + + +module.exports = { + moduleType: 'component', + name: 'legend', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw'), + style: _dereq_('./style') +}; + +},{"./attributes":99,"./defaults":101,"./draw":102,"./style":107}],107:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); + +var subTypes = _dereq_('../../traces/scatter/subtypes'); +var stylePie = _dereq_('../../traces/pie/style_one'); +var pieCastOption = _dereq_('../../traces/pie/helpers').castOption; + +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) { + var fullLayout = gd._fullLayout; + var legend = fullLayout.legend; + var constantItemSizing = legend.itemsizing === 'constant'; + + function boundLineWidth(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', 'translate(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(styleWaterfalls) + .each(styleFunnels) + .each(styleBars) + .each(styleBoxes) + .each(styleFunnelareas) + .each(stylePies) + .each(styleLines) + .each(stylePoints) + .each(styleCandles) + .each(styleOHLC); + + function styleLines(d) { + var d0 = d[0]; + var trace = d0.trace; + var showFill = trace.visible && trace.fill && trace.fill !== 'none'; + var showLine = subTypes.hasLines(trace); + var contours = trace.contours; + var showGradientLine = false; + var showGradientFill = false; + var dMod, tMod; + + 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; + } + } + + // with fill and no markers or text, move the line and fill up a bit + // so it's more centered + var markersOrText = subTypes.hasMarkers(trace) || subTypes.hasText(trace); + var anyFill = showFill || showGradientFill; + var anyLine = showLine || showGradientLine; + var pathStart = (markersOrText || !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 + 'h30v6h-30z') + .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 ? 'l30,0.0001' : 'h30')) + .call(showLine ? Drawing.lineGroupStyle : lineGradient); + + function fillGradient(s) { + if(s.size()) { + var gradientID = 'legendfill-' + trace.uid; + Drawing.gradient(s, gd, gradientID, 'horizontalreversed', + trace.colorscale, 'fill'); + } + } + + function lineGradient(s) { + if(s.size()) { + var gradientID = 'legendline-' + trace.uid; + Drawing.lineGroupStyle(s); + Drawing.gradient(s, gd, gradientID, 'horizontalreversed', + trace.colorscale, 'stroke'); + } + } + } + + function stylePoints(d) { + var d0 = d[0]; + var trace = d0.trace; + var showMarkers = subTypes.hasMarkers(trace); + var showText = subTypes.hasText(trace); + var showLines = subTypes.hasLines(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) { return array[0]; } + + // constrain text, markers, etc so they'll fit on the legend + if(showMarkers || showText || showLines) { + var dEdit = {}; + var tEdit = {}; + + if(showMarkers) { + 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(showLines) { + 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; + } + + var ptgroup = d3.select(this).select('g.legendpoints'); + + var pts = ptgroup.selectAll('path.scatterpts') + .data(showMarkers ? 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', 'translate(20,0)'); + pts.exit().remove(); + pts.call(Drawing.pointStyle, tMod, gd); + + // 'mrc' is set in pointStyle and used in textPointStyle: + // constrain it here + if(showMarkers) dMod[0].mrc = 3; + + var txt = ptgroup.selectAll('g.pointtext') + .data(showText ? dMod : []); + txt.enter() + .append('g').classed('pointtext', true) + .append('text').attr('transform', 'translate(20,0)'); + txt.exit().remove(); + txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd); + } + + function styleWaterfalls(d) { + var trace = d[0].trace; + + var ptsData = []; + if(trace.type === 'waterfall' && trace.visible) { + 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', 'translate(20,0)') + .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.type === desiredType && trace.visible); + + 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', 'translate(20,0)'); + 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') + .call(Color.fill, d0.mc || marker.color); + + 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(Registry.traceIs(trace, 'box-violin') && trace.visible ? [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', 'translate(20,0)'); + 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.type === 'candlestick' && trace.visible ? [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', 'translate(20,0)') + .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.type === 'ohlc' && trace.visible ? [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', 'translate(20,0)') + .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.type === desiredType && trace.visible); + + 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', 'translate(20,0)'); + 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); + } + } +}; + +},{"../../lib":169,"../../registry":257,"../../traces/pie/helpers":358,"../../traces/pie/style_one":364,"../../traces/scatter/subtypes":389,"../color":50,"../drawing":71,"d3":15}],108:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); +var Lib = _dereq_('../../lib'); +var Icons = _dereq_('../../fonts/ploticon'); + +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', + title: function(gd) { return _(gd, 'Zoom'); }, + attr: 'dragmode', + val: 'zoom', + icon: Icons.zoombox, + click: handleCartesian +}; + +modeBarButtons.pan2d = { + name: 'pan2d', + title: function(gd) { return _(gd, 'Pan'); }, + attr: 'dragmode', + val: 'pan', + icon: Icons.pan, + click: handleCartesian +}; + +modeBarButtons.select2d = { + name: 'select2d', + title: function(gd) { return _(gd, 'Box Select'); }, + attr: 'dragmode', + val: 'select', + icon: Icons.selectbox, + click: handleCartesian +}; + +modeBarButtons.lasso2d = { + name: 'lasso2d', + title: function(gd) { return _(gd, 'Lasso Select'); }, + attr: 'dragmode', + val: 'lasso', + icon: Icons.lasso, + click: handleCartesian +}; + +modeBarButtons.zoomIn2d = { + name: 'zoomIn2d', + title: function(gd) { return _(gd, 'Zoom in'); }, + attr: 'zoom', + val: 'in', + icon: Icons.zoom_plus, + click: handleCartesian +}; + +modeBarButtons.zoomOut2d = { + name: 'zoomOut2d', + title: function(gd) { return _(gd, 'Zoom out'); }, + attr: 'zoom', + val: 'out', + icon: Icons.zoom_minus, + click: handleCartesian +}; + +modeBarButtons.autoScale2d = { + name: 'autoScale2d', + title: function(gd) { return _(gd, 'Autoscale'); }, + attr: 'zoom', + val: 'auto', + icon: Icons.autoscale, + click: handleCartesian +}; + +modeBarButtons.resetScale2d = { + name: 'resetScale2d', + title: function(gd) { return _(gd, 'Reset axes'); }, + attr: 'zoom', + val: 'reset', + icon: Icons.home, + click: handleCartesian +}; + +modeBarButtons.hoverClosestCartesian = { + name: 'hoverClosestCartesian', + 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', + 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 = 'on'; + + 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]; + } + 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]); + } + } + } + fullLayout._cartesianSpikesEnabled = allSpikesEnabled; + } 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); + } else if(astr === 'hovermode' && val === 'closest') { + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + if(allSpikesEnabled === 'on' && !ax.showspikes) { + allSpikesEnabled = 'off'; + } + } + fullLayout._cartesianSpikesEnabled = allSpikesEnabled; + } + + aobj[astr] = val; + } + + Registry.call('_guiRelayout', gd, aobj); +} + +modeBarButtons.zoom3d = { + name: 'zoom3d', + title: function(gd) { return _(gd, 'Zoom'); }, + attr: 'scene.dragmode', + val: 'zoom', + icon: Icons.zoombox, + click: handleDrag3d +}; + +modeBarButtons.pan3d = { + name: 'pan3d', + 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', + title: function(gd) { return _(gd, 'Reset camera to default'); }, + attr: 'resetDefault', + icon: Icons.home, + click: handleCamera3d +}; + +modeBarButtons.resetCameraLastSave3d = { + name: 'resetCameraLastSave3d', + 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 fullLayout = gd._fullLayout; + var sceneIds = fullLayout._subplots.gl3d; + var aobj = {}; + + for(var i = 0; i < sceneIds.length; i++) { + var sceneId = sceneIds[i]; + var key = sceneId + '.camera'; + var scene = fullLayout[sceneId]._scene; + + if(attr === 'resetLastSave') { + aobj[key + '.up'] = scene.viewInitial.up; + aobj[key + '.eye'] = scene.viewInitial.eye; + aobj[key + '.center'] = scene.viewInitial.center; + } else if(attr === 'resetDefault') { + aobj[key + '.up'] = null; + aobj[key + '.eye'] = null; + aobj[key + '.center'] = null; + } + } + + Registry.call('_guiRelayout', gd, aobj); +} + +modeBarButtons.hoverClosest3d = { + name: 'hoverClosest3d', + 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', + title: function(gd) { return _(gd, 'Zoom in'); }, + attr: 'zoom', + val: 'in', + icon: Icons.zoom_plus, + click: handleGeo +}; + +modeBarButtons.zoomOutGeo = { + name: 'zoomOutGeo', + title: function(gd) { return _(gd, 'Zoom out'); }, + attr: 'zoom', + val: 'out', + icon: Icons.zoom_minus, + click: handleGeo +}; + +modeBarButtons.resetGeo = { + name: 'resetGeo', + title: function(gd) { return _(gd, 'Reset'); }, + attr: 'reset', + val: null, + icon: Icons.autoscale, + click: handleGeo +}; + +modeBarButtons.hoverClosestGeo = { + name: 'hoverClosestGeo', + 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); + } else if(attr === 'reset') { + resetView(gd, 'geo'); + } + } +} + +modeBarButtons.hoverClosestGl2d = { + name: 'hoverClosestGl2d', + 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', + 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; + + fullLayout._cartesianSpikesEnabled = fullLayout._cartesianSpikesEnabled === 'on' ? 'off' : 'on'; + + var aobj = setSpikelineVisibility(gd); + + Registry.call('_guiRelayout', gd, aobj); + } +}; + +function setSpikelineVisibility(gd) { + var fullLayout = gd._fullLayout; + var axList = axisIds.list(gd, null, true); + var aobj = {}; + + var ax, axName; + + for(var i = 0; i < axList.length; i++) { + ax = axList[i]; + axName = ax._name; + aobj[axName + '.showspikes'] = fullLayout._cartesianSpikesEnabled === 'on' ? true : ax._showSpikeInitial; + } + + return aobj; +} + +modeBarButtons.resetViewMapbox = { + name: 'resetViewMapbox', + title: function(gd) { return _(gd, 'Reset view'); }, + attr: 'reset', + icon: Icons.home, + click: function(gd) { + resetView(gd, 'mapbox'); + } +}; + +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":153,"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../plots/plots":245,"../../registry":257}],109:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +exports.manage = _dereq_('./manage'); + +},{"./manage":110}],110:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var axisIds = _dereq_('../../plots/cartesian/axis_ids'); +var scatterSubTypes = _dereq_('../../traces/scatter/subtypes'); +var Registry = _dereq_('../../registry'); + +var createModeBar = _dereq_('./modebar'); +var modeBarButtons = _dereq_('./buttons'); + +/** + * 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; + var buttonsToRemove = context.modeBarButtonsToRemove; + var buttonsToAdd = context.modeBarButtonsToAdd; + + 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 groups = []; + + function addGroup(newGroup) { + if(!newGroup.length) return; + + var out = []; + + for(var i = 0; i < newGroup.length; i++) { + var button = newGroup[i]; + if(buttonsToRemove.indexOf(button) !== -1) continue; + out.push(modeBarButtons[button]); + } + + 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) { + 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)) { + 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'); + } + + addGroup(dragModeGroup); + addGroup(zoomGroup.concat(resetGroup)); + addGroup(hoverGroup); + + 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":216,"../../registry":257,"../../traces/scatter/subtypes":389,"./buttons":108,"./modebar":111}],111:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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://plot.ly/'; + 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":153,"../../lib":169,"d3":15,"fast-isnumeric":17}],112:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":203,"../../plots/font_attributes":239,"../color/attributes":49}],113:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 +}; + +},{}],114:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plot_api/plot_template":203,"../../plots/array_container_defaults":209,"../color":50,"./attributes":112,"./constants":113}],115:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var Registry = _dereq_('../../registry'); +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 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.classed('user-select-none', true) + .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', 'translate(' + + (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', 'translate(' + lx + ',' + ly + ')'); +} + +},{"../../constants/alignment":145,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axis_ids":216,"../../plots/plots":245,"../../registry":257,"../color":50,"../drawing":71,"./constants":113,"./get_update_object":116,"d3":15}],116:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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]; +} + +},{"d3":15}],117:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + moduleType: 'component', + name: 'rangeselector', + + schema: { + subplots: { + xaxis: {rangeselector: _dereq_('./attributes')} + } + }, + + layoutAttributes: _dereq_('./attributes'), + handleDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw') +}; + +},{"./attributes":112,"./defaults":114,"./draw":115}],118:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":49}],119:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":212,"../../plots/cartesian/axis_ids":216,"./constants":120}],120:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 +}; + +},{}],121:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plot_api/plot_template":203,"../../plots/cartesian/axis_ids":216,"./attributes":118,"./oppaxis_attributes":125}],122:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var Registry = _dereq_('../../registry'); +var Plots = _dereq_('../../plots/plots'); + +var Lib = _dereq_('../../lib'); +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; + var tickHeight = opts._tickHeight; + + var oppBottom = opts._oppBottom; + + 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 - oppBottom) + + tickHeight + + opts._offsetShift + constants.extraPad + ); + + rangeSlider.attr('transform', 'translate(' + x + ',' + y + ')'); + + // update data <--> pixel coordinate conversion methods + + var range0 = axisOpts.r2l(opts.range[0]); + var range1 = axisOpts.r2l(opts.range[1]); + var dist = range1 - range0; + + opts.p2d = function(v) { + return (v / opts._width) * dist + range0; + }; + + opts.d2p = function(v) { + return (v - range0) / dist * opts._width; + }; + + opts._rl = [range0, range1]; + + 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) { + var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node(); + var grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node(); + var grabAreaMax = rangeSlider.select('rect.' + constants.grabAreaMaxClassName).node(); + + rangeSlider.on('mousedown', function() { + var event = d3.event; + var target = event.target; + var startX = event.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(); + + dragCover.addEventListener('mousemove', mouseMove); + dragCover.addEventListener('mouseup', mouseUp); + + function mouseMove(e) { + var delta = +e.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); + Lib.removeElement(dragCover); + } + }); +} + +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', 'translate(' + xMin + ',' + offset + ')'); + + rangeSlider.select('g.' + constants.grabberMaxClassName) + .attr('transform', 'translate(' + 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: 'translate(' + 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 + }; + + mockFigure.layout[oppAxisName] = { + type: oppAxisOpts.type, + domain: [0, 1], + range: oppAxisRangeOpts.rangemode !== 'match' ? oppAxisRangeOpts.range.slice() : oppAxisOpts.range.slice(), + calendar: oppAxisOpts.calendar + }; + + 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); + + // + if(gd._context.staticPlot) return; + + var grabAreaFixAttrs = { + width: constants.grabAreaWidth, + x: 0, + y: 0, + fill: constants.grabAreaFill, + cursor: constants.grabAreaCursor + }; + + 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":169,"../../lib/setcursor":188,"../../plots/cartesian":224,"../../plots/cartesian/axis_ids":216,"../../plots/plots":245,"../../registry":257,"../color":50,"../dragelement":68,"../drawing":71,"../titles":138,"./constants":120,"d3":15}],123:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var axisIDs = _dereq_('../../plots/cartesian/axis_ids'); +var constants = _dereq_('./constants'); +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 opts = ax[name]; + + var oppBottom = Infinity; + var counterAxes = ax._counterAxes; + for(var j = 0; j < counterAxes.length; j++) { + var counterId = counterAxes[j]; + var oppAxis = axisIDs.getFromId(gd, counterId); + oppBottom = Math.min(oppBottom, oppAxis.domain[0]); + } + opts._oppBottom = oppBottom; + + var tickHeight = (ax.side === 'bottom' && ax._boundingBox.height) || 0; + opts._tickHeight = tickHeight; + + return { + x: 0, + y: oppBottom, + l: 0, + r: 0, + t: 0, + b: opts._height + gd._fullLayout.margin.b + tickHeight, + pad: constants.extraPad + opts._offsetShift * 2 + }; +}; + +},{"../../plots/cartesian/axis_ids":216,"./constants":120}],124:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./attributes":118,"./calc_autorange":119,"./defaults":121,"./draw":122,"./helpers":123,"./oppaxis_attributes":125}],125:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' +}; + +},{}],126:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + +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', + + }, + editType: 'arraydraw' +}); + +},{"../../lib/extend":164,"../../plot_api/plot_template":203,"../../traces/scatter/attributes":366,"../annotations/attributes":35,"../drawing/attributes":70}],127:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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, bounds; + + if(shape.xref !== 'paper') { + 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') { + 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":169,"../../plots/cartesian/axes":213,"./constants":128,"./helpers":131}],128:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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 + } +}; + +},{}],129:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; + + coerce('layer'); + coerce('opacity'); + coerce('fillcolor'); + coerce('line.color'); + coerce('line.width'); + coerce('line.dash'); + + var dfltType = shapeIn.path ? 'path' : 'rect'; + var shapeType = coerce('type', dfltType); + 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, '', 'paper'); + + if(axRef !== 'paper') { + 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 V2.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":169,"../../plots/array_container_defaults":209,"../../plots/cartesian/axes":213,"./attributes":126,"./helpers":131}],130:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); +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 +}; + +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 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 options = gd._fullLayout.shapes[index] || {}; + + // 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 { + var plotinfo = gd._fullLayout._plots[options.xref + options.yref]; + if(plotinfo) { + 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 attrs = { + 'data-index': index, + 'fill-rule': 'evenodd', + d: getPathString(gd, options) + }; + var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)'; + + var path = shapeLayer.append('path') + .attr(attrs) + .style('opacity', options.opacity) + .call(Color.stroke, lineColor) + .call(Color.fill, options.fillcolor) + .call(Drawing.dashLine, options.line.dash, options.line.width); + + setClipPath(path, gd, options); + + if(gd._context.edits.shapePosition) setupDragElement(gd, path, options, index, shapeLayer); + } +} + +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 + var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, ''); + + Drawing.setClipUrl( + shapePath, + clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null, + gd + ); +} + +function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer) { + 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 editHelpers = arrayEditor(gd.layout, 'shapes', shapeOptions); + 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 ya = Axes.getFromId(gd, shapeOptions.yref); + var x2p = helpers.getDataToPixel(gd, xa); + var y2p = helpers.getDataToPixel(gd, ya, true); + var p2x = helpers.getPixelToData(gd, xa); + var p2y = helpers.getPixelToData(gd, ya, true); + + 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 = sensoryWidth / 2 > minSensoryWidth ? 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(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) { + // 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; + } + + function endDrag() { + 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() { + 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 newN = (~dragMode.indexOf('n')) ? n0 + dy : n0; + var newS = (~dragMode.indexOf('s')) ? s0 + dy : s0; + var newW = (~dragMode.indexOf('w')) ? w0 + dx : w0; + var newE = (~dragMode.indexOf('e')) ? e0 + dx : e0; + + // Do things in opposing direction for y-axis. + // Hint: for data-sized shapes the reversal of axis direction is done in p2y. + if(~dragMode.indexOf('n') && yPixelSized) newN = n0 - dy; + if(~dragMode.indexOf('s') && yPixelSized) 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 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) { + 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) { + 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; + }); +} + +},{"../../lib":169,"../../lib/setcursor":188,"../../plot_api/plot_template":203,"../../plots/cartesian/axes":213,"../../registry":257,"../color":50,"../dragelement":68,"../drawing":71,"./constants":128,"./helpers":131}],131:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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 V2.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) { + var gs = gd._fullLayout._size; + var dataToPixel; + + if(axis) { + 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) { + var gs = gd._fullLayout._size; + var pixelToData; + + if(axis) { + 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; +}; + +},{"../../lib":169,"./constants":128}],132:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var drawModule = _dereq_('./draw'); + +module.exports = { + moduleType: 'component', + name: 'shapes', + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + includeBasePlot: _dereq_('../../plots/cartesian/include_components')('shapes'), + + calcAutorange: _dereq_('./calc_autorange'), + draw: drawModule.draw, + drawOne: drawModule.drawOne +}; + +},{"../../plots/cartesian/include_components":223,"./attributes":126,"./calc_autorange":127,"./defaults":129,"./draw":130}],133:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":164,"../../plot_api/edit_types":196,"../../plot_api/plot_template":203,"../../plots/animation_attributes":208,"../../plots/font_attributes":239,"../../plots/pad_attributes":244,"./constants":134}],134:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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, +}; + +},{}],135:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/array_container_defaults":209,"./attributes":133,"./constants":134}],136:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 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.classed('user-select-none', true) + .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.classed('user-select-none', true) + .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 becasue of the transition duck-typing. + // It's also not necessary because there are no other transitions to preserve. + el.attr('transform', 'translate(' + (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":145,"../../lib":169,"../../lib/svg_text_utils":190,"../../plot_api/plot_template":203,"../../plots/plots":245,"../color":50,"../drawing":71,"./constants":134,"d3":15}],137:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var constants = _dereq_('./constants'); + +module.exports = { + moduleType: 'component', + name: constants.name, + + layoutAttributes: _dereq_('./attributes'), + supplyLayoutDefaults: _dereq_('./defaults'), + + draw: _dereq_('./draw') +}; + +},{"./attributes":133,"./constants":134,"./defaults":135,"./draw":136}],138:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var Plots = _dereq_('../../plots/plots'); +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../drawing'); +var Color = _dereq_('../color'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var interactConstants = _dereq_('../../constants/interactions'); + +module.exports = { + draw: draw +}; + +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 += 'translate(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 shift = 0; + var backside = { + left: 'right', + right: 'left', + top: 'bottom', + bottom: 'top' + }[avoid.side]; + var shiftSign = (['left', 'top'].indexOf(avoid.side) !== -1) ? + -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 || ( + (paperbb[avoid.side] - titlebb[avoid.side]) * + ((avoid.side === 'left' || avoid.side === 'top') ? -1 : 1)); + // 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', + 'translate(' + shiftTemplate + ')'); + } + } + } + + 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; +} + +},{"../../constants/interactions":148,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"../../registry":257,"../color":50,"../drawing":71,"d3":15,"fast-isnumeric":17}],139:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'} + ], + + }, + 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":164,"../../plot_api/edit_types":196,"../../plot_api/plot_template":203,"../../plots/font_attributes":239,"../../plots/pad_attributes":244,"../color/attributes":49}],140:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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: '▼' + } +}; + +},{}],141:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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('label'); + coerce('execute'); + } +} + +},{"../../lib":169,"../../plots/array_container_defaults":209,"./attributes":139,"./constants":140}],142:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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.classed('user-select-none', true) + .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; + + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex); + + if(buttonOpts.execute) { + 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.classed('user-select-none', true) + .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":145,"../../lib":169,"../../lib/svg_text_utils":190,"../../plot_api/plot_template":203,"../../plots/plots":245,"../color":50,"../drawing":71,"./constants":140,"./scrollbox":144,"d3":15}],143:[function(_dereq_,module,exports){ +arguments[4][137][0].apply(exports,arguments) +},{"./attributes":139,"./constants":140,"./defaults":141,"./draw":142,"dup":137}],144:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = ScrollBox; + +var d3 = _dereq_('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":169,"../color":50,"../drawing":71,"d3":15}],145:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' + } +}; + +},{}],146:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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-3.x-api-reference/blob/master/Time-Formatting.md#format' +}; + +},{}],147:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + COMPARISON_OPS: ['=', '!=', '<', '>=', '>', '<='], + COMPARISON_OPS2: ['=', '<', '>=', '>', '<='], + INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['], + SET_OPS: ['{}', '}{'], + CONSTRAINT_REDUCTION: { + // for contour constraints, open/closed endpoints are equivalent + '=': '=', + + '<': '<', + '<=': '<', + + '>': '>', + '>=': '>', + + '[]': '[]', + '()': '[]', + '[)': '[]', + '(]': '[]', + + '][': '][', + ')(': '][', + '](': '][', + ')[': '][' + } +}; + +},{}],148:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 +}; + +},{}],149:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 / 10000, + + /* + * 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 + */ + ONEAVGYEAR: 31557600000, // 365.25 days + ONEAVGMONTH: 2629800000, // 1/12 of ONEAVGYEAR + ONEDAY: 86400000, + 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' +}; + +},{}],150:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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 +}; + +},{}],151:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +// package version injected by `npm run preprocess` +exports.version = '1.49.4'; + +// inject promise polyfill +_dereq_('es6-promise').polyfill(); + +// inject plot css +_dereq_('../build/plotcss'); + +// inject default MathJax config +_dereq_('./fonts/mathjax_config')(); + +// 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/fx'), + _dereq_('./components/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') +]); + +// locales en and en-US are required for default behavior +register([ + _dereq_('./locale-en'), + _dereq_('./locale-en-us') +]); + +// plot icons +exports.Icons = _dereq_('./fonts/ploticon'); + +// unofficial 'beta' plot methods, use at your own risk +exports.Plots = _dereq_('./plots/plots'); +exports.Fx = _dereq_('./components/fx'); +exports.Snapshot = _dereq_('./snapshot'); +exports.PlotSchema = _dereq_('./plot_api/plot_schema'); +exports.Queue = _dereq_('./lib/queue'); + +// export d3 used in the bundle +exports.d3 = _dereq_('d3'); + +},{"../build/plotcss":1,"./components/annotations":43,"./components/annotations3d":48,"./components/colorbar":56,"./components/colorscale":62,"./components/errorbars":77,"./components/fx":89,"./components/grid":93,"./components/images":98,"./components/legend":106,"./components/rangeselector":117,"./components/rangeslider":124,"./components/shapes":132,"./components/sliders":137,"./components/updatemenus":143,"./fonts/mathjax_config":152,"./fonts/ploticon":153,"./lib/queue":183,"./locale-en":194,"./locale-en-us":193,"./plot_api":198,"./plot_api/plot_schema":202,"./plots/plots":245,"./registry":257,"./snapshot":262,"./traces/scatter":377,"d3":15,"es6-promise":16}],152:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* global MathJax:false */ + +module.exports = function() { + if(typeof MathJax !== 'undefined') { + var globalConfig = (window.PlotlyConfig || {}).MathJaxConfig !== 'local'; + + if(globalConfig) { + MathJax.Hub.Config({ + messageStyle: 'none', + skipStartupTypeset: true, + displayAlign: 'left', + tex2jax: { + inlineMath: [['$', '$'], ['\\(', '\\)']] + } + }); + MathJax.Hub.Configured(); + } + } +}; + +},{}],153:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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)' + }, + '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)' + }, + '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' + } +}; + +},{}],154:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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) + ); +}; + +},{}],155:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":176}],156:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isArray = Array.isArray; + +// IE9 fallbacks + +var ab = (typeof ArrayBuffer === 'undefined' || !ArrayBuffer.isView) ? + {isView: function() { return false; }} : + ArrayBuffer; + +var dv = (typeof DataView === 'undefined') ? + function() {} : + 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; +} + +},{}],157:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":149,"fast-isnumeric":17}],158:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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}); + }); + } +}; + +},{}],159:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + } +}; + +},{}],160:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var tinycolor = _dereq_('tinycolor2'); + +var baseTraceAttrs = _dereq_('../plots/attributes'); +var colorscales = _dereq_('../components/colorscale/scales'); +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; +}; + +/** 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/colorscale/scales":65,"../constants/interactions":148,"../plots/attributes":210,"./array":156,"./mod":176,"./nested_property":177,"./regex":184,"fast-isnumeric":17,"tinycolor2":33}],161:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); +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 = d3.time.format.utc; + +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 + * + * bool sunday is for week ticks, shift it to a Sunday. + */ +exports.dateTick0 = function(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, or [+\-]HH:?MM and we THROW IT AWAY + * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6 + * 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 http://support.microsoft.com/kb/244664: + * 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 + * http://www.filemaker.com/12help/html/add_view_data.4.21.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 = d3.time.format('%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 one item to + * d3's vocabulary: + * %{n}f where n is the max number of digits of fractional seconds + */ +var fracMatch = /%\d?f/g; +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)); + + 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":149,"../registry":257,"./loggers":173,"./mod":176,"d3":15,"fast-isnumeric":17}],162:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +var loggers = _dereq_('./loggers'); + +/** + * 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); +} + +module.exports = { + getGraphDiv: getGraphDiv, + isPlotDiv: isPlotDiv, + removeElement: removeElement, + addStyleRule: addStyleRule, + addRelatedStyleRule: addRelatedStyleRule, + deleteRelatedStyleRule: deleteRelatedStyleRule +}; + +},{"./loggers":173,"d3":15}],163:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":14}],164:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":170}],165:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; +}; + +},{}],166:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 + ); +} + +},{}],167:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":176}],168:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +// Simple helper functions +// none of these need any external deps + +module.exports = function identity(d) { return d; }; + +},{}],169:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +var isNumeric = _dereq_('fast-isnumeric'); + +var numConstants = _dereq_('../constants/numerical'); +var FP_SAFE = numConstants.FP_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.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.apply2DTransform = matrixModule.apply2DTransform; +lib.apply2DTransform2 = matrixModule.apply2DTransform2; + +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.clearResponsive = _dereq_('./clear_responsive'); + +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.cleanNumber = _dereq_('./clean_number'); + +lib.ensureNumber = function ensureNumber(v) { + if(!isNumeric(v)) return BADNUM; + v = Number(v); + if(v < -FP_SAFE || v > FP_SAFE) return BADNUM; + return isNumeric(v) ? Number(v) : BADNUM; +}; + +/** + * 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) { + var len = array.length; + var out = new Array(len); + for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2); + 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)) { + 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(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_IE9_OR_BELOW_REGEX = /MSIE [1-9]\./; +lib.isIE9orBelow = function() { + return lib.isIE() && IS_IE9_OR_BELOW_REGEX.test(window.navigator.userAgent); +}; + +var IS_SAFARI_REGEX = /Version\/[\d\.]+.*Safari/; +lib.isSafari = function() { + return IS_SAFARI_REGEX.test(window.navigator.userAgent); +}; + +/** + * Duck typing to recognize a d3 selection, mostly for IE9's benefit + * because it doesn't handle instanceof like modern browsers + */ +lib.isD3Selection = function(obj) { + return obj && (typeof obj.classed === 'function'); +}; + +/** + * 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 mulitple mutliply-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) { + if(SIMPLE_PROPERTY_REGEX.test(key)) { + return obj[key] || ''; + } + getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get; + return getterCache[key]() || ''; + }); +}; + +var TEMPLATE_STRING_FORMAT_SEPARATOR = /^:/; +var numberOfHoverTemplateWarnings = 0; +var maximumNumberOfHoverTemplateWarnings = 10; +/** + * 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 {obj} d3 locale + * @param {string} input string containing %{...:...} template strings + * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'} + * @param {obj} data objects containing substitution values + * + * @return {string} templated string + */ +lib.hovertemplateString = function(string, labels, d3locale) { + var args = arguments; + // 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, key, format) { + var obj, value, i; + for(i = 3; i < args.length; i++) { + obj = args[i]; + if(obj.hasOwnProperty(key)) { + value = obj[key]; + break; + } + + if(!SIMPLE_PROPERTY_REGEX.test(key)) { + value = getterCache[key] || lib.nestedProperty(obj, key).get(); + if(value) getterCache[key] = value; + } + if(value !== undefined) break; + } + + if(value === undefined) { + if(numberOfHoverTemplateWarnings < maximumNumberOfHoverTemplateWarnings) { + lib.warn('Variable \'' + key + '\' in hovertemplate could not be found!'); + value = match; + } + + if(numberOfHoverTemplateWarnings === maximumNumberOfHoverTemplateWarnings) { + lib.warn('Too many hovertemplate warnings - additional warnings will be suppressed'); + } + numberOfHoverTemplateWarnings++; + } + + if(format) { + var fmt; + if(d3locale) { + fmt = d3locale.numberFormat; + } else { + fmt = d3.format; + } + value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value); + } else { + if(labels.hasOwnProperty(key + 'Label')) value = labels[key + 'Label']; + } + 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; +}; + +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'; +}; + +},{"../constants/numerical":149,"./anchor_utils":154,"./angles":155,"./array":156,"./clean_number":157,"./clear_responsive":159,"./coerce":160,"./dates":161,"./dom":162,"./extend":164,"./filter_unique":165,"./filter_visible":166,"./geometry2d":167,"./identity":168,"./is_plain_object":170,"./keyed_container":171,"./localize":172,"./loggers":173,"./make_trace_groups":174,"./matrix":175,"./mod":176,"./nested_property":177,"./noop":178,"./notifier":179,"./push_unique":182,"./regex":184,"./relative_attr":185,"./relink_private":186,"./search":187,"./stats":189,"./throttle":191,"./to_log_range":192,"d3":15,"fast-isnumeric":17}],170:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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) === Object.prototype + ); +}; + +},{}],171:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":177}],172:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":257}],173:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/* eslint-disable no-console */ + +var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig; + +var loggers = module.exports = {}; + +/** + * ------------------------------------------ + * debugging tools + * ------------------------------------------ + */ + +loggers.log = function() { + if(dfltConfig.logging > 1) { + var messages = ['LOG:']; + + for(var i = 0; i < arguments.length; i++) { + messages.push(arguments[i]); + } + + apply(console.trace || console.log, messages); + } +}; + +loggers.warn = function() { + if(dfltConfig.logging > 0) { + var messages = ['WARN:']; + + for(var i = 0; i < arguments.length; i++) { + messages.push(arguments[i]); + } + + apply(console.trace || console.log, messages); + } +}; + +loggers.error = function() { + if(dfltConfig.logging > 0) { + var messages = ['ERROR:']; + + for(var i = 0; i < arguments.length; i++) { + messages.push(arguments[i]); + } + + apply(console.error, messages); + } +}; + +/* + * Robust apply, for IE9 where console.log doesn't support + * apply like other functions do + */ +function apply(f, args) { + if(f && f.apply) { + try { + // `this` should always be console, since here we're always + // applying a method of the console object. + f.apply(console, args); + return; + } catch(e) { /* in case apply failed, fall back on the code below */ } + } + + // no apply - just try calling the function on each arg independently + for(var i = 0; i < args.length; i++) { + try { + f(args[i]); + } catch(e) { + // still fails - last resort simple console.log + console.log(args[i]); + } + } +} + +},{"../plot_api/plot_config":201}],174:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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; +}; + +},{"d3":15}],175:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + + +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 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))); + }; +}; + +},{}],176:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 +}; + +},{}],177:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":156,"fast-isnumeric":17}],178:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +// Simple helper functions +// none of these need any external deps + +module.exports = function noop() {}; + +},{}],179:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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]); + } + + note.transition() + .duration(700) + .style('opacity', 1) + .transition() + .delay(ts) + .call(killNote); + }); +}; + +},{"d3":15,"fast-isnumeric":17}],180:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":188}],181:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":149,"./matrix":175}],182:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],183:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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.framework && gd.framework.isPolar) { + gd.framework.undo(); + return; + } + 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.framework && gd.framework.isPolar) { + gd.framework.redo(); + return; + } + 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":169,"../plot_api/plot_config":201}],184:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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); +}; + +},{}],185:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; +}; + +},{}],186:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":156,"./is_plain_object":170}],187:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var loggers = _dereq_('./loggers'); +var identity = _dereq_('./identity'); + +// 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); + + var l = vals.length - 1; + var minDiff = (vals[l] - vals[0]) || 1; + var errDiff = minDiff / (l || 1) / 10000; + var v2 = [vals[0]]; + + for(var i = 0; i < l; i++) { + // make sure values aren't just off by a rounding error + if(vals[i + 1] > vals[i] + errDiff) { + minDiff = Math.min(minDiff, vals[i + 1] - vals[i]); + v2.push(vals[i + 1]); + } + } + + return {vals: v2, 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; +}; + +},{"./identity":168,"./loggers":173,"fast-isnumeric":17}],188:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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); +}; + +},{}],189:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":156,"fast-isnumeric":17}],190:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +/* global MathJax:false */ + +var d3 = _dereq_('d3'); + +var Lib = _dereq_('../lib'); +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')] + + ') translate(' + [-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 = /(\r\n?|\n)/g; + +var SPLIT_TAGS = /(<[^<>]*>)/; + +var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i; + +var BR_TAG = //i; + +/* + * 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; +} + +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(); + this.style({ + top: (getTop() - cRect.top) + 'px', + left: (getLeft() - cRect.left) + 'px', + 'z-index': 1000 + }); + return this; + }; +} + +/* + * 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":145,"../constants/xmlns_namespaces":150,"../lib":169,"d3":15}],191:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + } +} + +},{}],192:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":17}],193:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' + } +}; + +},{}],194:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' + } +}; + +},{}],195:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":257}],196:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169}],197:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 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); + } +} + +/** + * 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":50,"../lib":169,"../plots/cartesian/axis_ids":216,"../plots/plots":245,"../registry":257,"fast-isnumeric":17,"gl-mat4/fromQuat":18}],198:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var main = _dereq_('./plot_api'); + +exports.plot = main.plot; +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":259,"./plot_api":200,"./template_api":205,"./to_image":206,"./validate":207}],199:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":170,"../lib/loggers":173,"../lib/noop":178,"../lib/search":187,"../registry":257,"./container_array_match":195}],200:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 Polar = _dereq_('../plots/polar/legacy'); + +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 svgTextUtils = _dereq_('../lib/svg_text_utils'); +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; + +/** + * Main 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 plot(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 Plotly.plot 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'); + + // Legacy polar plots + if(!fullLayout._has('polar') && data && data[0] && data[0].r) { + Lib.log('Legacy polar charts are deprecated!'); + return plotLegacyPolar(gd, data, layout); + } + + // so we don't try to re-call Plotly.plot from inside + // legend and colorbar, if margins changed + fullLayout._replotting = true; + + // make or remake the framework if we need to + if(graphWasEmpty) makePlotFramework(gd); + + // polar need a different framework + if(gd.framework !== makePlotFramework) { + gd.framework = makePlotFramework; + makePlotFramework(gd); + } + + // clear gradient defs on each .plot call, because we know we'll loop through all traces + Drawing.initGradients(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 Plotly.plot + 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); + + 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); + + 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, + Plots.previousPromises + ); + + // 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; + } +} + +function plotLegacyPolar(gd, data, layout) { + // build or reuse the container skeleton + var plotContainer = d3.select(gd).selectAll('.plot-container') + .data([0]); + plotContainer.enter() + .insert('div', ':first-child') + .classed('plot-container plotly', true); + var paperDiv = plotContainer.selectAll('.svg-container') + .data([0]); + paperDiv.enter().append('div') + .classed('svg-container', true) + .style('position', 'relative'); + + // empty it everytime for now + paperDiv.html(''); + + // fulfill gd requirements + if(data) gd.data = data; + if(layout) gd.layout = layout; + Polar.manager.fillLayout(gd); + + // resize canvas + paperDiv.style({ + width: gd._fullLayout.width + 'px', + height: gd._fullLayout.height + 'px' + }); + + // instantiate framework + gd.framework = Polar.manager.framework(gd); + + // plot + gd.framework({data: gd.data, layout: gd.layout}, paperDiv.node()); + + // set undo point + gd.framework.setUndoPoint(); + + // get the resulting svg for extending it + var polarPlotSVG = gd.framework.svg(); + + // editable title + var opacity = 1; + var txt = gd._fullLayout.title ? gd._fullLayout.title.text : ''; + if(txt === '' || !txt) opacity = 0; + + var titleLayout = function() { + this.call(svgTextUtils.convertToTspans, gd); + // TODO: html/mathjax + // TODO: center title + }; + + var title = polarPlotSVG.select('.title-group text') + .call(titleLayout); + + if(gd._context.edits.titleText) { + var placeholderText = Lib._(gd, 'Click to enter Plot title'); + if(!txt || txt === placeholderText) { + opacity = 0.2; + // placeholder is not going through convertToTspans + // so needs explicit data-unformatted + title.attr({'data-unformatted': placeholderText}) + .text(placeholderText) + .style({opacity: opacity}) + .on('mouseover.opacity', function() { + d3.select(this).transition().duration(100) + .style('opacity', 1); + }) + .on('mouseout.opacity', function() { + d3.select(this).transition().duration(1000) + .style('opacity', 0); + }); + } + + var setContenteditable = function() { + this.call(svgTextUtils.makeEditable, {gd: gd}) + .on('edit', function(text) { + gd.framework({layout: {title: {text: text}}}); + this.text(text) + .call(titleLayout); + this.call(setContenteditable); + }) + .on('cancel', function() { + var txt = this.attr('data-unformatted'); + this.text(txt).call(titleLayout); + }); + }; + title.call(setContenteditable); + } + + gd._context.setBackground(gd, gd._fullLayout.paper_bgcolor); + Plots.addLinks(gd); + + return Promise.resolve(); +} + +// 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.plot(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.plot(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.plot); + } 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'); + if(hovermode.get() === 'x') { + hovermode.set('y'); + } else if(hovermode.get() === 'y') { + hovermode.set('x'); + } + } + + // 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); + + if(gd.framework && gd.framework.isPolar) { + return Promise.resolve(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; + 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._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 axId; + + 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') { + var 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 + var constraints = fullLayout._axisConstraintGroups || []; + for(axId in rangesAltered) { + for(i = 0; i < constraints.length; i++) { + var group = constraints[i]; + if(group[axId]) { + // 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(gd.framework && gd.framework.isPolar) { + return Promise.resolve(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.plot); + } 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)/}, + {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 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); + + 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 if required + if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined; + // 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)) { + 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.plot); + } 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://plot.ly/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 resovled *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://plot.ly/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-Plotly.plot 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 Plotly.Plots.purge which does NOT clear _context! + delete gd._context; + + return gd; +} + +// ------------------------------------------------------- +// makePlotFramework: Create the plot container and axes +// ------------------------------------------------------- +function makePlotFramework(gd) { + var gd3 = d3.select(gd); + var fullLayout = gd._fullLayout; + + // 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('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'); + + 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 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.plot = plot; +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":50,"../components/drawing":71,"../constants/xmlns_namespaces":150,"../lib":169,"../lib/events":163,"../lib/queue":183,"../lib/svg_text_utils":190,"../plots/cartesian/axes":213,"../plots/cartesian/constants":219,"../plots/cartesian/graph_interact":222,"../plots/cartesian/select":230,"../plots/plots":245,"../plots/polar/legacy":248,"../registry":257,"./edit_types":196,"./helpers":197,"./manage_arrays":199,"./plot_config":201,"./plot_schema":202,"./subroutines":204,"d3":15,"fast-isnumeric":17,"has-hover":19}],201:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/** + * This will be transferred over to gd and overridden by + * config args to Plotly.plot. + * + * 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: 'https://plot.ly', + + }, + + 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: 'boolean', + dflt: 1, + + }, + + 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 +}; + +},{}],202:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + +// polar attributes are not part of the Registry yet +var polarAreaAttrs = _dereq_('../plots/polar/legacy/area_attributes'); +var polarAxisAttrs = _dereq_('../plots/polar/legacy/axis_attributes'); + +var editTypes = _dereq_('./edit_types'); + +var extendFlat = Lib.extendFlat; +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.concat('area').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 if(trace.type === 'area') { + valObject = polarAreaAttrs[head]; + } 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]; + + // Polar doesn't populate _modules or _basePlotModules + // just fall back on these when the others fail + if(head === 'radialaxis' || head === 'angularaxis') { + return polarAxisAttrs[head]; + } + return polarAxisAttrs.layout[head] || 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; + + if(type === 'area') { + _module = { attributes: polarAreaAttrs }; + basePlotModule = {}; + } else { + _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); + } + } + + // polar layout attributes + layoutAttributes = assignPolarLayoutAttrs(layoutAttributes); + + // 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.valType === 'data_array') { + // all 'data_array' attrs have role 'data' + attr.role = 'data'; + // all 'data_array' attrs have a corresponding 'src' attr + attrs[attrName + 'src'] = makeSrcAttr(attrName); + } else if(attr.arrayOk === true) { + // all 'arrayOk' 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 assignPolarLayoutAttrs(layoutAttributes) { + extendFlat(layoutAttributes, { + radialaxis: polarAxisAttrs.radialaxis, + angularaxis: polarAxisAttrs.angularaxis + }); + + extendFlat(layoutAttributes, polarAxisAttrs.layout); + + return layoutAttributes; +} + +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":169,"../plots/animation_attributes":208,"../plots/attributes":210,"../plots/frame_attributes":240,"../plots/layout_attributes":243,"../plots/polar/legacy/area_attributes":246,"../plots/polar/legacy/axis_attributes":247,"../registry":257,"./edit_types":196,"./plot_config":201}],203:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169,"../plots/attributes":210}],204:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 gd._promises.length && Promise.all(gd._promises); + } + + 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) { + 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 gd._promises.length && Promise.all(gd._promises); +} + +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('plot', 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; + + var cameraData = sceneLayout.camera; + scene.setCamera(cameraData); + } +}; + +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); + + // show annotations and shapes + Registry.getComponentMethod('shapes', 'draw')(gd); + Registry.getComponentMethod('annotations', '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 fullLayout = gd._fullLayout; + var axList = Axes.list(gd, '', true); + var matchGroups = fullLayout._axisMatchGroups || []; + var ax; + var axRng; + + for(var i = 0; i < axList.length; i++) { + ax = axList[i]; + cleanAxisConstraints(gd, ax); + doAutoRange(gd, ax); + } + + enforceAxisConstraints(gd); + + groupLoop: + for(var j = 0; j < matchGroups.length; j++) { + var group = matchGroups[j]; + var rng = null; + var id; + + for(id in group) { + ax = Axes.getFromId(gd, id); + if(ax.autorange === false) continue groupLoop; + + axRng = Lib.simpleMap(ax.range, ax.r2l); + if(rng) { + if(rng[0] < rng[1]) { + rng[0] = Math.min(rng[0], axRng[0]); + rng[1] = Math.max(rng[1], axRng[1]); + } else { + rng[0] = Math.max(rng[0], axRng[0]); + rng[1] = Math.min(rng[1], axRng[1]); + } + } else { + rng = axRng; + } + } + + for(id in group) { + ax = Axes.getFromId(gd, id); + ax.range = Lib.simpleMap(rng, ax.l2r); + ax._input.range = ax.range.slice(); + ax.setScale(); + } + } +}; + +// 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) { + Registry.getComponentMethod('shapes', 'draw')(gd); + Registry.getComponentMethod('images', 'draw')(gd); + Registry.getComponentMethod('annotations', 'draw')(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":50,"../components/drawing":71,"../components/modebar":109,"../components/titles":138,"../constants/alignment":145,"../lib":169,"../lib/clear_gl_canvases":158,"../plots/cartesian/autorange":212,"../plots/cartesian/axes":213,"../plots/cartesian/constraints":220,"../plots/plots":245,"../registry":257,"d3":15}],205:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169,"../plots/attributes":210,"../plots/plots":245,"./plot_config":201,"./plot_schema":202,"./plot_template":203}],206:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var plotApi = _dereq_('./plot_api'); +var Lib = _dereq_('../lib'); + +var helpers = _dereq_('../snapshot/helpers'); +var toSVG = _dereq_('../snapshot/tosvg'); +var svgToImg = _dereq_('../snapshot/svgtoimg'); + +var attrs = { + format: { + valType: 'enumerated', + values: ['png', 'jpeg', 'webp', 'svg'], + 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('Image format is not jpeg, png, svg or webp.'); + } + + 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; + + plotApi.purge(clonedGd); + document.body.removeChild(clonedGd); + + 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.plot(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":169,"../snapshot/helpers":261,"../snapshot/svgtoimg":263,"../snapshot/tosvg":265,"./plot_api":200,"fast-isnumeric":17}],207:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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) { + 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 isInfoArray = (nestedSchema || {}).valType === 'info_array'; + var isColorscale = (nestedSchema || {}).valType === 'colorscale'; + var items = (nestedSchema || {}).items; + + if(!isInSchema(schema, k)) { + list.push(format('schema', base, p)); + } else if(isPlainObject(valIn) && isPlainObject(valOut)) { + 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":169,"../plots/plots":245,"./plot_config":201,"./plot_schema":202}],208:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', + + } + } +}; + +},{}],209:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../plot_api/plot_template":203}],210:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +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', + + }, + 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":80}],211:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + + +module.exports = { + xaxis: { + valType: 'subplotid', + + dflt: 'x', + editType: 'calc+clearAxisTypes', + + }, + yaxis: { + valType: 'subplotid', + + dflt: 'y', + editType: 'calc+clearAxisTypes', + + } +}; + +},{}],212:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var FP_SAFE = _dereq_('../../constants/numerical').FP_SAFE; +var Registry = _dereq_('../../registry'); + +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 getPad = makePadFn(ax); + 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; + if(dv > 0) { + dp = axLen - getPad(minpt) - getPad(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, pad: 0}; + maxbest = {val: maxpt.val, pad: 0}; + mbest = dv / axLen; + } + } + } + } + + function getMaxPad(prev, pt) { + return Math.max(prev, getPad(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(getMaxPad, 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, pad: 0}; + } + if(maxbest.val <= 0) { + maxbest = {val: 0, pad: 0}; + } + } else if(nonNegative) { + if(minbest.val - mbest * getPad(minbest) < 0) { + minbest = {val: 0, pad: 0}; + } + if(maxbest.val <= 0) { + maxbest = {val: 1, pad: 0}; + } + } + + // in case it changed again... + mbest = (maxbest.val - minbest.val) / + (axLen - getPad(minbest) - getPad(maxbest)); + + newRange = [ + minbest.val - mbest * getPad(minbest), + maxbest.val + mbest * getPad(maxbest) + ]; + } + + // maintain reversal + if(axReverse) newRange.reverse(); + + return Lib.simpleMap(newRange, ax.l2r || Number); +} + +/* + * calculate the pixel padding for ax._min and ax._max entries with + * optional extrapad as 5% of the total axis length + */ +function makePadFn(ax) { + // 5% padding for points that specify extrapad: true + var extrappad = ax._length / 20; + + // 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) { return pt.pad + (pt.extrapad ? extrappad : 0); }; +} + +function concatExtremes(gd, ax) { + 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 || []); + + return {min: minArray, max: maxArray}; +} + +function doAutoRange(gd, ax) { + ax.setScale(); + + if(ax.autorange) { + ax.range = 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 + * + * @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 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); + 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; } + +},{"../../constants/numerical":149,"../../lib":169,"../../registry":257,"fast-isnumeric":17}],213:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); +var isNumeric = _dereq_('fast-isnumeric'); +var Plots = _dereq_('../../plots/plots'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +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 ONEAVGYEAR = constants.ONEAVGYEAR; +var ONEAVGMONTH = constants.ONEAVGMONTH; +var ONEDAY = constants.ONEDAY; +var ONEHOUR = constants.ONEHOUR; +var ONEMIN = constants.ONEMIN; +var ONESEC = constants.ONESEC; +var MINUS_SIGN = constants.MINUS_SIGN; +var BADNUM = constants.BADNUM; + +var MID_SHIFT = _dereq_('../../constants/alignment').MID_SHIFT; +var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING; + +var axes = module.exports = {}; + +axes.setConvert = _dereq_('./set_convert'); +var autoType = _dereq_('./axis_autotype'); + +var axisIds = _dereq_('./axis_ids'); +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; + +/* + * 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] || extraOption; + if(!extraOption) extraOption = dflt; + + // data-ref annotations are not supported in gl2d yet + + attrDef[refAttr] = { + valType: 'enumerated', + values: axlist.concat(extraOption ? [extraOption] : []), + dflt: dflt + }; + + // xref, yref + return Lib.coerce(containerIn, containerOut, attrDef, refAttr); +}; + +/* + * 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; + + if(axRef === 'paper' || axRef === 'pixel') { + 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), + _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 -= ONEDAY / 2; + } + 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) { + var rng = Lib.simpleMap(ax.range, ax.r2l); + + // 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 ? (ax.tickfont.size || 12) * 1.2 : 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; + + axes.autoTicks(ax, Math.abs(rng[1] - rng[0]) / nt); + // 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); + } + } + + // 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); +}; + +// 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) { + axes.prepTicks(ax); + var rng = Lib.simpleMap(ax.range, ax.r2l); + + // 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); + + // find the first tick + ax._tmin = axes.tickFirst(ax); + + // add a tiny bit so we get ticks which may have rounded out + var startTick = rng[0] * 1.0001 - rng[1] * 0.0001; + var endTick = rng[1] * 1.0001 - rng[0] * 0.0001; + // check for reversed axis + var axrev = (rng[1] < rng[0]); + + // 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 + var tickVals = []; + if(ax.type === 'category' || ax.type === 'multicategory') { + endTick = (axrev) ? Math.max(-0.5, endTick) : + Math.min(ax._categories.length - 0.5, endTick); + } + + var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L'); + + var xPrevious = null; + var maxTicks = Math.max(1000, ax._length || 0); + for(var x = ax._tmin; + (axrev) ? (x >= endTick) : (x <= endTick); + x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) { + // 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 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 = new Array(tickVals.length); + for(var i = 0; i < tickVals.length; i++) { + ticksOut[i] = axes.tickText( + ax, + tickVals[i].value, + false, // hover + tickVals[i].minor // noSuffixPrefix + ); + } + + 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 r0expanded = rng[0] * 1.0001 - rng[1] * 0.0001; + var r1expanded = rng[1] * 1.0001 - rng[0] * 0.0001; + var tickMin = Math.min(r0expanded, r1expanded); + var tickMax = Math.max(r0expanded, r1expanded); + 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); + + 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); + // 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, 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. + ax.tick0 = Lib.dateTick0(ax.calendar, true); + } 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); + if(Math.abs(rangeexp) > 3) { + 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 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 + else 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) + else 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; + } else throw 'unrecognized dtick ' + String(dtick); +}; + +// calculate the first tick on an axis +axes.tickFirst = function(ax) { + var r2l = ax.r2l || Number; + var rng = Lib.simpleMap(ax.range, r2l); + 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 = rng[0] * 1.0001 - rng[1] * 0.0001; + 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]) / 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} val: calcdata value to format + * @param {Optional(number)} val2: a second value to display + * + * @returns {string} `val` formatted as a string appropriate to this axis, or + * `val` and `val2` as a range (ie ' - ') if `val2` is provided and + * it's different from `val`. + */ +axes.hoverLabelText = function(ax, val, val2) { + if(val2 !== BADNUM && val2 !== val) { + return axes.hoverLabelText(ax, val) + ' - ' + axes.hoverLabelText(ax, val2); + } + + 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 || (headStr !== ax._prevDateHead)) { + dateStr += '
' + headStr; + ax._prevDateHead = headStr; + } + } + + 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, + 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]; + 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) + */ +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 mainSubplot = ax._mainSubplot; + var mainLinePosition = ax._mainLinePosition; + var mainMirrorPosition = ax._mainMirrorPosition; + var mainPlotinfo = fullLayout._plots[mainSubplot]; + var mainAxLayer = mainPlotinfo[axLetter + 'axislayer']; + var subplotsWithAx = ax._subplotsWith; + + 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; + } + + if(!ax.visible) return; + + // 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 + ax._tickAngles = {}; + + var transFn = axes.makeTransFn(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; + + if(ax.tickson === 'boundaries') { + var boundaryVals = getBoundaryVals(ax, vals); + valsClipped = axes.clipEnds(ax, boundaryVals); + tickVals = ax.ticks === 'inside' ? valsClipped : boundaryVals; + } else { + valsClipped = axes.clipEnds(ax, vals); + tickVals = ax.ticks === 'inside' ? valsClipped : vals; + } + + var gridVals = ax._gridVals = valsClipped; + var dividerVals = getDividerVals(ax, vals); + + if(!fullLayout._hasOnlyLargeSploms) { + // keep track of which subplots (by main conteraxis) 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: transFn + }); + axes.drawZeroLine(gd, ax, { + counterAxis: counterAxis, + layer: plotinfo.zerolinelayer, + path: gridPath, + transFn: transFn + }); + } + } + + 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 && ax.ticks === 'outside' && 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: transFn + }); + + 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: transFn + }); + } + + 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, + transFn: transFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition) + }); + }); + + if(ax.type === 'multicategory') { + var labelLength = 0; + var pad = {x: 2, y: 10}[axLetter]; + var sgn = tickSigns[2] * (ax.ticks === 'inside' ? -1 : 1); + + seq.push(function() { + labelLength += getLabelLevelSpan(ax, axId + 'tick') + pad; + labelLength += 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: transFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition + labelLength * sgn) + }); + }); + + seq.push(function() { + labelLength += getLabelLevelSpan(ax, axId + 'tick2'); + ax._labelLength = labelLength; + + return drawDividers(gd, ax, { + vals: dividerVals, + layer: mainAxLayer, + path: axes.makeTickPath(ax, mainLinePosition, sgn, labelLength), + transFn: transFn + }); + }); + } + + function extendRange(range, newRange) { + range[0] = Math.min(range[0], newRange[0]); + range[1] = Math.max(range[1], newRange[1]); + } + + function calcBoundingBox() { + if(ax.showticklabels) { + var gdBB = gd.getBoundingClientRect(); + var bBox = mainAxLayer.node().getBoundingClientRect(); + + /* + * the way we're going to use this, the positioning that matters + * is relative to the origin of gd. This is important particularly + * if gd is scrollable, and may have been scrolled between the time + * we calculate this and the time we use it + */ + + ax._boundingBox = { + width: bBox.width, + height: bBox.height, + left: bBox.left - gdBB.left, + right: bBox.right - gdBB.left, + top: bBox.top - gdBB.top, + bottom: bBox.bottom - gdBB.top + }; + } else { + var gs = fullLayout._size; + var pos; + + // set dummy bbox for ticklabel-less axes + + if(axLetter === 'x') { + pos = ax.anchor === 'free' ? + gs.t + gs.h * (1 - ax.position) : + gs.t + gs.h * (1 - ax._anchorAxis.domain[{bottom: 0, top: 1}[ax.side]]); + + ax._boundingBox = { + top: pos, + bottom: pos, + left: ax._offset, + right: ax._offset + ax._length, + width: ax._length, + height: 0 + }; + } else { + pos = ax.anchor === 'free' ? + gs.l + gs.w * ax.position : + gs.l + gs.w * ax._anchorAxis.domain[{left: 0, right: 1}[ax.side]]; + + ax._boundingBox = { + left: pos, + right: pos, + bottom: ax._offset + ax._length, + top: ax._offset, + height: ax._length, + width: 0 + }; + } + } + + /* + * for spikelines: what's the full domain of positions in the + * opposite direction that are associated with this axis? + * This means any axes that we make a subplot with, plus the + * position of the axis itself if it's free. + */ + if(subplotsWithAx) { + var fullRange = ax._counterSpan = [Infinity, -Infinity]; + + for(var i = 0; i < subplotsWithAx.length; i++) { + var plotinfo = fullLayout._plots[subplotsWithAx[i]]; + var counterAxis = plotinfo[(axLetter === 'x') ? 'yaxis' : 'xaxis']; + + extendRange(fullRange, [ + counterAxis._offset, + counterAxis._offset + counterAxis._length + ]); + } + + if(ax.anchor === 'free') { + extendRange(fullRange, (axLetter === 'x') ? + [ax._boundingBox.bottom, ax._boundingBox.top] : + [ax._boundingBox.right, ax._boundingBox.left]); + } + } + } + + var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax); + + function doAutoMargins() { + var s = ax.side.charAt(0); + var push; + var rangeSliderPush; + + if(hasRangeSlider) { + rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax); + } + Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush); + + if(ax.automargin && (!hasRangeSlider || s !== 'b')) { + push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + + var bbox = ax._boundingBox; + var titleOffset = getTitleOffset(gd, ax); + var anchorAxDomainIndex; + var offset; + + switch(axLetter + s) { + case 'xb': + anchorAxDomainIndex = 0; + offset = bbox.top - titleOffset; + push[s] = bbox.height; + break; + case 'xt': + anchorAxDomainIndex = 1; + offset = titleOffset - bbox.bottom; + push[s] = bbox.height; + break; + case 'yl': + anchorAxDomainIndex = 0; + offset = titleOffset - bbox.right; + push[s] = bbox.width; + break; + case 'yr': + anchorAxDomainIndex = 1; + offset = bbox.left - titleOffset; + push[s] = bbox.width; + break; + } + + push[counterLetter] = ax.anchor === 'free' ? + ax.position : + ax._anchorAxis.domain[anchorAxDomainIndex]; + + if(push[s] > 0) { + push[s] += offset; + } + + if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { + push[s] += ax.title.font.size; + } + + if(axLetter === 'x' && bbox.width > 0) { + var rExtra = bbox.right - (ax._offset + ax._length); + if(rExtra > 0) { + push.x = 1; + push.r = rExtra; + } + var lExtra = ax._offset - bbox.left; + if(lExtra > 0) { + push.x = 0; + push.l = lExtra; + } + } else if(axLetter === 'y' && bbox.height > 0) { + var bExtra = bbox.bottom - (ax._offset + ax._length); + if(bExtra > 0) { + push.y = 0; + push.b = bExtra; + } + var tExtra = ax._offset - bbox.top; + if(tExtra > 0) { + push.y = 1; + push.t = tExtra; + } + } + } + + Plots.autoMargin(gd, axAutoMarginID(ax), push); + } + + seq.push(calcBoundingBox, doAutoMargins); + + if(!opts.skipTitle && + !(hasRangeSlider && ax._boundingBox && 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; + + // 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, 0); + } + current = d.text2; + } + _push(vals[i - 1], 1); + } + + return out; +} + +function getLabelLevelSpan(ax, cls) { + var axLetter = ax._id.charAt(0); + var angle = ax._tickAngles[cls] || 0; + var rad = Lib.deg2rad(angle); + var sinA = Math.sin(rad); + var cosA = Math.cos(rad); + var maxX = 0; + var maxY = 0; + + // N.B. Drawing.bBox does not take into account rotate transforms + + ax._selections[cls].each(function() { + var thisLabel = selectTickLabel(this); + var bb = Drawing.bBox(thisLabel.node()); + var w = bb.width; + var h = bb.height; + maxX = Math.max(maxX, cosA * w, sinA * h); + maxY = Math.max(maxY, sinA * w, cosA * h); + }); + + return {x: maxY, y: maxX}[axLetter]; +} + +/** + * 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' + */ +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; }); + } + 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.makeTransFn = function(ax) { + var axLetter = ax._id.charAt(0); + var offset = ax._offset; + return axLetter === 'x' ? + function(d) { return 'translate(' + (offset + ax.l2p(d.x)) + ',0)'; } : + function(d) { return 'translate(0,' + (offset + ax.l2p(d.x)) + ')'; }; +}; + +/** + * 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} sng 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 axLetter = ax._id.charAt(0); + var ticksOnOutsideLabels = ax.tickson !== 'boundaries' && ax.ticks === 'outside'; + + var labelStandoff = 0; + var labelShift = 0; + + if(ticksOnOutsideLabels) { + labelStandoff += ax.ticklen; + } + if(angle && ax.ticks === 'outside') { + var rad = Lib.deg2rad(angle); + labelStandoff = ax.ticklen * Math.cos(rad) + 1; + labelShift = ax.ticklen * Math.sin(rad); + } + if(ax.showticklabels && (ticksOnOutsideLabels || ax.showline)) { + labelStandoff += 0.2 * ax.tickfont.size; + } + labelStandoff += (ax.linewidth || 1) / 2; + + var out = { + labelStandoff: labelStandoff, + labelShift: labelShift + }; + + var x0, y0, ff, flipIt; + + if(axLetter === 'x') { + flipIt = ax.side === 'bottom' ? 1 : -1; + x0 = labelShift * flipIt; + y0 = shift + labelStandoff * flipIt; + ff = ax.side === 'bottom' ? 1 : -0.2; + + out.xFn = function(d) { return d.dx + x0; }; + out.yFn = function(d) { return d.dy + y0 + d.fontSize * ff; }; + out.anchorFn = function(d, a) { + if(!isNumeric(a) || a === 0 || a === 180) { + return 'middle'; + } + return (a * flipIt < 0) ? 'end' : 'start'; + }; + out.heightFn = function(d, a, h) { + return (a < -60 || a > 60) ? -0.5 * h : + ax.side === 'top' ? -h : + 0; + }; + } else if(axLetter === 'y') { + flipIt = ax.side === 'right' ? 1 : -1; + x0 = labelStandoff; + y0 = -labelShift * flipIt; + ff = Math.abs(ax.tickangle) === 90 ? 0.5 : 0; + + out.xFn = function(d) { return d.dx + shift + (x0 + d.fontSize * ff) * flipIt; }; + 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 ax.side === 'right' ? 'start' : 'end'; + }; + out.heightFn = function(d, a, h) { + a *= ax.side === 'left' ? 1 : -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 ticks = opts.layer.selectAll('path.' + cls) + .data(ax.ticks ? opts.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); + + 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'); + + 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 axisIds.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'); +}; + +/** + * 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 + * @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 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 lastAngle = (ax._tickAngles || {})[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); + } + }); + + 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 += ' translate(0, ' + anchorHeight + ')'; + } + + if(mathjaxGroup.empty()) { + thisLabel.select('text').attr({ + transform: transform, + 'text-anchor': anchor + }); + } else { + var mjWidth = Drawing.bBox(mathjaxGroup.node()).width; + var mjShift = mjWidth * {end: -0.5, start: 0.5}[anchor]; + mathjaxGroup.attr('transform', transform + (mjShift ? 'translate(' + mjShift + ',0)' : '')); + } + }); + } + + // 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, lastAngle || tickAngle); + + function allLabelsReady() { + return labelsReady.length && Promise.all(labelsReady); + } + + function fixLabelOverlaps() { + positionLabels(tickLabels, tickAngle); + + var autoangle = null; + + // 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 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])) { + autoangle = rotate90 ? 90 : 30; + break; + } + } + } + + if(autoangle) { + positionLabels(tickLabels, autoangle); + } + } + + if(ax._tickAngles) { + ax._tickAngles[cls] = autoangle === null ? + (isNumeric(tickAngle) ? tickAngle : 0) : + autoangle; + } + } + + if(ax._selections) { + ax._selections[cls] = tickLabels; + } + + var done = Lib.syncOrAsync([allLabelsReady, fixLabelOverlaps]); + 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); +} + +function getTitleOffset(gd, ax) { + var gs = gd._fullLayout._size; + var axLetter = ax._id.charAt(0); + var side = ax.side; + var anchorAxis; + + if(ax.anchor !== 'free') { + anchorAxis = axisIds.getFromId(gd, ax.anchor); + } 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; + } +} + +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.type === 'multicategory') { + titleStandoff = ax._labelLength; + } else { + var offsetBase = 1.5; + titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0); + } + + var titleOffset = getTitleOffset(gd, ax); + + var transform, x, y; + + if(axLetter === 'x') { + x = ax._offset + ax._length / 2; + + if(ax.side === 'top') { + y = -titleStandoff - fontSize * (ax.showticklabels ? 1 : 0); + } else { + y = titleStandoff + fontSize * (ax.showticklabels ? 1.5 : 0.5); + } + y += titleOffset; + } else { + y = ax._offset + ax._length / 2; + + if(ax.side === 'right') { + x = titleStandoff + fontSize * (ax.showticklabels ? 1 : 0.5); + } else { + x = -titleStandoff - fontSize * (ax.showticklabels ? 0.5 : 0); + } + x += titleOffset; + + 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; + } + } + + 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._gridVals.length && + ( + 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(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) { + Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax)); + } + } +}; + +function axAutoMarginID(ax) { return ax._id + '.automargin'; } +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'; +} + +},{"../../components/color":50,"../../components/drawing":71,"../../components/titles":138,"../../constants/alignment":145,"../../constants/numerical":149,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"../../registry":257,"./autorange":212,"./axis_autotype":214,"./axis_ids":216,"./clean_ticks":218,"./layout_attributes":225,"./set_convert":231,"d3":15,"fast-isnumeric":17}],214:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +module.exports = function autoType(array, calendar, opts) { + opts = opts || {}; + + if(!opts.noMultiCategory && multiCategory(array)) return 'multicategory'; + if(moreDates(array, calendar)) return 'date'; + if(category(array)) return 'category'; + if(linearOK(array)) return 'linear'; + else return '-'; +}; + +// is there at least one number in array? If not, we should leave +// ax.type empty so it can be autoset later +function linearOK(array) { + if(!array) return false; + + for(var i = 0; i < array.length; i++) { + if(isNumeric(array[i])) 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) { + // test at most 1000 points, evenly spaced + var inc = Math.max(1, (a.length - 1) / 1000); + var dcnt = 0; + var ncnt = 0; + var seen = {}; + + for(var i = 0; i < a.length; i += inc) { + var ai = a[Math.round(i)]; + var stri = String(ai); + if(seen[stri]) continue; + seen[stri] = 1; + + if(Lib.isDateTime(ai, calendar)) dcnt += 1; + if(isNumeric(ai)) ncnt += 1; + } + + return (dcnt > ncnt * 2); +} + +// are the (x,y)-values in gd.data mostly text? +// require twice as many DISTINCT categories as distinct numbers +function category(a) { + // test at most 1000 points + var inc = Math.max(1, (a.length - 1) / 1000); + var curvenums = 0; + var curvecats = 0; + var seen = {}; + + for(var i = 0; i < a.length; i += inc) { + var ai = a[Math.round(i)]; + var stri = String(ai); + if(seen[stri]) continue; + seen[stri] = 1; + + if(typeof ai === 'boolean') curvecats++; + else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++; + else if(typeof ai === 'string') curvecats++; + } + + return curvecats > curvenums * 2; +} + +// very-loose requirements for multicategory, +// trace modules that should never auto-type to multicategory +// should be declared with 'noMultiCategory' +function multiCategory(a) { + return Lib.isArrayOrTypedArray(a[0]) && Lib.isArrayOrTypedArray(a[1]); +} + +},{"../../constants/numerical":149,"../../lib":169,"fast-isnumeric":17}],215:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); + +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'); + +/** + * 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 axType = containerOut.type; + + if(axType === 'date') { + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar); + } + + 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: Math.round(font.size * 1.2), + 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 = containerOut.type === 'multicategory'; + + if(!options.noTickson && + (containerOut.type === 'category' || isMultiCategory) && + (containerOut.ticks || containerOut.showgrid) + ) { + var ticksonDflt; + if(isMultiCategory) ticksonDflt = 'boundaries'; + coerce('tickson', ticksonDflt); + } + + if(isMultiCategory) { + var showDividers = coerce('showdividers'); + if(showDividers) { + coerce('dividercolor'); + coerce('dividerwidth'); + } + } + + return containerOut; +}; + +},{"../../lib":169,"../../registry":257,"./category_order_defaults":217,"./layout_attributes":225,"./line_grid_defaults":227,"./set_convert":231,"./tick_label_defaults":232,"./tick_mark_defaults":233,"./tick_value_defaults":234}],216:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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.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; +}; + +exports.cleanId = function cleanId(id, axLetter) { + if(!id.match(constants.AX_ID_PATTERN)) return; + if(axLetter && id.charAt(0) !== axLetter) return; + + var axNum = id.substr(1).replace(/^0+/, ''); + if(axNum === '1') axNum = ''; + return id.charAt(0) + axNum; +}; + +// 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; + + 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); +}; + +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; +}; + +},{"../../registry":257,"./constants":219}],217:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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(); + } + } +}; + +},{}],218:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); +var ONEDAY = _dereq_('../../constants/numerical').ONEDAY; + +/** + * 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)); + } + 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":149,"../../lib":169,"fast-isnumeric":17}],219:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; +var counterRegex = _dereq_('../../lib/regex').counter; + + +module.exports = { + + idRegex: { + x: counterRegex('x'), + y: counterRegex('y') + }, + + 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]*$/, + AX_NAME_PATTERN: /^[xyz]axis[0-9]*$/, + + // and for 2D subplots + SUBPLOT_PATTERN: /^x([0-9]*)y([0-9]*)$/, + + // 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: [ + '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":184}],220:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); +var id2name = _dereq_('./axis_ids').id2name; +var scaleZoom = _dereq_('./scale_zoom'); +var makePadFn = _dereq_('./autorange').makePadFn; +var concatExtremes = _dereq_('./autorange').concatExtremes; + +var ALMOST_EQUAL = _dereq_('../../constants/numerical').ALMOST_EQUAL; +var FROM_BL = _dereq_('../../constants/alignment').FROM_BL; + +exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, allAxisIds, layoutOut) { + var constraintGroups = layoutOut._axisConstraintGroups; + var matchGroups = layoutOut._axisMatchGroups; + var axId = containerOut._id; + var axLetter = axId.charAt(0); + var splomStash = ((layoutOut._splomAxes || {})[axLetter] || {})[axId] || {}; + var thisID = containerOut._id; + var letter = thisID.charAt(0); + + // coerce the constraint mechanics even if this axis has no scaleanchor + // because it may be the anchor of another axis. + var constrain = coerce('constrain'); + Lib.coerce(containerIn, containerOut, { + constraintoward: { + valType: 'enumerated', + values: letter === 'x' ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'], + dflt: letter === 'x' ? 'center' : 'middle' + } + }, 'constraintoward'); + + var matches, matchOpts; + + if((containerIn.matches || splomStash.matches) && !containerOut.fixedrange) { + matchOpts = getConstraintOpts(matchGroups, thisID, allAxisIds, layoutOut); + matches = Lib.coerce(containerIn, containerOut, { + matches: { + valType: 'enumerated', + values: matchOpts.linkableAxes || [], + dflt: splomStash.matches + } + }, 'matches'); + } + + // 'matches' wins over 'scaleanchor' (for now) + var scaleanchor, scaleOpts; + + if(!matches && containerIn.scaleanchor && !(containerOut.fixedrange && constrain !== 'domain')) { + scaleOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut, constrain); + scaleanchor = Lib.coerce(containerIn, containerOut, { + scaleanchor: { + valType: 'enumerated', + values: scaleOpts.linkableAxes || [] + } + }, 'scaleanchor'); + } + + if(matches) { + delete containerOut.constrain; + updateConstraintGroups(matchGroups, matchOpts.thisGroup, thisID, matches, 1); + } else if(allAxisIds.indexOf(containerIn.matches) !== -1) { + Lib.warn('ignored ' + containerOut._name + '.matches: "' + + containerIn.matches + '" to avoid either an infinite loop ' + + 'or because the target axis has fixed range.'); + } + + 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 = containerOut.scaleratio = 1; + + updateConstraintGroups(constraintGroups, scaleOpts.thisGroup, thisID, scaleanchor, scaleratio); + } else if(allAxisIds.indexOf(containerIn.scaleanchor) !== -1) { + Lib.warn('ignored ' + containerOut._name + '.scaleanchor: "' + + containerIn.scaleanchor + '" to avoid either an infinite loop ' + + 'and possibly inconsistent scaleratios, or because the target ' + + 'axis has fixed range or this axis declares a *matches* constraint.'); + } +}; + +// 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 allAxisIds to enforce this, also matching axis types. +function getConstraintOpts(groups, thisID, allAxisIds, layoutOut, constrain) { + var doesNotConstrainRange = constrain !== 'range'; + var thisType = layoutOut[id2name(thisID)].type; + var i, j, idj, axj; + + var linkableAxes = []; + for(j = 0; j < allAxisIds.length; j++) { + idj = allAxisIds[j]; + if(idj === thisID) continue; + + axj = layoutOut[id2name(idj)]; + if(axj.type === thisType) { + if(!axj.fixedrange) { + linkableAxes.push(idj); + } else if(doesNotConstrainRange && axj.anchor) { + // allow domain constraints on subplots where + // BOTH axes have fixedrange:true and constrain:domain + var counterAxj = layoutOut[id2name(axj.anchor)]; + if(counterAxj.fixedrange) { + linkableAxes.push(idj); + } + } + } + } + + for(i = 0; i < groups.length; i++) { + if(groups[i][thisID]) { + var thisGroup = groups[i]; + + var linkableAxesNoLoops = []; + for(j = 0; j < linkableAxes.length; j++) { + idj = linkableAxes[j]; + if(!thisGroup[idj]) linkableAxesNoLoops.push(idj); + } + return {linkableAxes: linkableAxesNoLoops, thisGroup: thisGroup}; + } + } + + return {linkableAxes: linkableAxes, thisGroup: null}; +} + +/* + * Add this axis to the axis constraint groups, which is the collection + * of axes that are all constrained together on scale. + * + * 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 + * scaleanchor: the id of the axis to scale it with + * scaleratio: the ratio of this axis to the scaleanchor axis + */ +function updateConstraintGroups(constraintGroups, thisGroup, thisID, scaleanchor, scaleratio) { + var i, j, groupi, keyj, thisGroupIndex; + + 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 scaleanchor axis. If it is, we need to merge the groups. + for(i = 0; i < constraintGroups.length; i++) { + groupi = constraintGroups[i]; + if(i !== thisGroupIndex && groupi[scaleanchor]) { + var baseScale = groupi[scaleanchor]; + for(j = 0; j < thisGroupKeys.length; j++) { + keyj = thisGroupKeys[j]; + groupi[keyj] = baseScale * scaleratio * thisGroup[keyj]; + } + constraintGroups.splice(thisGroupIndex, 1); + return; + } + } + + // otherwise, we insert the new scaleanchor 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++) { + thisGroup[thisGroupKeys[j]] *= scaleratio; + } + } + thisGroup[scaleanchor] = 1; +} + +exports.enforce = function enforce(gd) { + var fullLayout = gd._fullLayout; + var constraintGroups = fullLayout._axisConstraintGroups || []; + + var i, j, axisID, ax, normScale, mode, factor; + + for(i = 0; i < constraintGroups.length; i++) { + var group = constraintGroups[i]; + 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 getPad = makePadFn(ax); + + updateDomain(ax, factor); + var m = Math.abs(ax._m); + var extremes = 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 - getPad(minArray[k]) / m; + if(newVal > outerMin && newVal < rangeMin) { + rangeMin = newVal; + } + } + + for(k = 0; k < maxArray.length; k++) { + newVal = maxArray[k].val + getPad(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); + } + } + } + } +}; + +// 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":145,"../../constants/numerical":149,"../../lib":169,"./autorange":212,"./axis_ids":216,"./scale_zoom":229}],221:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +var tinycolor = _dereq_('tinycolor2'); +var supportsPassive = _dereq_('has-passive-events'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +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 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; + + 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; + + links = calcLinks(gd, gd._fullLayout._axisConstraintGroups, xaHash, yaHash); + matches = calcLinks(gd, gd._fullLayout._axisMatchGroups, xaHash, yaHash); + editX = ew || links.isSubplotConstrained || matches.isSubplotConstrained; + editY = ns || links.isSubplotConstrained || matches.isSubplotConstrained; + + 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(); + + 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(!isSelectOrLasso(dragModeNow)) dragModeNow = 'pan'; + } else if(e.ctrlKey) { + dragModeNow = 'pan'; + } + } else { + // all other draggers just pan + dragModeNow = 'pan'; + } + } + + if(dragModeNow === 'lasso') dragOptions.minDrag = 1; + else dragOptions.minDrag = undefined; + + if(isSelectOrLasso(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(isSelectOrLasso(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(!isSelectOrLasso(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 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; + 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, dx0 + x0)); + var y1 = Math.max(0, Math.min(ph, 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() { + // more strict than dragged, which allows you to come back to where you started + // and still count as dragged + if(Math.min(box.h, box.w) < MINDRAG * 2) { + return removeZoombox(gd); + } + + 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() { + 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) { + // 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') { + if(xActive) { + dragAxList(xaxes, dx); + updateMatchedAxRange('x'); + } + if(yActive) { + dragAxList(yaxes, dy); + updateMatchedAxRange('y'); + } + updateSubplots([xActive ? -dx : 0, yActive ? -dy : 0, 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]); + } + + if(links.isSubplotConstrained && xActive && yActive) { + // dragging a corner of a constrained subplot: + // respect the fixed corner, but harmonize dx and dy + var dxySign = ((xActive === 'w') === (yActive === 'n')) ? 1 : -1; + var dxyFraction = (dx / pw + dxySign * dy / ph) / 2; + dx = dxyFraction * pw; + dy = dxySign * dxyFraction * ph; + } + + 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; + + var xStart = (xActive === 'w') ? dx : 0; + var yStart = (yActive === 'n') ? dy : 0; + + if(links.isSubplotConstrained) { + var i; + if(!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(!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; + } + } + + updateMatchedAxRange('x'); + updateMatchedAxRange('y'); + updateSubplots([xStart, yStart, pw - dx, ph - dy]); + 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 && !xa.fixedrange && xaHash[xa._id]; + var editY2 = editY && !ya.fixedrange && yaHash[ya._id]; + + var xScaleFactor2, yScaleFactor2; + var clipDx, clipDy; + + if(editX2) { + xScaleFactor2 = xScaleFactor; + clipDx = ew ? 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 ? 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; + + 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) { + 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', 'translate(' + 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', 'translate(' + 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 isSelectOrLasso(dragmode) { + return dragmode === 'lasso' || dragmode === 'select'; +} + +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) { + var isSubplotConstrained = false; + var xLinks = {}; + var yLinks = {}; + var xID, yID, xLinkID, yLinkID; + + 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(!(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(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(!(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 { + 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":50,"../../components/dragelement":68,"../../components/drawing":71,"../../components/fx":89,"../../constants/alignment":145,"../../lib":169,"../../lib/clear_gl_canvases":158,"../../lib/setcursor":188,"../../lib/svg_text_utils":190,"../../plot_api/subroutines":204,"../../registry":257,"../plots":245,"./axes":213,"./axis_ids":216,"./constants":219,"./scale_zoom":229,"./select":230,"d3":15,"has-passive-events":20,"tinycolor2":33}],222:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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) { + 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":68,"../../components/fx":89,"../../lib/setcursor":188,"./constants":219,"./dragbox":221,"d3":15}],223:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); + +/** + * 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; + + var xref = itemi.xref; + var yref = itemi.yref; + + 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":169,"../../registry":257}],224:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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; + } 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":71,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../registry":257,"../get_data":241,"../plots":245,"./attributes":211,"./axis_ids":216,"./constants":219,"./graph_interact":222,"./layout_attributes":225,"./layout_defaults":226,"./transition_axes":235,"d3":15}],225:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK; +var DATE_FORMAT_LINK = _dereq_('../../constants/docs').DATE_FORMAT_LINK; + +var constants = _dereq_('./constants'); + +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', + + }), + 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, + + }, + 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'], + dflt: 'range', + + 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', + + }, + // 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', + + }, + 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'], + dflt: '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', + + }, + separatethousands: { + valType: 'boolean', + dflt: false, + + editType: 'ticks', + + }, + tickformat: { + valType: 'string', + dflt: '', + + editType: 'ticks', + + }, + 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', + + }, + // 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":49,"../../components/drawing/attributes":70,"../../constants/docs":146,"../../lib/extend":164,"../../plot_api/plot_template":203,"../font_attributes":239,"./constants":219}],226:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../../components/color'); +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 handleConstraintDefaults = _dereq_('./constraints').handleConstraintDefaults; +var handlePositionDefaults = _dereq_('./position_defaults'); + +var axisIds = _dereq_('./axis_ids'); +var id2name = axisIds.id2name; +var name2id = axisIds.name2id; + +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 ax2traces = {}; + var xaMayHide = {}; + var yaMayHide = {}; + var xaMustDisplay = {}; + var yaMustDisplay = {}; + var yaMustForward = {}; + var yaMayBackward = {}; + 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) yaMayBackward[yaName] = true; + } else { + if(yaName) yaMayHide[yaName] = true; + } + } else { + if(yaName) { + yaMustDisplay[yaName] = true; + yaMustForward[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); + + var axName, axLetter, axLayoutIn, axLayoutOut; + + 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; + } + + var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')}; + var allAxisIds = counterAxes.x.concat(counterAxes.y); + + 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; + } + + // first pass creates the containers, determines types, and handles most of the settings + for(i = 0; i < axNames.length; i++) { + axName = axNames[i]; + axLetter = axName.charAt(0); + + if(!Lib.isPlainObject(layoutIn[axName])) { + layoutIn[axName] = {}; + } + + axLayoutIn = layoutIn[axName]; + axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis'); + + var traces = ax2traces[axName] || []; + axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; }); + axLayoutOut._annIndices = []; + axLayoutOut._shapeIndices = []; + axLayoutOut._imgIndices = []; + axLayoutOut._subplotsWith = []; + axLayoutOut._counterAxes = []; + + // set up some private properties + axLayoutOut._name = axLayoutOut._attr = axName; + var id = axLayoutOut._id = name2id(axName); + + var overlayableAxes = getOverlayableAxes(axLetter, axName); + + var visibleDflt = + (axLetter === 'x' && !xaMustDisplay[axName] && xaMayHide[axName]) || + (axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]); + + var reverseDflt = + (axLetter === 'y' && !yaMustForward[axName] && yaMayBackward[axName]); + + var defaultOptions = { + letter: axLetter, + font: layoutOut.font, + outerTicks: outerTicks[axName], + showGrid: !noGrids[axName], + data: traces, + bgColor: bgColor, + calendar: layoutOut.calendar, + automargin: true, + visibleDflt: visibleDflt, + reverseDflt: reverseDflt, + splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[id] + }; + + coerce('uirevision', layoutOut.uirevision); + + handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions); + handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut); + + var spikecolor = coerce2('spikecolor'); + var spikethickness = coerce2('spikethickness'); + var spikedash = coerce2('spikedash'); + var spikemode = coerce2('spikemode'); + var spikesnap = coerce2('spikesnap'); + var showSpikes = coerce('showspikes', !!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: overlayableAxes, + grid: layoutOut.grid + }); + + 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). + + // sets of axes linked by `scaleanchor` along with the scaleratios compounded + // together, populated in handleConstraintDefaults + var constraintGroups = layoutOut._axisConstraintGroups = []; + // similar to _axisConstraintGroups, but for matching axes + var matchGroups = layoutOut._axisMatchGroups = []; + + for(i = 0; i < axNames.length; i++) { + axName = axNames[i]; + axLetter = axName.charAt(0); + axLayoutIn = layoutIn[axName]; + axLayoutOut = layoutOut[axName]; + + handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, allAxisIds, layoutOut); + } + + for(i = 0; i < matchGroups.length; i++) { + var group = matchGroups[i]; + var rng = null; + var autorange = null; + var axId; + + // find 'matching' range attrs + for(axId in group) { + axLayoutOut = layoutOut[id2name(axId)]; + if(!axLayoutOut.matches) { + rng = axLayoutOut.range; + autorange = axLayoutOut.autorange; + } + } + // if `ax.matches` values are reciprocal, + // pick values of first axis in group + if(rng === null || autorange === null) { + for(axId in group) { + axLayoutOut = layoutOut[id2name(axId)]; + rng = axLayoutOut.range; + autorange = axLayoutOut.autorange; + break; + } + } + // apply matching range attrs + for(axId in group) { + axLayoutOut = layoutOut[id2name(axId)]; + if(axLayoutOut.matches) { + axLayoutOut.range = rng.slice(); + axLayoutOut.autorange = autorange; + } + axLayoutOut._matchGroup = group; + } + + // remove matching axis from scaleanchor constraint groups (for now) + if(constraintGroups.length) { + for(axId in group) { + for(j = 0; j < constraintGroups.length; j++) { + var group2 = constraintGroups[j]; + for(var axId2 in group2) { + if(axId === axId2) { + Lib.warn('Axis ' + axId2 + ' is set with both ' + + 'a *scaleanchor* and *matches* constraint; ' + + 'ignoring the scale constraint.'); + + delete group2[axId2]; + if(Object.keys(group2).length < 2) { + constraintGroups.splice(j, 1); + } + } + } + } + } + } + } +}; + +},{"../../components/color":50,"../../lib":169,"../../plot_api/plot_template":203,"../../registry":257,"../layout_attributes":243,"./axis_defaults":215,"./axis_ids":216,"./constraints":220,"./layout_attributes":225,"./position_defaults":228,"./type_defaults":236}],227:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":49,"../../lib":169,"tinycolor2":33}],228:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169,"fast-isnumeric":17}],229:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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) + ]; +}; + +},{"../../constants/alignment":145}],230:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var polybool = _dereq_('polybooljs'); + +var Registry = _dereq_('../../registry'); +var Color = _dereq_('../../components/color'); +var Fx = _dereq_('../../components/fx'); + +var Lib = _dereq_('../../lib'); +var polygon = _dereq_('../../lib/polygon'); +var throttle = _dereq_('../../lib/throttle'); +var makeEventData = _dereq_('../../components/fx/helpers').makeEventData; +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; + +function getAxId(ax) { return ax._id; } + +function prepSelect(e, startX, startY, dragOptions, mode) { + var gd = dragOptions.gd; + var fullLayout = gd._fullLayout; + var zoomLayer = fullLayout._zoomlayer; + var dragBBox = dragOptions.element.getBoundingClientRect(); + var plotinfo = dragOptions.plotinfo; + var xs = plotinfo.xaxis._offset; + var ys = plotinfo.yaxis._offset; + var x0 = startX - dragBBox.left; + var y0 = startY - dragBBox.top; + 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; + + var filterPoly, selectionTester, mergedPolygons, currentPolygon; + var i, searchInfo, eventData; + + coerceSelectionsCache(e, gd, dragOptions); + + if(mode === 'lasso') { + filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX); + } + + var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data([1, 2]); + + outlines.enter() + .append('path') + .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; }) + .attr('transform', 'translate(' + xs + ', ' + ys + ')') + .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', 'translate(' + xs + ', ' + ys + ')') + .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); + + // in v2 (once log ranges are fixed), + // we'll be able to p2r here for all axis types + function p2r(ax, v) { + return ax.type === 'log' ? ax.p2d(v) : ax.p2r(v); + } + + function axValue(ax) { + var index = (ax._id.charAt(0) === 'y') ? 1 : 0; + return function(v) { return p2r(ax, v[index]); }; + } + + function ascending(a, b) { return a - b; } + + // allow subplots to override fillRangeItems routine + var fillRangeItems; + + if(plotinfo.fillRangeItems) { + fillRangeItems = plotinfo.fillRangeItems; + } else { + if(mode === 'select') { + 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 { + 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, dx0 + x0)); + y1 = Math.max(0, Math.min(ph, dy0 + y0)); + + var dx = Math.abs(x1 - x0); + var dy = Math.abs(y1 - y0); + + if(mode === 'select') { + var direction = fullLayout.selectdirection; + + if(fullLayout.selectdirection === '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 = fullLayout.selectdirection; + } + + if(direction === 'h') { + // horizontal motion: make a vertical box + currentPolygon = [[x0, 0], [x0, ph], [x1, ph], [x1, 0]]; + currentPolygon.xmin = Math.min(x0, x1); + currentPolygon.xmax = Math.max(x0, x1); + currentPolygon.ymin = Math.min(0, ph); + currentPolygon.ymax = Math.max(0, ph); + // 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: make a horizontal box + currentPolygon = [[0, y0], [0, y1], [pw, y1], [pw, y0]]; + currentPolygon.xmin = Math.min(0, pw); + currentPolygon.xmax = Math.max(0, pw); + currentPolygon.ymin = Math.min(y0, y1); + currentPolygon.ymax = 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 = [[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(mode === 'lasso') { + 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); + } + + // draw selection + drawSelection(mergedPolygons, outlines); + + + 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) { + var clickmode = fullLayout.clickmode; + + corners.remove(); + + 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 v2 - 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 v2 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); + }; +} + +function selectOnClick(evt, gd, xAxes, yAxes, subplot, dragOptions, polygonOutlines) { + var hoverData = gd._hoverdata; + var clickmode = gd._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) drawSelection(dragOptions.mergedPolygons, polygonOutlines); + + 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) { + var fullLayout = gd._fullLayout; + var plotinfo = dragOptions.plotinfo; + + var selectingOnSameSubplot = ( + fullLayout._lastSelectedSubplot && + fullLayout._lastSelectedSubplot === plotinfo.id + ); + var hasModifierKey = evt.shiftKey || evt.altKey; + + 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 plotinfo = dragOptions.plotinfo; + + plotinfo.selection = {}; + plotinfo.selection.selectionDefs = dragOptions.selectionDefs = []; + plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = []; +} + +function determineSearchTraces(gd, xAxes, yAxes, subplot) { + var searchTraces = []; + var xAxisIds = xAxes.map(getAxId); + var yAxisIds = yAxes.map(getAxId); + 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 drawSelection(polygons, outlines) { + var paths = []; + var i, d; + + for(i = 0; i < polygons.length; i++) { + var ppts = polygons[i]; + paths.push(ppts.join('L') + 'L' + ppts[0]); + } + + d = polygons.length > 0 ? + 'M' + paths.join('M') + 'Z' : + 'M0,0Z'; + outlines.attr('d', d); +} + +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; +} + +// until we get around to persistent selections, remove the outline +// here. The selection itself will be removed when the plot redraws +// at the end. +function clearSelect(gd) { + var fullLayout = gd._fullLayout || {}; + var zoomlayer = fullLayout._zoomlayer; + if(zoomlayer) { + zoomlayer.selectAll('.select-outline').remove(); + } +} + +module.exports = { + prepSelect: prepSelect, + clearSelect: clearSelect, + selectOnClick: selectOnClick +}; + +},{"../../components/color":50,"../../components/fx":89,"../../components/fx/helpers":85,"../../lib":169,"../../lib/clear_gl_canvases":158,"../../lib/polygon":181,"../../lib/throttle":191,"../../plot_api/subroutines":204,"../../registry":257,"./axis_ids":216,"./constants":219,"polybooljs":24}],231:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +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 constants = _dereq_('./constants'); +var axisIds = _dereq_('./axis_ids'); + +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 v2.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) { + // 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 l2p(v) { + if(!isNumeric(v)) return BADNUM; + + // include 2 fractional digits on pixel, for PDF zooming etc + return d3.round(ax._b + ax._m * v, 2); + } + + function p2l(px) { return (px - ax._b) / ax._m; } + + // 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 = getCategoryPosition(v); + return index !== undefined ? index : ax.fraction2r(0.5); + }; + + 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(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 matchGroups = fullLayout._axisMatchGroups; + if(matchGroups && matchGroups.length && ax._categories.length === 0) { + for(i = 0; i < matchGroups.length; i++) { + var group = matchGroups[i]; + if(group[axId]) { + 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(!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 occuring, 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); + + if(axLetter === 'y') { + 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; + } + + if(!isFinite(ax._m) || !isFinite(ax._b) || ax._length < 0) { + fullLayout._replotting = false; + throw new Error('Something went wrong with axis scaling'); + } + }; + + // 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) { + 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); + } + } 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; + } + } + + 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; + } + }; + + // should skip if not category nor multicategory + ax.clearCalc = function() { + var emptyCategories = function() { + ax._categories = []; + ax._categoriesMap = {}; + }; + + var matchGroups = fullLayout._axisMatchGroups; + + if(matchGroups && matchGroups.length) { + var found = false; + + for(var i = 0; i < matchGroups.length; i++) { + var group = matchGroups[i]; + + if(group[axId]) { + found = true; + 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 { + emptyCategories(); + } + break; + } + } + if(!found) emptyCategories(); + } else { + 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 = []; + var emptyCategories = function() { + ax._categories = []; + ax._categoriesMap = {}; + }; + + 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.utc : d3.time.format.utc; + 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":149,"../../lib":169,"./axis_ids":216,"./constants":219,"d3":15,"fast-isnumeric":17}],232:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = _dereq_('../../lib'); +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; + // as with titlefont.color, inherit axis.color only if one was + // explicitly provided + var dfltFontColor = (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'); + var tickformatStops = containerIn.tickformatstops; + if(Array.isArray(tickformatStops) && tickformatStops.length) { + handleArrayContainerDefaults(containerIn, containerOut, { + name: 'tickformatstops', + inclusionAttr: 'enabled', + handleItemDefaults: tickformatstopDefaults + }); + } + if(!tickFormat && axType !== 'date') { + coerce('showexponent', showAttrDflt); + coerce('exponentformat'); + 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'); + } +} + +},{"../../lib":169,"../array_container_defaults":209,"./layout_attributes":225}],233:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169,"./layout_attributes":225}],234:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var cleanTicks = _dereq_('./clean_ticks'); + +module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) { + var tickmode; + + if(containerIn.tickmode === 'array' && + (axType === 'log' || axType === 'date')) { + tickmode = containerOut.tickmode = 'auto'; + } else { + var tickmodeDefault = Array.isArray(containerIn.tickvals) ? 'array' : + containerIn.dtick ? 'linear' : + 'auto'; + 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( + containerIn.dtick, axType); + containerOut.tick0 = cleanTicks.tick0( + containerIn.tick0, axType, containerOut.calendar, dtick); + } else if(axType !== 'multicategory') { + var tickvals = coerce('tickvals'); + if(tickvals === undefined) containerOut.tickmode = 'auto'; + else coerce('ticktext'); + } +}; + +},{"./clean_ticks":218}],235:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var Registry = _dereq_('../../registry'); +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 xr0 = edit.xr0; + var xr1 = edit.xr1; + var xlen = xa._length; + var yr0 = edit.yr0; + var yr1 = edit.yr1; + var ylen = ya._length; + + var editX = !!xr1; + var editY = !!yr1; + var viewBox = []; + + if(editX) { + 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] = xr0[0] * (1 - progress) + progress * xr1[0]; + xa.range[1] = xr0[1] * (1 - progress) + progress * xr1[1]; + } else { + viewBox[0] = 0; + viewBox[2] = xlen; + } + + if(editY) { + 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] = yr0[0] * (1 - progress) + progress * yr1[0]; + ya.range[1] = 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]; + if(edit.xr1) aobj[edit.plotinfo.xaxis._name + '.range'] = edit.xr1.slice(); + if(edit.yr1) aobj[edit.plotinfo.yaxis._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]; + if(edit.xr0) aobj[edit.plotinfo.xaxis._name + '.range'] = edit.xr0.slice(); + if(edit.yr0) aobj[edit.plotinfo.yaxis._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":71,"../../registry":257,"./axes":213,"d3":15}],236:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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) { + 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); + + // 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')}; + var i; + + // 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":257,"./axis_autotype":214}],237:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../registry":257}],238:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + } + } + + coerce('domain.x', dfltX); + coerce('domain.y', dfltY); +}; + +},{"../lib/extend":164}],239:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],240:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', + + + } +}; + +},{}],241:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":257,"./cartesian/constants":219}],242:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; + +},{}],243:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var fontAttrs = _dereq_('./font_attributes'); +var animationAttrs = _dereq_('./animation_attributes'); +var colorAttrs = _dereq_('../components/color/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' + }, + 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' + }, + 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', + + }, + 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', + + }, + 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', + + }, + editType: 'modebar' + }, + + 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":49,"../lib/extend":164,"./animation_attributes":208,"./font_attributes":239,"./pad_attributes":244}],244:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 + }; +}; + +},{}],245:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +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 animationAttrs = _dereq_('./animation_attributes'); +var frameAttrs = _dereq_('./frame_attributes'); + +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); + + var fullLayout = gd._fullLayout || {}; + var hasPolar = fullLayout._has && fullLayout._has('polar'); + var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r; + + // do not work if polar is present + if(hasLegacyPolar) return; + + return new Promise(function(resolve) { + setTimeout(function() { + 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); + + return 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); + + 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; + resolve(gd); + }); + }, 100); + }); +}; + + +// 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 Plotly.plot() 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) { + gd.emit('plotly_beforeexport'); + + var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL; + + 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 + ); + + // TODO remove in v2.0.0 + // add has-plot-type refs to fullLayout for backward compatibility + newFullLayout._hasCartesian = newFullLayout._has('cartesian'); + newFullLayout._hasGeo = newFullLayout._has('geo'); + newFullLayout._hasGL3D = newFullLayout._has('gl3d'); + newFullLayout._hasGL2D = newFullLayout._has('gl2d'); + newFullLayout._hasTernary = newFullLayout._has('ternary'); + newFullLayout._hasPie = newFullLayout._has('pie'); + + // 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); + + // 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) { + oldFullLayout._zoomlayer.selectAll('.select-outline').remove(); + } + + + // 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 v2, 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 d3.locale(formatObj); +} + +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); + } +}; + +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 = ''; + + // 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? but for now just drop empty gl2d subplots + if(basePlotModule.name !== 'gl2d' || visible) { + 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')) { + traceOut._dfltShowLegend = true; + coerce('showlegend'); + coerce('legendgroup'); + } 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; + } + + 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'); + + // Make sure that autosize is defaulted to *true* + // on layouts with no set width and height for backward compatibly, + // in particular https://plot.ly/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 v2. + 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); + + coerce('modebar.orientation'); + coerce('modebar.bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5)); + var modebarDefaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor)); + coerce('modebar.color', Color.addOpacity(modebarDefaultColor, 0.3)); + coerce('modebar.activecolor', Color.addOpacity(modebarDefaultColor, 0.7)); + coerce('modebar.uirevision', uirevision); + + 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); +}; + +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 = parseFloat(computedStyle.width) || parseFloat(computedStyle.maxWidth) || fullLayout.width; + newHeight = parseFloat(computedStyle.height) || parseFloat(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; + } + if(fullLayout._geocontainer !== undefined) fullLayout._geocontainer.remove(); + + // 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.framework; + 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 Plotly.plot 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 = {}; +} + +/** + * 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 pushMargin = fullLayout._pushmargin; + var pushMarginIds = fullLayout._pushmarginIds; + + if(fullLayout.margin.autoexpand !== false) { + if(!o) { + delete pushMargin[id]; + delete pushMarginIds[id]; + } else { + var pad = o.pad; + if(pad === undefined) { + var margin = fullLayout.margin; + // 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(o.l + o.r > fullLayout.width * 0.5) o.l = o.r = 0; + if(o.b + o.t > fullLayout.height * 0.5) o.b = o.t = 0; + + 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) { + plots.doAutoMargin(gd); + } + } +}; + +plots.doAutoMargin = function(gd) { + var fullLayout = gd._fullLayout; + 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 width = fullLayout.width; + var height = fullLayout.height; + 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 >= 0 && newR >= 0 && width - (newL + newR) > 0 && 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 >= 0 && newT >= 0 && height - (newT + newB) > 0 && newB + newT > mb + mt) { + mb = newB; + mt = newT; + } + } + } + } + } + } + + 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; + } + return Registry.call('plot', gd); + } +}; + +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 + * @returns {Object|String} + */ +plots.graphJson = function(gd, dataonly, mode, output, useDefaults) { + // 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) { + if(typeof d === 'function') { + return null; + } + if(Lib.isPlainObject(d)) { + var o = {}; + var v, src; + for(v in d) { + // remove private elements and functions + // _ is for private, [ is a mistake ie [object Object] + if(typeof d[v] === 'function' || + ['_', '['].indexOf(v.charAt(0)) !== -1) { + continue; + } + + // 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') { + continue; + } + } 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)) { + continue; + } + } + } 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) { + continue; + } + } + + // OK, we're including this... recurse into it + o[v] = stripObj(d[v]); + } + return o; + } + + if(Array.isArray(d)) { + return d.map(stripObj); + } + + 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(gd.framework && gd.framework.isPolar) obj = gd.framework.getConfig(); + + if(frames) obj.frames = stripObj(frames); + + 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; + 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(); + } + + var yr1; + 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(); + } + + var editX; + if(xr0 && xr1 && (xr0[0] !== xr1[0] || xr0[1] !== xr1[1])) { + editX = {xr0: xr0, xr1: xr1}; + } + + var editY; + if(yr0 && yr1 && (yr0[0] !== yr1[0] || yr0[1] !== 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; + if(xr0[0] !== xr1[0] || xr0[1] !== xr1[1]) { + editX = {xr0: xr0, xr1: xr1}; + } + + var editY; + if(yr0[0] !== yr1[0] || yr0[1] !== 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() { + for(var j = 0; j < basePlotModules.length; j++) { + if(basePlotModules[j].transitionAxes) { + basePlotModules[j].transitionAxes(gd, axEdits, axisTransitionOpts, makeCallback); + } + } + } + + function transitionTraces() { + 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; + transitionTraces(); + setTimeout(transitionAxes, transitionOpts.duration); + } else { + axisTransitionOpts = transitionOpts; + transitionedTraces = null; + traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0}); + transitionAxes(); + transitionTraces(); + } + } 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 / funnelarea (and for legend) + fullLayout._piecolormap = {}; + fullLayout._sunburstcolormap = {}; + 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 + ); + } + + 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); + + // '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); + + // '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]; + + // 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]; + var axLetter = ax._id.charAt(0); + + // 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 cd = gd.calcdata[traceIndex]; + for(k = 0; k < cd.length; k++) { + var cdi = cd[k]; + var cat, catIndex, value; + + if(type === 'splom') { + // 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(axLetter === 'y') { + 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++) { + cat = categories[l]; + catIndex = ax._categoriesMap[cat]; + + // 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(type === 'scattergl') { + // If `scattergl`, collect all values stashed under cdi.t + for(l = 0; l < cdi.t.x.length; l++) { + if(axLetter === 'x') { + cat = cdi.t.x[l]; + catIndex = cat; + value = cdi.t.y[l]; + } + + if(axLetter === 'y') { + cat = cdi.t.y[l]; + catIndex = cat; + 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 + if(axLetter === 'x') { + cat = cdi.p + 1 ? cdi.p : cdi.x; + value = cdi.s || cdi.v || cdi.y; + } else if(axLetter === 'y') { + cat = cdi.p + 1 ? cdi.p : cdi.y; + value = cdi.s || cdi.v || cdi.x; + } + if(!Array.isArray(value)) value = [value]; + for(l = 0; l < value.length; l++) { + categoriesValue[cat][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) { + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + ax.clearCalc(); + if(ax.type === 'multicategory') { + ax.setupMultiCategory(fullData); + } + } +} + +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; +}; + +},{"../components/color":50,"../constants/numerical":149,"../lib":169,"../plot_api/plot_schema":202,"../plot_api/plot_template":203,"../registry":257,"./animation_attributes":208,"./attributes":210,"./cartesian/axis_ids":216,"./command":237,"./font_attributes":239,"./frame_attributes":240,"./layout_attributes":243,"d3":15,"fast-isnumeric":17}],246:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var scatterAttrs = _dereq_('../../../traces/scatter/attributes'); +var scatterMarkerAttrs = scatterAttrs.marker; +var extendFlat = _dereq_('../../../lib/extend').extendFlat; + +var deprecationWarning = [ + 'Area traces are deprecated!', + 'Please switch to the *barpolar* trace type.' +].join(' '); + +module.exports = { + r: extendFlat({}, scatterAttrs.r, { + + }), + t: extendFlat({}, scatterAttrs.t, { + + }), + marker: { + color: extendFlat({}, scatterMarkerAttrs.color, { + + }), + size: extendFlat({}, scatterMarkerAttrs.size, { + + }), + symbol: extendFlat({}, scatterMarkerAttrs.symbol, { + + }), + opacity: extendFlat({}, scatterMarkerAttrs.opacity, { + + }), + editType: 'calc' + } +}; + +},{"../../../lib/extend":164,"../../../traces/scatter/attributes":366}],247:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var axesAttrs = _dereq_('../../cartesian/layout_attributes'); +var extendFlat = _dereq_('../../../lib/extend').extendFlat; +var overrideAll = _dereq_('../../../plot_api/edit_types').overrideAll; + +var deprecationWarning = [ + 'Legacy polar charts are deprecated!', + 'Please switch to *polar* subplots.' +].join(' '); + +var domainAttr = extendFlat({}, axesAttrs.domain, { + +}); + +function mergeAttrs(axisName, nonCommonAttrs) { + var commonAttrs = { + showline: { + valType: 'boolean', + + + }, + showticklabels: { + valType: 'boolean', + + + }, + tickorientation: { + valType: 'enumerated', + values: ['horizontal', 'vertical'], + + + }, + ticklen: { + valType: 'number', + min: 0, + + + }, + tickcolor: { + valType: 'color', + + + }, + ticksuffix: { + valType: 'string', + + + }, + endpadding: { + valType: 'number', + + description: deprecationWarning, + }, + visible: { + valType: 'boolean', + + + } + }; + + return extendFlat({}, nonCommonAttrs, commonAttrs); +} + +module.exports = overrideAll({ + radialaxis: mergeAttrs('radial', { + range: { + valType: 'info_array', + + items: [ + { valType: 'number' }, + { valType: 'number' } + ], + + }, + domain: domainAttr, + orientation: { + valType: 'number', + + + } + }), + + angularaxis: mergeAttrs('angular', { + range: { + valType: 'info_array', + + items: [ + { valType: 'number', dflt: 0 }, + { valType: 'number', dflt: 360 } + ], + + }, + domain: domainAttr + }), + + // attributes that appear at layout root + layout: { + direction: { + valType: 'enumerated', + values: ['clockwise', 'counterclockwise'], + + + }, + orientation: { + valType: 'angle', + + + } + } +}, 'plot', 'nested'); + +},{"../../../lib/extend":164,"../../../plot_api/edit_types":196,"../../cartesian/layout_attributes":225}],248:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Polar = module.exports = _dereq_('./micropolar'); + +Polar.manager = _dereq_('./micropolar_manager'); + +},{"./micropolar":249,"./micropolar_manager":250}],249:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +var d3 = _dereq_('d3'); +var Lib = _dereq_('../../../lib'); +var extendDeepAll = Lib.extendDeepAll; +var MID_SHIFT = _dereq_('../../../constants/alignment').MID_SHIFT; + +var µ = module.exports = { version: '0.2.2' }; + +µ.Axis = function module() { + var config = { + data: [], + layout: {} + }, inputConfig = {}, liveConfig = {}; + var svg, container, dispatch = d3.dispatch('hover'), radialScale, angularScale; + var exports = {}; + function render(_container) { + container = _container || container; + var data = config.data; + var axisConfig = config.layout; + if (typeof container == 'string' || container.nodeName) container = d3.select(container); + container.datum(data).each(function(_data, _index) { + var dataOriginal = _data.slice(); + liveConfig = { + data: µ.util.cloneJson(dataOriginal), + layout: µ.util.cloneJson(axisConfig) + }; + var colorIndex = 0; + dataOriginal.forEach(function(d, i) { + if (!d.color) { + d.color = axisConfig.defaultColorRange[colorIndex]; + colorIndex = (colorIndex + 1) % axisConfig.defaultColorRange.length; + } + if (!d.strokeColor) { + d.strokeColor = d.geometry === 'LinePlot' ? d.color : d3.rgb(d.color).darker().toString(); + } + liveConfig.data[i].color = d.color; + liveConfig.data[i].strokeColor = d.strokeColor; + liveConfig.data[i].strokeDash = d.strokeDash; + liveConfig.data[i].strokeSize = d.strokeSize; + }); + var data = dataOriginal.filter(function(d, i) { + var visible = d.visible; + return typeof visible === 'undefined' || visible === true; + }); + var isStacked = false; + var dataWithGroupId = data.map(function(d, i) { + isStacked = isStacked || typeof d.groupId !== 'undefined'; + return d; + }); + if (isStacked) { + var grouped = d3.nest().key(function(d, i) { + return typeof d.groupId != 'undefined' ? d.groupId : 'unstacked'; + }).entries(dataWithGroupId); + var dataYStack = []; + var stacked = grouped.map(function(d, i) { + if (d.key === 'unstacked') return d.values; else { + var prevArray = d.values[0].r.map(function(d, i) { + return 0; + }); + d.values.forEach(function(d, i, a) { + d.yStack = [ prevArray ]; + dataYStack.push(prevArray); + prevArray = µ.util.sumArrays(d.r, prevArray); + }); + return d.values; + } + }); + data = d3.merge(stacked); + } + data.forEach(function(d, i) { + d.t = Array.isArray(d.t[0]) ? d.t : [ d.t ]; + d.r = Array.isArray(d.r[0]) ? d.r : [ d.r ]; + }); + var radius = Math.min(axisConfig.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2; + radius = Math.max(10, radius); + var chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ]; + var extent; + if (isStacked) { + var highestStackedValue = d3.max(µ.util.sumArrays(µ.util.arrayLast(data).r[0], µ.util.arrayLast(dataYStack))); + extent = [ 0, highestStackedValue ]; + } else extent = d3.extent(µ.util.flattenArray(data.map(function(d, i) { + return d.r; + }))); + if (axisConfig.radialAxis.domain != µ.DATAEXTENT) extent[0] = 0; + radialScale = d3.scale.linear().domain(axisConfig.radialAxis.domain != µ.DATAEXTENT && axisConfig.radialAxis.domain ? axisConfig.radialAxis.domain : extent).range([ 0, radius ]); + liveConfig.layout.radialAxis.domain = radialScale.domain(); + var angularDataMerged = µ.util.flattenArray(data.map(function(d, i) { + return d.t; + })); + var isOrdinal = typeof angularDataMerged[0] === 'string'; + var ticks; + if (isOrdinal) { + angularDataMerged = µ.util.deduplicate(angularDataMerged); + ticks = angularDataMerged.slice(); + angularDataMerged = d3.range(angularDataMerged.length); + data = data.map(function(d, i) { + var result = d; + d.t = [ angularDataMerged ]; + if (isStacked) result.yStack = d.yStack; + return result; + }); + } + var hasOnlyLineOrDotPlot = data.filter(function(d, i) { + return d.geometry === 'LinePlot' || d.geometry === 'DotPlot'; + }).length === data.length; + var needsEndSpacing = axisConfig.needsEndSpacing === null ? isOrdinal || !hasOnlyLineOrDotPlot : axisConfig.needsEndSpacing; + var useProvidedDomain = axisConfig.angularAxis.domain && axisConfig.angularAxis.domain != µ.DATAEXTENT && !isOrdinal && axisConfig.angularAxis.domain[0] >= 0; + var angularDomain = useProvidedDomain ? axisConfig.angularAxis.domain : d3.extent(angularDataMerged); + var angularDomainStep = Math.abs(angularDataMerged[1] - angularDataMerged[0]); + if (hasOnlyLineOrDotPlot && !isOrdinal) angularDomainStep = 0; + var angularDomainWithPadding = angularDomain.slice(); + if (needsEndSpacing && isOrdinal) angularDomainWithPadding[1] += angularDomainStep; + var tickCount = axisConfig.angularAxis.ticksCount || 4; + if (tickCount > 8) tickCount = tickCount / (tickCount / 8) + tickCount % 8; + if (axisConfig.angularAxis.ticksStep) { + tickCount = (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / tickCount; + } + var angularTicksStep = axisConfig.angularAxis.ticksStep || (angularDomainWithPadding[1] - angularDomainWithPadding[0]) / (tickCount * (axisConfig.minorTicks + 1)); + if (ticks) angularTicksStep = Math.max(Math.round(angularTicksStep), 1); + if (!angularDomainWithPadding[2]) angularDomainWithPadding[2] = angularTicksStep; + var angularAxisRange = d3.range.apply(this, angularDomainWithPadding); + angularAxisRange = angularAxisRange.map(function(d, i) { + return parseFloat(d.toPrecision(12)); + }); + angularScale = d3.scale.linear().domain(angularDomainWithPadding.slice(0, 2)).range(axisConfig.direction === 'clockwise' ? [ 0, 360 ] : [ 360, 0 ]); + liveConfig.layout.angularAxis.domain = angularScale.domain(); + liveConfig.layout.angularAxis.endPadding = needsEndSpacing ? angularDomainStep : 0; + svg = d3.select(this).select('svg.chart-root'); + if (typeof svg === 'undefined' || svg.empty()) { + var skeleton = "' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '"; + var doc = new DOMParser().parseFromString(skeleton, 'application/xml'); + var newSvg = this.appendChild(this.ownerDocument.importNode(doc.documentElement, true)); + svg = d3.select(newSvg); + } + svg.select('.guides-group').style({ + 'pointer-events': 'none' + }); + svg.select('.angular.axis-group').style({ + 'pointer-events': 'none' + }); + svg.select('.radial.axis-group').style({ + 'pointer-events': 'none' + }); + var chartGroup = svg.select('.chart-group'); + var lineStyle = { + fill: 'none', + stroke: axisConfig.tickColor + }; + var fontStyle = { + 'font-size': axisConfig.font.size, + 'font-family': axisConfig.font.family, + fill: axisConfig.font.color, + 'text-shadow': [ '-1px 0px', '1px -1px', '-1px 1px', '1px 1px' ].map(function(d, i) { + return ' ' + d + ' 0 ' + axisConfig.font.outlineColor; + }).join(',') + }; + var legendContainer; + if (axisConfig.showLegend) { + legendContainer = svg.select('.legend-group').attr({ + transform: 'translate(' + [ radius, axisConfig.margin.top ] + ')' + }).style({ + display: 'block' + }); + var elements = data.map(function(d, i) { + var datumClone = µ.util.cloneJson(d); + datumClone.symbol = d.geometry === 'DotPlot' ? d.dotType || 'circle' : d.geometry != 'LinePlot' ? 'square' : 'line'; + datumClone.visibleInLegend = typeof d.visibleInLegend === 'undefined' || d.visibleInLegend; + datumClone.color = d.geometry === 'LinePlot' ? d.strokeColor : d.color; + return datumClone; + }); + + µ.Legend().config({ + data: data.map(function(d, i) { + return d.name || 'Element' + i; + }), + legendConfig: extendDeepAll({}, + µ.Legend.defaultConfig().legendConfig, + { + container: legendContainer, + elements: elements, + reverseOrder: axisConfig.legend.reverseOrder + } + ) + })(); + + var legendBBox = legendContainer.node().getBBox(); + radius = Math.min(axisConfig.width - legendBBox.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig.margin.top - axisConfig.margin.bottom) / 2; + radius = Math.max(10, radius); + chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ]; + radialScale.range([ 0, radius ]); + liveConfig.layout.radialAxis.domain = radialScale.domain(); + legendContainer.attr('transform', 'translate(' + [ chartCenter[0] + radius, chartCenter[1] - radius ] + ')'); + } else { + legendContainer = svg.select('.legend-group').style({ + display: 'none' + }); + } + svg.attr({ + width: axisConfig.width, + height: axisConfig.height + }).style({ + opacity: axisConfig.opacity + }); + chartGroup.attr('transform', 'translate(' + chartCenter + ')').style({ + cursor: 'crosshair' + }); + var centeringOffset = [ (axisConfig.width - (axisConfig.margin.left + axisConfig.margin.right + radius * 2 + (legendBBox ? legendBBox.width : 0))) / 2, (axisConfig.height - (axisConfig.margin.top + axisConfig.margin.bottom + radius * 2)) / 2 ]; + centeringOffset[0] = Math.max(0, centeringOffset[0]); + centeringOffset[1] = Math.max(0, centeringOffset[1]); + svg.select('.outer-group').attr('transform', 'translate(' + centeringOffset + ')'); + if (axisConfig.title && axisConfig.title.text) { + var title = svg.select('g.title-group text').style(fontStyle).text(axisConfig.title.text); + var titleBBox = title.node().getBBox(); + title.attr({ + x: chartCenter[0] - titleBBox.width / 2, + y: chartCenter[1] - radius - 20 + }); + } + var radialAxis = svg.select('.radial.axis-group'); + if (axisConfig.radialAxis.gridLinesVisible) { + var gridCircles = radialAxis.selectAll('circle.grid-circle').data(radialScale.ticks(5)); + gridCircles.enter().append('circle').attr({ + 'class': 'grid-circle' + }).style(lineStyle); + gridCircles.attr('r', radialScale); + gridCircles.exit().remove(); + } + radialAxis.select('circle.outside-circle').attr({ + r: radius + }).style(lineStyle); + var backgroundCircle = svg.select('circle.background-circle').attr({ + r: radius + }).style({ + fill: axisConfig.backgroundColor, + stroke: axisConfig.stroke + }); + function currentAngle(d, i) { + return angularScale(d) % 360 + axisConfig.orientation; + } + if (axisConfig.radialAxis.visible) { + var axis = d3.svg.axis().scale(radialScale).ticks(5).tickSize(5); + radialAxis.call(axis).attr({ + transform: 'rotate(' + axisConfig.radialAxis.orientation + ')' + }); + radialAxis.selectAll('.domain').style(lineStyle); + radialAxis.selectAll('g>text').text(function(d, i) { + return this.textContent + axisConfig.radialAxis.ticksSuffix; + }).style(fontStyle).style({ + 'text-anchor': 'start' + }).attr({ + x: 0, + y: 0, + dx: 0, + dy: 0, + transform: function(d, i) { + if (axisConfig.radialAxis.tickOrientation === 'horizontal') { + return 'rotate(' + -axisConfig.radialAxis.orientation + ') translate(' + [ 0, fontStyle['font-size'] ] + ')'; + } else return 'translate(' + [ 0, fontStyle['font-size'] ] + ')'; + } + }); + radialAxis.selectAll('g>line').style({ + stroke: 'black' + }); + } + var angularAxis = svg.select('.angular.axis-group').selectAll('g.angular-tick').data(angularAxisRange); + var angularAxisEnter = angularAxis.enter().append('g').classed('angular-tick', true); + angularAxis.attr({ + transform: function(d, i) { + return 'rotate(' + currentAngle(d, i) + ')'; + } + }).style({ + display: axisConfig.angularAxis.visible ? 'block' : 'none' + }); + angularAxis.exit().remove(); + angularAxisEnter.append('line').classed('grid-line', true).classed('major', function(d, i) { + return i % (axisConfig.minorTicks + 1) == 0; + }).classed('minor', function(d, i) { + return !(i % (axisConfig.minorTicks + 1) == 0); + }).style(lineStyle); + angularAxisEnter.selectAll('.minor').style({ + stroke: axisConfig.minorTickColor + }); + angularAxis.select('line.grid-line').attr({ + x1: axisConfig.tickLength ? radius - axisConfig.tickLength : 0, + x2: radius + }).style({ + display: axisConfig.angularAxis.gridLinesVisible ? 'block' : 'none' + }); + angularAxisEnter.append('text').classed('axis-text', true).style(fontStyle); + var ticksText = angularAxis.select('text.axis-text').attr({ + x: radius + axisConfig.labelOffset, + dy: MID_SHIFT + 'em', + transform: function(d, i) { + var angle = currentAngle(d, i); + var rad = radius + axisConfig.labelOffset; + var orient = axisConfig.angularAxis.tickOrientation; + if (orient == 'horizontal') return 'rotate(' + -angle + ' ' + rad + ' 0)'; else if (orient == 'radial') return angle < 270 && angle > 90 ? 'rotate(180 ' + rad + ' 0)' : null; else return 'rotate(' + (angle <= 180 && angle > 0 ? -90 : 90) + ' ' + rad + ' 0)'; + } + }).style({ + 'text-anchor': 'middle', + display: axisConfig.angularAxis.labelsVisible ? 'block' : 'none' + }).text(function(d, i) { + if (i % (axisConfig.minorTicks + 1) != 0) return ''; + if (ticks) { + return ticks[d] + axisConfig.angularAxis.ticksSuffix; + } else return d + axisConfig.angularAxis.ticksSuffix; + }).style(fontStyle); + if (axisConfig.angularAxis.rewriteTicks) ticksText.text(function(d, i) { + if (i % (axisConfig.minorTicks + 1) != 0) return ''; + return axisConfig.angularAxis.rewriteTicks(this.textContent, i); + }); + var rightmostTickEndX = d3.max(chartGroup.selectAll('.angular-tick text')[0].map(function(d, i) { + return d.getCTM().e + d.getBBox().width; + })); + legendContainer.attr({ + transform: 'translate(' + [ radius + rightmostTickEndX, axisConfig.margin.top ] + ')' + }); + var hasGeometry = svg.select('g.geometry-group').selectAll('g').size() > 0; + var geometryContainer = svg.select('g.geometry-group').selectAll('g.geometry').data(data); + geometryContainer.enter().append('g').attr({ + 'class': function(d, i) { + return 'geometry geometry' + i; + } + }); + geometryContainer.exit().remove(); + if (data[0] || hasGeometry) { + var geometryConfigs = []; + data.forEach(function(d, i) { + var geometryConfig = {}; + geometryConfig.radialScale = radialScale; + geometryConfig.angularScale = angularScale; + geometryConfig.container = geometryContainer.filter(function(dB, iB) { + return iB == i; + }); + geometryConfig.geometry = d.geometry; + geometryConfig.orientation = axisConfig.orientation; + geometryConfig.direction = axisConfig.direction; + geometryConfig.index = i; + geometryConfigs.push({ + data: d, + geometryConfig: geometryConfig + }); + }); + var geometryConfigsGrouped = d3.nest().key(function(d, i) { + return typeof d.data.groupId != 'undefined' || 'unstacked'; + }).entries(geometryConfigs); + var geometryConfigsGrouped2 = []; + geometryConfigsGrouped.forEach(function(d, i) { + if (d.key === 'unstacked') geometryConfigsGrouped2 = geometryConfigsGrouped2.concat(d.values.map(function(d, i) { + return [ d ]; + })); else geometryConfigsGrouped2.push(d.values); + }); + geometryConfigsGrouped2.forEach(function(d, i) { + var geometry; + if (Array.isArray(d)) geometry = d[0].geometryConfig.geometry; else geometry = d.geometryConfig.geometry; + var finalGeometryConfig = d.map(function(dB, iB) { + return extendDeepAll(µ[geometry].defaultConfig(), dB); + }); + µ[geometry]().config(finalGeometryConfig)(); + }); + } + var guides = svg.select('.guides-group'); + var tooltipContainer = svg.select('.tooltips-group'); + var angularTooltip = µ.tooltipPanel().config({ + container: tooltipContainer, + fontSize: 8 + })(); + var radialTooltip = µ.tooltipPanel().config({ + container: tooltipContainer, + fontSize: 8 + })(); + var geometryTooltip = µ.tooltipPanel().config({ + container: tooltipContainer, + hasTick: true + })(); + var angularValue, radialValue; + if (!isOrdinal) { + var angularGuideLine = guides.select('line').attr({ + x1: 0, + y1: 0, + y2: 0 + }).style({ + stroke: 'grey', + 'pointer-events': 'none' + }); + chartGroup.on('mousemove.angular-guide', function(d, i) { + var mouseAngle = µ.util.getMousePos(backgroundCircle).angle; + angularGuideLine.attr({ + x2: -radius, + transform: 'rotate(' + mouseAngle + ')' + }).style({ + opacity: .5 + }); + var angleWithOriginOffset = (mouseAngle + 180 + 360 - axisConfig.orientation) % 360; + angularValue = angularScale.invert(angleWithOriginOffset); + var pos = µ.util.convertToCartesian(radius + 12, mouseAngle + 180); + angularTooltip.text(µ.util.round(angularValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]); + }).on('mouseout.angular-guide', function(d, i) { + guides.select('line').style({ + opacity: 0 + }); + }); + } + var angularGuideCircle = guides.select('circle').style({ + stroke: 'grey', + fill: 'none' + }); + chartGroup.on('mousemove.radial-guide', function(d, i) { + var r = µ.util.getMousePos(backgroundCircle).radius; + angularGuideCircle.attr({ + r: r + }).style({ + opacity: .5 + }); + radialValue = radialScale.invert(µ.util.getMousePos(backgroundCircle).radius); + var pos = µ.util.convertToCartesian(r, axisConfig.radialAxis.orientation); + radialTooltip.text(µ.util.round(radialValue)).move([ pos[0] + chartCenter[0], pos[1] + chartCenter[1] ]); + }).on('mouseout.radial-guide', function(d, i) { + angularGuideCircle.style({ + opacity: 0 + }); + geometryTooltip.hide(); + angularTooltip.hide(); + radialTooltip.hide(); + }); + svg.selectAll('.geometry-group .mark').on('mouseover.tooltip', function(d, i) { + var el = d3.select(this); + var color = this.style.fill; + var newColor = 'black'; + var opacity = this.style.opacity || 1; + el.attr({ + 'data-opacity': opacity + }); + if (color && color !== 'none') { + el.attr({ + 'data-fill': color + }); + newColor = d3.hsl(color).darker().toString(); + el.style({ + fill: newColor, + opacity: 1 + }); + var textData = { + t: µ.util.round(d[0]), + r: µ.util.round(d[1]) + }; + if (isOrdinal) textData.t = ticks[d[0]]; + var text = 't: ' + textData.t + ', r: ' + textData.r; + var bbox = this.getBoundingClientRect(); + var svgBBox = svg.node().getBoundingClientRect(); + var pos = [ bbox.left + bbox.width / 2 - centeringOffset[0] - svgBBox.left, bbox.top + bbox.height / 2 - centeringOffset[1] - svgBBox.top ]; + geometryTooltip.config({ + color: newColor + }).text(text); + geometryTooltip.move(pos); + } else { + color = this.style.stroke || 'black'; + el.attr({ + 'data-stroke': color + }); + newColor = d3.hsl(color).darker().toString(); + el.style({ + stroke: newColor, + opacity: 1 + }); + } + }).on('mousemove.tooltip', function(d, i) { + if (d3.event.which != 0) return false; + if (d3.select(this).attr('data-fill')) geometryTooltip.show(); + }).on('mouseout.tooltip', function(d, i) { + geometryTooltip.hide(); + var el = d3.select(this); + var fillColor = el.attr('data-fill'); + if (fillColor) el.style({ + fill: fillColor, + opacity: el.attr('data-opacity') + }); else el.style({ + stroke: el.attr('data-stroke'), + opacity: el.attr('data-opacity') + }); + }); + }); + return exports; + } + exports.render = function(_container) { + render(_container); + return this; + }; + exports.config = function(_x) { + if (!arguments.length) return config; + var xClone = µ.util.cloneJson(_x); + xClone.data.forEach(function(d, i) { + if (!config.data[i]) config.data[i] = {}; + extendDeepAll(config.data[i], µ.Axis.defaultConfig().data[0]); + extendDeepAll(config.data[i], d); + }); + extendDeepAll(config.layout, µ.Axis.defaultConfig().layout); + extendDeepAll(config.layout, xClone.layout); + return this; + }; + exports.getLiveConfig = function() { + return liveConfig; + }; + exports.getinputConfig = function() { + return inputConfig; + }; + exports.radialScale = function(_x) { + return radialScale; + }; + exports.angularScale = function(_x) { + return angularScale; + }; + exports.svg = function() { + return svg; + }; + d3.rebind(exports, dispatch, 'on'); + return exports; +}; + +µ.Axis.defaultConfig = function(d, i) { + var config = { + data: [ { + t: [ 1, 2, 3, 4 ], + r: [ 10, 11, 12, 13 ], + name: 'Line1', + geometry: 'LinePlot', + color: null, + strokeDash: 'solid', + strokeColor: null, + strokeSize: '1', + visibleInLegend: true, + opacity: 1 + } ], + layout: { + defaultColorRange: d3.scale.category10().range(), + title: null, + height: 450, + width: 500, + margin: { + top: 40, + right: 40, + bottom: 40, + left: 40 + }, + font: { + size: 12, + color: 'gray', + outlineColor: 'white', + family: 'Tahoma, sans-serif' + }, + direction: 'clockwise', + orientation: 0, + labelOffset: 10, + radialAxis: { + domain: null, + orientation: -45, + ticksSuffix: '', + visible: true, + gridLinesVisible: true, + tickOrientation: 'horizontal', + rewriteTicks: null + }, + angularAxis: { + domain: [ 0, 360 ], + ticksSuffix: '', + visible: true, + gridLinesVisible: true, + labelsVisible: true, + tickOrientation: 'horizontal', + rewriteTicks: null, + ticksCount: null, + ticksStep: null + }, + minorTicks: 0, + tickLength: null, + tickColor: 'silver', + minorTickColor: '#eee', + backgroundColor: 'none', + needsEndSpacing: null, + showLegend: true, + legend: { + reverseOrder: false + }, + opacity: 1 + } + }; + return config; +}; + +µ.util = {}; + +µ.DATAEXTENT = 'dataExtent'; + +µ.AREA = 'AreaChart'; + +µ.LINE = 'LinePlot'; + +µ.DOT = 'DotPlot'; + +µ.BAR = 'BarChart'; + +µ.util._override = function(_objA, _objB) { + for (var x in _objA) if (x in _objB) _objB[x] = _objA[x]; +}; + +µ.util._extend = function(_objA, _objB) { + for (var x in _objA) _objB[x] = _objA[x]; +}; + +µ.util._rndSnd = function() { + return Math.random() * 2 - 1 + (Math.random() * 2 - 1) + (Math.random() * 2 - 1); +}; + +µ.util.dataFromEquation2 = function(_equation, _step) { + var step = _step || 6; + var data = d3.range(0, 360 + step, step).map(function(deg, index) { + var theta = deg * Math.PI / 180; + var radius = _equation(theta); + return [ deg, radius ]; + }); + return data; +}; + +µ.util.dataFromEquation = function(_equation, _step, _name) { + var step = _step || 6; + var t = [], r = []; + d3.range(0, 360 + step, step).forEach(function(deg, index) { + var theta = deg * Math.PI / 180; + var radius = _equation(theta); + t.push(deg); + r.push(radius); + }); + var result = { + t: t, + r: r + }; + if (_name) result.name = _name; + return result; +}; + +µ.util.ensureArray = function(_val, _count) { + if (typeof _val === 'undefined') return null; + var arr = [].concat(_val); + return d3.range(_count).map(function(d, i) { + return arr[i] || arr[0]; + }); +}; + +µ.util.fillArrays = function(_obj, _valueNames, _count) { + _valueNames.forEach(function(d, i) { + _obj[d] = µ.util.ensureArray(_obj[d], _count); + }); + return _obj; +}; + +µ.util.cloneJson = function(json) { + return JSON.parse(JSON.stringify(json)); +}; + +µ.util.validateKeys = function(obj, keys) { + if (typeof keys === 'string') keys = keys.split('.'); + var next = keys.shift(); + return obj[next] && (!keys.length || objHasKeys(obj[next], keys)); +}; + +µ.util.sumArrays = function(a, b) { + return d3.zip(a, b).map(function(d, i) { + return d3.sum(d); + }); +}; + +µ.util.arrayLast = function(a) { + return a[a.length - 1]; +}; + +µ.util.arrayEqual = function(a, b) { + var i = Math.max(a.length, b.length, 1); + while (i-- >= 0 && a[i] === b[i]) ; + return i === -2; +}; + +µ.util.flattenArray = function(arr) { + var r = []; + while (!µ.util.arrayEqual(r, arr)) { + r = arr; + arr = [].concat.apply([], arr); + } + return arr; +}; + +µ.util.deduplicate = function(arr) { + return arr.filter(function(v, i, a) { + return a.indexOf(v) == i; + }); +}; + +µ.util.convertToCartesian = function(radius, theta) { + var thetaRadians = theta * Math.PI / 180; + var x = radius * Math.cos(thetaRadians); + var y = radius * Math.sin(thetaRadians); + return [ x, y ]; +}; + +µ.util.round = function(_value, _digits) { + var digits = _digits || 2; + var mult = Math.pow(10, digits); + return Math.round(_value * mult) / mult; +}; + +µ.util.getMousePos = function(_referenceElement) { + var mousePos = d3.mouse(_referenceElement.node()); + var mouseX = mousePos[0]; + var mouseY = mousePos[1]; + var mouse = {}; + mouse.x = mouseX; + mouse.y = mouseY; + mouse.pos = mousePos; + mouse.angle = (Math.atan2(mouseY, mouseX) + Math.PI) * 180 / Math.PI; + mouse.radius = Math.sqrt(mouseX * mouseX + mouseY * mouseY); + return mouse; +}; + +µ.util.duplicatesCount = function(arr) { + var uniques = {}, val; + var dups = {}; + for (var i = 0, len = arr.length; i < len; i++) { + val = arr[i]; + if (val in uniques) { + uniques[val]++; + dups[val] = uniques[val]; + } else { + uniques[val] = 1; + } + } + return dups; +}; + +µ.util.duplicates = function(arr) { + return Object.keys(µ.util.duplicatesCount(arr)); +}; + +µ.util.translator = function(obj, sourceBranch, targetBranch, reverse) { + if (reverse) { + var targetBranchCopy = targetBranch.slice(); + targetBranch = sourceBranch; + sourceBranch = targetBranchCopy; + } + var value = sourceBranch.reduce(function(previousValue, currentValue) { + if (typeof previousValue != 'undefined') return previousValue[currentValue]; + }, obj); + if (typeof value === 'undefined') return; + sourceBranch.reduce(function(previousValue, currentValue, index) { + if (typeof previousValue == 'undefined') return; + if (index === sourceBranch.length - 1) delete previousValue[currentValue]; + return previousValue[currentValue]; + }, obj); + targetBranch.reduce(function(previousValue, currentValue, index) { + if (typeof previousValue[currentValue] === 'undefined') previousValue[currentValue] = {}; + if (index === targetBranch.length - 1) previousValue[currentValue] = value; + return previousValue[currentValue]; + }, obj); +}; + +µ.PolyChart = function module() { + var config = [ µ.PolyChart.defaultConfig() ]; + var dispatch = d3.dispatch('hover'); + var dashArray = { + solid: 'none', + dash: [ 5, 2 ], + dot: [ 2, 5 ] + }; + var colorScale; + function exports() { + var geometryConfig = config[0].geometryConfig; + var container = geometryConfig.container; + if (typeof container == 'string') container = d3.select(container); + container.datum(config).each(function(_config, _index) { + var isStack = !!_config[0].data.yStack; + var data = _config.map(function(d, i) { + if (isStack) return d3.zip(d.data.t[0], d.data.r[0], d.data.yStack[0]); else return d3.zip(d.data.t[0], d.data.r[0]); + }); + var angularScale = geometryConfig.angularScale; + var domainMin = geometryConfig.radialScale.domain()[0]; + var generator = {}; + generator.bar = function(d, i, pI) { + var dataConfig = _config[pI].data; + var h = geometryConfig.radialScale(d[1]) - geometryConfig.radialScale(0); + var stackTop = geometryConfig.radialScale(d[2] || 0); + var w = dataConfig.barWidth; + d3.select(this).attr({ + 'class': 'mark bar', + d: 'M' + [ [ h + stackTop, -w / 2 ], [ h + stackTop, w / 2 ], [ stackTop, w / 2 ], [ stackTop, -w / 2 ] ].join('L') + 'Z', + transform: function(d, i) { + return 'rotate(' + (geometryConfig.orientation + angularScale(d[0])) + ')'; + } + }); + }; + generator.dot = function(d, i, pI) { + var stackedData = d[2] ? [ d[0], d[1] + d[2] ] : d; + var symbol = d3.svg.symbol().size(_config[pI].data.dotSize).type(_config[pI].data.dotType)(d, i); + d3.select(this).attr({ + 'class': 'mark dot', + d: symbol, + transform: function(d, i) { + var coord = convertToCartesian(getPolarCoordinates(stackedData)); + return 'translate(' + [ coord.x, coord.y ] + ')'; + } + }); + }; + var line = d3.svg.line.radial().interpolate(_config[0].data.lineInterpolation).radius(function(d) { + return geometryConfig.radialScale(d[1]); + }).angle(function(d) { + return geometryConfig.angularScale(d[0]) * Math.PI / 180; + }); + generator.line = function(d, i, pI) { + var lineData = d[2] ? data[pI].map(function(d, i) { + return [ d[0], d[1] + d[2] ]; + }) : data[pI]; + d3.select(this).each(generator['dot']).style({ + opacity: function(dB, iB) { + return +_config[pI].data.dotVisible; + }, + fill: markStyle.stroke(d, i, pI) + }).attr({ + 'class': 'mark dot' + }); + if (i > 0) return; + var lineSelection = d3.select(this.parentNode).selectAll('path.line').data([ 0 ]); + lineSelection.enter().insert('path'); + lineSelection.attr({ + 'class': 'line', + d: line(lineData), + transform: function(dB, iB) { + return 'rotate(' + (geometryConfig.orientation + 90) + ')'; + }, + 'pointer-events': 'none' + }).style({ + fill: function(dB, iB) { + return markStyle.fill(d, i, pI); + }, + 'fill-opacity': 0, + stroke: function(dB, iB) { + return markStyle.stroke(d, i, pI); + }, + 'stroke-width': function(dB, iB) { + return markStyle['stroke-width'](d, i, pI); + }, + 'stroke-dasharray': function(dB, iB) { + return markStyle['stroke-dasharray'](d, i, pI); + }, + opacity: function(dB, iB) { + return markStyle.opacity(d, i, pI); + }, + display: function(dB, iB) { + return markStyle.display(d, i, pI); + } + }); + }; + var angularRange = geometryConfig.angularScale.range(); + var triangleAngle = Math.abs(angularRange[1] - angularRange[0]) / data[0].length * Math.PI / 180; + var arc = d3.svg.arc().startAngle(function(d) { + return -triangleAngle / 2; + }).endAngle(function(d) { + return triangleAngle / 2; + }).innerRadius(function(d) { + return geometryConfig.radialScale(domainMin + (d[2] || 0)); + }).outerRadius(function(d) { + return geometryConfig.radialScale(domainMin + (d[2] || 0)) + geometryConfig.radialScale(d[1]); + }); + generator.arc = function(d, i, pI) { + d3.select(this).attr({ + 'class': 'mark arc', + d: arc, + transform: function(d, i) { + return 'rotate(' + (geometryConfig.orientation + angularScale(d[0]) + 90) + ')'; + } + }); + }; + var markStyle = { + fill: function(d, i, pI) { + return _config[pI].data.color; + }, + stroke: function(d, i, pI) { + return _config[pI].data.strokeColor; + }, + 'stroke-width': function(d, i, pI) { + return _config[pI].data.strokeSize + 'px'; + }, + 'stroke-dasharray': function(d, i, pI) { + return dashArray[_config[pI].data.strokeDash]; + }, + opacity: function(d, i, pI) { + return _config[pI].data.opacity; + }, + display: function(d, i, pI) { + return typeof _config[pI].data.visible === 'undefined' || _config[pI].data.visible ? 'block' : 'none'; + } + }; + var geometryLayer = d3.select(this).selectAll('g.layer').data(data); + geometryLayer.enter().append('g').attr({ + 'class': 'layer' + }); + var geometry = geometryLayer.selectAll('path.mark').data(function(d, i) { + return d; + }); + geometry.enter().append('path').attr({ + 'class': 'mark' + }); + geometry.style(markStyle).each(generator[geometryConfig.geometryType]); + geometry.exit().remove(); + geometryLayer.exit().remove(); + function getPolarCoordinates(d, i) { + var r = geometryConfig.radialScale(d[1]); + var t = (geometryConfig.angularScale(d[0]) + geometryConfig.orientation) * Math.PI / 180; + return { + r: r, + t: t + }; + } + function convertToCartesian(polarCoordinates) { + var x = polarCoordinates.r * Math.cos(polarCoordinates.t); + var y = polarCoordinates.r * Math.sin(polarCoordinates.t); + return { + x: x, + y: y + }; + } + }); + } + exports.config = function(_x) { + if (!arguments.length) return config; + _x.forEach(function(d, i) { + if (!config[i]) config[i] = {}; + extendDeepAll(config[i], µ.PolyChart.defaultConfig()); + extendDeepAll(config[i], d); + }); + return this; + }; + exports.getColorScale = function() { + return colorScale; + }; + d3.rebind(exports, dispatch, 'on'); + return exports; +}; + +µ.PolyChart.defaultConfig = function() { + var config = { + data: { + name: 'geom1', + t: [ [ 1, 2, 3, 4 ] ], + r: [ [ 1, 2, 3, 4 ] ], + dotType: 'circle', + dotSize: 64, + dotVisible: false, + barWidth: 20, + color: '#ffa500', + strokeSize: 1, + strokeColor: 'silver', + strokeDash: 'solid', + opacity: 1, + index: 0, + visible: true, + visibleInLegend: true + }, + geometryConfig: { + geometry: 'LinePlot', + geometryType: 'arc', + direction: 'clockwise', + orientation: 0, + container: 'body', + radialScale: null, + angularScale: null, + colorScale: d3.scale.category20() + } + }; + return config; +}; + +µ.BarChart = function module() { + return µ.PolyChart(); +}; + +µ.BarChart.defaultConfig = function() { + var config = { + geometryConfig: { + geometryType: 'bar' + } + }; + return config; +}; + +µ.AreaChart = function module() { + return µ.PolyChart(); +}; + +µ.AreaChart.defaultConfig = function() { + var config = { + geometryConfig: { + geometryType: 'arc' + } + }; + return config; +}; + +µ.DotPlot = function module() { + return µ.PolyChart(); +}; + +µ.DotPlot.defaultConfig = function() { + var config = { + geometryConfig: { + geometryType: 'dot', + dotType: 'circle' + } + }; + return config; +}; + +µ.LinePlot = function module() { + return µ.PolyChart(); +}; + +µ.LinePlot.defaultConfig = function() { + var config = { + geometryConfig: { + geometryType: 'line' + } + }; + return config; +}; + +µ.Legend = function module() { + var config = µ.Legend.defaultConfig(); + var dispatch = d3.dispatch('hover'); + function exports() { + var legendConfig = config.legendConfig; + var flattenData = config.data.map(function(d, i) { + return [].concat(d).map(function(dB, iB) { + var element = extendDeepAll({}, legendConfig.elements[i]); + element.name = dB; + element.color = [].concat(legendConfig.elements[i].color)[iB]; + return element; + }); + }); + var data = d3.merge(flattenData); + data = data.filter(function(d, i) { + return legendConfig.elements[i] && (legendConfig.elements[i].visibleInLegend || typeof legendConfig.elements[i].visibleInLegend === 'undefined'); + }); + if (legendConfig.reverseOrder) data = data.reverse(); + var container = legendConfig.container; + if (typeof container == 'string' || container.nodeName) container = d3.select(container); + var colors = data.map(function(d, i) { + return d.color; + }); + var lineHeight = legendConfig.fontSize; + var isContinuous = legendConfig.isContinuous == null ? typeof data[0] === 'number' : legendConfig.isContinuous; + var height = isContinuous ? legendConfig.height : lineHeight * data.length; + var legendContainerGroup = container.classed('legend-group', true); + var svg = legendContainerGroup.selectAll('svg').data([ 0 ]); + var svgEnter = svg.enter().append('svg').attr({ + width: 300, + height: height + lineHeight, + xmlns: 'http://www.w3.org/2000/svg', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + version: '1.1' + }); + svgEnter.append('g').classed('legend-axis', true); + svgEnter.append('g').classed('legend-marks', true); + var dataNumbered = d3.range(data.length); + var colorScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered).range(colors); + var dataScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered)[isContinuous ? 'range' : 'rangePoints']([ 0, height ]); + var shapeGenerator = function(_type, _size) { + var squareSize = _size * 3; + if (_type === 'line') { + return 'M' + [ [ -_size / 2, -_size / 12 ], [ _size / 2, -_size / 12 ], [ _size / 2, _size / 12 ], [ -_size / 2, _size / 12 ] ] + 'Z'; + } else if (d3.svg.symbolTypes.indexOf(_type) != -1) return d3.svg.symbol().type(_type).size(squareSize)(); else return d3.svg.symbol().type('square').size(squareSize)(); + }; + if (isContinuous) { + var gradient = svg.select('.legend-marks').append('defs').append('linearGradient').attr({ + id: 'grad1', + x1: '0%', + y1: '0%', + x2: '0%', + y2: '100%' + }).selectAll('stop').data(colors); + gradient.enter().append('stop'); + gradient.attr({ + offset: function(d, i) { + return i / (colors.length - 1) * 100 + '%'; + } + }).style({ + 'stop-color': function(d, i) { + return d; + } + }); + svg.append('rect').classed('legend-mark', true).attr({ + height: legendConfig.height, + width: legendConfig.colorBandWidth, + fill: 'url(#grad1)' + }); + } else { + var legendElement = svg.select('.legend-marks').selectAll('path.legend-mark').data(data); + legendElement.enter().append('path').classed('legend-mark', true); + legendElement.attr({ + transform: function(d, i) { + return 'translate(' + [ lineHeight / 2, dataScale(i) + lineHeight / 2 ] + ')'; + }, + d: function(d, i) { + var symbolType = d.symbol; + return shapeGenerator(symbolType, lineHeight); + }, + fill: function(d, i) { + return colorScale(i); + } + }); + legendElement.exit().remove(); + } + var legendAxis = d3.svg.axis().scale(dataScale).orient('right'); + var axis = svg.select('g.legend-axis').attr({ + transform: 'translate(' + [ isContinuous ? legendConfig.colorBandWidth : lineHeight, lineHeight / 2 ] + ')' + }).call(legendAxis); + axis.selectAll('.domain').style({ + fill: 'none', + stroke: 'none' + }); + axis.selectAll('line').style({ + fill: 'none', + stroke: isContinuous ? legendConfig.textColor : 'none' + }); + axis.selectAll('text').style({ + fill: legendConfig.textColor, + 'font-size': legendConfig.fontSize + }).text(function(d, i) { + return data[i].name; + }); + return exports; + } + exports.config = function(_x) { + if (!arguments.length) return config; + extendDeepAll(config, _x); + return this; + }; + d3.rebind(exports, dispatch, 'on'); + return exports; +}; + +µ.Legend.defaultConfig = function(d, i) { + var config = { + data: [ 'a', 'b', 'c' ], + legendConfig: { + elements: [ { + symbol: 'line', + color: 'red' + }, { + symbol: 'square', + color: 'yellow' + }, { + symbol: 'diamond', + color: 'limegreen' + } ], + height: 150, + colorBandWidth: 30, + fontSize: 12, + container: 'body', + isContinuous: null, + textColor: 'grey', + reverseOrder: false + } + }; + return config; +}; + +µ.tooltipPanel = function() { + var tooltipEl, tooltipTextEl, backgroundEl; + var config = { + container: null, + hasTick: false, + fontSize: 12, + color: 'white', + padding: 5 + }; + var id = 'tooltip-' + µ.tooltipPanel.uid++; + var tickSize = 10; + var exports = function() { + tooltipEl = config.container.selectAll('g.' + id).data([ 0 ]); + var tooltipEnter = tooltipEl.enter().append('g').classed(id, true).style({ + 'pointer-events': 'none', + display: 'none' + }); + backgroundEl = tooltipEnter.append('path').style({ + fill: 'white', + 'fill-opacity': .9 + }).attr({ + d: 'M0 0' + }); + tooltipTextEl = tooltipEnter.append('text').attr({ + dx: config.padding + tickSize, + dy: +config.fontSize * .3 + }); + return exports; + }; + exports.text = function(_text) { + var l = d3.hsl(config.color).l; + var strokeColor = l >= .5 ? '#aaa' : 'white'; + var fillColor = l >= .5 ? 'black' : 'white'; + var text = _text || ''; + tooltipTextEl.style({ + fill: fillColor, + 'font-size': config.fontSize + 'px' + }).text(text); + var padding = config.padding; + var bbox = tooltipTextEl.node().getBBox(); + var boxStyle = { + fill: config.color, + stroke: strokeColor, + 'stroke-width': '2px' + }; + var backGroundW = bbox.width + padding * 2 + tickSize; + var backGroundH = bbox.height + padding * 2; + backgroundEl.attr({ + d: 'M' + [ [ tickSize, -backGroundH / 2 ], [ tickSize, -backGroundH / 4 ], [ config.hasTick ? 0 : tickSize, 0 ], [ tickSize, backGroundH / 4 ], [ tickSize, backGroundH / 2 ], [ backGroundW, backGroundH / 2 ], [ backGroundW, -backGroundH / 2 ] ].join('L') + 'Z' + }).style(boxStyle); + tooltipEl.attr({ + transform: 'translate(' + [ tickSize, -backGroundH / 2 + padding * 2 ] + ')' + }); + tooltipEl.style({ + display: 'block' + }); + return exports; + }; + exports.move = function(_pos) { + if (!tooltipEl) return; + tooltipEl.attr({ + transform: 'translate(' + [ _pos[0], _pos[1] ] + ')' + }).style({ + display: 'block' + }); + return exports; + }; + exports.hide = function() { + if (!tooltipEl) return; + tooltipEl.style({ + display: 'none' + }); + return exports; + }; + exports.show = function() { + if (!tooltipEl) return; + tooltipEl.style({ + display: 'block' + }); + return exports; + }; + exports.config = function(_x) { + extendDeepAll(config, _x); + return exports; + }; + return exports; +}; + +µ.tooltipPanel.uid = 1; + +µ.adapter = {}; + +µ.adapter.plotly = function module() { + var exports = {}; + exports.convert = function(_inputConfig, reverse) { + var outputConfig = {}; + if (_inputConfig.data) { + outputConfig.data = _inputConfig.data.map(function(d, i) { + var r = extendDeepAll({}, d); + var toTranslate = [ + [ r, [ 'marker', 'color' ], [ 'color' ] ], + [ r, [ 'marker', 'opacity' ], [ 'opacity' ] ], + [ r, [ 'marker', 'line', 'color' ], [ 'strokeColor' ] ], + [ r, [ 'marker', 'line', 'dash' ], [ 'strokeDash' ] ], + [ r, [ 'marker', 'line', 'width' ], [ 'strokeSize' ] ], + [ r, [ 'marker', 'symbol' ], [ 'dotType' ] ], + [ r, [ 'marker', 'size' ], [ 'dotSize' ] ], + [ r, [ 'marker', 'barWidth' ], [ 'barWidth' ] ], + [ r, [ 'line', 'interpolation' ], [ 'lineInterpolation' ] ], + [ r, [ 'showlegend' ], [ 'visibleInLegend' ] ] + ]; + toTranslate.forEach(function(d, i) { + µ.util.translator.apply(null, d.concat(reverse)); + }); + + if (!reverse) delete r.marker; + if (reverse) delete r.groupId; + if (!reverse) { + if (r.type === 'scatter') { + if (r.mode === 'lines') r.geometry = 'LinePlot'; else if (r.mode === 'markers') r.geometry = 'DotPlot'; else if (r.mode === 'lines+markers') { + r.geometry = 'LinePlot'; + r.dotVisible = true; + } + } else if (r.type === 'area') r.geometry = 'AreaChart'; else if (r.type === 'bar') r.geometry = 'BarChart'; + delete r.mode; + delete r.type; + } else { + if (r.geometry === 'LinePlot') { + r.type = 'scatter'; + if (r.dotVisible === true) { + delete r.dotVisible; + r.mode = 'lines+markers'; + } else r.mode = 'lines'; + } else if (r.geometry === 'DotPlot') { + r.type = 'scatter'; + r.mode = 'markers'; + } else if (r.geometry === 'AreaChart') r.type = 'area'; else if (r.geometry === 'BarChart') r.type = 'bar'; + delete r.geometry; + } + return r; + }); + if (!reverse && _inputConfig.layout && _inputConfig.layout.barmode === 'stack') { + var duplicates = µ.util.duplicates(outputConfig.data.map(function(d, i) { + return d.geometry; + })); + outputConfig.data.forEach(function(d, i) { + var idx = duplicates.indexOf(d.geometry); + if (idx != -1) outputConfig.data[i].groupId = idx; + }); + } + } + if (_inputConfig.layout) { + var r = extendDeepAll({}, _inputConfig.layout); + var toTranslate = [ + [ r, [ 'plot_bgcolor' ], [ 'backgroundColor' ] ], + [ r, [ 'showlegend' ], [ 'showLegend' ] ], + [ r, [ 'radialaxis' ], [ 'radialAxis' ] ], + [ r, [ 'angularaxis' ], [ 'angularAxis' ] ], + [ r.angularaxis, [ 'showline' ], [ 'gridLinesVisible' ] ], + [ r.angularaxis, [ 'showticklabels' ], [ 'labelsVisible' ] ], + [ r.angularaxis, [ 'nticks' ], [ 'ticksCount' ] ], + [ r.angularaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ], + [ r.angularaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ], + [ r.angularaxis, [ 'range' ], [ 'domain' ] ], + [ r.angularaxis, [ 'endpadding' ], [ 'endPadding' ] ], + [ r.radialaxis, [ 'showline' ], [ 'gridLinesVisible' ] ], + [ r.radialaxis, [ 'tickorientation' ], [ 'tickOrientation' ] ], + [ r.radialaxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ], + [ r.radialaxis, [ 'range' ], [ 'domain' ] ], + [ r.angularAxis, [ 'showline' ], [ 'gridLinesVisible' ] ], + [ r.angularAxis, [ 'showticklabels' ], [ 'labelsVisible' ] ], + [ r.angularAxis, [ 'nticks' ], [ 'ticksCount' ] ], + [ r.angularAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ], + [ r.angularAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ], + [ r.angularAxis, [ 'range' ], [ 'domain' ] ], + [ r.angularAxis, [ 'endpadding' ], [ 'endPadding' ] ], + [ r.radialAxis, [ 'showline' ], [ 'gridLinesVisible' ] ], + [ r.radialAxis, [ 'tickorientation' ], [ 'tickOrientation' ] ], + [ r.radialAxis, [ 'ticksuffix' ], [ 'ticksSuffix' ] ], + [ r.radialAxis, [ 'range' ], [ 'domain' ] ], + [ r.font, [ 'outlinecolor' ], [ 'outlineColor' ] ], + [ r.legend, [ 'traceorder' ], [ 'reverseOrder' ] ], + [ r, [ 'labeloffset' ], [ 'labelOffset' ] ], + [ r, [ 'defaultcolorrange' ], [ 'defaultColorRange' ] ] + ]; + toTranslate.forEach(function(d, i) { + µ.util.translator.apply(null, d.concat(reverse)); + }); + + if (!reverse) { + if (r.angularAxis && typeof r.angularAxis.ticklen !== 'undefined') r.tickLength = r.angularAxis.ticklen; + if (r.angularAxis && typeof r.angularAxis.tickcolor !== 'undefined') r.tickColor = r.angularAxis.tickcolor; + } else { + if (typeof r.tickLength !== 'undefined') { + r.angularaxis.ticklen = r.tickLength; + delete r.tickLength; + } + if (r.tickColor) { + r.angularaxis.tickcolor = r.tickColor; + delete r.tickColor; + } + } + if (r.legend && typeof r.legend.reverseOrder != 'boolean') { + r.legend.reverseOrder = r.legend.reverseOrder != 'normal'; + } + if (r.legend && typeof r.legend.traceorder == 'boolean') { + r.legend.traceorder = r.legend.traceorder ? 'reversed' : 'normal'; + delete r.legend.reverseOrder; + } + if (r.margin && typeof r.margin.t != 'undefined') { + var source = [ 't', 'r', 'b', 'l', 'pad' ]; + var target = [ 'top', 'right', 'bottom', 'left', 'pad' ]; + var margin = {}; + d3.entries(r.margin).forEach(function(dB, iB) { + margin[target[source.indexOf(dB.key)]] = dB.value; + }); + r.margin = margin; + } + if (reverse) { + delete r.needsEndSpacing; + delete r.minorTickColor; + delete r.minorTicks; + delete r.angularaxis.ticksCount; + delete r.angularaxis.ticksCount; + delete r.angularaxis.ticksStep; + delete r.angularaxis.rewriteTicks; + delete r.angularaxis.nticks; + delete r.radialaxis.ticksCount; + delete r.radialaxis.ticksCount; + delete r.radialaxis.ticksStep; + delete r.radialaxis.rewriteTicks; + delete r.radialaxis.nticks; + } + outputConfig.layout = r; + } + return outputConfig; + }; + return exports; +}; + +},{"../../../constants/alignment":145,"../../../lib":169,"d3":15}],250:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +/* eslint-disable new-cap */ + +'use strict'; + +var d3 = _dereq_('d3'); +var Lib = _dereq_('../../../lib'); +var Color = _dereq_('../../../components/color'); + +var micropolar = _dereq_('./micropolar'); +var UndoManager = _dereq_('./undo_manager'); +var extendDeepAll = Lib.extendDeepAll; + +var manager = module.exports = {}; + +manager.framework = function(_gd) { + var config, previousConfigClone, plot, convertedInput, container; + var undoManager = new UndoManager(); + + function exports(_inputConfig, _container) { + if(_container) container = _container; + d3.select(d3.select(container).node().parentNode).selectAll('.svg-container>*:not(.chart-root)').remove(); + + config = (!config) ? + _inputConfig : + extendDeepAll(config, _inputConfig); + + if(!plot) plot = micropolar.Axis(); + convertedInput = micropolar.adapter.plotly().convert(config); + plot.config(convertedInput).render(container); + _gd.data = config.data; + _gd.layout = config.layout; + manager.fillLayout(_gd); + return config; + } + exports.isPolar = true; + exports.svg = function() { return plot.svg(); }; + exports.getConfig = function() { return config; }; + exports.getLiveConfig = function() { + return micropolar.adapter.plotly().convert(plot.getLiveConfig(), true); + }; + exports.getLiveScales = function() { return {t: plot.angularScale(), r: plot.radialScale()}; }; + exports.setUndoPoint = function() { + var that = this; + var configClone = micropolar.util.cloneJson(config); + (function(_configClone, _previousConfigClone) { + undoManager.add({ + undo: function() { + if(_previousConfigClone) that(_previousConfigClone); + }, + redo: function() { + that(_configClone); + } + }); + })(configClone, previousConfigClone); + previousConfigClone = micropolar.util.cloneJson(configClone); + }; + exports.undo = function() { undoManager.undo(); }; + exports.redo = function() { undoManager.redo(); }; + return exports; +}; + +manager.fillLayout = function(_gd) { + var container = d3.select(_gd).selectAll('.plot-container'); + var paperDiv = container.selectAll('.svg-container'); + var paper = _gd.framework && _gd.framework.svg && _gd.framework.svg(); + var dflts = { + width: 800, + height: 600, + paper_bgcolor: Color.background, + _container: container, + _paperdiv: paperDiv, + _paper: paper + }; + + _gd._fullLayout = extendDeepAll(dflts, _gd.layout); +}; + +},{"../../../components/color":50,"../../../lib":169,"./micropolar":249,"./undo_manager":251,"d3":15}],251:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +// Modified from https://github.com/ArthurClemens/Javascript-Undo-Manager +// Copyright (c) 2010-2013 Arthur Clemens, arthur@visiblearea.com +module.exports = function UndoManager() { + var undoCommands = []; + var index = -1; + var isExecuting = false; + var callback; + + function execute(command, action) { + if(!command) return this; + + isExecuting = true; + command[action](); + isExecuting = false; + + return this; + } + + return { + add: function(command) { + if(isExecuting) return this; + undoCommands.splice(index + 1, undoCommands.length - index); + undoCommands.push(command); + index = undoCommands.length - 1; + return this; + }, + setCallback: function(callbackFunc) { callback = callbackFunc; }, + undo: function() { + var command = undoCommands[index]; + if(!command) return this; + execute(command, 'undo'); + index -= 1; + if(callback) callback(command.undo); + return this; + }, + redo: function() { + var command = undoCommands[index + 1]; + if(!command) return this; + execute(command, 'redo'); + index += 1; + if(callback) callback(command.redo); + return this; + }, + clear: function() { + undoCommands = []; + index = -1; + }, + hasUndo: function() { return index !== -1; }, + hasRedo: function() { return index < (undoCommands.length - 1); }, + getCommands: function() { return undoCommands; }, + getPreviousCommand: function() { return undoCommands[index - 1]; }, + getIndex: function() { return index; } + }; +}; + +},{}],252:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169,"../plot_api/plot_template":203,"./domain":238}],253:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169,"../../plots/get_data":241,"./layout_attributes":254,"./layout_defaults":255,"./ternary":256}],254:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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: axesAttrs.title, + 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, + 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":49,"../../lib/extend":164,"../../plot_api/edit_types":196,"../cartesian/layout_attributes":225,"../domain":238}],255:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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: Math.round(options.font.size * 1.2), + 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":50,"../../lib":169,"../../plot_api/plot_template":203,"../cartesian/line_grid_defaults":227,"../cartesian/tick_label_defaults":232,"../cartesian/tick_mark_defaults":233,"../cartesian/tick_value_defaults":234,"../subplot_defaults":252,"./layout_attributes":254}],256:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); +var tinycolor = _dereq_('tinycolor2'); + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +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 Titles = _dereq_('../../components/titles'); +var prepSelect = _dereq_('../cartesian/select').prepSelect; +var selectOnClick = _dereq_('../cartesian/select').selectOnClick; +var clearSelect = _dereq_('../cartesian/select').clearSelect; +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 = 'translate(' + 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 = 'translate(' + (x0 - baxis._offset) + ',' + (y0 + h) + ')'; + + _this.layers.baxis.attr('transform', bTransform); + _this.layers.bgrid.attr('transform', bTransform); + + var aTransform = 'translate(' + (x0 + w / 2) + ',' + y0 + + ')rotate(30)translate(0,' + -aaxis._offset + ')'; + _this.layers.aaxis.attr('transform', aTransform); + _this.layers.agrid.attr('transform', aTransform); + + var cTransform = 'translate(' + (x0 + w / 2) + ',' + y0 + + ')rotate(-30)translate(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.makeTransFn(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.initInteractions = function() { + var _this = this; + var dragger = _this.layers.plotbg.select('path').node(); + var gd = _this.graphDiv; + var zoomLayer = gd._fullLayout._zoomlayer; + + // use plotbg for the main interactions + var dragOptions = { + element: dragger, + gd: gd, + plotinfo: { + id: _this.id, + xaxis: _this.xaxis, + yaxis: _this.yaxis + }, + subplot: _this.id, + prepFn: function(e, startX, startY) { + // these aren't available yet when initInteractions + // is called + dragOptions.xaxes = [_this.xaxis]; + dragOptions.yaxes = [_this.yaxis]; + var dragModeNow = gd._fullLayout.dragmode; + + if(dragModeNow === 'lasso') dragOptions.minDrag = 1; + else dragOptions.minDrag = undefined; + + if(dragModeNow === 'zoom') { + dragOptions.moveFn = zoomMove; + dragOptions.clickFn = clickZoomPan; + dragOptions.doneFn = zoomDone; + zoomPrep(e, startX, startY); + } else if(dragModeNow === 'pan') { + dragOptions.moveFn = plotDrag; + dragOptions.clickFn = clickZoomPan; + dragOptions.doneFn = dragDone; + panPrep(); + clearSelect(gd); + } else if(dragModeNow === 'select' || dragModeNow === 'lasso') { + prepSelect(e, startX, startY, 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, 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; + 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', 'translate(' + _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', 'translate(' + _this.x0 + ', ' + _this.y0 + ')') + .style({ + fill: Color.background, + stroke: Color.defaultLine, + 'stroke-width': 1, + opacity: 0 + }) + .attr('d', 'M0,0Z'); + + 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; + var y1 = y0 + dy0; + 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(); + 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 = 'translate(' + (_this.x0 + dx) + ',' + (_this.y0 + dy) + ')'; + _this.plotContainer.selectAll('.scatterlayer,.maplayer') + .attr('transform', plotTransform); + + var plotTransform2 = 'translate(' + -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(dragOptions); +}; + +function removeZoombox(gd) { + d3.select(gd) + .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners') + .remove(); +} + +},{"../../components/color":50,"../../components/dragelement":68,"../../components/drawing":71,"../../components/fx":89,"../../components/titles":138,"../../lib":169,"../../lib/extend":164,"../../registry":257,"../cartesian/axes":213,"../cartesian/constants":219,"../cartesian/select":230,"../cartesian/set_convert":231,"../plots":245,"d3":15,"tinycolor2":33}],257:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 plot.ly workspace hack, nothing to see here + if(traceType === 'various') return false; + + var _module = exports.modules[traceType]; + + if(!_module) { + if(traceType && traceType !== 'area') { + 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":162,"./lib/extend":164,"./lib/is_plain_object":170,"./lib/loggers":173,"./lib/noop":178,"./lib/push_unique":182,"./plots/attributes":210,"./plots/layout_attributes":243}],258:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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) { + // Polar plot compatibility + if(graphObj.framework && graphObj.framework.isPolar) { + graphObj = graphObj.framework.getConfig(); + } + + 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":169,"../registry":257}],259:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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.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; + + 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":169,"../plot_api/to_image":206,"./filesaver":260,"./helpers":261}],260:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + + if(Lib.isIE9orBelow()) { + reject(new Error('IE < 10 unsupported')); + } + + // 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":169,"./helpers":261}],261:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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() { + var fullLayout = gd._fullLayout || {}; + var hasPolar = fullLayout._has && fullLayout._has('polar'); + var hasLegacyPolar = !hasPolar && gd.data && gd.data[0] && gd.data[0].r; + + if(!hasLegacyPolar) { + Registry.getComponentMethod('colorbar', 'draw')(gd); + } + }; +}; + +exports.encodeSVG = function(svg) { + return 'data:image/svg+xml,' + encodeURIComponent(svg); +}; + +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 { + 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":257}],262:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":258,"./download":259,"./helpers":261,"./svgtoimg":263,"./toimage":264,"./tosvg":265}],263:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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.isIE9orBelow() || 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":169,"./helpers":261,"events":14}],264:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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('plot', clonedGd, clone.data, clone.layout, clone.config) + .then(redrawFunc) + .then(wait) + .catch(function(err) { + ev.emit('error', err); + }); + + + return ev; +} + +module.exports = toImage; + +},{"../lib":169,"../registry":257,"./cloneplot":258,"./helpers":261,"./svgtoimg":263,"./tosvg":265,"events":14}],265:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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; + + // 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)); + } + }); + + svg.selectAll('.point, .scatterpts, .legendfill>path, .legendlines>path, .cbfill').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, '\''); + + // 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":50,"../components/drawing":71,"../constants/xmlns_namespaces":150,"../lib":169,"d3":15}],266:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169}],267:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var scatterAttrs = _dereq_('../scatter/attributes'); +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var fontAttrs = _dereq_('../../plots/font_attributes'); +var constants = _dereq_('./constants.js'); + +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', + + } +}); + +module.exports = { + x: scatterAttrs.x, + x0: scatterAttrs.x0, + dx: scatterAttrs.dx, + y: scatterAttrs.y, + y0: scatterAttrs.y0, + dy: scatterAttrs.dy, + + text: scatterAttrs.text, + hovertext: scatterAttrs.hovertext, + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + + textposition: { + valType: 'enumerated', + + values: ['inside', 'outside', 'auto', 'none'], + dflt: 'none', + 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' + }, + + r: scatterAttrs.r, + t: scatterAttrs.t, + + _deprecated: { + bardir: { + valType: 'enumerated', + + editType: 'calc', + values: ['v', 'h'], + + } + } +}; + +},{"../../components/colorscale/attributes":57,"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../../plots/font_attributes":239,"../scatter/attributes":366,"./constants.js":269}],268:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +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; + + if(trace.orientation === 'h') { + size = xa.makeCalcdata(trace, 'x'); + pos = ya.makeCalcdata(trace, 'y'); + } else { + size = ya.makeCalcdata(trace, 'y'); + pos = xa.makeCalcdata(trace, 'x'); + } + + // 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(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":58,"../../components/colorscale/helpers":61,"../../plots/cartesian/axes":213,"../scatter/calc_selection":368,"./arrays_to_calcdata":266}],269:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = { + eventDataKeys: [] +}; + +},{}],270:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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/axis_ids').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]); + } + } + } + + var opts = { + 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], { + 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, { + 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); + + // 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, { + 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 allBaseAboveZero = true; + + 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 > 0 && bar.s > 0)) { + allBaseAboveZero = false; + } + } + + fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, { + tozero: !allBaseAboveZero, + 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) { + 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], { + 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 allBaseAboveZero = true; + 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 > 0 && bar.s > 0)) { + allBaseAboveZero = false; + } + } + } + + fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, { + tozero: !allBaseAboveZero, + 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":149,"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216,"../../registry":257,"./sieve.js":279,"fast-isnumeric":17}],271:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); +var Color = _dereq_('../../components/color'); +var Registry = _dereq_('../../registry'); + +var handleXYDefaults = _dereq_('../scatter/xy_defaults'); +var handleStyleDefaults = _dereq_('./style_defaults'); +var getAxisGroup = _dereq_('../../plots/cartesian/axis_ids').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; + } + + 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 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(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'); + } + + if(hasInside) { + if(moduleHasInsideanchor) coerce('insidetextanchor'); + } +} + +module.exports = { + supplyDefaults: supplyDefaults, + crossTraceDefaults: crossTraceDefaults, + handleGroupingDefaults: handleGroupingDefaults, + handleText: handleText +}; + +},{"../../components/color":50,"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":257,"../scatter/xy_defaults":391,"./attributes":267,"./style_defaults":281}],272:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"fast-isnumeric":17,"tinycolor2":33}],273:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; + +function hoverPoints(pointData, xval, yval, hovermode) { + var barPointData = hoverOnBars(pointData, xval, yval, hovermode); + + 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) { + 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; + + function thisBarMinPos(di) { return di[posLetter] - di.w / 2; } + function thisBarMaxPos(di) { return di[posLetter] + di.w / 2; } + + var minPos = isClosest ? + 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 ? + thisBarMaxPos : + function(di) { + return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2); + }; + + function _positionFn(_minPos, _maxPos) { + // 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, + maxHoverDistance + Math.min(1, Math.abs(_maxPos - _minPos) / pRangeCalc) - 1); + } + + function positionFn(di) { + return _positionFn(minPos(di), maxPos(di)); + } + + function thisBarPositionFn(di) { + return _positionFn(thisBarMinPos(di), thisBarMaxPos(di)); + } + + function sizeFn(di) { + var v = sizeVal; + var b = di.b; + var s = di[sizeLetter]; + + if(isWaterfall) { + s += Math.abs(di.rawS || 0); + } + + // 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); + } + + 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 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; + + // 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); + pointData[posLetter + 'LabelVal'] = di.p; + + // spikelines always want "closest" distance regardless of hovermode + pointData.spikeDistance = (sizeFn(di) + thisBarPositionFn(di)) / 2 + maxSpikeDistance - maxHoverDistance; + // 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":50,"../../components/fx":89,"../../lib":169,"../../registry":257,"./helpers":272}],274:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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, + selectPoints: _dereq_('./select'), + + moduleType: 'trace', + name: 'bar', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['bar-like', 'cartesian', 'svg', 'bar', 'oriented', 'errorBarsOK', 'showLegend', 'zoomScale'], + meta: { + + } +}; + +},{"../../plots/cartesian":224,"../scatter/marker_colorbar":383,"./arrays_to_calcdata":266,"./attributes":267,"./calc":268,"./cross_trace_calc":270,"./defaults":271,"./hover":273,"./layout_attributes":275,"./layout_defaults":276,"./plot":277,"./select":278,"./style":280}],275:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', + + } +}; + +},{}],276:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/cartesian/axes":213,"../../registry":257,"./layout_attributes":275}],277:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 style = _dereq_('./style'); +var helpers = _dereq_('./helpers'); +var attributes = _dereq_('./attributes'); + +var attributeText = attributes.text; +var attributeTextPosition = attributes.textposition; + +// padding in pixels around text +var TEXTPAD = 3; + +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 plot(gd, plotinfo, cdModule, traceLayer, opts) { + 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 + }; + } + + 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 pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points'); + + var bars = pointGroup.selectAll('g.point').data(Lib.identity); + + 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]; + + var isBlank = ( + x0 === x1 || + y0 === y1 || + !isNumeric(x0) || + !isNumeric(x1) || + !isNumeric(y0) || + !isNumeric(y1) + ); + // display zeros if line.width > 0 + if(isBlank && shouldDisplayZeros && helpers.getLineWidth(trace, di) && (isHorizontal ? x1 - x0 === 0 : y1 - y0 === 0)) { + isBlank = false; + } + di.isBlank = isBlank; + + // in waterfall mode `between` we need to adjust bar end points to match the connector width + if(adjustPixel) { + 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; + } + + var offset = d3.round((lw / 2) % 1, 2); + + function roundWithLine(v) { + // 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) { + // 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); + x1 = fixpx(x1, x0); + y0 = fixpx(y0, y1); + y1 = fixpx(y1, y0); + } + + Lib.ensureSingle(bar, 'path') + .style('vector-effect', 'non-scaling-stroke') + .attr('d', isBlank ? 'M0,0Z' : 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z') + .call(Drawing.setClipUrl, plotinfo.layerClipId, gd); + + appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts); + + 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); +} + +function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var fullLayout = gd._fullLayout; + var textPosition; + + function appendTextNode(bar, text, textFont) { + var textSelection = Lib.ensureSingle(bar, 'text') + .text(text) + .attr({ + 'class': 'bartext bartext-' + textPosition, + transform: '', + 'text-anchor': 'middle', + // prohibit tex interpretation until we can handle + // tex and regular text together + 'data-notex': 1 + }) + .call(Drawing.font, textFont) + .call(svgTextUtils.convertToTspans, gd); + + return textSelection; + } + + // get trace attributes + var trace = calcTrace[0].trace; + var isHorizontal = (trace.orientation === 'h'); + + var text = getText(calcTrace, i, xa, ya); + textPosition = getTextPosition(trace, i); + + // compute text position + var inStackOrRelativeMode = + opts.mode === 'stack' || + opts.mode === 'relative'; + + var calcBar = calcTrace[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(calcTrace[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; + + 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'; + textSelection = appendTextNode(bar, text, insideTextFont); + + 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) { + textSelection = appendTextNode(bar, text, + (textPosition === 'outside') ? + outsideTextFont : insideTextFont); + + textBB = Drawing.bBox(textSelection.node()), + textWidth = textBB.width, + textHeight = textBB.height; + + if(textWidth <= 0 || textHeight <= 0) { + textSelection.remove(); + return; + } + } + + // compute text transform + var transform, constrained; + if(textPosition === 'outside') { + constrained = + trace.constraintext === 'both' || + trace.constraintext === 'outside'; + + transform = getTransform(toMoveOutsideBar(x0, x1, y0, y1, textBB, { + isHorizontal: isHorizontal, + constrained: constrained, + angle: trace.textangle + })); + } else { + constrained = + trace.constraintext === 'both' || + trace.constraintext === 'inside'; + + transform = getTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, { + isHorizontal: isHorizontal, + constrained: constrained, + angle: trace.textangle, + anchor: trace.insidetextanchor + })); + } + + textSelection.attr('transform', transform); +} + +function getRotateFromAngle(angle) { + return (angle === 'auto') ? 0 : angle; +} + +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 || 0; + + var textWidth = textBB.width; + var textHeight = textBB.height; + var lx = Math.abs(x1 - x0); + var ly = Math.abs(y1 - y0); + + var textpad = ( + lx > (2 * TEXTPAD) && + ly > (2 * TEXTPAD) + ) ? TEXTPAD : 0; + + lx -= 2 * textpad; + ly -= 2 * textpad; + + var autoRotate = (angle === 'auto'); + var isAutoRotated = false; + if(autoRotate && + !(textWidth <= lx && textHeight <= ly) && + (textWidth > lx || textHeight > ly) && ( + !(textWidth > ly || textHeight > lx) || + ((textWidth < textHeight) !== (lx < ly)) + )) { + isAutoRotated = true; + } + + if(isAutoRotated) { + // don't rotate yet only swap bar width with height + var tmp = ly; + ly = lx; + lx = tmp; + } + + var rotate = getRotateFromAngle(angle); + var absSin = Math.abs(Math.sin(Math.PI / 180 * rotate)); + var absCos = Math.abs(Math.cos(Math.PI / 180 * rotate)); + + // compute and apply text padding + var dx = Math.max(lx * absCos, ly * absSin); + var dy = Math.max(lx * absSin, ly * absCos); + + var scale = (constrained) ? + Math.min(dx / textWidth, dy / textHeight) : + Math.max(absCos, absSin); + + scale = Math.min(1, scale); + + // compute text and target positions + var targetX = (x0 + x1) / 2; + var targetY = (y0 + y1) / 2; + + if(anchor !== 'middle') { // case of 'start' or 'end' + var targetWidth = scale * (isHorizontal !== isAutoRotated ? textHeight : textWidth); + var targetHeight = scale * (isHorizontal !== isAutoRotated ? textWidth : textHeight); + textpad += 0.5 * (targetWidth * absSin + targetHeight * absCos); + + if(isHorizontal) { + textpad *= dirSign(x0, x1); + targetX = (anchor === 'start') ? x0 + textpad : x1 - textpad; + } else { + textpad *= dirSign(y0, y1); + targetY = (anchor === 'start') ? y0 + textpad : y1 - textpad; + } + } + + var textX = (textBB.left + textBB.right) / 2; + var textY = (textBB.top + textBB.bottom) / 2; + + // lastly apply auto rotation + if(isAutoRotated) rotate += 90; + + return { + textX: textX, + textY: textY, + targetX: targetX, + targetY: targetY, + 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 absSin = Math.abs(Math.sin(Math.PI / 180 * rotate)); + var absCos = Math.abs(Math.cos(Math.PI / 180 * rotate)); + + // compute text and target positions + var targetWidth = scale * (isHorizontal ? textHeight : textWidth); + var targetHeight = scale * (isHorizontal ? textWidth : textHeight); + textpad += 0.5 * (targetWidth * absSin + targetHeight * absCos); + + var targetX = (x0 + x1) / 2; + var targetY = (y0 + y1) / 2; + + if(isHorizontal) { + targetX = x1 - textpad * dirSign(x1, x0); + } else { + targetY = y1 + textpad * dirSign(y0, y1); + } + + var textX = (textBB.left + textBB.right) / 2; + var textY = (textBB.top + textBB.bottom) / 2; + + return { + textX: textX, + textY: textY, + targetX: targetX, + targetY: targetY, + scale: scale, + rotate: rotate + }; +} + +function getTransform(opts) { + var textX = opts.textX; + var textY = opts.textY; + var targetX = opts.targetX; + var targetY = opts.targetY; + var scale = opts.scale; + var rotate = opts.rotate; + + var transformScale; + var transformRotate; + var transformTranslate; + + if(scale < 1) transformScale = 'scale(' + scale + ') '; + else { + scale = 1; + transformScale = ''; + } + + transformRotate = (rotate) ? + 'rotate(' + rotate + ' ' + textX + ' ' + textY + ') ' : ''; + + // Note that scaling also affects the center of the text box + var translateX = (targetX - scale * textX); + var translateY = (targetY - scale * textY); + transformTranslate = 'translate(' + translateX + ' ' + translateY + ')'; + + return transformTranslate + transformScale + transformRotate; +} + +function getText(calcTrace, index, xa, ya) { + var trace = calcTrace[0].trace; + + var value; + if(!trace.textinfo) { + value = helpers.getValue(trace.text, index); + } else { + value = calcTextinfo(calcTrace, index, xa, ya); + } + + return helpers.coerceString(attributeText, value); +} + +function getTextPosition(trace, index) { + var value = helpers.getValue(trace.textposition, index); + return helpers.coerceEnumerated(attributeTextPosition, value); +} + +function calcTextinfo(calcTrace, index, xa, ya) { + var trace = calcTrace[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 = calcTrace[index]; + + var parts = textinfo.split('+'); + var text = []; + var tx; + + var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; }; + + if(hasFlag('label')) { + text.push(formatLabel(calcTrace[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, + getTransform: getTransform, + toMoveInsideBar: toMoveInsideBar, + toMoveOutsideBar: toMoveOutsideBar +}; + +},{"../../components/color":50,"../../components/drawing":71,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../registry":257,"./attributes":267,"./helpers":272,"./style":280,"d3":15,"fast-isnumeric":17}],278:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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]; + } + } +} + +},{}],279:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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); + + 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":149,"../../lib":169}],280:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); + +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'); + 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 = 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.extendFlat({}, 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.mc || trace.marker.color; +} + +module.exports = { + style: style, + styleTextPoints: styleTextPoints, + styleOnSelect: styleOnSelect, + getInsideTextFont: getInsideTextFont, + getOutsideTextFont: getOutsideTextFont, + getBarColor: getBarColor +}; + +},{"../../components/color":50,"../../components/drawing":71,"../../lib":169,"../../registry":257,"./attributes":267,"./helpers":272,"d3":15}],281:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Color = _dereq_('../../components/color'); +var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale; +var colorscaleDefaults = _dereq_('../../components/colorscale/defaults'); + +module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) { + coerce('marker.color', defaultColor); + + if(hasColorscale(traceIn, 'marker')) { + 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'); + coerce('selected.marker.color'); + coerce('unselected.marker.color'); +}; + +},{"../../components/color":50,"../../components/colorscale/defaults":60,"../../components/colorscale/helpers":61}],282:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var scatterAttrs = _dereq_('../scatter/attributes'); +var barAttrs = _dereq_('../bar/attributes'); +var colorAttrs = _dereq_('../../components/color/attributes'); +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +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', + + }, + name: { + valType: 'string', + + editType: 'calc+clearAxisTypes', + + }, + text: extendFlat({}, scatterAttrs.text, { + + }), + hovertext: extendFlat({}, scatterAttrs.hovertext, { + + }), + hovertemplate: hovertemplateAttrs({ + + }), + whiskerwidth: { + valType: 'number', + min: 0, + max: 1, + dflt: 0.5, + + editType: 'calc', + + }, + notched: { + valType: 'boolean', + + editType: 'calc', + + }, + notchwidth: { + valType: 'number', + min: 0, + max: 0.5, + dflt: 0.25, + + editType: 'calc', + + }, + boxpoints: { + valType: 'enumerated', + values: ['all', 'outliers', 'suspectedoutliers', false], + dflt: 'outliers', + + editType: 'calc', + + }, + boxmean: { + valType: 'enumerated', + values: [true, 'sd', false], + dflt: false, + + editType: 'calc', + + }, + jitter: { + valType: 'number', + min: 0, + max: 1, + + editType: 'calc', + + }, + pointpos: { + valType: 'number', + min: -2, + max: 2, + + editType: 'calc', + + }, + orientation: { + valType: 'enumerated', + values: ['v', 'h'], + + editType: 'calc+clearAxisTypes', + + }, + + 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, + + offsetgroup: barAttrs.offsetgroup, + alignmentgroup: barAttrs.alignmentgroup, + + selected: { + marker: scatterAttrs.selected.marker, + editType: 'style' + }, + unselected: { + marker: scatterAttrs.unselected.marker, + editType: 'style' + }, + hoveron: { + valType: 'flaglist', + flags: ['boxes', 'points'], + dflt: 'boxes+points', + + editType: 'style', + + } +}; + +},{"../../components/color/attributes":49,"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../bar/attributes":267,"../scatter/attributes":366}],283:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); + +var Lib = _dereq_('../../lib'); +var _ = Lib._; +var Axes = _dereq_('../../plots/cartesian/axes'); + +// outlier definition based on http://www.physics.csbsju.edu/stats/box2.html +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; + var valAxis, valLetter; + var posAxis, posLetter; + + if(trace.orientation === 'h') { + valAxis = xa; + valLetter = 'x'; + posAxis = ya; + posLetter = 'y'; + } else { + valAxis = ya; + valLetter = 'y'; + posAxis = xa; + posLetter = 'x'; + } + + var val = valAxis.makeCalcdata(trace, valLetter); + var pos = getPos(trace, posLetter, posAxis, val, fullLayout[numKey]); + + var dv = Lib.distinctVals(pos); + var posDistinct = dv.vals; + var dPos = dv.minDiff / 2; + 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++) { + var v = val[i]; + if(!isNumeric(v)) continue; + + var n = Lib.findBin(pos[i], posBins); + if(n >= 0 && n < pLen) { + var pt = {v: v, i: i}; + arraysToCalcdata(pt, trace, i); + ptsPerBin[n].push(pt); + } + } + + var cdi; + var ptFilterFn = (trace.boxpoints || trace.points) === 'all' ? + Lib.identity : + function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); }; + + // build calcdata trace items, one item per distinct position + for(i = 0; i < pLen; i++) { + if(ptsPerBin[i].length > 0) { + var pts = ptsPerBin[i].sort(sortByVal); + var boxVals = pts.map(extractVal); + var bvLen = boxVals.length; + + cdi = {}; + cdi.pos = posDistinct[i]; + cdi.pts = pts; + + // Sort categories by values + cdi[posLetter] = cdi.pos; + cdi[valLetter] = cdi.pts.map(function(pt) { return pt.v; }); + + cdi.min = boxVals[0]; + cdi.max = boxVals[bvLen - 1]; + cdi.mean = Lib.mean(boxVals, bvLen); + cdi.sd = Lib.stdev(boxVals, bvLen, cdi.mean); + + // first quartile + cdi.q1 = Lib.interp(boxVals, 0.25); + // median + cdi.med = Lib.interp(boxVals, 0.5); + // third quartile + cdi.q3 = Lib.interp(boxVals, 0.75); + + // lower and upper fences - last point inside + // 1.5 interquartile ranges from quartiles + cdi.lf = Math.min( + cdi.q1, + boxVals[Math.min( + Lib.findBin(2.5 * cdi.q1 - 1.5 * cdi.q3, boxVals, true) + 1, + bvLen - 1 + )] + ); + cdi.uf = Math.max( + cdi.q3, + boxVals[Math.max( + Lib.findBin(2.5 * cdi.q3 - 1.5 * cdi.q1, boxVals), + 0 + )] + ); + + // lower and upper outliers - 3 IQR out (don't clip to max/min, + // this is only for discriminating suspected & far outliers) + cdi.lo = 4 * cdi.q1 - 3 * cdi.q3; + cdi.uo = 4 * cdi.q3 - 3 * cdi.q1; + + // lower and upper notches ~95% Confidence Intervals for median + var iqr = cdi.q3 - cdi.q1; + var mci = 1.57 * iqr / Math.sqrt(bvLen); + cdi.ln = cdi.med - mci; + cdi.un = cdi.med + mci; + + cdi.pts2 = pts.filter(ptFilterFn); + + cd.push(cdi); + } + } + + calcSelection(cd, trace); + var extremes = Axes.findExtremes(valAxis, val, {padded: true}); + trace._extremes[valAxis._id] = extremes; + + 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 getPos(trace, posLetter, posAxis, val, num) { + if(posLetter in trace) { + return posAxis.makeCalcdata(trace, posLetter); + } + + var pos0; + + if(posLetter + '0' in trace) { + 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']); + + return val.map(function() { return pos0c; }); +} + +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; +} + +function arraysToCalcdata(pt, trace, i) { + var trace2calc = { + text: 'tx', + hovertext: 'htx' + }; + + for(var k in trace2calc) { + if(Array.isArray(trace[k])) { + pt[trace2calc[k]] = trace[k][i]; + } + } +} + +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; } + +},{"../../lib":169,"../../plots/cartesian/axes":213,"fast-isnumeric":17}],284:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); +var getAxisGroup = _dereq_('../../plots/cartesian/axis_ids').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(calcTrace[j].pos); + shownPts += (calcTrace[j].pts2 || []).length; + } + } + + if(!pointList.length) return; + + // box plots - update dPos based on multiple traces + var boxdv = Lib.distinctVals(pointList); + 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, + // 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":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216}],285:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); +var Registry = _dereq_('../../registry'); +var Color = _dereq_('../../components/color'); +var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults; +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; + + coerce('line.color', (traceIn.marker || {}).color || defaultColor); + coerce('line.width'); + coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5)); + + coerce('whiskerwidth'); + coerce('boxmean'); + coerce('width'); + + var notched = coerce('notched', traceIn.notchwidth !== undefined); + if(notched) coerce('notchwidth'); + + handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'}); +} + +function handleSampleDefaults(traceIn, traceOut, coerce, layout) { + var y = coerce('y'); + var x = coerce('x'); + var hasX = x && x.length; + + var defaultOrientation, len; + + if(y && y.length) { + defaultOrientation = 'v'; + if(hasX) { + len = Math.min(Lib.minRowLength(x), Lib.minRowLength(y)); + } else { + coerce('x0'); + len = Lib.minRowLength(y); + } + } else if(hasX) { + defaultOrientation = 'h'; + coerce('y0'); + len = Lib.minRowLength(x); + } else { + traceOut.visible = false; + return; + } + traceOut._length = len; + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + + coerce('orientation', defaultOrientation); +} + +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 points = coerce( + prefix + 'points', + (outlierColorDflt || lineoutliercolor) ? 'suspectedoutliers' : undefined + ); + + if(points) { + coerce('jitter', points === 'all' ? 0.3 : 0); + coerce('pointpos', points === '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(points === '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":50,"../../lib":169,"../../registry":257,"../bar/defaults":271,"./attributes":282}],286:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],287:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 di.pos + t.bPos - 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.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); + + // 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 pa; + if(trace.orientation === 'h') { + pa = ya; + closePtData.xLabelVal = pt.x; + closePtData.yLabelVal = di.pos; + } else { + pa = xa; + closePtData.xLabelVal = di.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":50,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],288:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":224,"./attributes":282,"./calc":283,"./cross_trace_calc":284,"./defaults":285,"./event_data":286,"./hover":287,"./layout_attributes":289,"./layout_defaults":290,"./plot":291,"./select":292,"./style":293}],289:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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', + + } +}; + +},{}],290:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../registry":257,"./layout_attributes":289}],291:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 posAxis = axes.pos; + var valAxis = axes.val; + 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 pos = d.pos; + var posc = posAxis.c2p(pos + bPos, true) + bPosPxOffset; + var pos0 = posAxis.c2p(pos + bPos - bdPos0, true) + bPosPxOffset; + var pos1 = posAxis.c2p(pos + bPos + bdPos1, true) + bPosPxOffset; + var posw0 = posAxis.c2p(pos + bPos - wdPos, true) + bPosPxOffset; + var posw1 = posAxis.c2p(pos + bPos + wdPos, true) + bPosPxOffset; + var posm0 = posAxis.c2p(pos + bPos - bdPos0 * nw, true) + bPosPxOffset; + var posm1 = posAxis.c2p(pos + bPos + bdPos1 * nw, true) + 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(trace.orientation === 'h') { + 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 posAxis = axes.pos; + var valAxis = axes.val; + 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 posc = posAxis.c2p(d.pos + bPos, true) + bPosPxOffset; + var pos0 = posAxis.c2p(d.pos + bPos - bdPos0, true) + bPosPxOffset; + var pos1 = posAxis.c2p(d.pos + bPos + bdPos1, true) + 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":71,"../../lib":169,"d3":15}],292:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],293:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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":50,"../../components/drawing":71,"d3":15}],294:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var heatmapAttrs = _dereq_('../heatmap/attributes'); +var scatterAttrs = _dereq_('../scatter/attributes'); +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 FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK; + +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, + text: heatmapAttrs.text, + hovertext: heatmapAttrs.hovertext, + transpose: heatmapAttrs.transpose, + xtype: heatmapAttrs.xtype, + ytype: heatmapAttrs.ytype, + zhoverformat: heatmapAttrs.zhoverformat, + hovertemplate: heatmapAttrs.hovertemplate, + + connectgaps: 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', + + }, + 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: extendFlat({}, scatterLineAttrs.width, { + editType: 'style+colorbars' + }), + dash: dash, + smoothing: extendFlat({}, scatterLineAttrs.smoothing, { + + }), + editType: 'plot' + } +}, + colorScaleAttrs('', { + cLetter: 'z', + autoColorDflt: false, + editTypeOverride: 'calc' + }) +); + +},{"../../components/colorscale/attributes":57,"../../components/drawing/attributes":70,"../../constants/docs":146,"../../constants/filter_ops":147,"../../lib/extend":164,"../../plots/font_attributes":239,"../heatmap/attributes":316,"../scatter/attributes":366}],295:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":62,"../heatmap/calc":317,"./end_plus":305,"./set_contours":313}],296:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function(pathinfo, operation, perimeter, trace) { + // Abandon all hope, ye who enter here. + var i, v1, v2; + var pi0 = pathinfo[0]; + var na = pi0.x.length; + var nb = pi0.y.length; + var z = pi0.z; + var contours = trace.contours; + + 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]); + } + + pi0.prefixBoundary = false; + + switch(operation) { + case '>': + if(contours.value > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + case '<': + if(contours.value < boundaryMin) { + pi0.prefixBoundary = true; + } + break; + case '[]': + v1 = Math.min.apply(null, contours.value); + v2 = Math.max.apply(null, contours.value); + if(v2 < boundaryMin || v1 > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + case '][': + v1 = Math.min.apply(null, contours.value); + v2 = Math.max.apply(null, contours.value); + if(v1 < boundaryMin && v2 > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + } +}; + +},{}],297:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var extractOpts = _dereq_('../../components/colorscale').extractOpts; +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 = extractOpts(trace); + opts._fillgradient = trace.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":62,"./end_plus":305,"./make_color_map":310}],298:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 + } +}; + +},{}],299:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":50,"../../constants/filter_ops":147,"./label_defaults":309,"fast-isnumeric":17}],300:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":147,"fast-isnumeric":17}],301:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'); +}; + +},{}],302:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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. +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 two contour levels in pathinfo. We + // simply concatenate the info into one pathinfo and flip all of the data + // in one. 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]); + } + 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 + // simply concatenate the info into one pathinfo and flip all of the data + // in one. This will draw the contour 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]); + } + + while(pi1.edgepaths.length) { + pi0.edgepaths.push(op1(pi1.edgepaths.shift())); + } + while(pi1.paths.length) { + pi0.paths.push(op1(pi1.paths.shift())); + } + return [pi0]; + } +}; + +function copyPathinfo(pi) { + return Lib.extendFlat({}, pi, { + edgepaths: Lib.extendDeep([], pi.edgepaths), + paths: Lib.extendDeep([], pi.paths) + }); +} + +},{"../../lib":169}],303:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); + +var handleXYZDefaults = _dereq_('../heatmap/xyz_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; + } + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + + 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":169,"../heatmap/xyz_defaults":330,"./attributes":294,"./constraint_defaults":299,"./contours_defaults":301,"./style_defaults":315}],304:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./constraint_mapping":300,"./end_plus":305}],305:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; +}; + +},{}],306:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 startLocStr = loc.join(','); + var locStr = startLocStr; + var mi = pi.crossings[locStr]; + var marchStep = startStep(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 startStepStr = marchStep.join(','); + var m = pi.z.length; + var n = pi.z[0].length; + 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]; + + // don't include the same point multiple times + if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop(); + locStr = loc.join(','); + + var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) || + (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2)); + var closedLoop = (locStr === startLocStr) && (marchStep.join(',') === startStepStr); + + // 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, startLocStr, 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 startStep(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":169,"./constants":298}],307:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Color = _dereq_('../../components/color'); + +var heatmapHoverPoints = _dereq_('../heatmap/hover'); + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer) { + var hoverData = heatmapHoverPoints(pointData, xval, yval, hovermode, hoverLayer, true); + + 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":50,"../heatmap/hover":323}],308:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":224,"./attributes":294,"./calc":295,"./colorbar":297,"./defaults":303,"./hover":307,"./plot":312,"./style":314}],309:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169}],310:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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":62,"./end_plus":305,"d3":15}],311:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":298}],312:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); + +var Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); +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') { + fillPathinfo = convertToConstraints(pathinfo, contours._operation); + closeBoundaries(fillPathinfo, contours._operation, perimeter, trace); + } + + // 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 fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill'); + + var fillitems = fillgroup.selectAll('path') + .data(contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '=') ? 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 = joinAllPaths(pi, perimeter); + + if(!fullpath) d3.select(this).remove(); + else d3.select(this).attr('d', fullpath).style('stroke', 'none'); + }); +} + +function initFullPath(pi, perimeter) { + var prefixBoundary = pi.prefixBoundary; + if(prefixBoundary === undefined) { + var edgeVal2 = Math.min(pi.z[0][0], pi.z[0][1]); + prefixBoundary = (!pi.edgepaths.length && edgeVal2 > pi.level); + } + + if(prefixBoundary) { + // TODO: why does ^^ not work for constraints? + // pi.prefixBoundary gets set by closeBoundaries + return 'M' + perimeter.join('L') + 'Z'; + } + return ''; +} + +function joinAllPaths(pi, perimeter) { + var fullpath = initFullPath(pi, perimeter); + 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(contours, cd0.t.cb, gd._fullLayout); + + 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(contours, colorbar, fullLayout) { + if(contours.labelformat) { + return fullLayout._d3locale.numberFormat(contours.labelformat); + } else { + var formatAxis; + if(colorbar) { + formatAxis = colorbar.axis; + } else { + formatAxis = { + type: 'linear', + _id: 'ycontour', + showexponent: 'all', + exponentformat: 'B' + }; + + 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 bBox = Drawing.bBox(dummyText.node(), true); + + return { + text: text, + width: bBox.width, + height: bBox.height, + 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 halfWidth = textOpts.width / 2; + var halfHeight = textOpts.height / 2; + + var x = loc.x; + var y = loc.y; + var theta = loc.theta; + + var sin = Math.sin(theta); + var cos = Math.cos(theta); + var dxw = halfWidth * cos; + var dxh = halfHeight * sin; + var dyw = halfWidth * sin; + var dyh = -halfHeight * cos; + var bBoxPts = [ + [x - dxw - dxh, y - dyw - dyh], + [x + dxw - dxh, y + dyw - dyh], + [x + dxw + dxh, y + dyw + dyh], + [x - dxw + dxh, y - dyw + dyh], + ]; + + labelData.push({ + text: textOpts.text, + x: x, + y: y, + dy: textOpts.dy, + theta: theta, + level: textOpts.level, + width: textOpts.width, + height: textOpts.height + }); + + 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 clips = gd._fullLayout._clips; + var clipId = 'clip' + cd0.trace.uid; + + var clipPath = clips.selectAll('#' + clipId) + .data(cd0.trace.connectgaps ? [] : [0]); + clipPath.enter().append('clipPath') + .classed('contourclip', true) + .attr('id', clipId); + clipPath.exit().remove(); + + if(cd0.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]); + var fullpath = joinAllPaths(clipPathInfo, perimeter); + + var path = Lib.ensureSingle(clipPath, 'path', ''); + path.attr('d', fullpath); + } 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/drawing":71,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../plots/cartesian/set_convert":231,"../heatmap/plot":327,"./close_boundaries":296,"./constants":298,"./convert_to_constraints":302,"./empty_pathinfo":304,"./find_all_paths":306,"./make_crossings":311,"d3":15}],313:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/cartesian/axes":213}],314:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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":71,"../heatmap/style":328,"./make_color_map":310,"d3":15}],315:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":60,"./label_defaults":309}],316:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var scatterAttrs = _dereq_('../scatter/attributes'); +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK; + +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'}}), + + 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', + + }, + connectgaps: { + valType: 'boolean', + dflt: false, + + editType: 'calc', + + }, + xgap: { + valType: 'number', + dflt: 0, + min: 0, + + editType: 'plot', + + }, + ygap: { + valType: 'number', + dflt: 0, + min: 0, + + editType: 'plot', + + }, + zhoverformat: { + valType: 'string', + dflt: '', + + editType: 'none', + + }, + hovertemplate: hovertemplateAttrs() +}, { + transforms: undefined +}, + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) +); + +},{"../../components/colorscale/attributes":57,"../../components/fx/hovertemplate_attributes":88,"../../constants/docs":146,"../../lib/extend":164,"../scatter/attributes":366}],317:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); +var Axes = _dereq_('../../plots/cartesian/axes'); + +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'); + +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; + var x0; + var dx; + var y; + var y0; + var dy; + var z; + var i; + var binned; + + // cancel minimum tick spacings (only applies to bars and boxes) + xa._minDtick = 0; + ya._minDtick = 0; + + if(isHist) { + binned = histogram2dCalc(gd, trace); + x = binned.x; + x0 = binned.x0; + dx = binned.dx; + 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 { + x = trace._x = trace.x ? xa.makeCalcdata(trace, 'x') : []; + y = trace._y = trace.y ? ya.makeCalcdata(trace, 'y') : []; + } + + x0 = trace.x0; + dx = trace.dx; + y0 = trace.y0; + dy = trace.dy; + + z = clean2dArray(zIn, trace, xa, ya); + + if(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(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]; +}; + +},{"../../components/colorscale/calc":58,"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":257,"../histogram2d/calc":345,"./clean_2d_array":318,"./convert_column_xyz":320,"./find_empties":322,"./interp2d":325,"./make_bound_array":326}],318:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":149,"../../lib":169,"fast-isnumeric":17}],319:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + min: 'zmin', + max: 'zmax' +}; + +},{}],320:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = _dereq_('../../lib'); +var BADNUM = _dereq_('../../constants/numerical').BADNUM; + +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); + 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; + + for(i = 0; i < arrayVarNames.length; i++) { + newArrays[i] = Lib.init2dArray(col2vals.length, col1vals.length); + } + + if(hasColumnText) { + text = Lib.init2dArray(col2vals.length, col1vals.length); + } + if(hasColumnHoverText) { + hovertext = Lib.init2dArray(col2vals.length, col1vals.length); + } + + 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]; + } + + 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];}); + } +}; + +},{"../../constants/numerical":149,"../../lib":169}],321:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = _dereq_('../../lib'); + +var handleXYZDefaults = _dereq_('./xyz_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; + } + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + + handleStyleDefaults(traceIn, traceOut, coerce, layout); + + coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false)); + + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); +}; + +},{"../../components/colorscale/defaults":60,"../../lib":169,"./attributes":316,"./style_defaults":329,"./xyz_defaults":330}],322:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169}],323:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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, hoverLayer, contour) { + 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(contour) { + 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]); + + if(contour) { + x1 = x0; + xl = x[nx]; + y1 = y0; + yl = y[ny]; + } else { + xl = xc ? xc[nx] : ((x[nx] + x[nx + 1]) / 2); + yl = yc ? yc[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; + + 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: [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":62,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],324:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'], + meta: { + + } +}; + +},{"../../plots/cartesian":224,"./attributes":316,"./calc":317,"./colorbar":319,"./defaults":321,"./hover":323,"./plot":327,"./style":328}],325:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169}],326:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../registry":257}],327:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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":62,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../registry":257,"d3":15,"tinycolor2":33}],328:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('d3'); + +module.exports = function style(gd) { + d3.select(gd).selectAll('.hm image') + .style('opacity', function(d) { + return d.trace.opacity; + }); +}; + +},{"d3":15}],329:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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'); +}; + +},{}],330:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + } + + 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":169,"../../registry":257,"fast-isnumeric":17}],331:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var barAttrs = _dereq_('../bar/attributes'); +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +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', + + }, + + 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 + } +}; + +},{"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../bar/attributes":267,"./bin_attributes":333,"./constants":337}],332:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; +}; + +},{}],333:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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' + }; +}; + +},{}],334:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":17}],335:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":149,"../../plots/cartesian/axes":213}],336:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 + }; + } + + // bin the data + // and make histogram-specific pt-number-to-cd-index map object + var nMax = size.length; + var uniqueValsPerBin = true; + var leftGap = Infinity; + var rightGap = Infinity; + 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); + } + } + + var roundFn; + if(!uniqueValsPerBin) { + roundFn = getBinSpanLabelRound(leftGap, rightGap, binEdges, pa, calendar); + } + + // 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 { + 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":169,"../../plots/cartesian/axes":213,"../../registry":257,"../bar/arrays_to_calcdata":266,"./average":332,"./bin_functions":334,"./bin_label_vals":335,"./norm_functions":343,"fast-isnumeric":17}],337:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = { + eventDataKeys: ['binNumber'] +}; + +},{}],338:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 = axisIds.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; + for(i = 0; i < traces.length; i++) { + traceOut = traces[i]; + binGroupFound = coerce('bingroup'); + break; + } + + 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":169,"../../plots/cartesian/axis_ids":216,"../../registry":257,"../bar/defaults":271}],339:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'); + + 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":50,"../../lib":169,"../../registry":257,"../bar/style_defaults":281,"./attributes":331}],340:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],341:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var barHover = _dereq_('../bar/hover').hoverPoints; +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; + +module.exports = function hoverPoints(pointData, xval, yval, hovermode) { + var pts = barHover(pointData, xval, yval, hovermode); + + 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); + } + + if(trace.hovermplate) pointData.hovertemplate = trace.hovertemplate; + + return pts; +}; + +},{"../../plots/cartesian/axes":213,"../bar/hover":273}],342:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":224,"../bar/cross_trace_calc":270,"../bar/layout_attributes":275,"../bar/layout_defaults":276,"../bar/plot":277,"../bar/select":278,"../bar/style":280,"../scatter/marker_colorbar":383,"./attributes":331,"./calc":336,"./cross_trace_defaults":338,"./defaults":339,"./event_data":340,"./hover":341}],343:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; + } +}; + +},{}],344:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var histogramAttrs = _dereq_('../histogram/attributes'); +var makeBinAttrs = _dereq_('../histogram/bin_attributes'); +var heatmapAttrs = _dereq_('../heatmap/attributes'); +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +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, + zhoverformat: heatmapAttrs.zhoverformat, + hovertemplate: hovertemplateAttrs({}, {keys: 'z'}) + }, + colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) +); + +},{"../../components/colorscale/attributes":57,"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../heatmap/attributes":316,"../histogram/attributes":331,"../histogram/bin_attributes":333}],345:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../plots/cartesian/axes":213,"../histogram/average":332,"../histogram/bin_functions":334,"../histogram/bin_label_vals":335,"../histogram/calc":336,"../histogram/norm_functions":343}],346:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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'); +}; + +},{"../../components/colorscale/defaults":60,"../../lib":169,"../heatmap/style_defaults":329,"./attributes":344,"./sample_defaults":349}],347:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var heatmapHover = _dereq_('../heatmap/hover'); +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; + +module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLayer, contour) { + var pts = heatmapHover(pointData, xval, yval, hovermode, hoverLayer, contour); + + if(!pts) return; + + pointData = pts[0]; + var indices = pointData.index; + var ny = indices[0]; + var nx = indices[1]; + var cd0 = pointData.cd[0]; + var xRange = cd0.xRanges[nx]; + var yRange = cd0.yRanges[ny]; + + pointData.xLabel = hoverLabelText(pointData.xa, xRange[0], xRange[1]); + pointData.yLabel = hoverLabelText(pointData.ya, yRange[0], yRange[1]); + + return pts; +}; + +},{"../../plots/cartesian/axes":213,"../heatmap/hover":323}],348:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'], + meta: { + + + } +}; + +},{"../../plots/cartesian":224,"../heatmap/calc":317,"../heatmap/colorbar":319,"../heatmap/plot":327,"../heatmap/style":328,"../histogram/cross_trace_defaults":338,"../histogram/event_data":340,"./attributes":344,"./defaults":346,"./hover":347}],349:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../registry":257}],350:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var histogram2dAttrs = _dereq_('../histogram2d/attributes'); +var contourAttrs = _dereq_('../contour/attributes'); +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); + +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: contourAttrs.line, + zhoverformat: histogram2dAttrs.zhoverformat, + hovertemplate: histogram2dAttrs.hovertemplate +}, + colorScaleAttrs('', { + cLetter: 'z', + editTypeOverride: 'calc' + }) +); + +},{"../../components/colorscale/attributes":57,"../../lib/extend":164,"../contour/attributes":294,"../histogram2d/attributes":344}],351:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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'); +}; + +},{"../../lib":169,"../contour/contours_defaults":301,"../contour/style_defaults":315,"../histogram2d/sample_defaults":349,"./attributes":350}],352:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":224,"../contour/calc":295,"../contour/colorbar":297,"../contour/hover":307,"../contour/plot":312,"../contour/style":314,"../histogram/cross_trace_defaults":338,"./attributes":350,"./defaults":351}],353:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var plotAttrs = _dereq_('../../plots/attributes'); +var domainAttrs = _dereq_('../../plots/domain').attributes; +var fontAttrs = _dereq_('../../plots/font_attributes'); +var colorAttrs = _dereq_('../../components/color/attributes'); +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); + +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: 'calc', + + }, + 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({}, plotAttrs.hoverinfo, { + flags: ['label', 'text', 'value', 'percent', 'name'] + }), + hovertemplate: hovertemplateAttrs({}, { + keys: ['label', 'color', 'value', 'percent', 'text'] + }), + textposition: { + valType: 'enumerated', + + values: ['inside', 'outside', 'auto', 'none'], + dflt: 'auto', + arrayOk: true, + editType: 'plot', + + }, + textfont: extendFlat({}, textFontAttrs, { + + }), + insidetextfont: extendFlat({}, textFontAttrs, { + + }), + outsidetextfont: extendFlat({}, textFontAttrs, { + + }), + + 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":49,"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/domain":238,"../../plots/font_attributes":239}],354:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Registry = _dereq_('../../registry'); +var getModuleCalcData = _dereq_('../../plots/get_data').getModuleCalcData; + +exports.name = 'pie'; + +exports.plot = function(gd) { + var Pie = Registry.getModule('pie'); + var cdPie = getModuleCalcData(gd.calcdata, Pie)[0]; + Pie.plot(gd, cdPie); +}; + +exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + var hadPie = (oldFullLayout._has && oldFullLayout._has('pie')); + var hasPie = (newFullLayout._has && newFullLayout._has('pie')); + + if(hadPie && !hasPie) { + oldFullLayout._pielayer.selectAll('g.trace').remove(); + } +}; + +},{"../../plots/get_data":241,"../../registry":257}],355:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; +var tinycolor = _dereq_('tinycolor2'); + +var Color = _dereq_('../../components/color'); +var helpers = _dereq_('./helpers'); +var isValidTextValue = _dereq_('../../lib').isValidTextValue; + +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 hasVals = isArrayOrTypedArray(vals) && vals.length; + + var i, pt; + + if(trace.dlabel) { + labels = new Array(vals.length); + for(i = 0; i < vals.length; i++) { + labels[i] = String(trace.label0 + i * trace.dlabel); + } + } + + var allThisTraceLabels = {}; + var pullColor = makePullColorFn(fullLayout['_' + trace.type + 'colormap']); + var seriesLen = (hasVals ? vals : labels).length; + var vTotal = 0; + var isAggregated = false; + + for(i = 0; i < seriesLen; i++) { + var v, label, hidden; + if(hasVals) { + 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; + + // now insert text + var textinfo = trace.textinfo; + if(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; + + for(i = 0; i < cd.length; i++) { + pt = cd[i]; + 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 / vTotal, separators)); + pt.text = text.join('
'); + } + } + + 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":50,"../../lib":169,"./helpers":358,"fast-isnumeric":17,"tinycolor2":33}],356:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); +var attributes = _dereq_('./attributes'); +var handleDomainDefaults = _dereq_('../../plots/domain').defaults; +var handleText = _dereq_('../bar/defaults').handleText; + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var len; + var vals = coerce('values'); + var hasVals = Lib.isArrayOrTypedArray(vals); + var labels = coerce('labels'); + if(Array.isArray(labels)) { + len = labels.length; + if(hasVals) len = Math.min(len, vals.length); + } else if(hasVals) { + len = vals.length; + + 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 textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent'); + coerce('hovertext'); + coerce('hovertemplate'); + + if(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 + }); + } + + 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'); +}; + +},{"../../lib":169,"../../plots/domain":238,"../bar/defaults":271,"./attributes":353}],357:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":85}],358:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); + +exports.formatPiePercent = function formatPiePercent(v, separators) { + var vRounded = (v * 100).toPrecision(3); + if(vRounded.lastIndexOf('.') !== -1) { + vRounded = vRounded.replace(/[.]?0+$/, ''); + } + return Lib.numSeparate(vRounded, separators) + '%'; +}; + +exports.formatPieValue = function formatPieValue(v, separators) { + var vRounded = v.toPrecision(10); + if(vRounded.lastIndexOf('.') !== -1) { + vRounded = vRounded.replace(/[.]?0+$/, ''); + } + 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) return v; + } +}; + +exports.castOption = function castOption(item, indices) { + if(Array.isArray(item)) return exports.getFirstFilled(item, indices); + else if(item) return item; +}; + +},{"../../lib":169}],359:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + 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":353,"./base_plot":354,"./calc":355,"./defaults":356,"./layout_attributes":360,"./layout_defaults":361,"./plot":362,"./style":363,"./style_one":364}],360:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + hiddenlabels: { + valType: 'data_array', + + editType: 'calc', + + }, + piecolorway: { + valType: 'colorlist', + + editType: 'calc', + + }, + extendpiecolors: { + valType: 'boolean', + dflt: true, + + editType: 'calc', + + } +}; + +},{}],361:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"./layout_attributes":360}],362:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var Fx = _dereq_('../../components/fx'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Lib = _dereq_('../../lib'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); + +var helpers = _dereq_('./helpers'); +var eventData = _dereq_('./event_data'); + +function plot(gd, cdModule) { + var fullLayout = gd._fullLayout; + + prerenderTitles(cdModule, gd); + layoutAreas(cdModule, fullLayout._size); + + 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) { + 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 + 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); + }); + + sliceText.text(pt.text) + .attr({ + 'class': 'slicetext', + transform: '', + 'text-anchor': 'middle' + }) + .call(Drawing.font, textPosition === 'outside' ? + determineOutsideTextFont(trace, pt, gd._fullLayout.font) : + determineInsideTextFont(trace, pt, gd._fullLayout.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) { + sliceText.call(Drawing.font, trace.outsidetextfont); + if(trace.outsidetextfont.family !== trace.insidetextfont.family || + trace.outsidetextfont.size !== trace.insidetextfont.size) { + textBB = Drawing.bBox(sliceText.node()); + } + transform = transformOutsideText(textBB, pt); + } + } + + var translateX = cx + pt.pxmid[0] * transform.rCenter + (transform.x || 0); + var translateY = cy + pt.pxmid[1] * transform.rCenter + (transform.y || 0); + + // save some stuff to use later ensure no labels overlap + if(transform.outside) { + pt.yLabelMin = translateY - textBB.height / 2; + pt.yLabelMid = translateY; + pt.yLabelMax = translateY + textBB.height / 2; + pt.labelExtraX = 0; + pt.labelExtraY = 0; + hasOutsideText = true; + } + + sliceText.attr('transform', + 'translate(' + translateX + ',' + translateY + ')' + + (transform.scale < 1 ? ('scale(' + transform.scale + ')') : '') + + (transform.rotate ? ('rotate(' + transform.rotate + ')') : '') + + 'translate(' + + (-(textBB.left + textBB.right) / 2) + ',' + + (-(textBB.top + textBB.bottom) / 2) + + ')'); + }); + }); + + // 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, fullLayout._size); + } + + titleText.attr('transform', + 'translate(' + transform.x + ',' + transform.y + ')' + + (transform.scale < 1 ? ('scale(' + transform.scale + ')') : '') + + 'translate(' + transform.tx + ',' + transform.ty + ')'); + }); + + // now make sure no labels overlap (at least within one pie) + if(hasOutsideText) scootLabels(quadrants, trace); + + plotTextLines(slices, trace); + }); + }); + + // 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'); + + sliceText.attr('transform', 'translate(' + pt.labelExtraX + ',' + pt.labelExtraY + ')' + + sliceText.attr('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 textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height); + var textAspect = textBB.width / textBB.height; + var halfAngle = pt.halfangle; + var ring = pt.ring; + var rInscribed = pt.rInscribed; + var r = cd0.r || pt.rpx1; + + // 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 transform = { + scale: rInscribed * r * 2 / textDiameter, + + // and the center position and rotation in this case + rCenter: 1 - rInscribed, + rotate: 0 + }; + + if(transform.scale >= 1) return transform; + + // max size if text is rotated radially + var Qr = textAspect + 1 / (2 * Math.tan(halfAngle)); + var maxHalfHeightRotRadial = r * Math.min( + 1 / (Math.sqrt(Qr * Qr + 0.5) + Qr), + ring / (Math.sqrt(textAspect * textAspect + ring / 2) + textAspect) + ); + var radialTransform = { + scale: maxHalfHeightRotRadial * 2 / textBB.height, + rCenter: Math.cos(maxHalfHeightRotRadial / r) - + maxHalfHeightRotRadial * textAspect / r, + rotate: (180 / Math.PI * pt.midangle + 720) % 180 - 90 + }; + + // max size if text is rotated tangentially + var aspectInv = 1 / textAspect; + var Qt = aspectInv + 1 / (2 * Math.tan(halfAngle)); + var maxHalfWidthTangential = r * Math.min( + 1 / (Math.sqrt(Qt * Qt + 0.5) + Qt), + ring / (Math.sqrt(aspectInv * aspectInv + ring / 2) + aspectInv) + ); + var tangentialTransform = { + scale: maxHalfWidthTangential * 2 / textBB.width, + rCenter: Math.cos(maxHalfWidthTangential / r) - + maxHalfWidthTangential / textAspect / r, + rotate: (180 / Math.PI * pt.midangle + 810) % 180 - 90 + }; + // if we need a rotated transform, pick the biggest one + // even if both are bigger than 1 + var rotatedTransform = tangentialTransform.scale > radialTransform.scale ? + tangentialTransform : radialTransform; + + if(transform.scale < 1 && rotatedTransform.scale > transform.scale) return rotatedTransform; + return transform; +} + +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 trace = cd0.trace; + var currentAngle = trace.rotation * Math.PI / 180; + 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'; + } + + function getCoords(angle) { + return [cd0.r * Math.sin(angle), -cd0.r * Math.cos(angle)]; + } + + currentCoords = getCoords(currentAngle); + + for(i = 0; i < cd.length; i++) { + cdi = cd[i]; + if(cdi.hidden) continue; + + cdi[firstPt] = currentCoords; + + currentAngle += angleFactor * cdi.v / 2; + cdi.pxmid = getCoords(currentAngle); + cdi.midangle = currentAngle; + + currentAngle += angleFactor * cdi.v / 2; + currentCoords = getCoords(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); + } +} + +module.exports = { + plot: plot, + transformInsideText: transformInsideText, + determineInsideTextFont: determineInsideTextFont, + positionTitleOutside: positionTitleOutside, + prerenderTitles: prerenderTitles, + layoutAreas: layoutAreas, + attachFxHandlers: attachFxHandlers, +}; + +},{"../../components/color":50,"../../components/drawing":71,"../../components/fx":89,"../../lib":169,"../../lib/svg_text_utils":190,"./event_data":357,"./helpers":358,"d3":15}],363:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('d3'); + +var styleOne = _dereq_('./style_one'); + +module.exports = function style(gd) { + gd._fullLayout._pielayer.selectAll('.trace').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); + }); + }); +}; + +},{"./style_one":364,"d3":15}],364:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":50,"./helpers":358}],365:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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.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":169}],366:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +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; + +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, + + }, + + 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', + + }, + 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, + + }), + + r: { + valType: 'data_array', + editType: 'calc', + + }, + t: { + valType: 'data_array', + editType: 'calc', + + } +}; + +},{"../../components/colorscale/attributes":57,"../../components/drawing":71,"../../components/drawing/attributes":70,"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../../plots/font_attributes":239,"./constants":370}],367:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = _dereq_('fast-isnumeric'); +var Lib = _dereq_('../../lib'); + +var Axes = _dereq_('../../plots/cartesian/axes'); +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 x = xa.makeCalcdata(trace, 'x'); + var y = ya.makeCalcdata(trace, 'y'); + 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); + } + + 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]; + } 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":149,"../../lib":169,"../../plots/cartesian/axes":213,"./arrays_to_calcdata":365,"./calc_selection":368,"./colorscale_calc":369,"./subtypes":389,"fast-isnumeric":17}],368:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var Lib = _dereq_('../../lib'); + +module.exports = function calcSelection(cd, trace) { + if(Lib.isArrayOrTypedArray(trace.selectedpoints)) { + Lib.tagSelected(cd, trace); + } +}; + +},{"../../lib":169}],369:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":58,"../../components/colorscale/helpers":61,"./subtypes":389}],370:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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: [] +}; + +},{}],371:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":367}],372:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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; + } + } + } + } +}; + +},{}],373:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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 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; + + 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)) { + 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":169,"../../registry":257,"./attributes":366,"./constants":370,"./fillcolor_defaults":374,"./line_defaults":378,"./line_shape_defaults":380,"./marker_defaults":384,"./stack_defaults":387,"./subtypes":389,"./text_defaults":390,"./xy_defaults":391}],374:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":50,"../../lib":169}],375:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":50,"./subtypes":389}],376:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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); + var d = (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink); + return d; + }; + 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); + 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.x; + var yLabelVal = (orientation === 'v') ? sizeVal : 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":50,"../../components/fx":89,"../../lib":169,"../../registry":257,"./get_trace_color":375}],377:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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'), + 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":224,"./arrays_to_calcdata":365,"./attributes":366,"./calc":367,"./cross_trace_calc":371,"./cross_trace_defaults":372,"./defaults":373,"./hover":376,"./marker_colorbar":383,"./plot":385,"./select":386,"./style":388,"./subtypes":389}],378:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":60,"../../components/colorscale/helpers":61,"../../lib":169}],379:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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 = xa.c2p(di.x); + var 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":149,"../../lib":169,"./constants":370}],380:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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'); +}; + +},{}],381:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],382:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":17}],383:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = { + container: 'marker', + min: 'cmin', + max: 'cmax' +}; + +},{}],384:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":50,"../../components/colorscale/defaults":60,"../../components/colorscale/helpers":61,"./subtypes":389}],385:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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 becuase 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":71,"../../lib":169,"../../lib/polygon":181,"../../registry":257,"./line_points":379,"./link_traces":381,"./subtypes":389,"d3":15}],386:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":389}],387:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; + } +}; + +},{}],388:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = _dereq_('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":71,"../../registry":257,"d3":15}],389:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169}],390:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":169}],391:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../../registry":257}],392:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var scatterAttrs = _dereq_('../scatter/attributes'); +var plotAttrs = _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, { + + }), + 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({}, plotAttrs.hoverinfo, { + flags: ['a', 'b', 'c', 'text', 'name'] + }), + hoveron: scatterAttrs.hoveron, + hovertemplate: hovertemplateAttrs(), +}; + +},{"../../components/colorscale/attributes":57,"../../components/drawing/attributes":70,"../../components/fx/hovertemplate_attributes":88,"../../lib/extend":164,"../../plots/attributes":210,"../scatter/attributes":366}],393:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":365,"../scatter/calc":367,"../scatter/calc_selection":368,"../scatter/colorscale_calc":369,"fast-isnumeric":17}],394:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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)) { + 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":169,"../scatter/constants":370,"../scatter/fillcolor_defaults":374,"../scatter/line_defaults":378,"../scatter/line_shape_defaults":380,"../scatter/marker_defaults":384,"../scatter/subtypes":389,"../scatter/text_defaults":390,"./attributes":392}],395:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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; +}; + +},{}],396:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scatterHover = _dereq_('../scatter/hover'); +var Axes = _dereq_('../../plots/cartesian/axes'); + + +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]; + + newPointData.a = cdi.a; + newPointData.b = cdi.b; + newPointData.c = cdi.c; + + newPointData.xLabelVal = undefined; + newPointData.yLabelVal = undefined; + + var ternary = newPointData.subplot; + newPointData.aLabel = Axes.tickText(ternary.aaxis, cdi.a, 'hover').text; + newPointData.bLabel = Axes.tickText(ternary.baxis, cdi.b, 'hover').text; + newPointData.cLabel = Axes.tickText(ternary.caxis, cdi.c, 'hover').text; + + var trace = newPointData.trace; + 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(ternary.aaxis, newPointData.aLabel); + if(parts.indexOf('b') !== -1) textPart(ternary.baxis, newPointData.bLabel); + if(parts.indexOf('c') !== -1) textPart(ternary.caxis, newPointData.cLabel); + } + newPointData.extraText = text.join('
'); + newPointData.hovertemplate = trace.hovertemplate; + return scatterPointData; +}; + +},{"../../plots/cartesian/axes":213,"../scatter/hover":376}],397:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + colorbar: _dereq_('../scatter/marker_colorbar'), + 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":253,"../scatter/marker_colorbar":383,"../scatter/select":386,"../scatter/style":388,"./attributes":392,"./calc":393,"./defaults":394,"./event_data":395,"./hover":396,"./plot":398}],398:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'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":385}],399:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var boxAttrs = _dereq_('../box/attributes'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = { + y: boxAttrs.y, + x: boxAttrs.x, + x0: boxAttrs.x0, + y0: boxAttrs.y0, + 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":164,"../box/attributes":282}],400:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":149,"../../lib":169,"../../plots/cartesian/axes":213,"../box/calc":283,"./helpers":403}],401:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":284}],402:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":50,"../../lib":169,"../box/defaults":285,"./attributes":399}],403:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169}],404:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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, 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) + ', ' + 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":169,"../../plots/cartesian/axes":213,"../box/hover":287,"./helpers":403}],405:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":224,"../box/defaults":285,"../box/select":292,"../scatter/style":388,"./attributes":399,"./calc":400,"./cross_trace_calc":401,"./defaults":402,"./hover":404,"./layout_attributes":406,"./layout_defaults":407,"./plot":408,"./style":409}],406:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../box/layout_attributes":289}],407:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'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":169,"../box/layout_defaults":290,"./layout_attributes":406}],408:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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 + }); + 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 = d.pos + bPos; + var posCenterPx = posAxis.c2p(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] = density[i].t; + } + 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] = density[i].t; + } + 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; + } else { + bdPosScaled = [bdPos * boxWidth / 2, 0]; + bPosPxOffset = boxLineWidth; + } + + // 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":71,"../../lib":169,"../box/plot":291,"../scatter/line_points":379,"./helpers":403,"d3":15}],409:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var d3 = _dereq_('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":50,"../scatter/style":388,"d3":15}]},{},[10])(10) +}); diff --git a/static/babybuddy/js/graph.239e93d5162b.js.gz b/static/babybuddy/js/graph.239e93d5162b.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..ae6ec192c538969394351f896b0cd4c1cd5f3768 GIT binary patch literal 651389 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%0AFQDc$wu7&Z)Z404cZ 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�H#|bZPw`0FW=y_waD?AV?^R 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

k*ze7~kSGW`j`c_a+2rZ1`NE=qU|cBAkH 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 literal 0 HcmV?d00001 diff --git a/static/rest_framework/docs/css/base.3208b6cc4466.css b/static/rest_framework/docs/css/base.3208b6cc4466.css new file mode 100644 index 00000000..0be2bafa --- /dev/null +++ b/static/rest_framework/docs/css/base.3208b6cc4466.css @@ -0,0 +1,359 @@ +h1 { + font-size: 45px; +} + +.intro-code { + margin-top: 20px; +} + +pre.highlight code * { + white-space: nowrap; // this sets all children inside to nowrap +} + +pre.highlight { + overflow-x: auto; // this sets the scrolling in x +} + +pre.highlight code { + white-space: pre; // forces to respect

 formatting
+}
+
+.main-container {
+  padding-left: 30px;
+  padding-right: 30px;
+}
+
+.btn:focus,
+.btn:focus:active {
+  outline: none;
+}
+
+.sidebar {
+  overflow: auto;
+  font-family: verdana, sans-serif;
+  font-size: 12px;
+  font-weight: 200;
+  background-color: #2e353d;
+  position: fixed;
+  top: 0px;
+  width: 225px;
+  height: 100%;
+  color: #FFF;
+}
+
+.sidebar .brand {
+    background-color: #23282e;
+    display: block;
+    text-align: center;
+    padding: 25px 0;
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+.sidebar .brand a {
+    color: #FFF;
+}
+
+.sidebar .brand a:hover,
+.sidebar .brand a:active,
+.sidebar .brand a:focus {
+  text-decoration: none;
+}
+
+.sidebar .toggle-btn {
+  display: none;
+}
+
+.sidebar .menu-list {
+  width: inherit;
+}
+
+.sidebar .menu-list ul,
+.sidebar .menu-list li {
+  background: #2e353d;
+  list-style: none;
+  padding: 0px;
+  margin: 0px;
+  line-height: 35px;
+  cursor: pointer;
+}
+
+.sidebar .menu-list ul :not(collapsed) .arrow:before,
+.sidebar .menu-list li :not(collapsed) .arrow:before {
+  font-family: FontAwesome;
+  content: "\f078";
+  display: inline-block;
+  padding-left: 10px;
+  padding-right: 10px;
+  vertical-align: middle;
+  float: right;
+}
+
+.sidebar .menu-list ul .active,
+.sidebar .menu-list li .active {
+  border-left: 3px solid #d19b3d;
+  background-color: #4f5b69;
+}
+
+.sidebar .menu-list ul .sub-menu li.active,
+.sidebar .menu-list li .sub-menu li.active {
+  color: #d19b3d;
+}
+
+.sidebar .menu-list ul .sub-menu li.active a,
+.sidebar .menu-list li .sub-menu li.active a {
+  color: #d19b3d;
+}
+
+.sidebar .menu-list ul .sub-menu li,
+.sidebar .menu-list li .sub-menu li {
+  background-color: #181c20;
+  border: none;
+  border-bottom: 1px solid #23282e;
+  margin-left: 0px;
+  line-height: 1.4;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  padding-right: 10px;
+  padding-left: 25px;
+}
+
+.sidebar .menu-list ul .sub-menu li:hover,
+.sidebar .menu-list li .sub-menu li:hover {
+  background-color: #020203;
+}
+
+
+.sidebar .menu-list ul .sub-menu li a,
+.sidebar .menu-list li .sub-menu li a {
+  display: block;
+}
+
+.sidebar .menu-list ul .sub-menu li a:before,
+.sidebar .menu-list li .sub-menu li a:before {
+  font-family: FontAwesome;
+  font-size: 14px;
+  font-weight: bold;
+  content: "\f105";
+  display: inline;
+  vertical-align: middle;
+  padding-left: 0;
+  padding-right: 7px;
+  margin-left: -12px;
+}
+
+.sidebar .menu-list li {
+  padding-left: 0px;
+  border-left: 3px solid #2e353d;
+  border-bottom: 1px solid #23282e;
+}
+
+.sidebar .menu-list li a {
+  text-decoration: none;
+  color: white;
+}
+
+.sidebar .menu-list li a i {
+  padding-left: 10px;
+  width: 20px;
+  padding-right: 20px;
+}
+
+.sidebar .menu-list li:hover {
+  border-left: 3px solid #d19b3d;
+  background-color: #4f5b69;
+  -webkit-transition: all 1s ease;
+  -moz-transition: all 1s ease;
+  -o-transition: all 1s ease;
+  -ms-transition: all 1s ease;
+  transition: all 1s ease;
+}
+
+.sidebar #menu-content {
+  padding-bottom: 70px;
+}
+
+body {
+  margin: 0px;
+  padding: 0px;
+}
+
+.coredocs-section-title {
+    margin-top: 20px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid lightgrey;
+}
+
+.coredocs-link-title a,
+.coredocs-section-title a {
+  display: none;
+}
+
+.coredocs-link-title a,
+.coredocs-section-title a {
+  text-decoration: none;
+}
+
+.coredocs-link-title:hover a,
+.coredocs-section-title:hover a {
+  display: inline;
+  font-size: 20px;
+}
+
+.coredocs-section-title:last-child {
+    margin-top: 0;
+}
+
+
+/* @group Language Switcher */
+
+.sidebar .menu-list.menu-list-bottom {
+    margin-bottom: 0;
+    position: fixed;
+    width: inherit;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    border-top: 1px solid #23282e;
+}
+
+.sidebar .menu-list-bottom li span {
+  float: right;
+  margin-right: 20px;
+  color: #d19b3d;
+}
+
+/* @end Language Switcher */
+
+
+/* @group Docs Content */
+
+.docs-content .meta .label {
+    vertical-align: middle;
+    font-size: 14px;
+    font-weight: normal;
+}
+
+.docs-content .meta code {
+    vertical-align: middle;
+    padding: .2em .6em .3em;
+    font-size: 14px;
+}
+
+.docs-content .btn {
+  font-size: inherit;
+}
+
+.code-samples pre {
+  margin-top: 20px;
+}
+
+/* @end Docs Content */
+
+
+@media (max-width: 767px) {
+  .main-container {
+    padding-left: 15px;
+    padding-right: 15px;
+  }
+
+  .sidebar {
+    position: relative;
+    width: 100%;
+    margin-bottom: 10px;
+    overflow: visible;
+  }
+
+  .sidebar .toggle-btn {
+    display: block;
+    cursor: pointer;
+    position: absolute;
+    right: 10px;
+    top: 10px;
+    z-index: 10 !important;
+    padding: 3px;
+    width: 40px;
+    text-align: center;
+  }
+
+  .sidebar .menu-list.menu-list-bottom {
+    position: static;
+  }
+
+  .sidebar .brand {
+    margin-top: 0;
+    margin-bottom: 0;
+
+    text-align: left !important;
+    font-size: 22px;
+    padding: 0;
+    padding-left: 20px;
+    line-height: 50px !important;
+  }
+}
+
+@media (min-width: 767px) {
+  .sidebar .menu-list .menu-content {
+    display: block;
+  }
+  #main {
+    width:calc(100% - 225px);
+    float: right;
+  }
+}
+
+@media (min-width: 992px) {
+  .modal-lg {
+      width: 980px;
+  }
+}
+
+.api-modal .modal-title .fa {
+  color: #93c54b;
+}
+
+.api-modal .modal-body .request-awaiting {
+  padding: 35px 10px;
+  color: #7F8177;
+  text-align: center;
+}
+
+.api-modal .modal-body .meta {
+  margin-bottom: 20px;
+}
+
+.api-modal .modal-body .meta .label {
+    vertical-align: middle;
+    font-size: 14px;
+    font-weight: normal;
+}
+
+.api-modal .modal-body .meta code {
+    vertical-align: middle;
+    padding: .2em .6em .3em;
+    font-size: 14px;
+}
+
+.api-modal .modal-content .toggle-view {
+  text-align: right;
+  float: right;
+}
+
+.api-modal .modal-content .response .well {
+  margin: 0;
+  max-height: 550px;
+}
+
+.highlight {
+    background-color: #f7f7f9
+}
+
+.checkbox label.control-label {
+    font-weight: bold
+}
+
+@media (min-width: 768px) {
+    .navbar-nav.navbar-right:last-child {
+        margin-right: 0 !important;
+    }
+}
diff --git a/static/rest_framework/docs/css/base.3208b6cc4466.css.gz b/static/rest_framework/docs/css/base.3208b6cc4466.css.gz
new file mode 100644
index 0000000000000000000000000000000000000000..a7987fa5ce6e56ef9432db41d6db14e15bac490b
GIT binary patch
literal 1604
zcmV-K2D|wmiwFP!00002|Fu|KZ`(E$e)q578VqQQNU`O(j_so8upv)-+v{E^iMEMQ
zqDIoO(;@$ThZkKaQF6MX4bs@u(Yb!-;~|d=^zRJ8zdbKiB5oSKe58s*vcef?E%{mCYNxf_-63_P
z5=o*Y!i=HpNLemOiD)S)yso$tAF9`S`7;rF#?R4Zi||QtqqMJA9SM?|;Ed7o0E*FN
z#MgnRAk)#f=OQB#eb%yHv}c0Iiew6X2DvZ#aenlGg2+3r$B=#Mhv=j?3IW9FbSK2);
zD0|r=c$(u9-y(@i8A&4OzLBxzUnJH;vvelbzhpkwYbnm24uYS`9D-zAY|%|ZmaAoM
zLgJDt%F8X<(+e?61}HA#GtJczG$kwh{peIL=JQ|m_gdlOHjD#B24@GK*xBEqh_5`atK5t{D?2=|v#t`slKYQQ_}{JRzf
z-X3*@ZUYZ&jYCfi28;m{OHMK_uub(41hL|W10xZD$Gp;{Bp_Ol@)R*DZC|+x(eene
zP@|$Jb{kx0)M#ldMHriOYMP@`yuSqnGmu6lL>+T}_
zN4OUuz(V>&Rit2gs3$T#WiKQmSekgFf`6NQ&Zpm&Q;$aQdF_=vrA_P9FW}v+|1eGAjnJ4DD>2mv#1GX#2Gv`
zl>IL~6Z2p|pTpR=)>^>#v89U+`y^MgzGWB&K6A1Q6u=JAv%8PztHyDK{>0_sgbxJ$
zb*3tV0Tg|_3${x8$(6>}-kH)(d533`;hbx$?Y-AwJ%n5X&plk&E%U7$RQn1X0;fzn
znEPW}6M;t;!P!G+jljVmoU=|+|A4BX-(8QHe40AdT?g(djADjU!dw=|_k-Zp)7yMX
zeHdmo2LVf+i-ajVO<%_eDNy`L|FK$pc8{vr#8{
zkjL5Ag5(sV_XWO04o>S&upxdh%0kxwgH2=Ar=WVSau2{hXzCvAyKokSVLhRGh^~*)
zgKlV9t-T(2rjn*M=f0`_0Wy559-PB`|8NR@aZ<$VpwGPzPx!s4t};mLyi}0eETE%%6Y-{-f`NOw_=6C;w3_r_(NdW|XE(un
zbNvdId+J{`k{i5&djY%#CX#rtL5Fbg3coiR?M}k#hQ`j`YcQh7-uHZParPK*%(cxX
zXJk0Ap(M^pj8X3|04ZBOmd;bKtS!S#IbQgZb3>vW@|C|V51D`R~
z_j=vo#9l)*z^%2eA0HO$^==kM;)E?jiyiu@k<)IjQ!oA`=AJV7(+g(0vT7RFK~G&0UT$-m_J$CefZM$)2gY2e+M0$!S`$U+1O3yNV2DtUy!L$
ztc?o}IPO$zaBdrI)Q7qopdU*744oRmpZcRE*TJ~eA*6bM84!=|o&NxCpWB547ytkT
C{TodH

literal 0
HcmV?d00001

diff --git a/static/rest_framework/docs/css/base.css b/static/rest_framework/docs/css/base.css
index b81fdef5..0be2bafa 100644
--- a/static/rest_framework/docs/css/base.css
+++ b/static/rest_framework/docs/css/base.css
@@ -30,7 +30,7 @@ pre.highlight code {
 
 .sidebar {
   overflow: auto;
-  font-family: verdana;
+  font-family: verdana, sans-serif;
   font-size: 12px;
   font-weight: 200;
   background-color: #2e353d;
@@ -111,7 +111,11 @@ pre.highlight code {
   border: none;
   border-bottom: 1px solid #23282e;
   margin-left: 0px;
-  text-indent: 10px;
+  line-height: 1.4;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  padding-right: 10px;
+  padding-left: 25px;
 }
 
 .sidebar .menu-list ul .sub-menu li:hover,
@@ -128,11 +132,14 @@ pre.highlight code {
 .sidebar .menu-list ul .sub-menu li a:before,
 .sidebar .menu-list li .sub-menu li a:before {
   font-family: FontAwesome;
+  font-size: 14px;
+  font-weight: bold;
   content: "\f105";
-  display: inline-block;
-  padding-left: 10px;
-  padding-right: 10px;
+  display: inline;
   vertical-align: middle;
+  padding-left: 0;
+  padding-right: 7px;
+  margin-left: -12px;
 }
 
 .sidebar .menu-list li {
diff --git a/static/rest_framework/docs/css/base.css.gz b/static/rest_framework/docs/css/base.css.gz
index 1c91807b512c82858875e1d06b419cc2b243d384..a7987fa5ce6e56ef9432db41d6db14e15bac490b 100644
GIT binary patch
literal 1604
zcmV-K2D|wmiwFP!00002|Fu|KZ`(E$e)q578VqQQNU`O(j_so8upv)-+v{E^iMEMQ
zqDIoO(;@$ThZkKaQF6MX4bs@u(Yb!-;~|d=^zRJ8zdbKiB5oSKe58s*vcef?E%{mCYNxf_-63_P
z5=o*Y!i=HpNLemOiD)S)yso$tAF9`S`7;rF#?R4Zi||QtqqMJA9SM?|;Ed7o0E*FN
z#MgnRAk)#f=OQB#eb%yHv}c0Iiew6X2DvZ#aenlGg2+3r$B=#Mhv=j?3IW9FbSK2);
zD0|r=c$(u9-y(@i8A&4OzLBxzUnJH;vvelbzhpkwYbnm24uYS`9D-zAY|%|ZmaAoM
zLgJDt%F8X<(+e?61}HA#GtJczG$kwh{peIL=JQ|m_gdlOHjD#B24@GK*xBEqh_5`atK5t{D?2=|v#t`slKYQQ_}{JRzf
z-X3*@ZUYZ&jYCfi28;m{OHMK_uub(41hL|W10xZD$Gp;{Bp_Ol@)R*DZC|+x(eene
zP@|$Jb{kx0)M#ldMHriOYMP@`yuSqnGmu6lL>+T}_
zN4OUuz(V>&Rit2gs3$T#WiKQmSekgFf`6NQ&Zpm&Q;$aQdF_=vrA_P9FW}v+|1eGAjnJ4DD>2mv#1GX#2Gv`
zl>IL~6Z2p|pTpR=)>^>#v89U+`y^MgzGWB&K6A1Q6u=JAv%8PztHyDK{>0_sgbxJ$
zb*3tV0Tg|_3${x8$(6>}-kH)(d533`;hbx$?Y-AwJ%n5X&plk&E%U7$RQn1X0;fzn
znEPW}6M;t;!P!G+jljVmoU=|+|A4BX-(8QHe40AdT?g(djADjU!dw=|_k-Zp)7yMX
zeHdmo2LVf+i-ajVO<%_eDNy`L|FK$pc8{vr#8{
zkjL5Ag5(sV_XWO04o>S&upxdh%0kxwgH2=Ar=WVSau2{hXzCvAyKokSVLhRGh^~*)
zgKlV9t-T(2rjn*M=f0`_0Wy559-PB`|8NR@aZ<$VpwGPzPx!s4t};mLyi}0eETE%%6Y-{-f`NOw_=6C;w3_r_(NdW|XE(un
zbNvdId+J{`k{i5&djY%#CX#rtL5Fbg3coiR?M}k#hQ`j`YcQh7-uHZParPK*%(cxX
zXJk0Ap(M^pj8X3|04ZBOmd;bKtS!S#IbQgZb3>vW@|C|V51D`R~
z_j=vo#9l)*z^%2eA0HO$^==kM;)E?jiyiu@k<)IjQ!oA`=AJV7(+g(0vT7RFK~G&0UT$-m_J$CefZM$)2gY2e+M0$!S`$U+1O3yNV2DtUy!L$
ztc?o}IPO$zaBdrI)Q7qopdU*744oRmpZcRE*TJ~eA*6bM84!=|o&NxCpWB547ytkT
C{TodH

literal 1563
zcmV+$2ITo4iwFP!00002|Gikh?P?if)B3eoc#}#+vefF9!euQjVeE8>?mhg6l@9nX&PQ|tQ{XNo>qUHMUgNyeikv1hc?QY-@JIM>WvXm_}v
z?0Jh|H^(Ke*KNobiPg$1oru*bna}lFinGVP;Kwot!x$G^bd`|hYMC1|xTK2ma*KBK
zOw5u2hqLlTb9Det$;v(-9P7n={;PiW3?CjILQCRQ;4*ip8`5Bz+$H4RZ0A%~41;Yc
zUykq7`|j?^w0AT3CFj2M+Rmt2Hsc_4zSQPLy(G`P;F
z;nGAXK!+xYql`l4_4d_57
z-t1QC?PgL1c}yd{0`e}Z)2B76d1+piH%UYIP4e&$NPLx`PRi7-T-+_P#MWFBi6&`W
zWPCYVG_h$4p(|N7dJaFPbkH(4=v>ry%w;?J6>HQQOFCxLJb};Bh<%ORSBA>Ps9X78
zUMb^JIT)mVQyEyySN}s97_rwOEEM5bjpcY#v@c_ATr8ftu%wEjrBLkRk$^pUDa!G-OcAl=L^BqCTEd!vGXpWNrur{&c4=vLeID`UGuX5dpF
z-?jHw%JXMK(YcYs3tKaXz>=J2GLp~-gZxNQ#l}a3R^Qd2s@@ren`zPaf;@MGLLNOj
ziMm*fD1*oPp#Q{Yq6Hh^=P)+T)f0p|+7i*}%##aV-!cq6mKmT10$}^r+4V>CRpYoq
z-*CA<;yposo~X*8=R+T_2UDf_=0X#4Z#Bjyy!|nt-&)#e2j^UAvJWnS=K(H^8LzHE
zURS6gFwEIrIP8>a3xaYnc-?ES5$MH-eb#d79}pGvyUQ`-PZKAfb)cTYC}ub%%z1IV
zuM5Yr8=ob?U{>mxlUW}ONNUaNQ*!FOjuTR#_*VZ}l43-OzAQg$v`B*~LaPx;TvQCk
z=gQrmgD()gqkDdweJw~%F?wI%bL8N(zFk-64~E&$wB5TjR^1Zh?^d?~@_|!(f^NfE
z5QcSA7$DkDuf1$&s#-IJKT%0j>+?`lcV-(NWP77es2@%tFOG@?8Pup@v!{OiZhFD9
zUP8anqT)i~Qgz_4^wiGUn}*xqP~EZWVg+fGmkNBF4e)3W|F0U(d+O=n_e^Y;)uaQ9
zrj!NnX>PD!FkQiNhy9C2a*bC|Zvd|Y9ZB4~poehq0>4)p?T*6ohQ!X^YcQh7zGC^{
z?Cey$G1WGkq)8>7Lz!TEC!mi0=5FruL?<<_Xk3dOv)`G#arW
ze;)~81D`O}ug}`eiJkmw;Cim>hr7jkUC%WgBu>aOWwBL$JaU@Lb?V?xs@zi|e_9Fe
z39ZYyt42?hoSK%$$*hsof9o*LP`@YTr37%C2xDPqY4724lTWKU65*@f!5)0KhR?=c
zG6#}9ru>XdjAAWZaKLdZVnF~>&hpVjz4O@s`B35~$kYh_`LDLP_S&shLGleuukh&H
N`4<3NCP``+002*9{#yV5

diff --git a/static/rest_framework/js/jquery-3.4.1.min.220afd743d9e.js b/static/rest_framework/js/jquery-3.4.1.min.220afd743d9e.js
new file mode 100644
index 00000000..a1c07fd8
--- /dev/null
+++ b/static/rest_framework/js/jquery-3.4.1.min.220afd743d9e.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 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]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 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