diff --git a/package-lock.json b/package-lock.json index 5623c835..0dfd73a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,18 +24,18 @@ } }, "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.7.tgz", + "integrity": "sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.7", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.7", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -72,12 +72,12 @@ } }, "@babel/generator": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", - "integrity": "sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==", + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", + "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", "dev": true, "requires": { - "@babel/types": "^7.7.2", + "@babel/types": "^7.7.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -92,43 +92,43 @@ } }, "@babel/helper-function-name": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz", - "integrity": "sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-get-function-arity": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz", - "integrity": "sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", "dev": true, "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz", - "integrity": "sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", "dev": true, "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helpers": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.0.tgz", - "integrity": "sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", + "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", "dev": true, "requires": { - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/highlight": { @@ -143,34 +143,43 @@ } }, "@babel/parser": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", - "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==", + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz", + "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==", "dev": true }, + "@babel/runtime": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", + "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, "@babel/template": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", - "integrity": "sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/traverse": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.2.tgz", - "integrity": "sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -194,9 +203,9 @@ } }, "@babel/types": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", - "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -379,6 +388,12 @@ "@turf/helpers": "6.x" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -408,6 +423,12 @@ "integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==", "dev": true }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", @@ -458,9 +479,9 @@ "dev": true }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true }, "acorn-dynamic-import": { @@ -469,6 +490,12 @@ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", "dev": true }, + "acorn-jsx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "dev": true + }, "add-line-numbers": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/add-line-numbers/-/add-line-numbers-1.0.1.tgz", @@ -675,15 +702,6 @@ "readable-stream": "^2.0.6" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -922,17 +940,17 @@ "dev": true }, "autoprefixer": { - "version": "9.7.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.1.tgz", - "integrity": "sha512-w3b5y1PXWlhYulevrTJ0lizkQ5CyqfeU6BIRDbuhsMupstHQOeb1Ur80tcB1zxSu7AwyY/qCQ7Vvqklh31ZBFw==", + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.3.tgz", + "integrity": "sha512-8T5Y1C5Iyj6PgkPSFd0ODvK9DIleuPKUPYniNxybS47g2k2wFgLZ46lGQHlBuGKIAEV8fbCDfKCCRS1tvOgc3Q==", "dev": true, "requires": { - "browserslist": "^4.7.2", - "caniuse-lite": "^1.0.30001006", + "browserslist": "^4.8.0", + "caniuse-lite": "^1.0.30001012", "chalk": "^2.4.2", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.21", + "postcss": "^7.0.23", "postcss-value-parser": "^4.0.2" }, "dependencies": { @@ -1127,9 +1145,9 @@ "dev": true }, "bootstrap": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", - "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz", + "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==", "dev": true }, "boundary-cells": { @@ -1191,14 +1209,14 @@ } }, "browserslist": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.2.tgz", - "integrity": "sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.3.tgz", + "integrity": "sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001004", - "electron-to-chromium": "^1.3.295", - "node-releases": "^1.1.38" + "caniuse-lite": "^1.0.30001017", + "electron-to-chromium": "^1.3.322", + "node-releases": "^1.1.44" } }, "buble": { @@ -1218,15 +1236,9 @@ }, "dependencies": { "acorn": { - "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.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "dev": true }, "chalk": { @@ -1298,28 +1310,10 @@ "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", "dev": true }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camelcase": { @@ -1347,9 +1341,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001008", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001008.tgz", - "integrity": "sha512-b8DJyb+VVXZGRgJUa30cbk8gKHZ3LOZTBLaUEEVr2P4xpmFigOCc62CO4uzquW641Ouq1Rm9N+rWLWdSYDaDIw==", + "version": "1.0.30001018", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001018.tgz", + "integrity": "sha512-GTHI7xdD2EX/U9UgNCEdekriT81N6ZwipsTGKAI1hrv3VEC96BvS5RVoc9Odlf9ftS92oxgflAGbYLi8UjIiVA==", "dev": true }, "canvas-fit": { @@ -1733,9 +1727,9 @@ } }, "commander": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "compare-angle": { @@ -1882,26 +1876,35 @@ "dev": true }, "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "dev": true, "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" }, "dependencies": { "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", "dev": true, "requires": { + "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true } } }, @@ -1975,13 +1978,21 @@ "dev": true }, "css-tree": { - "version": "1.0.0-alpha.29", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", - "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", "dev": true, "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "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 + } } }, "csscolorparser": { @@ -1997,12 +2008,12 @@ "dev": true }, "csso": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", - "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.2.tgz", + "integrity": "sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==", "dev": true, "requires": { - "css-tree": "1.0.0-alpha.29" + "css-tree": "1.0.0-alpha.37" } }, "cubic-hermite": { @@ -2060,12 +2071,6 @@ "yargs": "~3.10.0" } }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", @@ -2141,9 +2146,9 @@ "dev": true }, "d3-dispatch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", - "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", "dev": true }, "d3-force": { @@ -2159,45 +2164,45 @@ } }, "d3-hierarchy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", - "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==", "dev": true }, "d3-interpolate": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", - "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", "dev": true, "requires": { "d3-color": "1" } }, "d3-path": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz", - "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", "dev": true }, "d3-quadtree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz", - "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==", "dev": true }, "d3-shape": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", - "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", "dev": true, "requires": { "d3-path": "1" } }, "d3-timer": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", - "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==", "dev": true }, "dashdash": { @@ -2241,10 +2246,18 @@ "dev": true }, "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } }, "deep-is": { "version": "0.1.3", @@ -2430,9 +2443,9 @@ } }, "dom-serializer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", - "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", "dev": true, "requires": { "domelementtype": "^2.0.1", @@ -2598,9 +2611,9 @@ } }, "electron-to-chromium": { - "version": "1.3.306", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.306.tgz", - "integrity": "sha512-frDqXvrIROoYvikSKTIKbHbzO6M3/qC6kCIt/1FOa9kALe++c4VAJnwjSFvf1tYLEUsP2n9XZ4XSCyqc3l7A/A==", + "version": "1.3.323", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.323.tgz", + "integrity": "sha512-c7pOUGnqNv6otzwcedViWOTGMEUG70PkhrTzVtc20Txh9nDC4s8zWvocJl7q+OpoC0ACXtxud8PX2y8zk/RZiw==", "dev": true }, "element-size": { @@ -2649,21 +2662,30 @@ } }, "es-abstract": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", - "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", + "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } } }, "es-to-primitive": { @@ -2734,9 +2756,9 @@ "dev": true }, "escodegen": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", - "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.1.tgz", + "integrity": "sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ==", "dev": true, "requires": { "esprima": "^3.1.3", @@ -2746,12 +2768,6 @@ "source-map": "~0.6.1" }, "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2762,21 +2778,21 @@ } }, "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "dev": true }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "events": { @@ -2839,9 +2855,9 @@ } }, "ext": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.2.0.tgz", - "integrity": "sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", "dev": true, "requires": { "type": "^2.0.0" @@ -4176,9 +4192,9 @@ } }, "gl-mesh3d": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/gl-mesh3d/-/gl-mesh3d-2.1.1.tgz", - "integrity": "sha512-UuDnuSE/xX8y9/B6EtDsBKllEmKDVmuiD9lsFoQdUq4FPSvwFOo9rKH3fsjK2pfxsTigguF6GhFjHqq+sJKQWg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gl-mesh3d/-/gl-mesh3d-2.2.0.tgz", + "integrity": "sha512-wO6EKjBUo/k7ZLGsMACWGETjmjfsGwwoDWEKjDbjyjo1qPvgkTQQB9Y8p+OKGjE6GeihsfQuoqGBUTu9tiAOmg==", "dev": true, "requires": { "barycentric": "^1.0.1", @@ -4406,13 +4422,13 @@ }, "dependencies": { "es5-ext": { - "version": "0.10.52", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", - "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, "requires": { "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.2", + "es6-symbol": "~3.1.3", "next-tick": "~1.0.0" }, "dependencies": { @@ -4995,14 +5011,28 @@ } }, "gulp-csso": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/gulp-csso/-/gulp-csso-3.0.1.tgz", - "integrity": "sha512-zhkvq06x1SJrpBN8YNJfc1PDono2+xjB6nI9UmBPh88nS4Weuz0hZMgJ4YruOw9Bf+oDrX71U6pkos6pIQhc1g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/gulp-csso/-/gulp-csso-4.0.1.tgz", + "integrity": "sha512-Kg8gqmd6XcUlMTdBbqdCEcpHumc8ytc4khgm9AXeCjl8eHx7b6tC11y8haizFI+Zw/cSHL6HCj7GwGLwxxBUFQ==", "dev": true, "requires": { - "csso": "^3.0.0", - "plugin-error": "^0.1.2", + "csso": "^4.0.0", + "plugin-error": "^1.0.0", "vinyl-sourcemaps-apply": "^0.2.1" + }, + "dependencies": { + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + } } }, "gulp-flatten": { @@ -5098,12 +5128,12 @@ } }, "gulp-spawn": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/gulp-spawn/-/gulp-spawn-0.4.4.tgz", - "integrity": "sha512-I9vsYBL3WCm0dszPo54T1laU4BFdWibIPx+5j2l41roQ31q83jKKgCA3X167sR+GezRM3vni0AlBCw8GCddcXQ==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/gulp-spawn/-/gulp-spawn-0.4.5.tgz", + "integrity": "sha512-MX9vFKWZ2qrW38TKdYuf8fnVKn3oaE5XxZXQbit87ThLT3vSk8/p/8cB0VI47OVjG7H4e6/zk1H25WgcQbB8tw==", "dev": true, "requires": { - "plexer": "^1.0.2", + "plexer": "^2.0.0", "plugin-error": "^1.0.1" }, "dependencies": { @@ -5122,37 +5152,67 @@ } }, "gulp-stylelint": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/gulp-stylelint/-/gulp-stylelint-10.0.0.tgz", - "integrity": "sha512-k4NZwtqRIiFnk0g1yb4UZ/+axnTVPMMDqzz1XPMLI0PPBgx91TDBzbVZexuMLUeVNoEYW8iMRUKrk3NDS+WNJw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/gulp-stylelint/-/gulp-stylelint-11.0.0.tgz", + "integrity": "sha512-Hk2DungSqeWIye5hgmB36IMN9oIIsOBieAaQZw0HAVuIljDGcfH6ng0wmJf3G418jv6KCdgpTZb0EIN1L5KXEw==", "dev": true, "requires": { - "chalk": "^2.4.2", + "chalk": "^3.0.0", "fancy-log": "^1.3.3", "mkdirp": "^0.5.1", "plugin-error": "^1.0.1", "source-map": "^0.7.3", - "strip-ansi": "^5.2.0", + "strip-ansi": "^6.0.0", "through2": "^3.0.1" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", @@ -5172,12 +5232,21 @@ "dev": true }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } }, "through2": { @@ -5432,19 +5501,19 @@ } }, "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "dev": true, "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "dependencies": { "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true } } @@ -5607,6 +5676,12 @@ "is-decimal": "^1.0.0" } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5629,9 +5704,9 @@ } }, "is-blob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.0.1.tgz", - "integrity": "sha512-SmqVJYMnAeqrKLcwq6TXu1rpAg3yipVlMZIqR5u510rxoOzJGW9GQY6g+WtWkcc44pjbWAuxzZDCkbgf5e6r0Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz", + "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==", "dev": true }, "is-browser": { @@ -5656,9 +5731,9 @@ } }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, "is-data-descriptor": { @@ -5682,9 +5757,9 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, "is-decimal": { @@ -5712,12 +5787,6 @@ } } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -5847,12 +5916,12 @@ } }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, "requires": { - "has": "^1.0.1" + "has": "^1.0.3" } }, "is-regexp": { @@ -5883,12 +5952,20 @@ "dev": true }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } } }, "is-typedarray": { @@ -5978,16 +6055,6 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -6070,9 +6137,9 @@ "dev": true }, "known-css-properties": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.16.0.tgz", - "integrity": "sha512-0g5vDDPvNnQk7WM/aE92dTDxXJoOE0biiIcUb3qkn/F6h/ZQZPlZIbE2XSXH2vFPfphkgCxuR2vH6HHnobEOaQ==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.17.0.tgz", + "integrity": "sha512-Vi3nxDGMm/z+lAaCjvAR1u+7fiv+sG6gU/iYDj5QOF8h76ytK9EW/EKfF0NeTyiGBi8Jy6Hklty/vxISrLox3w==", "dev": true }, "last-run": { @@ -6162,6 +6229,12 @@ "resolve": "^1.1.7" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -6205,6 +6278,24 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", + "dev": true + }, + "lodash.isregexp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isregexp/-/lodash.isregexp-4.0.1.tgz", + "integrity": "sha1-4T5kezDNVZdSoEzZEghvr32hwws=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -6260,9 +6351,9 @@ } }, "magic-string": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz", - "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.5.tgz", + "integrity": "sha512-vIO/BOm9odBHBAGwv0gZPLJeO9IpwliiIc0uPeAW93rrFMJ/R3M665IAEfOU/IW3kD4S9AtEn76lfTn1Yif+9A==", "dev": true, "requires": { "sourcemap-codec": "^1.4.4" @@ -6493,18 +6584,18 @@ } }, "mdast-util-compact": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.3.tgz", - "integrity": "sha512-nRiU5GpNy62rZppDKbLwhhtw5DXoFMqw9UNZFmlPsNaQCZ//WLjGKUwWMdJrUH+Se7UvtO2gXtAMe0g/N+eI5w==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz", + "integrity": "sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg==", "dev": true, "requires": { "unist-util-visit": "^1.1.0" } }, "mdn-data": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", - "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", "dev": true }, "meow": { @@ -6894,9 +6985,9 @@ } }, "node-releases": { - "version": "1.1.39", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.39.tgz", - "integrity": "sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA==", + "version": "1.1.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.44.tgz", + "integrity": "sha512-NwbdvJyR7nrcGrXvKAvzc5raj/NkoJudkarh2yIpJ4t0NH4aqjUDz/486P+ynIW5eokKOfzGNRdYoLfBlomruw==", "dev": true, "requires": { "semver": "^6.3.0" @@ -7114,9 +7205,15 @@ } }, "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==", "dev": true }, "object-keys": { @@ -7197,17 +7294,17 @@ } }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "word-wrap": "~1.2.3" } }, "orbit-camera-controller": { @@ -7302,6 +7399,15 @@ "repeat-string": "^1.3.0" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parenthesis": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.7.tgz", @@ -7528,19 +7634,32 @@ } }, "plexer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/plexer/-/plexer-1.0.3.tgz", - "integrity": "sha512-U8iVMgA9Asku2az1HCoYUywL54hypBQ0d2svuDb3aO8MsuvhQKz/I3db2WtZcRQWe42N/3E8Pru/F/WvzIh6UA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/plexer/-/plexer-2.0.0.tgz", + "integrity": "sha512-MWAhwDaaaNA9sfbZz00HjSxrwOxEUD8BW4pHG0obnjvJpyG3RcOjSIcDIQFW3NP+RDJlzPbyXdgCR3V24tyUdw==", "dev": true, "requires": { "isstream": "^0.1.2", - "readable-stream": "^2.0.2" + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "plotly.js": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.51.1.tgz", - "integrity": "sha512-EOAP6hne7e8RyH2fo0dyTuRKiAO04TVYECK65EMrK8jnP5AqSbHIypnroNCm/nfYj3sn5DTl+GX5XsJKpyM4Aw==", + "version": "1.51.3", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.51.3.tgz", + "integrity": "sha512-1HsGdm+TPkJzxNvC3Whwh2U97FGrwCVDJ+W6s202qV5hJ0GQjqokPxGZalnjZDBLm0E1QAdQmiXBRVl10Pw+0A==", "dev": true, "requires": { "@plotly/d3-sankey": "0.7.2", @@ -7566,7 +7685,7 @@ "gl-heatmap2d": "^1.0.5", "gl-line3d": "^1.1.11", "gl-mat4": "^1.2.0", - "gl-mesh3d": "^2.1.1", + "gl-mesh3d": "^2.1.3", "gl-plot2d": "^1.4.2", "gl-plot3d": "^2.3.0", "gl-pointcloud2d": "^1.0.2", @@ -7726,9 +7845,9 @@ "dev": true }, "postcss": { - "version": "7.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", - "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.26.tgz", + "integrity": "sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -7857,13 +7976,13 @@ } }, "postcss-sass": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.1.tgz", - "integrity": "sha512-YDdykeDHylqiD2CdXuP7K1aDz7hCflGVB6H6lqabWVab5mVOWhguUuWZYpFU22/E12AEGiMlOfZnLqr343zhVA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.2.tgz", + "integrity": "sha512-hcRgnd91OQ6Ot9R90PE/khUDCJHG8Uxxd3F7Y0+9VHjBiJgNv7sK5FxyHMCBtoLmmkzVbSj3M3OlqUfLJpq0CQ==", "dev": true, "requires": { "gonzales-pe": "^4.2.4", - "postcss": "^7.0.14" + "postcss": "^7.0.21" } }, "postcss-scss": { @@ -8246,6 +8365,12 @@ "regenerate": "^1.4.0" } }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -8262,6 +8387,16 @@ "integrity": "sha1-kEih6uuHD01IDavHb8Qs3MC8OnI=", "dev": true }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "regexpu-core": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", @@ -8283,9 +8418,9 @@ "dev": true }, "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz", + "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -8334,13 +8469,13 @@ }, "dependencies": { "es5-ext": { - "version": "0.10.52", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", - "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, "requires": { "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.2", + "es6-symbol": "~3.1.3", "next-tick": "~1.0.0" }, "dependencies": { @@ -9177,9 +9312,9 @@ "dev": true }, "sourcemap-codec": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", - "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.7.tgz", + "integrity": "sha512-RuN23NzhAOuUtaivhcrjXx1OPXsFeH9m5sI373/U7+tGLKihjUyboZAzOadytMjnqHp1f45RGk1IzDKCpDpSYA==", "dev": true }, "sparkles": { @@ -9255,9 +9390,9 @@ } }, "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, "sshpk": { @@ -9290,12 +9425,12 @@ "dev": true }, "static-eval": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", - "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.3.tgz", + "integrity": "sha512-zsxDGucfAh8T339sSKgpFbvg15Fms2IVaJGC+jqp0bVsxhcpM+iMeAI8weNo8dmf4OblgifTBUoyk1vGVtYw2w==", "dev": true, "requires": { - "escodegen": "^1.8.1" + "escodegen": "^1.11.1" } }, "static-extend": { @@ -9529,20 +9664,20 @@ } }, "string.prototype.trim": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", - "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz", + "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.0", - "function-bind": "^1.0.2" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1" } }, "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", "dev": true, "requires": { "define-properties": "^1.1.3", @@ -9550,9 +9685,9 @@ } }, "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", "dev": true, "requires": { "define-properties": "^1.1.3", @@ -9620,15 +9755,15 @@ "dev": true }, "stylelint": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-11.1.1.tgz", - "integrity": "sha512-Vx6TAJsxG6qksiFvxQTKriQhp1CqUWdpTDITEkAjTR+l+8Af7qNlvrUDXfpuFJgXh/ayF8xdMSKE+SstcsPmMA==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-12.0.1.tgz", + "integrity": "sha512-1mn39pqZiC/e8KUPoRMc1WMM83Upb2ILaSGxkCvKxALHutEOs2txcPQocJiXdO4Zx4FY4prGqjlkwrbthAxqig==", "dev": true, "requires": { - "autoprefixer": "^9.5.1", + "autoprefixer": "^9.7.1", "balanced-match": "^1.0.0", - "chalk": "^2.4.2", - "cosmiconfig": "^5.2.0", + "chalk": "^3.0.0", + "cosmiconfig": "^6.0.0", "debug": "^4.1.1", "execall": "^2.0.0", "file-entry-cache": "^5.0.1", @@ -9636,19 +9771,19 @@ "global-modules": "^2.0.0", "globby": "^9.2.0", "globjoin": "^0.1.4", - "html-tags": "^3.0.0", - "ignore": "^5.0.6", + "html-tags": "^3.1.0", + "ignore": "^5.1.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", - "known-css-properties": "^0.16.0", + "known-css-properties": "^0.17.0", "leven": "^3.1.0", - "lodash": "^4.17.14", + "lodash": "^4.17.15", "log-symbols": "^3.0.0", - "mathml-tag-names": "^2.1.0", + "mathml-tag-names": "^2.1.1", "meow": "^5.0.0", - "micromatch": "^4.0.0", + "micromatch": "^4.0.2", "normalize-selector": "^0.2.0", - "postcss": "^7.0.14", + "postcss": "^7.0.21", "postcss-html": "^0.36.0", "postcss-jsx": "^0.36.3", "postcss-less": "^3.1.4", @@ -9657,22 +9792,22 @@ "postcss-reporter": "^6.0.1", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^4.0.1", - "postcss-sass": "^0.4.1", + "postcss-sass": "^0.4.2", "postcss-scss": "^2.0.0", "postcss-selector-parser": "^3.1.0", "postcss-syntax": "^0.36.2", "postcss-value-parser": "^4.0.2", "resolve-from": "^5.0.0", - "signal-exit": "^3.0.2", "slash": "^3.0.0", "specificity": "^0.4.1", - "string-width": "^4.1.0", - "strip-ansi": "^5.2.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", "style-search": "^0.1.0", "sugarss": "^2.0.0", "svg-tags": "^1.0.0", - "table": "^5.2.3", - "v8-compile-cache": "^2.1.0" + "table": "^5.4.6", + "v8-compile-cache": "^2.1.0", + "write-file-atomic": "^3.0.1" }, "dependencies": { "@nodelib/fs.stat": { @@ -9682,11 +9817,21 @@ "dev": true }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -9714,16 +9859,30 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -9867,6 +10026,12 @@ } } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "indent-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", @@ -10020,23 +10185,23 @@ } }, "string-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", - "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } }, "strip-bom": { @@ -10051,6 +10216,15 @@ "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", "dev": true }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -10084,32 +10258,34 @@ "dev": true }, "stylelint-config-recommended-scss": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-4.0.0.tgz", - "integrity": "sha512-aEy0ENUrH4ASgFCu2mMcqBUAX0l4CPXg0XucJXdW+I7mdqJ7ICddkxP1eamBNBZ1QToc/wsuLmTQcalk3qYpsw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-4.1.0.tgz", + "integrity": "sha512-4012ca0weVi92epm3RRBRZcRJIyl5vJjJ/tJAKng+Qat5+cnmuCwyOI2vXkKdjNfGd0gvzyKCKEkvTMDcbtd7Q==", "dev": true, "requires": { "stylelint-config-recommended": "^3.0.0" } }, "stylelint-order": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-3.1.1.tgz", - "integrity": "sha512-4gP/r8j/6JGZ/LL41b2sYtQqfwZl4VSqTp7WeIwI67v/OXNQ08dnn64BGXNwAUSgb2+YIvIOxQaMzqMyQMzoyQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-4.0.0.tgz", + "integrity": "sha512-bXV0v+jfB0+JKsqIn3mLglg1Dj2QCYkFHNfL1c+rVMEmruZmW5LUqT/ARBERfBm8SFtCuXpEdatidw/3IkcoiA==", "dev": true, "requires": { "lodash": "^4.17.15", - "postcss": "^7.0.17", + "postcss": "^7.0.26", "postcss-sorting": "^5.0.1" } }, "stylelint-scss": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.12.1.tgz", - "integrity": "sha512-k6B78HrqJ6pib5yOtmId7osVGrE2Amcf0VU+tdzk+oqwXe/0+VKtbkogeXrmGKt+z54QhxvjEO++qlE/a7EWlA==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.13.0.tgz", + "integrity": "sha512-SaLnvQyndaPcsgVJsMh6zJ1uKVzkRZJx+Wg/stzoB1mTBdEmGketbHrGbMQNymzH/0mJ06zDSpeCDvNxqIJE5A==", "dev": true, "requires": { - "lodash": "^4.17.15", + "lodash.isboolean": "^3.0.3", + "lodash.isregexp": "^4.0.1", + "lodash.isstring": "^4.0.1", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-selector-parser": "^6.0.2", @@ -10284,23 +10460,24 @@ } }, "tape": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/tape/-/tape-4.11.0.tgz", - "integrity": "sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.12.1.tgz", + "integrity": "sha512-xoK2ariLmdGxqyXhhxfIZlr0czNB8hNJeVQmHN4D7ZyBn30GUoa4q2oM4cX8jNhnj1mtILXn1ugbfxc0tTDKtA==", "dev": true, "requires": { - "deep-equal": "~1.0.1", + "deep-equal": "~1.1.1", "defined": "~1.0.0", "for-each": "~0.3.3", "function-bind": "~1.1.1", - "glob": "~7.1.4", + "glob": "~7.1.6", "has": "~1.0.3", "inherits": "~2.0.4", + "is-regex": "~1.0.5", "minimist": "~1.2.0", - "object-inspect": "~1.6.0", - "resolve": "~1.11.1", + "object-inspect": "~1.7.0", + "resolve": "~1.14.1", "resumer": "~0.0.0", - "string.prototype.trim": "~1.1.2", + "string.prototype.trim": "~1.2.1", "through": "~2.3.8" }, "dependencies": { @@ -10325,9 +10502,9 @@ "dev": true }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.1.tgz", + "integrity": "sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -10695,6 +10872,15 @@ "dup": "^1.0.0" } }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "uglify-js": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.3.tgz", @@ -10844,9 +11030,9 @@ } }, "unist-util-find-all-after": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.4.tgz", - "integrity": "sha512-CaxvMjTd+yF93BKLJvZnEfqdM7fgEACsIpQqz8vIj9CJnUb9VpyymFS3tg6TCtgrF7vfCJBF5jbT2Ox9CBRYRQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.5.tgz", + "integrity": "sha512-lWgIc3rrTMTlK1Y0hEuL+k+ApzFk78h+lsaa2gHf63Gp5Ww+mt11huDniuaoq1H+XMK2lIIjjPkncxXcDp3QDw==", "dev": true, "requires": { "unist-util-is": "^3.0.0" @@ -10859,18 +11045,18 @@ "dev": true }, "unist-util-remove-position": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.3.tgz", - "integrity": "sha512-CtszTlOjP2sBGYc2zcKA/CvNdTdEs3ozbiJ63IPBxh8iZg42SCCb8m04f8z2+V1aSk5a7BxbZKEdoDjadmBkWA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", "dev": true, "requires": { "unist-util-visit": "^1.1.0" } }, "unist-util-stringify-position": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.1.tgz", - "integrity": "sha512-Zqlf6+FRI39Bah8Q6ZnNGrEHUhwJOkHde2MHVk96lLyftfJJckaPslKgzhVcviXj8KcE9UJM9F+a4JEiBUTYgA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.2.tgz", + "integrity": "sha512-nK5n8OGhZ7ZgUwoUbL8uiVRwAbZyzBsB/Ddrlbu6jwwubFza4oe15KlyEaLNMXQW1svOQq4xesUeqA85YrIUQA==", "dev": true, "requires": { "@types/unist": "^2.0.2" @@ -11213,18 +11399,18 @@ } }, "vfile-location": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.5.tgz", - "integrity": "sha512-Pa1ey0OzYBkLPxPZI3d9E+S4BmvfVwNAAXrrqGbwTVXWaX2p9kM1zZ+n35UtVM06shmWKH4RPRN8KI80qE3wNQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", + "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", "dev": true }, "vfile-message": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.1.tgz", - "integrity": "sha512-KtasSV+uVU7RWhUn4Lw+wW1Zl/nW8JWx7JCPps10Y9JRRIDeDXf8wfBLoOSsJLyo27DqMyAi54C6Jf/d6Kr2Bw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.2.tgz", + "integrity": "sha512-gNV2Y2fDvDOOqq8bEe7cF3DXU6QgV4uA9zMR2P8tix11l1r7zju3zry3wZ8sx+BEfuO6WQ7z2QzfWTvqHQiwsA==", "dev": true, "requires": { - "@types/unist": "^2.0.2", + "@types/unist": "^2.0.0", "unist-util-stringify-position": "^2.0.0" } }, @@ -11359,10 +11545,16 @@ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true }, "world-calendars": { @@ -11399,6 +11591,18 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", + "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, "x-is-string": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", @@ -11423,6 +11627,15 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, + "yaml": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", + "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.6.3" + } + }, "yargs": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", diff --git a/package.json b/package.json index e7d676c1..13d5d173 100644 --- a/package.json +++ b/package.json @@ -7,27 +7,27 @@ "url": "https://github.com/cdubz/babybuddy.git" }, "devDependencies": { - "bootstrap": "4.3.1", + "bootstrap": "4.4.1", "del": "^5.1.0", "font-awesome": "^4.7.0", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", - "gulp-csso": "^3.0.1", + "gulp-csso": "^4.0.1", "gulp-flatten": "^0.4.0", "gulp-sass": "^4.0.2", "gulp-sass-glob": "^1.1.0", - "gulp-spawn": "^0.4.4", - "gulp-stylelint": "^10.0.0", + "gulp-spawn": "^0.4.5", + "gulp-stylelint": "^11.0.0", "gulp-uglify": "^3.0.2", "jquery": "^3.4.1", "moment": "^2.24.0", - "plotly.js": "^1.51.1", + "plotly.js": "^1.51.3", "popper.js": "^1.16.0", "pump": "^3.0.0", - "stylelint": "^11.1.1", - "stylelint-config-recommended-scss": "^4.0.0", - "stylelint-order": "^3.1.1", - "stylelint-scss": "^3.12.1", + "stylelint": "^12.0.1", + "stylelint-config-recommended-scss": "^4.1.0", + "stylelint-order": "^4.0.0", + "stylelint-scss": "^3.13.0", "tempusdominus-bootstrap-4": "^5.1.2", "tempusdominus-core": "^5.0.3" }, diff --git a/static/admin/js/SelectBox.b49f008d186b.js b/static/admin/js/SelectBox.b49f008d186b.js deleted file mode 100644 index 1a14959b..00000000 --- a/static/admin/js/SelectBox.b49f008d186b.js +++ /dev/null @@ -1,144 +0,0 @@ -(function($) { - 'use strict'; - var SelectBox = { - cache: {}, - init: function(id) { - var box = document.getElementById(id); - var node; - SelectBox.cache[id] = []; - var cache = SelectBox.cache[id]; - var boxOptions = box.options; - var boxOptionsLength = boxOptions.length; - for (var i = 0, j = boxOptionsLength; i < j; i++) { - node = boxOptions[i]; - cache.push({value: node.value, text: node.text, displayed: 1}); - } - }, - redisplay: function(id) { - // Repopulate HTML select box from cache - var box = document.getElementById(id); - var node; - $(box).empty(); // clear all options - var new_options = box.outerHTML.slice(0, -9); // grab just the opening tag - var cache = SelectBox.cache[id]; - for (var i = 0, j = cache.length; i < j; i++) { - node = cache[i]; - if (node.displayed) { - var new_option = new Option(node.text, node.value, false, false); - // Shows a tooltip when hovering over the option - new_option.setAttribute("title", node.text); - new_options += new_option.outerHTML; - } - } - new_options += ''; - box.outerHTML = new_options; - }, - filter: function(id, text) { - // Redisplay the HTML select box, displaying only the choices containing ALL - // the words in text. (It's an AND search.) - var tokens = text.toLowerCase().split(/\s+/); - var node, token; - var cache = SelectBox.cache[id]; - for (var i = 0, j = cache.length; i < j; i++) { - node = cache[i]; - node.displayed = 1; - var node_text = node.text.toLowerCase(); - var numTokens = tokens.length; - for (var k = 0; k < numTokens; k++) { - token = tokens[k]; - if (node_text.indexOf(token) === -1) { - node.displayed = 0; - break; // Once the first token isn't found we're done - } - } - } - SelectBox.redisplay(id); - }, - delete_from_cache: function(id, value) { - var node, delete_index = null; - var cache = SelectBox.cache[id]; - for (var i = 0, j = cache.length; i < j; i++) { - node = cache[i]; - if (node.value === value) { - delete_index = i; - break; - } - } - cache.splice(delete_index, 1); - }, - add_to_cache: function(id, option) { - SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); - }, - cache_contains: function(id, value) { - // Check if an item is contained in the cache - var node; - var cache = SelectBox.cache[id]; - for (var i = 0, j = cache.length; i < j; i++) { - node = cache[i]; - if (node.value === value) { - return true; - } - } - return false; - }, - move: function(from, to) { - var from_box = document.getElementById(from); - var option; - var boxOptions = from_box.options; - var boxOptionsLength = boxOptions.length; - for (var i = 0, j = boxOptionsLength; i < j; i++) { - option = boxOptions[i]; - var option_value = option.value; - if (option.selected && SelectBox.cache_contains(from, option_value)) { - SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1}); - SelectBox.delete_from_cache(from, option_value); - } - } - SelectBox.redisplay(from); - SelectBox.redisplay(to); - }, - move_all: function(from, to) { - var from_box = document.getElementById(from); - var option; - var boxOptions = from_box.options; - var boxOptionsLength = boxOptions.length; - for (var i = 0, j = boxOptionsLength; i < j; i++) { - option = boxOptions[i]; - var option_value = option.value; - if (SelectBox.cache_contains(from, option_value)) { - SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1}); - SelectBox.delete_from_cache(from, option_value); - } - } - SelectBox.redisplay(from); - SelectBox.redisplay(to); - }, - sort: function(id) { - SelectBox.cache[id].sort(function(a, b) { - a = a.text.toLowerCase(); - b = b.text.toLowerCase(); - try { - if (a > b) { - return 1; - } - if (a < b) { - return -1; - } - } - catch (e) { - // silently fail on IE 'unknown' exception - } - return 0; - } ); - }, - select_all: function(id) { - var box = document.getElementById(id); - var boxOptions = box.options; - var boxOptionsLength = boxOptions.length; - for (var i = 0; i < boxOptionsLength; i++) { - boxOptions[i].selected = 'selected'; - } - } - }; - window.SelectBox = SelectBox; -})(django.jQuery); diff --git a/static/admin/js/SelectBox.b49f008d186b.js.gz b/static/admin/js/SelectBox.b49f008d186b.js.gz deleted file mode 100644 index f743a4a8..00000000 Binary files a/static/admin/js/SelectBox.b49f008d186b.js.gz and /dev/null differ diff --git a/static/admin/js/actions.2fb8e8349c22.js b/static/admin/js/actions.2fb8e8349c22.js deleted file mode 100644 index 524616fb..00000000 --- a/static/admin/js/actions.2fb8e8349c22.js +++ /dev/null @@ -1,153 +0,0 @@ -/*global gettext, interpolate, ngettext*/ -(function($) { - 'use strict'; - var lastChecked; - - $.fn.actions = function(opts) { - var options = $.extend({}, $.fn.actions.defaults, opts); - var actionCheckboxes = $(this); - var list_editable_changed = false; - var showQuestion = function() { - $(options.acrossClears).hide(); - $(options.acrossQuestions).show(); - $(options.allContainer).hide(); - }, - showClear = function() { - $(options.acrossClears).show(); - $(options.acrossQuestions).hide(); - $(options.actionContainer).toggleClass(options.selectedClass); - $(options.allContainer).show(); - $(options.counterContainer).hide(); - }, - reset = function() { - $(options.acrossClears).hide(); - $(options.acrossQuestions).hide(); - $(options.allContainer).hide(); - $(options.counterContainer).show(); - }, - clearAcross = function() { - reset(); - $(options.acrossInput).val(0); - $(options.actionContainer).removeClass(options.selectedClass); - }, - checker = function(checked) { - if (checked) { - showQuestion(); - } else { - reset(); - } - $(actionCheckboxes).prop("checked", checked) - .parent().parent().toggleClass(options.selectedClass, checked); - }, - updateCounter = function() { - var sel = $(actionCheckboxes).filter(":checked").length; - // data-actions-icnt is defined in the generated HTML - // and contains the total amount of objects in the queryset - var actions_icnt = $('.action-counter').data('actionsIcnt'); - $(options.counterContainer).html(interpolate( - ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), { - sel: sel, - cnt: actions_icnt - }, true)); - $(options.allToggle).prop("checked", function() { - var value; - if (sel === actionCheckboxes.length) { - value = true; - showQuestion(); - } else { - value = false; - clearAcross(); - } - return value; - }); - }; - // Show counter by default - $(options.counterContainer).show(); - // Check state of checkboxes and reinit state if needed - $(this).filter(":checked").each(function(i) { - $(this).parent().parent().toggleClass(options.selectedClass); - updateCounter(); - if ($(options.acrossInput).val() === 1) { - showClear(); - } - }); - $(options.allToggle).show().on('click', function() { - checker($(this).prop("checked")); - updateCounter(); - }); - $("a", options.acrossQuestions).on('click', function(event) { - event.preventDefault(); - $(options.acrossInput).val(1); - showClear(); - }); - $("a", options.acrossClears).on('click', function(event) { - event.preventDefault(); - $(options.allToggle).prop("checked", false); - clearAcross(); - checker(0); - updateCounter(); - }); - lastChecked = null; - $(actionCheckboxes).on('click', function(event) { - if (!event) { event = window.event; } - var target = event.target ? event.target : event.srcElement; - if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) { - var inrange = false; - $(lastChecked).prop("checked", target.checked) - .parent().parent().toggleClass(options.selectedClass, target.checked); - $(actionCheckboxes).each(function() { - if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) { - inrange = (inrange) ? false : true; - } - if (inrange) { - $(this).prop("checked", target.checked) - .parent().parent().toggleClass(options.selectedClass, target.checked); - } - }); - } - $(target).parent().parent().toggleClass(options.selectedClass, target.checked); - lastChecked = target; - updateCounter(); - }); - $('form#changelist-form table#result_list tr').on('change', 'td:gt(0) :input', function() { - list_editable_changed = true; - }); - $('form#changelist-form button[name="index"]').on('click', function(event) { - if (list_editable_changed) { - return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); - } - }); - $('form#changelist-form input[name="_save"]').on('click', function(event) { - var action_changed = false; - $('select option:selected', options.actionContainer).each(function() { - if ($(this).val()) { - action_changed = true; - } - }); - if (action_changed) { - if (list_editable_changed) { - return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")); - } else { - return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button.")); - } - } - }); - }; - /* Setup plugin defaults */ - $.fn.actions.defaults = { - actionContainer: "div.actions", - counterContainer: "span.action-counter", - allContainer: "div.actions span.all", - acrossInput: "div.actions input.select-across", - acrossQuestions: "div.actions span.question", - acrossClears: "div.actions span.clear", - allToggle: "#action-toggle", - selectedClass: "selected" - }; - $(document).ready(function() { - var $actionsEls = $('tr input.action-select'); - if ($actionsEls.length > 0) { - $actionsEls.actions(); - } - }); -})(django.jQuery); diff --git a/static/admin/js/actions.2fb8e8349c22.js.gz b/static/admin/js/actions.2fb8e8349c22.js.gz deleted file mode 100644 index 9e8b13ed..00000000 Binary files a/static/admin/js/actions.2fb8e8349c22.js.gz and /dev/null differ diff --git a/static/admin/js/admin/DateTimeShortcuts.c085703ffd82.js b/static/admin/js/admin/DateTimeShortcuts.c085703ffd82.js deleted file mode 100644 index 08b2785c..00000000 --- a/static/admin/js/admin/DateTimeShortcuts.c085703ffd82.js +++ /dev/null @@ -1,423 +0,0 @@ -/*global Calendar, findPosX, findPosY, getStyle, get_format, gettext, gettext_noop, interpolate, ngettext, quickElement*/ -// Inserts shortcut buttons after all of the following: -// -// -(function() { - 'use strict'; - var DateTimeShortcuts = { - calendars: [], - calendarInputs: [], - clockInputs: [], - clockHours: { - default_: [ - [gettext_noop('Now'), -1], - [gettext_noop('Midnight'), 0], - [gettext_noop('6 a.m.'), 6], - [gettext_noop('Noon'), 12], - [gettext_noop('6 p.m.'), 18] - ] - }, - dismissClockFunc: [], - dismissCalendarFunc: [], - calendarDivName1: 'calendarbox', // name of calendar
that gets toggled - calendarDivName2: 'calendarin', // name of
that contains calendar - calendarLinkName: 'calendarlink',// name of the link that is used to toggle - clockDivName: 'clockbox', // name of clock
that gets toggled - clockLinkName: 'clocklink', // name of the link that is used to toggle - shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts - timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch - timezoneOffset: 0, - init: function() { - var body = document.getElementsByTagName('body')[0]; - var serverOffset = body.getAttribute('data-admin-utc-offset'); - if (serverOffset) { - var localOffset = new Date().getTimezoneOffset() * -60; - DateTimeShortcuts.timezoneOffset = localOffset - serverOffset; - } - - var inputs = document.getElementsByTagName('input'); - for (var i = 0; i < inputs.length; i++) { - var inp = inputs[i]; - if (inp.getAttribute('type') === 'text' && inp.className.match(/vTimeField/)) { - DateTimeShortcuts.addClock(inp); - DateTimeShortcuts.addTimezoneWarning(inp); - } - else if (inp.getAttribute('type') === 'text' && inp.className.match(/vDateField/)) { - DateTimeShortcuts.addCalendar(inp); - DateTimeShortcuts.addTimezoneWarning(inp); - } - } - }, - // Return the current time while accounting for the server timezone. - now: function() { - var body = document.getElementsByTagName('body')[0]; - var serverOffset = body.getAttribute('data-admin-utc-offset'); - if (serverOffset) { - var localNow = new Date(); - var localOffset = localNow.getTimezoneOffset() * -60; - localNow.setTime(localNow.getTime() + 1000 * (serverOffset - localOffset)); - return localNow; - } else { - return new Date(); - } - }, - // Add a warning when the time zone in the browser and backend do not match. - addTimezoneWarning: function(inp) { - var warningClass = DateTimeShortcuts.timezoneWarningClass; - var timezoneOffset = DateTimeShortcuts.timezoneOffset / 3600; - - // Only warn if there is a time zone mismatch. - if (!timezoneOffset) { - return; - } - - // Check if warning is already there. - if (inp.parentNode.querySelectorAll('.' + warningClass).length) { - return; - } - - var message; - if (timezoneOffset > 0) { - message = ngettext( - 'Note: You are %s hour ahead of server time.', - 'Note: You are %s hours ahead of server time.', - timezoneOffset - ); - } - else { - timezoneOffset *= -1; - message = ngettext( - 'Note: You are %s hour behind server time.', - 'Note: You are %s hours behind server time.', - timezoneOffset - ); - } - message = interpolate(message, [timezoneOffset]); - - var warning = document.createElement('span'); - warning.className = warningClass; - warning.textContent = message; - inp.parentNode.appendChild(document.createElement('br')); - inp.parentNode.appendChild(warning); - }, - // Add clock widget to a given field - addClock: function(inp) { - var num = DateTimeShortcuts.clockInputs.length; - DateTimeShortcuts.clockInputs[num] = inp; - DateTimeShortcuts.dismissClockFunc[num] = function() { DateTimeShortcuts.dismissClock(num); return true; }; - - // Shortcut links (clock icon and "Now" link) - var shortcuts_span = document.createElement('span'); - shortcuts_span.className = DateTimeShortcuts.shortCutsClass; - inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling); - var now_link = document.createElement('a'); - now_link.setAttribute('href', "#"); - now_link.textContent = gettext('Now'); - now_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.handleClockQuicklink(num, -1); - }); - var clock_link = document.createElement('a'); - clock_link.setAttribute('href', '#'); - clock_link.id = DateTimeShortcuts.clockLinkName + num; - clock_link.addEventListener('click', function(e) { - e.preventDefault(); - // avoid triggering the document click handler to dismiss the clock - e.stopPropagation(); - DateTimeShortcuts.openClock(num); - }); - - quickElement( - 'span', clock_link, '', - 'class', 'clock-icon', - 'title', gettext('Choose a Time') - ); - shortcuts_span.appendChild(document.createTextNode('\u00A0')); - shortcuts_span.appendChild(now_link); - shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0')); - shortcuts_span.appendChild(clock_link); - - // Create clock link div - // - // Markup looks like: - //
- //

Choose a time

- // - //

Cancel

- //
- - var clock_box = document.createElement('div'); - clock_box.style.display = 'none'; - clock_box.style.position = 'absolute'; - clock_box.className = 'clockbox module'; - clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num); - document.body.appendChild(clock_box); - clock_box.addEventListener('click', function(e) { e.stopPropagation(); }); - - quickElement('h2', clock_box, gettext('Choose a time')); - var time_list = quickElement('ul', clock_box); - time_list.className = 'timelist'; - // The list of choices can be overridden in JavaScript like this: - // DateTimeShortcuts.clockHours.name = [['3 a.m.', 3]]; - // where name is the name attribute of the . - var name = typeof DateTimeShortcuts.clockHours[inp.name] === 'undefined' ? 'default_' : inp.name; - DateTimeShortcuts.clockHours[name].forEach(function(element) { - var time_link = quickElement('a', quickElement('li', time_list), gettext(element[0]), 'href', '#'); - time_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.handleClockQuicklink(num, element[1]); - }); - }); - - var cancel_p = quickElement('p', clock_box); - cancel_p.className = 'calendar-cancel'; - var cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#'); - cancel_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.dismissClock(num); - }); - - document.addEventListener('keyup', function(event) { - if (event.which === 27) { - // ESC key closes popup - DateTimeShortcuts.dismissClock(num); - event.preventDefault(); - } - }); - }, - openClock: function(num) { - var clock_box = document.getElementById(DateTimeShortcuts.clockDivName + num); - var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName + num); - - // Recalculate the clockbox position - // is it left-to-right or right-to-left layout ? - if (getStyle(document.body, 'direction') !== 'rtl') { - clock_box.style.left = findPosX(clock_link) + 17 + 'px'; - } - else { - // since style's width is in em, it'd be tough to calculate - // px value of it. let's use an estimated px for now - // TODO: IE returns wrong value for findPosX when in rtl mode - // (it returns as it was left aligned), needs to be fixed. - clock_box.style.left = findPosX(clock_link) - 110 + 'px'; - } - clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px'; - - // Show the clock box - clock_box.style.display = 'block'; - document.addEventListener('click', DateTimeShortcuts.dismissClockFunc[num]); - }, - dismissClock: function(num) { - document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none'; - document.removeEventListener('click', DateTimeShortcuts.dismissClockFunc[num]); - }, - handleClockQuicklink: function(num, val) { - var d; - if (val === -1) { - d = DateTimeShortcuts.now(); - } - else { - d = new Date(1970, 1, 1, val, 0, 0, 0); - } - DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]); - DateTimeShortcuts.clockInputs[num].focus(); - DateTimeShortcuts.dismissClock(num); - }, - // Add calendar widget to a given field. - addCalendar: function(inp) { - var num = DateTimeShortcuts.calendars.length; - - DateTimeShortcuts.calendarInputs[num] = inp; - DateTimeShortcuts.dismissCalendarFunc[num] = function() { DateTimeShortcuts.dismissCalendar(num); return true; }; - - // Shortcut links (calendar icon and "Today" link) - var shortcuts_span = document.createElement('span'); - shortcuts_span.className = DateTimeShortcuts.shortCutsClass; - inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling); - var today_link = document.createElement('a'); - today_link.setAttribute('href', '#'); - today_link.appendChild(document.createTextNode(gettext('Today'))); - today_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.handleCalendarQuickLink(num, 0); - }); - var cal_link = document.createElement('a'); - cal_link.setAttribute('href', '#'); - cal_link.id = DateTimeShortcuts.calendarLinkName + num; - cal_link.addEventListener('click', function(e) { - e.preventDefault(); - // avoid triggering the document click handler to dismiss the calendar - e.stopPropagation(); - DateTimeShortcuts.openCalendar(num); - }); - quickElement( - 'span', cal_link, '', - 'class', 'date-icon', - 'title', gettext('Choose a Date') - ); - shortcuts_span.appendChild(document.createTextNode('\u00A0')); - shortcuts_span.appendChild(today_link); - shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0')); - shortcuts_span.appendChild(cal_link); - - // Create calendarbox div. - // - // Markup looks like: - // - //
- //

- // - // February 2003 - //

- //
- // - //
- //
- // Yesterday | Today | Tomorrow - //
- //

Cancel

- //
- var cal_box = document.createElement('div'); - cal_box.style.display = 'none'; - cal_box.style.position = 'absolute'; - cal_box.className = 'calendarbox module'; - cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num); - document.body.appendChild(cal_box); - cal_box.addEventListener('click', function(e) { e.stopPropagation(); }); - - // next-prev links - var cal_nav = quickElement('div', cal_box); - var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', '#'); - cal_nav_prev.className = 'calendarnav-previous'; - cal_nav_prev.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.drawPrev(num); - }); - - var cal_nav_next = quickElement('a', cal_nav, '>', 'href', '#'); - cal_nav_next.className = 'calendarnav-next'; - cal_nav_next.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.drawNext(num); - }); - - // main box - var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num); - cal_main.className = 'calendar'; - DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num)); - DateTimeShortcuts.calendars[num].drawCurrent(); - - // calendar shortcuts - var shortcuts = quickElement('div', cal_box); - shortcuts.className = 'calendar-shortcuts'; - var day_link = quickElement('a', shortcuts, gettext('Yesterday'), 'href', '#'); - day_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.handleCalendarQuickLink(num, -1); - }); - shortcuts.appendChild(document.createTextNode('\u00A0|\u00A0')); - day_link = quickElement('a', shortcuts, gettext('Today'), 'href', '#'); - day_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.handleCalendarQuickLink(num, 0); - }); - shortcuts.appendChild(document.createTextNode('\u00A0|\u00A0')); - day_link = quickElement('a', shortcuts, gettext('Tomorrow'), 'href', '#'); - day_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.handleCalendarQuickLink(num, +1); - }); - - // cancel bar - var cancel_p = quickElement('p', cal_box); - cancel_p.className = 'calendar-cancel'; - var cancel_link = quickElement('a', cancel_p, gettext('Cancel'), 'href', '#'); - cancel_link.addEventListener('click', function(e) { - e.preventDefault(); - DateTimeShortcuts.dismissCalendar(num); - }); - document.addEventListener('keyup', function(event) { - if (event.which === 27) { - // ESC key closes popup - DateTimeShortcuts.dismissCalendar(num); - event.preventDefault(); - } - }); - }, - openCalendar: function(num) { - var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1 + num); - var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName + num); - var inp = DateTimeShortcuts.calendarInputs[num]; - - // Determine if the current value in the input has a valid date. - // If so, draw the calendar with that date's year and month. - if (inp.value) { - var format = get_format('DATE_INPUT_FORMATS')[0]; - var selected = inp.value.strptime(format); - var year = selected.getUTCFullYear(); - var month = selected.getUTCMonth() + 1; - var re = /\d{4}/; - if (re.test(year.toString()) && month >= 1 && month <= 12) { - DateTimeShortcuts.calendars[num].drawDate(month, year, selected); - } - } - - // Recalculate the clockbox position - // is it left-to-right or right-to-left layout ? - if (getStyle(document.body, 'direction') !== 'rtl') { - cal_box.style.left = findPosX(cal_link) + 17 + 'px'; - } - else { - // since style's width is in em, it'd be tough to calculate - // px value of it. let's use an estimated px for now - // TODO: IE returns wrong value for findPosX when in rtl mode - // (it returns as it was left aligned), needs to be fixed. - cal_box.style.left = findPosX(cal_link) - 180 + 'px'; - } - cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px'; - - cal_box.style.display = 'block'; - document.addEventListener('click', DateTimeShortcuts.dismissCalendarFunc[num]); - }, - dismissCalendar: function(num) { - document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none'; - document.removeEventListener('click', DateTimeShortcuts.dismissCalendarFunc[num]); - }, - drawPrev: function(num) { - DateTimeShortcuts.calendars[num].drawPreviousMonth(); - }, - drawNext: function(num) { - DateTimeShortcuts.calendars[num].drawNextMonth(); - }, - handleCalendarCallback: function(num) { - var format = get_format('DATE_INPUT_FORMATS')[0]; - // the format needs to be escaped a little - format = format.replace('\\', '\\\\') - .replace('\r', '\\r') - .replace('\n', '\\n') - .replace('\t', '\\t') - .replace("'", "\\'"); - return function(y, m, d) { - DateTimeShortcuts.calendarInputs[num].value = new Date(y, m - 1, d).strftime(format); - DateTimeShortcuts.calendarInputs[num].focus(); - document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none'; - }; - }, - handleCalendarQuickLink: function(num, offset) { - var d = DateTimeShortcuts.now(); - d.setDate(d.getDate() + offset); - DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]); - DateTimeShortcuts.calendarInputs[num].focus(); - DateTimeShortcuts.dismissCalendar(num); - } - }; - - window.addEventListener('load', DateTimeShortcuts.init); - window.DateTimeShortcuts = DateTimeShortcuts; -})(); diff --git a/static/admin/js/admin/DateTimeShortcuts.c085703ffd82.js.gz b/static/admin/js/admin/DateTimeShortcuts.c085703ffd82.js.gz deleted file mode 100644 index c5ecf1ba..00000000 Binary files a/static/admin/js/admin/DateTimeShortcuts.c085703ffd82.js.gz and /dev/null differ diff --git a/static/admin/js/cancel.d6b4b556c3aa.js b/static/admin/js/cancel.d6b4b556c3aa.js deleted file mode 100644 index 04ec812a..00000000 --- a/static/admin/js/cancel.d6b4b556c3aa.js +++ /dev/null @@ -1,13 +0,0 @@ -(function($) { - 'use strict'; - $(function() { - $('.cancel-link').on('click', function(e) { - e.preventDefault(); - if (window.location.search.indexOf('&_popup=1') === -1) { - window.history.back(); // Go back if not a popup. - } else { - window.close(); // Otherwise, close the popup. - } - }); - }); -})(django.jQuery); diff --git a/static/admin/js/cancel.d6b4b556c3aa.js.gz b/static/admin/js/cancel.d6b4b556c3aa.js.gz deleted file mode 100644 index e60263d1..00000000 Binary files a/static/admin/js/cancel.d6b4b556c3aa.js.gz and /dev/null differ diff --git a/static/admin/js/core.9c3bce7d1006.js b/static/admin/js/core.9c3bce7d1006.js deleted file mode 100644 index 591394e5..00000000 --- a/static/admin/js/core.9c3bce7d1006.js +++ /dev/null @@ -1,212 +0,0 @@ -// Core javascript helper functions - -// basic browser identification & version -var isOpera = (navigator.userAgent.indexOf("Opera") >= 0) && parseFloat(navigator.appVersion); -var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]); - -// quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]); -function quickElement() { - 'use strict'; - var obj = document.createElement(arguments[0]); - if (arguments[2]) { - var textNode = document.createTextNode(arguments[2]); - obj.appendChild(textNode); - } - var len = arguments.length; - for (var i = 3; i < len; i += 2) { - obj.setAttribute(arguments[i], arguments[i + 1]); - } - arguments[1].appendChild(obj); - return obj; -} - -// "a" is reference to an object -function removeChildren(a) { - 'use strict'; - while (a.hasChildNodes()) { - a.removeChild(a.lastChild); - } -} - -// ---------------------------------------------------------------------------- -// Find-position functions by PPK -// See https://www.quirksmode.org/js/findpos.html -// ---------------------------------------------------------------------------- -function findPosX(obj) { - 'use strict'; - var curleft = 0; - if (obj.offsetParent) { - while (obj.offsetParent) { - curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft); - obj = obj.offsetParent; - } - // IE offsetParent does not include the top-level - if (isIE && obj.parentElement) { - curleft += obj.offsetLeft - obj.scrollLeft; - } - } else if (obj.x) { - curleft += obj.x; - } - return curleft; -} - -function findPosY(obj) { - 'use strict'; - var curtop = 0; - if (obj.offsetParent) { - while (obj.offsetParent) { - curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop); - obj = obj.offsetParent; - } - // IE offsetParent does not include the top-level - if (isIE && obj.parentElement) { - curtop += obj.offsetTop - obj.scrollTop; - } - } else if (obj.y) { - curtop += obj.y; - } - return curtop; -} - -//----------------------------------------------------------------------------- -// Date object extensions -// ---------------------------------------------------------------------------- -(function() { - 'use strict'; - Date.prototype.getTwelveHours = function() { - var hours = this.getHours(); - if (hours === 0) { - return 12; - } - else { - return hours <= 12 ? hours : hours - 12; - } - }; - - Date.prototype.getTwoDigitMonth = function() { - return (this.getMonth() < 9) ? '0' + (this.getMonth() + 1) : (this.getMonth() + 1); - }; - - Date.prototype.getTwoDigitDate = function() { - return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate(); - }; - - Date.prototype.getTwoDigitTwelveHour = function() { - return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours(); - }; - - Date.prototype.getTwoDigitHour = function() { - return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours(); - }; - - Date.prototype.getTwoDigitMinute = function() { - return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); - }; - - Date.prototype.getTwoDigitSecond = function() { - return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds(); - }; - - Date.prototype.getHourMinute = function() { - return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute(); - }; - - Date.prototype.getHourMinuteSecond = function() { - return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond(); - }; - - Date.prototype.getFullMonthName = function() { - return typeof window.CalendarNamespace === "undefined" - ? this.getTwoDigitMonth() - : window.CalendarNamespace.monthsOfYear[this.getMonth()]; - }; - - Date.prototype.strftime = function(format) { - var fields = { - B: this.getFullMonthName(), - c: this.toString(), - d: this.getTwoDigitDate(), - H: this.getTwoDigitHour(), - I: this.getTwoDigitTwelveHour(), - m: this.getTwoDigitMonth(), - M: this.getTwoDigitMinute(), - p: (this.getHours() >= 12) ? 'PM' : 'AM', - S: this.getTwoDigitSecond(), - w: '0' + this.getDay(), - x: this.toLocaleDateString(), - X: this.toLocaleTimeString(), - y: ('' + this.getFullYear()).substr(2, 4), - Y: '' + this.getFullYear(), - '%': '%' - }; - var result = '', i = 0; - while (i < format.length) { - if (format.charAt(i) === '%') { - result = result + fields[format.charAt(i + 1)]; - ++i; - } - else { - result = result + format.charAt(i); - } - ++i; - } - return result; - }; - -// ---------------------------------------------------------------------------- -// String object extensions -// ---------------------------------------------------------------------------- - String.prototype.pad_left = function(pad_length, pad_string) { - var new_string = this; - for (var i = 0; new_string.length < pad_length; i++) { - new_string = pad_string + new_string; - } - return new_string; - }; - - String.prototype.strptime = function(format) { - var split_format = format.split(/[.\-/]/); - var date = this.split(/[.\-/]/); - var i = 0; - var day, month, year; - while (i < split_format.length) { - switch (split_format[i]) { - case "%d": - day = date[i]; - break; - case "%m": - month = date[i] - 1; - break; - case "%Y": - year = date[i]; - break; - case "%y": - year = date[i]; - break; - } - ++i; - } - // Create Date object from UTC since the parsed value is supposed to be - // in UTC, not local time. Also, the calendar uses UTC functions for - // date extraction. - return new Date(Date.UTC(year, month, day)); - }; - -})(); -// ---------------------------------------------------------------------------- -// Get the computed style for and element -// ---------------------------------------------------------------------------- -function getStyle(oElm, strCssRule) { - 'use strict'; - var strValue = ""; - if(document.defaultView && document.defaultView.getComputedStyle) { - strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); - } - else if(oElm.currentStyle) { - strCssRule = strCssRule.replace(/\-(\w)/g, function(strMatch, p1) { - return p1.toUpperCase(); - }); - strValue = oElm.currentStyle[strCssRule]; - } - return strValue; -} diff --git a/static/admin/js/core.9c3bce7d1006.js.gz b/static/admin/js/core.9c3bce7d1006.js.gz deleted file mode 100644 index 1e37902b..00000000 Binary files a/static/admin/js/core.9c3bce7d1006.js.gz and /dev/null differ diff --git a/static/admin/js/inlines.3b3fb199b064.js b/static/admin/js/inlines.3b3fb199b064.js deleted file mode 100644 index 045ef1ec..00000000 --- a/static/admin/js/inlines.3b3fb199b064.js +++ /dev/null @@ -1,298 +0,0 @@ -/*global DateTimeShortcuts, SelectFilter*/ -/** - * Django admin inlines - * - * Based on jQuery Formset 1.1 - * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com) - * @requires jQuery 1.2.6 or later - * - * Copyright (c) 2009, Stanislaus Madueke - * All rights reserved. - * - * Spiced up with Code from Zain Memon's GSoC project 2009 - * and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip. - * - * Licensed under the New BSD License - * See: https://opensource.org/licenses/bsd-license.php - */ -(function($) { - 'use strict'; - $.fn.formset = function(opts) { - var options = $.extend({}, $.fn.formset.defaults, opts); - var $this = $(this); - var $parent = $this.parent(); - var updateElementIndex = function(el, prefix, ndx) { - var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))"); - var replacement = prefix + "-" + ndx; - if ($(el).prop("for")) { - $(el).prop("for", $(el).prop("for").replace(id_regex, replacement)); - } - if (el.id) { - el.id = el.id.replace(id_regex, replacement); - } - if (el.name) { - el.name = el.name.replace(id_regex, replacement); - } - }; - var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off"); - var nextIndex = parseInt(totalForms.val(), 10); - var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off"); - // only show the add button if we are allowed to add more items, - // note that max_num = None translates to a blank string. - var showAddButton = maxForms.val() === '' || (maxForms.val() - totalForms.val()) > 0; - $this.each(function(i) { - $(this).not("." + options.emptyCssClass).addClass(options.formCssClass); - }); - if ($this.length && showAddButton) { - var addButton = options.addButton; - if (addButton === null) { - if ($this.prop("tagName") === "TR") { - // If forms are laid out as table rows, insert the - // "add" button in a new table row: - var numCols = this.eq(-1).children().length; - $parent.append('' + options.addText + ""); - addButton = $parent.find("tr:last a"); - } else { - // Otherwise, insert it immediately after the last form: - $this.filter(":last").after('"); - addButton = $this.filter(":last").next().find("a"); - } - } - addButton.on('click', function(e) { - e.preventDefault(); - var template = $("#" + options.prefix + "-empty"); - var row = template.clone(true); - row.removeClass(options.emptyCssClass) - .addClass(options.formCssClass) - .attr("id", options.prefix + "-" + nextIndex); - if (row.is("tr")) { - // If the forms are laid out in table rows, insert - // the remove button into the last table cell: - row.children(":last").append('
' + options.deleteText + "
"); - } else if (row.is("ul") || row.is("ol")) { - // If they're laid out as an ordered/unordered list, - // insert an
  • after the last list item: - row.append('
  • ' + options.deleteText + "
  • "); - } else { - // Otherwise, just insert the remove button as the - // last child element of the form's container: - row.children(":first").append('' + options.deleteText + ""); - } - row.find("*").each(function() { - updateElementIndex(this, options.prefix, totalForms.val()); - }); - // Insert the new form when it has been fully edited - row.insertBefore($(template)); - // Update number of total forms - $(totalForms).val(parseInt(totalForms.val(), 10) + 1); - nextIndex += 1; - // Hide add button in case we've hit the max, except we want to add infinitely - if ((maxForms.val() !== '') && (maxForms.val() - totalForms.val()) <= 0) { - addButton.parent().hide(); - } - // The delete button of each row triggers a bunch of other things - row.find("a." + options.deleteCssClass).on('click', function(e1) { - e1.preventDefault(); - // Remove the parent form containing this button: - row.remove(); - nextIndex -= 1; - // If a post-delete callback was provided, call it with the deleted form: - if (options.removed) { - options.removed(row); - } - $(document).trigger('formset:removed', [row, options.prefix]); - // Update the TOTAL_FORMS form count. - var forms = $("." + options.formCssClass); - $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); - // Show add button again once we drop below max - if ((maxForms.val() === '') || (maxForms.val() - forms.length) > 0) { - addButton.parent().show(); - } - // Also, update names and ids for all remaining form controls - // so they remain in sequence: - var i, formCount; - var updateElementCallback = function() { - updateElementIndex(this, options.prefix, i); - }; - for (i = 0, formCount = forms.length; i < formCount; i++) { - updateElementIndex($(forms).get(i), options.prefix, i); - $(forms.get(i)).find("*").each(updateElementCallback); - } - }); - // If a post-add callback was supplied, call it with the added form: - if (options.added) { - options.added(row); - } - $(document).trigger('formset:added', [row, options.prefix]); - }); - } - return this; - }; - - /* Setup plugin defaults */ - $.fn.formset.defaults = { - prefix: "form", // The form prefix for your django formset - addText: "add another", // Text for the add link - deleteText: "remove", // Text for the delete link - addCssClass: "add-row", // CSS class applied to the add link - deleteCssClass: "delete-row", // CSS class applied to the delete link - emptyCssClass: "empty-row", // CSS class applied to the empty row - formCssClass: "dynamic-form", // CSS class applied to each form in a formset - added: null, // Function called each time a new form is added - removed: null, // Function called each time a form is deleted - addButton: null // Existing add button to use - }; - - - // Tabular inlines --------------------------------------------------------- - $.fn.tabularFormset = function(selector, options) { - var $rows = $(this); - var alternatingRows = function(row) { - $(selector).not(".add-row").removeClass("row1 row2") - .filter(":even").addClass("row1").end() - .filter(":odd").addClass("row2"); - }; - - var reinitDateTimeShortCuts = function() { - // Reinitialize the calendar and clock widgets by force - if (typeof DateTimeShortcuts !== "undefined") { - $(".datetimeshortcuts").remove(); - DateTimeShortcuts.init(); - } - }; - - var updateSelectFilter = function() { - // If any SelectFilter widgets are a part of the new form, - // instantiate a new SelectFilter instance for it. - if (typeof SelectFilter !== 'undefined') { - $('.selectfilter').each(function(index, value) { - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length - 1], false); - }); - $('.selectfilterstacked').each(function(index, value) { - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length - 1], true); - }); - } - }; - - var initPrepopulatedFields = function(row) { - row.find('.prepopulated_field').each(function() { - var field = $(this), - input = field.find('input, select, textarea'), - dependency_list = input.data('dependency_list') || [], - dependencies = []; - $.each(dependency_list, function(i, field_name) { - dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id')); - }); - if (dependencies.length) { - input.prepopulate(dependencies, input.attr('maxlength')); - } - }); - }; - - $rows.formset({ - prefix: options.prefix, - addText: options.addText, - formCssClass: "dynamic-" + options.prefix, - deleteCssClass: "inline-deletelink", - deleteText: options.deleteText, - emptyCssClass: "empty-form", - removed: alternatingRows, - added: function(row) { - initPrepopulatedFields(row); - reinitDateTimeShortCuts(); - updateSelectFilter(); - alternatingRows(row); - }, - addButton: options.addButton - }); - - return $rows; - }; - - // Stacked inlines --------------------------------------------------------- - $.fn.stackedFormset = function(selector, options) { - var $rows = $(this); - var updateInlineLabel = function(row) { - $(selector).find(".inline_label").each(function(i) { - var count = i + 1; - $(this).html($(this).html().replace(/(#\d+)/g, "#" + count)); - }); - }; - - var reinitDateTimeShortCuts = function() { - // Reinitialize the calendar and clock widgets by force, yuck. - if (typeof DateTimeShortcuts !== "undefined") { - $(".datetimeshortcuts").remove(); - DateTimeShortcuts.init(); - } - }; - - var updateSelectFilter = function() { - // If any SelectFilter widgets were added, instantiate a new instance. - if (typeof SelectFilter !== "undefined") { - $(".selectfilter").each(function(index, value) { - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length - 1], false); - }); - $(".selectfilterstacked").each(function(index, value) { - var namearr = value.name.split('-'); - SelectFilter.init(value.id, namearr[namearr.length - 1], true); - }); - } - }; - - var initPrepopulatedFields = function(row) { - row.find('.prepopulated_field').each(function() { - var field = $(this), - input = field.find('input, select, textarea'), - dependency_list = input.data('dependency_list') || [], - dependencies = []; - $.each(dependency_list, function(i, field_name) { - dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id')); - }); - if (dependencies.length) { - input.prepopulate(dependencies, input.attr('maxlength')); - } - }); - }; - - $rows.formset({ - prefix: options.prefix, - addText: options.addText, - formCssClass: "dynamic-" + options.prefix, - deleteCssClass: "inline-deletelink", - deleteText: options.deleteText, - emptyCssClass: "empty-form", - removed: updateInlineLabel, - added: function(row) { - initPrepopulatedFields(row); - reinitDateTimeShortCuts(); - updateSelectFilter(); - updateInlineLabel(row); - }, - addButton: options.addButton - }); - - return $rows; - }; - - $(document).ready(function() { - $(".js-inline-admin-formset").each(function() { - var data = $(this).data(), - inlineOptions = data.inlineFormset, - selector; - switch(data.inlineType) { - case "stacked": - selector = inlineOptions.name + "-group .inline-related"; - $(selector).stackedFormset(selector, inlineOptions.options); - break; - case "tabular": - selector = inlineOptions.name + "-group .tabular.inline-related tbody:first > tr"; - $(selector).tabularFormset(selector, inlineOptions.options); - break; - } - }); - }); -})(django.jQuery); diff --git a/static/admin/js/inlines.3b3fb199b064.js.gz b/static/admin/js/inlines.3b3fb199b064.js.gz deleted file mode 100644 index 8204bd76..00000000 Binary files a/static/admin/js/inlines.3b3fb199b064.js.gz and /dev/null differ diff --git a/static/admin/js/urlify.4087f3e18796.js b/static/admin/js/urlify.4087f3e18796.js deleted file mode 100644 index 2d70dcd0..00000000 --- a/static/admin/js/urlify.4087f3e18796.js +++ /dev/null @@ -1,195 +0,0 @@ -/*global XRegExp*/ -(function() { - 'use strict'; - - var LATIN_MAP = { - 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', - 'Ç': 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', - 'Î': 'I', 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', - 'Õ': 'O', 'Ö': 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', - 'Ü': 'U', 'Ű': 'U', 'Ý': 'Y', 'Þ': 'TH', 'Ÿ': 'Y', 'ß': 'ss', 'à': 'a', - 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', - 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', - 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', - 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', - 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y' - }; - var LATIN_SYMBOLS_MAP = { - '©': '(c)' - }; - var GREEK_MAP = { - 'α': 'a', 'β': 'b', 'γ': 'g', 'δ': 'd', 'ε': 'e', 'ζ': 'z', 'η': 'h', - 'θ': '8', 'ι': 'i', 'κ': 'k', 'λ': 'l', 'μ': 'm', 'ν': 'n', 'ξ': '3', - 'ο': 'o', 'π': 'p', 'ρ': 'r', 'σ': 's', 'τ': 't', 'υ': 'y', 'φ': 'f', - 'χ': 'x', 'ψ': 'ps', 'ω': 'w', 'ά': 'a', 'έ': 'e', 'ί': 'i', 'ό': 'o', - 'ύ': 'y', 'ή': 'h', 'ώ': 'w', 'ς': 's', 'ϊ': 'i', 'ΰ': 'y', 'ϋ': 'y', - 'ΐ': 'i', 'Α': 'A', 'Β': 'B', 'Γ': 'G', 'Δ': 'D', 'Ε': 'E', 'Ζ': 'Z', - 'Η': 'H', 'Θ': '8', 'Ι': 'I', 'Κ': 'K', 'Λ': 'L', 'Μ': 'M', 'Ν': 'N', - 'Ξ': '3', 'Ο': 'O', 'Π': 'P', 'Ρ': 'R', 'Σ': 'S', 'Τ': 'T', 'Υ': 'Y', - 'Φ': 'F', 'Χ': 'X', 'Ψ': 'PS', 'Ω': 'W', 'Ά': 'A', 'Έ': 'E', 'Ί': 'I', - 'Ό': 'O', 'Ύ': 'Y', 'Ή': 'H', 'Ώ': 'W', 'Ϊ': 'I', 'Ϋ': 'Y' - }; - var TURKISH_MAP = { - 'ş': 's', 'Ş': 'S', 'ı': 'i', 'İ': 'I', 'ç': 'c', 'Ç': 'C', 'ü': 'u', - 'Ü': 'U', 'ö': 'o', 'Ö': 'O', 'ğ': 'g', 'Ğ': 'G' - }; - var ROMANIAN_MAP = { - 'ă': 'a', 'î': 'i', 'ș': 's', 'ț': 't', 'â': 'a', - 'Ă': 'A', 'Î': 'I', 'Ș': 'S', 'Ț': 'T', 'Â': 'A' - }; - var RUSSIAN_MAP = { - 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', - 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'j', 'к': 'k', 'л': 'l', 'м': 'm', - 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', - 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch', 'ш': 'sh', 'щ': 'sh', 'ъ': '', - 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya', - 'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'Yo', - 'Ж': 'Zh', 'З': 'Z', 'И': 'I', 'Й': 'J', 'К': 'K', 'Л': 'L', 'М': 'M', - 'Н': 'N', 'О': 'O', 'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T', 'У': 'U', - 'Ф': 'F', 'Х': 'H', 'Ц': 'C', 'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Sh', 'Ъ': '', - 'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya' - }; - var UKRAINIAN_MAP = { - 'Є': 'Ye', 'І': 'I', 'Ї': 'Yi', 'Ґ': 'G', 'є': 'ye', 'і': 'i', - 'ї': 'yi', 'ґ': 'g' - }; - var CZECH_MAP = { - 'č': 'c', 'ď': 'd', 'ě': 'e', 'ň': 'n', 'ř': 'r', 'š': 's', 'ť': 't', - 'ů': 'u', 'ž': 'z', 'Č': 'C', 'Ď': 'D', 'Ě': 'E', 'Ň': 'N', 'Ř': 'R', - 'Š': 'S', 'Ť': 'T', 'Ů': 'U', 'Ž': 'Z' - }; - var SLOVAK_MAP = { - 'á': 'a', 'ä': 'a', 'č': 'c', 'ď': 'd', 'é': 'e', 'í': 'i', 'ľ': 'l', - 'ĺ': 'l', 'ň': 'n', 'ó': 'o', 'ô': 'o', 'ŕ': 'r', 'š': 's', 'ť': 't', - 'ú': 'u', 'ý': 'y', 'ž': 'z', - 'Á': 'a', 'Ä': 'A', 'Č': 'C', 'Ď': 'D', 'É': 'E', 'Í': 'I', 'Ľ': 'L', - 'Ĺ': 'L', 'Ň': 'N', 'Ó': 'O', 'Ô': 'O', 'Ŕ': 'R', 'Š': 'S', 'Ť': 'T', - 'Ú': 'U', 'Ý': 'Y', 'Ž': 'Z' - }; - var POLISH_MAP = { - 'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n', 'ó': 'o', 'ś': 's', - 'ź': 'z', 'ż': 'z', - 'Ą': 'A', 'Ć': 'C', 'Ę': 'E', 'Ł': 'L', 'Ń': 'N', 'Ó': 'O', 'Ś': 'S', - 'Ź': 'Z', 'Ż': 'Z' - }; - var LATVIAN_MAP = { - 'ā': 'a', 'č': 'c', 'ē': 'e', 'ģ': 'g', 'ī': 'i', 'ķ': 'k', 'ļ': 'l', - 'ņ': 'n', 'š': 's', 'ū': 'u', 'ž': 'z', - 'Ā': 'A', 'Č': 'C', 'Ē': 'E', 'Ģ': 'G', 'Ī': 'I', 'Ķ': 'K', 'Ļ': 'L', - 'Ņ': 'N', 'Š': 'S', 'Ū': 'U', 'Ž': 'Z' - }; - var ARABIC_MAP = { - 'أ': 'a', 'ب': 'b', 'ت': 't', 'ث': 'th', 'ج': 'g', 'ح': 'h', 'خ': 'kh', 'د': 'd', - 'ذ': 'th', 'ر': 'r', 'ز': 'z', 'س': 's', 'ش': 'sh', 'ص': 's', 'ض': 'd', 'ط': 't', - 'ظ': 'th', 'ع': 'aa', 'غ': 'gh', 'ف': 'f', 'ق': 'k', 'ك': 'k', 'ل': 'l', 'م': 'm', - 'ن': 'n', 'ه': 'h', 'و': 'o', 'ي': 'y' - }; - var LITHUANIAN_MAP = { - 'ą': 'a', 'č': 'c', 'ę': 'e', 'ė': 'e', 'į': 'i', 'š': 's', 'ų': 'u', - 'ū': 'u', 'ž': 'z', - 'Ą': 'A', 'Č': 'C', 'Ę': 'E', 'Ė': 'E', 'Į': 'I', 'Š': 'S', 'Ų': 'U', - 'Ū': 'U', 'Ž': 'Z' - }; - var SERBIAN_MAP = { - 'ђ': 'dj', 'ј': 'j', 'љ': 'lj', 'њ': 'nj', 'ћ': 'c', 'џ': 'dz', - 'đ': 'dj', 'Ђ': 'Dj', 'Ј': 'j', 'Љ': 'Lj', 'Њ': 'Nj', 'Ћ': 'C', - 'Џ': 'Dz', 'Đ': 'Dj' - }; - var AZERBAIJANI_MAP = { - 'ç': 'c', 'ə': 'e', 'ğ': 'g', 'ı': 'i', 'ö': 'o', 'ş': 's', 'ü': 'u', - 'Ç': 'C', 'Ə': 'E', 'Ğ': 'G', 'İ': 'I', 'Ö': 'O', 'Ş': 'S', 'Ü': 'U' - }; - var GEORGIAN_MAP = { - 'ა': 'a', 'ბ': 'b', 'გ': 'g', 'დ': 'd', 'ე': 'e', 'ვ': 'v', 'ზ': 'z', - 'თ': 't', 'ი': 'i', 'კ': 'k', 'ლ': 'l', 'მ': 'm', 'ნ': 'n', 'ო': 'o', - 'პ': 'p', 'ჟ': 'j', 'რ': 'r', 'ს': 's', 'ტ': 't', 'უ': 'u', 'ფ': 'f', - 'ქ': 'q', 'ღ': 'g', 'ყ': 'y', 'შ': 'sh', 'ჩ': 'ch', 'ც': 'c', 'ძ': 'dz', - 'წ': 'w', 'ჭ': 'ch', 'ხ': 'x', 'ჯ': 'j', 'ჰ': 'h' - }; - - var ALL_DOWNCODE_MAPS = [ - LATIN_MAP, - LATIN_SYMBOLS_MAP, - GREEK_MAP, - TURKISH_MAP, - ROMANIAN_MAP, - RUSSIAN_MAP, - UKRAINIAN_MAP, - CZECH_MAP, - SLOVAK_MAP, - POLISH_MAP, - LATVIAN_MAP, - ARABIC_MAP, - LITHUANIAN_MAP, - SERBIAN_MAP, - AZERBAIJANI_MAP, - GEORGIAN_MAP - ]; - - var Downcoder = { - 'Initialize': function() { - if (Downcoder.map) { // already made - return; - } - Downcoder.map = {}; - Downcoder.chars = []; - for (var i = 0; i < ALL_DOWNCODE_MAPS.length; i++) { - var lookup = ALL_DOWNCODE_MAPS[i]; - for (var c in lookup) { - if (lookup.hasOwnProperty(c)) { - Downcoder.map[c] = lookup[c]; - } - } - } - for (var k in Downcoder.map) { - if (Downcoder.map.hasOwnProperty(k)) { - Downcoder.chars.push(k); - } - } - Downcoder.regex = new RegExp(Downcoder.chars.join('|'), 'g'); - } - }; - - function downcode(slug) { - Downcoder.Initialize(); - return slug.replace(Downcoder.regex, function(m) { - return Downcoder.map[m]; - }); - } - - - function URLify(s, num_chars, allowUnicode) { - // changes, e.g., "Petty theft" to "petty-theft" - // remove all these words from the string before urlifying - if (!allowUnicode) { - s = downcode(s); - } - var hasUnicodeChars = /[^\u0000-\u007f]/.test(s); - // Remove English words only if the string contains ASCII (English) - // characters. - if (!hasUnicodeChars) { - var removeList = [ - "a", "an", "as", "at", "before", "but", "by", "for", "from", - "is", "in", "into", "like", "of", "off", "on", "onto", "per", - "since", "than", "the", "this", "that", "to", "up", "via", - "with" - ]; - var r = new RegExp('\\b(' + removeList.join('|') + ')\\b', 'gi'); - s = s.replace(r, ''); - } - // if downcode doesn't hit, the char will be stripped here - if (allowUnicode) { - // Keep Unicode letters including both lowercase and uppercase - // characters, whitespace, and dash; remove other characters. - s = XRegExp.replace(s, XRegExp('[^-_\\p{L}\\p{N}\\s]', 'g'), ''); - } else { - s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars - } - s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces - s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens - s = s.substring(0, num_chars); // trim to first num_chars chars - s = s.replace(/-+$/g, ''); // trim any trailing hyphens - return s.toLowerCase(); // convert to lowercase - } - window.URLify = URLify; -})(); diff --git a/static/admin/js/urlify.4087f3e18796.js.gz b/static/admin/js/urlify.4087f3e18796.js.gz deleted file mode 100644 index 7720851b..00000000 Binary files a/static/admin/js/urlify.4087f3e18796.js.gz and /dev/null differ diff --git a/static/babybuddy/css/app.3349e431532d.css.gz b/static/babybuddy/css/app.3349e431532d.css.gz deleted file mode 100644 index e446bdd1..00000000 Binary files a/static/babybuddy/css/app.3349e431532d.css.gz and /dev/null differ diff --git a/static/babybuddy/css/app.16b7aa6d5a19.css b/static/babybuddy/css/app.73dfa23efbad.css similarity index 93% rename from static/babybuddy/css/app.16b7aa6d5a19.css rename to static/babybuddy/css/app.73dfa23efbad.css index aeb2bad8..39cf826a 100644 --- a/static/babybuddy/css/app.16b7aa6d5a19.css +++ b/static/babybuddy/css/app.73dfa23efbad.css @@ -21,7 +21,7 @@ border-color: inherit; } /*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Bootstrap v4.4.1 (https://getbootstrap.com/) * Copyright 2011-2019 The Bootstrap Authors * Copyright 2011-2019 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) @@ -82,7 +82,7 @@ body { text-align: left; background-color: #212529; } -[tabindex="-1"]:focus { +[tabindex="-1"]:focus:not(:focus-visible) { outline: 0 !important; } hr { @@ -161,14 +161,12 @@ a { color: #0e778d; text-decoration: underline; } -a:not([href]):not([tabindex]) { +a:not([href]) { color: inherit; text-decoration: none; } - a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { + a:not([href]):hover { color: inherit; text-decoration: none; } - a:not([href]):not([tabindex]):focus { - outline: 0; } pre, code, @@ -438,7 +436,7 @@ mark, code { font-size: 87.5%; color: #e83e8c; - word-break: break-word; } + word-wrap: break-word; } a > code { color: inherit; } @@ -485,13 +483,29 @@ pre { .container { max-width: 1140px; } } -.container-fluid { +.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; } } + +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; } } + +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; } } + +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; } } + .row { display: flex; flex-wrap: wrap; @@ -522,6 +536,30 @@ pre { flex-grow: 1; max-width: 100%; } +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; } + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; } + +.row-cols-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; } + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; } + +.row-cols-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-auto { flex: 0 0 auto; width: auto; @@ -658,6 +696,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-sm-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-sm-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-sm-auto { flex: 0 0 auto; width: auto; @@ -758,6 +814,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-md-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-md-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-md-auto { flex: 0 0 auto; width: auto; @@ -858,6 +932,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-lg-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-lg-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-lg-auto { flex: 0 0 auto; width: auto; @@ -958,6 +1050,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-xl-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-xl-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-xl-auto { flex: 0 0 auto; width: auto; @@ -1364,6 +1474,9 @@ pre { .form-control::-ms-expand { background-color: transparent; border: 0; } + .form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #000; } .form-control:focus { color: #495057; background-color: #fff; @@ -1408,9 +1521,9 @@ select.form-control:focus::-ms-value { .form-control-plaintext { display: block; width: 100%; - padding-top: 0.375rem; - padding-bottom: 0.375rem; + padding: 0.375rem 0; margin-bottom: 0; + font-size: 1rem; line-height: 1.5; color: #212529; background-color: transparent; @@ -1466,6 +1579,7 @@ textarea.form-control { position: absolute; margin-top: 0.3rem; margin-left: -1.25rem; } + .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { color: #6c757d; } @@ -1504,20 +1618,22 @@ textarea.form-control { background-color: rgba(40, 167, 69, 0.9); border-radius: 0.25rem; } +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; } + .was-validated .form-control:valid, .form-control.is-valid { border-color: #28a745; padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); + background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - .was-validated .form-control:valid ~ .valid-feedback, - .was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, - .form-control.is-valid ~ .valid-tooltip { - display: block; } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-right: calc(1.5em + 0.75rem); @@ -1525,20 +1641,11 @@ textarea.form-control { .was-validated .custom-select:valid, .custom-select.is-valid { border-color: #28a745; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - .was-validated .custom-select:valid ~ .valid-feedback, - .was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, - .custom-select.is-valid ~ .valid-tooltip { - display: block; } - -.was-validated .form-control-file:valid ~ .valid-feedback, -.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, -.form-control-file.is-valid ~ .valid-tooltip { - display: block; } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #28a745; } @@ -1553,11 +1660,6 @@ textarea.form-control { .was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { border-color: #28a745; } -.was-validated .custom-control-input:valid ~ .valid-feedback, -.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, -.custom-control-input.is-valid ~ .valid-tooltip { - display: block; } - .was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { border-color: #34ce57; background-color: #34ce57; } @@ -1571,11 +1673,6 @@ textarea.form-control { .was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { border-color: #28a745; } -.was-validated .custom-file-input:valid ~ .valid-feedback, -.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, -.custom-file-input.is-valid ~ .valid-tooltip { - display: block; } - .was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } @@ -1601,20 +1698,22 @@ textarea.form-control { background-color: rgba(220, 53, 69, 0.9); border-radius: 0.25rem; } +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; } + .was-validated .form-control:invalid, .form-control.is-invalid { border-color: #dc3545; padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); + background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } - .was-validated .form-control:invalid ~ .invalid-feedback, - .was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, - .form-control.is-invalid ~ .invalid-tooltip { - display: block; } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-right: calc(1.5em + 0.75rem); @@ -1622,20 +1721,11 @@ textarea.form-control { .was-validated .custom-select:invalid, .custom-select.is-invalid { border-color: #dc3545; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } - .was-validated .custom-select:invalid ~ .invalid-feedback, - .was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, - .custom-select.is-invalid ~ .invalid-tooltip { - display: block; } - -.was-validated .form-control-file:invalid ~ .invalid-feedback, -.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, -.form-control-file.is-invalid ~ .invalid-tooltip { - display: block; } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } @@ -1650,11 +1740,6 @@ textarea.form-control { .was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { border-color: #dc3545; } -.was-validated .custom-control-input:invalid ~ .invalid-feedback, -.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, -.custom-control-input.is-invalid ~ .invalid-tooltip { - display: block; } - .was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { border-color: #e4606d; background-color: #e4606d; } @@ -1668,11 +1753,6 @@ textarea.form-control { .was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { border-color: #dc3545; } -.was-validated .custom-file-input:invalid ~ .invalid-feedback, -.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, -.custom-file-input.is-invalid ~ .invalid-tooltip { - display: block; } - .was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } @@ -1728,6 +1808,7 @@ textarea.form-control { color: #ced4da; text-align: center; vertical-align: middle; + cursor: pointer; user-select: none; background-color: transparent; border: 1px solid transparent; @@ -1761,6 +1842,9 @@ fieldset:disabled a.btn { background-color: #1b5878; border-color: #19506d; } .btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #1b5878; + border-color: #19506d; box-shadow: 0 0 0 0.2rem rgba(67, 133, 167, 0.5); } .btn-primary.disabled, .btn-primary:disabled { color: #fff; @@ -1784,6 +1868,9 @@ fieldset:disabled a.btn { background-color: #d97a00; border-color: #cc7200; } .btn-secondary:focus, .btn-secondary.focus { + color: #fff; + background-color: #d97a00; + border-color: #cc7200; box-shadow: 0 0 0 0.2rem rgba(222, 127, 6, 0.5); } .btn-secondary.disabled, .btn-secondary:disabled { color: #212529; @@ -1807,6 +1894,9 @@ fieldset:disabled a.btn { background-color: #1c7644; border-color: #196c3e; } .btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #1c7644; + border-color: #196c3e; box-shadow: 0 0 0 0.2rem rgba(68, 165, 111, 0.5); } .btn-success.disabled, .btn-success:disabled { color: #fff; @@ -1830,6 +1920,9 @@ fieldset:disabled a.btn { background-color: #1295b0; border-color: #108ba5; } .btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } .btn-info.disabled, .btn-info:disabled { color: #fff; @@ -1853,6 +1946,9 @@ fieldset:disabled a.btn { background-color: #ffb11c; border-color: #ffac0f; } .btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #ffb11c; + border-color: #ffac0f; box-shadow: 0 0 0 0.2rem rgba(222, 167, 62, 0.5); } .btn-warning.disabled, .btn-warning:disabled { color: #212529; @@ -1876,6 +1972,9 @@ fieldset:disabled a.btn { background-color: #881d28; border-color: #7d1b25; } .btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } .btn-danger.disabled, .btn-danger:disabled { color: #fff; @@ -1899,6 +1998,9 @@ fieldset:disabled a.btn { background-color: #e2e6ea; border-color: #dae0e5; } .btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } .btn-light.disabled, .btn-light:disabled { color: #212529; @@ -1922,6 +2024,9 @@ fieldset:disabled a.btn { background-color: #23272b; border-color: #1d2124; } .btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } .btn-dark.disabled, .btn-dark:disabled { color: #fff; @@ -1945,6 +2050,9 @@ fieldset:disabled a.btn { background-color: #1295b0; border-color: #108ba5; } .btn-debug:focus, .btn-debug.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } .btn-debug.disabled, .btn-debug:disabled { color: #fff; @@ -1968,6 +2076,9 @@ fieldset:disabled a.btn { background-color: #881d28; border-color: #7d1b25; } .btn-error:focus, .btn-error.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } .btn-error.disabled, .btn-error:disabled { color: #fff; @@ -2549,8 +2660,8 @@ input[type="button"].btn-block { .input-group > .custom-select, .input-group > .custom-file { position: relative; - flex: 1 1 auto; - width: 1%; + flex: 1 1 0%; + min-width: 0; margin-bottom: 0; } .input-group > .form-control + .form-control, .input-group > .form-control + .custom-select, @@ -2698,7 +2809,10 @@ input[type="button"].btn-block { .custom-control-input { position: absolute; + left: 0; z-index: -1; + width: 1rem; + height: 1.25rem; opacity: 0; } .custom-control-input:checked ~ .custom-control-label::before { color: #fff; @@ -2712,9 +2826,9 @@ input[type="button"].btn-block { color: #fff; background-color: #b3d7ff; border-color: #b3d7ff; } - .custom-control-input:disabled ~ .custom-control-label { + .custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { color: #6c757d; } - .custom-control-input:disabled ~ .custom-control-label::before { + .custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { background-color: #e9ecef; } .custom-control-label { @@ -2746,14 +2860,14 @@ input[type="button"].btn-block { border-radius: 0.25rem; } .custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { border-color: #007bff; background-color: #007bff; } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } .custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } @@ -2765,7 +2879,7 @@ input[type="button"].btn-block { border-radius: 50%; } .custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } @@ -2804,8 +2918,7 @@ input[type="button"].btn-block { line-height: 1.5; color: #495057; vertical-align: middle; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; - background-color: #fff; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; border: 1px solid #ced4da; border-radius: 0.25rem; appearance: none; } @@ -2825,6 +2938,9 @@ input[type="button"].btn-block { background-color: #e9ecef; } .custom-select::-ms-expand { display: none; } + .custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; } .custom-select-sm { height: calc(1.5em + 0.5rem + 2px); @@ -2857,6 +2973,7 @@ input[type="button"].btn-block { .custom-file-input:focus ~ .custom-file-label { border-color: #80bdff; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-file-input[disabled] ~ .custom-file-label, .custom-file-input:disabled ~ .custom-file-label { background-color: #e9ecef; } .custom-file-input:lang(en) ~ .custom-file-label::after { @@ -2896,7 +3013,7 @@ input[type="button"].btn-block { .custom-range { width: 100%; - height: calc(1rem + 0.4rem); + height: 1.4rem; padding: 0; background-color: transparent; appearance: none; } @@ -3076,8 +3193,8 @@ input[type="button"].btn-block { align-items: center; justify-content: space-between; padding: 0.5rem 1rem; } - .navbar > .container, - .navbar > .container-fluid { + .navbar .container, + .navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl { display: flex; flex-wrap: wrap; align-items: center; @@ -3138,7 +3255,7 @@ input[type="button"].btn-block { @media (max-width: 575.98px) { .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3154,7 +3271,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { flex-wrap: nowrap; } .navbar-expand-sm .navbar-collapse { display: flex !important; @@ -3164,7 +3281,7 @@ input[type="button"].btn-block { @media (max-width: 767.98px) { .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3180,7 +3297,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { flex-wrap: nowrap; } .navbar-expand-md .navbar-collapse { display: flex !important; @@ -3190,7 +3307,7 @@ input[type="button"].btn-block { @media (max-width: 991.98px) { .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3206,7 +3323,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { flex-wrap: nowrap; } .navbar-expand-lg .navbar-collapse { display: flex !important; @@ -3216,7 +3333,7 @@ input[type="button"].btn-block { @media (max-width: 1199.98px) { .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3232,7 +3349,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { flex-wrap: nowrap; } .navbar-expand-xl .navbar-collapse { display: flex !important; @@ -3244,7 +3361,7 @@ input[type="button"].btn-block { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand > .container, - .navbar-expand > .container-fluid { + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { padding-right: 0; padding-left: 0; } .navbar-expand .navbar-nav { @@ -3255,7 +3372,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand > .container, - .navbar-expand > .container-fluid { + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { flex-wrap: nowrap; } .navbar-expand .navbar-collapse { display: flex !important; @@ -3286,7 +3403,7 @@ input[type="button"].btn-block { border-color: rgba(0, 0, 0, 0.1); } .navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-light .navbar-text { color: rgba(0, 0, 0, 0.5); } @@ -3318,7 +3435,7 @@ input[type="button"].btn-block { border-color: rgba(255, 255, 255, 0.1); } .navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-dark .navbar-text { color: rgba(255, 255, 255, 0.5); } @@ -3349,6 +3466,7 @@ input[type="button"].btn-block { .card-body { flex: 1 1 auto; + min-height: 1px; padding: 1.25rem; } .card-title { @@ -3402,70 +3520,68 @@ input[type="button"].btn-block { left: 0; padding: 1.25rem; } -.card-img { - width: 100%; - border-radius: calc(0.25rem - 1px); } +.card-img, +.card-img-top, +.card-img-bottom { + flex-shrink: 0; + width: 100%; } +.card-img, .card-img-top { - width: 100%; border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } +.card-img, .card-img-bottom { - width: 100%; border-bottom-right-radius: calc(0.25rem - 1px); border-bottom-left-radius: calc(0.25rem - 1px); } -.card-deck { - display: flex; - flex-direction: column; } - .card-deck .card { - margin-bottom: 15px; } - @media (min-width: 576px) { - .card-deck { - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; } - .card-deck .card { - display: flex; - flex: 1 0 0%; - flex-direction: column; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; } } +.card-deck .card { + margin-bottom: 15px; } -.card-group { - display: flex; - flex-direction: column; } - .card-group > .card { - margin-bottom: 15px; } - @media (min-width: 576px) { - .card-group { - flex-flow: row wrap; } - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0; } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; +@media (min-width: 576px) { + .card-deck { + display: flex; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; } + .card-deck .card { + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; } } + +.card-group > .card { + margin-bottom: 15px; } + +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { border-bottom-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; } } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; } } .card-columns .card { margin-bottom: 0.75rem; } @@ -3482,19 +3598,15 @@ input[type="button"].btn-block { .accordion > .card { overflow: hidden; } - .accordion > .card:not(:first-of-type) .card-header:first-child { - border-radius: 0; } - .accordion > .card:not(:first-of-type):not(:last-of-type) { - border-bottom: 0; - border-radius: 0; } - .accordion > .card:first-of-type { + .accordion > .card:not(:last-of-type) { border-bottom: 0; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } - .accordion > .card:last-of-type { + .accordion > .card:not(:first-of-type) { border-top-left-radius: 0; border-top-right-radius: 0; } - .accordion > .card .card-header { + .accordion > .card > .card-header { + border-radius: 0; margin-bottom: -1px; } .breadcrumb { @@ -3545,7 +3657,7 @@ input[type="button"].btn-block { background-color: #212529; border-color: #343a40; } .page-link:focus { - z-index: 2; + z-index: 3; outline: 0; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } @@ -3559,7 +3671,7 @@ input[type="button"].btn-block { border-bottom-right-radius: 0.25rem; } .page-item.active .page-link { - z-index: 1; + z-index: 3; color: #ced4da; background-color: #226f97; border-color: #226f97; } @@ -3869,6 +3981,7 @@ input[type="button"].btn-block { display: flex; flex-direction: column; justify-content: center; + overflow: hidden; color: #fff; text-align: center; white-space: nowrap; @@ -3918,14 +4031,12 @@ input[type="button"].btn-block { position: relative; display: block; padding: 0.75rem 1.25rem; - margin-bottom: -1px; background-color: #343a40; border: 1px solid rgba(0, 0, 0, 0.125); } .list-group-item:first-child { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .list-group-item:last-child { - margin-bottom: 0; border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .list-group-item.disabled, .list-group-item:disabled { @@ -3937,99 +4048,110 @@ input[type="button"].btn-block { color: #fff; background-color: #007bff; border-color: #007bff; } + .list-group-item + .list-group-item { + border-top-width: 0; } + .list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; } .list-group-horizontal { flex-direction: row; } - .list-group-horizontal .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } + .list-group-horizontal .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal .list-group-item.active { + margin-top: 0; } + .list-group-horizontal .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } @media (min-width: 576px) { .list-group-horizontal-sm { flex-direction: row; } - .list-group-horizontal-sm .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-sm .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-sm .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-sm .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-sm .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-sm .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-sm .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-sm .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 768px) { .list-group-horizontal-md { flex-direction: row; } - .list-group-horizontal-md .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-md .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-md .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-md .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-md .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-md .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-md .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-md .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 992px) { .list-group-horizontal-lg { flex-direction: row; } - .list-group-horizontal-lg .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-lg .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-lg .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-lg .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-lg .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-lg .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-lg .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-lg .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 1200px) { .list-group-horizontal-xl { flex-direction: row; } - .list-group-horizontal-xl .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-xl .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-xl .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-xl .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-xl .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-xl .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-xl .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-xl .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } .list-group-flush .list-group-item { - border-right: 0; - border-left: 0; + border-right-width: 0; + border-left-width: 0; border-radius: 0; } - .list-group-flush .list-group-item:last-child { - margin-bottom: -1px; } - -.list-group-flush:first-child .list-group-item:first-child { - border-top: 0; } + .list-group-flush .list-group-item:first-child { + border-top-width: 0; } .list-group-flush:last-child .list-group-item:last-child { - margin-bottom: 0; - border-bottom: 0; } + border-bottom-width: 0; } .list-group-item-primary { color: #123a4f; @@ -4227,6 +4349,8 @@ a.close.disabled { transition: none; } } .modal.show .modal-dialog { transform: none; } + .modal.modal-static .modal-dialog { + transform: scale(1.02); } .modal-dialog-scrollable { display: flex; @@ -4288,8 +4412,8 @@ a.close.disabled { justify-content: space-between; padding: 1rem 1rem; border-bottom: 1px solid #dee2e6; - border-top-left-radius: 0.3rem; - border-top-right-radius: 0.3rem; } + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } .modal-header .close { padding: 1rem 1rem; margin: -1rem -1rem -1rem auto; } @@ -4305,16 +4429,15 @@ a.close.disabled { .modal-footer { display: flex; + flex-wrap: wrap; align-items: center; justify-content: flex-end; - padding: 1rem; + padding: 0.75rem; border-top: 1px solid #dee2e6; - border-bottom-right-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; } - .modal-footer > :not(:first-child) { - margin-left: .25rem; } - .modal-footer > :not(:last-child) { - margin-right: .25rem; } + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); } + .modal-footer > * { + margin: 0.25rem; } .modal-scrollbar-measure { position: absolute; @@ -4473,7 +4596,7 @@ a.close.disabled { .bs-popover-top, .bs-popover-auto[x-placement^="top"] { margin-bottom: 0.5rem; } .bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { - bottom: calc((0.5rem + 1px) * -1); } + bottom: calc(-0.5rem - 1px); } .bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { bottom: 0; border-width: 0.5rem 0.5rem 0; @@ -4486,7 +4609,7 @@ a.close.disabled { .bs-popover-right, .bs-popover-auto[x-placement^="right"] { margin-left: 0.5rem; } .bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { - left: calc((0.5rem + 1px) * -1); + left: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } @@ -4502,7 +4625,7 @@ a.close.disabled { .bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { margin-top: 0.5rem; } .bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { - top: calc((0.5rem + 1px) * -1); } + top: calc(-0.5rem - 1px); } .bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { top: 0; border-width: 0 0.5rem 0.5rem 0.5rem; @@ -4524,7 +4647,7 @@ a.close.disabled { .bs-popover-left, .bs-popover-auto[x-placement^="left"] { margin-right: 0.5rem; } .bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { - right: calc((0.5rem + 1px) * -1); + right: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } @@ -4607,7 +4730,7 @@ a.close.disabled { .carousel-fade .active.carousel-item-right { z-index: 0; opacity: 0; - transition: 0s 0.6s opacity; } + transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { @@ -4653,10 +4776,10 @@ a.close.disabled { background: no-repeat 50% / 100% 100%; } .carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); } .carousel-indicators { position: absolute; @@ -5601,6 +5724,7 @@ button.bg-error:focus { width: 1px; height: 1px; padding: 0; + margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; diff --git a/static/babybuddy/css/app.3349e431532d.css b/static/babybuddy/css/app.b562c9b5953b.css similarity index 93% rename from static/babybuddy/css/app.3349e431532d.css rename to static/babybuddy/css/app.b562c9b5953b.css index aeb2bad8..39cf826a 100644 --- a/static/babybuddy/css/app.3349e431532d.css +++ b/static/babybuddy/css/app.b562c9b5953b.css @@ -21,7 +21,7 @@ border-color: inherit; } /*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Bootstrap v4.4.1 (https://getbootstrap.com/) * Copyright 2011-2019 The Bootstrap Authors * Copyright 2011-2019 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) @@ -82,7 +82,7 @@ body { text-align: left; background-color: #212529; } -[tabindex="-1"]:focus { +[tabindex="-1"]:focus:not(:focus-visible) { outline: 0 !important; } hr { @@ -161,14 +161,12 @@ a { color: #0e778d; text-decoration: underline; } -a:not([href]):not([tabindex]) { +a:not([href]) { color: inherit; text-decoration: none; } - a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { + a:not([href]):hover { color: inherit; text-decoration: none; } - a:not([href]):not([tabindex]):focus { - outline: 0; } pre, code, @@ -438,7 +436,7 @@ mark, code { font-size: 87.5%; color: #e83e8c; - word-break: break-word; } + word-wrap: break-word; } a > code { color: inherit; } @@ -485,13 +483,29 @@ pre { .container { max-width: 1140px; } } -.container-fluid { +.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; } } + +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; } } + +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; } } + +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; } } + .row { display: flex; flex-wrap: wrap; @@ -522,6 +536,30 @@ pre { flex-grow: 1; max-width: 100%; } +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; } + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; } + +.row-cols-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; } + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; } + +.row-cols-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-auto { flex: 0 0 auto; width: auto; @@ -658,6 +696,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-sm-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-sm-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-sm-auto { flex: 0 0 auto; width: auto; @@ -758,6 +814,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-md-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-md-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-md-auto { flex: 0 0 auto; width: auto; @@ -858,6 +932,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-lg-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-lg-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-lg-auto { flex: 0 0 auto; width: auto; @@ -958,6 +1050,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-xl-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-xl-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-xl-auto { flex: 0 0 auto; width: auto; @@ -1364,6 +1474,9 @@ pre { .form-control::-ms-expand { background-color: transparent; border: 0; } + .form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #000; } .form-control:focus { color: #495057; background-color: #fff; @@ -1408,9 +1521,9 @@ select.form-control:focus::-ms-value { .form-control-plaintext { display: block; width: 100%; - padding-top: 0.375rem; - padding-bottom: 0.375rem; + padding: 0.375rem 0; margin-bottom: 0; + font-size: 1rem; line-height: 1.5; color: #212529; background-color: transparent; @@ -1466,6 +1579,7 @@ textarea.form-control { position: absolute; margin-top: 0.3rem; margin-left: -1.25rem; } + .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { color: #6c757d; } @@ -1504,20 +1618,22 @@ textarea.form-control { background-color: rgba(40, 167, 69, 0.9); border-radius: 0.25rem; } +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; } + .was-validated .form-control:valid, .form-control.is-valid { border-color: #28a745; padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); + background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - .was-validated .form-control:valid ~ .valid-feedback, - .was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, - .form-control.is-valid ~ .valid-tooltip { - display: block; } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-right: calc(1.5em + 0.75rem); @@ -1525,20 +1641,11 @@ textarea.form-control { .was-validated .custom-select:valid, .custom-select.is-valid { border-color: #28a745; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - .was-validated .custom-select:valid ~ .valid-feedback, - .was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, - .custom-select.is-valid ~ .valid-tooltip { - display: block; } - -.was-validated .form-control-file:valid ~ .valid-feedback, -.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, -.form-control-file.is-valid ~ .valid-tooltip { - display: block; } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #28a745; } @@ -1553,11 +1660,6 @@ textarea.form-control { .was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { border-color: #28a745; } -.was-validated .custom-control-input:valid ~ .valid-feedback, -.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, -.custom-control-input.is-valid ~ .valid-tooltip { - display: block; } - .was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { border-color: #34ce57; background-color: #34ce57; } @@ -1571,11 +1673,6 @@ textarea.form-control { .was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { border-color: #28a745; } -.was-validated .custom-file-input:valid ~ .valid-feedback, -.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, -.custom-file-input.is-valid ~ .valid-tooltip { - display: block; } - .was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } @@ -1601,20 +1698,22 @@ textarea.form-control { background-color: rgba(220, 53, 69, 0.9); border-radius: 0.25rem; } +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; } + .was-validated .form-control:invalid, .form-control.is-invalid { border-color: #dc3545; padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); + background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } - .was-validated .form-control:invalid ~ .invalid-feedback, - .was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, - .form-control.is-invalid ~ .invalid-tooltip { - display: block; } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-right: calc(1.5em + 0.75rem); @@ -1622,20 +1721,11 @@ textarea.form-control { .was-validated .custom-select:invalid, .custom-select.is-invalid { border-color: #dc3545; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } - .was-validated .custom-select:invalid ~ .invalid-feedback, - .was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, - .custom-select.is-invalid ~ .invalid-tooltip { - display: block; } - -.was-validated .form-control-file:invalid ~ .invalid-feedback, -.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, -.form-control-file.is-invalid ~ .invalid-tooltip { - display: block; } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } @@ -1650,11 +1740,6 @@ textarea.form-control { .was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { border-color: #dc3545; } -.was-validated .custom-control-input:invalid ~ .invalid-feedback, -.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, -.custom-control-input.is-invalid ~ .invalid-tooltip { - display: block; } - .was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { border-color: #e4606d; background-color: #e4606d; } @@ -1668,11 +1753,6 @@ textarea.form-control { .was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { border-color: #dc3545; } -.was-validated .custom-file-input:invalid ~ .invalid-feedback, -.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, -.custom-file-input.is-invalid ~ .invalid-tooltip { - display: block; } - .was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } @@ -1728,6 +1808,7 @@ textarea.form-control { color: #ced4da; text-align: center; vertical-align: middle; + cursor: pointer; user-select: none; background-color: transparent; border: 1px solid transparent; @@ -1761,6 +1842,9 @@ fieldset:disabled a.btn { background-color: #1b5878; border-color: #19506d; } .btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #1b5878; + border-color: #19506d; box-shadow: 0 0 0 0.2rem rgba(67, 133, 167, 0.5); } .btn-primary.disabled, .btn-primary:disabled { color: #fff; @@ -1784,6 +1868,9 @@ fieldset:disabled a.btn { background-color: #d97a00; border-color: #cc7200; } .btn-secondary:focus, .btn-secondary.focus { + color: #fff; + background-color: #d97a00; + border-color: #cc7200; box-shadow: 0 0 0 0.2rem rgba(222, 127, 6, 0.5); } .btn-secondary.disabled, .btn-secondary:disabled { color: #212529; @@ -1807,6 +1894,9 @@ fieldset:disabled a.btn { background-color: #1c7644; border-color: #196c3e; } .btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #1c7644; + border-color: #196c3e; box-shadow: 0 0 0 0.2rem rgba(68, 165, 111, 0.5); } .btn-success.disabled, .btn-success:disabled { color: #fff; @@ -1830,6 +1920,9 @@ fieldset:disabled a.btn { background-color: #1295b0; border-color: #108ba5; } .btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } .btn-info.disabled, .btn-info:disabled { color: #fff; @@ -1853,6 +1946,9 @@ fieldset:disabled a.btn { background-color: #ffb11c; border-color: #ffac0f; } .btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #ffb11c; + border-color: #ffac0f; box-shadow: 0 0 0 0.2rem rgba(222, 167, 62, 0.5); } .btn-warning.disabled, .btn-warning:disabled { color: #212529; @@ -1876,6 +1972,9 @@ fieldset:disabled a.btn { background-color: #881d28; border-color: #7d1b25; } .btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } .btn-danger.disabled, .btn-danger:disabled { color: #fff; @@ -1899,6 +1998,9 @@ fieldset:disabled a.btn { background-color: #e2e6ea; border-color: #dae0e5; } .btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } .btn-light.disabled, .btn-light:disabled { color: #212529; @@ -1922,6 +2024,9 @@ fieldset:disabled a.btn { background-color: #23272b; border-color: #1d2124; } .btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } .btn-dark.disabled, .btn-dark:disabled { color: #fff; @@ -1945,6 +2050,9 @@ fieldset:disabled a.btn { background-color: #1295b0; border-color: #108ba5; } .btn-debug:focus, .btn-debug.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } .btn-debug.disabled, .btn-debug:disabled { color: #fff; @@ -1968,6 +2076,9 @@ fieldset:disabled a.btn { background-color: #881d28; border-color: #7d1b25; } .btn-error:focus, .btn-error.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } .btn-error.disabled, .btn-error:disabled { color: #fff; @@ -2549,8 +2660,8 @@ input[type="button"].btn-block { .input-group > .custom-select, .input-group > .custom-file { position: relative; - flex: 1 1 auto; - width: 1%; + flex: 1 1 0%; + min-width: 0; margin-bottom: 0; } .input-group > .form-control + .form-control, .input-group > .form-control + .custom-select, @@ -2698,7 +2809,10 @@ input[type="button"].btn-block { .custom-control-input { position: absolute; + left: 0; z-index: -1; + width: 1rem; + height: 1.25rem; opacity: 0; } .custom-control-input:checked ~ .custom-control-label::before { color: #fff; @@ -2712,9 +2826,9 @@ input[type="button"].btn-block { color: #fff; background-color: #b3d7ff; border-color: #b3d7ff; } - .custom-control-input:disabled ~ .custom-control-label { + .custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { color: #6c757d; } - .custom-control-input:disabled ~ .custom-control-label::before { + .custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { background-color: #e9ecef; } .custom-control-label { @@ -2746,14 +2860,14 @@ input[type="button"].btn-block { border-radius: 0.25rem; } .custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { border-color: #007bff; background-color: #007bff; } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } .custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } @@ -2765,7 +2879,7 @@ input[type="button"].btn-block { border-radius: 50%; } .custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } @@ -2804,8 +2918,7 @@ input[type="button"].btn-block { line-height: 1.5; color: #495057; vertical-align: middle; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; - background-color: #fff; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; border: 1px solid #ced4da; border-radius: 0.25rem; appearance: none; } @@ -2825,6 +2938,9 @@ input[type="button"].btn-block { background-color: #e9ecef; } .custom-select::-ms-expand { display: none; } + .custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; } .custom-select-sm { height: calc(1.5em + 0.5rem + 2px); @@ -2857,6 +2973,7 @@ input[type="button"].btn-block { .custom-file-input:focus ~ .custom-file-label { border-color: #80bdff; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-file-input[disabled] ~ .custom-file-label, .custom-file-input:disabled ~ .custom-file-label { background-color: #e9ecef; } .custom-file-input:lang(en) ~ .custom-file-label::after { @@ -2896,7 +3013,7 @@ input[type="button"].btn-block { .custom-range { width: 100%; - height: calc(1rem + 0.4rem); + height: 1.4rem; padding: 0; background-color: transparent; appearance: none; } @@ -3076,8 +3193,8 @@ input[type="button"].btn-block { align-items: center; justify-content: space-between; padding: 0.5rem 1rem; } - .navbar > .container, - .navbar > .container-fluid { + .navbar .container, + .navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl { display: flex; flex-wrap: wrap; align-items: center; @@ -3138,7 +3255,7 @@ input[type="button"].btn-block { @media (max-width: 575.98px) { .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3154,7 +3271,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { flex-wrap: nowrap; } .navbar-expand-sm .navbar-collapse { display: flex !important; @@ -3164,7 +3281,7 @@ input[type="button"].btn-block { @media (max-width: 767.98px) { .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3180,7 +3297,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { flex-wrap: nowrap; } .navbar-expand-md .navbar-collapse { display: flex !important; @@ -3190,7 +3307,7 @@ input[type="button"].btn-block { @media (max-width: 991.98px) { .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3206,7 +3323,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { flex-wrap: nowrap; } .navbar-expand-lg .navbar-collapse { display: flex !important; @@ -3216,7 +3333,7 @@ input[type="button"].btn-block { @media (max-width: 1199.98px) { .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3232,7 +3349,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { flex-wrap: nowrap; } .navbar-expand-xl .navbar-collapse { display: flex !important; @@ -3244,7 +3361,7 @@ input[type="button"].btn-block { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand > .container, - .navbar-expand > .container-fluid { + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { padding-right: 0; padding-left: 0; } .navbar-expand .navbar-nav { @@ -3255,7 +3372,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand > .container, - .navbar-expand > .container-fluid { + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { flex-wrap: nowrap; } .navbar-expand .navbar-collapse { display: flex !important; @@ -3286,7 +3403,7 @@ input[type="button"].btn-block { border-color: rgba(0, 0, 0, 0.1); } .navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-light .navbar-text { color: rgba(0, 0, 0, 0.5); } @@ -3318,7 +3435,7 @@ input[type="button"].btn-block { border-color: rgba(255, 255, 255, 0.1); } .navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-dark .navbar-text { color: rgba(255, 255, 255, 0.5); } @@ -3349,6 +3466,7 @@ input[type="button"].btn-block { .card-body { flex: 1 1 auto; + min-height: 1px; padding: 1.25rem; } .card-title { @@ -3402,70 +3520,68 @@ input[type="button"].btn-block { left: 0; padding: 1.25rem; } -.card-img { - width: 100%; - border-radius: calc(0.25rem - 1px); } +.card-img, +.card-img-top, +.card-img-bottom { + flex-shrink: 0; + width: 100%; } +.card-img, .card-img-top { - width: 100%; border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } +.card-img, .card-img-bottom { - width: 100%; border-bottom-right-radius: calc(0.25rem - 1px); border-bottom-left-radius: calc(0.25rem - 1px); } -.card-deck { - display: flex; - flex-direction: column; } - .card-deck .card { - margin-bottom: 15px; } - @media (min-width: 576px) { - .card-deck { - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; } - .card-deck .card { - display: flex; - flex: 1 0 0%; - flex-direction: column; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; } } +.card-deck .card { + margin-bottom: 15px; } -.card-group { - display: flex; - flex-direction: column; } - .card-group > .card { - margin-bottom: 15px; } - @media (min-width: 576px) { - .card-group { - flex-flow: row wrap; } - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0; } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; +@media (min-width: 576px) { + .card-deck { + display: flex; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; } + .card-deck .card { + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; } } + +.card-group > .card { + margin-bottom: 15px; } + +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { border-bottom-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; } } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; } } .card-columns .card { margin-bottom: 0.75rem; } @@ -3482,19 +3598,15 @@ input[type="button"].btn-block { .accordion > .card { overflow: hidden; } - .accordion > .card:not(:first-of-type) .card-header:first-child { - border-radius: 0; } - .accordion > .card:not(:first-of-type):not(:last-of-type) { - border-bottom: 0; - border-radius: 0; } - .accordion > .card:first-of-type { + .accordion > .card:not(:last-of-type) { border-bottom: 0; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } - .accordion > .card:last-of-type { + .accordion > .card:not(:first-of-type) { border-top-left-radius: 0; border-top-right-radius: 0; } - .accordion > .card .card-header { + .accordion > .card > .card-header { + border-radius: 0; margin-bottom: -1px; } .breadcrumb { @@ -3545,7 +3657,7 @@ input[type="button"].btn-block { background-color: #212529; border-color: #343a40; } .page-link:focus { - z-index: 2; + z-index: 3; outline: 0; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } @@ -3559,7 +3671,7 @@ input[type="button"].btn-block { border-bottom-right-radius: 0.25rem; } .page-item.active .page-link { - z-index: 1; + z-index: 3; color: #ced4da; background-color: #226f97; border-color: #226f97; } @@ -3869,6 +3981,7 @@ input[type="button"].btn-block { display: flex; flex-direction: column; justify-content: center; + overflow: hidden; color: #fff; text-align: center; white-space: nowrap; @@ -3918,14 +4031,12 @@ input[type="button"].btn-block { position: relative; display: block; padding: 0.75rem 1.25rem; - margin-bottom: -1px; background-color: #343a40; border: 1px solid rgba(0, 0, 0, 0.125); } .list-group-item:first-child { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .list-group-item:last-child { - margin-bottom: 0; border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .list-group-item.disabled, .list-group-item:disabled { @@ -3937,99 +4048,110 @@ input[type="button"].btn-block { color: #fff; background-color: #007bff; border-color: #007bff; } + .list-group-item + .list-group-item { + border-top-width: 0; } + .list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; } .list-group-horizontal { flex-direction: row; } - .list-group-horizontal .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } + .list-group-horizontal .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal .list-group-item.active { + margin-top: 0; } + .list-group-horizontal .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } @media (min-width: 576px) { .list-group-horizontal-sm { flex-direction: row; } - .list-group-horizontal-sm .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-sm .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-sm .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-sm .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-sm .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-sm .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-sm .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-sm .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 768px) { .list-group-horizontal-md { flex-direction: row; } - .list-group-horizontal-md .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-md .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-md .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-md .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-md .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-md .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-md .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-md .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 992px) { .list-group-horizontal-lg { flex-direction: row; } - .list-group-horizontal-lg .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-lg .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-lg .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-lg .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-lg .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-lg .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-lg .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-lg .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 1200px) { .list-group-horizontal-xl { flex-direction: row; } - .list-group-horizontal-xl .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-xl .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-xl .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-xl .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-xl .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-xl .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-xl .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-xl .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } .list-group-flush .list-group-item { - border-right: 0; - border-left: 0; + border-right-width: 0; + border-left-width: 0; border-radius: 0; } - .list-group-flush .list-group-item:last-child { - margin-bottom: -1px; } - -.list-group-flush:first-child .list-group-item:first-child { - border-top: 0; } + .list-group-flush .list-group-item:first-child { + border-top-width: 0; } .list-group-flush:last-child .list-group-item:last-child { - margin-bottom: 0; - border-bottom: 0; } + border-bottom-width: 0; } .list-group-item-primary { color: #123a4f; @@ -4227,6 +4349,8 @@ a.close.disabled { transition: none; } } .modal.show .modal-dialog { transform: none; } + .modal.modal-static .modal-dialog { + transform: scale(1.02); } .modal-dialog-scrollable { display: flex; @@ -4288,8 +4412,8 @@ a.close.disabled { justify-content: space-between; padding: 1rem 1rem; border-bottom: 1px solid #dee2e6; - border-top-left-radius: 0.3rem; - border-top-right-radius: 0.3rem; } + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } .modal-header .close { padding: 1rem 1rem; margin: -1rem -1rem -1rem auto; } @@ -4305,16 +4429,15 @@ a.close.disabled { .modal-footer { display: flex; + flex-wrap: wrap; align-items: center; justify-content: flex-end; - padding: 1rem; + padding: 0.75rem; border-top: 1px solid #dee2e6; - border-bottom-right-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; } - .modal-footer > :not(:first-child) { - margin-left: .25rem; } - .modal-footer > :not(:last-child) { - margin-right: .25rem; } + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); } + .modal-footer > * { + margin: 0.25rem; } .modal-scrollbar-measure { position: absolute; @@ -4473,7 +4596,7 @@ a.close.disabled { .bs-popover-top, .bs-popover-auto[x-placement^="top"] { margin-bottom: 0.5rem; } .bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { - bottom: calc((0.5rem + 1px) * -1); } + bottom: calc(-0.5rem - 1px); } .bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { bottom: 0; border-width: 0.5rem 0.5rem 0; @@ -4486,7 +4609,7 @@ a.close.disabled { .bs-popover-right, .bs-popover-auto[x-placement^="right"] { margin-left: 0.5rem; } .bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { - left: calc((0.5rem + 1px) * -1); + left: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } @@ -4502,7 +4625,7 @@ a.close.disabled { .bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { margin-top: 0.5rem; } .bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { - top: calc((0.5rem + 1px) * -1); } + top: calc(-0.5rem - 1px); } .bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { top: 0; border-width: 0 0.5rem 0.5rem 0.5rem; @@ -4524,7 +4647,7 @@ a.close.disabled { .bs-popover-left, .bs-popover-auto[x-placement^="left"] { margin-right: 0.5rem; } .bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { - right: calc((0.5rem + 1px) * -1); + right: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } @@ -4607,7 +4730,7 @@ a.close.disabled { .carousel-fade .active.carousel-item-right { z-index: 0; opacity: 0; - transition: 0s 0.6s opacity; } + transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { @@ -4653,10 +4776,10 @@ a.close.disabled { background: no-repeat 50% / 100% 100%; } .carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); } .carousel-indicators { position: absolute; @@ -5601,6 +5724,7 @@ button.bg-error:focus { width: 1px; height: 1px; padding: 0; + margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; diff --git a/static/babybuddy/css/app.b562c9b5953b.css.gz b/static/babybuddy/css/app.b562c9b5953b.css.gz new file mode 100644 index 00000000..a5aaa775 Binary files /dev/null and b/static/babybuddy/css/app.b562c9b5953b.css.gz differ diff --git a/static/babybuddy/css/app.css b/static/babybuddy/css/app.css index 8c64dc65..0c568773 100644 --- a/static/babybuddy/css/app.css +++ b/static/babybuddy/css/app.css @@ -21,7 +21,7 @@ border-color: inherit; } /*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Bootstrap v4.4.1 (https://getbootstrap.com/) * Copyright 2011-2019 The Bootstrap Authors * Copyright 2011-2019 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) @@ -82,7 +82,7 @@ body { text-align: left; background-color: #212529; } -[tabindex="-1"]:focus { +[tabindex="-1"]:focus:not(:focus-visible) { outline: 0 !important; } hr { @@ -161,14 +161,12 @@ a { color: #0e778d; text-decoration: underline; } -a:not([href]):not([tabindex]) { +a:not([href]) { color: inherit; text-decoration: none; } - a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { + a:not([href]):hover { color: inherit; text-decoration: none; } - a:not([href]):not([tabindex]):focus { - outline: 0; } pre, code, @@ -438,7 +436,7 @@ mark, code { font-size: 87.5%; color: #e83e8c; - word-break: break-word; } + word-wrap: break-word; } a > code { color: inherit; } @@ -485,13 +483,29 @@ pre { .container { max-width: 1140px; } } -.container-fluid { +.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; } } + +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; } } + +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; } } + +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; } } + .row { display: flex; flex-wrap: wrap; @@ -522,6 +536,30 @@ pre { flex-grow: 1; max-width: 100%; } +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; } + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; } + +.row-cols-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; } + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; } + +.row-cols-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } + .col-auto { flex: 0 0 auto; width: auto; @@ -658,6 +696,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-sm-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-sm-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-sm-auto { flex: 0 0 auto; width: auto; @@ -758,6 +814,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-md-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-md-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-md-auto { flex: 0 0 auto; width: auto; @@ -858,6 +932,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-lg-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-lg-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-lg-auto { flex: 0 0 auto; width: auto; @@ -958,6 +1050,24 @@ pre { flex-basis: 0; flex-grow: 1; max-width: 100%; } + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; } + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; } + .row-cols-xl-3 > * { + flex: 0 0 33.33333%; + max-width: 33.33333%; } + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; } + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; } + .row-cols-xl-6 > * { + flex: 0 0 16.66667%; + max-width: 16.66667%; } .col-xl-auto { flex: 0 0 auto; width: auto; @@ -1364,6 +1474,9 @@ pre { .form-control::-ms-expand { background-color: transparent; border: 0; } + .form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #000; } .form-control:focus { color: #495057; background-color: #fff; @@ -1408,9 +1521,9 @@ select.form-control:focus::-ms-value { .form-control-plaintext { display: block; width: 100%; - padding-top: 0.375rem; - padding-bottom: 0.375rem; + padding: 0.375rem 0; margin-bottom: 0; + font-size: 1rem; line-height: 1.5; color: #212529; background-color: transparent; @@ -1466,6 +1579,7 @@ textarea.form-control { position: absolute; margin-top: 0.3rem; margin-left: -1.25rem; } + .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { color: #6c757d; } @@ -1504,20 +1618,22 @@ textarea.form-control { background-color: rgba(40, 167, 69, 0.9); border-radius: 0.25rem; } +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; } + .was-validated .form-control:valid, .form-control.is-valid { border-color: #28a745; padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); + background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - .was-validated .form-control:valid ~ .valid-feedback, - .was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, - .form-control.is-valid ~ .valid-tooltip { - display: block; } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { padding-right: calc(1.5em + 0.75rem); @@ -1525,20 +1641,11 @@ textarea.form-control { .was-validated .custom-select:valid, .custom-select.is-valid { border-color: #28a745; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } - .was-validated .custom-select:valid ~ .valid-feedback, - .was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, - .custom-select.is-valid ~ .valid-tooltip { - display: block; } - -.was-validated .form-control-file:valid ~ .valid-feedback, -.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, -.form-control-file.is-valid ~ .valid-tooltip { - display: block; } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { color: #28a745; } @@ -1553,11 +1660,6 @@ textarea.form-control { .was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { border-color: #28a745; } -.was-validated .custom-control-input:valid ~ .valid-feedback, -.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, -.custom-control-input.is-valid ~ .valid-tooltip { - display: block; } - .was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { border-color: #34ce57; background-color: #34ce57; } @@ -1571,11 +1673,6 @@ textarea.form-control { .was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { border-color: #28a745; } -.was-validated .custom-file-input:valid ~ .valid-feedback, -.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, -.custom-file-input.is-valid ~ .valid-tooltip { - display: block; } - .was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { border-color: #28a745; box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } @@ -1601,20 +1698,22 @@ textarea.form-control { background-color: rgba(220, 53, 69, 0.9); border-radius: 0.25rem; } +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; } + .was-validated .form-control:invalid, .form-control.is-invalid { border-color: #dc3545; padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center right calc(0.375em + 0.1875rem); + background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } - .was-validated .form-control:invalid ~ .invalid-feedback, - .was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, - .form-control.is-invalid ~ .invalid-tooltip { - display: block; } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { padding-right: calc(1.5em + 0.75rem); @@ -1622,20 +1721,11 @@ textarea.form-control { .was-validated .custom-select:invalid, .custom-select.is-invalid { border-color: #dc3545; - padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem); - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } - .was-validated .custom-select:invalid ~ .invalid-feedback, - .was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, - .custom-select.is-invalid ~ .invalid-tooltip { - display: block; } - -.was-validated .form-control-file:invalid ~ .invalid-feedback, -.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, -.form-control-file.is-invalid ~ .invalid-tooltip { - display: block; } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { color: #dc3545; } @@ -1650,11 +1740,6 @@ textarea.form-control { .was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { border-color: #dc3545; } -.was-validated .custom-control-input:invalid ~ .invalid-feedback, -.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, -.custom-control-input.is-invalid ~ .invalid-tooltip { - display: block; } - .was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { border-color: #e4606d; background-color: #e4606d; } @@ -1668,11 +1753,6 @@ textarea.form-control { .was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { border-color: #dc3545; } -.was-validated .custom-file-input:invalid ~ .invalid-feedback, -.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, -.custom-file-input.is-invalid ~ .invalid-tooltip { - display: block; } - .was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { border-color: #dc3545; box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } @@ -1728,6 +1808,7 @@ textarea.form-control { color: #ced4da; text-align: center; vertical-align: middle; + cursor: pointer; user-select: none; background-color: transparent; border: 1px solid transparent; @@ -1761,6 +1842,9 @@ fieldset:disabled a.btn { background-color: #1b5878; border-color: #19506d; } .btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #1b5878; + border-color: #19506d; box-shadow: 0 0 0 0.2rem rgba(67, 133, 167, 0.5); } .btn-primary.disabled, .btn-primary:disabled { color: #fff; @@ -1784,6 +1868,9 @@ fieldset:disabled a.btn { background-color: #d97a00; border-color: #cc7200; } .btn-secondary:focus, .btn-secondary.focus { + color: #fff; + background-color: #d97a00; + border-color: #cc7200; box-shadow: 0 0 0 0.2rem rgba(222, 127, 6, 0.5); } .btn-secondary.disabled, .btn-secondary:disabled { color: #212529; @@ -1807,6 +1894,9 @@ fieldset:disabled a.btn { background-color: #1c7644; border-color: #196c3e; } .btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #1c7644; + border-color: #196c3e; box-shadow: 0 0 0 0.2rem rgba(68, 165, 111, 0.5); } .btn-success.disabled, .btn-success:disabled { color: #fff; @@ -1830,6 +1920,9 @@ fieldset:disabled a.btn { background-color: #1295b0; border-color: #108ba5; } .btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } .btn-info.disabled, .btn-info:disabled { color: #fff; @@ -1853,6 +1946,9 @@ fieldset:disabled a.btn { background-color: #ffb11c; border-color: #ffac0f; } .btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #ffb11c; + border-color: #ffac0f; box-shadow: 0 0 0 0.2rem rgba(222, 167, 62, 0.5); } .btn-warning.disabled, .btn-warning:disabled { color: #212529; @@ -1876,6 +1972,9 @@ fieldset:disabled a.btn { background-color: #881d28; border-color: #7d1b25; } .btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } .btn-danger.disabled, .btn-danger:disabled { color: #fff; @@ -1899,6 +1998,9 @@ fieldset:disabled a.btn { background-color: #e2e6ea; border-color: #dae0e5; } .btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } .btn-light.disabled, .btn-light:disabled { color: #212529; @@ -1922,6 +2024,9 @@ fieldset:disabled a.btn { background-color: #23272b; border-color: #1d2124; } .btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } .btn-dark.disabled, .btn-dark:disabled { color: #fff; @@ -1945,6 +2050,9 @@ fieldset:disabled a.btn { background-color: #1295b0; border-color: #108ba5; } .btn-debug:focus, .btn-debug.focus { + color: #fff; + background-color: #1295b0; + border-color: #108ba5; box-shadow: 0 0 0 0.2rem rgba(56, 190, 218, 0.5); } .btn-debug.disabled, .btn-debug:disabled { color: #fff; @@ -1968,6 +2076,9 @@ fieldset:disabled a.btn { background-color: #881d28; border-color: #7d1b25; } .btn-error:focus, .btn-error.focus { + color: #fff; + background-color: #881d28; + border-color: #7d1b25; box-shadow: 0 0 0 0.2rem rgba(180, 69, 80, 0.5); } .btn-error.disabled, .btn-error:disabled { color: #fff; @@ -2549,8 +2660,8 @@ input[type="button"].btn-block { .input-group > .custom-select, .input-group > .custom-file { position: relative; - flex: 1 1 auto; - width: 1%; + flex: 1 1 0%; + min-width: 0; margin-bottom: 0; } .input-group > .form-control + .form-control, .input-group > .form-control + .custom-select, @@ -2698,7 +2809,10 @@ input[type="button"].btn-block { .custom-control-input { position: absolute; + left: 0; z-index: -1; + width: 1rem; + height: 1.25rem; opacity: 0; } .custom-control-input:checked ~ .custom-control-label::before { color: #fff; @@ -2712,9 +2826,9 @@ input[type="button"].btn-block { color: #fff; background-color: #b3d7ff; border-color: #b3d7ff; } - .custom-control-input:disabled ~ .custom-control-label { + .custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { color: #6c757d; } - .custom-control-input:disabled ~ .custom-control-label::before { + .custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { background-color: #e9ecef; } .custom-control-label { @@ -2746,14 +2860,14 @@ input[type="button"].btn-block { border-radius: 0.25rem; } .custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { border-color: #007bff; background-color: #007bff; } .custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } .custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } @@ -2765,7 +2879,7 @@ input[type="button"].btn-block { border-radius: 50%; } .custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { background-color: rgba(0, 123, 255, 0.5); } @@ -2804,8 +2918,7 @@ input[type="button"].btn-block { line-height: 1.5; color: #495057; vertical-align: middle; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; - background-color: #fff; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; border: 1px solid #ced4da; border-radius: 0.25rem; appearance: none; } @@ -2825,6 +2938,9 @@ input[type="button"].btn-block { background-color: #e9ecef; } .custom-select::-ms-expand { display: none; } + .custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; } .custom-select-sm { height: calc(1.5em + 0.5rem + 2px); @@ -2857,6 +2973,7 @@ input[type="button"].btn-block { .custom-file-input:focus ~ .custom-file-label { border-color: #80bdff; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-file-input[disabled] ~ .custom-file-label, .custom-file-input:disabled ~ .custom-file-label { background-color: #e9ecef; } .custom-file-input:lang(en) ~ .custom-file-label::after { @@ -2896,7 +3013,7 @@ input[type="button"].btn-block { .custom-range { width: 100%; - height: calc(1rem + 0.4rem); + height: 1.4rem; padding: 0; background-color: transparent; appearance: none; } @@ -3076,8 +3193,8 @@ input[type="button"].btn-block { align-items: center; justify-content: space-between; padding: 0.5rem 1rem; } - .navbar > .container, - .navbar > .container-fluid { + .navbar .container, + .navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl { display: flex; flex-wrap: wrap; align-items: center; @@ -3138,7 +3255,7 @@ input[type="button"].btn-block { @media (max-width: 575.98px) { .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3154,7 +3271,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid { + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { flex-wrap: nowrap; } .navbar-expand-sm .navbar-collapse { display: flex !important; @@ -3164,7 +3281,7 @@ input[type="button"].btn-block { @media (max-width: 767.98px) { .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3180,7 +3297,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid { + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { flex-wrap: nowrap; } .navbar-expand-md .navbar-collapse { display: flex !important; @@ -3190,7 +3307,7 @@ input[type="button"].btn-block { @media (max-width: 991.98px) { .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3206,7 +3323,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid { + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { flex-wrap: nowrap; } .navbar-expand-lg .navbar-collapse { display: flex !important; @@ -3216,7 +3333,7 @@ input[type="button"].btn-block { @media (max-width: 1199.98px) { .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { padding-right: 0; padding-left: 0; } } @@ -3232,7 +3349,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid { + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { flex-wrap: nowrap; } .navbar-expand-xl .navbar-collapse { display: flex !important; @@ -3244,7 +3361,7 @@ input[type="button"].btn-block { flex-flow: row nowrap; justify-content: flex-start; } .navbar-expand > .container, - .navbar-expand > .container-fluid { + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { padding-right: 0; padding-left: 0; } .navbar-expand .navbar-nav { @@ -3255,7 +3372,7 @@ input[type="button"].btn-block { padding-right: 0.5rem; padding-left: 0.5rem; } .navbar-expand > .container, - .navbar-expand > .container-fluid { + .navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { flex-wrap: nowrap; } .navbar-expand .navbar-collapse { display: flex !important; @@ -3286,7 +3403,7 @@ input[type="button"].btn-block { border-color: rgba(0, 0, 0, 0.1); } .navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-light .navbar-text { color: rgba(0, 0, 0, 0.5); } @@ -3318,7 +3435,7 @@ input[type="button"].btn-block { border-color: rgba(255, 255, 255, 0.1); } .navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .navbar-dark .navbar-text { color: rgba(255, 255, 255, 0.5); } @@ -3349,6 +3466,7 @@ input[type="button"].btn-block { .card-body { flex: 1 1 auto; + min-height: 1px; padding: 1.25rem; } .card-title { @@ -3402,70 +3520,68 @@ input[type="button"].btn-block { left: 0; padding: 1.25rem; } -.card-img { - width: 100%; - border-radius: calc(0.25rem - 1px); } +.card-img, +.card-img-top, +.card-img-bottom { + flex-shrink: 0; + width: 100%; } +.card-img, .card-img-top { - width: 100%; border-top-left-radius: calc(0.25rem - 1px); border-top-right-radius: calc(0.25rem - 1px); } +.card-img, .card-img-bottom { - width: 100%; border-bottom-right-radius: calc(0.25rem - 1px); border-bottom-left-radius: calc(0.25rem - 1px); } -.card-deck { - display: flex; - flex-direction: column; } - .card-deck .card { - margin-bottom: 15px; } - @media (min-width: 576px) { - .card-deck { - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; } - .card-deck .card { - display: flex; - flex: 1 0 0%; - flex-direction: column; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; } } +.card-deck .card { + margin-bottom: 15px; } -.card-group { - display: flex; - flex-direction: column; } - .card-group > .card { - margin-bottom: 15px; } - @media (min-width: 576px) { - .card-group { - flex-flow: row wrap; } - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0; } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; +@media (min-width: 576px) { + .card-deck { + display: flex; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; } + .card-deck .card { + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; } } + +.card-group > .card { + margin-bottom: 15px; } + +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { border-bottom-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; } } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; } } .card-columns .card { margin-bottom: 0.75rem; } @@ -3482,19 +3598,15 @@ input[type="button"].btn-block { .accordion > .card { overflow: hidden; } - .accordion > .card:not(:first-of-type) .card-header:first-child { - border-radius: 0; } - .accordion > .card:not(:first-of-type):not(:last-of-type) { - border-bottom: 0; - border-radius: 0; } - .accordion > .card:first-of-type { + .accordion > .card:not(:last-of-type) { border-bottom: 0; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } - .accordion > .card:last-of-type { + .accordion > .card:not(:first-of-type) { border-top-left-radius: 0; border-top-right-radius: 0; } - .accordion > .card .card-header { + .accordion > .card > .card-header { + border-radius: 0; margin-bottom: -1px; } .breadcrumb { @@ -3545,7 +3657,7 @@ input[type="button"].btn-block { background-color: #212529; border-color: #343a40; } .page-link:focus { - z-index: 2; + z-index: 3; outline: 0; box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } @@ -3559,7 +3671,7 @@ input[type="button"].btn-block { border-bottom-right-radius: 0.25rem; } .page-item.active .page-link { - z-index: 1; + z-index: 3; color: #ced4da; background-color: #226f97; border-color: #226f97; } @@ -3869,6 +3981,7 @@ input[type="button"].btn-block { display: flex; flex-direction: column; justify-content: center; + overflow: hidden; color: #fff; text-align: center; white-space: nowrap; @@ -3918,14 +4031,12 @@ input[type="button"].btn-block { position: relative; display: block; padding: 0.75rem 1.25rem; - margin-bottom: -1px; background-color: #343a40; border: 1px solid rgba(0, 0, 0, 0.125); } .list-group-item:first-child { border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; } .list-group-item:last-child { - margin-bottom: 0; border-bottom-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; } .list-group-item.disabled, .list-group-item:disabled { @@ -3937,99 +4048,110 @@ input[type="button"].btn-block { color: #fff; background-color: #007bff; border-color: #007bff; } + .list-group-item + .list-group-item { + border-top-width: 0; } + .list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; } .list-group-horizontal { flex-direction: row; } - .list-group-horizontal .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } + .list-group-horizontal .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal .list-group-item.active { + margin-top: 0; } + .list-group-horizontal .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } @media (min-width: 576px) { .list-group-horizontal-sm { flex-direction: row; } - .list-group-horizontal-sm .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-sm .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-sm .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-sm .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-sm .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-sm .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-sm .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-sm .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 768px) { .list-group-horizontal-md { flex-direction: row; } - .list-group-horizontal-md .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-md .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-md .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-md .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-md .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-md .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-md .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-md .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 992px) { .list-group-horizontal-lg { flex-direction: row; } - .list-group-horizontal-lg .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-lg .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-lg .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-lg .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-lg .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-lg .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-lg .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-lg .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } @media (min-width: 1200px) { .list-group-horizontal-xl { flex-direction: row; } - .list-group-horizontal-xl .list-group-item { - margin-right: -1px; - margin-bottom: 0; } - .list-group-horizontal-xl .list-group-item:first-child { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; } - .list-group-horizontal-xl .list-group-item:last-child { - margin-right: 0; - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0; } } + .list-group-horizontal-xl .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-xl .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-xl .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-xl .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-xl .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } .list-group-flush .list-group-item { - border-right: 0; - border-left: 0; + border-right-width: 0; + border-left-width: 0; border-radius: 0; } - .list-group-flush .list-group-item:last-child { - margin-bottom: -1px; } - -.list-group-flush:first-child .list-group-item:first-child { - border-top: 0; } + .list-group-flush .list-group-item:first-child { + border-top-width: 0; } .list-group-flush:last-child .list-group-item:last-child { - margin-bottom: 0; - border-bottom: 0; } + border-bottom-width: 0; } .list-group-item-primary { color: #123a4f; @@ -4227,6 +4349,8 @@ a.close.disabled { transition: none; } } .modal.show .modal-dialog { transform: none; } + .modal.modal-static .modal-dialog { + transform: scale(1.02); } .modal-dialog-scrollable { display: flex; @@ -4288,8 +4412,8 @@ a.close.disabled { justify-content: space-between; padding: 1rem 1rem; border-bottom: 1px solid #dee2e6; - border-top-left-radius: 0.3rem; - border-top-right-radius: 0.3rem; } + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } .modal-header .close { padding: 1rem 1rem; margin: -1rem -1rem -1rem auto; } @@ -4305,16 +4429,15 @@ a.close.disabled { .modal-footer { display: flex; + flex-wrap: wrap; align-items: center; justify-content: flex-end; - padding: 1rem; + padding: 0.75rem; border-top: 1px solid #dee2e6; - border-bottom-right-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; } - .modal-footer > :not(:first-child) { - margin-left: .25rem; } - .modal-footer > :not(:last-child) { - margin-right: .25rem; } + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); } + .modal-footer > * { + margin: 0.25rem; } .modal-scrollbar-measure { position: absolute; @@ -4473,7 +4596,7 @@ a.close.disabled { .bs-popover-top, .bs-popover-auto[x-placement^="top"] { margin-bottom: 0.5rem; } .bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { - bottom: calc((0.5rem + 1px) * -1); } + bottom: calc(-0.5rem - 1px); } .bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { bottom: 0; border-width: 0.5rem 0.5rem 0; @@ -4486,7 +4609,7 @@ a.close.disabled { .bs-popover-right, .bs-popover-auto[x-placement^="right"] { margin-left: 0.5rem; } .bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { - left: calc((0.5rem + 1px) * -1); + left: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } @@ -4502,7 +4625,7 @@ a.close.disabled { .bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { margin-top: 0.5rem; } .bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { - top: calc((0.5rem + 1px) * -1); } + top: calc(-0.5rem - 1px); } .bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { top: 0; border-width: 0 0.5rem 0.5rem 0.5rem; @@ -4524,7 +4647,7 @@ a.close.disabled { .bs-popover-left, .bs-popover-auto[x-placement^="left"] { margin-right: 0.5rem; } .bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { - right: calc((0.5rem + 1px) * -1); + right: calc(-0.5rem - 1px); width: 0.5rem; height: 1rem; margin: 0.3rem 0; } @@ -4607,7 +4730,7 @@ a.close.disabled { .carousel-fade .active.carousel-item-right { z-index: 0; opacity: 0; - transition: 0s 0.6s opacity; } + transition: opacity 0s 0.6s; } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { @@ -4653,10 +4776,10 @@ a.close.disabled { background: no-repeat 50% / 100% 100%; } .carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e"); } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); } .carousel-indicators { position: absolute; @@ -5601,6 +5724,7 @@ button.bg-error:focus { width: 1px; height: 1px; padding: 0; + margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; diff --git a/static/babybuddy/css/app.css.gz b/static/babybuddy/css/app.css.gz index 559e2f3b..1b8ed645 100644 Binary files a/static/babybuddy/css/app.css.gz and b/static/babybuddy/css/app.css.gz differ diff --git a/static/babybuddy/js/app.0079764f3fc1.js b/static/babybuddy/js/app.0079764f3fc1.js deleted file mode 100644 index 45679c10..00000000 --- a/static/babybuddy/js/app.0079764f3fc1.js +++ /dev/null @@ -1,159 +0,0 @@ -/* Baby Buddy - * - * Default namespace for the Baby Buddy app. - */ -if (typeof jQuery === 'undefined') { - throw new Error('Baby Buddy requires jQuery.') -} - -var BabyBuddy = function () { - var BabyBuddy = {}; - return BabyBuddy; -}(); - -/* Baby Buddy Timer - * - * Uses a supplied ID to run a timer. The element using the ID must have - * three children with the following classes: - * * timer-seconds - * * timer-minutes - * * timer-hours - */ -BabyBuddy.Timer = function ($) { - var runIntervalId = null; - var timerId = null; - var timerElement = null; - var lastUpdate = moment(); - - var Timer = { - run: function(timer_id, element_id) { - timerId = timer_id; - timerElement = $('#' + element_id); - - if (timerElement.length == 0) { - console.error('BBTimer: Timer element not found.'); - return false; - } - - if (timerElement.find('.timer-seconds').length == 0 - || timerElement.find('.timer-minutes').length == 0 - || timerElement.find('.timer-hours').length == 0) { - console.error('BBTimer: Element does not contain expected children.'); - return false; - } - - runIntervalId = setInterval(this.tick, 1000); - - // If the page just came in to view, update the timer data with the - // current actual duration. This will (potentially) help mobile - // phones that lock with the timer page open. - Visibility.change(function (e, state) { - if (state == 'visible' && moment().diff(lastUpdate) > 2000) { - Timer.update(); - } - }); - }, - - tick: function() { - var s = timerElement.find('.timer-seconds'); - var seconds = Number(s.text()); - if (seconds < 59) { - s.text(seconds + 1); - return; - } - else { - s.text(0); - } - - var m = timerElement.find('.timer-minutes'); - var minutes = Number(m.text()); - if (minutes < 59) { - m.text(minutes + 1); - return; - } - else { - m.text(0); - } - - var h = timerElement.find('.timer-hours'); - var hours = Number(h.text()); - h.text(hours + 1); - }, - - update: function() { - $.get('/api/timers/' + timerId + '/', function(data) { - if (data && 'duration' in data) { - clearInterval(runIntervalId); - var duration = moment.duration(data.duration); - timerElement.find('.timer-hours').text(duration.hours()); - timerElement.find('.timer-minutes').text(duration.minutes()); - timerElement.find('.timer-seconds').text(duration.seconds()); - lastUpdate = moment(); - - if (data['active']) { - runIntervalId = setInterval(Timer.tick, 1000); - } - else { - timerElement.addClass('timer-stopped'); - } - } - }); - } - }; - - return Timer; -}(jQuery); - -/* Baby Buddy Dashboard - * - * Provides a "watch" function to update the dashboard at one minute intervals - * and/or on visibility state changes. - */ -BabyBuddy.Dashboard = function ($) { - var runIntervalId = null; - var dashboardElement = null; - var hidden = null; - - var Dashboard = { - watch: function(element_id, refresh_rate) { - dashboardElement = $('#' + element_id); - - if (dashboardElement.length == 0) { - console.error('Baby Buddy: Dashboard element not found.'); - return false; - } - - if (typeof document.hidden !== "undefined") { - hidden = "hidden"; - } - else if (typeof document.msHidden !== "undefined") { - hidden = "msHidden"; - } - else if (typeof document.webkitHidden !== "undefined") { - hidden = "webkitHidden"; - } - - if (typeof window.addEventListener === "undefined" || typeof document.hidden === "undefined") { - if (refresh_rate) { - runIntervalId = setInterval(this.update, refresh_rate); - } - } - else { - window.addEventListener('focus', Dashboard.handleVisibilityChange, false); - } - }, - - handleVisibilityChange: function() { - if (!document[hidden]) { - Dashboard.update(); - } - }, - - update: function() { - // TODO: Someday maybe update in place? - location.reload(); - } - }; - - return Dashboard; -}(jQuery); diff --git a/static/babybuddy/js/app.0079764f3fc1.js.gz b/static/babybuddy/js/app.0079764f3fc1.js.gz deleted file mode 100644 index d6a3d23f..00000000 Binary files a/static/babybuddy/js/app.0079764f3fc1.js.gz and /dev/null differ diff --git a/static/babybuddy/js/graph.239e93d5162b.js b/static/babybuddy/js/graph.239e93d5162b.js deleted file mode 100644 index 08a7192a..00000000 --- a/static/babybuddy/js/graph.239e93d5162b.js +++ /dev/null @@ -1,84270 +0,0 @@ -/** -* 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 deleted file mode 100644 index ae6ec192..00000000 Binary files a/static/babybuddy/js/graph.239e93d5162b.js.gz and /dev/null differ diff --git a/static/babybuddy/js/graph.a7dcc3322745.js b/static/babybuddy/js/graph.a6254127d410.js similarity index 99% rename from static/babybuddy/js/graph.a7dcc3322745.js rename to static/babybuddy/js/graph.a6254127d410.js index 891482bb..7cb2d646 100644 --- a/static/babybuddy/js/graph.a7dcc3322745.js +++ b/static/babybuddy/js/graph.a6254127d410.js @@ -1,5 +1,5 @@ /** -* plotly.js (cartesian) v1.51.1 +* plotly.js (cartesian) v1.51.3 * Copyright 2012-2019, Plotly, Inc. * All rights reserved. * Licensed under the MIT license @@ -85,7 +85,7 @@ for(var selector in rules) { module.exports = _dereq_('../src/traces/bar'); -},{"../src/traces/bar":275}],3:[function(_dereq_,module,exports){ +},{"../src/traces/bar":276}],3:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -98,7 +98,7 @@ module.exports = _dereq_('../src/traces/bar'); module.exports = _dereq_('../src/traces/box'); -},{"../src/traces/box":289}],4:[function(_dereq_,module,exports){ +},{"../src/traces/box":290}],4:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -111,7 +111,7 @@ module.exports = _dereq_('../src/traces/box'); module.exports = _dereq_('../src/traces/contour'); -},{"../src/traces/contour":309}],5:[function(_dereq_,module,exports){ +},{"../src/traces/contour":310}],5:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -137,7 +137,7 @@ module.exports = _dereq_('../src/core'); module.exports = _dereq_('../src/traces/heatmap'); -},{"../src/traces/heatmap":325}],7:[function(_dereq_,module,exports){ +},{"../src/traces/heatmap":326}],7:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -150,7 +150,7 @@ module.exports = _dereq_('../src/traces/heatmap'); module.exports = _dereq_('../src/traces/histogram'); -},{"../src/traces/histogram":343}],8:[function(_dereq_,module,exports){ +},{"../src/traces/histogram":344}],8:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -163,7 +163,7 @@ module.exports = _dereq_('../src/traces/histogram'); module.exports = _dereq_('../src/traces/histogram2d'); -},{"../src/traces/histogram2d":349}],9:[function(_dereq_,module,exports){ +},{"../src/traces/histogram2d":350}],9:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -176,7 +176,7 @@ module.exports = _dereq_('../src/traces/histogram2d'); module.exports = _dereq_('../src/traces/histogram2dcontour'); -},{"../src/traces/histogram2dcontour":353}],10:[function(_dereq_,module,exports){ +},{"../src/traces/histogram2dcontour":354}],10:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -189,7 +189,7 @@ module.exports = _dereq_('../src/traces/histogram2dcontour'); module.exports = _dereq_('../src/traces/image'); -},{"../src/traces/image":360}],11:[function(_dereq_,module,exports){ +},{"../src/traces/image":361}],11:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -231,7 +231,7 @@ module.exports = Plotly; module.exports = _dereq_('../src/traces/pie'); -},{"../src/traces/pie":369}],13:[function(_dereq_,module,exports){ +},{"../src/traces/pie":370}],13:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -244,7 +244,7 @@ module.exports = _dereq_('../src/traces/pie'); module.exports = _dereq_('../src/traces/scatterternary'); -},{"../src/traces/scatterternary":407}],14:[function(_dereq_,module,exports){ +},{"../src/traces/scatterternary":410}],14:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -257,7 +257,7 @@ module.exports = _dereq_('../src/traces/scatterternary'); module.exports = _dereq_('../src/traces/violin'); -},{"../src/traces/violin":415}],15:[function(_dereq_,module,exports){ +},{"../src/traces/violin":418}],15:[function(_dereq_,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -19603,9 +19603,9 @@ var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM; var subTypes = _dereq_('../../traces/scatter/subtypes'); var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func'); +var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue; var drawing = module.exports = {}; -var appendArrayPointValue = _dereq_('../fx/helpers').appendArrayPointValue; // ----------------------------------------------------- // styling functions for plot elements @@ -20257,32 +20257,36 @@ function extracTextFontSize(d, trace) { } // draw text at points -drawing.textPointStyle = function(s, trace, gd, inLegend) { +drawing.textPointStyle = function(s, trace, gd) { if(!s.size()) return; var selectedTextColorFn; - if(trace.selectedpoints) { var fns = drawing.makeSelectedTextStyleFns(trace); selectedTextColorFn = fns.selectedTextColorFn; } - var template = trace.texttemplate; - // If styling text in legends, do not use texttemplate - if(inLegend) template = false; + var texttemplate = trace.texttemplate; + var fullLayout = gd._fullLayout; + s.each(function(d) { var p = d3.select(this); - var text = Lib.extractOption(d, trace, template ? 'txt' : 'tx', template ? 'texttemplate' : 'text'); + + var text = texttemplate ? + Lib.extractOption(d, trace, 'txt', 'texttemplate') : + Lib.extractOption(d, trace, 'tx', 'text'); if(!text && text !== 0) { p.remove(); return; } - if(template) { - var pt = {}; - appendArrayPointValue(pt, trace, d.i); - text = Lib.texttemplateString(text, {}, gd._fullLayout._d3locale, pt, d, trace._meta || {}); + if(texttemplate) { + var labels = trace._module.formatLabels ? trace._module.formatLabels(d, trace, fullLayout) : {}; + var pointValues = {}; + appendArrayPointValue(pointValues, trace, d.i); + var meta = trace._meta || {}; + text = Lib.texttemplateString(text, labels, fullLayout._d3locale, pointValues, d, meta); } var pos = d.tp || trace.textposition; @@ -20733,7 +20737,7 @@ drawing.setTextPointsScale = function(selection, xScale, yScale) { }); }; -},{"../../constants/alignment":145,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../lib/svg_text_utils":190,"../../registry":258,"../../traces/scatter/make_bubble_size_func":392,"../../traces/scatter/subtypes":399,"../color":51,"../colorscale":63,"../fx/helpers":86,"./symbol_defs":73,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],73:[function(_dereq_,module,exports){ +},{"../../components/fx/helpers":86,"../../constants/alignment":145,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../lib/svg_text_utils":190,"../../registry":258,"../../traces/scatter/make_bubble_size_func":394,"../../traces/scatter/subtypes":401,"../color":51,"../colorscale":63,"./symbol_defs":73,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],73:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -21839,7 +21843,7 @@ function errorCoords(d, xa, ya) { return out; } -},{"../../traces/scatter/subtypes":399,"../drawing":72,"d3":16,"fast-isnumeric":18}],80:[function(_dereq_,module,exports){ +},{"../../traces/scatter/subtypes":401,"../drawing":72,"d3":16,"fast-isnumeric":18}],80:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -23195,7 +23199,7 @@ function createHoverText(hoverData, opts, gd) { .text(s.text()) .call(Drawing.font, commonLabelFont); var dummyBB = dummy.node().getBoundingClientRect(); - if(dummyBB.width < tbb.width) { + if(Math.round(dummyBB.width) < Math.round(tbb.width)) { s.attr('x', ltx - dummyBB.width); } dummy.remove(); @@ -26591,6 +26595,7 @@ var Registry = _dereq_('../../registry'); var Lib = _dereq_('../../lib'); var Drawing = _dereq_('../drawing'); var Color = _dereq_('../color'); +var extractOpts = _dereq_('../colorscale/helpers').extractOpts; var subTypes = _dereq_('../../traces/scatter/subtypes'); var stylePie = _dereq_('../../traces/pie/style_one'); @@ -26607,7 +26612,7 @@ module.exports = function style(s, gd) { var legend = fullLayout.legend; var constantItemSizing = legend.itemsizing === 'constant'; - function boundLineWidth(mlw, cont, max, cst) { + var boundLineWidth = function(mlw, cont, max, cst) { var v; if(mlw + 1) { v = mlw; @@ -26617,7 +26622,7 @@ module.exports = function style(s, gd) { return 0; } return constantItemSizing ? cst : Math.min(v, max); - } + }; s.each(function(d) { var traceGroup = d3.select(this); @@ -26681,6 +26686,29 @@ module.exports = function style(s, gd) { var showGradientFill = false; var dMod, tMod; + var cOpts = extractOpts(trace); + var colorscale = cOpts.colorscale; + var reversescale = cOpts.reversescale; + + var fillGradient = function(s) { + if(s.size()) { + var gradientID = 'legendfill-' + trace.uid; + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale), + colorscale, 'fill'); + } + }; + + var lineGradient = function(s) { + if(s.size()) { + var gradientID = 'legendline-' + trace.uid; + Drawing.lineGroupStyle(s); + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale), + colorscale, 'stroke'); + } + }; + if(contours) { var coloring = contours.coloring; @@ -26735,23 +26763,6 @@ module.exports = function style(s, gd) { // 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) { @@ -26825,6 +26836,9 @@ module.exports = function style(s, gd) { // always show legend items in base state tMod.selectedpoints = null; + + // never show texttemplate + tMod.texttemplate = null; } var ptgroup = d3.select(this).select('g.legendpoints'); @@ -26848,14 +26862,14 @@ module.exports = function style(s, gd) { .append('g').classed('pointtext', true) .append('text').attr('transform', 'translate(20,0)'); txt.exit().remove(); - txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd, true); + txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd); } function styleWaterfalls(d) { var trace = d[0].trace; var ptsData = []; - if(trace.type === 'waterfall' && trace.visible) { + if(trace.visible && trace.type === 'waterfall') { 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']]; @@ -26898,7 +26912,7 @@ module.exports = function style(s, gd) { var markerLine = marker.line || {}; var isVisible = (!desiredType) ? Registry.traceIs(trace, 'bar') : - (trace.type === desiredType && trace.visible); + (trace.visible && trace.type === desiredType); var barpath = d3.select(lThis).select('g.legendpoints') .selectAll('path.legend' + desiredType) @@ -26925,7 +26939,7 @@ module.exports = function style(s, gd) { var pts = d3.select(this).select('g.legendpoints') .selectAll('path.legendbox') - .data(Registry.traceIs(trace, 'box-violin') && trace.visible ? [d] : []); + .data(trace.visible && Registry.traceIs(trace, 'box-violin') ? [d] : []); pts.enter().append('path').classed('legendbox', true) // if we want the median bar, prepend M6,0H-6 .attr('d', 'M6,6H-6V-6H6Z') @@ -26963,7 +26977,7 @@ module.exports = function style(s, gd) { var pts = d3.select(this).select('g.legendpoints') .selectAll('path.legendcandle') - .data(trace.type === 'candlestick' && trace.visible ? [d, d] : []); + .data(trace.visible && trace.type === 'candlestick' ? [d, d] : []); pts.enter().append('path').classed('legendcandle', true) .attr('d', function(_, i) { if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing @@ -26990,7 +27004,7 @@ module.exports = function style(s, gd) { var pts = d3.select(this).select('g.legendpoints') .selectAll('path.legendohlc') - .data(trace.type === 'ohlc' && trace.visible ? [d, d] : []); + .data(trace.visible && trace.type === 'ohlc' ? [d, d] : []); pts.enter().append('path').classed('legendohlc', true) .attr('d', function(_, i) { if(i) return 'M-15,0H0M-8,-6V0'; // increasing @@ -27025,7 +27039,7 @@ module.exports = function style(s, gd) { var trace = d0.trace; var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) : - (trace.type === desiredType && trace.visible); + (trace.visible && trace.type === desiredType); var pts = d3.select(lThis).select('g.legendpoints') .selectAll('path.legend' + desiredType) @@ -27050,7 +27064,11 @@ module.exports = function style(s, gd) { } }; -},{"../../lib":169,"../../registry":258,"../../traces/pie/helpers":368,"../../traces/pie/style_one":374,"../../traces/scatter/subtypes":399,"../color":51,"../drawing":72,"d3":16}],108:[function(_dereq_,module,exports){ +function getGradientDirection(reversescale) { + return reversescale ? 'horizontal' : 'horizontalreversed'; +} + +},{"../../lib":169,"../../registry":258,"../../traces/pie/helpers":369,"../../traces/pie/style_one":375,"../../traces/scatter/subtypes":401,"../color":51,"../colorscale/helpers":62,"../drawing":72,"d3":16}],108:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -27970,7 +27988,7 @@ function fillCustomButton(customButtons) { return customButtons; } -},{"../../plots/cartesian/axis_ids":216,"../../registry":258,"../../traces/scatter/subtypes":399,"./buttons":108,"./modebar":111}],111:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/axis_ids":216,"../../registry":258,"../../traces/scatter/subtypes":401,"./buttons":108,"./modebar":111}],111:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -30025,7 +30043,7 @@ module.exports = templatedArray('shape', { editType: 'arraydraw' }); -},{"../../lib/extend":164,"../../plot_api/plot_template":203,"../../traces/scatter/attributes":376,"../annotations/attributes":36,"../drawing/attributes":71}],127:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plot_api/plot_template":203,"../../traces/scatter/attributes":377,"../annotations/attributes":36,"../drawing/attributes":71}],127:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34164,7 +34182,7 @@ exports.svgAttrs = { 'use strict'; // package version injected by `npm run preprocess` -exports.version = '1.51.1'; +exports.version = '1.51.3'; // inject promise polyfill _dereq_('es6-promise').polyfill(); @@ -34233,7 +34251,7 @@ exports.Queue = _dereq_('./lib/queue'); // export d3 used in the bundle exports.d3 = _dereq_('d3'); -},{"../build/plotcss":1,"./components/annotations":44,"./components/annotations3d":49,"./components/colorbar":57,"./components/colorscale":63,"./components/errorbars":78,"./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":258,"./snapshot":263,"./traces/scatter":387,"d3":16,"es6-promise":17}],152:[function(_dereq_,module,exports){ +},{"../build/plotcss":1,"./components/annotations":44,"./components/annotations3d":49,"./components/colorbar":57,"./components/colorscale":63,"./components/errorbars":78,"./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":258,"./snapshot":263,"./traces/scatter":389,"d3":16,"es6-promise":17}],152:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -60529,12 +60547,15 @@ plots.redrawText = function(gd) { plots.resize = function(gd) { gd = Lib.getGraphDiv(gd); - return new Promise(function(resolve, reject) { + var resolveLastResize; + var p = new Promise(function(resolve, reject) { if(!gd || Lib.isHidden(gd)) { reject(new Error('Resize must be passed a displayed plot div element.')); } if(gd._redrawTimer) clearTimeout(gd._redrawTimer); + if(gd._resolveResize) resolveLastResize = gd._resolveResize; + gd._resolveResize = resolve; gd._redrawTimer = setTimeout(function() { // return if there is nothing to resize or is hidden @@ -60554,10 +60575,17 @@ plots.resize = function(gd) { Registry.call('relayout', gd, {autosize: true}).then(function() { gd.changed = oldchanged; - resolve(gd); + // Only resolve if a new call hasn't been made! + if(gd._resolveResize === resolve) { + delete gd._resolveResize; + resolve(gd); + } }); }, 100); }); + + if(resolveLastResize) resolveLastResize(p); + return p; }; @@ -63281,6 +63309,15 @@ plots.doCalcdata = function(gd, traces) { ); } + // clear relinked cmin/cmax values in shared axes to start aggregation from scratch + for(var k in fullLayout._colorAxes) { + var cOpts = fullLayout[k]; + if(cOpts.cauto !== false) { + delete cOpts.cmin; + delete cOpts.cmax; + } + } + var hasCalcTransform = false; function transformCalci(i) { @@ -63741,7 +63778,7 @@ module.exports = { } }; -},{"../../../lib/extend":164,"../../../traces/scatter/attributes":376}],247:[function(_dereq_,module,exports){ +},{"../../../lib/extend":164,"../../../traces/scatter/attributes":377}],247:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68217,7 +68254,7 @@ module.exports = { } }; -},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"../scatter/attributes":376,"./constants":270}],269:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"../scatter/attributes":377,"./constants":270}],269:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68282,7 +68319,7 @@ module.exports = function calc(gd, trace) { return cd; }; -},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"../../plots/cartesian/axes":213,"../scatter/calc_selection":378,"./arrays_to_calcdata":267}],270:[function(_dereq_,module,exports){ +},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"../../plots/cartesian/axes":213,"../scatter/calc_selection":379,"./arrays_to_calcdata":267}],270:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68295,8 +68332,13 @@ module.exports = function calc(gd, trace) { 'use strict'; module.exports = { - TEXTPAD: 3, // padding in pixels around text - eventDataKeys: [] + // padding in pixels around text + TEXTPAD: 3, + // 'value' and 'label' are not really necessary for bar traces, + // but they were made available to `texttemplate` (maybe by accident) + // via tokens `%{value}` and `%{label}` starting in 1.50.0, + // so let's include them in the event data also. + eventDataKeys: ['value', 'label'] }; },{}],271:[function(_dereq_,module,exports){ @@ -69067,7 +69109,7 @@ module.exports = { setGroupPositions: setGroupPositions }; -},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216,"../../registry":258,"./sieve.js":280,"fast-isnumeric":18}],272:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216,"../../registry":258,"./sieve.js":281,"fast-isnumeric":18}],272:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69241,7 +69283,36 @@ module.exports = { handleText: handleText }; -},{"../../components/color":51,"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":258,"../scatter/xy_defaults":401,"./attributes":268,"./style_defaults":282}],273:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":258,"../scatter/xy_defaults":403,"./attributes":268,"./style_defaults":283}],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'; + +module.exports = function eventData(out, pt, trace) { + // standard cartesian event data + out.x = 'xVal' in pt ? pt.xVal : pt.x; + out.y = 'yVal' in pt ? pt.yVal : pt.y; + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + if(trace.orientation === 'h') { + out.label = out.y; + out.value = out.x; + } else { + out.label = out.x; + out.value = out.y; + } + + return out; +}; + +},{}],274:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69319,7 +69390,7 @@ exports.getLineWidth = function(trace, di) { return w; }; -},{"../../lib":169,"fast-isnumeric":18,"tinycolor2":34}],274:[function(_dereq_,module,exports){ +},{"../../lib":169,"fast-isnumeric":18,"tinycolor2":34}],275:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69337,6 +69408,7 @@ var Color = _dereq_('../../components/color'); var fillText = _dereq_('../../lib').fillText; var getLineWidth = _dereq_('./helpers').getLineWidth; +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; function hoverPoints(pointData, xval, yval, hovermode) { var barPointData = hoverOnBars(pointData, xval, yval, hovermode); @@ -69473,6 +69545,9 @@ function hoverOnBars(pointData, xval, yval, hovermode) { pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true); pointData[posLetter + 'LabelVal'] = di.p; + pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal']); + pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal']); + // 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 @@ -69500,7 +69575,7 @@ module.exports = { getTraceColor: getTraceColor }; -},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../registry":258,"./helpers":273}],275:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"./helpers":274}],276:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69525,6 +69600,7 @@ module.exports = { style: _dereq_('./style').style, styleOnSelect: _dereq_('./style').styleOnSelect, hoverPoints: _dereq_('./hover').hoverPoints, + eventData: _dereq_('./event_data'), selectPoints: _dereq_('./select'), moduleType: 'trace', @@ -69537,7 +69613,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"../scatter/marker_colorbar":393,"./arrays_to_calcdata":267,"./attributes":268,"./calc":269,"./cross_trace_calc":271,"./defaults":272,"./hover":274,"./layout_attributes":276,"./layout_defaults":277,"./plot":278,"./select":279,"./style":281}],276:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../scatter/marker_colorbar":395,"./arrays_to_calcdata":267,"./attributes":268,"./calc":269,"./cross_trace_calc":271,"./defaults":272,"./event_data":273,"./hover":275,"./layout_attributes":277,"./layout_defaults":278,"./plot":279,"./select":280,"./style":282}],277:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69585,7 +69661,7 @@ module.exports = { } }; -},{}],277:[function(_dereq_,module,exports){ +},{}],278:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69645,7 +69721,7 @@ module.exports = function(layoutIn, layoutOut, fullData) { coerce('bargroupgap'); }; -},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"./layout_attributes":276}],278:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"./layout_attributes":277}],279:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70222,31 +70298,42 @@ function calcTexttemplate(fullLayout, calcTrace, index, xa, ya) { var trace = calcTrace[0].trace; var texttemplate = Lib.castOption(trace, index, 'texttemplate'); if(!texttemplate) return ''; - var isHorizontal = (trace.orientation === 'h'); var isWaterfall = (trace.type === 'waterfall'); var isFunnel = (trace.type === 'funnel'); + var pLetter, pAxis; + var vLetter, vAxis; + if(trace.orientation === 'h') { + pLetter = 'y'; + pAxis = ya; + vLetter = 'x'; + vAxis = xa; + } else { + pLetter = 'x'; + pAxis = xa; + vLetter = 'y'; + vAxis = ya; + } + function formatLabel(u) { - 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; + return tickText(vAxis, +v, true).text; } var cdi = calcTrace[index]; var obj = {}; obj.label = cdi.p; - obj.labelLabel = formatLabel(cdi.p); + obj.labelLabel = obj[pLetter + 'Label'] = formatLabel(cdi.p); var tx = Lib.castOption(trace, cdi.i, 'text'); if(tx === 0 || tx) obj.text = tx; obj.value = cdi.s; - obj.valueLabel = formatNumber(cdi.s); + obj.valueLabel = obj[vLetter + 'Label'] = formatNumber(cdi.s); var pt = {}; appendArrayPointValue(pt, trace, cdi.i); @@ -70357,7 +70444,7 @@ module.exports = { toMoveOutsideBar: toMoveOutsideBar }; -},{"../../components/color":51,"../../components/drawing":72,"../../components/fx/helpers":86,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../registry":258,"./attributes":268,"./constants":270,"./helpers":273,"./style":281,"d3":16,"fast-isnumeric":18}],279:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../components/fx/helpers":86,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../registry":258,"./attributes":268,"./constants":270,"./helpers":274,"./style":282,"d3":16,"fast-isnumeric":18}],280:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70421,7 +70508,7 @@ function getCentroid(d, xa, ya, isHorizontal, isFunnel) { } } -},{}],280:[function(_dereq_,module,exports){ +},{}],281:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70532,7 +70619,7 @@ Sieve.prototype.getLabel = function getLabel(position, value) { return prefix + label; }; -},{"../../constants/numerical":149,"../../lib":169}],281:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169}],282:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70709,7 +70796,7 @@ module.exports = { getBarColor: getBarColor }; -},{"../../components/color":51,"../../components/drawing":72,"../../lib":169,"../../registry":258,"./attributes":268,"./helpers":273,"d3":16}],282:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../lib":169,"../../registry":258,"./attributes":268,"./helpers":274,"d3":16}],283:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70747,7 +70834,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, default coerce('unselected.marker.color'); }; -},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62}],283:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62}],284:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70959,7 +71046,7 @@ module.exports = { } }; -},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"../scatter/attributes":376}],284:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"../scatter/attributes":377}],285:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71031,6 +71118,9 @@ module.exports = function calc(gd, trace) { Lib.identity : function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); }; + var minLowerNotch = Infinity; + var maxUpperNotch = -Infinity; + // build calcdata trace items, one item per distinct position for(i = 0; i < pLen; i++) { if(ptsPerBin[i].length > 0) { @@ -71085,6 +71175,8 @@ module.exports = function calc(gd, trace) { var mci = 1.57 * iqr / Math.sqrt(bvLen); cdi.ln = cdi.med - mci; cdi.un = cdi.med + mci; + minLowerNotch = Math.min(minLowerNotch, cdi.ln); + maxUpperNotch = Math.max(maxUpperNotch, cdi.un); cdi.pts2 = pts.filter(ptFilterFn); @@ -71093,8 +71185,11 @@ module.exports = function calc(gd, trace) { } calcSelection(cd, trace); - var extremes = Axes.findExtremes(valAxis, val, {padded: true}); - trace._extremes[valAxis._id] = extremes; + + trace._extremes[valAxis._id] = Axes.findExtremes(valAxis, + trace.notched ? val.concat([minLowerNotch, maxUpperNotch]) : val, + {padded: true} + ); if(cd.length > 0) { cd[0].t = { @@ -71208,7 +71303,7 @@ function sortByVal(a, b) { return a.v - b.v; } function extractVal(o) { return o.v; } -},{"../../lib":169,"../../plots/cartesian/axes":213,"fast-isnumeric":18}],285:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"fast-isnumeric":18}],286:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71437,7 +71532,7 @@ module.exports = { setPositionOffset: setPositionOffset }; -},{"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216}],286:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216}],287:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71581,7 +71676,7 @@ module.exports = { handlePointsDefaults: handlePointsDefaults }; -},{"../../components/color":51,"../../lib":169,"../../registry":258,"../bar/defaults":272,"./attributes":283}],287:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../registry":258,"../bar/defaults":272,"./attributes":284}],288:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71606,7 +71701,7 @@ module.exports = function eventData(out, pt) { return out; }; -},{}],288:[function(_dereq_,module,exports){ +},{}],289:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71891,7 +71986,7 @@ module.exports = { hoverOnPoints: hoverOnPoints }; -},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],289:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],290:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71926,7 +72021,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"./attributes":283,"./calc":284,"./cross_trace_calc":285,"./defaults":286,"./event_data":287,"./hover":288,"./layout_attributes":290,"./layout_defaults":291,"./plot":292,"./select":293,"./style":294}],290:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":284,"./calc":285,"./cross_trace_calc":286,"./defaults":287,"./event_data":288,"./hover":289,"./layout_attributes":291,"./layout_defaults":292,"./plot":293,"./select":294,"./style":295}],291:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71967,7 +72062,7 @@ module.exports = { } }; -},{}],291:[function(_dereq_,module,exports){ +},{}],292:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72013,7 +72108,7 @@ module.exports = { _supply: _supply }; -},{"../../lib":169,"../../registry":258,"./layout_attributes":290}],292:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"./layout_attributes":291}],293:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72337,7 +72432,7 @@ module.exports = { plotBoxMean: plotBoxMean }; -},{"../../components/drawing":72,"../../lib":169,"d3":16}],293:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"d3":16}],294:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72386,7 +72481,7 @@ module.exports = function selectPoints(searchInfo, selectionTester) { return selection; }; -},{}],294:[function(_dereq_,module,exports){ +},{}],295:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72460,7 +72555,7 @@ module.exports = { styleOnSelect: styleOnSelect }; -},{"../../components/color":51,"../../components/drawing":72,"d3":16}],295:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"d3":16}],296:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72647,7 +72742,7 @@ module.exports = extendFlat({ }) ); -},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../constants/docs":146,"../../constants/filter_ops":147,"../../lib/extend":164,"../../plots/font_attributes":239,"../heatmap/attributes":317,"../scatter/attributes":376}],296:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../constants/docs":146,"../../constants/filter_ops":147,"../../lib/extend":164,"../../plots/font_attributes":239,"../heatmap/attributes":318,"../scatter/attributes":377}],297:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72700,7 +72795,7 @@ module.exports = function calc(gd, trace) { return cd; }; -},{"../../components/colorscale":63,"../heatmap/calc":318,"./end_plus":306,"./set_contours":314}],297:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../heatmap/calc":319,"./end_plus":307,"./set_contours":315}],298:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72790,7 +72885,7 @@ module.exports = function(pathinfo, contours) { } }; -},{}],298:[function(_dereq_,module,exports){ +},{}],299:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72839,7 +72934,7 @@ module.exports = { calc: calc }; -},{"../../components/colorscale":63,"./end_plus":306,"./make_color_map":311}],299:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"./end_plus":307,"./make_color_map":312}],300:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72917,7 +73012,7 @@ module.exports = { } }; -},{}],300:[function(_dereq_,module,exports){ +},{}],301:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73011,7 +73106,7 @@ function handleConstraintValueDefaults(coerce, contours) { } } -},{"../../components/color":51,"../../constants/filter_ops":147,"./label_defaults":310,"fast-isnumeric":18}],301:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../constants/filter_ops":147,"./label_defaults":311,"fast-isnumeric":18}],302:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73090,7 +73185,7 @@ function makeInequalitySettings(operation) { }; } -},{"../../constants/filter_ops":147,"fast-isnumeric":18}],302:[function(_dereq_,module,exports){ +},{"../../constants/filter_ops":147,"fast-isnumeric":18}],303:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73119,7 +73214,7 @@ module.exports = function handleContourDefaults(traceIn, traceOut, coerce, coerc if(autoContour || !contourSize) coerce('ncontours'); }; -},{}],303:[function(_dereq_,module,exports){ +},{}],304:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73219,7 +73314,7 @@ function copyPathinfo(pi) { }); } -},{"../../lib":169}],304:[function(_dereq_,module,exports){ +},{"../../lib":169}],305:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73270,7 +73365,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } }; -},{"../../lib":169,"../heatmap/xyz_defaults":331,"./attributes":295,"./constraint_defaults":300,"./contours_defaults":302,"./style_defaults":316}],305:[function(_dereq_,module,exports){ +},{"../../lib":169,"../heatmap/xyz_defaults":332,"./attributes":296,"./constraint_defaults":301,"./contours_defaults":303,"./style_defaults":317}],306:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73334,7 +73429,7 @@ module.exports = function emptyPathinfo(contours, plotinfo, cd0) { return pathinfo; }; -},{"../../lib":169,"./constraint_mapping":301,"./end_plus":306}],306:[function(_dereq_,module,exports){ +},{"../../lib":169,"./constraint_mapping":302,"./end_plus":307}],307:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73354,7 +73449,7 @@ module.exports = function endPlus(contours) { return contours.end + contours.size / 1e6; }; -},{}],307:[function(_dereq_,module,exports){ +},{}],308:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73648,7 +73743,7 @@ function getInterpPx(pi, loc, step) { } } -},{"../../lib":169,"./constants":299}],308:[function(_dereq_,module,exports){ +},{"../../lib":169,"./constants":300}],309:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73683,7 +73778,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay return hoverData; }; -},{"../../components/color":51,"../heatmap/hover":324}],309:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../heatmap/hover":325}],310:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73712,7 +73807,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"./attributes":295,"./calc":296,"./colorbar":298,"./defaults":304,"./hover":308,"./plot":313,"./style":315}],310:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":296,"./calc":297,"./colorbar":299,"./defaults":305,"./hover":309,"./plot":314,"./style":316}],311:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73742,7 +73837,7 @@ module.exports = function handleLabelDefaults(coerce, layout, lineColor, opts) { if(opts.hasHover !== false) coerce('zhoverformat'); }; -},{"../../lib":169}],311:[function(_dereq_,module,exports){ +},{"../../lib":169}],312:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73826,7 +73921,7 @@ module.exports = function makeColorMap(trace) { ); }; -},{"../../components/colorscale":63,"./end_plus":306,"d3":16}],312:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"./end_plus":307,"d3":16}],313:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73918,7 +74013,7 @@ function getMarchingIndex(val, corners) { return (mi === 15) ? 0 : mi; } -},{"./constants":299}],313:[function(_dereq_,module,exports){ +},{"./constants":300}],314:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74602,7 +74697,7 @@ function makeClipMask(cd0) { return z; } -},{"../../components/colorscale":63,"../../components/drawing":72,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../plots/cartesian/set_convert":231,"../heatmap/plot":328,"./close_boundaries":297,"./constants":299,"./convert_to_constraints":303,"./empty_pathinfo":305,"./find_all_paths":307,"./make_crossings":312,"d3":16}],314:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../../components/drawing":72,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../plots/cartesian/set_convert":231,"../heatmap/plot":329,"./close_boundaries":298,"./constants":300,"./convert_to_constraints":304,"./empty_pathinfo":306,"./find_all_paths":308,"./make_crossings":313,"d3":16}],315:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74706,7 +74801,7 @@ function autoContours(start, end, ncontours) { return dummyAx; } -},{"../../lib":169,"../../plots/cartesian/axes":213}],315:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213}],316:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74787,7 +74882,7 @@ module.exports = function style(gd) { heatmapStyle(gd); }; -},{"../../components/drawing":72,"../heatmap/style":329,"./make_color_map":311,"d3":16}],316:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../heatmap/style":330,"./make_color_map":312,"d3":16}],317:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74832,7 +74927,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, handleLabelDefaults(coerce, layout, lineColor, opts); }; -},{"../../components/colorscale/defaults":61,"./label_defaults":310}],317:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"./label_defaults":311}],318:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74945,7 +75040,7 @@ module.exports = extendFlat({ colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); -},{"../../components/colorscale/attributes":58,"../../constants/docs":146,"../../lib/extend":164,"../../plots/template_attributes":253,"../scatter/attributes":376}],318:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../constants/docs":146,"../../lib/extend":164,"../../plots/template_attributes":253,"../scatter/attributes":377}],319:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75105,7 +75200,7 @@ module.exports = function calc(gd, trace) { return [cd0]; }; -},{"../../components/colorscale/calc":59,"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"../histogram2d/calc":346,"./clean_2d_array":319,"./convert_column_xyz":321,"./find_empties":323,"./interp2d":326,"./make_bound_array":327}],319:[function(_dereq_,module,exports){ +},{"../../components/colorscale/calc":59,"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"../histogram2d/calc":347,"./clean_2d_array":320,"./convert_column_xyz":322,"./find_empties":324,"./interp2d":327,"./make_bound_array":328}],320:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75182,7 +75277,7 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { return zNew; }; -},{"../../constants/numerical":149,"../../lib":169,"fast-isnumeric":18}],320:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"fast-isnumeric":18}],321:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75198,7 +75293,7 @@ module.exports = { max: 'zmax' }; -},{}],321:[function(_dereq_,module,exports){ +},{}],322:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75276,7 +75371,7 @@ module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, } }; -},{"../../constants/numerical":149,"../../lib":169}],322:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169}],323:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75319,7 +75414,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); }; -},{"../../components/colorscale/defaults":61,"../../lib":169,"./attributes":317,"./style_defaults":330,"./xyz_defaults":331}],323:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"../../lib":169,"./attributes":318,"./style_defaults":331,"./xyz_defaults":332}],324:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75424,7 +75519,7 @@ module.exports = function findEmpties(z) { return empties.sort(function(a, b) { return b[2] - a[2]; }); }; -},{"../../lib":169}],324:[function(_dereq_,module,exports){ +},{"../../lib":169}],325:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75555,7 +75650,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay })]; }; -},{"../../components/colorscale":63,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],325:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],326:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75584,7 +75679,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"./attributes":317,"./calc":318,"./colorbar":320,"./defaults":322,"./hover":324,"./plot":328,"./style":329}],326:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":318,"./calc":319,"./colorbar":321,"./defaults":323,"./hover":325,"./plot":329,"./style":330}],327:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75717,7 +75812,7 @@ function iterateInterp2d(z, emptyPoints, overshoot) { return maxFractionalChange; } -},{"../../lib":169}],327:[function(_dereq_,module,exports){ +},{"../../lib":169}],328:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75805,7 +75900,7 @@ module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, return arrayOut; }; -},{"../../lib":169,"../../registry":258}],328:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],329:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76210,7 +76305,7 @@ function putColor(pixels, pxIndex, c) { pixels[pxIndex + 3] = Math.round(c[3] * 255); } -},{"../../components/colorscale":63,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../registry":258,"d3":16,"tinycolor2":34}],329:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../registry":258,"d3":16,"tinycolor2":34}],330:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76231,7 +76326,7 @@ module.exports = function style(gd) { }); }; -},{"d3":16}],330:[function(_dereq_,module,exports){ +},{"d3":16}],331:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76254,7 +76349,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce) { coerce('zhoverformat'); }; -},{}],331:[function(_dereq_,module,exports){ +},{}],332:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76351,7 +76446,7 @@ function isValidZ(z) { return (allRowsAreArrays && oneRowIsFilled && hasOneNumber); } -},{"../../lib":169,"../../registry":258,"fast-isnumeric":18}],332:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"fast-isnumeric":18}],333:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76492,7 +76587,7 @@ module.exports = { } }; -},{"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"./bin_attributes":334,"./constants":338}],333:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"./bin_attributes":335,"./constants":339}],334:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76517,7 +76612,7 @@ module.exports = function doAvg(size, counts) { return total; }; -},{}],334:[function(_dereq_,module,exports){ +},{}],335:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76552,7 +76647,7 @@ module.exports = function makeBinAttrs(axLetter, match) { }; }; -},{}],335:[function(_dereq_,module,exports){ +},{}],336:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76626,7 +76721,7 @@ module.exports = { } }; -},{"fast-isnumeric":18}],336:[function(_dereq_,module,exports){ +},{"fast-isnumeric":18}],337:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76802,7 +76897,7 @@ function dateParts(v, pa, calendar) { return parts; } -},{"../../constants/numerical":149,"../../plots/cartesian/axes":213}],337:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../plots/cartesian/axes":213}],338:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77359,7 +77454,7 @@ module.exports = { calcAllAutoBins: calcAllAutoBins }; -},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"../bar/arrays_to_calcdata":267,"./average":333,"./bin_functions":335,"./bin_label_vals":336,"./norm_functions":344,"fast-isnumeric":18}],338:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"../bar/arrays_to_calcdata":267,"./average":334,"./bin_functions":336,"./bin_label_vals":337,"./norm_functions":345,"fast-isnumeric":18}],339:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77375,7 +77470,7 @@ module.exports = { eventDataKeys: ['binNumber'] }; -},{}],339:[function(_dereq_,module,exports){ +},{}],340:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77653,7 +77748,7 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { } }; -},{"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":258,"../bar/defaults":272}],340:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":258,"../bar/defaults":272}],341:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77727,7 +77822,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'}); }; -},{"../../components/color":51,"../../lib":169,"../../registry":258,"../bar/style_defaults":282,"./attributes":332}],341:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../registry":258,"../bar/style_defaults":283,"./attributes":333}],342:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77776,7 +77871,7 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) { return out; }; -},{}],342:[function(_dereq_,module,exports){ +},{}],343:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77811,7 +77906,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { return pts; }; -},{"../../plots/cartesian/axes":213,"../bar/hover":274}],343:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/axes":213,"../bar/hover":275}],344:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77861,7 +77956,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"../bar/cross_trace_calc":271,"../bar/layout_attributes":276,"../bar/layout_defaults":277,"../bar/plot":278,"../bar/select":279,"../bar/style":281,"../scatter/marker_colorbar":393,"./attributes":332,"./calc":337,"./cross_trace_defaults":339,"./defaults":340,"./event_data":341,"./hover":342}],344:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../bar/cross_trace_calc":271,"../bar/layout_attributes":277,"../bar/layout_defaults":278,"../bar/plot":279,"../bar/select":280,"../bar/style":282,"../scatter/marker_colorbar":395,"./attributes":333,"./calc":338,"./cross_trace_defaults":340,"./defaults":341,"./event_data":342,"./hover":343}],345:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77896,7 +77991,7 @@ module.exports = { } }; -},{}],345:[function(_dereq_,module,exports){ +},{}],346:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77962,7 +78057,7 @@ module.exports = extendFlat( colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); -},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../../plots/template_attributes":253,"../heatmap/attributes":317,"../histogram/attributes":332,"../histogram/bin_attributes":334}],346:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../../plots/template_attributes":253,"../heatmap/attributes":318,"../histogram/attributes":333,"../histogram/bin_attributes":335}],347:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78181,7 +78276,7 @@ function getRanges(edges, uniqueVals, gapLow, gapHigh, ax, calendar) { return out; } -},{"../../lib":169,"../../plots/cartesian/axes":213,"../histogram/average":333,"../histogram/bin_functions":335,"../histogram/bin_label_vals":336,"../histogram/calc":337,"../histogram/norm_functions":344}],347:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../histogram/average":334,"../histogram/bin_functions":336,"../histogram/bin_label_vals":337,"../histogram/calc":338,"../histogram/norm_functions":345}],348:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78214,7 +78309,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertemplate'); }; -},{"../../components/colorscale/defaults":61,"../../lib":169,"../heatmap/style_defaults":330,"./attributes":345,"./sample_defaults":350}],348:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"../../lib":169,"../heatmap/style_defaults":331,"./attributes":346,"./sample_defaults":351}],349:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78248,7 +78343,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay return pts; }; -},{"../../plots/cartesian/axes":213,"../heatmap/hover":324}],349:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/axes":213,"../heatmap/hover":325}],350:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78282,7 +78377,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"../heatmap/calc":318,"../heatmap/colorbar":320,"../heatmap/plot":328,"../heatmap/style":329,"../histogram/cross_trace_defaults":339,"../histogram/event_data":341,"./attributes":345,"./defaults":347,"./hover":348}],350:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../heatmap/calc":319,"../heatmap/colorbar":321,"../heatmap/plot":329,"../heatmap/style":330,"../histogram/cross_trace_defaults":340,"../histogram/event_data":342,"./attributes":346,"./defaults":348,"./hover":349}],351:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78327,7 +78422,7 @@ module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout coerce('autobiny'); }; -},{"../../lib":169,"../../registry":258}],351:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],352:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78385,7 +78480,7 @@ module.exports = extendFlat({ }) ); -},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../contour/attributes":295,"../histogram2d/attributes":345}],352:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../contour/attributes":296,"../histogram2d/attributes":346}],353:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78422,7 +78517,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertemplate'); }; -},{"../../lib":169,"../contour/contours_defaults":302,"../contour/style_defaults":316,"../histogram2d/sample_defaults":350,"./attributes":351}],353:[function(_dereq_,module,exports){ +},{"../../lib":169,"../contour/contours_defaults":303,"../contour/style_defaults":317,"../histogram2d/sample_defaults":351,"./attributes":352}],354:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78454,7 +78549,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"../contour/calc":296,"../contour/colorbar":298,"../contour/hover":308,"../contour/plot":313,"../contour/style":315,"../histogram/cross_trace_defaults":339,"./attributes":351,"./defaults":352}],354:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../contour/calc":297,"../contour/colorbar":299,"../contour/hover":309,"../contour/plot":314,"../contour/style":316,"../histogram/cross_trace_defaults":340,"./attributes":352,"./defaults":353}],355:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78465,7 +78560,7 @@ module.exports = { 'use strict'; -var plotAttrs = _dereq_('../../plots/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var extendFlat = _dereq_('../../lib/extend').extendFlat; var colormodel = _dereq_('./constants').colormodel; @@ -78555,7 +78650,7 @@ module.exports = extendFlat({ editType: 'plot', }, - hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { flags: ['x', 'y', 'z', 'color', 'name', 'text'], dflt: 'x+y+z+text+name' }), @@ -78566,7 +78661,7 @@ module.exports = extendFlat({ transforms: undefined }); -},{"../../lib/extend":164,"../../plots/attributes":210,"../../plots/template_attributes":253,"./constants":356}],355:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plots/attributes":210,"../../plots/template_attributes":253,"./constants":357}],356:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78654,7 +78749,7 @@ function makeScaler(trace) { }; } -},{"../../lib":169,"../../plots/cartesian/axes":213,"./constants":356,"fast-isnumeric":18}],356:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"./constants":357,"fast-isnumeric":18}],357:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78704,7 +78799,7 @@ module.exports = { } }; -},{}],357:[function(_dereq_,module,exports){ +},{}],358:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78745,7 +78840,7 @@ module.exports = function supplyDefaults(traceIn, traceOut) { traceOut._length = null; }; -},{"../../lib":169,"./attributes":354,"./constants":356}],358:[function(_dereq_,module,exports){ +},{"../../lib":169,"./attributes":355,"./constants":357}],359:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78766,7 +78861,7 @@ module.exports = function eventData(out, pt) { return out; }; -},{}],359:[function(_dereq_,module,exports){ +},{}],360:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78858,7 +78953,7 @@ module.exports = function hoverPoints(pointData, xval, yval) { })]; }; -},{"../../components/fx":89,"../../lib":169,"./constants":356}],360:[function(_dereq_,module,exports){ +},{"../../components/fx":89,"../../lib":169,"./constants":357}],361:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78888,7 +78983,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"./attributes":354,"./calc":355,"./defaults":357,"./event_data":358,"./hover":359,"./plot":361,"./style":362}],361:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":355,"./calc":356,"./defaults":358,"./event_data":359,"./hover":360,"./plot":362,"./style":363}],362:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79019,7 +79114,7 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { }); }; -},{"../../constants/xmlns_namespaces":150,"../../lib":169,"./constants":356,"d3":16}],362:[function(_dereq_,module,exports){ +},{"../../constants/xmlns_namespaces":150,"../../lib":169,"./constants":357,"d3":16}],363:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79039,7 +79134,7 @@ module.exports = function style(gd) { }); }; -},{"d3":16}],363:[function(_dereq_,module,exports){ +},{"d3":16}],364:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79050,7 +79145,7 @@ module.exports = function style(gd) { 'use strict'; -var plotAttrs = _dereq_('../../plots/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); var domainAttrs = _dereq_('../../plots/domain').attributes; var fontAttrs = _dereq_('../../plots/font_attributes'); var colorAttrs = _dereq_('../../components/color/attributes'); @@ -79159,7 +79254,7 @@ module.exports = { editType: 'calc', }, - hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { flags: ['label', 'text', 'value', 'percent', 'name'] }), hovertemplate: hovertemplateAttrs({}, { @@ -79302,7 +79397,7 @@ module.exports = { } }; -},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/domain":238,"../../plots/font_attributes":239,"../../plots/template_attributes":253}],364:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/domain":238,"../../plots/font_attributes":239,"../../plots/template_attributes":253}],365:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79325,7 +79420,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout); }; -},{"../../plots/plots":245}],365:[function(_dereq_,module,exports){ +},{"../../plots/plots":245}],366:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79509,7 +79604,7 @@ module.exports = { generateExtendedColors: generateExtendedColors }; -},{"../../components/color":51,"../../lib":169,"fast-isnumeric":18,"tinycolor2":34}],366:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"fast-isnumeric":18,"tinycolor2":34}],367:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79600,7 +79695,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('pull'); }; -},{"../../lib":169,"../../plots/domain":238,"../bar/defaults":272,"./attributes":363}],367:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/domain":238,"../bar/defaults":272,"./attributes":364}],368:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79649,7 +79744,7 @@ module.exports = function eventData(pt, trace) { return out; }; -},{"../../components/fx/helpers":86}],368:[function(_dereq_,module,exports){ +},{"../../components/fx/helpers":86}],369:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79691,7 +79786,7 @@ exports.castOption = function castOption(item, indices) { else if(item) return item; }; -},{"../../lib":169}],369:[function(_dereq_,module,exports){ +},{"../../lib":169}],370:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79724,7 +79819,7 @@ module.exports = { } }; -},{"./attributes":363,"./base_plot":364,"./calc":365,"./defaults":366,"./layout_attributes":370,"./layout_defaults":371,"./plot":372,"./style":373,"./style_one":374}],370:[function(_dereq_,module,exports){ +},{"./attributes":364,"./base_plot":365,"./calc":366,"./defaults":367,"./layout_attributes":371,"./layout_defaults":372,"./plot":373,"./style":374,"./style_one":375}],371:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79757,7 +79852,7 @@ module.exports = { } }; -},{}],371:[function(_dereq_,module,exports){ +},{}],372:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79782,7 +79877,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { coerce('extendpiecolors'); }; -},{"../../lib":169,"./layout_attributes":370}],372:[function(_dereq_,module,exports){ +},{"../../lib":169,"./layout_attributes":371}],373:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80820,7 +80915,7 @@ module.exports = { attachFxHandlers: attachFxHandlers, }; -},{"../../components/color":51,"../../components/drawing":72,"../../components/fx":89,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"./event_data":367,"./helpers":368,"d3":16}],373:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../components/fx":89,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"./event_data":368,"./helpers":369,"d3":16}],374:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80849,7 +80944,7 @@ module.exports = function style(gd) { }); }; -},{"./style_one":374,"d3":16}],374:[function(_dereq_,module,exports){ +},{"./style_one":375,"d3":16}],375:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80873,7 +80968,7 @@ module.exports = function styleOne(s, pt, trace) { .call(Color.stroke, lineColor); }; -},{"../../components/color":51,"./helpers":368}],375:[function(_dereq_,module,exports){ +},{"../../components/color":51,"./helpers":369}],376:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80925,7 +81020,7 @@ module.exports = function arraysToCalcdata(cd, trace) { } }; -},{"../../lib":169}],376:[function(_dereq_,module,exports){ +},{"../../lib":169}],377:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81339,7 +81434,7 @@ module.exports = { } }; -},{"../../components/colorscale/attributes":58,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"./constants":380}],377:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"./constants":381}],378:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81624,7 +81719,7 @@ module.exports = { getStackOpts: getStackOpts }; -},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"./arrays_to_calcdata":375,"./calc_selection":378,"./colorscale_calc":379,"./subtypes":399,"fast-isnumeric":18}],378:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"./arrays_to_calcdata":376,"./calc_selection":379,"./colorscale_calc":380,"./subtypes":401,"fast-isnumeric":18}],379:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81643,7 +81738,7 @@ module.exports = function calcSelection(cd, trace) { } }; -},{"../../lib":169}],379:[function(_dereq_,module,exports){ +},{"../../lib":169}],380:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81686,7 +81781,7 @@ module.exports = function calcMarkerColorscale(gd, trace) { } }; -},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"./subtypes":399}],380:[function(_dereq_,module,exports){ +},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"./subtypes":401}],381:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81715,7 +81810,7 @@ module.exports = { eventDataKeys: [] }; -},{}],381:[function(_dereq_,module,exports){ +},{}],382:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81896,7 +81991,7 @@ function getInterp(calcTrace, index, position, posAttr) { return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]); } -},{"./calc":377}],382:[function(_dereq_,module,exports){ +},{"./calc":378}],383:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81935,7 +82030,7 @@ module.exports = function crossTraceDefaults(fullData) { } }; -},{}],383:[function(_dereq_,module,exports){ +},{}],384:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82025,7 +82120,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout Lib.coerceSelectionMarkerOpacity(traceOut, coerce); }; -},{"../../lib":169,"../../registry":258,"./attributes":376,"./constants":380,"./fillcolor_defaults":384,"./line_defaults":388,"./line_shape_defaults":390,"./marker_defaults":394,"./stack_defaults":397,"./subtypes":399,"./text_defaults":400,"./xy_defaults":401}],384:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"./attributes":377,"./constants":381,"./fillcolor_defaults":385,"./line_defaults":390,"./line_shape_defaults":392,"./marker_defaults":396,"./stack_defaults":399,"./subtypes":401,"./text_defaults":402,"./xy_defaults":403}],385:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82062,7 +82157,33 @@ module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coe )); }; -},{"../../components/color":51,"../../lib":169}],385:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169}],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 Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function formatLabels(cdi, trace, fullLayout) { + var labels = {}; + + var mockGd = {_fullLayout: fullLayout}; + var xa = Axes.getFromTrace(mockGd, trace, 'x'); + var ya = Axes.getFromTrace(mockGd, trace, 'y'); + + labels.xLabel = Axes.tickText(xa, cdi.x, true).text; + labels.yLabel = Axes.tickText(ya, cdi.y, true).text; + + return labels; +}; + +},{"../../plots/cartesian/axes":213}],387:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82111,7 +82232,7 @@ module.exports = function getTraceColor(trace, di) { } }; -},{"../../components/color":51,"./subtypes":399}],386:[function(_dereq_,module,exports){ +},{"../../components/color":51,"./subtypes":401}],388:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82306,7 +82427,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { } }; -},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../registry":258,"./get_trace_color":385}],387:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../registry":258,"./get_trace_color":387}],389:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82333,6 +82454,7 @@ module.exports = { arraysToCalcdata: _dereq_('./arrays_to_calcdata'), plot: _dereq_('./plot'), colorbar: _dereq_('./marker_colorbar'), + formatLabels: _dereq_('./format_labels'), style: _dereq_('./style').style, styleOnSelect: _dereq_('./style').styleOnSelect, hoverPoints: _dereq_('./hover'), @@ -82351,7 +82473,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"./arrays_to_calcdata":375,"./attributes":376,"./calc":377,"./cross_trace_calc":381,"./cross_trace_defaults":382,"./defaults":383,"./hover":386,"./marker_colorbar":393,"./plot":395,"./select":396,"./style":398,"./subtypes":399}],388:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./arrays_to_calcdata":376,"./attributes":377,"./calc":378,"./cross_trace_calc":382,"./cross_trace_defaults":383,"./defaults":384,"./format_labels":386,"./hover":388,"./marker_colorbar":395,"./plot":397,"./select":398,"./style":400,"./subtypes":401}],390:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82382,7 +82504,7 @@ module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, if(!(opts || {}).noDash) coerce('line.dash'); }; -},{"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"../../lib":169}],389:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"../../lib":169}],391:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82843,7 +82965,7 @@ module.exports = function linePoints(d, opts) { return segments; }; -},{"../../constants/numerical":149,"../../lib":169,"./constants":380}],390:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"./constants":381}],392:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82862,7 +82984,7 @@ module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) { if(shape === 'spline') coerce('line.smoothing'); }; -},{}],391:[function(_dereq_,module,exports){ +},{}],393:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82952,7 +83074,7 @@ module.exports = function linkTraces(gd, plotinfo, cdscatter) { return cdscatterSorted; }; -},{}],392:[function(_dereq_,module,exports){ +},{}],394:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82994,7 +83116,7 @@ module.exports = function makeBubbleSizeFn(trace) { }; }; -},{"fast-isnumeric":18}],393:[function(_dereq_,module,exports){ +},{"fast-isnumeric":18}],395:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83012,7 +83134,7 @@ module.exports = { max: 'cmax' }; -},{}],394:[function(_dereq_,module,exports){ +},{}],396:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83093,7 +83215,7 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout } }; -},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"./subtypes":399}],395:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"./subtypes":401}],397:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83654,7 +83776,7 @@ function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) { }); } -},{"../../components/drawing":72,"../../lib":169,"../../lib/polygon":181,"../../registry":258,"./line_points":389,"./link_traces":391,"./subtypes":399,"d3":16}],396:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"../../lib/polygon":181,"../../registry":258,"./line_points":391,"./link_traces":393,"./subtypes":401,"d3":16}],398:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83708,7 +83830,7 @@ module.exports = function selectPoints(searchInfo, selectionTester) { return selection; }; -},{"./subtypes":399}],397:[function(_dereq_,module,exports){ +},{"./subtypes":401}],399:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83813,7 +83935,7 @@ module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) } }; -},{}],398:[function(_dereq_,module,exports){ +},{}],400:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83884,7 +84006,7 @@ module.exports = { styleOnSelect: styleOnSelect }; -},{"../../components/drawing":72,"../../registry":258,"d3":16}],399:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../registry":258,"d3":16}],401:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83923,7 +84045,7 @@ module.exports = { } }; -},{"../../lib":169}],400:[function(_dereq_,module,exports){ +},{"../../lib":169}],402:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83953,7 +84075,7 @@ module.exports = function(traceIn, traceOut, layout, coerce, opts) { } }; -},{"../../lib":169}],401:[function(_dereq_,module,exports){ +},{"../../lib":169}],403:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83997,7 +84119,7 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { return len; }; -},{"../../lib":169,"../../registry":258}],402:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],404:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84011,7 +84133,7 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; var scatterAttrs = _dereq_('../scatter/attributes'); -var plotAttrs = _dereq_('../../plots/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); var dash = _dereq_('../../components/drawing/attributes').dash; @@ -84098,14 +84220,14 @@ module.exports = { selected: scatterAttrs.selected, unselected: scatterAttrs.unselected, - hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { flags: ['a', 'b', 'c', 'text', 'name'] }), hoveron: scatterAttrs.hoveron, hovertemplate: hovertemplateAttrs(), }; -},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/template_attributes":253,"../scatter/attributes":376}],403:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/template_attributes":253,"../scatter/attributes":377}],405:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84186,7 +84308,7 @@ module.exports = function calc(gd, trace) { return cd; }; -},{"../scatter/arrays_to_calcdata":375,"../scatter/calc":377,"../scatter/calc_selection":378,"../scatter/colorscale_calc":379,"fast-isnumeric":18}],404:[function(_dereq_,module,exports){ +},{"../scatter/arrays_to_calcdata":376,"../scatter/calc":378,"../scatter/calc_selection":379,"../scatter/colorscale_calc":380,"fast-isnumeric":18}],406:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84290,7 +84412,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout Lib.coerceSelectionMarkerOpacity(traceOut, coerce); }; -},{"../../lib":169,"../scatter/constants":380,"../scatter/fillcolor_defaults":384,"../scatter/line_defaults":388,"../scatter/line_shape_defaults":390,"../scatter/marker_defaults":394,"../scatter/subtypes":399,"../scatter/text_defaults":400,"./attributes":402}],405:[function(_dereq_,module,exports){ +},{"../../lib":169,"../scatter/constants":381,"../scatter/fillcolor_defaults":385,"../scatter/line_defaults":390,"../scatter/line_shape_defaults":392,"../scatter/marker_defaults":396,"../scatter/subtypes":401,"../scatter/text_defaults":402,"./attributes":404}],407:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84322,7 +84444,7 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) { return out; }; -},{}],406:[function(_dereq_,module,exports){ +},{}],408:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84331,12 +84453,33 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) { * LICENSE file in the root directory of this source tree. */ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function formatLabels(cdi, trace, fullLayout) { + var labels = {}; + + var subplot = fullLayout[trace.subplot]._subplot; + labels.aLabel = Axes.tickText(subplot.aaxis, cdi.a, true).text; + labels.bLabel = Axes.tickText(subplot.baxis, cdi.b, true).text; + labels.cLabel = Axes.tickText(subplot.caxis, cdi.c, true).text; + + return labels; +}; + +},{"../../plots/cartesian/axes":213}],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 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); @@ -84365,6 +84508,8 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { } var cdi = newPointData.cd[newPointData.index]; + var trace = newPointData.trace; + var subplot = newPointData.subplot; newPointData.a = cdi.a; newPointData.b = cdi.b; @@ -84373,12 +84518,13 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { 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 fullLayout = {}; + fullLayout[trace.subplot] = {_subplot: subplot}; + var labels = trace._module.formatLabels(cdi, trace, fullLayout); + newPointData.aLabel = labels.aLabel; + newPointData.bLabel = labels.bLabel; + newPointData.cLabel = labels.cLabel; - var trace = newPointData.trace; var hoverinfo = cdi.hi || trace.hoverinfo; var text = []; function textPart(ax, val) { @@ -84387,16 +84533,16 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { 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); + if(parts.indexOf('a') !== -1) textPart(subplot.aaxis, newPointData.aLabel); + if(parts.indexOf('b') !== -1) textPart(subplot.baxis, newPointData.bLabel); + if(parts.indexOf('c') !== -1) textPart(subplot.caxis, newPointData.cLabel); } newPointData.extraText = text.join('
    '); newPointData.hovertemplate = trace.hovertemplate; return scatterPointData; }; -},{"../../plots/cartesian/axes":213,"../scatter/hover":386}],407:[function(_dereq_,module,exports){ +},{"../scatter/hover":388}],410:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84411,6 +84557,7 @@ module.exports = { attributes: _dereq_('./attributes'), supplyDefaults: _dereq_('./defaults'), colorbar: _dereq_('../scatter/marker_colorbar'), + formatLabels: _dereq_('./format_labels'), calc: _dereq_('./calc'), plot: _dereq_('./plot'), style: _dereq_('../scatter/style').style, @@ -84429,7 +84576,7 @@ module.exports = { } }; -},{"../../plots/ternary":254,"../scatter/marker_colorbar":393,"../scatter/select":396,"../scatter/style":398,"./attributes":402,"./calc":403,"./defaults":404,"./event_data":405,"./hover":406,"./plot":408}],408:[function(_dereq_,module,exports){ +},{"../../plots/ternary":254,"../scatter/marker_colorbar":395,"../scatter/select":398,"../scatter/style":400,"./attributes":404,"./calc":405,"./defaults":406,"./event_data":407,"./format_labels":408,"./hover":409,"./plot":411}],411:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84462,7 +84609,7 @@ module.exports = function plot(gd, ternary, moduleCalcData) { scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer); }; -},{"../scatter/plot":395}],409:[function(_dereq_,module,exports){ +},{"../scatter/plot":397}],412:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84661,7 +84808,7 @@ module.exports = { } }; -},{"../../lib/extend":164,"../box/attributes":283}],410:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../box/attributes":284}],413:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84837,7 +84984,7 @@ function calcSpan(trace, cdi, valAxis, bandwidth) { return spanOut; } -},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"../box/calc":284,"./helpers":413}],411:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"../box/calc":285,"./helpers":416}],414:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84880,7 +85027,7 @@ module.exports = function crossTraceCalc(gd, plotinfo) { } }; -},{"../box/cross_trace_calc":285}],412:[function(_dereq_,module,exports){ +},{"../box/cross_trace_calc":286}],415:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -84941,7 +85088,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout if(!meanLineVisible) traceOut.meanline = {visible: false}; }; -},{"../../components/color":51,"../../lib":169,"../box/defaults":286,"./attributes":409}],413:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../box/defaults":287,"./attributes":412}],416:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85014,7 +85161,7 @@ exports.getKdeValue = function(calcItem, trace, valueDist) { exports.extractVal = function(o) { return o.v; }; -},{"../../lib":169}],414:[function(_dereq_,module,exports){ +},{"../../lib":169}],417:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85126,7 +85273,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay return closeData; }; -},{"../../lib":169,"../../plots/cartesian/axes":213,"../box/hover":288,"./helpers":413}],415:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../box/hover":289,"./helpers":416}],418:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85160,7 +85307,7 @@ module.exports = { } }; -},{"../../plots/cartesian":224,"../box/defaults":286,"../box/select":293,"../scatter/style":398,"./attributes":409,"./calc":410,"./cross_trace_calc":411,"./defaults":412,"./hover":414,"./layout_attributes":416,"./layout_defaults":417,"./plot":418,"./style":419}],416:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../box/defaults":287,"../box/select":294,"../scatter/style":400,"./attributes":412,"./calc":413,"./cross_trace_calc":414,"./defaults":415,"./hover":417,"./layout_attributes":419,"./layout_defaults":420,"./plot":421,"./style":422}],419:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85186,7 +85333,7 @@ module.exports = { }) }; -},{"../../lib":169,"../box/layout_attributes":290}],417:[function(_dereq_,module,exports){ +},{"../../lib":169,"../box/layout_attributes":291}],420:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85208,7 +85355,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { boxLayoutDefaults._supply(layoutIn, layoutOut, fullData, coerce, 'violin'); }; -},{"../../lib":169,"../box/layout_defaults":291,"./layout_attributes":416}],418:[function(_dereq_,module,exports){ +},{"../../lib":169,"../box/layout_defaults":292,"./layout_attributes":419}],421:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85398,7 +85545,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { }); }; -},{"../../components/drawing":72,"../../lib":169,"../box/plot":292,"../scatter/line_points":389,"./helpers":413,"d3":16}],419:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"../box/plot":293,"../scatter/line_points":391,"./helpers":416,"d3":16}],422:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -85453,5 +85600,5 @@ module.exports = function style(gd) { }); }; -},{"../../components/color":51,"../scatter/style":398,"d3":16}]},{},[11])(11) +},{"../../components/color":51,"../scatter/style":400,"d3":16}]},{},[11])(11) }); diff --git a/static/babybuddy/js/graph.a6254127d410.js.gz b/static/babybuddy/js/graph.a6254127d410.js.gz new file mode 100644 index 00000000..9da51fa4 Binary files /dev/null and b/static/babybuddy/js/graph.a6254127d410.js.gz differ diff --git a/static/babybuddy/js/graph.a7dcc3322745.js.gz b/static/babybuddy/js/graph.a7dcc3322745.js.gz deleted file mode 100644 index 27c21c23..00000000 Binary files a/static/babybuddy/js/graph.a7dcc3322745.js.gz and /dev/null differ diff --git a/static/babybuddy/js/graph.da32e0532ca2.js b/static/babybuddy/js/graph.da32e0532ca2.js deleted file mode 100644 index e0637637..00000000 --- a/static/babybuddy/js/graph.da32e0532ca2.js +++ /dev/null @@ -1,83990 +0,0 @@ -/** -* plotly.js (cartesian) v1.48.2 -* 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":168}],2:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = { - 'undo': { - 'width': 857.1, - 'height': 1000, - 'path': 'm857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'home': { - 'width': 928.6, - 'height': 1000, - 'path': 'm786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'camera-retro': { - 'width': 1000, - 'height': 1000, - 'path': 'm518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'zoombox': { - 'width': 1000, - 'height': 1000, - 'path': 'm1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'pan': { - 'width': 1000, - 'height': 1000, - 'path': 'm1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'zoom_plus': { - 'width': 875, - 'height': 1000, - 'path': 'm1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'zoom_minus': { - 'width': 875, - 'height': 1000, - 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'autoscale': { - 'width': 1000, - 'height': 1000, - 'path': 'm250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'tooltip_basic': { - 'width': 1500, - 'height': 1000, - 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'tooltip_compare': { - 'width': 1125, - 'height': 1000, - 'path': 'm187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'plotlylogo': { - 'width': 1542, - 'height': 1000, - 'path': 'm0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'z-axis': { - 'width': 1000, - 'height': 1000, - 'path': 'm833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - '3d_rotate': { - 'width': 1000, - 'height': 1000, - 'path': 'm922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'camera': { - 'width': 1000, - 'height': 1000, - 'path': 'm500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'movie': { - 'width': 1000, - 'height': 1000, - 'path': 'm938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'question': { - 'width': 857.1, - 'height': 1000, - 'path': 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'disk': { - 'width': 857.1, - 'height': 1000, - 'path': 'm214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - '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)' - }, - 'newplotlylogo': { - 'name': 'newplotlylogo', - 'svg': 'plotly-logomark' - } -}; - -},{}],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/bar'); - -},{"../src/traces/bar":273}],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/box'); - -},{"../src/traces/box":287}],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/traces/contour'); - -},{"../src/traces/contour":307}],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/core'); - -},{"../src/core":151}],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/heatmap'); - -},{"../src/traces/heatmap":323}],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/histogram'); - -},{"../src/traces/histogram":341}],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/histogram2d'); - -},{"../src/traces/histogram2d":347}],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'; - -module.exports = _dereq_('../src/traces/histogram2dcontour'); - -},{"../src/traces/histogram2dcontour":351}],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'; - -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":3,"./box":4,"./contour":5,"./core":6,"./heatmap":7,"./histogram":8,"./histogram2d":9,"./histogram2dcontour":10,"./pie":12,"./scatterternary":13,"./violin":14}],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/pie'); - -},{"../src/traces/pie":358}],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/scatterternary'); - -},{"../src/traces/scatterternary":396}],14:[function(_dereq_,module,exports){ -/** -* Copyright 2012-2019, Plotly, Inc. -* All rights reserved. -* -* 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":404}],15:[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); - }; -} - -},{}],16:[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; -}(); -},{}],17:[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":33}],18:[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":23}],19:[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; -}; -},{}],20:[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":22}],21:[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":22}],22:[function(_dereq_,module,exports){ -module.exports = true; -},{}],23:[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; -} - -},{}],24:[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() - } -} - -},{}],25:[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":26,"./lib/epsilon":27,"./lib/geojson":28,"./lib/intersecter":29,"./lib/segment-chainer":31,"./lib/segment-selector":32}],26:[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; - -},{}],27:[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; - -},{}],28:[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; - -},{}],29:[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":30}],30:[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; - -},{}],31:[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; - -},{}],32:[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; - -},{}],33:[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; }; - -},{}],34:[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); - -},{}],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'; - -/** - * 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 - } -]; - -},{}],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 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":202,"../../plots/cartesian/constants":218,"../../plots/font_attributes":238,"./arrow_paths":35}],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 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":168,"../../plots/cartesian/axes":212,"./draw":42}],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 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":168,"../../plot_api/plot_template":202,"../../registry":256}],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 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":168,"../color":51}],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 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":191,"fast-isnumeric":18}],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 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":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":36,"./common_defaults":39}],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 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; - - var annTextGroupInner = annTextGroup.append('g') - .style('pointer-events', textEvents ? 'all' : null) - .call(setCursor, 'pointer') - .on('click', function() { - gd._dragging = false; - - var eventData = { - index: index, - annotation: options._input, - fullAnnotation: options, - event: d3.event - }; - - if(subplotId) { - eventData.subplotId = subplotId; - } - - gd.emit('plotly_clickannotation', eventData); - }); - - 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); - }, - 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":168,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/cartesian/axes":212,"../../plots/plots":244,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"../fx":90,"./draw_arrow_head":43,"d3":16}],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 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":51,"./arrow_paths":35,"d3":16}],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 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":222,"./attributes":36,"./calc_autorange":37,"./click":38,"./convert_coords":40,"./defaults":41,"./draw":42}],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 annAtts = _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: annAtts.visible, - x: { - valType: 'any', - - - }, - y: { - valType: 'any', - - - }, - z: { - valType: 'any', - - - }, - ax: { - valType: 'number', - - - }, - ay: { - valType: 'number', - - - }, - - xanchor: annAtts.xanchor, - xshift: annAtts.xshift, - yanchor: annAtts.yanchor, - yshift: annAtts.yshift, - - text: annAtts.text, - textangle: annAtts.textangle, - font: annAtts.font, - width: annAtts.width, - height: annAtts.height, - opacity: annAtts.opacity, - align: annAtts.align, - valign: annAtts.valign, - bgcolor: annAtts.bgcolor, - bordercolor: annAtts.bordercolor, - borderpad: annAtts.borderpad, - borderwidth: annAtts.borderwidth, - showarrow: annAtts.showarrow, - arrowcolor: annAtts.arrowcolor, - arrowhead: annAtts.arrowhead, - startarrowhead: annAtts.startarrowhead, - arrowside: annAtts.arrowside, - arrowsize: annAtts.arrowsize, - startarrowsize: annAtts.startarrowsize, - arrowwidth: annAtts.arrowwidth, - standoff: annAtts.standoff, - startstandoff: annAtts.startstandoff, - hovertext: annAtts.hovertext, - hoverlabel: annAtts.hoverlabel, - captureevents: annAtts.captureevents, - - // maybes later? - // clicktoshow: annAtts.clicktoshow, - // xclick: annAtts.xclick, - // yclick: annAtts.yclick, - - // not needed! - // axref: 'pixel' - // ayref: 'pixel' - // xref: 'x' - // yref: 'y - // zref: 'z' -}), 'calc', 'from-root'); - -},{"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../annotations/attributes":36}],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'); - -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":168,"../../plots/cartesian/axes":212}],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 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":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"../annotations/common_defaults":39,"./attributes":45}],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 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":241,"../annotations/draw":42}],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'; - -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":168,"../../registry":256,"./attributes":45,"./convert":46,"./defaults":47,"./draw":48}],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'; - - -// 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); - -},{}],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 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":50,"fast-isnumeric":18,"tinycolor2":34}],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'; - -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":162,"../../plot_api/edit_types":195,"../../plots/cartesian/layout_attributes":224,"../../plots/font_attributes":238}],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'; - -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' - } -}; - -},{}],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 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":168,"../../plot_api/plot_template":202,"../../plots/cartesian/tick_label_defaults":231,"../../plots/cartesian/tick_mark_defaults":232,"../../plots/cartesian/tick_value_defaults":233,"./attributes":52}],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 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":146,"../../lib":168,"../../lib/extend":162,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_defaults":214,"../../plots/cartesian/layout_attributes":224,"../../plots/cartesian/position_defaults":227,"../../plots/plots":244,"../../registry":256,"../color":51,"../colorscale/helpers":62,"../dragelement":69,"../drawing":72,"../titles":139,"./constants":53,"d3":16,"tinycolor2":34}],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'; - -var Lib = _dereq_('../../lib'); - - -module.exports = function hasColorbar(container) { - return Lib.isPlainObject(container.colorbar); -}; - -},{"../../lib":168}],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'; - -module.exports = { - moduleType: 'component', - name: 'colorbar', - - attributes: _dereq_('./attributes'), - supplyDefaults: _dereq_('./defaults'), - - draw: _dereq_('./draw').draw, - hasColorbar: _dereq_('./has_colorbar') -}; - -},{"./attributes":52,"./defaults":54,"./draw":55,"./has_colorbar":56}],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 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":183,"../colorbar/attributes":52,"./scales.js":66}],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 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":168,"./helpers":62,"fast-isnumeric":18}],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 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 relinkColorAtts(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++) { - relinkColorAtts(trace, cbOpts[j]); - } - } else { - relinkColorAtts(trace, cbOpts); - } - } - - if(hasColorscale(trace, 'marker.line')) { - relinkColorAtts(trace, { - container: 'marker.line', - min: 'cmin', - max: 'cmax' - }); - } - } - - for(var k in fullLayout._colorAxes) { - relinkColorAtts(fullLayout[k], {min: 'cmin', max: 'cmax'}); - } -}; - -},{"../../lib":168,"./helpers":62}],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 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":168,"../../registry":256,"../colorbar/defaults":54,"../colorbar/has_colorbar":56,"./scales":66,"fast-isnumeric":18}],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 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":168,"../color":51,"./scales":66,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],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 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":58,"./calc":59,"./cross_trace_defaults":60,"./defaults":61,"./helpers":62,"./layout_attributes":64,"./layout_defaults":65,"./scales":66}],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 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":162,"./attributes":58,"./scales":66}],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 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":168,"../../plot_api/plot_template":202,"./defaults":61,"./layout_attributes":64}],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'; - -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":34}],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'; - - -// 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; -}; - -},{}],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 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":168}],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 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 interactConstants = _dereq_('../../constants/interactions'); - -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 DBLCLICKDELAY = interactConstants.DBLCLICKDELAY; - 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 < DBLCLICKDELAY) { - // 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 > DBLCLICKDELAY) { - 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 - ); -} - -},{"../../constants/interactions":148,"../../lib":168,"../../plots/cartesian/constants":218,"./align":67,"./cursor":68,"./unhover":70,"has-hover":20,"has-passive-events":21,"mouse-event-offset":24}],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'; - - -var Events = _dereq_('../../lib/events'); -var throttle = _dereq_('../../lib/throttle'); -var getGraphDiv = _dereq_('../../lib/get_graph_div'); - -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/events":161,"../../lib/get_graph_div":166,"../../lib/throttle":190,"../fx/constants":84}],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'; - -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', - -}; - -},{}],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'); -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":146,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../lib/svg_text_utils":189,"../../registry":256,"../../traces/scatter/make_bubble_size_func":381,"../../traces/scatter/subtypes":388,"../color":51,"../colorscale":63,"./symbol_defs":73,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],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'; - -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":16}],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'; - - -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', - - } - } -}; - -},{}],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'; - -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":168,"../../plots/cartesian/axes":212,"../../registry":256,"./compute_error":76,"fast-isnumeric":18}],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'; - - -/** - * 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)); - }; - } -} - -},{}],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 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":168,"../../plot_api/plot_template":202,"../../registry":256,"./attributes":74,"fast-isnumeric":18}],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 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":168,"../../plot_api/edit_types":195,"./attributes":74,"./calc":75,"./compute_error":76,"./defaults":77,"./plot":79,"./style":80}],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 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":388,"../drawing":72,"d3":16,"fast-isnumeric":18}],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 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":51,"d3":16}],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 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":162,"../../plots/font_attributes":238,"./layout_attributes":91}],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 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":168,"../../registry":256}],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'; - -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":256,"./hover":87}],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'; - -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' -}; - -},{}],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'); -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":168,"./attributes":81,"./hoverlabel_defaults":88}],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 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":168}],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 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) { - 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 + '
    '; - 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, ax, fullLayout) { - var nummoves = 0; - var axSign = 1; - var nLabels = hoverLabels.size(); - - // make groups of touching points - var pointgroups = new Array(nLabels); - - hoverLabels.each(function(d, i) { - var axis = d[ax]; - var axIsX = axis._id.charAt(0) === 'x'; - var rng = axis.range; - if(!i && rng && ((rng[0] > rng[1]) !== axIsX)) axSign = -1; - pointgroups[i] = [{ - datum: d, - i: i, - 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":168,"../../lib/events":161,"../../lib/override_cursor":179,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"./constants":84,"./helpers":86,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],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 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":168}],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'; - -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; -}; - -},{}],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 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":168,"../dragelement":69,"./attributes":81,"./calc":82,"./click":83,"./constants":84,"./defaults":85,"./helpers":86,"./hover":87,"./layout_attributes":91,"./layout_defaults":92,"./layout_global_defaults":93,"d3":16}],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 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":238,"./constants":84}],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 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":168,"./layout_attributes":91}],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 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":168,"./hoverlabel_defaults":88,"./layout_attributes":91}],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 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":168,"../../lib/regex":183,"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218,"../../plots/domain":237}],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 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":202,"../../plots/cartesian/constants":218}],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 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":191,"fast-isnumeric":18}],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 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":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":95}],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'; - -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":212,"../drawing":72,"d3":16}],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'; - -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":222,"./attributes":95,"./convert_coords":96,"./defaults":97,"./draw":98}],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'; - -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":238,"../color/attributes":50}],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'; - -module.exports = { - scrollBarWidth: 6, - scrollBarMinHeight: 20, - scrollBarColor: '#808BA4', - scrollBarMargin: 4, - textOffsetX: 40 -}; - -},{}],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 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":168,"../../plot_api/plot_template":202,"../../plots/layout_attributes":242,"../../registry":256,"./attributes":100,"./helpers":106}],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 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 interactConstants = _dereq_('../../constants/interactions'); -var alignmentConstants = _dereq_('../../constants/alignment'); -var LINE_SPACING = alignmentConstants.LINE_SPACING; -var FROM_TL = alignmentConstants.FROM_TL; -var FROM_BR = alignmentConstants.FROM_BR; - -var getLegendData = _dereq_('./get_legend_data'); -var style = _dereq_('./style'); -var helpers = _dereq_('./helpers'); - -var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY; - -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); - }, DBLCLICKDELAY); - } 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 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 < DBLCLICKDELAY) { - // 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 > DBLCLICKDELAY) { - 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":146,"../../constants/interactions":148,"../../lib":168,"../../lib/events":161,"../../lib/svg_text_utils":189,"../../plots/plots":244,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"./constants":101,"./get_legend_data":104,"./handle_click":105,"./helpers":106,"./style":108,"d3":16}],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 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":256,"./helpers":106}],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'; - -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":168,"../../registry":256}],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'; - -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; -}; - -},{}],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'; - - -module.exports = { - moduleType: 'component', - name: 'legend', - - layoutAttributes: _dereq_('./attributes'), - supplyLayoutDefaults: _dereq_('./defaults'), - - draw: _dereq_('./draw'), - style: _dereq_('./style') -}; - -},{"./attributes":100,"./defaults":102,"./draw":103,"./style":108}],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 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":168,"../../registry":256,"../../traces/pie/helpers":357,"../../traces/pie/style_one":363,"../../traces/scatter/subtypes":388,"../color":51,"../drawing":72,"d3":16}],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'; - -var Registry = _dereq_('../../registry'); -var Plots = _dereq_('../../plots/plots'); -var axisIds = _dereq_('../../plots/cartesian/axis_ids'); -var Lib = _dereq_('../../lib'); -var Icons = _dereq_('../../../build/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.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); -} - -},{"../../../build/ploticon":2,"../../lib":168,"../../plots/cartesian/axis_ids":215,"../../plots/plots":244,"../../registry":256}],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'; - -exports.manage = _dereq_('./manage'); - -},{"./manage":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 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, - context.modeBarButtonsToRemove, - context.modeBarButtonsToAdd, - context.showSendToCloud - ); - } - - if(modeBar) modeBar.update(gd, buttonGroups); - else fullLayout._modeBar = createModeBar(gd, buttonGroups); -}; - -// logic behind which buttons are displayed by default -function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) { - var fullLayout = gd._fullLayout; - var fullData = gd._fullData; - - 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(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((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; -} - -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":215,"../../registry":256,"../../traces/scatter/subtypes":388,"./buttons":109,"./modebar":112}],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 d3 = _dereq_('d3'); -var isNumeric = _dereq_('fast-isnumeric'); - -var Lib = _dereq_('../../lib'); -var Icons = _dereq_('../../../build/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; - -},{"../../../build/ploticon":2,"../../lib":168,"d3":16,"fast-isnumeric":18}],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'; - -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":202,"../../plots/font_attributes":238,"../color/attributes":50}],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'; - - -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 -}; - -},{}],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 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":168,"../../plot_api/plot_template":202,"../../plots/array_container_defaults":208,"../color":51,"./attributes":113,"./constants":114}],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'); - -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":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axis_ids":215,"../../plots/plots":244,"../../registry":256,"../color":51,"../drawing":72,"./constants":114,"./get_update_object":117,"d3":16}],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'; - -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":16}],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'; - -module.exports = { - moduleType: 'component', - name: 'rangeselector', - - schema: { - subplots: { - xaxis: {rangeselector: _dereq_('./attributes')} - } - }, - - layoutAttributes: _dereq_('./attributes'), - handleDefaults: _dereq_('./defaults'), - - draw: _dereq_('./draw') -}; - -},{"./attributes":113,"./defaults":115,"./draw":116}],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 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":50}],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'; - -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":211,"../../plots/cartesian/axis_ids":215,"./constants":121}],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'; - -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 -}; - -},{}],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 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":168,"../../plot_api/plot_template":202,"../../plots/cartesian/axis_ids":215,"./attributes":119,"./oppaxis_attributes":126}],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 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 margin = fullLayout.margin; - var graphSize = fullLayout._size; - var domain = axisOpts.domain; - var tickHeight = opts._tickHeight; - - var oppBottom = opts._oppBottom; - - opts._width = graphSize.w * (domain[1] - domain[0]); - - var x = Math.round(margin.l + (graphSize.w * domain[0])); - - var y = Math.round( - graphSize.t + graphSize.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":168,"../../lib/setcursor":187,"../../plots/cartesian":223,"../../plots/cartesian/axis_ids":215,"../../plots/plots":244,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"../titles":139,"./constants":121,"d3":16}],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 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":215,"./constants":121}],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'; - -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":168,"./attributes":119,"./calc_autorange":120,"./defaults":122,"./draw":123,"./helpers":124,"./oppaxis_attributes":126}],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'; - -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' -}; - -},{}],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 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":162,"../../plot_api/plot_template":202,"../../traces/scatter/attributes":365,"../annotations/attributes":36,"../drawing/attributes":71}],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'; - -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":168,"../../plots/cartesian/axes":212,"./constants":129,"./helpers":132}],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'; - - -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 - } -}; - -},{}],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 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":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":127,"./helpers":132}],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 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":168,"../../lib/setcursor":187,"../../plot_api/plot_template":202,"../../plots/cartesian/axes":212,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"./constants":129,"./helpers":132}],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 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":168,"./constants":129}],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 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":222,"./attributes":127,"./calc_autorange":128,"./defaults":130,"./draw":131}],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'; - -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":162,"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../../plots/animation_attributes":207,"../../plots/font_attributes":238,"../../plots/pad_attributes":243,"./constants":135}],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'; - - -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, -}; - -},{}],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 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":168,"../../plots/array_container_defaults":208,"./attributes":134,"./constants":135}],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 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":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/plots":244,"../color":51,"../drawing":72,"./constants":135,"d3":16}],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 constants = _dereq_('./constants'); - -module.exports = { - moduleType: 'component', - name: constants.name, - - layoutAttributes: _dereq_('./attributes'), - supplyLayoutDefaults: _dereq_('./defaults'), - - draw: _dereq_('./draw') -}; - -},{"./attributes":134,"./constants":135,"./defaults":136,"./draw":137}],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 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":168,"../../lib/svg_text_utils":189,"../../plots/plots":244,"../../registry":256,"../color":51,"../drawing":72,"d3":16,"fast-isnumeric":18}],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'; - -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":162,"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../../plots/font_attributes":238,"../../plots/pad_attributes":243,"../color/attributes":50}],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'; - - -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: '▼' - } -}; - -},{}],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 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":168,"../../plots/array_container_defaults":208,"./attributes":140,"./constants":141}],143:[function(_dereq_,module,exports){ -/** -* Copyright 2012-2019, Plotly, Inc. -* All rights reserved. -* -* This 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":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/plots":244,"../color":51,"../drawing":72,"./constants":141,"./scrollbox":145,"d3":16}],144:[function(_dereq_,module,exports){ -arguments[4][138][0].apply(exports,arguments) -},{"./attributes":140,"./constants":141,"./defaults":142,"./draw":143,"dup":138}],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'; - -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":168,"../color":51,"../drawing":72,"d3":16}],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'; - -// 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' - } -}; - -},{}],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, - - // ms between first mousedown and 2nd mouseup to constitute dblclick... - // we don't seem to have access to the system setting - DBLCLICKDELAY: 300, - - // 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.48.2'; - -// 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_('../build/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,"../build/ploticon":2,"./components/annotations":44,"./components/annotations3d":49,"./components/colorbar":57,"./components/colorscale":63,"./components/errorbars":78,"./components/fx":90,"./components/grid":94,"./components/images":99,"./components/legend":107,"./components/rangeselector":118,"./components/rangeslider":125,"./components/shapes":133,"./components/sliders":138,"./components/updatemenus":144,"./fonts/mathjax_config":152,"./lib/queue":182,"./locale-en":193,"./locale-en-us":192,"./plot_api":197,"./plot_api/plot_schema":201,"./plots/plots":244,"./registry":256,"./snapshot":261,"./traces/scatter":376,"d3":16,"es6-promise":17}],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'; - - -/** - * 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) - ); -}; - -},{}],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'; - -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":175}],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 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; -} - -},{}],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 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":18}],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'; - -/** - * 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}); - }); - } -}; - -},{}],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 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; - } -}; - -},{}],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'; - -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":66,"../constants/interactions":148,"../plots/attributes":209,"./array":155,"./mod":175,"./nested_property":176,"./regex":183,"fast-isnumeric":18,"tinycolor2":34}],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 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":256,"./loggers":172,"./mod":175,"d3":16,"fast-isnumeric":18}],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'; - -/* 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":15}],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 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":169}],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'; - - -/** - * 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; -}; - -},{}],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'; - -/** 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 - ); -} - -},{}],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'; - -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":175}],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'; - -/** - * 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 - */ -module.exports = function(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'); - } - - return gd; // otherwise assume that gd is a DOM element -}; - -},{}],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'; - -// Simple helper functions -// none of these need any external deps - -module.exports = function identity(d) { return d; }; - -},{}],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'; - -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; - -lib.getGraphDiv = _dereq_('./get_graph_div'); - -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) { - if(lib.isArrayOrTypedArray(traceAttr)) { - var imax = Math.min(traceAttr.length, cd.length); - for(var i = 0; i < imax; i++) cd[i][cdAttr] = traceAttr[i]; - } -}; - -/** 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.isPlotDiv = function(el) { - var el3 = d3.select(el); - return el3.node() instanceof HTMLElement && - el3.size() && - el3.classed('js-plotly-plot'); -}; - -lib.removeElement = function(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 - */ -lib.addStyleRule = function(selector, styleString) { - lib.addRelatedStyleRule('global', selector, styleString); -}; - -/** - * for dynamically adding style rules - * to a stylesheet uniquely identified by a uid - */ -lib.addRelatedStyleRule = function(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 lib.warn('addStyleRule failed'); -}; - -/** - * to remove from the page a stylesheet identified by a given uid - */ -lib.deleteRelatedStyleRule = function(uid) { - var id = 'plotly.js-style-' + uid; - var style = document.getElementById(id); - if(style) lib.removeElement(style); -}; - -lib.isIE = function() { - return typeof window.navigator.msSaveBlob !== 'undefined'; -}; - -/** - * 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; -}; - -},{"../constants/numerical":149,"./anchor_utils":153,"./angles":154,"./array":155,"./clean_number":156,"./clear_responsive":158,"./coerce":159,"./dates":160,"./extend":162,"./filter_unique":163,"./filter_visible":164,"./geometry2d":165,"./get_graph_div":166,"./identity":167,"./is_plain_object":169,"./keyed_container":170,"./localize":171,"./loggers":172,"./make_trace_groups":173,"./matrix":174,"./mod":175,"./nested_property":176,"./noop":177,"./notifier":178,"./push_unique":181,"./regex":183,"./relative_attr":184,"./relink_private":185,"./search":186,"./stats":188,"./throttle":190,"./to_log_range":191,"d3":16,"fast-isnumeric":18}],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'; - -// 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 - ); -}; - -},{}],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'; - -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":176}],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 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":256}],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'; - -/* 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":200}],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'; - -/** - * 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(); - - return traces; -}; - -},{}],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'; - - -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))); - }; -}; - -},{}],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'; - -/** - * 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 -}; - -},{}],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'; - -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":155,"fast-isnumeric":18}],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'; - -// Simple helper functions -// none of these need any external deps - -module.exports = function noop() {}; - -},{}],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'; - -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":16,"fast-isnumeric":18}],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 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":187}],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 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":174}],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'; - -/** - * 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; -}; - -},{}],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'; - -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":168,"../plot_api/plot_config":200}],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'; - -/* - * 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); -}; - -},{}],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'; - -// 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; -}; - -},{}],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'; - -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":155,"./is_plain_object":169}],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 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":167,"./loggers":172,"fast-isnumeric":18}],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'; - -// 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); -}; - -},{}],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'; - -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":155,"fast-isnumeric":18}],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'; - -/* 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":146,"../constants/xmlns_namespaces":150,"../lib":168,"d3":16}],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'; - -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; - } -} - -},{}],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 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":18}],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'; - -module.exports = { - moduleType: 'locale', - name: 'en-US', - dictionary: { - 'Click to enter Colorscale title': 'Click to enter Colorscale title' - }, - format: { - date: '%m/%d/%Y' - } -}; - -},{}],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', - 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' - } -}; - -},{}],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'; - -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":256}],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 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":168}],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 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":51,"../lib":168,"../plots/cartesian/axis_ids":215,"../plots/plots":244,"../registry":256,"fast-isnumeric":18,"gl-mat4/fromQuat":19}],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 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":258,"./plot_api":199,"./template_api":204,"./to_image":205,"./validate":206}],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 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":169,"../lib/loggers":172,"../lib/noop":177,"../lib/search":186,"../registry":256,"./container_array_match":194}],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 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() { 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) { - return PlotSchema.getTraceValObject(trace, parts); - } - - 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(0); - 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); - - // 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":51,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":168,"../lib/events":161,"../lib/queue":182,"../lib/svg_text_utils":189,"../plots/cartesian/axes":212,"../plots/cartesian/constants":218,"../plots/cartesian/graph_interact":221,"../plots/cartesian/select":229,"../plots/plots":244,"../plots/polar/legacy":247,"../registry":256,"./edit_types":195,"./helpers":196,"./manage_arrays":198,"./plot_config":200,"./plot_schema":201,"./subroutines":203,"d3":16,"fast-isnumeric":18,"has-hover":20}],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'; - -/** - * 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', - - }, - - 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, - - }, - 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 -}; - -},{}],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'; - -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 || {}, - type: type, - attributes: formatAttributes(attributes), - }; - - // trace-specific layout attributes - if(_module.layoutAttributes) { - var layoutAttributes = {}; - - extendDeepAll(layoutAttributes, _module.layoutAttributes); - out.layoutAttributes = formatAttributes(layoutAttributes); - } - - 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":168,"../plots/animation_attributes":207,"../plots/attributes":209,"../plots/frame_attributes":239,"../plots/layout_attributes":242,"../plots/polar/legacy/area_attributes":245,"../plots/polar/legacy/axis_attributes":246,"../registry":256,"./edit_types":195,"./plot_config":200}],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 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":168,"../plots/attributes":209}],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 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":51,"../components/drawing":72,"../components/modebar":110,"../components/titles":139,"../constants/alignment":146,"../lib":168,"../lib/clear_gl_canvases":157,"../plots/cartesian/autorange":211,"../plots/cartesian/axes":212,"../plots/cartesian/constraints":219,"../plots/plots":244,"../registry":256,"d3":16}],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 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":168,"../plots/attributes":209,"../plots/plots":244,"./plot_config":200,"./plot_schema":201,"./plot_template":202}],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 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, - - } -}; - -var IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/; - -/** 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('data:image/svg+xml,' + encodeURIComponent(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(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":168,"../snapshot/helpers":260,"../snapshot/svgtoimg":262,"../snapshot/tosvg":264,"./plot_api":199,"fast-isnumeric":18}],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 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":168,"../plots/plots":244,"./plot_config":200,"./plot_schema":201}],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'; - -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', - - } - } -}; - -},{}],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'; - -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":168,"../plot_api/plot_template":202}],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 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":81}],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'; - - -module.exports = { - xaxis: { - valType: 'subplotid', - - dflt: 'x', - editType: 'calc+clearAxisTypes', - - }, - yaxis: { - valType: 'subplotid', - - dflt: 'y', - editType: 'calc+clearAxisTypes', - - } -}; - -},{}],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'; - -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":168,"../../registry":256,"fast-isnumeric":18}],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 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 vals = []; - if(ax.type === 'category' || ax.type === 'multicategory') { - endTick = (axrev) ? Math.max(-0.5, endTick) : - Math.min(ax._categories.length - 0.5, endTick); - } - - 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(vals.length > maxTicks || x === xPrevious) break; - xPrevious = x; - - vals.push(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) { - vals.pop(); - } - - // save the last tick as well as first, so we can - // show the exponent only on the last one - ax._tmax = vals[vals.length - 1]; - - // 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(vals.length); - for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]); - - 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) { - 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(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 - }); - - 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":51,"../../components/drawing":72,"../../components/titles":139,"../../constants/alignment":146,"../../constants/numerical":149,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/plots":244,"../../registry":256,"./autorange":211,"./axis_autotype":213,"./axis_ids":215,"./clean_ticks":217,"./layout_attributes":224,"./set_convert":230,"d3":16,"fast-isnumeric":18}],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 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":168,"fast-isnumeric":18}],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 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":168,"../../registry":256,"./category_order_defaults":216,"./layout_attributes":224,"./line_grid_defaults":226,"./set_convert":230,"./tick_label_defaults":231,"./tick_mark_defaults":232,"./tick_value_defaults":233}],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 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":256,"./constants":218}],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'; - -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(); - } - } -}; - -},{}],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'; - -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":168,"fast-isnumeric":18}],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 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":183}],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 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":146,"../../constants/numerical":149,"../../lib":168,"./autorange":211,"./axis_ids":215,"./scale_zoom":228}],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 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 - // DBLCLICKDELAY ms on the drag bars - // numClicks stores how many mousedowns have been seen - // within DBLCLICKDELAY 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":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../constants/alignment":146,"../../lib":168,"../../lib/clear_gl_canvases":157,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plot_api/subroutines":203,"../../registry":256,"../plots":244,"./axes":212,"./axis_ids":215,"./constants":218,"./scale_zoom":228,"./select":229,"d3":16,"has-passive-events":21,"tinycolor2":34}],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 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":69,"../../components/fx":90,"../../lib/setcursor":187,"./constants":218,"./dragbox":220,"d3":16}],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 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":168,"../../registry":256}],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 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); - - 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":72,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../registry":256,"../get_data":240,"../plots":244,"./attributes":210,"./axis_ids":215,"./constants":218,"./graph_interact":221,"./layout_attributes":224,"./layout_defaults":225,"./transition_axes":234,"d3":16}],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 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 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":50,"../../components/drawing/attributes":71,"../../lib/extend":162,"../../plot_api/plot_template":202,"../font_attributes":238,"./constants":218}],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 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":51,"../../lib":168,"../../plot_api/plot_template":202,"../../registry":256,"../layout_attributes":242,"./axis_defaults":214,"./axis_ids":215,"./constraints":219,"./layout_attributes":224,"./position_defaults":227,"./type_defaults":235}],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 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":50,"../../lib":168,"tinycolor2":34}],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 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":168,"fast-isnumeric":18}],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 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":146}],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 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); - } - - 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":51,"../../components/fx":90,"../../components/fx/helpers":86,"../../lib":168,"../../lib/clear_gl_canvases":157,"../../lib/polygon":180,"../../lib/throttle":190,"../../plot_api/subroutines":203,"../../registry":256,"./axis_ids":215,"./constants":218,"polybooljs":25}],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 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":168,"./axis_ids":215,"./constants":218,"d3":16,"fast-isnumeric":18}],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 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":168,"../array_container_defaults":208,"./layout_attributes":224}],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'); - - -/** - * 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":168,"./layout_attributes":224}],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 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":217}],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 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":72,"../../registry":256,"./axes":212,"d3":16}],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 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":256,"./axis_autotype":213}],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 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; - if(Array.isArray(attr)) { - 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 { - thisTraces = traces ? traces.slice(0) : 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":168,"../registry":256}],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 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":162}],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'; - -/* - * 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; -}; - -},{}],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'; - -module.exports = { - _isLinkedToArray: 'frames_entry', - - group: { - valType: 'string', - - - }, - name: { - valType: 'string', - - - }, - traces: { - valType: 'any', - - - }, - baseframe: { - valType: 'string', - - - }, - data: { - valType: 'any', - - - }, - layout: { - valType: 'any', - - - } -}; - -},{}],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'; - -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":256,"./cartesian/constants":218}],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'; - -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; - -},{}],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'; - -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":50,"../lib/extend":162,"./animation_attributes":207,"./font_attributes":238,"./pad_attributes":243}],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'; - -/** - * 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 - }; -}; - -},{}],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'; - -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) { - function isHidden(gd) { - var display = window.getComputedStyle(gd).display; - return !display || display === 'none'; - } - - if(!gd || 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) || 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(0); - 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) { - // 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":51,"../constants/numerical":149,"../lib":168,"../plot_api/plot_schema":201,"../plot_api/plot_template":202,"../registry":256,"./animation_attributes":207,"./attributes":209,"./cartesian/axis_ids":215,"./command":236,"./font_attributes":238,"./frame_attributes":239,"./layout_attributes":242,"d3":16,"fast-isnumeric":18}],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 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":162,"../../../traces/scatter/attributes":365}],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 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":162,"../../../plot_api/edit_types":195,"../../cartesian/layout_attributes":224}],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 Polar = module.exports = _dereq_('./micropolar'); - -Polar.manager = _dereq_('./micropolar_manager'); - -},{"./micropolar":248,"./micropolar_manager":249}],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. -*/ - -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":146,"../../../lib":168,"d3":16}],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. -*/ - -/* 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":51,"../../../lib":168,"./micropolar":248,"./undo_manager":250,"d3":16}],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. -*/ - -'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; } - }; -}; - -},{}],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'; - -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":168,"../plot_api/plot_template":202,"./domain":237}],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 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":168,"../../plots/get_data":240,"./layout_attributes":253,"./layout_defaults":254,"./ternary":255}],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 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":50,"../../lib/extend":162,"../../plot_api/edit_types":195,"../cartesian/layout_attributes":224,"../domain":237}],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 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":51,"../../lib":168,"../../plot_api/plot_template":202,"../cartesian/line_grid_defaults":226,"../cartesian/tick_label_defaults":231,"../cartesian/tick_mark_defaults":232,"../cartesian/tick_value_defaults":233,"../subplot_defaults":251,"./layout_attributes":253}],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 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":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../components/titles":139,"../../lib":168,"../../lib/extend":162,"../../registry":256,"../cartesian/axes":212,"../cartesian/constants":218,"../cartesian/select":229,"../cartesian/set_convert":230,"../plots":244,"d3":16,"tinycolor2":34}],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 Loggers = _dereq_('./lib/loggers'); -var noop = _dereq_('./lib/noop'); -var pushUnique = _dereq_('./lib/push_unique'); -var isPlainObject = _dereq_('./lib/is_plain_object'); -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); - } -} - -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/extend":162,"./lib/is_plain_object":169,"./lib/loggers":172,"./lib/noop":177,"./lib/push_unique":181,"./plots/attributes":209,"./plots/layout_attributes":242}],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 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":168,"../registry":256}],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 toImage = _dereq_('../plot_api/to_image'); -var Lib = _dereq_('../lib'); -var fileSaver = _dereq_('./filesaver'); - -/** 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 ../plot_api/to_image) - * @return {promise} - */ -function downloadImage(gd, opts) { - var _gd; - if(!Lib.isPlainObject(gd)) _gd = Lib.getGraphDiv(gd); - - // check for undefined opts - opts = opts || {}; - // default to png - opts.format = opts.format || 'png'; - - 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('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.')); - } - - 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); - }).then(function(name) { - resolve(name); - }).catch(function(err) { - if(_gd) _gd._snapshotInProgress = false; - reject(err); - }); - }); -} - -module.exports = downloadImage; - -},{"../lib":168,"../plot_api/to_image":205,"./filesaver":259}],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. -*/ - -/* -* 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 -*/ - -'use strict'; - -var fileSaver = function(url, name) { - var saveLink = document.createElement('a'); - var canUseSaveLink = 'download' in saveLink; - var isSafari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); - var promise = new Promise(function(resolve, reject) { - // IE <10 is explicitly unsupported - if(typeof navigator !== 'undefined' && /MSIE [1-9]\./.test(navigator.userAgent)) { - reject(new Error('IE < 10 unsupported')); - } - - // First try a.download, then web filesystem, then object URLs - if(isSafari) { - // Safari doesn't allow downloading of blob urls - document.location.href = 'data:application/octet-stream' + url.slice(url.search(/[,;]/)); - resolve(name); - } - - if(!name) { - name = 'download'; - } - - if(canUseSaveLink) { - saveLink.href = url; - saveLink.download = name; - document.body.appendChild(saveLink); - saveLink.click(); - document.body.removeChild(saveLink); - resolve(name); - } - - // IE 10+ (native saveAs) - if(typeof navigator !== 'undefined' && navigator.msSaveBlob) { - // At this point we are only dealing with a SVG encoded as - // a data URL (since IE only supports SVG) - var encoded = url.split(/^data:image\/svg\+xml,/)[1]; - var svg = decodeURIComponent(encoded); - navigator.msSaveBlob(new Blob([svg]), name); - resolve(name); - } - - reject(new Error('download error')); - }); - - return promise; -}; - -module.exports = fileSaver; - -},{}],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 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); - } - }; -}; - -},{"../registry":256}],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 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":257,"./download":258,"./helpers":260,"./svgtoimg":262,"./toimage":263,"./tosvg":264}],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 Lib = _dereq_('../lib'); -var EventEmitter = _dereq_('events').EventEmitter; - -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('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.'); - 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(); - - // for Safari support, eliminate createObjectURL - // this decision could cause problems if content - // is not restricted to svg - var url = 'data:image/svg+xml,' + encodeURIComponent(svg); - - canvas.width = w1; - canvas.height = h1; - - img.onload = function() { - var imgData; - - // 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) { - 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":168,"events":15}],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 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":168,"../registry":256,"./cloneplot":257,"./helpers":260,"./svgtoimg":262,"./tosvg":264,"events":15}],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 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":51,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":168,"d3":16}],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 mergeArray = _dereq_('../../lib').mergeArray; - -// 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; - - mergeArray(trace.text, cd, 'tx'); - mergeArray(trace.hovertext, cd, 'htx'); - - var marker = trace.marker; - if(marker) { - mergeArray(marker.opacity, cd, 'mo'); - mergeArray(marker.color, cd, 'mc'); - - var markerLine = marker.line; - if(markerLine) { - mergeArray(markerLine.color, cd, 'mlc'); - mergeArray(markerLine.width, cd, 'mlw'); - } - } -}; - -},{"../../lib":168}],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 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":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/font_attributes":238,"../scatter/attributes":365,"./constants.js":268}],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 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":59,"../../components/colorscale/helpers":62,"../../plots/cartesian/axes":212,"../scatter/calc_selection":367,"./arrays_to_calcdata":265}],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'; - -module.exports = { - eventDataKeys: [] -}; - -},{}],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'; - -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":168,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_ids":215,"../../registry":256,"./sieve.js":278,"fast-isnumeric":18}],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 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":51,"../../lib":168,"../../plots/cartesian/axis_ids":215,"../../registry":256,"../scatter/xy_defaults":390,"./attributes":266,"./style_defaults":280}],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 isNumeric = _dereq_('fast-isnumeric'); -var tinycolor = _dereq_('tinycolor2'); - -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; -}; - -},{"fast-isnumeric":18,"tinycolor2":34}],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 Fx = _dereq_('../../components/fx'); -var Registry = _dereq_('../../registry'); -var Color = _dereq_('../../components/color'); - -var fillText = _dereq_('../../lib').fillText; - -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 = di.mlw || trace.marker.line.width; - - if(Color.opacity(mc)) return mc; - else if(Color.opacity(mlc) && mlw) return mlc; -} - -module.exports = { - hoverPoints: hoverPoints, - hoverOnBars: hoverOnBars, - getTraceColor: getTraceColor -}; - -},{"../../components/color":51,"../../components/fx":90,"../../lib":168,"../../registry":256}],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'; - -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":223,"../scatter/marker_colorbar":382,"./arrays_to_calcdata":265,"./attributes":266,"./calc":267,"./cross_trace_calc":269,"./defaults":270,"./hover":272,"./layout_attributes":274,"./layout_defaults":275,"./plot":276,"./select":277,"./style":279}],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 = { - 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', - - } -}; - -},{}],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'; - -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":168,"../../plots/cartesian/axes":212,"../../registry":256,"./layout_attributes":274}],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 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'); - - if(!plotinfo.isRangePlot) cd[0].node3 = plotGroup; - - 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 = di.isBlank = !( - isNumeric(x0) && isNumeric(x1) && - isNumeric(y0) && isNumeric(y1) && - (x0 !== x1 || (shouldDisplayZeros && isHorizontal)) && - (y0 !== y1 || (shouldDisplayZeros && !isHorizontal)) - ); - - // 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 = (di.mlw + 1 || trace.marker.line.width + 1 || - (di.trace ? di.trace.marker.line.width : 0) + 1) - 1; - 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 = getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, { - isHorizontal: isHorizontal, - constrained: constrained, - angle: trace.textangle - }); - } else { - constrained = - trace.constraintext === 'both' || - trace.constraintext === 'inside'; - - transform = getTransformToMoveInsideBar(x0, x1, y0, y1, textBB, { - isHorizontal: isHorizontal, - constrained: constrained, - angle: trace.textangle, - anchor: trace.insidetextanchor - }); - } - - textSelection.attr('transform', transform); -} - -function getRotationFromAngle(angle) { - return (angle === 'auto') ? 0 : angle; -} - -function getTransformToMoveInsideBar(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 rotation = getRotationFromAngle(angle); - var absSin = Math.abs(Math.sin(Math.PI / 180 * rotation)); - var absCos = Math.abs(Math.cos(Math.PI / 180 * rotation)); - - // 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) rotation += 90; - - return getTransform(textX, textY, targetX, targetY, scale, rotation); -} - -function getTransformToMoveOutsideBar(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 rotation and scale - var scale = 1; - if(constrained) { - scale = (isHorizontal) ? - Math.min(1, ly / textHeight) : - Math.min(1, lx / textWidth); - } - - var rotation = getRotationFromAngle(angle); - var absSin = Math.abs(Math.sin(Math.PI / 180 * rotation)); - var absCos = Math.abs(Math.cos(Math.PI / 180 * rotation)); - - // 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 getTransform(textX, textY, targetX, targetY, scale, rotation); -} - -function getTransform(textX, textY, targetX, targetY, scale, rotation) { - var transformScale; - var transformRotate; - var transformTranslate; - - if(scale < 1) transformScale = 'scale(' + scale + ') '; - else { - scale = 1; - transformScale = ''; - } - - transformRotate = (rotation) ? - 'rotate(' + rotation + ' ' + 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, - getTransformToMoveInsideBar: getTransformToMoveInsideBar, - getTransformToMoveOutsideBar: getTransformToMoveOutsideBar -}; - -},{"../../components/color":51,"../../components/drawing":72,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../registry":256,"./attributes":266,"./helpers":271,"./style":279,"d3":16,"fast-isnumeric":18}],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'; - -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]; - } - } -} - -},{}],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 = 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":168}],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'; - -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, cd) { - var s = cd ? cd[0].node3 : 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) { - var s = cd[0].node3; - var trace = cd[0].trace; - - if(trace.selectedpoints) { - stylePointsInSelectionMode(s, trace, gd); - } else { - stylePoints(s, trace, gd); - - Registry.getComponentMethod('errorbars', 'style')(s); - } -} - -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":51,"../../components/drawing":72,"../../lib":168,"../../registry":256,"./attributes":266,"./helpers":271,"d3":16}],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 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":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62}],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 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":50,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../bar/attributes":266,"../scatter/attributes":365}],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 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":168,"../../plots/cartesian/axes":212,"fast-isnumeric":18}],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 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":168,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_ids":215}],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 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":51,"../../lib":168,"../../registry":256,"../bar/defaults":270,"./attributes":281}],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'; - -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; -}; - -},{}],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'; - -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', 'min', 'q1', 'q3', '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[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":51,"../../components/fx":90,"../../lib":168,"../../plots/cartesian/axes":212}],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'; - -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":223,"./attributes":281,"./calc":282,"./cross_trace_calc":283,"./defaults":284,"./event_data":285,"./hover":286,"./layout_attributes":288,"./layout_defaults":289,"./plot":290,"./select":291,"./style":292}],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 = { - 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', - - } -}; - -},{}],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'; - -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":168,"../../registry":256,"./layout_attributes":288}],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 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; - if(!plotinfo.isRangePlot) cd0.node3 = plotGroup; - - // 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":72,"../../lib":168,"d3":16}],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'; - -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; -}; - -},{}],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'; - -var d3 = _dereq_('d3'); -var Color = _dereq_('../../components/color'); -var Drawing = _dereq_('../../components/drawing'); - -function style(gd, cd) { - var s = cd ? cd[0].node3 : 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) { - var s = cd[0].node3; - var trace = cd[0].trace; - var pts = s.selectAll('path.point'); - - if(trace.selectedpoints) { - Drawing.selectedPointStyle(pts, trace); - } else { - Drawing.pointStyle(pts, trace, gd); - } -} - -module.exports = { - style: style, - styleOnSelect: styleOnSelect -}; - -},{"../../components/color":51,"../../components/drawing":72,"d3":16}],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 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 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":58,"../../components/drawing/attributes":71,"../../constants/filter_ops":147,"../../lib/extend":162,"../../plots/font_attributes":238,"../heatmap/attributes":315,"../scatter/attributes":365}],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 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":63,"../heatmap/calc":316,"./end_plus":304,"./set_contours":312}],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'; - -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; - } -}; - -},{}],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'; - -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":63,"./end_plus":304,"./make_color_map":309}],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'; -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 - } -}; - -},{}],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'; -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":51,"../../constants/filter_ops":147,"./label_defaults":308,"fast-isnumeric":18}],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 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":18}],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'; - -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'); -}; - -},{}],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'; - -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":168}],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'); - -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":168,"../heatmap/xyz_defaults":329,"./attributes":293,"./constraint_defaults":298,"./contours_defaults":300,"./style_defaults":314}],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 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":168,"./constraint_mapping":299,"./end_plus":304}],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'; - -/* - * 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; -}; - -},{}],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'; - -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":168,"./constants":297}],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 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":51,"../heatmap/hover":322}],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'; - -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":223,"./attributes":293,"./calc":294,"./colorbar":296,"./defaults":302,"./hover":306,"./plot":311,"./style":313}],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'; - -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":168}],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 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":63,"./end_plus":304,"d3":16}],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 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":297}],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 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":72,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../plots/cartesian/set_convert":230,"../heatmap/plot":326,"./close_boundaries":295,"./constants":297,"./convert_to_constraints":301,"./empty_pathinfo":303,"./find_all_paths":305,"./make_crossings":310,"d3":16}],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 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":168,"../../plots/cartesian/axes":212}],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 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":72,"../heatmap/style":327,"./make_color_map":309,"d3":16}],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 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":61,"./label_defaults":308}],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 scatterAttrs = _dereq_('../scatter/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); -var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); - -var extendFlat = _dereq_('../../lib/extend').extendFlat; - -module.exports = extendFlat({ - z: { - valType: 'data_array', - editType: 'calc', - - }, - x: extendFlat({}, scatterAttrs.x, {impliedEdits: {xtype: 'array'}}), - x0: extendFlat({}, scatterAttrs.x0, {impliedEdits: {xtype: 'scaled'}}), - dx: extendFlat({}, scatterAttrs.dx, {impliedEdits: {xtype: 'scaled'}}), - y: extendFlat({}, scatterAttrs.y, {impliedEdits: {ytype: 'array'}}), - y0: extendFlat({}, scatterAttrs.y0, {impliedEdits: {ytype: 'scaled'}}), - dy: extendFlat({}, scatterAttrs.dy, {impliedEdits: {ytype: 'scaled'}}), - - 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":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../scatter/attributes":365}],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 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":59,"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":256,"../histogram2d/calc":344,"./clean_2d_array":317,"./convert_column_xyz":319,"./find_empties":321,"./interp2d":324,"./make_bound_array":325}],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 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); - - var zNew = new Array(rowlen); - - if(ya && ya.type === 'category') rowlen = ya._categories.length; - 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":168,"fast-isnumeric":18}],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'; - -module.exports = { - min: 'zmin', - max: 'zmax' -}; - -},{}],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'; - -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":168}],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 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":61,"../../lib":168,"./attributes":315,"./style_defaults":328,"./xyz_defaults":329}],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 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":168}],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 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":63,"../../components/fx":90,"../../lib":168,"../../plots/cartesian/axes":212}],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'; - -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":223,"./attributes":315,"./calc":316,"./colorbar":318,"./defaults":320,"./hover":322,"./plot":326,"./style":327}],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'; - -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":168}],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 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":168,"../../registry":256}],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 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":63,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../registry":256,"d3":16,"tinycolor2":34}],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'); - -module.exports = function style(gd) { - d3.select(gd).selectAll('.hm image') - .style('opacity', function(d) { - return d.trace.opacity; - }); -}; - -},{"d3":16}],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'; - -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'); -}; - -},{}],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'; - -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":168,"../../registry":256,"fast-isnumeric":18}],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 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":89,"../../lib/extend":162,"../bar/attributes":266,"./bin_attributes":332,"./constants":336}],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'; - - -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; -}; - -},{}],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 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' - }; -}; - -},{}],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'; - -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":18}],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 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":212}],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 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":168,"../../plots/cartesian/axes":212,"../../registry":256,"../bar/arrays_to_calcdata":265,"./average":331,"./bin_functions":333,"./bin_label_vals":334,"./norm_functions":342,"fast-isnumeric":18}],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'; - -module.exports = { - eventDataKeys: ['binNumber'] -}; - -},{}],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'; - -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":168,"../../plots/cartesian/axis_ids":215,"../../registry":256,"../bar/defaults":270}],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 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":51,"../../lib":168,"../../registry":256,"../bar/style_defaults":280,"./attributes":330}],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'; - -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; -}; - -},{}],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'; - -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":212,"../bar/hover":272}],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'; - -/** - * 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":223,"../bar/cross_trace_calc":269,"../bar/layout_attributes":274,"../bar/layout_defaults":275,"../bar/plot":276,"../bar/select":277,"../bar/style":279,"../scatter/marker_colorbar":382,"./attributes":330,"./calc":335,"./cross_trace_defaults":337,"./defaults":338,"./event_data":339,"./hover":340}],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'; - - -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; - } -}; - -},{}],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'; - -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":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../heatmap/attributes":315,"../histogram/attributes":330,"../histogram/bin_attributes":332}],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 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":168,"../../plots/cartesian/axes":212,"../histogram/average":331,"../histogram/bin_functions":333,"../histogram/bin_label_vals":334,"../histogram/calc":335,"../histogram/norm_functions":342}],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 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":61,"../../lib":168,"../heatmap/style_defaults":328,"./attributes":343,"./sample_defaults":348}],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 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":212,"../heatmap/hover":322}],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'; - -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":223,"../heatmap/calc":316,"../heatmap/colorbar":318,"../heatmap/plot":326,"../heatmap/style":327,"../histogram/cross_trace_defaults":337,"../histogram/event_data":339,"./attributes":343,"./defaults":345,"./hover":346}],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'; - -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":168,"../../registry":256}],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 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":58,"../../lib/extend":162,"../contour/attributes":293,"../histogram2d/attributes":343}],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 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":168,"../contour/contours_defaults":300,"../contour/style_defaults":314,"../histogram2d/sample_defaults":348,"./attributes":349}],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'; - -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":223,"../contour/calc":294,"../contour/colorbar":296,"../contour/hover":306,"../contour/plot":311,"../contour/style":313,"../histogram/cross_trace_defaults":337,"./attributes":349,"./defaults":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'; - -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":50,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/attributes":209,"../../plots/domain":237,"../../plots/font_attributes":238}],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 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":240,"../../registry":256}],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 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":51,"../../lib":168,"./helpers":357,"fast-isnumeric":18,"tinycolor2":34}],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 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":168,"../../plots/domain":237,"../bar/defaults":270,"./attributes":352}],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 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":86}],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 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":168}],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'; - -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":352,"./base_plot":353,"./calc":354,"./defaults":355,"./layout_attributes":359,"./layout_defaults":360,"./plot":361,"./style":362,"./style_one":363}],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 = { - hiddenlabels: { - valType: 'data_array', - - editType: 'calc', - - }, - piecolorway: { - valType: 'colorlist', - - editType: 'calc', - - }, - extendpiecolors: { - valType: 'boolean', - dflt: true, - - editType: 'calc', - - } -}; - -},{}],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'; - -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":168,"./layout_attributes":359}],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 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":51,"../../components/drawing":72,"../../components/fx":90,"../../lib":168,"../../lib/svg_text_utils":189,"./event_data":356,"./helpers":357,"d3":16}],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 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":363,"d3":16}],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 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":51,"./helpers":357}],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 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.mergeArray(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.mergeArray(marker.size, cd, 'ms'); - Lib.mergeArray(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.mergeArray(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":168}],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 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":58,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/font_attributes":238,"./constants":369}],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 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":168,"../../plots/cartesian/axes":212,"./arrays_to_calcdata":364,"./calc_selection":367,"./colorscale_calc":368,"./subtypes":388,"fast-isnumeric":18}],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 Lib = _dereq_('../../lib'); - -module.exports = function calcSelection(cd, trace) { - if(Lib.isArrayOrTypedArray(trace.selectedpoints)) { - Lib.tagSelected(cd, trace); - } -}; - -},{"../../lib":168}],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 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":59,"../../components/colorscale/helpers":62,"./subtypes":388}],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'; - -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: [] -}; - -},{}],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'; - -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":366}],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'; - - -// 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; - } - } - } - } -}; - -},{}],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'; - -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":168,"../../registry":256,"./attributes":365,"./constants":369,"./fillcolor_defaults":373,"./line_defaults":377,"./line_shape_defaults":379,"./marker_defaults":383,"./stack_defaults":386,"./subtypes":388,"./text_defaults":389,"./xy_defaults":390}],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 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":51,"../../lib":168}],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 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":51,"./subtypes":388}],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 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":51,"../../components/fx":90,"../../lib":168,"../../registry":256,"./get_trace_color":374}],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 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":223,"./arrays_to_calcdata":364,"./attributes":365,"./calc":366,"./cross_trace_calc":370,"./cross_trace_defaults":371,"./defaults":372,"./hover":375,"./marker_colorbar":382,"./plot":384,"./select":385,"./style":387,"./subtypes":388}],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 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":61,"../../components/colorscale/helpers":62,"../../lib":168}],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 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":168,"./constants":369}],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'; - - -// common to 'scatter' and 'scatterternary' -module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) { - var shape = coerce('line.shape'); - if(shape === 'spline') coerce('line.smoothing'); -}; - -},{}],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'; - -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; -}; - -},{}],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 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":18}],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'; - -module.exports = { - container: 'marker', - min: 'cmin', - max: 'cmax' -}; - -},{}],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'; - -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":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"./subtypes":388}],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 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 - if(!plotinfo.isRangePlot) cdscatter[0].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":72,"../../lib":168,"../../lib/polygon":180,"../../registry":256,"./line_points":378,"./link_traces":380,"./subtypes":388,"d3":16}],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 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":388}],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 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; - } -}; - -},{}],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 d3 = _dereq_('d3'); -var Drawing = _dereq_('../../components/drawing'); -var Registry = _dereq_('../../registry'); - -function style(gd, cd) { - var s = cd ? cd[0].node3 : 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) { - var s = cd[0].node3; - var trace = cd[0].trace; - - if(trace.selectedpoints) { - Drawing.selectedPointStyle(s.selectAll('path.point'), trace); - Drawing.selectedTextStyle(s.selectAll('text'), trace); - } else { - stylePoints(s, trace, gd); - styleText(s, trace, gd); - } -} - -module.exports = { - style: style, - stylePoints: stylePoints, - styleText: styleText, - styleOnSelect: styleOnSelect -}; - -},{"../../components/drawing":72,"../../registry":256,"d3":16}],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 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":168}],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'); - -/* - * 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":168}],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'); -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":168,"../../registry":256}],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 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":58,"../../components/drawing/attributes":71,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/attributes":209,"../scatter/attributes":365}],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 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":364,"../scatter/calc":366,"../scatter/calc_selection":367,"../scatter/colorscale_calc":368,"fast-isnumeric":18}],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 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":168,"../scatter/constants":369,"../scatter/fillcolor_defaults":373,"../scatter/line_defaults":377,"../scatter/line_shape_defaults":379,"../scatter/marker_defaults":383,"../scatter/subtypes":388,"../scatter/text_defaults":389,"./attributes":391}],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'; - -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; -}; - -},{}],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'; - -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; - // TODO: nice formatting, and label by axis title, for a, b, and c? - - var trace = newPointData.trace; - var ternary = newPointData.subplot; - var hoverinfo = cdi.hi || trace.hoverinfo; - var text = []; - function textPart(ax, val) { - text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); - } - if(!trace.hovertemplate) { - var parts = hoverinfo.split('+'); - if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c']; - if(parts.indexOf('a') !== -1) textPart(ternary.aaxis, cdi.a); - if(parts.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b); - if(parts.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c); - } - newPointData.extraText = text.join('
    '); - newPointData.hovertemplate = trace.hovertemplate; - return scatterPointData; -}; - -},{"../../plots/cartesian/axes":212,"../scatter/hover":375}],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'; - -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":252,"../scatter/marker_colorbar":382,"../scatter/select":385,"../scatter/style":387,"./attributes":391,"./calc":392,"./defaults":393,"./event_data":394,"./hover":395,"./plot":397}],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'; - -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":384}],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 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":162,"../box/attributes":281}],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 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":168,"../../plots/cartesian/axes":212,"../box/calc":282,"./helpers":402}],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 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":283}],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 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":51,"../../lib":168,"../box/defaults":284,"./attributes":398}],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'); - -// 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":168}],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'); -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":168,"../../plots/cartesian/axes":212,"../box/hover":286,"./helpers":402}],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'; - -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":223,"../box/defaults":284,"../box/select":291,"../scatter/style":387,"./attributes":398,"./calc":399,"./cross_trace_calc":400,"./defaults":401,"./hover":403,"./layout_attributes":405,"./layout_defaults":406,"./plot":407,"./style":408}],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'; - -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":168,"../box/layout_attributes":288}],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 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":168,"../box/layout_defaults":289,"./layout_attributes":405}],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 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(!plotinfo.isRangePlot) cd0.node3 = plotGroup; - - 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":72,"../../lib":168,"../box/plot":290,"../scatter/line_points":378,"./helpers":402,"d3":16}],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 Color = _dereq_('../../components/color'); -var stylePoints = _dereq_('../scatter/style').stylePoints; - -module.exports = function style(gd, cd) { - var s = cd ? cd[0].node3 : 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":51,"../scatter/style":387,"d3":16}]},{},[11])(11) -}); diff --git a/static/babybuddy/js/graph.da32e0532ca2.js.gz b/static/babybuddy/js/graph.da32e0532ca2.js.gz deleted file mode 100644 index f7fa75b6..00000000 Binary files a/static/babybuddy/js/graph.da32e0532ca2.js.gz and /dev/null differ diff --git a/static/babybuddy/js/graph.js b/static/babybuddy/js/graph.js index e0637637..7cb2d646 100644 --- a/static/babybuddy/js/graph.js +++ b/static/babybuddy/js/graph.js @@ -1,5 +1,5 @@ /** -* plotly.js (cartesian) v1.48.2 +* plotly.js (cartesian) v1.51.3 * Copyright 2012-2019, Plotly, Inc. * All rights reserved. * Licensed under the MIT license @@ -72,137 +72,7 @@ for(var selector in rules) { Lib.addStyleRule(fullSelector, rules[selector]); } -},{"../src/lib":168}],2:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = { - 'undo': { - 'width': 857.1, - 'height': 1000, - 'path': 'm857 350q0-87-34-166t-91-137-137-92-166-34q-96 0-183 41t-147 114q-4 6-4 13t5 11l76 77q6 5 14 5 9-1 13-7 41-53 100-82t126-29q58 0 110 23t92 61 61 91 22 111-22 111-61 91-92 61-110 23q-55 0-105-20t-90-57l77-77q17-16 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l72-72q60 57 137 88t159 31q87 0 166-34t137-92 91-137 34-166z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'home': { - 'width': 928.6, - 'height': 1000, - 'path': 'm786 296v-267q0-15-11-26t-25-10h-214v214h-143v-214h-214q-15 0-25 10t-11 26v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-4-7 2-12 7l-35 41q-4 5-3 13t6 12l401 334q18 15 42 15t43-15l136-114v109q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q5-5 6-12t-4-13z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'camera-retro': { - 'width': 1000, - 'height': 1000, - 'path': 'm518 386q0 8-5 13t-13 5q-37 0-63-27t-26-63q0-8 5-13t13-5 12 5 5 13q0 23 16 38t38 16q8 0 13 5t5 13z m125-73q0-59-42-101t-101-42-101 42-42 101 42 101 101 42 101-42 42-101z m-572-320h858v71h-858v-71z m643 320q0 89-62 152t-152 62-151-62-63-152 63-151 151-63 152 63 62 151z m-571 358h214v72h-214v-72z m-72-107h858v143h-462l-36-71h-360v-72z m929 143v-714q0-30-21-51t-50-21h-858q-29 0-50 21t-21 51v714q0 30 21 51t50 21h858q29 0 50-21t21-51z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'zoombox': { - 'width': 1000, - 'height': 1000, - 'path': 'm1000-25l-250 251c40 63 63 138 63 218 0 224-182 406-407 406-224 0-406-182-406-406s183-406 407-406c80 0 155 22 218 62l250-250 125 125z m-812 250l0 438 437 0 0-438-437 0z m62 375l313 0 0-312-313 0 0 312z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'pan': { - 'width': 1000, - 'height': 1000, - 'path': 'm1000 350l-187 188 0-125-250 0 0 250 125 0-188 187-187-187 125 0 0-250-250 0 0 125-188-188 186-187 0 125 252 0 0-250-125 0 187-188 188 188-125 0 0 250 250 0 0-126 187 188z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'zoom_plus': { - 'width': 875, - 'height': 1000, - 'path': 'm1 787l0-875 875 0 0 875-875 0z m687-500l-187 0 0-187-125 0 0 187-188 0 0 125 188 0 0 187 125 0 0-187 187 0 0-125z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'zoom_minus': { - 'width': 875, - 'height': 1000, - 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'autoscale': { - 'width': 1000, - 'height': 1000, - 'path': 'm250 850l-187 0-63 0 0-62 0-188 63 0 0 188 187 0 0 62z m688 0l-188 0 0-62 188 0 0-188 62 0 0 188 0 62-62 0z m-875-938l0 188-63 0 0-188 0-62 63 0 187 0 0 62-187 0z m875 188l0-188-188 0 0-62 188 0 62 0 0 62 0 188-62 0z m-125 188l-1 0-93-94-156 156 156 156 92-93 2 0 0 250-250 0 0-2 93-92-156-156-156 156 94 92 0 2-250 0 0-250 0 0 93 93 157-156-157-156-93 94 0 0 0-250 250 0 0 0-94 93 156 157 156-157-93-93 0 0 250 0 0 250z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'tooltip_basic': { - 'width': 1500, - 'height': 1000, - 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'tooltip_compare': { - 'width': 1125, - 'height': 1000, - 'path': 'm187 786l0 2-187-188 188-187 0 0 937 0 0 373-938 0z m0-499l0 1-187-188 188-188 0 0 937 0 0 376-938-1z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'plotlylogo': { - 'width': 1542, - 'height': 1000, - 'path': 'm0-10h182v-140h-182v140z m228 146h183v-286h-183v286z m225 714h182v-1000h-182v1000z m225-285h182v-715h-182v715z m225 142h183v-857h-183v857z m231-428h182v-429h-182v429z m225-291h183v-138h-183v138z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'z-axis': { - 'width': 1000, - 'height': 1000, - 'path': 'm833 5l-17 108v41l-130-65 130-66c0 0 0 38 0 39 0-1 36-14 39-25 4-15-6-22-16-30-15-12-39-16-56-20-90-22-187-23-279-23-261 0-341 34-353 59 3 60 228 110 228 110-140-8-351-35-351-116 0-120 293-142 474-142 155 0 477 22 477 142 0 50-74 79-163 96z m-374 94c-58-5-99-21-99-40 0-24 65-43 144-43 79 0 143 19 143 43 0 19-42 34-98 40v216h87l-132 135-133-135h88v-216z m167 515h-136v1c16 16 31 34 46 52l84 109v54h-230v-71h124v-1c-16-17-28-32-44-51l-89-114v-51h245v72z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - '3d_rotate': { - 'width': 1000, - 'height': 1000, - 'path': 'm922 660c-5 4-9 7-14 11-359 263-580-31-580-31l-102 28 58-400c0 1 1 1 2 2 118 108 351 249 351 249s-62 27-100 42c88 83 222 183 347 122 16-8 30-17 44-27-2 1-4 2-6 4z m36-329c0 0 64 229-88 296-62 27-124 14-175-11 157-78 225-208 249-266 8-19 11-31 11-31 2 5 6 15 11 32-5-13-8-20-8-20z m-775-239c70-31 117-50 198-32-121 80-199 346-199 346l-96-15-58-12c0 0 55-226 155-287z m603 133l-317-139c0 0 4-4 19-14 7-5 24-15 24-15s-177-147-389 4c235-287 536-112 536-112l31-22 100 299-4-1z m-298-153c6-4 14-9 24-15 0 0-17 10-24 15z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'camera': { - 'width': 1000, - 'height': 1000, - 'path': 'm500 450c-83 0-150-67-150-150 0-83 67-150 150-150 83 0 150 67 150 150 0 83-67 150-150 150z m400 150h-120c-16 0-34 13-39 29l-31 93c-6 15-23 28-40 28h-340c-16 0-34-13-39-28l-31-94c-6-15-23-28-40-28h-120c-55 0-100-45-100-100v-450c0-55 45-100 100-100h800c55 0 100 45 100 100v450c0 55-45 100-100 100z m-400-550c-138 0-250 112-250 250 0 138 112 250 250 250 138 0 250-112 250-250 0-138-112-250-250-250z m365 380c-19 0-35 16-35 35 0 19 16 35 35 35 19 0 35-16 35-35 0-19-16-35-35-35z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'movie': { - 'width': 1000, - 'height': 1000, - 'path': 'm938 413l-188-125c0 37-17 71-44 94 64 38 107 107 107 187 0 121-98 219-219 219-121 0-219-98-219-219 0-61 25-117 66-156h-115c30 33 49 76 49 125 0 103-84 187-187 187s-188-84-188-187c0-57 26-107 65-141-38-22-65-62-65-109v-250c0-70 56-126 125-126h500c69 0 125 56 125 126l188-126c34 0 62 28 62 63v375c0 35-28 63-62 63z m-750 0c-69 0-125 56-125 125s56 125 125 125 125-56 125-125-56-125-125-125z m406-1c-87 0-157 70-157 157 0 86 70 156 157 156s156-70 156-156-70-157-156-157z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'question': { - 'width': 857.1, - 'height': 1000, - 'path': 'm500 82v107q0 8-5 13t-13 5h-107q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h107q8 0 13 5t5 13z m143 375q0 49-31 91t-77 65-95 23q-136 0-207-119-9-14 4-24l74-55q4-4 10-4 9 0 14 7 30 38 48 51 19 14 48 14 27 0 48-15t21-33q0-21-11-34t-38-25q-35-16-65-48t-29-70v-20q0-8 5-13t13-5h107q8 0 13 5t5 13q0 10 12 27t30 28q18 10 28 16t25 19 25 27 16 34 7 45z m214-107q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - 'disk': { - 'width': 857.1, - 'height': 1000, - 'path': 'm214-7h429v214h-429v-214z m500 0h72v500q0 8-6 21t-11 20l-157 156q-5 6-19 12t-22 5v-232q0-22-15-38t-38-16h-322q-22 0-37 16t-16 38v232h-72v-714h72v232q0 22 16 38t37 16h465q22 0 38-16t15-38v-232z m-214 518v178q0 8-5 13t-13 5h-107q-7 0-13-5t-5-13v-178q0-8 5-13t13-5h107q7 0 13 5t5 13z m357-18v-518q0-22-15-38t-38-16h-750q-23 0-38 16t-16 38v750q0 22 16 38t38 16h517q23 0 50-12t42-26l156-157q16-15 27-42t11-49z', - 'transform': 'matrix(1 0 0 -1 0 850)' - }, - '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)' - }, - 'newplotlylogo': { - 'name': 'newplotlylogo', - 'svg': 'plotly-logomark' - } -}; - -},{}],3:[function(_dereq_,module,exports){ +},{"../src/lib":169}],2:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -215,7 +85,7 @@ module.exports = { module.exports = _dereq_('../src/traces/bar'); -},{"../src/traces/bar":273}],4:[function(_dereq_,module,exports){ +},{"../src/traces/bar":276}],3:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -228,7 +98,7 @@ module.exports = _dereq_('../src/traces/bar'); module.exports = _dereq_('../src/traces/box'); -},{"../src/traces/box":287}],5:[function(_dereq_,module,exports){ +},{"../src/traces/box":290}],4:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -241,7 +111,7 @@ module.exports = _dereq_('../src/traces/box'); module.exports = _dereq_('../src/traces/contour'); -},{"../src/traces/contour":307}],6:[function(_dereq_,module,exports){ +},{"../src/traces/contour":310}],5:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -254,7 +124,7 @@ module.exports = _dereq_('../src/traces/contour'); module.exports = _dereq_('../src/core'); -},{"../src/core":151}],7:[function(_dereq_,module,exports){ +},{"../src/core":151}],6:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -267,7 +137,7 @@ module.exports = _dereq_('../src/core'); module.exports = _dereq_('../src/traces/heatmap'); -},{"../src/traces/heatmap":323}],8:[function(_dereq_,module,exports){ +},{"../src/traces/heatmap":326}],7:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -280,7 +150,7 @@ module.exports = _dereq_('../src/traces/heatmap'); module.exports = _dereq_('../src/traces/histogram'); -},{"../src/traces/histogram":341}],9:[function(_dereq_,module,exports){ +},{"../src/traces/histogram":344}],8:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -293,7 +163,7 @@ module.exports = _dereq_('../src/traces/histogram'); module.exports = _dereq_('../src/traces/histogram2d'); -},{"../src/traces/histogram2d":347}],10:[function(_dereq_,module,exports){ +},{"../src/traces/histogram2d":350}],9:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -306,7 +176,20 @@ module.exports = _dereq_('../src/traces/histogram2d'); module.exports = _dereq_('../src/traces/histogram2dcontour'); -},{"../src/traces/histogram2dcontour":351}],11:[function(_dereq_,module,exports){ +},{"../src/traces/histogram2dcontour":354}],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'; + +module.exports = _dereq_('../src/traces/image'); + +},{"../src/traces/image":361}],11:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -326,6 +209,7 @@ Plotly.register([ _dereq_('./histogram'), _dereq_('./histogram2d'), _dereq_('./histogram2dcontour'), + _dereq_('./image'), _dereq_('./pie'), _dereq_('./contour'), _dereq_('./scatterternary'), @@ -334,7 +218,7 @@ Plotly.register([ module.exports = Plotly; -},{"./bar":3,"./box":4,"./contour":5,"./core":6,"./heatmap":7,"./histogram":8,"./histogram2d":9,"./histogram2dcontour":10,"./pie":12,"./scatterternary":13,"./violin":14}],12:[function(_dereq_,module,exports){ +},{"./bar":2,"./box":3,"./contour":4,"./core":5,"./heatmap":6,"./histogram":7,"./histogram2d":8,"./histogram2dcontour":9,"./image":10,"./pie":12,"./scatterternary":13,"./violin":14}],12:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -347,7 +231,7 @@ module.exports = Plotly; module.exports = _dereq_('../src/traces/pie'); -},{"../src/traces/pie":358}],13:[function(_dereq_,module,exports){ +},{"../src/traces/pie":370}],13:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -360,7 +244,7 @@ module.exports = _dereq_('../src/traces/pie'); module.exports = _dereq_('../src/traces/scatterternary'); -},{"../src/traces/scatterternary":396}],14:[function(_dereq_,module,exports){ +},{"../src/traces/scatterternary":410}],14:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -373,7 +257,7 @@ module.exports = _dereq_('../src/traces/scatterternary'); module.exports = _dereq_('../src/traces/violin'); -},{"../src/traces/violin":404}],15:[function(_dereq_,module,exports){ +},{"../src/traces/violin":418}],15:[function(_dereq_,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -15219,7 +15103,7 @@ module.exports = templatedArray('annotation', { } }); -},{"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218,"../../plots/font_attributes":238,"./arrow_paths":35}],37:[function(_dereq_,module,exports){ +},{"../../plot_api/plot_template":203,"../../plots/cartesian/constants":219,"../../plots/font_attributes":239,"./arrow_paths":35}],37:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -15308,7 +15192,7 @@ function calcAxisExpansion(ann, ax) { ann._extremes[axId] = extremes; } -},{"../../lib":168,"../../plots/cartesian/axes":212,"./draw":42}],38:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"./draw":42}],38:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -15446,7 +15330,7 @@ function clickData2r(d, ax) { return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d); } -},{"../../lib":168,"../../plot_api/plot_template":202,"../../registry":256}],39:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plot_api/plot_template":203,"../../registry":258}],39:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -15525,7 +15409,7 @@ module.exports = function handleAnnotationCommonDefaults(annIn, annOut, fullLayo coerce('captureevents', !!hoverText); }; -},{"../../lib":168,"../color":51}],40:[function(_dereq_,module,exports){ +},{"../../lib":169,"../color":51}],40:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -15588,7 +15472,7 @@ module.exports = function convertCoords(gd, ax, newType, doExtra) { } }; -},{"../../lib/to_log_range":191,"fast-isnumeric":18}],41:[function(_dereq_,module,exports){ +},{"../../lib/to_log_range":192,"fast-isnumeric":18}],41:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -15695,7 +15579,7 @@ function handleAnnotationDefaults(annIn, annOut, fullLayout) { } } -},{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":36,"./common_defaults":39}],42:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/array_container_defaults":209,"../../plots/cartesian/axes":213,"./attributes":36,"./common_defaults":39}],42:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -15837,24 +15721,25 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { 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; - - var eventData = { - index: index, - annotation: options._input, - fullAnnotation: options, - event: d3.event - }; - - if(subplotId) { - eventData.subplotId = subplotId; - } - - gd.emit('plotly_clickannotation', eventData); + gd.emit('plotly_clickannotation', makeEventData(d3.event)); }); if(options.hovertext) { @@ -16089,9 +15974,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { 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) { + if(annotationIsOffscreen) { annTextGroupInner.remove(); return; } @@ -16359,6 +16242,11 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { setCursor(annTextGroupInner, csr); }, + clickFn: function(_, initialEvent) { + if(options.captureevents) { + gd.emit('plotly_clickannotation', makeEventData(initialEvent)); + } + }, doneFn: function() { setCursor(annTextGroupInner); Registry.call('_guiRelayout', gd, getUpdateObj()); @@ -16391,7 +16279,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { } else annText.call(textLayout); } -},{"../../lib":168,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/cartesian/axes":212,"../../plots/plots":244,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"../fx":90,"./draw_arrow_head":43,"d3":16}],43:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../lib/setcursor":188,"../../lib/svg_text_utils":190,"../../plot_api/plot_template":203,"../../plots/cartesian/axes":213,"../../plots/plots":245,"../../registry":258,"../color":51,"../dragelement":69,"../drawing":72,"../fx":89,"./draw_arrow_head":43,"d3":16}],43:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -16576,7 +16464,7 @@ module.exports = { convertCoords: _dereq_('./convert_coords') }; -},{"../../plots/cartesian/include_components":222,"./attributes":36,"./calc_autorange":37,"./click":38,"./convert_coords":40,"./defaults":41,"./draw":42}],45:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/include_components":223,"./attributes":36,"./calc_autorange":37,"./click":38,"./convert_coords":40,"./defaults":41,"./draw":42}],45:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -16588,12 +16476,12 @@ module.exports = { 'use strict'; -var annAtts = _dereq_('../annotations/attributes'); +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: annAtts.visible, + visible: annAttrs.visible, x: { valType: 'any', @@ -16620,41 +16508,41 @@ module.exports = overrideAll(templatedArray('annotation', { }, - xanchor: annAtts.xanchor, - xshift: annAtts.xshift, - yanchor: annAtts.yanchor, - yshift: annAtts.yshift, + xanchor: annAttrs.xanchor, + xshift: annAttrs.xshift, + yanchor: annAttrs.yanchor, + yshift: annAttrs.yshift, - text: annAtts.text, - textangle: annAtts.textangle, - font: annAtts.font, - width: annAtts.width, - height: annAtts.height, - opacity: annAtts.opacity, - align: annAtts.align, - valign: annAtts.valign, - bgcolor: annAtts.bgcolor, - bordercolor: annAtts.bordercolor, - borderpad: annAtts.borderpad, - borderwidth: annAtts.borderwidth, - showarrow: annAtts.showarrow, - arrowcolor: annAtts.arrowcolor, - arrowhead: annAtts.arrowhead, - startarrowhead: annAtts.startarrowhead, - arrowside: annAtts.arrowside, - arrowsize: annAtts.arrowsize, - startarrowsize: annAtts.startarrowsize, - arrowwidth: annAtts.arrowwidth, - standoff: annAtts.standoff, - startstandoff: annAtts.startstandoff, - hovertext: annAtts.hovertext, - hoverlabel: annAtts.hoverlabel, - captureevents: annAtts.captureevents, + 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: annAtts.clicktoshow, - // xclick: annAtts.xclick, - // yclick: annAtts.yclick, + // clicktoshow: annAttrs.clicktoshow, + // xclick: annAttrs.xclick, + // yclick: annAttrs.yclick, // not needed! // axref: 'pixel' @@ -16664,7 +16552,7 @@ module.exports = overrideAll(templatedArray('annotation', { // zref: 'z' }), 'calc', 'from-root'); -},{"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../annotations/attributes":36}],46:[function(_dereq_,module,exports){ +},{"../../plot_api/edit_types":196,"../../plot_api/plot_template":203,"../annotations/attributes":36}],46:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -16729,7 +16617,7 @@ function mockAnnAxes(ann, scene) { }; } -},{"../../lib":168,"../../plots/cartesian/axes":212}],47:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213}],47:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -16805,7 +16693,7 @@ function handleAnnotationDefaults(annIn, annOut, sceneLayout, opts) { } } -},{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"../annotations/common_defaults":39,"./attributes":45}],48:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/array_container_defaults":209,"../../plots/cartesian/axes":213,"../annotations/common_defaults":39,"./attributes":45}],48:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -16857,7 +16745,7 @@ module.exports = function draw(scene) { } }; -},{"../../plots/gl3d/project":241,"../annotations/draw":42}],49:[function(_dereq_,module,exports){ +},{"../../plots/gl3d/project":242,"../annotations/draw":42}],49:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -16905,7 +16793,7 @@ function includeGL3D(layoutIn, layoutOut) { } } -},{"../../lib":168,"../../registry":256,"./attributes":45,"./convert":46,"./defaults":47,"./draw":48}],50:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"./attributes":45,"./convert":46,"./defaults":47,"./draw":48}],50:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -17298,7 +17186,7 @@ module.exports = overrideAll({ } }, 'colorbars', 'from-root'); -},{"../../lib/extend":162,"../../plot_api/edit_types":195,"../../plots/cartesian/layout_attributes":224,"../../plots/font_attributes":238}],53:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plot_api/edit_types":196,"../../plots/cartesian/layout_attributes":225,"../../plots/font_attributes":239}],53:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -17392,7 +17280,7 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) { coerce('title.side'); }; -},{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/cartesian/tick_label_defaults":231,"../../plots/cartesian/tick_mark_defaults":232,"../../plots/cartesian/tick_value_defaults":233,"./attributes":52}],55:[function(_dereq_,module,exports){ +},{"../../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":52}],55:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -17623,7 +17511,8 @@ function drawColorBar(g, opts, gd) { opts._xLeftFrac = xLeftFrac; opts._yBottomFrac = yBottomFrac; - var ax = mockColorBarAxis(gd, opts, zrange); + // stash mocked axis for contour label formatting + var ax = opts._axis = mockColorBarAxis(gd, opts, zrange); // position can't go in through supplyDefaults // because that restricts it to [0,1] @@ -18117,7 +18006,7 @@ module.exports = { draw: draw }; -},{"../../constants/alignment":146,"../../lib":168,"../../lib/extend":162,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_defaults":214,"../../plots/cartesian/layout_attributes":224,"../../plots/cartesian/position_defaults":227,"../../plots/plots":244,"../../registry":256,"../color":51,"../colorscale/helpers":62,"../dragelement":69,"../drawing":72,"../titles":139,"./constants":53,"d3":16,"tinycolor2":34}],56:[function(_dereq_,module,exports){ +},{"../../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":258,"../color":51,"../colorscale/helpers":62,"../dragelement":69,"../drawing":72,"../titles":138,"./constants":53,"d3":16,"tinycolor2":34}],56:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18136,7 +18025,7 @@ module.exports = function hasColorbar(container) { return Lib.isPlainObject(container.colorbar); }; -},{"../../lib":168}],57:[function(_dereq_,module,exports){ +},{"../../lib":169}],57:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18366,7 +18255,7 @@ module.exports = function colorScaleAttrs(context, opts) { return attrs; }; -},{"../../lib/regex":183,"../colorbar/attributes":52,"./scales.js":66}],59:[function(_dereq_,module,exports){ +},{"../../lib/regex":184,"../colorbar/attributes":52,"./scales.js":66}],59:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18445,7 +18334,7 @@ module.exports = function calc(gd, trace, opts) { } }; -},{"../../lib":168,"./helpers":62,"fast-isnumeric":18}],60:[function(_dereq_,module,exports){ +},{"../../lib":169,"./helpers":62,"fast-isnumeric":18}],60:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18468,7 +18357,7 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { } } - function relinkColorAtts(outerCont, cbOpt) { + function relinkColorAttrs(outerCont, cbOpt) { var cont = cbOpt.container ? Lib.nestedProperty(outerCont, cbOpt.container).get() : outerCont; @@ -18501,15 +18390,15 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { if(cbOpts) { if(Array.isArray(cbOpts)) { for(var j = 0; j < cbOpts.length; j++) { - relinkColorAtts(trace, cbOpts[j]); + relinkColorAttrs(trace, cbOpts[j]); } } else { - relinkColorAtts(trace, cbOpts); + relinkColorAttrs(trace, cbOpts); } } if(hasColorscale(trace, 'marker.line')) { - relinkColorAtts(trace, { + relinkColorAttrs(trace, { container: 'marker.line', min: 'cmin', max: 'cmax' @@ -18518,11 +18407,11 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { } for(var k in fullLayout._colorAxes) { - relinkColorAtts(fullLayout[k], {min: 'cmin', max: 'cmax'}); + relinkColorAttrs(fullLayout[k], {min: 'cmin', max: 'cmax'}); } }; -},{"../../lib":168,"./helpers":62}],61:[function(_dereq_,module,exports){ +},{"../../lib":169,"./helpers":62}],61:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18644,7 +18533,7 @@ module.exports = function colorScaleDefaults(parentContIn, parentContOut, layout } }; -},{"../../lib":168,"../../registry":256,"../colorbar/defaults":54,"../colorbar/has_colorbar":56,"./scales":66,"fast-isnumeric":18}],62:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"../colorbar/defaults":54,"../colorbar/has_colorbar":56,"./scales":66,"fast-isnumeric":18}],62:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18664,11 +18553,11 @@ var Color = _dereq_('../color'); var isValidScale = _dereq_('./scales').isValid; -function hasColorscale(trace, containerStr) { +function hasColorscale(trace, containerStr, colorKey) { var container = containerStr ? Lib.nestedProperty(trace, containerStr).get() || {} : trace; - var color = container.color; + var color = container[colorKey || 'color']; var isArrayWithOneNumber = false; if(Lib.isArrayOrTypedArray(color)) { @@ -18884,7 +18773,7 @@ module.exports = { makeColorScaleFuncFromTrace: makeColorScaleFuncFromTrace }; -},{"../../lib":168,"../color":51,"./scales":66,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],63:[function(_dereq_,module,exports){ +},{"../../lib":169,"../color":51,"./scales":66,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],63:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -18987,7 +18876,7 @@ module.exports = { })) }; -},{"../../lib/extend":162,"./attributes":58,"./scales":66}],65:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"./attributes":58,"./scales":66}],65:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -19038,7 +18927,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { } }; -},{"../../lib":168,"../../plot_api/plot_template":202,"./defaults":61,"./layout_attributes":64}],66:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plot_api/plot_template":203,"./defaults":61,"./layout_attributes":64}],66:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -19313,7 +19202,7 @@ module.exports = function getCursor(x, y, xanchor, yanchor) { return cursorset[y][x]; }; -},{"../../lib":168}],69:[function(_dereq_,module,exports){ +},{"../../lib":169}],69:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -19330,7 +19219,6 @@ var supportsPassive = _dereq_('has-passive-events'); var removeElement = _dereq_('../../lib').removeElement; var constants = _dereq_('../../plots/cartesian/constants'); -var interactConstants = _dereq_('../../constants/interactions'); var dragElement = module.exports = {}; @@ -19398,7 +19286,7 @@ dragElement.unhoverRaw = unhover.raw; dragElement.init = function init(options) { var gd = options.gd; var numClicks = 1; - var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY; + var doubleClickDelay = gd._context.doubleClickDelay; var element = options.element; var startX, @@ -19453,7 +19341,7 @@ dragElement.init = function init(options) { } newMouseDownTime = (new Date()).getTime(); - if(newMouseDownTime - gd._mouseDownTime < DBLCLICKDELAY) { + if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) { // in a click train numClicks += 1; } else { @@ -19480,7 +19368,7 @@ dragElement.init = function init(options) { if(options.dragmode !== false) { e.preventDefault(); document.addEventListener('mousemove', onMove); - document.addEventListener('touchmove', onMove); + document.addEventListener('touchmove', onMove, {passive: false}); } return; @@ -19539,7 +19427,7 @@ dragElement.init = function init(options) { // don't count as a dblClick unless the mouseUp is also within // the dblclick delay - if((new Date()).getTime() - gd._mouseDownTime > DBLCLICKDELAY) { + if((new Date()).getTime() - gd._mouseDownTime > doubleClickDelay) { numClicks = Math.max(numClicks - 1, 1); } @@ -19606,7 +19494,7 @@ function pointerOffset(e) { ); } -},{"../../constants/interactions":148,"../../lib":168,"../../plots/cartesian/constants":218,"./align":67,"./cursor":68,"./unhover":70,"has-hover":20,"has-passive-events":21,"mouse-event-offset":24}],70:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/constants":219,"./align":67,"./cursor":68,"./unhover":70,"has-hover":20,"has-passive-events":21,"mouse-event-offset":24}],70:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -19615,19 +19503,16 @@ function pointerOffset(e) { * 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/get_graph_div'); +var getGraphDiv = _dereq_('../../lib/dom').getGraphDiv; var hoverConstants = _dereq_('../fx/constants'); var unhover = module.exports = {}; - unhover.wrapped = function(gd, evt, subplot) { gd = getGraphDiv(gd); @@ -19664,7 +19549,7 @@ unhover.raw = function raw(gd, evt) { } }; -},{"../../lib/events":161,"../../lib/get_graph_div":166,"../../lib/throttle":190,"../fx/constants":84}],71:[function(_dereq_,module,exports){ +},{"../../lib/dom":162,"../../lib/events":163,"../../lib/throttle":191,"../fx/constants":84}],71:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -19718,6 +19603,7 @@ var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM; var subTypes = _dereq_('../../traces/scatter/subtypes'); var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func'); +var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue; var drawing = module.exports = {}; @@ -20133,7 +20019,7 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) { fill: 'none' }); } else { - sel.style('stroke-width', lineWidth + 'px'); + sel.style('stroke-width', (d.isBlank ? 0 : lineWidth) + 'px'); var markerGradient = marker.gradient; @@ -20375,21 +20261,34 @@ drawing.textPointStyle = function(s, trace, gd) { if(!s.size()) return; var selectedTextColorFn; - if(trace.selectedpoints) { var fns = drawing.makeSelectedTextStyleFns(trace); selectedTextColorFn = fns.selectedTextColorFn; } + var texttemplate = trace.texttemplate; + var fullLayout = gd._fullLayout; + s.each(function(d) { var p = d3.select(this); - var text = Lib.extractOption(d, trace, 'tx', 'text'); + + var text = texttemplate ? + Lib.extractOption(d, trace, 'txt', 'texttemplate') : + Lib.extractOption(d, trace, 'tx', 'text'); if(!text && text !== 0) { p.remove(); return; } + if(texttemplate) { + var labels = trace._module.formatLabels ? trace._module.formatLabels(d, trace, fullLayout) : {}; + var pointValues = {}; + appendArrayPointValue(pointValues, trace, d.i); + var meta = trace._meta || {}; + text = Lib.texttemplateString(text, labels, fullLayout._d3locale, pointValues, d, meta); + } + var pos = d.tp || trace.textposition; var fontSize = extracTextFontSize(d, trace); var fontColor = selectedTextColorFn ? @@ -20838,7 +20737,7 @@ drawing.setTextPointsScale = function(selection, xScale, yScale) { }); }; -},{"../../constants/alignment":146,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../lib/svg_text_utils":189,"../../registry":256,"../../traces/scatter/make_bubble_size_func":381,"../../traces/scatter/subtypes":388,"../color":51,"../colorscale":63,"./symbol_defs":73,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],73:[function(_dereq_,module,exports){ +},{"../../components/fx/helpers":86,"../../constants/alignment":145,"../../constants/interactions":148,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../lib/svg_text_utils":190,"../../registry":258,"../../traces/scatter/make_bubble_size_func":394,"../../traces/scatter/subtypes":401,"../color":51,"../colorscale":63,"./symbol_defs":73,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],73:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -21527,7 +21426,7 @@ function calcOneAxis(calcTrace, trace, axis, coord) { baseExtremes.max = baseExtremes.max.concat(extremes.max); } -},{"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":256,"./compute_error":76,"fast-isnumeric":18}],76:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"./compute_error":76,"fast-isnumeric":18}],76:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -21703,7 +21602,7 @@ module.exports = function(traceIn, traceOut, defaultColor, opts) { } }; -},{"../../lib":168,"../../plot_api/plot_template":202,"../../registry":256,"./attributes":74,"fast-isnumeric":18}],78:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plot_api/plot_template":203,"../../registry":258,"./attributes":74,"fast-isnumeric":18}],78:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -21772,7 +21671,7 @@ function hoverInfo(calcPoint, trace, hoverPoint) { } } -},{"../../lib":168,"../../plot_api/edit_types":195,"./attributes":74,"./calc":75,"./compute_error":76,"./defaults":77,"./plot":79,"./style":80}],79:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plot_api/edit_types":196,"./attributes":74,"./calc":75,"./compute_error":76,"./defaults":77,"./plot":79,"./style":80}],79:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -21944,7 +21843,7 @@ function errorCoords(d, xa, ya) { return out; } -},{"../../traces/scatter/subtypes":388,"../drawing":72,"d3":16,"fast-isnumeric":18}],80:[function(_dereq_,module,exports){ +},{"../../traces/scatter/subtypes":401,"../drawing":72,"d3":16,"fast-isnumeric":18}],80:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -22017,7 +21916,7 @@ module.exports = { } }; -},{"../../lib/extend":162,"../../plots/font_attributes":238,"./layout_attributes":91}],82:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plots/font_attributes":239,"./layout_attributes":90}],82:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -22076,7 +21975,7 @@ function paste(traceAttr, cd, cdAttr, fn) { } } -},{"../../lib":168,"../../registry":256}],83:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],83:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -22113,7 +22012,7 @@ module.exports = function click(gd, evt, subplot) { } }; -},{"../../registry":256,"./hover":87}],84:[function(_dereq_,module,exports){ +},{"../../registry":258,"./hover":87}],84:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -22171,7 +22070,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleHoverLabelDefaults(traceIn, traceOut, coerce, opts); }; -},{"../../lib":168,"./attributes":81,"./hoverlabel_defaults":88}],86:[function(_dereq_,module,exports){ +},{"../../lib":169,"./attributes":81,"./hoverlabel_defaults":88}],86:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -22413,7 +22312,7 @@ function getPointData(val, pointNumber) { } } -},{"../../lib":168}],87:[function(_dereq_,module,exports){ +},{"../../lib":169}],87:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -22640,24 +22539,20 @@ function _hover(gd, evt, subplot, noHoverEvent) { for(var i = 0; i < len; i++) { var spId = subplots[i]; - // 'cartesian' case - var plotObj = plots[spId]; - if(plotObj) { + if(plots[spId]) { + // 'cartesian' case 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; + xaArray[i] = plots[spId].xaxis; + yaArray[i] = plots[spId].yaxis; + } else if(fullLayout[spId] && fullLayout[spId]._subplot) { + // other subplot types + var _subplot = fullLayout[spId]._subplot; + xaArray[i] = _subplot.xaxis; + yaArray[i] = _subplot.yaxis; + } else { + Lib.warn('Unrecognized subplot: ' + spId); + return; } - - // other subplot types - var _subplot = fullLayout[spId]._subplot; - xaArray[i] = _subplot.xaxis; - yaArray[i] = _subplot.yaxis; } var hovermode = evt.hovermode || fullLayout.hovermode; @@ -23021,7 +22916,7 @@ function _hover(gd, evt, subplot, noHoverEvent) { var result = dragElement.unhoverRaw(gd, evt); if(hasCartesian && ((spikePoints.hLinePoint !== null) || (spikePoints.vLinePoint !== null))) { if(spikesChanged(oldspikepoints)) { - createSpikelines(spikePoints, spikelineOpts); + createSpikelines(gd, spikePoints, spikelineOpts); } } return result; @@ -23029,7 +22924,7 @@ function _hover(gd, evt, subplot, noHoverEvent) { if(hasCartesian) { if(spikesChanged(oldspikepoints)) { - createSpikelines(spikePoints, spikelineOpts); + createSpikelines(gd, spikePoints, spikelineOpts); } } @@ -23192,6 +23087,11 @@ function createHoverText(hoverData, opts, gd) { var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine; var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor); var contrastColor = Color.contrast(commonBgColor); + var commonLabelFont = { + family: commonLabelOpts.font.family || fontFamily, + size: commonLabelOpts.font.size || fontSize, + color: commonLabelOpts.font.color || contrastColor + }; lpath.style({ fill: commonBgColor, @@ -23199,41 +23099,76 @@ function createHoverText(hoverData, opts, gd) { }); ltext.text(t0) - .call(Drawing.font, - commonLabelOpts.font.family || fontFamily, - commonLabelOpts.font.size || fontSize, - commonLabelOpts.font.color || contrastColor - ) + .call(Drawing.font, commonLabelFont) .call(svgTextUtils.positionText, 0, 0) .call(svgTextUtils.convertToTspans, gd); label.attr('transform', ''); var tbb = ltext.node().getBoundingClientRect(); + var lx, ly; + if(hovermode === 'x') { + var topsign = xa.side === 'top' ? '-' : ''; + ltext.attr('text-anchor', 'middle') .call(svgTextUtils.positionText, 0, (xa.side === 'top' ? (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) : (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD))); - 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'); + lx = xa._offset + (c0.x0 + c0.x1) / 2; + ly = ya._offset + (xa.side === 'top' ? 0 : ya._length); - label.attr('transform', 'translate(' + - (xa._offset + (c0.x0 + c0.x1) / 2) + ',' + - (ya._offset + (xa.side === 'top' ? 0 : ya._length)) + ')'); + var halfWidth = tbb.width / 2 + HOVERTEXTPAD; + + if(lx < halfWidth) { + lx = halfWidth; + + lpath.attr('d', 'M-' + (halfWidth - HOVERARROWSIZE) + ',0' + + 'L-' + (halfWidth - HOVERARROWSIZE * 2) + ',' + topsign + HOVERARROWSIZE + + 'H' + (HOVERTEXTPAD + tbb.width / 2) + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + halfWidth + + 'V' + topsign + HOVERARROWSIZE + + 'Z'); + } else if(lx > (fullLayout.width - halfWidth)) { + lx = fullLayout.width - halfWidth; + + lpath.attr('d', 'M' + (halfWidth - HOVERARROWSIZE) + ',0' + + 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + halfWidth + + 'V' + topsign + HOVERARROWSIZE + + 'H' + (halfWidth - HOVERARROWSIZE * 2) + 'Z'); + } else { + lpath.attr('d', 'M0,0' + + 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE + + 'H' + (HOVERTEXTPAD + tbb.width / 2) + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + (HOVERTEXTPAD + tbb.width / 2) + + 'V' + topsign + HOVERARROWSIZE + + 'H-' + HOVERARROWSIZE + 'Z'); + } } else { - 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 anchor; + var sgn; + var leftsign; + if(ya.side === 'right') { + anchor = 'start'; + sgn = 1; + leftsign = ''; + lx = xa._offset + xa._length; + } else { + anchor = 'end'; + sgn = -1; + leftsign = '-'; + lx = xa._offset; + } + + ly = ya._offset + (c0.y0 + c0.y1) / 2; + + ltext.attr('text-anchor', anchor); - var leftsign = ya.side === 'right' ? '' : '-'; lpath.attr('d', 'M0,0' + 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE + 'V' + (HOVERTEXTPAD + tbb.height / 2) + @@ -23241,10 +23176,49 @@ function createHoverText(hoverData, opts, gd) { '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) + ')'); + var halfHeight = tbb.height / 2; + var lty = outerTop - tbb.top - halfHeight; + var clipId = 'clip' + fullLayout._uid + 'commonlabel' + ya._id; + var clipPath; + + if(lx < (tbb.width + 2 * HOVERTEXTPAD + HOVERARROWSIZE)) { + clipPath = 'M-' + (HOVERARROWSIZE + HOVERTEXTPAD) + '-' + halfHeight + + 'h-' + (tbb.width - HOVERTEXTPAD) + + 'V' + halfHeight + + 'h' + (tbb.width - HOVERTEXTPAD) + 'Z'; + + var ltx = tbb.width - lx + HOVERTEXTPAD; + svgTextUtils.positionText(ltext, ltx, lty); + + // shift each line (except the longest) so that start-of-line + // is always visible + if(anchor === 'end') { + ltext.selectAll('tspan').each(function() { + var s = d3.select(this); + var dummy = Drawing.tester.append('text') + .text(s.text()) + .call(Drawing.font, commonLabelFont); + var dummyBB = dummy.node().getBoundingClientRect(); + if(Math.round(dummyBB.width) < Math.round(tbb.width)) { + s.attr('x', ltx - dummyBB.width); + } + dummy.remove(); + }); + } + } else { + svgTextUtils.positionText(ltext, sgn * (HOVERTEXTPAD + HOVERARROWSIZE), lty); + clipPath = null; + } + + var textClip = fullLayout._topclips.selectAll('#' + clipId).data(clipPath ? [0] : []); + textClip.enter().append('clipPath').attr('id', clipId).append('path'); + textClip.exit().remove(); + textClip.select('path').attr('d', clipPath); + Drawing.setClipUrl(ltext, clipPath ? clipId : null, gd); } + + label.attr('transform', 'translate(' + lx + ',' + ly + ')'); + // remove the "close but not quite" points // because of error bars, only take up to a space hoverData = hoverData.filter(function(d) { @@ -23255,10 +23229,11 @@ function createHoverText(hoverData, opts, gd) { // 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') @@ -23312,11 +23287,15 @@ function createHoverText(hoverData, opts, gd) { if(d.zLabel !== undefined) { if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '
    '; if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '
    '; - text += (text ? 'z: ' : '') + d.zLabel; + 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; + 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 + ')'; @@ -23471,22 +23450,25 @@ function createHoverText(hoverData, opts, gd) { // 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, ax, fullLayout) { +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, i) { - var axis = d[ax]; - var axIsX = axis._id.charAt(0) === 'x'; - var rng = axis.range; - if(!i && rng && ((rng[0] > rng[1]) !== axIsX)) axSign = -1; - pointgroups[i] = [{ + 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, - i: i, traceIndex: d.trace.index, dp: 0, pos: d.pos, @@ -23804,9 +23786,10 @@ function cleanPoint(d, hovermode) { return d; } -function createSpikelines(closestPoints, opts) { +function createSpikelines(gd, closestPoints, opts) { var container = opts.container; var fullLayout = opts.fullLayout; + var gs = fullLayout._size; var evt = opts.event; var showY = !!closestPoints.hLinePoint; var showX = !!closestPoints.vLinePoint; @@ -23841,8 +23824,7 @@ function createSpikelines(closestPoints, opts) { 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 xEdge = Axes.getPxPosition(gd, ya); var xBase, xEndSpike; if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) { @@ -23851,8 +23833,14 @@ function createSpikelines(closestPoints, opts) { xEndSpike = hLinePointX; } if(yMode.indexOf('across') !== -1) { - xBase = ya._counterSpan[0]; - xEndSpike = ya._counterSpan[1]; + var xAcross0 = ya._counterDomainMin; + var xAcross1 = ya._counterDomainMax; + if(ya.anchor === 'free') { + xAcross0 = Math.min(xAcross0, ya.position); + xAcross1 = Math.max(xAcross1, ya.position); + } + xBase = gs.l + xAcross0 * gs.w; + xEndSpike = gs.l + xAcross1 * gs.w; } // Foreground horizontal line (to y-axis) @@ -23915,8 +23903,7 @@ function createSpikelines(closestPoints, opts) { 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 yEdge = Axes.getPxPosition(gd, xa); var yBase, yEndSpike; if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) { @@ -23925,8 +23912,14 @@ function createSpikelines(closestPoints, opts) { yEndSpike = vLinePointY; } if(xMode.indexOf('across') !== -1) { - yBase = xa._counterSpan[0]; - yEndSpike = xa._counterSpan[1]; + var yAcross0 = xa._counterDomainMin; + var yAcross1 = xa._counterDomainMax; + if(xa.anchor === 'free') { + yAcross0 = Math.min(yAcross0, xa.position); + yAcross1 = Math.max(yAcross1, xa.position); + } + yBase = gs.t + (1 - yAcross1) * gs.h; + yEndSpike = gs.t + (1 - yAcross0) * gs.h; } // Foreground vertical line (to x-axis) @@ -24005,7 +23998,7 @@ function plainText(s, len) { }); } -},{"../../lib":168,"../../lib/events":161,"../../lib/override_cursor":179,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"./constants":84,"./helpers":86,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],88:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../lib/events":163,"../../lib/override_cursor":180,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../registry":258,"../color":51,"../dragelement":69,"../drawing":72,"./constants":84,"./helpers":86,"d3":16,"fast-isnumeric":18,"tinycolor2":34}],88:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24028,52 +24021,7 @@ module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts coerce('hoverlabel.align', opts.align); }; -},{"../../lib":168}],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'; - -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; -}; - -},{}],90:[function(_dereq_,module,exports){ +},{"../../lib":169}],89:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24152,7 +24100,7 @@ function castHoverinfo(trace, fullLayout, ptNumber) { return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce); } -},{"../../lib":168,"../dragelement":69,"./attributes":81,"./calc":82,"./click":83,"./constants":84,"./defaults":85,"./helpers":86,"./hover":87,"./layout_attributes":91,"./layout_defaults":92,"./layout_global_defaults":93,"d3":16}],91:[function(_dereq_,module,exports){ +},{"../../lib":169,"../dragelement":69,"./attributes":81,"./calc":82,"./click":83,"./constants":84,"./defaults":85,"./helpers":86,"./hover":87,"./layout_attributes":90,"./layout_defaults":91,"./layout_global_defaults":92,"d3":16}],90:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24255,7 +24203,7 @@ module.exports = { } }; -},{"../../plots/font_attributes":238,"./constants":84}],92:[function(_dereq_,module,exports){ +},{"../../plots/font_attributes":239,"./constants":84}],91:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24329,7 +24277,7 @@ function isHoriz(fullData, fullLayout) { return true; } -},{"../../lib":168,"./layout_attributes":91}],93:[function(_dereq_,module,exports){ +},{"../../lib":169,"./layout_attributes":90}],92:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24352,7 +24300,7 @@ module.exports = function supplyLayoutGlobalDefaults(layoutIn, layoutOut) { handleHoverLabelDefaults(layoutIn, layoutOut, coerce); }; -},{"../../lib":168,"./hoverlabel_defaults":88,"./layout_attributes":91}],94:[function(_dereq_,module,exports){ +},{"../../lib":169,"./hoverlabel_defaults":88,"./layout_attributes":90}],93:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24726,7 +24674,7 @@ module.exports = { contentDefaults: contentDefaults }; -},{"../../lib":168,"../../lib/regex":183,"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218,"../../plots/domain":237}],95:[function(_dereq_,module,exports){ +},{"../../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. @@ -24861,7 +24809,7 @@ module.exports = templatedArray('image', { editType: 'arraydraw' }); -},{"../../plot_api/plot_template":202,"../../plots/cartesian/constants":218}],96:[function(_dereq_,module,exports){ +},{"../../plot_api/plot_template":203,"../../plots/cartesian/constants":219}],95:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -24942,7 +24890,7 @@ module.exports = function convertCoords(gd, ax, newType, doExtra) { } }; -},{"../../lib/to_log_range":191,"fast-isnumeric":18}],97:[function(_dereq_,module,exports){ +},{"../../lib/to_log_range":192,"fast-isnumeric":18}],96:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -25007,7 +24955,7 @@ function imageDefaults(imageIn, imageOut, fullLayout) { return imageOut; } -},{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":95}],98:[function(_dereq_,module,exports){ +},{"../../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. @@ -25084,49 +25032,54 @@ module.exports = function draw(gd) { function setImage(d) { var thisImage = d3.select(this); - if(this.img && this.img.src === d.source) { + if(this._imgSrc === d.source) { return; } thisImage.attr('xmlns', xmlnsNamespaces.svg); - var imagePromise = new Promise(function(resolve) { - var img = new Image(); - this.img = img; + if(d.source && d.source.slice(0, 5) === 'data:') { + thisImage.attr('xlink:href', d.source); + this._imgSrc = d.source; + } else { + var imagePromise = new Promise(function(resolve) { + var img = new Image(); + this.img = img; - // If not set, a `tainted canvas` error is thrown - img.setAttribute('crossOrigin', 'anonymous'); - img.onerror = errorHandler; - img.onload = function() { - var canvas = document.createElement('canvas'); - canvas.width = this.width; - canvas.height = this.height; + // 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 ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0); - var dataURL = canvas.toDataURL('image/png'); + var dataURL = canvas.toDataURL('image/png'); - thisImage.attr('xlink:href', dataURL); + 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(); - }; + // 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); - thisImage.on('error', errorHandler); + img.src = d.source; + this._imgSrc = d.source; - img.src = d.source; + function errorHandler() { + thisImage.remove(); + resolve(); + } + }.bind(this)); - function errorHandler() { - thisImage.remove(); - resolve(); - } - }.bind(this)); - - gd._promises.push(imagePromise); + gd._promises.push(imagePromise); + } } function applyAttributes(d) { @@ -25227,7 +25180,7 @@ module.exports = function draw(gd) { } }; -},{"../../constants/xmlns_namespaces":150,"../../plots/cartesian/axes":212,"../drawing":72,"d3":16}],99:[function(_dereq_,module,exports){ +},{"../../constants/xmlns_namespaces":150,"../../plots/cartesian/axes":213,"../drawing":72,"d3":16}],98:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -25251,7 +25204,7 @@ module.exports = { convertCoords: _dereq_('./convert_coords') }; -},{"../../plots/cartesian/include_components":222,"./attributes":95,"./convert_coords":96,"./defaults":97,"./draw":98}],100:[function(_dereq_,module,exports){ +},{"../../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. @@ -25346,7 +25299,6 @@ module.exports = { valType: 'number', min: -2, max: 3, - dflt: 1.02, editType: 'legend', @@ -25363,7 +25315,6 @@ module.exports = { valType: 'number', min: -2, max: 3, - dflt: 1, editType: 'legend', @@ -25371,7 +25322,6 @@ module.exports = { yanchor: { valType: 'enumerated', values: ['auto', 'top', 'middle', 'bottom'], - dflt: 'auto', editType: 'legend', @@ -25393,7 +25343,7 @@ module.exports = { editType: 'legend' }; -},{"../../plots/font_attributes":238,"../color/attributes":50}],101:[function(_dereq_,module,exports){ +},{"../../plots/font_attributes":239,"../color/attributes":50}],100:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -25409,10 +25359,15 @@ module.exports = { scrollBarMinHeight: 20, scrollBarColor: '#808BA4', scrollBarMargin: 4, - textOffsetX: 40 + scrollBarEnterAttrs: {rx: 20, ry: 3, width: 0, height: 0}, + + // number of px between legend symbol and legend text (always in x direction) + textGap: 40, + // number of px between each legend item (x and/or y direction) + itemGap: 5 }; -},{}],102:[function(_dereq_,module,exports){ +},{}],101:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -25439,8 +25394,6 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { var legendReallyHasATrace = false; var defaultOrder = 'normal'; - var defaultX, defaultY, defaultXAnchor, defaultYAnchor; - for(var i = 0; i < fullData.length; i++) { var trace = fullData[i]; @@ -25497,20 +25450,26 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { 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'; + var orientation = coerce('orientation'); + var defaultX, defaultY, defaultYAnchor; + + if(orientation === 'h') { + defaultX = 0; + + if(Registry.getComponentMethod('rangeslider', 'isVisible')(layoutIn.xaxis)) { defaultY = 1.1; defaultYAnchor = 'bottom'; } else { - defaultX = 0; - defaultXAnchor = 'left'; + // maybe use y=1.1 / yanchor=bottom as above + // to avoid https://github.com/plotly/plotly.js/issues/1199 + // in v2 defaultY = -0.1; defaultYAnchor = 'top'; } + } else { + defaultX = 1.02; + defaultY = 1; + defaultYAnchor = 'auto'; } coerce('traceorder', defaultOrder); @@ -25522,14 +25481,14 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { coerce('itemdoubleclick'); coerce('x', defaultX); - coerce('xanchor', defaultXAnchor); + coerce('xanchor'); coerce('y', defaultY); coerce('yanchor', defaultYAnchor); coerce('valign'); Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); }; -},{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/layout_attributes":242,"../../registry":256,"./attributes":100,"./helpers":106}],103:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plot_api/plot_template":203,"../../plots/layout_attributes":243,"../../registry":258,"./attributes":99,"./helpers":105}],102:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -25553,7 +25512,6 @@ var svgTextUtils = _dereq_('../../lib/svg_text_utils'); var handleClick = _dereq_('./handle_click'); var constants = _dereq_('./constants'); -var interactConstants = _dereq_('../../constants/interactions'); var alignmentConstants = _dereq_('../../constants/alignment'); var LINE_SPACING = alignmentConstants.LINE_SPACING; var FROM_TL = alignmentConstants.FROM_TL; @@ -25563,8 +25521,6 @@ var getLegendData = _dereq_('./get_legend_data'); var style = _dereq_('./style'); var helpers = _dereq_('./helpers'); -var DBLCLICKDELAY = interactConstants.DBLCLICKDELAY; - module.exports = function draw(gd) { var fullLayout = gd._fullLayout; var clipId = 'legend' + fullLayout._uid; @@ -25580,26 +25536,11 @@ module.exports = function draw(gd) { if(!fullLayout.showlegend || !legendData.length) { fullLayout._infolayer.selectAll('.legend').remove(); fullLayout._topdefs.select('#' + clipId).remove(); - - Plots.autoMargin(gd, 'legend'); - return; + return Plots.autoMargin(gd, 'legend'); } - 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) { @@ -25609,7 +25550,6 @@ module.exports = function draw(gd) { 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'); @@ -25617,26 +25557,15 @@ module.exports = function draw(gd) { 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'); + s.attr(constants.scrollBarEnterAttrs) + .call(Color.fill, constants.scrollBarColor); }); - var groups = scrollBox.selectAll('g.groups') - .data(legendData); - - groups.enter().append('g') - .attr('class', 'groups'); - + 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); - + var traces = groups.selectAll('g.traces').data(Lib.identity); traces.enter().append('g').attr('class', 'traces'); traces.exit().remove(); @@ -25648,84 +25577,38 @@ module.exports = function draw(gd) { return trace.visible === 'legendonly' ? 0.5 : 1; } }) - .each(function() { - d3.select(this) - .call(drawTexts, gd, maxLength); - }) + .each(function() { d3.select(this).call(drawTexts, gd); }) .call(style, gd) - .each(function() { - d3.select(this) - .call(setupTraceToggle, gd); - }); + .each(function() { d3.select(this).call(setupTraceToggle, gd); }); - Lib.syncOrAsync([Plots.previousPromises, + Lib.syncOrAsync([ + Plots.previousPromises, + function() { return computeLegendDimensions(gd, groups, traces); }, function() { - if(firstRender) { - computeLegendDimensions(gd, groups, traces); - expandMargin(gd); - } + // IF expandMargin return a Promise (which is truthy), + // we're under a doAutoMargin redraw, so we don't have to + // draw the remaining pieces below + if(expandMargin(gd)) return; - // 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); + var bw = opts.borderwidth; - if(Lib.isRightAnchor(opts)) { - lx -= opts._width; - } else if(Lib.isCenterAnchor(opts)) { - lx -= opts._width / 2; - } + var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width; + var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight; - if(Lib.isBottomAnchor(opts)) { - ly -= opts._height; - } else if(Lib.isMiddleAnchor(opts)) { - ly -= opts._height / 2; - } + if(fullLayout.margin.autoexpand) { + var lx0 = lx; + var ly0 = ly; - // Make sure the legend left and right sides are visible - var legendWidth = opts._width; - var legendWidthMax = gs.w; + lx = Lib.constrain(lx, 0, fullLayout.width - opts._width); + ly = Lib.constrain(ly, 0, fullLayout.height - opts._effHeight); - 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); + if(lx !== lx0) { + Lib.log('Constrain legend.x to make legend fit inside graph'); + } + if(ly !== ly0) { + Lib.log('Constrain legend.y to make legend fit inside graph'); + } } // Set size and position of all the elements that make up a legend: @@ -25736,22 +25619,22 @@ module.exports = function draw(gd) { scrollBar.on('.drag', null); legend.on('wheel', null); - if(opts._height <= legendHeight || gd._context.staticPlot) { + if(opts._height <= opts._maxHeight || 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 + width: opts._width - bw, + height: opts._effHeight - bw, + x: bw / 2, + y: bw / 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 + width: opts._width - 2 * bw, + height: opts._effHeight - 2 * bw, + x: bw, + y: bw }); Drawing.setClipUrl(scrollBox, clipId, gd); @@ -25760,11 +25643,11 @@ module.exports = function draw(gd) { delete opts._scrollY; } else { var scrollBarHeight = Math.max(constants.scrollBarMinHeight, - legendHeight * legendHeight / opts._height); - var scrollBarYMax = legendHeight - + opts._effHeight * opts._effHeight / opts._height); + var scrollBarYMax = opts._effHeight - scrollBarHeight - 2 * constants.scrollBarMargin; - var scrollBoxYMax = opts._height - legendHeight; + var scrollBoxYMax = opts._height - opts._effHeight; var scrollRatio = scrollBarYMax / scrollBoxYMax; var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax); @@ -25772,33 +25655,34 @@ module.exports = function draw(gd) { // increase the background and clip-path width // by the scrollbar width and margin bg.attr({ - width: legendWidth - - 2 * opts.borderwidth + + width: opts._width - + 2 * bw + constants.scrollBarWidth + constants.scrollBarMargin, - height: legendHeight - opts.borderwidth, - x: opts.borderwidth / 2, - y: opts.borderwidth / 2 + height: opts._effHeight - bw, + x: bw / 2, + y: bw / 2 }); clipPath.select('rect').attr({ - width: legendWidth - - 2 * opts.borderwidth + + width: opts._width - + 2 * bw + constants.scrollBarWidth + constants.scrollBarMargin, - height: legendHeight - 2 * opts.borderwidth, - x: opts.borderwidth, - y: opts.borderwidth + scrollBoxY + height: opts._effHeight - 2 * bw, + x: bw, + y: bw + scrollBoxY }); Drawing.setClipUrl(scrollBox, clipId, gd); scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + // scroll legend by mousewheel or touchpad swipe up/down legend.on('wheel', function() { scrollBoxY = Lib.constrain( opts._scrollY + - d3.event.deltaY / scrollBarYMax * scrollBoxYMax, + ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax), 0, scrollBoxYMax); scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) { @@ -25806,41 +25690,74 @@ module.exports = function draw(gd) { } }); - var eventY0, scrollBoxY0; + var eventY0, eventY1, scrollBoxY0; - var drag = d3.behavior.drag() + var getScrollBarDragY = function(scrollBoxY0, eventY0, eventY1) { + var y = ((eventY1 - eventY0) / scrollRatio) + scrollBoxY0; + return Lib.constrain(y, 0, scrollBoxYMax); + }; + + var getNaturalDragY = function(scrollBoxY0, eventY0, eventY1) { + var y = ((eventY0 - eventY1) / scrollRatio) + scrollBoxY0; + return Lib.constrain(y, 0, scrollBoxYMax); + }; + + // scroll legend by dragging scrollBAR + var scrollBarDrag = d3.behavior.drag() .on('dragstart', function() { - eventY0 = d3.event.sourceEvent.clientY; + var e = d3.event.sourceEvent; + if(e.type === 'touchstart') { + eventY0 = e.changedTouches[0].clientY; + } else { + eventY0 = e.clientY; + } scrollBoxY0 = scrollBoxY; }) .on('drag', function() { var e = d3.event.sourceEvent; if(e.buttons === 2 || e.ctrlKey) return; - - scrollBoxY = Lib.constrain( - (e.clientY - eventY0) / scrollRatio + scrollBoxY0, - 0, scrollBoxYMax); + if(e.type === 'touchmove') { + eventY1 = e.changedTouches[0].clientY; + } else { + eventY1 = e.clientY; + } + scrollBoxY = getScrollBarDragY(scrollBoxY0, eventY0, eventY1); scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); }); + scrollBar.call(scrollBarDrag); - scrollBar.call(drag); + // scroll legend by touch-dragging scrollBOX + var scrollBoxTouchDrag = d3.behavior.drag() + .on('dragstart', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchstart') { + eventY0 = e.changedTouches[0].clientY; + scrollBoxY0 = scrollBoxY; + } + }) + .on('drag', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchmove') { + eventY1 = e.changedTouches[0].clientY; + scrollBoxY = getNaturalDragY(scrollBoxY0, eventY0, eventY1); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + } + }); + scrollBox.call(scrollBoxTouchDrag); } - function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) { opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; Drawing.setTranslate(scrollBox, 0, -scrollBoxY); Drawing.setRect( scrollBar, - legendWidth, + opts._width, constants.scrollBarMargin + scrollBoxY * scrollRatio, constants.scrollBarWidth, scrollBarHeight ); - clipPath.select('rect').attr({ - y: opts.borderwidth + scrollBoxY - }); + clipPath.select('rect').attr('y', bw + scrollBoxY); } if(gd._context.edits.legendPosition) { @@ -25853,7 +25770,6 @@ module.exports = function draw(gd) { gd: gd, prepFn: function() { var transform = Drawing.getTranslate(legend); - x0 = transform.x; y0 = transform.y; }, @@ -25890,7 +25806,6 @@ module.exports = function draw(gd) { function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { var trace = legendItem.data()[0][0].trace; - var evtData = { event: evt, node: legendItem.node(), @@ -25917,7 +25832,7 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { if(numClicks === 1) { legend._clickTimeout = setTimeout(function() { handleClick(legendItem, gd, numClicks); - }, DBLCLICKDELAY); + }, gd._context.doubleClickDelay); } else if(numClicks === 2) { if(legend._clickTimeout) clearTimeout(legend._clickTimeout); gd._legendMouseDownTime = 0; @@ -25927,13 +25842,15 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) { } } -function drawTexts(g, gd, maxLength) { +function drawTexts(g, gd) { var legendItem = g.data()[0][0]; var fullLayout = gd._fullLayout; + var opts = fullLayout.legend; var trace = legendItem.trace; var isPieLike = Registry.traceIs(trace, 'pie-like'); var traceIndex = trace.index; var isEditable = gd._context.edits.legendText && !isPieLike; + var maxNameLength = opts._maxNameLength; var name = isPieLike ? legendItem.label : trace.name; if(trace._meta) { @@ -25945,9 +25862,9 @@ function drawTexts(g, gd, maxLength) { textEl.attr('text-anchor', 'start') .classed('user-select-none', true) .call(Drawing.font, fullLayout.legend.font) - .text(isEditable ? ensureLength(name, maxLength) : name); + .text(isEditable ? ensureLength(name, maxNameLength) : name); - svgTextUtils.positionText(textEl, constants.textOffsetX, 0); + svgTextUtils.positionText(textEl, constants.textGap, 0); function textLayout(s) { svgTextUtils.convertToTspans(s, gd, function() { @@ -25959,7 +25876,7 @@ function drawTexts(g, gd, maxLength) { textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) .call(textLayout) .on('edit', function(newName) { - this.text(ensureLength(newName, maxLength)) + this.text(ensureLength(newName, maxNameLength)) .call(textLayout); var fullInput = legendItem.trace._fullInput || {}; @@ -26001,6 +25918,7 @@ function ensureLength(str, maxLength) { } function setupTraceToggle(g, gd) { + var doubleClickDelay = gd._context.doubleClickDelay; var newMouseDownTime; var numClicks = 1; @@ -26012,7 +25930,7 @@ function setupTraceToggle(g, gd) { traceToggle.on('mousedown', function() { newMouseDownTime = (new Date()).getTime(); - if(newMouseDownTime - gd._legendMouseDownTime < DBLCLICKDELAY) { + if(newMouseDownTime - gd._legendMouseDownTime < doubleClickDelay) { // in a click train numClicks += 1; } else { @@ -26025,7 +25943,7 @@ function setupTraceToggle(g, gd) { if(gd._dragged || gd._editing) return; var legend = gd._fullLayout.legend; - if((new Date()).getTime() - gd._legendMouseDownTime > DBLCLICKDELAY) { + if((new Date()).getTime() - gd._legendMouseDownTime > doubleClickDelay) { numClicks = Math.max(numClicks - 1, 1); } @@ -26065,7 +25983,7 @@ function computeTextDimensions(g, gd) { // 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); + svgTextUtils.positionText(text, constants.textGap, textY); } legendItem.lineHeight = lineHeight; @@ -26073,243 +25991,202 @@ function computeTextDimensions(g, gd) { legendItem.width = width; } +/* + * Computes in fullLayout.legend: + * + * - _height: legend height including items past scrollbox height + * - _maxHeight: maximum legend height before scrollbox is required + * - _effHeight: legend height w/ or w/o scrollbox + * + * - _width: legend width + * - _maxWidth (for orientation:h only): maximum width before starting new row + */ function computeLegendDimensions(gd, groups, traces) { var fullLayout = gd._fullLayout; var opts = fullLayout.legend; - var borderwidth = opts.borderwidth; + var gs = fullLayout._size; + var isVertical = helpers.isVertical(opts); var isGrouped = helpers.isGrouped(opts); - var extraWidth = 0; + var bw = opts.borderwidth; + var bw2 = 2 * bw; + var textGap = constants.textGap; + var itemGap = constants.itemGap; + var endPad = 2 * (bw + itemGap); - var traceGap = 5; + var yanchor = getYanchor(opts); + var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top'); + var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom'); + // - if below/above plot area, give it the maximum potential margin-push value + // - otherwise, extend the height of the plot area + opts._maxHeight = Math.max( + (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h, + 30 + ); + + var toggleRectWidth = 0; opts._width = 0; opts._height = 0; - if(helpers.isVertical(opts)) { + if(isVertical) { + traces.each(function(d) { + var h = d[0].height; + Drawing.setTranslate(this, bw, itemGap + bw + opts._height + h / 2); + opts._height += h; + opts._width = Math.max(opts._width, d[0].width); + }); + + toggleRectWidth = textGap + opts._width; + opts._width += itemGap + textGap + bw2; + opts._height += endPad; + 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; + var xanchor = getXanchor(opts); + var isLeftOfPlotArea = opts.x < 0 || (opts.x === 0 && xanchor === 'right'); + var isRightOfPlotArea = opts.x > 1 || (opts.x === 1 && xanchor === 'left'); + var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea; + var hw = fullLayout.width / 2; - // calculate largest width for traces and use for width of all legend items + // - if placed within x-margins, extend the width of the plot area + // - else if below/above plot area and anchored in the margin, extend to opposite margin, + // - otherwise give it the maximum potential margin-push value + opts._maxWidth = Math.max( + isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) : + isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) : + gs.w, + 2 * textGap); + var maxItemWidth = 0; + var combinedItemWidth = 0; traces.each(function(d) { - maxTraceWidth = Math.max(40 + d[0].width, maxTraceWidth); - fullTracesWidth += 40 + d[0].width + traceGap; + var w = d[0].width + textGap; + maxItemWidth = Math.max(maxItemWidth, w); + combinedItemWidth += w; }); - // check if legend fits in one row - var oneRowLegend = fullLayout._size.w > borderwidth + fullTracesWidth - traceGap; + toggleRectWidth = null; + var maxRowWidth = 0; - traces.each(function(d) { - var legendItem = d[0]; - var traceWidth = oneRowLegend ? 40 + d[0].width : maxTraceWidth; + if(isGrouped) { + var maxGroupHeightInRow = 0; + var groupOffsetX = 0; + var groupOffsetY = 0; + groups.each(function() { + var maxWidthInGroup = 0; + var offsetY = 0; + d3.select(this).selectAll('g.traces').each(function(d) { + var h = d[0].height; + Drawing.setTranslate(this, 0, itemGap + bw + h / 2 + offsetY); + offsetY += h; + maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width); + }); + maxGroupHeightInRow = Math.max(maxGroupHeightInRow, offsetY); - if((borderwidth + offsetX + traceGap + traceWidth) > fullLayout._size.w) { - offsetX = 0; - rowHeight += maxTraceHeight; - opts._height += maxTraceHeight; - // reset for next row - maxTraceHeight = 0; - } + var next = maxWidthInGroup + itemGap; - Drawing.setTranslate(this, - (borderwidth + offsetX), - (5 + borderwidth + legendItem.height / 2) + rowHeight); + if((next + bw + groupOffsetX) > opts._maxWidth) { + maxRowWidth = Math.max(maxRowWidth, groupOffsetX); + groupOffsetX = 0; + groupOffsetY += maxGroupHeightInRow + opts.tracegroupgap; + maxGroupHeightInRow = offsetY; + } - opts._width += traceGap + traceWidth; + Drawing.setTranslate(this, groupOffsetX, groupOffsetY); - // keep track of tallest trace in group - offsetX += traceGap + traceWidth; - maxTraceHeight = Math.max(legendItem.height, maxTraceHeight); - }); + groupOffsetX += next; + }); - if(oneRowLegend) { - opts._height = maxTraceHeight; + opts._width = Math.max(maxRowWidth, groupOffsetX) + bw; + opts._height = groupOffsetY + maxGroupHeightInRow + endPad; } else { - opts._height += maxTraceHeight; - } + var nTraces = traces.size(); + var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < opts._maxWidth; - opts._width += borderwidth * 2; - opts._height += 10 + borderwidth * 2; + var maxItemHeightInRow = 0; + var offsetX = 0; + var offsetY = 0; + var rowWidth = 0; + traces.each(function(d) { + var h = d[0].height; + var w = textGap + d[0].width; + var next = (oneRowLegend ? w : maxItemWidth) + itemGap; + + if((next + bw + offsetX) > opts._maxWidth) { + maxRowWidth = Math.max(maxRowWidth, rowWidth); + offsetX = 0; + offsetY += maxItemHeightInRow; + opts._height += maxItemHeightInRow; + maxItemHeightInRow = 0; + } + + Drawing.setTranslate(this, bw + offsetX, itemGap + bw + h / 2 + offsetY); + + rowWidth = offsetX + w + itemGap; + offsetX += next; + maxItemHeightInRow = Math.max(maxItemHeightInRow, h); + }); + + if(oneRowLegend) { + opts._width = offsetX + bw2; + opts._height = maxItemHeightInRow + endPad; + } else { + opts._width = Math.max(maxRowWidth, rowWidth) + bw2; + opts._height += maxItemHeightInRow + endPad; + } + } } - // 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 - ); + opts._effHeight = Math.min(opts._height, opts._maxHeight); + var edits = gd._context.edits; + var isEditable = edits.legendText || 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 - ); + var traceToggle = d3.select(this).select('.legendtoggle'); + var h = d[0].height; + var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width)); + if(!isVertical) w += itemGap / 2; + Drawing.setRect(traceToggle, 0, -h / 2, w, h); }); } function expandMargin(gd) { var fullLayout = gd._fullLayout; var opts = fullLayout.legend; + var xanchor = getXanchor(opts); + var yanchor = getYanchor(opts); - 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', { + return 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]) + b: opts._effHeight * (FROM_BR[yanchor]), + t: opts._effHeight * (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 - }); +function getXanchor(opts) { + return Lib.isRightAnchor(opts) ? 'right' : + Lib.isCenterAnchor(opts) ? 'center' : + 'left'; } -},{"../../constants/alignment":146,"../../constants/interactions":148,"../../lib":168,"../../lib/events":161,"../../lib/svg_text_utils":189,"../../plots/plots":244,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"./constants":101,"./get_legend_data":104,"./handle_click":105,"./helpers":106,"./style":108,"d3":16}],104:[function(_dereq_,module,exports){ +function getYanchor(opts) { + return Lib.isBottomAnchor(opts) ? 'bottom' : + Lib.isMiddleAnchor(opts) ? 'middle' : + 'top'; +} + +},{"../../constants/alignment":145,"../../lib":169,"../../lib/events":163,"../../lib/svg_text_utils":190,"../../plots/plots":245,"../../registry":258,"../color":51,"../dragelement":69,"../drawing":72,"./constants":100,"./get_legend_data":103,"./handle_click":104,"./helpers":105,"./style":107,"d3":16}],103:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -26329,13 +26206,14 @@ module.exports = function getLegendData(calcdata, opts) { var hasOneNonBlankGroup = false; var slicesShown = {}; var lgroupi = 0; + var maxNameLength = 0; var i, j; function addOneItem(legendGroup, legendItem) { // each '' legend group is treated as a separate group if(legendGroup === '' || !helpers.isGrouped(opts)) { - var uniqueGroup = '~~i' + lgroupi; // TODO: check this against fullData legendgroups? - + // TODO: check this against fullData legendgroups? + var uniqueGroup = '~~i' + lgroupi; lgroups.push(uniqueGroup); lgroupToTraces[uniqueGroup] = [[legendItem]]; lgroupi++; @@ -26343,7 +26221,9 @@ module.exports = function getLegendData(calcdata, opts) { lgroups.push(legendGroup); hasOneNonBlankGroup = true; lgroupToTraces[legendGroup] = [[legendItem]]; - } else lgroupToTraces[legendGroup].push([legendItem]); + } else { + lgroupToTraces[legendGroup].push([legendItem]); + } } // build an { legendgroup: [cd0, cd0], ... } object @@ -26371,9 +26251,13 @@ module.exports = function getLegendData(calcdata, opts) { }); slicesShown[lgroup][labelj] = true; + maxNameLength = Math.max(maxNameLength, (labelj || '').length); } } - } else addOneItem(lgroup, cd0); + } else { + addOneItem(lgroup, cd0); + maxNameLength = Math.max(maxNameLength, (trace.name || '').length); + } } // won't draw a legend in this case @@ -26402,12 +26286,15 @@ module.exports = function getLegendData(calcdata, opts) { lgroupsLength = 1; } - // needed in repositionLegend + // number of legend groups - needed in legend/draw.js opts._lgroupsLength = lgroupsLength; + // maximum name/label length - needed in legend/draw.js + opts._maxNameLength = maxNameLength; + return legendData; }; -},{"../../registry":256,"./helpers":106}],105:[function(_dereq_,module,exports){ +},{"../../registry":258,"./helpers":105}],104:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -26643,7 +26530,7 @@ module.exports = function handleClick(g, gd, numClicks) { } }; -},{"../../lib":168,"../../registry":256}],106:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],105:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -26667,7 +26554,7 @@ exports.isReversed = function isReversed(legendLayout) { return (legendLayout.traceorder || '').indexOf('reversed') !== -1; }; -},{}],107:[function(_dereq_,module,exports){ +},{}],106:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -26691,7 +26578,7 @@ module.exports = { style: _dereq_('./style') }; -},{"./attributes":100,"./defaults":102,"./draw":103,"./style":108}],108:[function(_dereq_,module,exports){ +},{"./attributes":99,"./defaults":101,"./draw":102,"./style":107}],107:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -26708,6 +26595,7 @@ var Registry = _dereq_('../../registry'); var Lib = _dereq_('../../lib'); var Drawing = _dereq_('../drawing'); var Color = _dereq_('../color'); +var extractOpts = _dereq_('../colorscale/helpers').extractOpts; var subTypes = _dereq_('../../traces/scatter/subtypes'); var stylePie = _dereq_('../../traces/pie/style_one'); @@ -26724,7 +26612,7 @@ module.exports = function style(s, gd) { var legend = fullLayout.legend; var constantItemSizing = legend.itemsizing === 'constant'; - function boundLineWidth(mlw, cont, max, cst) { + var boundLineWidth = function(mlw, cont, max, cst) { var v; if(mlw + 1) { v = mlw; @@ -26734,7 +26622,7 @@ module.exports = function style(s, gd) { return 0; } return constantItemSizing ? cst : Math.min(v, max); - } + }; s.each(function(d) { var traceGroup = d3.select(this); @@ -26798,6 +26686,29 @@ module.exports = function style(s, gd) { var showGradientFill = false; var dMod, tMod; + var cOpts = extractOpts(trace); + var colorscale = cOpts.colorscale; + var reversescale = cOpts.reversescale; + + var fillGradient = function(s) { + if(s.size()) { + var gradientID = 'legendfill-' + trace.uid; + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale), + colorscale, 'fill'); + } + }; + + var lineGradient = function(s) { + if(s.size()) { + var gradientID = 'legendline-' + trace.uid; + Drawing.lineGroupStyle(s); + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale), + colorscale, 'stroke'); + } + }; + if(contours) { var coloring = contours.coloring; @@ -26852,23 +26763,6 @@ module.exports = function style(s, gd) { // 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) { @@ -26942,6 +26836,9 @@ module.exports = function style(s, gd) { // always show legend items in base state tMod.selectedpoints = null; + + // never show texttemplate + tMod.texttemplate = null; } var ptgroup = d3.select(this).select('g.legendpoints'); @@ -26972,7 +26869,7 @@ module.exports = function style(s, gd) { var trace = d[0].trace; var ptsData = []; - if(trace.type === 'waterfall' && trace.visible) { + if(trace.visible && trace.type === 'waterfall') { 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']]; @@ -27015,7 +26912,7 @@ module.exports = function style(s, gd) { var markerLine = marker.line || {}; var isVisible = (!desiredType) ? Registry.traceIs(trace, 'bar') : - (trace.type === desiredType && trace.visible); + (trace.visible && trace.type === desiredType); var barpath = d3.select(lThis).select('g.legendpoints') .selectAll('path.legend' + desiredType) @@ -27042,7 +26939,7 @@ module.exports = function style(s, gd) { var pts = d3.select(this).select('g.legendpoints') .selectAll('path.legendbox') - .data(Registry.traceIs(trace, 'box-violin') && trace.visible ? [d] : []); + .data(trace.visible && Registry.traceIs(trace, 'box-violin') ? [d] : []); pts.enter().append('path').classed('legendbox', true) // if we want the median bar, prepend M6,0H-6 .attr('d', 'M6,6H-6V-6H6Z') @@ -27080,7 +26977,7 @@ module.exports = function style(s, gd) { var pts = d3.select(this).select('g.legendpoints') .selectAll('path.legendcandle') - .data(trace.type === 'candlestick' && trace.visible ? [d, d] : []); + .data(trace.visible && trace.type === 'candlestick' ? [d, d] : []); pts.enter().append('path').classed('legendcandle', true) .attr('d', function(_, i) { if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing @@ -27107,7 +27004,7 @@ module.exports = function style(s, gd) { var pts = d3.select(this).select('g.legendpoints') .selectAll('path.legendohlc') - .data(trace.type === 'ohlc' && trace.visible ? [d, d] : []); + .data(trace.visible && trace.type === 'ohlc' ? [d, d] : []); pts.enter().append('path').classed('legendohlc', true) .attr('d', function(_, i) { if(i) return 'M-15,0H0M-8,-6V0'; // increasing @@ -27142,7 +27039,7 @@ module.exports = function style(s, gd) { var trace = d0.trace; var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) : - (trace.type === desiredType && trace.visible); + (trace.visible && trace.type === desiredType); var pts = d3.select(lThis).select('g.legendpoints') .selectAll('path.legend' + desiredType) @@ -27167,7 +27064,11 @@ module.exports = function style(s, gd) { } }; -},{"../../lib":168,"../../registry":256,"../../traces/pie/helpers":357,"../../traces/pie/style_one":363,"../../traces/scatter/subtypes":388,"../color":51,"../drawing":72,"d3":16}],109:[function(_dereq_,module,exports){ +function getGradientDirection(reversescale) { + return reversescale ? 'horizontal' : 'horizontalreversed'; +} + +},{"../../lib":169,"../../registry":258,"../../traces/pie/helpers":369,"../../traces/pie/style_one":375,"../../traces/scatter/subtypes":401,"../color":51,"../colorscale/helpers":62,"../drawing":72,"d3":16}],108:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -27182,7 +27083,7 @@ var Registry = _dereq_('../../registry'); var Plots = _dereq_('../../plots/plots'); var axisIds = _dereq_('../../plots/cartesian/axis_ids'); var Lib = _dereq_('../../lib'); -var Icons = _dereq_('../../../build/ploticon'); +var Icons = _dereq_('../../fonts/ploticon'); var _ = Lib._; @@ -27259,6 +27160,15 @@ modeBarButtons.sendDataToCloud = { } }; +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'); }, @@ -27360,7 +27270,7 @@ function handleCartesian(gd, ev) { var fullLayout = gd._fullLayout; var aobj = {}; var axList = axisIds.list(gd, null, true); - var allSpikesEnabled = 'on'; + var allSpikesEnabled = fullLayout._cartesianSpikesEnabled; var ax, i; @@ -27375,8 +27285,9 @@ function handleCartesian(gd, ev) { if(!ax.fixedrange) { axName = ax._name; - if(val === 'auto') aobj[axName + '.autorange'] = true; - else if(val === 'reset') { + if(val === 'auto') { + aobj[axName + '.autorange'] = true; + } else if(val === 'reset') { if(ax._rangeInitial === undefined) { aobj[axName + '.autorange'] = true; } else { @@ -27384,6 +27295,8 @@ function handleCartesian(gd, ev) { aobj[axName + '.range[0]'] = rangeInitial[0]; aobj[axName + '.range[1]'] = rangeInitial[1]; } + + // N.B. "reset" also resets showspikes if(ax._showSpikeInitial !== undefined) { aobj[axName + '.showspikes'] = ax._showSpikeInitial; if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) { @@ -27406,25 +27319,18 @@ function handleCartesian(gd, ev) { } } } - 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; } + fullLayout._cartesianSpikesEnabled = allSpikesEnabled; + Registry.call('_guiRelayout', gd, aobj); } @@ -27504,22 +27410,32 @@ function handleCamera3d(gd, ev) { var button = ev.currentTarget; var attr = button.getAttribute('data-attr'); var fullLayout = gd._fullLayout; - var sceneIds = fullLayout._subplots.gl3d; + var sceneIds = fullLayout._subplots.gl3d || []; var aobj = {}; for(var i = 0; i < sceneIds.length; i++) { var sceneId = sceneIds[i]; - var key = sceneId + '.camera'; + var camera = sceneId + '.camera'; + var aspectratio = sceneId + '.aspectratio'; var scene = fullLayout[sceneId]._scene; + var didUpdate; if(attr === 'resetLastSave') { - aobj[key + '.up'] = scene.viewInitial.up; - aobj[key + '.eye'] = scene.viewInitial.eye; - aobj[key + '.center'] = scene.viewInitial.center; + aobj[camera + '.up'] = scene.viewInitial.up; + aobj[camera + '.eye'] = scene.viewInitial.eye; + aobj[camera + '.center'] = scene.viewInitial.center; + didUpdate = true; } else if(attr === 'resetDefault') { - aobj[key + '.up'] = null; - aobj[key + '.eye'] = null; - aobj[key + '.center'] = null; + aobj[camera + '.up'] = null; + aobj[camera + '.eye'] = null; + aobj[camera + '.center'] = null; + didUpdate = true; + } + + if(didUpdate) { + aobj[aspectratio + '.x'] = scene.viewInitial.aspectratio.x; + aobj[aspectratio + '.y'] = scene.viewInitial.aspectratio.y; + aobj[aspectratio + '.z'] = scene.viewInitial.aspectratio.z; } } @@ -27541,7 +27457,7 @@ function getNextHover3d(gd, ev) { var button = ev.currentTarget; var val = button._previousVal; var fullLayout = gd._fullLayout; - var sceneIds = fullLayout._subplots.gl3d; + var sceneIds = fullLayout._subplots.gl3d || []; var axes = ['xaxis', 'yaxis', 'zaxis']; @@ -27742,26 +27658,22 @@ modeBarButtons.toggleSpikelines = { val: 'on', click: function(gd) { var fullLayout = gd._fullLayout; + var allSpikesEnabled = fullLayout._cartesianSpikesEnabled; - fullLayout._cartesianSpikesEnabled = fullLayout._cartesianSpikesEnabled === 'on' ? 'off' : 'on'; - - var aobj = setSpikelineVisibility(gd); - - Registry.call('_guiRelayout', gd, aobj); + fullLayout._cartesianSpikesEnabled = allSpikesEnabled === 'on' ? 'off' : 'on'; + Registry.call('_guiRelayout', gd, setSpikelineVisibility(gd)); } }; function setSpikelineVisibility(gd) { var fullLayout = gd._fullLayout; + var areSpikesOn = fullLayout._cartesianSpikesEnabled === 'on'; var axList = axisIds.list(gd, null, true); var aobj = {}; - 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; + var ax = axList[i]; + aobj[ax._name + '.showspikes'] = areSpikesOn ? true : ax._showSpikeInitial; } return aobj; @@ -27779,7 +27691,7 @@ modeBarButtons.resetViewMapbox = { function resetView(gd, subplotType) { var fullLayout = gd._fullLayout; - var subplotIds = fullLayout._subplots[subplotType]; + var subplotIds = fullLayout._subplots[subplotType] || []; var aObj = {}; for(var i = 0; i < subplotIds.length; i++) { @@ -27797,7 +27709,7 @@ function resetView(gd, subplotType) { Registry.call('_guiRelayout', gd, aObj); } -},{"../../../build/ploticon":2,"../../lib":168,"../../plots/cartesian/axis_ids":215,"../../plots/plots":244,"../../registry":256}],110:[function(_dereq_,module,exports){ +},{"../../fonts/ploticon":153,"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../plots/plots":245,"../../registry":258}],109:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -27811,7 +27723,7 @@ function resetView(gd, subplotType) { exports.manage = _dereq_('./manage'); -},{"./manage":111}],111:[function(_dereq_,module,exports){ +},{"./manage":110}],110:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -27873,12 +27785,7 @@ module.exports = function manageModeBar(gd) { } else if(!context.displayModeBar && context.watermark) { buttonGroups = []; } else { - buttonGroups = getButtonGroups( - gd, - context.modeBarButtonsToRemove, - context.modeBarButtonsToAdd, - context.showSendToCloud - ); + buttonGroups = getButtonGroups(gd); } if(modeBar) modeBar.update(gd, buttonGroups); @@ -27886,9 +27793,12 @@ module.exports = function manageModeBar(gd) { }; // logic behind which buttons are displayed by default -function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) { +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'); @@ -27920,7 +27830,8 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) { // buttons common to all plot types var commonGroup = ['toImage']; - if(showSendToCloud) commonGroup.push('sendDataToCloud'); + if(context.showEditInChartStudio) commonGroup.push('editInChartStudio'); + else if(context.showSendToCloud) commonGroup.push('sendDataToCloud'); addGroup(commonGroup); var zoomGroup = []; @@ -27960,6 +27871,9 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) { if(hasCartesian) { hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian']; } + if(hasNoHover(fullData)) { + hoverGroup = []; + } if((hasCartesian || hasGL2D) && !allAxesFixed) { zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d']; @@ -28030,6 +27944,14 @@ function isSelectable(fullData) { 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])) { @@ -28066,7 +27988,7 @@ function fillCustomButton(customButtons) { return customButtons; } -},{"../../plots/cartesian/axis_ids":215,"../../registry":256,"../../traces/scatter/subtypes":388,"./buttons":109,"./modebar":112}],112:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/axis_ids":216,"../../registry":258,"../../traces/scatter/subtypes":401,"./buttons":108,"./modebar":111}],111:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -28082,7 +28004,7 @@ var d3 = _dereq_('d3'); var isNumeric = _dereq_('fast-isnumeric'); var Lib = _dereq_('../../lib'); -var Icons = _dereq_('../../../build/ploticon'); +var Icons = _dereq_('../../fonts/ploticon'); var Parser = new DOMParser(); /** @@ -28412,7 +28334,7 @@ function createModeBar(gd, buttons) { module.exports = createModeBar; -},{"../../../build/ploticon":2,"../../lib":168,"d3":16,"fast-isnumeric":18}],113:[function(_dereq_,module,exports){ +},{"../../fonts/ploticon":153,"../../lib":169,"d3":16,"fast-isnumeric":18}],112:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -28548,7 +28470,7 @@ module.exports = { editType: 'plot' }; -},{"../../plot_api/plot_template":202,"../../plots/font_attributes":238,"../color/attributes":50}],114:[function(_dereq_,module,exports){ +},{"../../plot_api/plot_template":203,"../../plots/font_attributes":239,"../color/attributes":50}],113:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -28577,7 +28499,7 @@ module.exports = { darkAmount: 10 }; -},{}],115:[function(_dereq_,module,exports){ +},{}],114:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -28669,7 +28591,7 @@ function getPosDflt(containerOut, layout, counterAxes) { return [containerOut.domain[0], posY + constants.yPad]; } -},{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/array_container_defaults":208,"../color":51,"./attributes":113,"./constants":114}],116:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plot_api/plot_template":203,"../../plots/array_container_defaults":209,"../color":51,"./attributes":112,"./constants":113}],115:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -28926,7 +28848,7 @@ function reposition(gd, buttons, opts, axName, selector) { selector.attr('transform', 'translate(' + lx + ',' + ly + ')'); } -},{"../../constants/alignment":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axis_ids":215,"../../plots/plots":244,"../../registry":256,"../color":51,"../drawing":72,"./constants":114,"./get_update_object":117,"d3":16}],117:[function(_dereq_,module,exports){ +},{"../../constants/alignment":145,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axis_ids":216,"../../plots/plots":245,"../../registry":258,"../color":51,"../drawing":72,"./constants":113,"./get_update_object":116,"d3":16}],116:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -28980,7 +28902,7 @@ function getXRange(axisLayout, buttonLayout) { return [range0, range1]; } -},{"d3":16}],118:[function(_dereq_,module,exports){ +},{"d3":16}],117:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29007,7 +28929,7 @@ module.exports = { draw: _dereq_('./draw') }; -},{"./attributes":113,"./defaults":115,"./draw":116}],119:[function(_dereq_,module,exports){ +},{"./attributes":112,"./defaults":114,"./draw":115}],118:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29081,7 +29003,7 @@ module.exports = { editType: 'calc' }; -},{"../color/attributes":50}],120:[function(_dereq_,module,exports){ +},{"../color/attributes":50}],119:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29115,7 +29037,7 @@ module.exports = function calcAutorange(gd) { } }; -},{"../../plots/cartesian/autorange":211,"../../plots/cartesian/axis_ids":215,"./constants":121}],121:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/autorange":212,"../../plots/cartesian/axis_ids":216,"./constants":120}],120:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29171,7 +29093,7 @@ module.exports = { extraPad: 15 }; -},{}],122:[function(_dereq_,module,exports){ +},{}],121:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29257,7 +29179,7 @@ module.exports = function handleDefaults(layoutIn, layoutOut, axName) { containerOut._input = containerIn; }; -},{"../../lib":168,"../../plot_api/plot_template":202,"../../plots/cartesian/axis_ids":215,"./attributes":119,"./oppaxis_attributes":126}],123:[function(_dereq_,module,exports){ +},{"../../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. @@ -29366,20 +29288,16 @@ module.exports = function(gd) { // update range slider dimensions - var margin = fullLayout.margin; - var graphSize = fullLayout._size; + var gs = fullLayout._size; var domain = axisOpts.domain; - var tickHeight = opts._tickHeight; - var oppBottom = opts._oppBottom; + opts._width = gs.w * (domain[1] - domain[0]); - opts._width = graphSize.w * (domain[1] - domain[0]); - - var x = Math.round(margin.l + (graphSize.w * domain[0])); + var x = Math.round(gs.l + (gs.w * domain[0])); var y = Math.round( - graphSize.t + graphSize.h * (1 - oppBottom) + - tickHeight + + gs.t + gs.h * (1 - axisOpts._counterDomainMin) + + (axisOpts.side === 'bottom' ? axisOpts._depth : 0) + opts._offsetShift + constants.extraPad ); @@ -29837,7 +29755,7 @@ function drawGrabbers(rangeSlider, gd, axisOpts, opts) { grabAreaMax.attr('height', opts._height); } -},{"../../lib":168,"../../lib/setcursor":187,"../../plots/cartesian":223,"../../plots/cartesian/axis_ids":215,"../../plots/plots":244,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"../titles":139,"./constants":121,"d3":16}],124:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../lib/setcursor":188,"../../plots/cartesian":224,"../../plots/cartesian/axis_ids":216,"../../plots/plots":245,"../../registry":258,"../color":51,"../dragelement":69,"../drawing":72,"../titles":138,"./constants":120,"d3":16}],123:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29849,7 +29767,9 @@ function drawGrabbers(rangeSlider, gd, axisOpts, opts) { 'use strict'; var axisIDs = _dereq_('../../plots/cartesian/axis_ids'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); var constants = _dereq_('./constants'); +var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING; var name = constants.name; function isVisible(ax) { @@ -29882,32 +29802,35 @@ exports.makeData = function(fullLayout) { }; exports.autoMarginOpts = function(gd, ax) { + var fullLayout = gd._fullLayout; var opts = ax[name]; + var axLetter = ax._id.charAt(0); - 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]); + var bottomDepth = 0; + var titleHeight = 0; + if(ax.side === 'bottom') { + bottomDepth = ax._depth; + if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { + // as in rangeslider/draw.js + titleHeight = 1.5 * ax.title.font.size + 10 + opts._offsetShift; + // multi-line extra bump + var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length; + titleHeight += extraLines * ax.title.font.size * LINE_SPACING; + } } - opts._oppBottom = oppBottom; - - var tickHeight = (ax.side === 'bottom' && ax._boundingBox.height) || 0; - opts._tickHeight = tickHeight; return { x: 0, - y: oppBottom, + y: ax._counterDomainMin, l: 0, r: 0, t: 0, - b: opts._height + gd._fullLayout.margin.b + tickHeight, + b: opts._height + bottomDepth + Math.max(fullLayout.margin.b, titleHeight), pad: constants.extraPad + opts._offsetShift * 2 }; }; -},{"../../plots/cartesian/axis_ids":215,"./constants":121}],125:[function(_dereq_,module,exports){ +},{"../../constants/alignment":145,"../../lib/svg_text_utils":190,"../../plots/cartesian/axis_ids":216,"./constants":120}],124:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -29946,7 +29869,7 @@ module.exports = { autoMarginOpts: helpers.autoMarginOpts }; -},{"../../lib":168,"./attributes":119,"./calc_autorange":120,"./defaults":122,"./draw":123,"./helpers":124,"./oppaxis_attributes":126}],126:[function(_dereq_,module,exports){ +},{"../../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. @@ -29984,7 +29907,7 @@ module.exports = { editType: 'calc' }; -},{}],127:[function(_dereq_,module,exports){ +},{}],126:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -30120,7 +30043,7 @@ module.exports = templatedArray('shape', { editType: 'arraydraw' }); -},{"../../lib/extend":162,"../../plot_api/plot_template":202,"../../traces/scatter/attributes":365,"../annotations/attributes":36,"../drawing/attributes":71}],128:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plot_api/plot_template":203,"../../traces/scatter/attributes":377,"../annotations/attributes":36,"../drawing/attributes":71}],127:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -30237,7 +30160,7 @@ function shapeBounds(ax, v0, v1, path, paramsToUse) { if(max >= min) return [min, max]; } -},{"../../lib":168,"../../plots/cartesian/axes":212,"./constants":129,"./helpers":132}],129:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"./constants":128,"./helpers":131}],128:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -30301,7 +30224,7 @@ module.exports = { } }; -},{}],130:[function(_dereq_,module,exports){ +},{}],129:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -30424,7 +30347,7 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) { } } -},{"../../lib":168,"../../plots/array_container_defaults":208,"../../plots/cartesian/axes":212,"./attributes":127,"./helpers":132}],131:[function(_dereq_,module,exports){ +},{"../../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. @@ -31041,7 +30964,7 @@ function movePath(pathIn, moveX, moveY) { }); } -},{"../../lib":168,"../../lib/setcursor":187,"../../plot_api/plot_template":202,"../../plots/cartesian/axes":212,"../../registry":256,"../color":51,"../dragelement":69,"../drawing":72,"./constants":129,"./helpers":132}],132:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../lib/setcursor":188,"../../plot_api/plot_template":203,"../../plots/cartesian/axes":213,"../../registry":258,"../color":51,"../dragelement":69,"../drawing":72,"./constants":128,"./helpers":131}],131:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -31162,7 +31085,7 @@ exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) { return strokeWidthIsOdd ? posValAsInt + 0.5 : posValAsInt; }; -},{"../../lib":168,"./constants":129}],133:[function(_dereq_,module,exports){ +},{"../../lib":169,"./constants":128}],132:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -31189,7 +31112,7 @@ module.exports = { drawOne: drawModule.drawOne }; -},{"../../plots/cartesian/include_components":222,"./attributes":127,"./calc_autorange":128,"./defaults":130,"./draw":131}],134:[function(_dereq_,module,exports){ +},{"../../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. @@ -31432,7 +31355,7 @@ module.exports = overrideAll(templatedArray('slider', { } }), 'arraydraw', 'from-root'); -},{"../../lib/extend":162,"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../../plots/animation_attributes":207,"../../plots/font_attributes":238,"../../plots/pad_attributes":243,"./constants":135}],135:[function(_dereq_,module,exports){ +},{"../../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. @@ -31526,7 +31449,7 @@ module.exports = { currentValueInset: 0, }; -},{}],136:[function(_dereq_,module,exports){ +},{}],135:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -31641,7 +31564,7 @@ function stepDefaults(valueIn, valueOut) { } } -},{"../../lib":168,"../../plots/array_container_defaults":208,"./attributes":134,"./constants":135}],137:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/array_container_defaults":209,"./attributes":133,"./constants":134}],136:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -32273,7 +32196,7 @@ function drawRail(sliderGroup, sliderOpts) { ); } -},{"../../constants/alignment":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/plots":244,"../color":51,"../drawing":72,"./constants":135,"d3":16}],138:[function(_dereq_,module,exports){ +},{"../../constants/alignment":145,"../../lib":169,"../../lib/svg_text_utils":190,"../../plot_api/plot_template":203,"../../plots/plots":245,"../color":51,"../drawing":72,"./constants":134,"d3":16}],137:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -32296,7 +32219,7 @@ module.exports = { draw: _dereq_('./draw') }; -},{"./attributes":134,"./constants":135,"./defaults":136,"./draw":137}],139:[function(_dereq_,module,exports){ +},{"./attributes":133,"./constants":134,"./defaults":135,"./draw":136}],138:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -32319,10 +32242,7 @@ var Color = _dereq_('../color'); var svgTextUtils = _dereq_('../../lib/svg_text_utils'); var interactConstants = _dereq_('../../constants/interactions'); -module.exports = { - draw: draw -}; - +var OPPOSITE_SIDE = _dereq_('../../constants/alignment').OPPOSITE_SIDE; var numStripRE = / [XY][0-9]* /; /** @@ -32466,16 +32386,10 @@ function draw(gd, titleClass, options) { // 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 backside = OPPOSITE_SIDE[avoid.side]; + var shiftSign = (avoid.side === 'left' || avoid.side === 'top') ? -1 : 1; var pad = isNumeric(avoid.pad) ? avoid.pad : 2; + var titlebb = Drawing.bBox(titleGroup.node()); var paperbb = { left: 0, @@ -32483,12 +32397,15 @@ function draw(gd, titleClass, options) { right: fullLayout.width, bottom: fullLayout.height }; - var maxshift = avoid.maxShift || ( - (paperbb[avoid.side] - titlebb[avoid.side]) * - ((avoid.side === 'left' || avoid.side === 'top') ? -1 : 1)); + + var maxshift = avoid.maxShift || + shiftSign * (paperbb[avoid.side] - titlebb[avoid.side]); + var shift = 0; + // Prevent the title going off the paper - if(maxshift < 0) shift = maxshift; - else { + 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; @@ -32510,6 +32427,7 @@ function draw(gd, titleClass, options) { }); shift = Math.min(maxshift, shift); } + if(shift > 0 || maxshift < 0) { var shiftTemplate = { left: [-shift, 0], @@ -32517,8 +32435,7 @@ function draw(gd, titleClass, options) { top: [0, -shift], bottom: [0, shift] }[avoid.side]; - titleGroup.attr('transform', - 'translate(' + shiftTemplate + ')'); + titleGroup.attr('transform', 'translate(' + shiftTemplate + ')'); } } } @@ -32565,7 +32482,11 @@ function draw(gd, titleClass, options) { return group; } -},{"../../constants/interactions":148,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/plots":244,"../../registry":256,"../color":51,"../drawing":72,"d3":16,"fast-isnumeric":18}],140:[function(_dereq_,module,exports){ +module.exports = { + draw: draw +}; + +},{"../../constants/alignment":145,"../../constants/interactions":148,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"../../registry":258,"../color":51,"../drawing":72,"d3":16,"fast-isnumeric":18}],139:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -32606,6 +32527,17 @@ var buttonsAttrs = templatedArray('button', { {valType: 'any'} ], + }, + args2: { + valType: 'info_array', + + freeLength: true, + items: [ + {valType: 'any'}, + {valType: 'any'}, + {valType: 'any'} + ], + }, label: { valType: 'string', @@ -32723,7 +32655,7 @@ module.exports = overrideAll(templatedArray('updatemenu', { } }), 'arraydraw', 'from-root'); -},{"../../lib/extend":162,"../../plot_api/edit_types":195,"../../plot_api/plot_template":202,"../../plots/font_attributes":238,"../../plots/pad_attributes":243,"../color/attributes":50}],141:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plot_api/edit_types":196,"../../plot_api/plot_template":203,"../../plots/font_attributes":239,"../../plots/pad_attributes":244,"../color/attributes":50}],140:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -32804,7 +32736,7 @@ module.exports = { } }; -},{}],142:[function(_dereq_,module,exports){ +},{}],141:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -32881,12 +32813,13 @@ function buttonDefaults(buttonIn, buttonOut) { if(visible) { coerce('method'); coerce('args'); + coerce('args2'); coerce('label'); coerce('execute'); } } -},{"../../lib":168,"../../plots/array_container_defaults":208,"./attributes":140,"./constants":141}],143:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/array_container_defaults":209,"./attributes":139,"./constants":140}],142:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -33008,6 +32941,7 @@ module.exports = function draw(gd) { 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); }); @@ -33195,10 +33129,14 @@ function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) { // 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); + if(buttonOpts.args2 && menuOpts.active === buttonIndex) { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2); + } else { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args); + } } gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active}); @@ -33534,9 +33472,9 @@ function removeAllButtons(gButton, newMenuIndexAttr) { .selectAll('g.' + constants.dropdownButtonClassName).remove(); } -},{"../../constants/alignment":146,"../../lib":168,"../../lib/svg_text_utils":189,"../../plot_api/plot_template":202,"../../plots/plots":244,"../color":51,"../drawing":72,"./constants":141,"./scrollbox":145,"d3":16}],144:[function(_dereq_,module,exports){ -arguments[4][138][0].apply(exports,arguments) -},{"./attributes":140,"./constants":141,"./defaults":142,"./draw":143,"dup":138}],145:[function(_dereq_,module,exports){ +},{"../../constants/alignment":145,"../../lib":169,"../../lib/svg_text_utils":190,"../../plot_api/plot_template":203,"../../plots/plots":245,"../color":51,"../drawing":72,"./constants":140,"./scrollbox":144,"d3":16}],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. @@ -34001,7 +33939,7 @@ ScrollBox.prototype.setTranslate = function setTranslate(translateX, translateY) } }; -},{"../../lib":168,"../color":51,"../drawing":72,"d3":16}],146:[function(_dereq_,module,exports){ +},{"../../lib":169,"../color":51,"../drawing":72,"d3":16}],145:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34066,6 +34004,22 @@ module.exports = { } }; +},{}],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. @@ -34123,10 +34077,6 @@ module.exports = { SHOW_PLACEHOLDER: 100, HIDE_PLACEHOLDER: 1000, - // ms between first mousedown and 2nd mouseup to constitute dblclick... - // we don't seem to have access to the system setting - DBLCLICKDELAY: 300, - // opacity dimming fraction for points that are not in selection DESELECTDIM: 0.2 }; @@ -34232,7 +34182,7 @@ exports.svgAttrs = { 'use strict'; // package version injected by `npm run preprocess` -exports.version = '1.48.2'; +exports.version = '1.51.3'; // inject promise polyfill _dereq_('es6-promise').polyfill(); @@ -34289,7 +34239,7 @@ register([ ]); // plot icons -exports.Icons = _dereq_('../build/ploticon'); +exports.Icons = _dereq_('./fonts/ploticon'); // unofficial 'beta' plot methods, use at your own risk exports.Plots = _dereq_('./plots/plots'); @@ -34301,7 +34251,7 @@ exports.Queue = _dereq_('./lib/queue'); // export d3 used in the bundle exports.d3 = _dereq_('d3'); -},{"../build/plotcss":1,"../build/ploticon":2,"./components/annotations":44,"./components/annotations3d":49,"./components/colorbar":57,"./components/colorscale":63,"./components/errorbars":78,"./components/fx":90,"./components/grid":94,"./components/images":99,"./components/legend":107,"./components/rangeselector":118,"./components/rangeslider":125,"./components/shapes":133,"./components/sliders":138,"./components/updatemenus":144,"./fonts/mathjax_config":152,"./lib/queue":182,"./locale-en":193,"./locale-en-us":192,"./plot_api":197,"./plot_api/plot_schema":201,"./plots/plots":244,"./registry":256,"./snapshot":261,"./traces/scatter":376,"d3":16,"es6-promise":17}],152:[function(_dereq_,module,exports){ +},{"../build/plotcss":1,"./components/annotations":44,"./components/annotations3d":49,"./components/colorbar":57,"./components/colorscale":63,"./components/errorbars":78,"./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":258,"./snapshot":263,"./traces/scatter":389,"d3":16,"es6-promise":17}],152:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34341,6 +34291,150 @@ module.exports = function() { * 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'; @@ -34396,7 +34490,7 @@ exports.isBottomAnchor = function isBottomAnchor(opts) { ); }; -},{}],154:[function(_dereq_,module,exports){ +},{}],155:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34637,7 +34731,7 @@ module.exports = { pathAnnulus: pathAnnulus }; -},{"./mod":175}],155:[function(_dereq_,module,exports){ +},{"./mod":176}],156:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34794,7 +34888,7 @@ function _rowLength(z, fn, len0) { return 0; } -},{}],156:[function(_dereq_,module,exports){ +},{}],157:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34827,7 +34921,7 @@ module.exports = function cleanNumber(v) { return BADNUM; }; -},{"../constants/numerical":149,"fast-isnumeric":18}],157:[function(_dereq_,module,exports){ +},{"../constants/numerical":149,"fast-isnumeric":18}],158:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34855,7 +34949,7 @@ module.exports = function clearGlCanvases(gd) { } }; -},{}],158:[function(_dereq_,module,exports){ +},{}],159:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -34878,7 +34972,7 @@ module.exports = function clearResponsive(gd) { } }; -},{}],159:[function(_dereq_,module,exports){ +},{}],160:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -35342,7 +35436,7 @@ function validate(value, opts) { } exports.validate = validate; -},{"../components/colorscale/scales":66,"../constants/interactions":148,"../plots/attributes":209,"./array":155,"./mod":175,"./nested_property":176,"./regex":183,"fast-isnumeric":18,"tinycolor2":34}],160:[function(_dereq_,module,exports){ +},{"../components/colorscale/scales":66,"../constants/interactions":148,"../plots/attributes":210,"./array":156,"./mod":176,"./nested_property":177,"./regex":184,"fast-isnumeric":18,"tinycolor2":34}],161:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -35928,7 +36022,110 @@ exports.findExactDates = function(data, calendar) { }; }; -},{"../constants/numerical":149,"../registry":256,"./loggers":172,"./mod":175,"d3":16,"fast-isnumeric":18}],161:[function(_dereq_,module,exports){ +},{"../constants/numerical":149,"../registry":258,"./loggers":173,"./mod":176,"d3":16,"fast-isnumeric":18}],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":16}],163:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36101,7 +36298,7 @@ var Events = { module.exports = Events; -},{"events":15}],162:[function(_dereq_,module,exports){ +},{"events":15}],164:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36215,7 +36412,7 @@ function _extend(inputs, isDeep, keepAllKeys, noArrayCopies) { return target; } -},{"./is_plain_object.js":169}],163:[function(_dereq_,module,exports){ +},{"./is_plain_object.js":170}],165:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36266,7 +36463,7 @@ module.exports = function filterUnique(array) { return out; }; -},{}],164:[function(_dereq_,module,exports){ +},{}],166:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36314,7 +36511,7 @@ function isCalcData(cont) { ); } -},{}],165:[function(_dereq_,module,exports){ +},{}],167:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36558,44 +36755,7 @@ exports.findPointOnPath = function findPointOnPath(path, val, coord, opts) { return pt; }; -},{"./mod":175}],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'; - -/** - * 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 - */ -module.exports = function(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'); - } - - return gd; // otherwise assume that gd is a DOM element -}; - -},{}],167:[function(_dereq_,module,exports){ +},{"./mod":176}],168:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36611,7 +36771,7 @@ module.exports = function(gd) { module.exports = function identity(d) { return d; }; -},{}],168:[function(_dereq_,module,exports){ +},{}],169:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -36620,7 +36780,6 @@ module.exports = function identity(d) { return d; }; * LICENSE file in the root directory of this source tree. */ - 'use strict'; var d3 = _dereq_('d3'); @@ -36752,7 +36911,13 @@ lib.throttle = throttleModule.throttle; lib.throttleDone = throttleModule.done; lib.clearThrottle = throttleModule.clear; -lib.getGraphDiv = _dereq_('./get_graph_div'); +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'); @@ -37068,13 +37233,25 @@ lib.noneOrAll = function(containerIn, containerOut, attrList) { * @param {object} cd : calcdata trace * @param {string} cdAttr : calcdata key */ -lib.mergeArray = function(traceAttr, cd, cdAttr) { +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++) cd[i][cdAttr] = traceAttr[i]; + 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) * @@ -37263,6 +37440,8 @@ lib.minExtend = function(obj1, obj2) { } else { objOut[k] = v.slice(0, arrayLen); } + } else if(lib.isTypedArray(v)) { + objOut[k] = v.subarray(0, arrayLen); } else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]); else objOut[k] = v; } @@ -37290,63 +37469,20 @@ lib.containsAny = function(s, fragments) { return false; }; -lib.isPlotDiv = function(el) { - var el3 = d3.select(el); - return el3.node() instanceof HTMLElement && - el3.size() && - el3.classed('js-plotly-plot'); -}; - -lib.removeElement = function(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 - */ -lib.addStyleRule = function(selector, styleString) { - lib.addRelatedStyleRule('global', selector, styleString); -}; - -/** - * for dynamically adding style rules - * to a stylesheet uniquely identified by a uid - */ -lib.addRelatedStyleRule = function(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 lib.warn('addStyleRule failed'); -}; - -/** - * to remove from the page a stylesheet identified by a given uid - */ -lib.deleteRelatedStyleRule = function(uid) { - var id = 'plotly.js-style-' + uid; - var style = document.getElementById(id); - if(style) lib.removeElement(style); -}; - 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 @@ -37604,7 +37740,7 @@ lib.numSeparate = function(value, separators, separatethousands) { return x1 + x2; }; -lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)(:[^}]*)?}/g; +lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)([:|\|][^}]*)?}/g; var SIMPLE_PROPERTY_REGEX = /^\w*$/; /** @@ -37633,9 +37769,25 @@ lib.templateString = function(string, obj) { }); }; -var TEMPLATE_STRING_FORMAT_SEPARATOR = /^:/; -var numberOfHoverTemplateWarnings = 0; -var maximumNumberOfHoverTemplateWarnings = 10; +var hovertemplateWarnings = { + max: 10, + count: 0, + name: 'hovertemplate' +}; +lib.hovertemplateString = function() { + return templateFormatString.apply(hovertemplateWarnings, arguments); +}; + +var texttemplateWarnings = { + max: 10, + count: 0, + name: 'texttemplate' +}; +lib.texttemplateString = function() { + return templateFormatString.apply(texttemplateWarnings, arguments); +}; + +var TEMPLATE_STRING_FORMAT_SEPARATOR = /^[:|\|]/; /** * Substitute values from an object into a string and optionally formats them using d3-format, * or fallback to associated labels. @@ -37645,15 +37797,17 @@ var maximumNumberOfHoverTemplateWarnings = 10; * 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} d3 locale * @param {obj} data objects containing substitution values * * @return {string} templated string */ -lib.hovertemplateString = function(string, labels, d3locale) { +function templateFormatString(string, labels, d3locale) { + var opts = this; var args = arguments; + if(!labels) labels = {}; // Not all that useful, but cache nestedProperty instantiation // just in case it speeds things up *slightly*: var getterCache = {}; @@ -37662,6 +37816,7 @@ lib.hovertemplateString = function(string, labels, d3locale) { var obj, value, i; for(i = 3; i < args.length; i++) { obj = args[i]; + if(!obj) continue; if(obj.hasOwnProperty(key)) { value = obj[key]; break; @@ -37674,32 +37829,38 @@ lib.hovertemplateString = function(string, labels, d3locale) { if(value !== undefined) break; } - if(value === undefined) { - if(numberOfHoverTemplateWarnings < maximumNumberOfHoverTemplateWarnings) { - lib.warn('Variable \'' + key + '\' in hovertemplate could not be found!'); + if(value === undefined && opts) { + if(opts.count < opts.max) { + lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!'); value = match; } - if(numberOfHoverTemplateWarnings === maximumNumberOfHoverTemplateWarnings) { - lib.warn('Too many hovertemplate warnings - additional warnings will be suppressed'); + if(opts.count === opts.max) { + lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed'); } - numberOfHoverTemplateWarnings++; + opts.count++; + + return match; } if(format) { var fmt; - if(d3locale) { - fmt = d3locale.numberFormat; - } else { - fmt = d3.format; + if(format[0] === ':') { + fmt = d3locale ? d3locale.numberFormat : d3.format; + value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value); + } + + if(format[0] === '|') { + fmt = d3locale ? d3locale.timeFormat.utc : d3.time.format.utc; + var ms = lib.dateTime2ms(value); + value = lib.formatDate(ms, format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''), false, fmt); } - 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 @@ -37774,6 +37935,10 @@ lib.isValidTextValue = function(v) { return v || v === 0; }; +/** + * @param {number} ratio + * @param {number} n (number of decimal places) + */ lib.formatPercent = function(ratio, n) { n = n || 0; var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%'; @@ -37786,7 +37951,41 @@ lib.formatPercent = function(ratio, n) { return str; }; -},{"../constants/numerical":149,"./anchor_utils":153,"./angles":154,"./array":155,"./clean_number":156,"./clear_responsive":158,"./coerce":159,"./dates":160,"./extend":162,"./filter_unique":163,"./filter_visible":164,"./geometry2d":165,"./get_graph_div":166,"./identity":167,"./is_plain_object":169,"./keyed_container":170,"./localize":171,"./loggers":172,"./make_trace_groups":173,"./matrix":174,"./mod":175,"./nested_property":176,"./noop":177,"./notifier":178,"./push_unique":181,"./regex":183,"./relative_attr":184,"./relink_private":185,"./search":186,"./stats":188,"./throttle":190,"./to_log_range":191,"d3":16,"fast-isnumeric":18}],169:[function(_dereq_,module,exports){ +lib.isHidden = function(gd) { + var display = window.getComputedStyle(gd).display; + return !display || display === 'none'; +}; + +lib.getTextTransform = function(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; +}; + +},{"../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":16,"fast-isnumeric":18}],170:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -37814,7 +38013,7 @@ module.exports = function isPlainObject(obj) { ); }; -},{}],170:[function(_dereq_,module,exports){ +},{}],171:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38007,7 +38206,7 @@ module.exports = function keyedContainer(baseObj, path, keyName, valueName) { return obj; }; -},{"./nested_property":176}],171:[function(_dereq_,module,exports){ +},{"./nested_property":177}],172:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38063,7 +38262,7 @@ module.exports = function localize(gd, s) { return s; }; -},{"../registry":256}],172:[function(_dereq_,module,exports){ +},{"../registry":258}],173:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38147,7 +38346,7 @@ function apply(f, args) { } } -},{"../plot_api/plot_config":200}],173:[function(_dereq_,module,exports){ +},{"../plot_api/plot_config":201}],174:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38156,9 +38355,10 @@ function apply(f, args) { * 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 * @@ -38181,10 +38381,15 @@ module.exports = function makeTraceGroups(traceLayer, cdModule, 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; }; -},{}],174:[function(_dereq_,module,exports){ +},{"d3":16}],175:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38291,7 +38496,7 @@ exports.apply2DTransform2 = function(transform) { }; }; -},{}],175:[function(_dereq_,module,exports){ +},{}],176:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38326,7 +38531,7 @@ module.exports = { modHalf: modHalf }; -},{}],176:[function(_dereq_,module,exports){ +},{}],177:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38572,7 +38777,7 @@ function badContainer(container, propStr, propParts) { }; } -},{"./array":155,"fast-isnumeric":18}],177:[function(_dereq_,module,exports){ +},{"./array":156,"fast-isnumeric":18}],178:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38588,7 +38793,7 @@ function badContainer(container, propStr, propParts) { module.exports = function noop() {}; -},{}],178:[function(_dereq_,module,exports){ +},{}],179:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38670,7 +38875,7 @@ module.exports = function(text, displayLength) { }); }; -},{"d3":16,"fast-isnumeric":18}],179:[function(_dereq_,module,exports){ +},{"d3":16,"fast-isnumeric":18}],180:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38718,7 +38923,7 @@ module.exports = function overrideCursor(el3, csr) { } }; -},{"./setcursor":187}],180:[function(_dereq_,module,exports){ +},{"./setcursor":188}],181:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -38970,7 +39175,7 @@ polygon.filter = function filter(pts, tolerance) { }; }; -},{"../constants/numerical":149,"./matrix":174}],181:[function(_dereq_,module,exports){ +},{"../constants/numerical":149,"./matrix":175}],182:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39008,7 +39213,7 @@ module.exports = function pushUnique(array, item) { return array; }; -},{}],182:[function(_dereq_,module,exports){ +},{}],183:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39216,7 +39421,7 @@ queue.plotDo = function(gd, func, args) { module.exports = queue; -},{"../lib":168,"../plot_api/plot_config":200}],183:[function(_dereq_,module,exports){ +},{"../lib":169,"../plot_api/plot_config":201}],184:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39246,7 +39451,7 @@ exports.counter = function(head, tail, openEnded, matchBeginning) { return new RegExp(startWithPrefix + head + '([2-9]|[1-9][0-9]+)?' + fullTail); }; -},{}],184:[function(_dereq_,module,exports){ +},{}],185:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39299,7 +39504,7 @@ module.exports = function(baseAttr, relativeAttr) { return baseAttr + relativeAttr; }; -},{}],185:[function(_dereq_,module,exports){ +},{}],186:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39357,7 +39562,7 @@ module.exports = function relinkPrivateKeys(toContainer, fromContainer) { } }; -},{"./array":155,"./is_plain_object":169}],186:[function(_dereq_,module,exports){ +},{"./array":156,"./is_plain_object":170}],187:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39544,7 +39749,7 @@ exports.findIndexOfMin = function(arr, fn) { return ind; }; -},{"./identity":167,"./loggers":172,"fast-isnumeric":18}],187:[function(_dereq_,module,exports){ +},{"./identity":168,"./loggers":173,"fast-isnumeric":18}],188:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39567,7 +39772,7 @@ module.exports = function setCursor(el3, csr) { if(csr) el3.classed('cursor-' + csr, true); }; -},{}],188:[function(_dereq_,module,exports){ +},{}],189:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39677,7 +39882,7 @@ exports.interp = function(arr, n) { return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)]; }; -},{"./array":155,"fast-isnumeric":18}],189:[function(_dereq_,module,exports){ +},{"./array":156,"fast-isnumeric":18}],190:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -39939,13 +40144,14 @@ var ZERO_WIDTH_SPACE = '\u200b'; */ var PROTOCOLS = ['http:', 'https:', 'mailto:', '', undefined, ':']; -var NEWLINES = /(\r\n?|\n)/g; +var NEWLINES = exports.NEWLINES = /(\r\n?|\n)/g; var SPLIT_TAGS = /(<[^<>]*>)/; var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i; var BR_TAG = //i; +exports.BR_TAG_ALL = //gi; /* * style and href: pull them out of either single or double quotes. Also @@ -40492,7 +40698,7 @@ exports.makeEditable = function(context, options) { return d3.rebind(context, dispatch, 'on'); }; -},{"../constants/alignment":146,"../constants/xmlns_namespaces":150,"../lib":168,"d3":16}],190:[function(_dereq_,module,exports){ +},{"../constants/alignment":145,"../constants/xmlns_namespaces":150,"../lib":169,"d3":16}],191:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -40595,7 +40801,7 @@ function _clearTimeout(cache) { } } -},{}],191:[function(_dereq_,module,exports){ +},{}],192:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -40623,7 +40829,7 @@ module.exports = function toLogRange(val, range) { return newVal; }; -},{"fast-isnumeric":18}],192:[function(_dereq_,module,exports){ +},{"fast-isnumeric":18}],193:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -40645,7 +40851,7 @@ module.exports = { } }; -},{}],193:[function(_dereq_,module,exports){ +},{}],194:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -40688,7 +40894,7 @@ module.exports = { } }; -},{}],194:[function(_dereq_,module,exports){ +},{}],195:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -40746,7 +40952,7 @@ module.exports = function containerArrayMatch(astr) { return {array: arrayStr, index: Number(match[1]), property: match[3] || ''}; }; -},{"../registry":256}],195:[function(_dereq_,module,exports){ +},{"../registry":258}],196:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -40871,7 +41077,7 @@ function overrideOne(attr, editTypeOverride, overrideContainers, key) { } } -},{"../lib":168}],196:[function(_dereq_,module,exports){ +},{"../lib":169}],197:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -41563,7 +41769,7 @@ exports.clearAxisTypes = function(gd, traces, layoutUpdate) { } }; -},{"../components/color":51,"../lib":168,"../plots/cartesian/axis_ids":215,"../plots/plots":244,"../registry":256,"fast-isnumeric":18,"gl-mat4/fromQuat":19}],197:[function(_dereq_,module,exports){ +},{"../components/color":51,"../lib":169,"../plots/cartesian/axis_ids":216,"../plots/plots":245,"../registry":258,"fast-isnumeric":18,"gl-mat4/fromQuat":19}],198:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -41606,7 +41812,7 @@ var templateApi = _dereq_('./template_api'); exports.makeTemplate = templateApi.makeTemplate; exports.validateTemplate = templateApi.validateTemplate; -},{"../snapshot/download":258,"./plot_api":199,"./template_api":204,"./to_image":205,"./validate":206}],198:[function(_dereq_,module,exports){ +},{"../snapshot/download":260,"./plot_api":200,"./template_api":205,"./to_image":206,"./validate":207}],199:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -41819,7 +42025,7 @@ exports.applyContainerArrayChanges = function applyContainerArrayChanges(gd, np, return true; }; -},{"../lib/is_plain_object":169,"../lib/loggers":172,"../lib/noop":177,"../lib/search":186,"../registry":256,"./container_array_match":194}],199:[function(_dereq_,module,exports){ +},{"../lib/is_plain_object":170,"../lib/loggers":173,"../lib/noop":178,"../lib/search":187,"../registry":258,"./container_array_match":195}],200:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -42004,7 +42210,7 @@ function plot(gd, data, layout, config) { if(gd._context.responsive) { if(!gd._responsiveChartHandler) { // Keep a reference to the resize handler to purge it down the road - gd._responsiveChartHandler = function() { Plots.resize(gd); }; + gd._responsiveChartHandler = function() { if(!Lib.isHidden(gd)) Plots.resize(gd); }; // Listen to window resize window.addEventListener('resize', gd._responsiveChartHandler); @@ -42112,6 +42318,17 @@ function plot(gd, data, layout, config) { subroutines.drawMarginPushers(gd); Axes.allowAutoMargin(gd); + // TODO can this be moved elsewhere? + if(fullLayout._has('pie')) { + var fullData = gd._fullData; + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + if(trace.type === 'pie' && trace.automargin) { + Plots.allowAutoMargin(gd, 'pie.' + trace.uid + '.automargin'); + } + } + } + Plots.doAutoMargin(gd); return Plots.previousPromises(gd); } @@ -44272,7 +44489,7 @@ var traceUIControlPatterns = [ {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'}, {pattern: /^dimensions\[\d+\]\.constraintrange/}, {pattern: /^node\.(x|y|groups)/}, // for Sankey nodes - {pattern: /^level$/}, // for Sunburst traces + {pattern: /^level$/}, // for Sunburst & Treemap traces // below this you must be in editable: true mode // TODO: I still put name and title with `trace.uirevision` @@ -44479,6 +44696,7 @@ function react(gd, data, layout, config) { function addFrames() { return exports.addFrames(gd, frames); } gd = Lib.getGraphDiv(gd); + helpers.clearPromiseQueue(gd); var oldFullData = gd._fullData; var oldFullLayout = gd._fullLayout; @@ -44633,7 +44851,11 @@ function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRe var i, trace; function getTraceValObject(parts) { - return PlotSchema.getTraceValObject(trace, parts); + var out = PlotSchema.getTraceValObject(trace, parts); + if(!trace._module.animatable && out.anim) { + out.anim = false; + } + return out; } var diffOpts = { @@ -45442,7 +45664,7 @@ function deleteFrames(gd, frameList) { } } - frameList = frameList.slice(0); + frameList = frameList.slice(); frameList.sort(); for(i = frameList.length - 1; i >= 0; i--) { @@ -45598,9 +45820,15 @@ function makePlotFramework(gd) { // single pie layer for the whole plot fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true); + // single treemap layer for the whole plot + fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true); + // single sunburst layer for the whole plot fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true); + // single indicator layer for the whole plot + fullLayout._indicatorlayer = fullLayout._toppaper.append('g').classed('indicatorlayer', true); + // fill in image server scrape-svg fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true); @@ -45659,7 +45887,7 @@ exports._guiUpdate = guiEdit(update); exports._storeDirectGUIEdit = _storeDirectGUIEdit; -},{"../components/color":51,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":168,"../lib/events":161,"../lib/queue":182,"../lib/svg_text_utils":189,"../plots/cartesian/axes":212,"../plots/cartesian/constants":218,"../plots/cartesian/graph_interact":221,"../plots/cartesian/select":229,"../plots/plots":244,"../plots/polar/legacy":247,"../registry":256,"./edit_types":195,"./helpers":196,"./manage_arrays":198,"./plot_config":200,"./plot_schema":201,"./subroutines":203,"d3":16,"fast-isnumeric":18,"has-hover":20}],200:[function(_dereq_,module,exports){ +},{"../components/color":51,"../components/drawing":72,"../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":258,"./edit_types":196,"./helpers":197,"./manage_arrays":199,"./plot_config":201,"./plot_schema":202,"./subroutines":204,"d3":16,"fast-isnumeric":18,"has-hover":20}],201:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -45788,6 +46016,12 @@ var configAttributes = { values: [false, 'reset', 'autosize', 'reset+autosize'], dflt: 'reset+autosize', + }, + doubleClickDelay: { + valType: 'number', + dflt: 300, + min: 0, + }, showAxisDragHandles: { @@ -45839,6 +46073,11 @@ var configAttributes = { valType: 'boolean', dflt: false, + }, + showEditInChartStudio: { + valType: 'boolean', + dflt: false, + }, modeBarButtonsToRemove: { valType: 'any', @@ -45953,7 +46192,7 @@ module.exports = { dfltConfig: dfltConfig }; -},{}],201:[function(_dereq_,module,exports){ +},{}],202:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -45962,7 +46201,6 @@ module.exports = { * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Registry = _dereq_('../registry'); @@ -46445,6 +46683,7 @@ function getTraceAttributes(type) { var out = { meta: _module.meta || {}, categories: _module.categories || {}, + animatable: Boolean(_module.animatable), type: type, attributes: formatAttributes(attributes), }; @@ -46457,6 +46696,15 @@ function getTraceAttributes(type) { 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; } @@ -46655,7 +46903,7 @@ function insertAttrs(baseAttrs, newAttrs, astr) { np.set(extendDeepAll(np.get() || {}, newAttrs)); } -},{"../lib":168,"../plots/animation_attributes":207,"../plots/attributes":209,"../plots/frame_attributes":239,"../plots/layout_attributes":242,"../plots/polar/legacy/area_attributes":245,"../plots/polar/legacy/axis_attributes":246,"../registry":256,"./edit_types":195,"./plot_config":200}],202:[function(_dereq_,module,exports){ +},{"../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":258,"./edit_types":196,"./plot_config":201}],203:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -46967,7 +47215,7 @@ exports.arrayEditor = function(parentIn, containerStr, itemOut) { }; }; -},{"../lib":168,"../plots/attributes":209}],203:[function(_dereq_,module,exports){ +},{"../lib":169,"../plots/attributes":210}],204:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -47042,7 +47290,7 @@ function lsInner(gd) { // 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); + return Plots.previousPromises(gd); } function getLinePosition(ax, counterAx, side) { @@ -47317,7 +47565,7 @@ function lsInner(gd) { Axes.makeClipPaths(gd); - return gd._promises.length && Promise.all(gd._promises); + return Plots.previousPromises(gd); } function shouldShowLinesOrTicks(ax, subplot) { @@ -47548,8 +47796,7 @@ exports.doCamera = function(gd) { var sceneLayout = fullLayout[sceneIds[i]]; var scene = sceneLayout._scene; - var cameraData = sceneLayout.camera; - scene.setCamera(cameraData); + scene.setViewport(sceneLayout); } }; @@ -47569,9 +47816,11 @@ exports.drawData = function(gd) { // styling separate from drawing Plots.style(gd); - // show annotations and shapes + // draw components that can be drawn on axes, + // and that do not push the margins Registry.getComponentMethod('shapes', 'draw')(gd); Registry.getComponentMethod('annotations', 'draw')(gd); + Registry.getComponentMethod('images', 'draw')(gd); // Mark the first render as complete fullLayout._replotting = false; @@ -47687,9 +47936,6 @@ exports.doAutoRangeAndConstraints = function(gd) { // 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? @@ -47709,7 +47955,7 @@ exports.drawMarginPushers = function(gd) { Registry.getComponentMethod('colorbar', 'draw')(gd); }; -},{"../components/color":51,"../components/drawing":72,"../components/modebar":110,"../components/titles":139,"../constants/alignment":146,"../lib":168,"../lib/clear_gl_canvases":157,"../plots/cartesian/autorange":211,"../plots/cartesian/axes":212,"../plots/cartesian/constraints":219,"../plots/plots":244,"../registry":256,"d3":16}],204:[function(_dereq_,module,exports){ +},{"../components/color":51,"../components/drawing":72,"../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":258,"d3":16}],205:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -48171,7 +48417,7 @@ function format(opts) { return opts; } -},{"../lib":168,"../plots/attributes":209,"../plots/plots":244,"./plot_config":200,"./plot_schema":201,"./plot_template":202}],205:[function(_dereq_,module,exports){ +},{"../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. @@ -48226,8 +48472,6 @@ var attrs = { } }; -var IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/; - /** Plotly.toImage * * @param {object | string | HTML div} gd @@ -48331,7 +48575,7 @@ function toImage(gd, opts) { if(imageDataOnly) { return resolve(svg); } else { - return resolve('data:image/svg+xml,' + encodeURIComponent(svg)); + return resolve(helpers.encodeSVG(svg)); } } @@ -48358,7 +48602,7 @@ function toImage(gd, opts) { function urlToImageData(url) { if(imageDataOnly) { - return url.replace(IMAGE_URL_PREFIX, ''); + return url.replace(helpers.IMAGE_URL_PREFIX, ''); } else { return url; } @@ -48376,7 +48620,7 @@ function toImage(gd, opts) { module.exports = toImage; -},{"../lib":168,"../snapshot/helpers":260,"../snapshot/svgtoimg":262,"../snapshot/tosvg":264,"./plot_api":199,"fast-isnumeric":18}],206:[function(_dereq_,module,exports){ +},{"../lib":169,"../snapshot/helpers":262,"../snapshot/svgtoimg":264,"../snapshot/tosvg":266,"./plot_api":200,"fast-isnumeric":18}],207:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -48803,7 +49047,7 @@ function convertPathToAttributeString(path) { return astr; } -},{"../lib":168,"../plots/plots":244,"./plot_config":200,"./plot_schema":201}],207:[function(_dereq_,module,exports){ +},{"../lib":169,"../plots/plots":245,"./plot_config":201,"./plot_schema":202}],208:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -48915,7 +49159,7 @@ module.exports = { } }; -},{}],208:[function(_dereq_,module,exports){ +},{}],209:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -49010,7 +49254,7 @@ module.exports = function handleArrayContainerDefaults(parentObjIn, parentObjOut return contOut; }; -},{"../lib":168,"../plot_api/plot_template":202}],209:[function(_dereq_,module,exports){ +},{"../lib":169,"../plot_api/plot_template":203}],210:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -49151,7 +49395,7 @@ module.exports = { } }; -},{"../components/fx/attributes":81}],210:[function(_dereq_,module,exports){ +},{"../components/fx/attributes":81}],211:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -49180,7 +49424,7 @@ module.exports = { } }; -},{}],211:[function(_dereq_,module,exports){ +},{}],212:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -49481,6 +49725,8 @@ function doAutoRange(gd, ax) { * (unless one end is overridden by tozero) * tozero: (boolean) make sure to include zero if axis is linear, * and make it a tight bound if possible + * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus) + * is linearized (for log scale axes) * * @return {object} * - min {array of objects} @@ -49503,6 +49749,7 @@ function findExtremes(ax, data, opts) { var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-'); var isLog = ax.type === 'log'; var hasArrayOption = false; + var vpadLinearized = opts.vpadLinearized || false; var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax; function makePadAccessor(item) { @@ -49554,16 +49801,22 @@ function findExtremes(ax, data, opts) { 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(vpadLinearized) { + dmin = ax.c2l(di) - vpadminus(i); + dmax = ax.c2l(di) + vpadplus(i); + } else { + vmin = di - vpadminus(i); + vmax = di + vpadplus(i); + // special case for log axes: if vpad makes this object span + // more than an order of mag, clip it to one order. This is so + // we don't have non-positive errors or absurdly large lower + // range due to rounding errors + if(isLog && vmin < vmax / 10) vmin = vmax / 10; + + dmin = ax.c2l(vmin); + dmax = ax.c2l(vmax); + } if(tozero) { dmin = Math.min(0, dmin); @@ -49670,7 +49923,7 @@ function goodNumber(v) { function lessOrEqual(v0, v1) { return v0 <= v1; } function greaterOrEqual(v0, v1) { return v0 >= v1; } -},{"../../constants/numerical":149,"../../lib":168,"../../registry":256,"fast-isnumeric":18}],212:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../registry":258,"fast-isnumeric":18}],213:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -49679,7 +49932,6 @@ function greaterOrEqual(v0, v1) { return v0 >= v1; } * LICENSE file in the root directory of this source tree. */ - 'use strict'; var d3 = _dereq_('d3'); @@ -49706,8 +49958,11 @@ 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 alignmentConstants = _dereq_('../../constants/alignment'); +var MID_SHIFT = alignmentConstants.MID_SHIFT; +var CAP_SHIFT = alignmentConstants.CAP_SHIFT; +var LINE_SPACING = alignmentConstants.LINE_SPACING; +var OPPOSITE_SIDE = alignmentConstants.OPPOSITE_SIDE; var axes = module.exports = {}; @@ -50244,12 +50499,14 @@ axes.calcTicks = function calcTicks(ax) { if((ax._tmin < startTick) !== axrev) return []; // return the full set of tick vals - var 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; @@ -50257,21 +50514,29 @@ axes.calcTicks = function calcTicks(ax) { 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(vals.length > maxTicks || x === xPrevious) break; + if(tickVals.length > maxTicks || x === xPrevious) break; xPrevious = x; - vals.push(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) { - vals.pop(); + tickVals.pop(); } // save the last tick as well as first, so we can // show the exponent only on the last one - ax._tmax = vals[vals.length - 1]; + 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. @@ -50280,8 +50545,15 @@ axes.calcTicks = function calcTicks(ax) { ax._prevDateHead = ''; ax._inCalcTicks = true; - var ticksOut = new Array(vals.length); - for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]); + 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; @@ -50496,7 +50768,6 @@ function autoTickRound(ax) { 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)) { @@ -50610,7 +50881,7 @@ axes.tickFirst = function(ax) { // 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) { +axes.tickText = function(ax, x, hover, noSuffixPrefix) { var out = tickTextObj(ax, x); var arrayMode = ax.tickmode === 'array'; var extraPrecision = hover || arrayMode; @@ -50656,8 +50927,10 @@ axes.tickText = function(ax, x, hover) { else formatLinear(ax, out, hover, extraPrecision, hideexp); // add prefix and suffix - if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text; - if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix; + 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 @@ -51291,6 +51564,25 @@ axes.draw = function(gd, arg, opts) { * @param {object} ax (full) axis object * @param {object} opts * - @param {boolean} skipTitle (set to true to skip axis title draw call) + * + * Depends on: + * - ax._mainSubplot (from linkSubplots) + * - ax._mainAxis + * - ax._anchorAxis + * - ax._subplotsWith + * - ax._counterDomainMin, ax._counterDomainMax (optionally, from linkSubplots) + * - ax._tickAngles (on redraw only, old value relinked during supplyDefaults) + * - ax._mainLinePosition (from lsInner) + * - ax._mainMirrorPosition + * - ax._linepositions + * + * Fills in: + * - ax._vals: + * - ax._gridVals: + * - ax._selections: + * - ax._tickAngles: + * - ax._depth (when required only): + * - and calls ax.setScale */ axes.drawOne = function(gd, ax, opts) { opts = opts || {}; @@ -51303,12 +51595,10 @@ axes.drawOne = function(gd, ax, opts) { 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 mainPlotinfo = fullLayout._plots[ax._mainSubplot]; var mainAxLayer = mainPlotinfo[axLetter + 'axislayer']; - var subplotsWithAx = ax._subplotsWith; var vals = ax._vals = axes.calcTicks(ax); @@ -51319,13 +51609,29 @@ axes.drawOne = function(gd, ax, opts) { 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 + // linkup 'previous' tick angles on redraws + if(ax._tickAngles) ax._prevTickAngles = ax._tickAngles; ax._tickAngles = {}; + // measure [in px] between axis position and outward-most part of bounding box + // (touching either the tick label or ticks) + // depth can be expansive to compute, so we only do so when required + ax._depth = null; + + // calcLabelLevelBbox can be expensive, + // so make sure to not call it twice during the same Axes.drawOne call + // by stashing label-level bounding boxes per tick-label class + var llbboxes = {}; + function getLabelLevelBbox(suffix) { + var cls = axId + (suffix || 'tick'); + if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls); + return llbboxes[cls]; + } + + if(!ax.visible) return; var transFn = axes.makeTransFn(ax); var tickVals; @@ -51346,7 +51652,9 @@ axes.drawOne = function(gd, ax, opts) { var dividerVals = getDividerVals(ax, vals); if(!fullLayout._hasOnlyLargeSploms) { - // keep track of which subplots (by main conteraxis) we've already + var subplotsWithAx = ax._subplotsWith; + + // keep track of which subplots (by main counter axis) we've already // drawn grids for, so we don't overdraw overlaying subplots var finishedGrids = {}; @@ -51414,7 +51722,9 @@ axes.drawOne = function(gd, ax, opts) { transFn: transFn }); - tickSubplots = Object.keys(ax._linepositions || {}); + if(ax.mirror === 'allticks') { + tickSubplots = Object.keys(ax._linepositions || {}); + } } for(i = 0; i < tickSubplots.length; i++) { @@ -51448,13 +51758,12 @@ axes.drawOne = function(gd, ax, opts) { }); 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; + var bboxKey = {x: 'height', y: 'width'}[axLetter]; + var standoff = getLabelLevelBbox()[bboxKey] + pad + + (ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0); return axes.drawLabels(gd, ax, { vals: getSecondaryLabelVals(ax, vals), @@ -51463,197 +51772,129 @@ axes.drawOne = function(gd, ax, opts) { repositionOnUpdate: true, secondary: true, transFn: transFn, - labelFns: axes.makeLabelFns(ax, mainLinePosition + labelLength * sgn) + labelFns: axes.makeLabelFns(ax, mainLinePosition + standoff * tickSigns[4]) }); }); seq.push(function() { - labelLength += getLabelLevelSpan(ax, axId + 'tick2'); - ax._labelLength = labelLength; + ax._depth = tickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition); return drawDividers(gd, ax, { vals: dividerVals, layer: mainAxLayer, - path: axes.makeTickPath(ax, mainLinePosition, sgn, labelLength), + path: axes.makeTickPath(ax, mainLinePosition, tickSigns[4], ax._depth), 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]); - } - } + } else if(ax.title.hasOwnProperty('standoff')) { + seq.push(function() { + ax._depth = tickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition); + }); } var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax); - function doAutoMargins() { + seq.push(function() { var s = ax.side.charAt(0); + var sMirror = OPPOSITE_SIDE[ax.side].charAt(0); + var pos = axes.getPxPosition(gd, ax); + var outsideTickLen = ax.ticks === 'outside' ? ax.ticklen : 0; + var llbbox; + var push; + var mirrorPush; var rangeSliderPush; - if(hasRangeSlider) { - rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax); + if(ax.automargin || hasRangeSlider) { + if(ax.type === 'multicategory') { + llbbox = getLabelLevelBbox('tick2'); + } else { + llbbox = getLabelLevelBbox(); + if(axLetter === 'x' && s === 'b') { + ax._depth = Math.max(llbbox.width > 0 ? llbbox.bottom - pos : 0, outsideTickLen); + } + } } - Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush); - if(ax.automargin && (!hasRangeSlider || s !== 'b')) { + if(ax.automargin) { push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + var domainIndices = [0, 1]; - var bbox = ax._boundingBox; - var titleOffset = getTitleOffset(gd, ax); - var anchorAxDomainIndex; - var offset; + if(axLetter === 'x') { + if(s === 'b') { + push[s] = ax._depth; + } else { + push[s] = ax._depth = Math.max(llbbox.width > 0 ? pos - llbbox.top : 0, outsideTickLen); + domainIndices.reverse(); + } - 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; + if(llbbox.width > 0) { + var rExtra = llbbox.right - (ax._offset + ax._length); + if(rExtra > 0) { + push.xr = 1; + push.r = rExtra; + } + var lExtra = ax._offset - llbbox.left; + if(lExtra > 0) { + push.xl = 0; + push.l = lExtra; + } + } + } else { + if(s === 'l') { + push[s] = ax._depth = Math.max(llbbox.height > 0 ? pos - llbbox.left : 0, outsideTickLen); + } else { + push[s] = ax._depth = Math.max(llbbox.height > 0 ? llbbox.right - pos : 0, outsideTickLen); + domainIndices.reverse(); + } + + if(llbbox.height > 0) { + var bExtra = llbbox.bottom - (ax._offset + ax._length); + if(bExtra > 0) { + push.yb = 0; + push.b = bExtra; + } + var tExtra = ax._offset - llbbox.top; + if(tExtra > 0) { + push.yt = 1; + push.t = tExtra; + } + } } push[counterLetter] = ax.anchor === 'free' ? ax.position : - ax._anchorAxis.domain[anchorAxDomainIndex]; - - if(push[s] > 0) { - push[s] += offset; - } + ax._anchorAxis.domain[domainIndices[0]]; if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { - push[s] += ax.title.font.size; + push[s] += approxTitleDepth(ax) + (ax.title.standoff || 0); } - 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; + if(ax.mirror && ax.anchor !== 'free') { + mirrorPush = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + + mirrorPush[sMirror] = ax.linewidth; + if(ax.mirror && ax.mirror !== true) mirrorPush[sMirror] += outsideTickLen; + + if(ax.mirror === true || ax.mirror === 'ticks') { + mirrorPush[counterLetter] = ax._anchorAxis.domain[domainIndices[1]]; + } else if(ax.mirror === 'all' || ax.mirror === 'allticks') { + mirrorPush[counterLetter] = [ax._counterDomainMin, ax._counterDomainMax][domainIndices[1]]; } } } - Plots.autoMargin(gd, axAutoMarginID(ax), push); - } + if(hasRangeSlider) { + rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax); + } - seq.push(calcBoundingBox, doAutoMargins); + Plots.autoMargin(gd, axAutoMarginID(ax), push); + Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush); + Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush); + }); if(!opts.skipTitle && - !(hasRangeSlider && ax._boundingBox && ax.side === 'bottom') + !(hasRangeSlider && ax.side === 'bottom') ) { seq.push(function() { return drawTitle(gd, ax); }); } @@ -51731,27 +51972,45 @@ function getDividerVals(ax, vals) { 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; +function calcLabelLevelBbox(ax, cls) { + var top, bottom; + var left, right; - // N.B. Drawing.bBox does not take into account rotate transforms + if(ax._selections[cls].size()) { + top = Infinity; + bottom = -Infinity; + left = Infinity; + right = -Infinity; + ax._selections[cls].each(function() { + var thisLabel = selectTickLabel(this); + // Use parent node , to make Drawing.bBox + // retrieve a bbox computed with transform info + // + // To improve perf, it would be nice to use `thisLabel.node()` + // (like in fixLabelOverlaps) instead and use Axes.getPxPosition + // together with the makeLabelFns outputs and `tickangle` + // to compute one bbox per (tick value x tick style) + var bb = Drawing.bBox(thisLabel.node().parentNode); + top = Math.min(top, bb.top); + bottom = Math.max(bottom, bb.bottom); + left = Math.min(left, bb.left); + right = Math.max(right, bb.right); + }); + } else { + top = 0; + bottom = 0; + left = 0; + right = 0; + } - 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]; + return { + top: top, + bottom: bottom, + left: left, + right: right, + height: bottom - top, + width: right - left + }; } /** @@ -51766,6 +52025,7 @@ function getLabelLevelSpan(ax, cls) { * - [1]: sign for bottom/left ticks (i.e. positive SVG direction) * - [2]: sign for ticks corresponding to 'ax.side' * - [3]: sign for ticks mirroring 'ax.side' + * - [4]: sign of arrow starting at axis pointing towards margin */ axes.getTickSigns = function(ax) { var axLetter = ax._id.charAt(0); @@ -51776,6 +52036,10 @@ axes.getTickSigns = function(ax) { if((ax.ticks !== 'inside') === (axLetter === 'x')) { out = out.map(function(v) { return -v; }); } + // independent of `ticks`; do not flip this one + if(ax.side) { + out.push({l: -1, t: -1, r: 1, b: 1}[ax.side.charAt(0)]); + } return out; }; @@ -51804,7 +52068,7 @@ axes.makeTransFn = function(ax) { * - {number} ticklen * - {number} linewidth * @param {number} shift along direction of ticklen - * @param {1 or -1} sng tick sign + * @param {1 or -1} sgn tick sign * @param {number (optional)} len tick length * @return {string} */ @@ -52072,6 +52336,7 @@ axes.drawZeroLine = function(gd, ax, opts) { * - {number} tickangle * - {object (optional)} _selections * - {object} (optional)} _tickAngles + * - {object} (optional)} _prevTickAngles * @param {object} opts * - {array of object} vals (calcTicks output-like) * - {d3 selection} layer @@ -52088,13 +52353,14 @@ axes.drawZeroLine = function(gd, ax, opts) { axes.drawLabels = function(gd, ax, opts) { opts = opts || {}; + var fullLayout = gd._fullLayout; var axId = ax._id; var axLetter = axId.charAt(0); var cls = opts.cls || axId + 'tick'; var vals = opts.vals; var labelFns = opts.labelFns; var tickAngle = opts.secondary ? 0 : ax.tickangle; - var lastAngle = (ax._tickAngles || {})[cls]; + var prevAngle = (ax._prevTickAngles || {})[cls]; var tickLabels = opts.layer.selectAll('g.' + cls) .data(ax.showticklabels ? vals : [], tickDataFn); @@ -52179,17 +52445,17 @@ axes.drawLabels = function(gd, ax, opts) { // 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); + positionLabels(tickLabels, (prevAngle + 1) ? prevAngle : tickAngle); function allLabelsReady() { return labelsReady.length && Promise.all(labelsReady); } + var autoangle = null; + 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 @@ -52256,19 +52522,36 @@ axes.drawLabels = function(gd, ax, opts) { 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]); + var seq = [allLabelsReady]; + + // N.B. during auto-margin redraws, if the axis fixed its label overlaps + // by rotating 90 degrees, do not attempt to re-fix its label overlaps + // as this can lead to infinite redraw loops! + if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) { + autoangle = 90; + seq.push(function() { + positionLabels(tickLabels, prevAngle); + }); + } else { + seq.push(fixLabelOverlaps); + } + + // save current tick angle for future redraws + if(ax._tickAngles) { + seq.push(function() { + ax._tickAngles[cls] = autoangle === null ? + (isNumeric(tickAngle) ? tickAngle : 0) : + autoangle; + }); + } + + var done = Lib.syncOrAsync(seq); if(done && done.then) gd._promises.push(done); return done; }; @@ -52308,14 +52591,28 @@ function drawDividers(gd, ax, opts) { .attr('d', opts.path); } -function getTitleOffset(gd, ax) { +/** + * Get axis position in px, that is the distance for the graph's + * top (left) edge for x (y) axes. + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} side + * if anchored: + * - {object} _anchorAxis + * Otherwise: + * - {number} position + * @return {number} + */ +axes.getPxPosition = function(gd, ax) { var gs = gd._fullLayout._size; var axLetter = ax._id.charAt(0); var side = ax.side; var anchorAxis; if(ax.anchor !== 'free') { - anchorAxis = axisIds.getFromId(gd, ax.anchor); + anchorAxis = ax._anchorAxis; } else if(axLetter === 'x') { anchorAxis = { _offset: gs.t + (1 - (ax.position || 0)) * gs.h, @@ -52333,8 +52630,48 @@ function getTitleOffset(gd, ax) { } else if(side === 'bottom' || side === 'right') { return anchorAxis._offset + anchorAxis._length; } +}; + +/** + * Approximate axis title depth (w/o computing its bounding box) + * + * @param {object} ax (full) axis object + * - {string} title.text + * - {number} title.font.size + * - {number} title.standoff + * @return {number} (in px) + */ +function approxTitleDepth(ax) { + var fontSize = ax.title.font.size; + var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length; + if(ax.title.hasOwnProperty('standoff')) { + return extraLines ? + fontSize * (CAP_SHIFT + (extraLines * LINE_SPACING)) : + fontSize * CAP_SHIFT; + } else { + return extraLines ? + fontSize * (extraLines + 1) * LINE_SPACING : + fontSize; + } } +/** + * Draw axis title, compute default standoff if necessary + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} _name + * - {string} side + * - {number} title.font.size + * - {object} _selections + * + * - {number} _depth + * - {number} title.standoff + * OR + * - {number} linewidth + * - {boolean} showticklabels + */ function drawTitle(gd, ax) { var fullLayout = gd._fullLayout; var axId = ax._id; @@ -52342,36 +52679,37 @@ function drawTitle(gd, ax) { var fontSize = ax.title.font.size; var titleStandoff; - if(ax.type === 'multicategory') { - titleStandoff = ax._labelLength; + + if(ax.title.hasOwnProperty('standoff')) { + titleStandoff = ax._depth + ax.title.standoff + approxTitleDepth(ax); } else { - var offsetBase = 1.5; - titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0); + if(ax.type === 'multicategory') { + titleStandoff = ax._depth; + } else { + var offsetBase = 1.5; + titleStandoff = 10 + fontSize * offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0); + } + + if(axLetter === 'x') { + titleStandoff += ax.side === 'top' ? + fontSize * (ax.showticklabels ? 1 : 0) : + fontSize * (ax.showticklabels ? 1.5 : 0.5); + } else { + titleStandoff += ax.side === 'right' ? + fontSize * (ax.showticklabels ? 1 : 0.5) : + fontSize * (ax.showticklabels ? 0.5 : 0); + } } - var titleOffset = getTitleOffset(gd, ax); - + var pos = axes.getPxPosition(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; + y = (ax.side === 'top') ? pos - titleStandoff : pos + titleStandoff; } 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; - + x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; transform = {rotate: '-90', offset: 0}; } @@ -52390,6 +52728,10 @@ function drawTitle(gd, ax) { avoid.offsetLeft = translation.x; avoid.offsetTop = translation.y; } + + if(ax.title.hasOwnProperty('standoff')) { + avoid.pad = 0; + } } return Titles.draw(gd, axId + 'title', { @@ -52408,7 +52750,6 @@ axes.shouldShowZeroLine = function(gd, ax, counterAxis) { (rng[0] * rng[1] <= 0) && ax.zeroline && (ax.type === 'linear' || ax.type === '-') && - ax._gridVals.length && ( clipEnds(ax, 0) || !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) || @@ -52519,6 +52860,9 @@ axes.allowAutoMargin = function(gd) { var ax = axList[i]; if(ax.automargin) { Plots.allowAutoMargin(gd, axAutoMarginID(ax)); + if(ax.mirror) { + Plots.allowAutoMargin(gd, axMirrorAutoMarginID(ax)); + } } if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) { Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax)); @@ -52527,6 +52871,7 @@ axes.allowAutoMargin = function(gd) { }; function axAutoMarginID(ax) { return ax._id + '.automargin'; } +function axMirrorAutoMarginID(ax) { return axAutoMarginID(ax) + '.mirror'; } function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; } // swap all the presentation attributes of the axes showing these traces @@ -52675,7 +53020,7 @@ function isAngular(ax) { return ax._id === 'angularaxis'; } -},{"../../components/color":51,"../../components/drawing":72,"../../components/titles":139,"../../constants/alignment":146,"../../constants/numerical":149,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/plots":244,"../../registry":256,"./autorange":211,"./axis_autotype":213,"./axis_ids":215,"./clean_ticks":217,"./layout_attributes":224,"./set_convert":230,"d3":16,"fast-isnumeric":18}],213:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../components/titles":138,"../../constants/alignment":145,"../../constants/numerical":149,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"../../registry":258,"./autorange":212,"./axis_autotype":214,"./axis_ids":216,"./clean_ticks":218,"./layout_attributes":225,"./set_convert":231,"d3":16,"fast-isnumeric":18}],214:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -52770,7 +53115,7 @@ function multiCategory(a) { return Lib.isArrayOrTypedArray(a[0]) && Lib.isArrayOrTypedArray(a[1]); } -},{"../../constants/numerical":149,"../../lib":168,"fast-isnumeric":18}],214:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"fast-isnumeric":18}],215:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -52893,7 +53238,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, return containerOut; }; -},{"../../lib":168,"../../registry":256,"./category_order_defaults":216,"./layout_attributes":224,"./line_grid_defaults":226,"./set_convert":230,"./tick_label_defaults":231,"./tick_mark_defaults":232,"./tick_value_defaults":233}],215:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"./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. @@ -53020,7 +53365,7 @@ exports.getAxisGroup = function getAxisGroup(fullLayout, axId) { return axId; }; -},{"../../registry":256,"./constants":218}],216:[function(_dereq_,module,exports){ +},{"../../registry":258,"./constants":219}],217:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -53114,7 +53459,7 @@ module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, } }; -},{}],217:[function(_dereq_,module,exports){ +},{}],218:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -53202,7 +53547,7 @@ exports.tick0 = function(tick0, axType, calendar, dtick) { return isNumeric(tick0) ? Number(tick0) : 0; }; -},{"../../constants/numerical":149,"../../lib":168,"fast-isnumeric":18}],218:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"fast-isnumeric":18}],219:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -53268,6 +53613,7 @@ module.exports = { // Layers to keep trace types in the right order // N.B. each 'unique' plot method must have its own layer traceLayerClasses: [ + 'imagelayer', 'heatmaplayer', 'contourcarpetlayer', 'contourlayer', 'funnellayer', 'waterfalllayer', 'barlayer', @@ -53291,7 +53637,7 @@ module.exports = { } }; -},{"../../lib/regex":183}],219:[function(_dereq_,module,exports){ +},{"../../lib/regex":184}],220:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -53311,7 +53657,11 @@ 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) { +exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, opts) { + var allAxisIds = opts.allAxisIds; + var layoutOut = opts.layoutOut; + var scaleanchorDflt = opts.scaleanchorDflt; + var constrainDflt = opts.constrainDflt; var constraintGroups = layoutOut._axisConstraintGroups; var matchGroups = layoutOut._axisMatchGroups; var axId = containerOut._id; @@ -53322,7 +53672,7 @@ exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, a // coerce the constraint mechanics even if this axis has no scaleanchor // because it may be the anchor of another axis. - var constrain = coerce('constrain'); + var constrain = coerce('constrain', constrainDflt); Lib.coerce(containerIn, containerOut, { constraintoward: { valType: 'enumerated', @@ -53347,14 +53697,17 @@ exports.handleConstraintDefaults = function(containerIn, containerOut, coerce, a // 'matches' wins over 'scaleanchor' (for now) var scaleanchor, scaleOpts; - if(!matches && containerIn.scaleanchor && !(containerOut.fixedrange && constrain !== 'domain')) { + if(!matches && + !(containerOut.fixedrange && constrain !== 'domain') && + (containerIn.scaleanchor || scaleanchorDflt) + ) { scaleOpts = getConstraintOpts(constraintGroups, thisID, allAxisIds, layoutOut, constrain); scaleanchor = Lib.coerce(containerIn, containerOut, { scaleanchor: { valType: 'enumerated', values: scaleOpts.linkableAxes || [] } - }, 'scaleanchor'); + }, 'scaleanchor', scaleanchorDflt); } if(matches) { @@ -53670,7 +54023,7 @@ function updateDomain(ax, factor) { ax.setScale(); } -},{"../../constants/alignment":146,"../../constants/numerical":149,"../../lib":168,"./autorange":211,"./axis_ids":215,"./scale_zoom":228}],220:[function(_dereq_,module,exports){ +},{"../../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. @@ -53724,9 +54077,9 @@ var SHOWZOOMOUTTIP = true; // 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 - // DBLCLICKDELAY ms on the drag bars + // `gd._context.doubleClickDelay` ms on the drag bars // numClicks stores how many mousedowns have been seen - // within DBLCLICKDELAY so we can check for click or doubleclick events + // 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; @@ -54102,14 +54455,7 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) { } 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); @@ -54917,7 +55263,7 @@ module.exports = { attachWheelEventHandler: attachWheelEventHandler }; -},{"../../components/color":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../constants/alignment":146,"../../lib":168,"../../lib/clear_gl_canvases":157,"../../lib/setcursor":187,"../../lib/svg_text_utils":189,"../../plot_api/subroutines":203,"../../registry":256,"../plots":244,"./axes":212,"./axis_ids":215,"./constants":218,"./scale_zoom":228,"./select":229,"d3":16,"has-passive-events":21,"tinycolor2":34}],221:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/dragelement":69,"../../components/drawing":72,"../../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":258,"../plots":245,"./axes":213,"./axis_ids":216,"./constants":219,"./scale_zoom":229,"./select":230,"d3":16,"has-passive-events":21,"tinycolor2":34}],222:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -54979,7 +55325,7 @@ exports.initInteractions = function initInteractions(gd) { // 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) { + if((gd._fullLayout._hoversubplot === subplot) && gd._fullLayout._plots[subplot]) { Fx.hover(gd, evt, subplot); } }; @@ -55085,7 +55431,7 @@ exports.updateFx = function(gd) { setCursor(fullLayout._draggers, cursor); }; -},{"../../components/dragelement":69,"../../components/fx":90,"../../lib/setcursor":187,"./constants":218,"./dragbox":220,"d3":16}],222:[function(_dereq_,module,exports){ +},{"../../components/dragelement":69,"../../components/fx":89,"../../lib/setcursor":188,"./constants":219,"./dragbox":221,"d3":16}],223:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -55160,7 +55506,7 @@ module.exports = function makeIncludeComponents(containerArrayName) { }; }; -},{"../../lib":168,"../../registry":256}],223:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],224:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -55402,7 +55748,8 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback layers.enter().append('g') .attr('class', function(d) { return d.className; }) - .classed('mlayer', true); + .classed('mlayer', true) + .classed('rangeplot', plotinfo.isRangePlot); layers.exit().remove(); @@ -55776,7 +56123,7 @@ exports.toSVG = function(gd) { exports.updateFx = _dereq_('./graph_interact').updateFx; -},{"../../components/drawing":72,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../registry":256,"../get_data":240,"../plots":244,"./attributes":210,"./axis_ids":215,"./constants":218,"./graph_interact":221,"./layout_attributes":224,"./layout_defaults":225,"./transition_axes":234,"d3":16}],224:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../registry":258,"../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":16}],225:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -55793,8 +56140,10 @@ var dash = _dereq_('../../components/drawing/attributes').dash; var extendFlat = _dereq_('../../lib/extend').extendFlat; var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray; -var constants = _dereq_('./constants'); +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: { @@ -55821,6 +56170,13 @@ module.exports = { editType: 'ticks', }), + standoff: { + valType: 'number', + + min: 0, + editType: 'ticks', + + }, editType: 'ticks' }, type: { @@ -56368,7 +56724,7 @@ module.exports = { } }; -},{"../../components/color/attributes":50,"../../components/drawing/attributes":71,"../../lib/extend":162,"../../plot_api/plot_template":202,"../font_attributes":238,"./constants":218}],225:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../components/drawing/attributes":71,"../../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. @@ -56410,8 +56766,9 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { var yaMayHide = {}; var xaMustDisplay = {}; var yaMustDisplay = {}; - var yaMustForward = {}; - var yaMayBackward = {}; + var yaMustNotReverse = {}; + var yaMayReverse = {}; + var axHasImage = {}; var outerTicks = {}; var noGrids = {}; var i, j; @@ -56445,14 +56802,17 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { if(trace.type === 'funnel') { if(trace.orientation === 'h') { if(xaName) xaMayHide[xaName] = true; - if(yaName) yaMayBackward[yaName] = true; + if(yaName) yaMayReverse[yaName] = true; } else { if(yaName) yaMayHide[yaName] = true; } + } else if(trace.type === 'image') { + if(yaName) axHasImage[yaName] = true; + if(xaName) axHasImage[xaName] = true; } else { if(yaName) { yaMustDisplay[yaName] = true; - yaMustForward[yaName] = true; + yaMustNotReverse[yaName] = true; } if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) { @@ -56563,7 +56923,11 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { (axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]); var reverseDflt = - (axLetter === 'y' && !yaMustForward[axName] && yaMayBackward[axName]); + (axLetter === 'y' && + ( + (!yaMustNotReverse[axName] && yaMayReverse[axName]) || + axHasImage[axName] + )); var defaultOptions = { letter: axLetter, @@ -56606,6 +56970,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { grid: layoutOut.grid }); + coerce('title.standoff'); + axLayoutOut._input = axLayoutIn; } @@ -56663,7 +57029,22 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { axLayoutIn = layoutIn[axName]; axLayoutOut = layoutOut[axName]; - handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, allAxisIds, layoutOut); + var scaleanchorDflt; + if(axLetter === 'y' && !axLayoutIn.hasOwnProperty('scaleanchor') && axHasImage[axName]) { + scaleanchorDflt = axLayoutOut.anchor; + } else {scaleanchorDflt = undefined;} + + var constrainDflt; + if(!axLayoutIn.hasOwnProperty('constrain') && axHasImage[axName]) { + constrainDflt = 'domain'; + } else {constrainDflt = undefined;} + + handleConstraintDefaults(axLayoutIn, axLayoutOut, coerce, { + allAxisIds: allAxisIds, + layoutOut: layoutOut, + scaleanchorDflt: scaleanchorDflt, + constrainDflt: constrainDflt + }); } for(i = 0; i < matchGroups.length; i++) { @@ -56723,7 +57104,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { } }; -},{"../../components/color":51,"../../lib":168,"../../plot_api/plot_template":202,"../../registry":256,"../layout_attributes":242,"./axis_defaults":214,"./axis_ids":215,"./constraints":219,"./layout_attributes":224,"./position_defaults":227,"./type_defaults":235}],226:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../plot_api/plot_template":203,"../../registry":258,"../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. @@ -56788,7 +57169,7 @@ module.exports = function handleLineGridDefaults(containerIn, containerOut, coer } }; -},{"../../components/color/attributes":50,"../../lib":168,"tinycolor2":34}],227:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../lib":169,"tinycolor2":34}],228:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -56876,7 +57257,7 @@ module.exports = function handlePositionDefaults(containerIn, containerOut, coer return containerOut; }; -},{"../../lib":168,"fast-isnumeric":18}],228:[function(_dereq_,module,exports){ +},{"../../lib":169,"fast-isnumeric":18}],229:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -56904,7 +57285,7 @@ module.exports = function scaleZoom(ax, factor, centerFraction) { ]; }; -},{"../../constants/alignment":146}],229:[function(_dereq_,module,exports){ +},{"../../constants/alignment":145}],230:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -57641,7 +58022,10 @@ function updateSelectedState(gd, searchTraces, eventData) { var _module = searchInfo._module; var fn = _module.styleOnSelect || _module.style; - if(fn) fn(gd, cd); + if(fn) { + fn(gd, cd, cd[0].node3); + if(cd[0].nodeRangePlot3) fn(gd, cd, cd[0].nodeRangePlot3); + } } if(hasRegl) { @@ -57706,7 +58090,7 @@ module.exports = { selectOnClick: selectOnClick }; -},{"../../components/color":51,"../../components/fx":90,"../../components/fx/helpers":86,"../../lib":168,"../../lib/clear_gl_canvases":157,"../../lib/polygon":180,"../../lib/throttle":190,"../../plot_api/subroutines":203,"../../registry":256,"./axis_ids":215,"./constants":218,"polybooljs":25}],230:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../components/fx/helpers":86,"../../lib":169,"../../lib/clear_gl_canvases":158,"../../lib/polygon":181,"../../lib/throttle":191,"../../plot_api/subroutines":204,"../../registry":258,"./axis_ids":216,"./constants":219,"polybooljs":25}],231:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -58107,6 +58491,10 @@ module.exports = function setConvert(ax, fullLayout) { // make sure we don't later mutate the defaults dflt = dflt.slice(); + if(ax.rangemode === 'tozero' || ax.rangemode === 'nonnegative') { + dflt[0] = 0; + } + if(!range || range.length !== 2) { Lib.nestedProperty(ax, rangeAttr).set(dflt); return; @@ -58372,7 +58760,7 @@ module.exports = function setConvert(ax, fullLayout) { delete ax._forceTick0; }; -},{"../../constants/numerical":149,"../../lib":168,"./axis_ids":215,"./constants":218,"d3":16,"fast-isnumeric":18}],231:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"./axis_ids":216,"./constants":219,"d3":16,"fast-isnumeric":18}],232:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -58491,7 +58879,7 @@ function tickformatstopDefaults(valueIn, valueOut) { } } -},{"../../lib":168,"../array_container_defaults":208,"./layout_attributes":224}],232:[function(_dereq_,module,exports){ +},{"../../lib":169,"../array_container_defaults":209,"./layout_attributes":225}],233:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -58524,7 +58912,7 @@ module.exports = function handleTickDefaults(containerIn, containerOut, coerce, } }; -},{"../../lib":168,"./layout_attributes":224}],233:[function(_dereq_,module,exports){ +},{"../../lib":169,"./layout_attributes":225}],234:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -58566,7 +58954,7 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe } }; -},{"./clean_ticks":217}],234:[function(_dereq_,module,exports){ +},{"./clean_ticks":218}],235:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -58580,6 +58968,7 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe var d3 = _dereq_('d3'); var Registry = _dereq_('../../registry'); +var Lib = _dereq_('../../lib'); var Drawing = _dereq_('../../components/drawing'); var Axes = _dereq_('./axes'); @@ -58638,37 +59027,35 @@ module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnComple 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 editX = !!edit.xr1; + var editY = !!edit.yr1; var viewBox = []; if(editX) { + var xr0 = Lib.simpleMap(edit.xr0, xa.r2l); + var xr1 = Lib.simpleMap(edit.xr1, xa.r2l); var dx0 = xr0[1] - xr0[0]; var dx1 = xr1[1] - xr1[0]; viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen; viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0); - xa.range[0] = xr0[0] * (1 - progress) + progress * xr1[0]; - xa.range[1] = xr0[1] * (1 - progress) + progress * xr1[1]; + xa.range[0] = xa.l2r(xr0[0] * (1 - progress) + progress * xr1[0]); + xa.range[1] = xa.l2r(xr0[1] * (1 - progress) + progress * xr1[1]); } else { viewBox[0] = 0; viewBox[2] = xlen; } if(editY) { + var yr0 = Lib.simpleMap(edit.yr0, ya.r2l); + var yr1 = Lib.simpleMap(edit.yr1, ya.r2l); var dy0 = yr0[1] - yr0[0]; var dy1 = yr1[1] - yr1[0]; viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen; viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0); - ya.range[0] = yr0[0] * (1 - progress) + progress * yr1[0]; - ya.range[1] = yr0[1] * (1 - progress) + progress * yr1[1]; + ya.range[0] = xa.l2r(yr0[0] * (1 - progress) + progress * yr1[0]); + ya.range[1] = ya.l2r(yr0[1] * (1 - progress) + progress * yr1[1]); } else { viewBox[1] = 0; viewBox[3] = ylen; @@ -58713,8 +59100,10 @@ module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnComple 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(); + var xa = edit.plotinfo.xaxis; + var ya = edit.plotinfo.yaxis; + if(edit.xr1) aobj[xa._name + '.range'] = edit.xr1.slice(); + if(edit.yr1) aobj[ya._name + '.range'] = edit.yr1.slice(); } // Signal that this transition has completed: @@ -58732,8 +59121,10 @@ module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnComple 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(); + var xa = edit.plotinfo.xaxis; + var ya = edit.plotinfo.yaxis; + if(edit.xr0) aobj[xa._name + '.range'] = edit.xr0.slice(); + if(edit.yr0) aobj[ya._name + '.range'] = edit.yr0.slice(); } return Registry.call('relayout', gd, aobj).then(function() { @@ -58776,7 +59167,7 @@ module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnComple return Promise.resolve(); }; -},{"../../components/drawing":72,"../../registry":256,"./axes":212,"d3":16}],235:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"../../registry":258,"./axes":213,"d3":16}],236:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -58905,7 +59296,7 @@ function isBoxWithoutPositionCoords(trace, axLetter) { ); } -},{"../../registry":256,"./axis_autotype":213}],236:[function(_dereq_,module,exports){ +},{"../../registry":258,"./axis_autotype":214}],237:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59268,9 +59659,13 @@ function computeDataBindings(gd, args) { traces = null; } - crawl(aobj, function(path, attrName, attr) { + crawl(aobj, function(path, attrName, _attr) { var thisTraces; - if(Array.isArray(attr)) { + 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); @@ -59280,7 +59675,8 @@ function computeDataBindings(gd, args) { thisTraces[j] = traces ? traces[j] : j; } } else { - thisTraces = traces ? traces.slice(0) : null; + attr = _attr; + thisTraces = traces ? traces.slice() : null; } // Convert [7] to just 7 when traces is null: @@ -59327,7 +59723,7 @@ function crawl(attrs, callback, path, depth) { }); } -},{"../lib":168,"../registry":256}],237:[function(_dereq_,module,exports){ +},{"../lib":169,"../registry":258}],238:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59431,11 +59827,15 @@ exports.defaults = function(containerOut, layout, coerce, dfltDomains) { } } - coerce('domain.x', dfltX); - coerce('domain.y', dfltY); + var x = coerce('domain.x', dfltX); + var y = coerce('domain.y', dfltY); + + // don't accept bad input data + if(!(x[0] < x[1])) containerOut.domain.x = dfltX.slice(); + if(!(y[0] < y[1])) containerOut.domain.y = dfltY.slice(); }; -},{"../lib/extend":162}],238:[function(_dereq_,module,exports){ +},{"../lib/extend":164}],239:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59500,7 +59900,7 @@ module.exports = function(opts) { return attrs; }; -},{}],239:[function(_dereq_,module,exports){ +},{}],240:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59546,7 +59946,7 @@ module.exports = { } }; -},{}],240:[function(_dereq_,module,exports){ +},{}],241:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59675,7 +60075,7 @@ exports.getSubplotData = function getSubplotData(data, type, subplotId) { return subplotData; }; -},{"../registry":256,"./cartesian/constants":218}],241:[function(_dereq_,module,exports){ +},{"../registry":258,"./cartesian/constants":219}],242:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59709,7 +60109,7 @@ function project(camera, v) { module.exports = project; -},{}],242:[function(_dereq_,module,exports){ +},{}],243:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -59872,7 +60272,8 @@ module.exports = { valType: 'boolean', dflt: true, - editType: 'plot' + editType: 'plot', + }, editType: 'plot' }, @@ -59998,7 +60399,6 @@ module.exports = { editType: 'none' }), - _deprecated: { title: { valType: 'string', @@ -60013,7 +60413,7 @@ module.exports = { } }; -},{"../components/color/attributes":50,"../lib/extend":162,"./animation_attributes":207,"./font_attributes":238,"./pad_attributes":243}],243:[function(_dereq_,module,exports){ +},{"../components/color/attributes":50,"../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. @@ -60068,7 +60468,7 @@ module.exports = function(opts) { }; }; -},{}],244:[function(_dereq_,module,exports){ +},{}],245:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -60094,6 +60494,8 @@ var axisIDs = _dereq_('./cartesian/axis_ids'); var animationAttrs = _dereq_('./animation_attributes'); var frameAttrs = _dereq_('./frame_attributes'); +var getModuleCalcData = _dereq_('../plots/get_data').getModuleCalcData; + var relinkPrivateKeys = Lib.relinkPrivateKeys; var _ = Lib._; @@ -60145,21 +60547,19 @@ plots.redrawText = function(gd) { plots.resize = function(gd) { gd = Lib.getGraphDiv(gd); - return new Promise(function(resolve, reject) { - function isHidden(gd) { - var display = window.getComputedStyle(gd).display; - return !display || display === 'none'; - } - - if(!gd || isHidden(gd)) { + var resolveLastResize; + var p = new Promise(function(resolve, reject) { + if(!gd || Lib.isHidden(gd)) { reject(new Error('Resize must be passed a displayed plot div element.')); } if(gd._redrawTimer) clearTimeout(gd._redrawTimer); + if(gd._resolveResize) resolveLastResize = gd._resolveResize; + gd._resolveResize = resolve; gd._redrawTimer = setTimeout(function() { // return if there is nothing to resize or is hidden - if(!gd.layout || (gd.layout.width && gd.layout.height) || isHidden(gd)) { + if(!gd.layout || (gd.layout.width && gd.layout.height) || Lib.isHidden(gd)) { resolve(gd); return; } @@ -60175,10 +60575,17 @@ plots.resize = function(gd) { Registry.call('relayout', gd, {autosize: true}).then(function() { gd.changed = oldchanged; - resolve(gd); + // Only resolve if a new call hasn't been made! + if(gd._resolveResize === resolve) { + delete gd._resolveResize; + resolve(gd); + } }); }, 100); }); + + if(resolveLastResize) resolveLastResize(p); + return p; }; @@ -60996,6 +61403,26 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa ax._counterAxes.sort(axisIDs.idSort); ax._subplotsWith.sort(Lib.subplotSort); ax._mainSubplot = findMainSubplot(ax, newFullLayout); + + // find "full" domain span of counter axes, + // this loop can be costly, so only compute it when required + if(ax._counterAxes.length && ( + (ax.spikemode && ax.spikemode.indexOf('across') !== -1) || + (ax.automargin && ax.mirror && ax.anchor !== 'free') || + Registry.getComponentMethod('rangeslider', 'isVisible')(ax) + )) { + var min = 1; + var max = 0; + for(j = 0; j < ax._counterAxes.length; j++) { + var ax2 = axisIDs.getFromId(mockGd, ax._counterAxes[j]); + min = Math.min(min, ax2.domain[0]); + max = Math.max(max, ax2.domain[1]); + } + if(min < max) { + ax._counterDomainMin = min; + ax._counterDomainMax = max; + } + } } }; @@ -61721,7 +62148,6 @@ plots.purge = function(gd) { fullLayout._glcontainer.remove(); fullLayout._glcanvas = null; } - if(fullLayout._geocontainer !== undefined) fullLayout._geocontainer.remove(); // remove modebar if(fullLayout._modeBar) fullLayout._modeBar.destroy(); @@ -61896,8 +62322,14 @@ plots.autoMargin = function(gd, id, o) { // 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; + if(o.l + o.r > fullLayout.width * 0.5) { + Lib.log('Margin push', id, 'is too big in x, dropping'); + o.l = o.r = 0; + } + if(o.b + o.t > fullLayout.height * 0.5) { + Lib.log('Margin push', id, 'is too big in y, dropping'); + o.b = o.t = 0; + } var xl = o.xl !== undefined ? o.xl : o.x; var xr = o.xr !== undefined ? o.xr : o.x; @@ -61914,7 +62346,7 @@ plots.autoMargin = function(gd, id, o) { } if(!fullLayout._replotting) { - plots.doAutoMargin(gd); + return plots.doAutoMargin(gd); } } }; @@ -62011,7 +62443,19 @@ plots.doAutoMargin = function(gd) { } else { fullLayout._redrawFromAutoMarginCount = 1; } - return Registry.call('plot', gd); + + // Always allow at least one redraw and give each margin-push + // call 3 loops to converge. Of course, for most cases this way too many, + // but let's keep things on the safe side until we fix our + // auto-margin pipeline problems: + // https://github.com/plotly/plotly.js/issues/2704 + var maxNumberOfRedraws = 3 * (1 + Object.keys(pushMarginIds).length); + + if(fullLayout._redrawFromAutoMarginCount < maxNumberOfRedraws) { + return Registry.call('plot', gd); + } else { + Lib.warn('Too many auto-margin redraws.'); + } } }; @@ -62483,27 +62927,30 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) var xr0 = xa.range.slice(); var yr0 = ya.range.slice(); - var xr1; + var xr1 = null; + var yr1 = null; + var editX = null; + var editY = null; + if(Array.isArray(newLayout[xa._name + '.range'])) { xr1 = newLayout[xa._name + '.range'].slice(); } else if(Array.isArray((newLayout[xa._name] || {}).range)) { xr1 = newLayout[xa._name].range.slice(); } - - 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])) { + if(xr0 && xr1 && + (xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) + ) { editX = {xr0: xr0, xr1: xr1}; } - - var editY; - if(yr0 && yr1 && (yr0[0] !== yr1[0] || yr0[1] !== yr1[1])) { + if(yr0 && yr1 && + (ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) + ) { editY = {yr0: yr0, yr1: yr1}; } @@ -62594,13 +63041,13 @@ plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLay xa.setScale(); ya.setScale(); - var editX; - if(xr0[0] !== xr1[0] || xr0[1] !== xr1[1]) { + var editX = null; + var editY = null; + + if(xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) { editX = {xr0: xr0, xr1: xr1}; } - - var editY; - if(yr0[0] !== yr1[0] || yr0[1] !== yr1[1]) { + if(ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) { editY = {yr0: yr0, yr1: yr1}; } @@ -62645,14 +63092,14 @@ plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLay axisTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0}); transitionedTraces = allTraceIndices; traceTransitionOpts = transitionOpts; - transitionTraces(); setTimeout(transitionAxes, transitionOpts.duration); + transitionTraces(); } else { axisTransitionOpts = transitionOpts; transitionedTraces = null; traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0}); + setTimeout(transitionTraces, axisTransitionOpts.duration); transitionAxes(); - transitionTraces(); } } else if(axEdits.length) { axisTransitionOpts = transitionOpts; @@ -62813,7 +63260,7 @@ plots.doCalcdata = function(gd, traces) { // 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(0); + var oldCalcdata = (gd.calcdata || []).slice(); gd.calcdata = calcdata; // extra helper variables @@ -62829,9 +63276,10 @@ plots.doCalcdata = function(gd, traces) { gd._hmpixcount = 0; gd._hmlumcount = 0; - // for sharing colors across pies / sunbursts / funnelarea (and for legend) + // for sharing colors across pies / sunbursts / treemap / funnelarea (and for legend) fullLayout._piecolormap = {}; fullLayout._sunburstcolormap = {}; + fullLayout._treemapcolormap = {}; fullLayout._funnelareacolormap = {}; // If traces were specified and this trace was not included, @@ -62861,6 +63309,15 @@ plots.doCalcdata = function(gd, traces) { ); } + // clear relinked cmin/cmax values in shared axes to start aggregation from scratch + for(var k in fullLayout._colorAxes) { + var cOpts = fullLayout[k]; + if(cOpts.cauto !== false) { + delete cOpts.cmin; + delete cOpts.cmax; + } + } + var hasCalcTransform = false; function transformCalci(i) { @@ -62958,6 +63415,9 @@ plots.doCalcdata = function(gd, traces) { // 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); @@ -63259,7 +63719,22 @@ plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subpl subplot.traceHash = traceHash; }; -},{"../components/color":51,"../constants/numerical":149,"../lib":168,"../plot_api/plot_schema":201,"../plot_api/plot_template":202,"../registry":256,"./animation_attributes":207,"./attributes":209,"./cartesian/axis_ids":215,"./command":236,"./font_attributes":238,"./frame_attributes":239,"./layout_attributes":242,"d3":16,"fast-isnumeric":18}],245:[function(_dereq_,module,exports){ +plots.plotBasePlot = function(desiredType, gd, traces, transitionOpts, makeOnCompleteCallback) { + var _module = Registry.getModule(desiredType); + var cdmodule = getModuleCalcData(gd.calcdata, _module)[0]; + _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback); +}; + +plots.cleanBasePlot = function(desiredType, newFullData, newFullLayout, oldFullData, oldFullLayout) { + var had = (oldFullLayout._has && oldFullLayout._has(desiredType)); + var has = (newFullLayout._has && newFullLayout._has(desiredType)); + + if(had && !has) { + oldFullLayout['_' + desiredType + 'layer'].selectAll('g.trace').remove(); + } +}; + +},{"../components/color":51,"../constants/numerical":149,"../lib":169,"../plot_api/plot_schema":202,"../plot_api/plot_template":203,"../plots/get_data":241,"../registry":258,"./animation_attributes":208,"./attributes":210,"./cartesian/axis_ids":216,"./command":237,"./font_attributes":239,"./frame_attributes":240,"./layout_attributes":243,"d3":16,"fast-isnumeric":18}],246:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -63303,7 +63778,7 @@ module.exports = { } }; -},{"../../../lib/extend":162,"../../../traces/scatter/attributes":365}],246:[function(_dereq_,module,exports){ +},{"../../../lib/extend":164,"../../../traces/scatter/attributes":377}],247:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -63425,7 +63900,7 @@ module.exports = overrideAll({ } }, 'plot', 'nested'); -},{"../../../lib/extend":162,"../../../plot_api/edit_types":195,"../../cartesian/layout_attributes":224}],247:[function(_dereq_,module,exports){ +},{"../../../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. @@ -63440,7 +63915,7 @@ var Polar = module.exports = _dereq_('./micropolar'); Polar.manager = _dereq_('./micropolar_manager'); -},{"./micropolar":248,"./micropolar_manager":249}],248:[function(_dereq_,module,exports){ +},{"./micropolar":249,"./micropolar_manager":250}],249:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -64860,7 +65335,7 @@ var µ = module.exports = { version: '0.2.2' }; return exports; }; -},{"../../../constants/alignment":146,"../../../lib":168,"d3":16}],249:[function(_dereq_,module,exports){ +},{"../../../constants/alignment":145,"../../../lib":169,"d3":16}],250:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -64946,7 +65421,7 @@ manager.fillLayout = function(_gd) { _gd._fullLayout = extendDeepAll(dflts, _gd.layout); }; -},{"../../../components/color":51,"../../../lib":168,"./micropolar":248,"./undo_manager":250,"d3":16}],250:[function(_dereq_,module,exports){ +},{"../../../components/color":51,"../../../lib":169,"./micropolar":249,"./undo_manager":251,"d3":16}],251:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -65012,7 +65487,7 @@ module.exports = function UndoManager() { }; }; -},{}],251:[function(_dereq_,module,exports){ +},{}],252:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -65097,7 +65572,90 @@ module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, o } }; -},{"../lib":168,"../plot_api/plot_template":202,"./domain":237}],252:[function(_dereq_,module,exports){ +},{"../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 FORMAT_LINK = _dereq_('../constants/docs').FORMAT_LINK; +var DATE_FORMAT_LINK = _dereq_('../constants/docs').DATE_FORMAT_LINK; + +var templateFormatStringDescription = [ + 'Variables are inserted using %{variable}, for example "y: %{y}".', + 'Numbers are formatted using d3-format\'s syntax %{variable:d3-format}, for example "Price: %{y:$.2f}".', + FORMAT_LINK, + 'for details on the formatting syntax.', + 'Dates are formatted using d3-time-format\'s syntax %{variable|d3-time-format}, for example "Day: %{2019-01-01|%A}".', + DATE_FORMAT_LINK, + 'for details on the date formatting syntax.' +].join(' '); + +function describeVariables(extra) { + var descPart = extra.description ? ' ' + extra.description : ''; + var keys = extra.keys || []; + if(keys.length > 0) { + var quotedKeys = []; + for(var i = 0; i < keys.length; i++) { + quotedKeys[i] = '`' + keys[i] + '`'; + } + descPart = descPart + 'Finally, the template string has access to '; + if(keys.length === 1) { + descPart = 'variable ' + quotedKeys[0]; + } else { + descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.'; + } + } + return descPart; +} + +exports.hovertemplateAttrs = function(opts, extra) { + opts = opts || {}; + extra = extra || {}; + + var descPart = describeVariables(extra); + + var hovertemplate = { + valType: 'string', + + dflt: '', + editType: opts.editType || 'none', + + }; + + if(opts.arrayOk !== false) { + hovertemplate.arrayOk = true; + } + + return hovertemplate; +}; + +exports.texttemplateAttrs = function(opts, extra) { + opts = opts || {}; + extra = extra || {}; + + var descPart = describeVariables(extra); + + var texttemplate = { + valType: 'string', + + dflt: '', + editType: opts.editType || 'calc', + + }; + + if(opts.arrayOk !== false) { + texttemplate.arrayOk = true; + } + return texttemplate; +}; + +},{"../constants/docs":146}],254:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -65181,7 +65739,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) } }; -},{"../../lib":168,"../../plots/get_data":240,"./layout_attributes":253,"./layout_defaults":254,"./ternary":255}],253:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/get_data":241,"./layout_attributes":255,"./layout_defaults":256,"./ternary":257}],255:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -65200,7 +65758,11 @@ var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; var extendFlat = _dereq_('../../lib/extend').extendFlat; var ternaryAxesAttrs = { - title: axesAttrs.title, + title: { + text: axesAttrs.title.text, + font: axesAttrs.title.font + // TODO does standoff here make sense? + }, color: axesAttrs.color, // ticks tickmode: axesAttrs.tickmode, @@ -65284,7 +65846,7 @@ attrs.aaxis.uirevision = attrs.baxis.uirevision = attrs.caxis.uirevision = { }; -},{"../../components/color/attributes":50,"../../lib/extend":162,"../../plot_api/edit_types":195,"../cartesian/layout_attributes":224,"../domain":237}],254:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plot_api/edit_types":196,"../cartesian/layout_attributes":225,"../domain":238}],256:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -65416,7 +65978,7 @@ function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut coerce('layer'); } -},{"../../components/color":51,"../../lib":168,"../../plot_api/plot_template":202,"../cartesian/line_grid_defaults":226,"../cartesian/tick_label_defaults":231,"../cartesian/tick_mark_defaults":232,"../cartesian/tick_value_defaults":233,"../subplot_defaults":251,"./layout_attributes":253}],255:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../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":255}],257:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66173,7 +66735,7 @@ function removeZoombox(gd) { .remove(); } -},{"../../components/color":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":90,"../../components/titles":139,"../../lib":168,"../../lib/extend":162,"../../registry":256,"../cartesian/axes":212,"../cartesian/constants":218,"../cartesian/select":229,"../cartesian/set_convert":230,"../plots":244,"d3":16,"tinycolor2":34}],256:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/dragelement":69,"../../components/drawing":72,"../../components/fx":89,"../../components/titles":138,"../../lib":169,"../../lib/extend":164,"../../registry":258,"../cartesian/axes":213,"../cartesian/constants":219,"../cartesian/select":230,"../cartesian/set_convert":231,"../plots":245,"d3":16,"tinycolor2":34}],258:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66188,6 +66750,7 @@ 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'); @@ -66443,6 +67006,26 @@ function registerTraceModule(_module) { 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) { @@ -66618,7 +67201,7 @@ function getTraceType(traceType) { return traceType; } -},{"./lib/extend":162,"./lib/is_plain_object":169,"./lib/loggers":172,"./lib/noop":177,"./lib/push_unique":181,"./plots/attributes":209,"./plots/layout_attributes":242}],257:[function(_dereq_,module,exports){ +},{"./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}],259:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66791,7 +67374,7 @@ module.exports = function clonePlot(graphObj, options) { return plotTile; }; -},{"../lib":168,"../registry":256}],258:[function(_dereq_,module,exports){ +},{"../lib":169,"../registry":258}],260:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66802,27 +67385,30 @@ module.exports = function clonePlot(graphObj, options) { 'use strict'; -var toImage = _dereq_('../plot_api/to_image'); var Lib = _dereq_('../lib'); -var fileSaver = _dereq_('./filesaver'); -/** Plotly.downloadImage +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 ../plot_api/to_image) + * @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); - // check for undefined opts opts = opts || {}; - // default to png opts.format = opts.format || 'png'; + opts.imageDataOnly = true; return new Promise(function(resolve, reject) { if(_gd && _gd._snapshotInProgress) { @@ -66835,7 +67421,7 @@ function downloadImage(gd, opts) { // does not allow toDataURL // svg format will work though if(Lib.isIE() && opts.format !== 'svg') { - reject(new Error('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.')); + reject(new Error(helpers.MSG_IE_BAD_FORMAT)); } if(_gd) _gd._snapshotInProgress = true; @@ -66846,7 +67432,7 @@ function downloadImage(gd, opts) { promise.then(function(result) { if(_gd) _gd._snapshotInProgress = false; - return fileSaver(result, filename); + return fileSaver(result, filename, opts.format); }).then(function(name) { resolve(name); }).catch(function(err) { @@ -66858,7 +67444,7 @@ function downloadImage(gd, opts) { module.exports = downloadImage; -},{"../lib":168,"../plot_api/to_image":205,"./filesaver":259}],259:[function(_dereq_,module,exports){ +},{"../lib":169,"../plot_api/to_image":206,"./filesaver":261,"./helpers":262}],261:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66867,6 +67453,11 @@ module.exports = downloadImage; * 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 @@ -66879,58 +67470,61 @@ module.exports = downloadImage; * License: MIT * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md */ - -'use strict'; - -var fileSaver = function(url, name) { +function fileSaver(url, name, format) { var saveLink = document.createElement('a'); var canUseSaveLink = 'download' in saveLink; - var isSafari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); + var promise = new Promise(function(resolve, reject) { - // IE <10 is explicitly unsupported - if(typeof navigator !== 'undefined' && /MSIE [1-9]\./.test(navigator.userAgent)) { + var blob; + var objectUrl; + + if(Lib.isIE9orBelow()) { reject(new Error('IE < 10 unsupported')); } - // First try a.download, then web filesystem, then object URLs - if(isSafari) { - // Safari doesn't allow downloading of blob urls - document.location.href = 'data:application/octet-stream' + url.slice(url.search(/[,;]/)); - resolve(name); - } - - if(!name) { - name = 'download'; - } - - if(canUseSaveLink) { - saveLink.href = url; - saveLink.download = name; - document.body.appendChild(saveLink); - saveLink.click(); - document.body.removeChild(saveLink); - resolve(name); + // 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(typeof navigator !== 'undefined' && navigator.msSaveBlob) { - // At this point we are only dealing with a SVG encoded as + if(Lib.isIE()) { + // At this point we are only dealing with a decoded SVG as // a data URL (since IE only supports SVG) - var encoded = url.split(/^data:image\/svg\+xml,/)[1]; - var svg = decodeURIComponent(encoded); - navigator.msSaveBlob(new Blob([svg]), name); - resolve(name); + 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; -},{}],260:[function(_dereq_,module,exports){ +},{"../lib":169,"./helpers":262}],262:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66965,7 +67559,49 @@ exports.getRedrawFunc = function(gd) { }; }; -},{"../registry":256}],261:[function(_dereq_,module,exports){ +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":258}],263:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -66991,7 +67627,7 @@ var Snapshot = { module.exports = Snapshot; -},{"./cloneplot":257,"./download":258,"./helpers":260,"./svgtoimg":262,"./toimage":263,"./tosvg":264}],262:[function(_dereq_,module,exports){ +},{"./cloneplot":259,"./download":260,"./helpers":262,"./svgtoimg":264,"./toimage":265,"./tosvg":266}],264:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67005,6 +67641,8 @@ module.exports = Snapshot; var Lib = _dereq_('../lib'); var EventEmitter = _dereq_('events').EventEmitter; +var helpers = _dereq_('./helpers'); + function svgToImg(opts) { var ev = opts.emitter || new EventEmitter(); @@ -67015,7 +67653,7 @@ function svgToImg(opts) { // IE only support svg if(Lib.isIE() && format !== 'svg') { - var ieSvgError = new Error('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.'); + var ieSvgError = new Error(helpers.MSG_IE_BAD_FORMAT); reject(ieSvgError); // eventually remove the ev // in favor of promises @@ -67035,11 +67673,14 @@ function svgToImg(opts) { var ctx = canvas.getContext('2d'); var img = new Image(); + var svgBlob, url; - // for Safari support, eliminate createObjectURL - // this decision could cause problems if content - // is not restricted to svg - var url = 'data:image/svg+xml,' + encodeURIComponent(svg); + 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; @@ -67047,6 +67688,9 @@ function svgToImg(opts) { 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') { @@ -67084,6 +67728,9 @@ function svgToImg(opts) { }; img.onerror = function(err) { + svgBlob = null; + helpers.revokeObjectURL(url); + reject(err); // eventually remove the ev // in favor of promises @@ -67107,7 +67754,7 @@ function svgToImg(opts) { module.exports = svgToImg; -},{"../lib":168,"events":15}],263:[function(_dereq_,module,exports){ +},{"../lib":169,"./helpers":262,"events":15}],265:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67184,7 +67831,7 @@ function toImage(gd, opts) { module.exports = toImage; -},{"../lib":168,"../registry":256,"./cloneplot":257,"./helpers":260,"./svgtoimg":262,"./tosvg":264,"events":15}],264:[function(_dereq_,module,exports){ +},{"../lib":169,"../registry":258,"./cloneplot":259,"./helpers":262,"./svgtoimg":264,"./tosvg":266,"events":15}],266:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67365,7 +68012,7 @@ module.exports = function toSVG(gd, format, scale) { return s; }; -},{"../components/color":51,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":168,"d3":16}],265:[function(_dereq_,module,exports){ +},{"../components/color":51,"../components/drawing":72,"../constants/xmlns_namespaces":150,"../lib":169,"d3":16}],267:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67376,29 +68023,29 @@ module.exports = function toSVG(gd, format, scale) { 'use strict'; -var mergeArray = _dereq_('../../lib').mergeArray; +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; - mergeArray(trace.text, cd, 'tx'); - mergeArray(trace.hovertext, cd, 'htx'); + Lib.mergeArray(trace.text, cd, 'tx'); + Lib.mergeArray(trace.hovertext, cd, 'htx'); var marker = trace.marker; if(marker) { - mergeArray(marker.opacity, cd, 'mo'); - mergeArray(marker.color, cd, 'mc'); + Lib.mergeArray(marker.opacity, cd, 'mo', true); + Lib.mergeArray(marker.color, cd, 'mc'); var markerLine = marker.line; if(markerLine) { - mergeArray(markerLine.color, cd, 'mlc'); - mergeArray(markerLine.width, cd, 'mlw'); + Lib.mergeArray(markerLine.color, cd, 'mlc'); + Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw'); } } }; -},{"../../lib":168}],266:[function(_dereq_,module,exports){ +},{"../../lib":169}],268:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67410,10 +68057,11 @@ module.exports = function arraysToCalcdata(cd, trace) { 'use strict'; var scatterAttrs = _dereq_('../scatter/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); var fontAttrs = _dereq_('../../plots/font_attributes'); -var constants = _dereq_('./constants.js'); +var constants = _dereq_('./constants'); var extendFlat = _dereq_('../../lib/extend').extendFlat; @@ -67460,6 +68108,9 @@ module.exports = { dy: scatterAttrs.dy, text: scatterAttrs.text, + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: constants.eventDataKeys + }), hovertext: scatterAttrs.hovertext, hovertemplate: hovertemplateAttrs({}, { keys: constants.eventDataKeys @@ -67603,7 +68254,7 @@ module.exports = { } }; -},{"../../components/colorscale/attributes":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/font_attributes":238,"../scatter/attributes":365,"./constants.js":268}],267:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"../scatter/attributes":377,"./constants":270}],269:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67668,7 +68319,7 @@ module.exports = function calc(gd, trace) { return cd; }; -},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"../../plots/cartesian/axes":212,"../scatter/calc_selection":367,"./arrays_to_calcdata":265}],268:[function(_dereq_,module,exports){ +},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"../../plots/cartesian/axes":213,"../scatter/calc_selection":379,"./arrays_to_calcdata":267}],270:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -67681,10 +68332,16 @@ module.exports = function calc(gd, trace) { 'use strict'; module.exports = { - eventDataKeys: [] + // padding in pixels around text + TEXTPAD: 3, + // 'value' and 'label' are not really necessary for bar traces, + // but they were made available to `texttemplate` (maybe by accident) + // via tokens `%{value}` and `%{label}` starting in 1.50.0, + // so let's include them in the event data also. + eventDataKeys: ['value', 'label'] }; -},{}],269:[function(_dereq_,module,exports){ +},{}],271:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68452,7 +69109,7 @@ module.exports = { setGroupPositions: setGroupPositions }; -},{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_ids":215,"../../registry":256,"./sieve.js":278,"fast-isnumeric":18}],270:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216,"../../registry":258,"./sieve.js":281,"fast-isnumeric":18}],272:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68610,6 +69267,8 @@ function handleText(traceIn, traceOut, layout, coerce, textposition, opts) { if(moduleHasConstrain) coerce('constraintext'); if(moduleHasCliponaxis) coerce('cliponaxis'); if(moduleHasTextangle) coerce('textangle'); + + coerce('texttemplate'); } if(hasInside) { @@ -68624,7 +69283,36 @@ module.exports = { handleText: handleText }; -},{"../../components/color":51,"../../lib":168,"../../plots/cartesian/axis_ids":215,"../../registry":256,"../scatter/xy_defaults":390,"./attributes":266,"./style_defaults":280}],271:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":258,"../scatter/xy_defaults":403,"./attributes":268,"./style_defaults":283}],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'; + +module.exports = function eventData(out, pt, trace) { + // standard cartesian event data + out.x = 'xVal' in pt ? pt.xVal : pt.x; + out.y = 'yVal' in pt ? pt.yVal : pt.y; + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + if(trace.orientation === 'h') { + out.label = out.y; + out.value = out.x; + } else { + out.label = out.x; + out.value = out.y; + } + + return out; +}; + +},{}],274:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68637,6 +69325,7 @@ module.exports = { var isNumeric = _dereq_('fast-isnumeric'); var tinycolor = _dereq_('tinycolor2'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; exports.coerceString = function(attributeDefinition, value, defaultValue) { if(typeof value === 'string') { @@ -68692,7 +69381,16 @@ exports.getValue = function(arrayOrScalar, index) { return value; }; -},{"fast-isnumeric":18,"tinycolor2":34}],272:[function(_dereq_,module,exports){ +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":18,"tinycolor2":34}],275:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68709,6 +69407,8 @@ var Registry = _dereq_('../../registry'); var Color = _dereq_('../../components/color'); var fillText = _dereq_('../../lib').fillText; +var getLineWidth = _dereq_('./helpers').getLineWidth; +var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText; function hoverPoints(pointData, xval, yval, hovermode) { var barPointData = hoverOnBars(pointData, xval, yval, hovermode); @@ -68845,6 +69545,9 @@ function hoverOnBars(pointData, xval, yval, hovermode) { pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true); pointData[posLetter + 'LabelVal'] = di.p; + pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal']); + pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal']); + // 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 @@ -68860,7 +69563,7 @@ function hoverOnBars(pointData, xval, yval, hovermode) { function getTraceColor(trace, di) { var mc = di.mcc || trace.marker.color; var mlc = di.mlcc || trace.marker.line.color; - var mlw = di.mlw || trace.marker.line.width; + var mlw = getLineWidth(trace, di); if(Color.opacity(mc)) return mc; else if(Color.opacity(mlc) && mlw) return mlc; @@ -68872,7 +69575,7 @@ module.exports = { getTraceColor: getTraceColor }; -},{"../../components/color":51,"../../components/fx":90,"../../lib":168,"../../registry":256}],273:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"./helpers":274}],276:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68897,18 +69600,20 @@ module.exports = { style: _dereq_('./style').style, styleOnSelect: _dereq_('./style').styleOnSelect, hoverPoints: _dereq_('./hover').hoverPoints, + eventData: _dereq_('./event_data'), selectPoints: _dereq_('./select'), moduleType: 'trace', name: 'bar', basePlotModule: _dereq_('../../plots/cartesian'), categories: ['bar-like', 'cartesian', 'svg', 'bar', 'oriented', 'errorBarsOK', 'showLegend', 'zoomScale'], + animatable: true, meta: { } }; -},{"../../plots/cartesian":223,"../scatter/marker_colorbar":382,"./arrays_to_calcdata":265,"./attributes":266,"./calc":267,"./cross_trace_calc":269,"./defaults":270,"./hover":272,"./layout_attributes":274,"./layout_defaults":275,"./plot":276,"./select":277,"./style":279}],274:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../scatter/marker_colorbar":395,"./arrays_to_calcdata":267,"./attributes":268,"./calc":269,"./cross_trace_calc":271,"./defaults":272,"./event_data":273,"./hover":275,"./layout_attributes":277,"./layout_defaults":278,"./plot":279,"./select":280,"./style":282}],277:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -68956,7 +69661,7 @@ module.exports = { } }; -},{}],275:[function(_dereq_,module,exports){ +},{}],278:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69016,7 +69721,7 @@ module.exports = function(layoutIn, layoutOut, fullData) { coerce('bargroupgap'); }; -},{"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":256,"./layout_attributes":274}],276:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"./layout_attributes":277}],279:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69040,13 +69745,22 @@ var tickText = _dereq_('../../plots/cartesian/axes').tickText; var style = _dereq_('./style'); var helpers = _dereq_('./helpers'); +var constants = _dereq_('./constants'); var attributes = _dereq_('./attributes'); var attributeText = attributes.text; var attributeTextPosition = attributes.textposition; -// padding in pixels around text -var TEXTPAD = 3; +var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue; + +var TEXTPAD = constants.TEXTPAD; + +function keyFunc(d) {return d.id;} +function getKeyFunc(trace) { + if(trace.ids) { + return keyFunc; + } +} function dirSign(a, b) { return (a < b) ? 1 : -1; @@ -69068,7 +69782,28 @@ function getXY(di, xa, ya, isHorizontal) { return isHorizontal ? [s, p] : [p, s]; } -function plot(gd, plotinfo, cdModule, traceLayer, opts) { +function transition(selection, opts, makeOnCompleteCallback) { + if(hasTransition(opts)) { + var onComplete; + if(makeOnCompleteCallback) { + onComplete = makeOnCompleteCallback(); + } + return selection + .transition() + .duration(opts.duration) + .ease(opts.easing) + .each('end', function() { onComplete && onComplete(); }) + .each('interrupt', function() { onComplete && onComplete(); }); + } else { + return selection; + } +} + +function hasTransition(transitionOpts) { + return transitionOpts && transitionOpts.duration > 0; +} + +function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) { var xa = plotinfo.xaxis; var ya = plotinfo.yaxis; var fullLayout = gd._fullLayout; @@ -69088,7 +69823,7 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { var isWaterfall = (trace.type === 'waterfall'); var isFunnel = (trace.type === 'funnel'); var isBar = (trace.type === 'bar'); - var shouldDisplayZeros = isBar || isFunnel; + var shouldDisplayZeros = (isBar || isFunnel); var adjustPixel = 0; if(isWaterfall && trace.connector.visible && trace.connector.mode === 'between') { @@ -69097,11 +69832,10 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { var isHorizontal = (trace.orientation === 'h'); - if(!plotinfo.isRangePlot) cd[0].node3 = plotGroup; - var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points'); - var bars = pointGroup.selectAll('g.point').data(Lib.identity); + var keyFunc = getKeyFunc(trace); + var bars = pointGroup.selectAll('g.point').data(Lib.identity, keyFunc); bars.enter().append('g') .classed('point', true); @@ -69115,7 +69849,6 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { // 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]; @@ -69123,15 +69856,25 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { var y0 = xy[1][0]; var y1 = xy[1][1]; - var isBlank = di.isBlank = !( - isNumeric(x0) && isNumeric(x1) && - isNumeric(y0) && isNumeric(y1) && - (x0 !== x1 || (shouldDisplayZeros && isHorizontal)) && - (y0 !== y1 || (shouldDisplayZeros && !isHorizontal)) + 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; + + if(isBlank && isHorizontal) x1 = x0; + if(isBlank && !isHorizontal) y1 = y0; // in waterfall mode `between` we need to adjust bar end points to match the connector width - if(adjustPixel) { + if(adjustPixel && !isBlank) { if(isHorizontal) { x0 -= dirSign(x0, x1) * adjustPixel; x1 += dirSign(x0, x1) * adjustPixel; @@ -69151,8 +69894,7 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { mc = cont.color; } } else { - lw = (di.mlw + 1 || trace.marker.line.width + 1 || - (di.trace ? di.trace.marker.line.width : 0) + 1) - 1; + lw = helpers.getLineWidth(trace, di); mc = di.mc || trace.marker.color; } @@ -69191,12 +69933,18 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { y1 = fixpx(y1, y0); } - Lib.ensureSingle(bar, 'path') + var sel = transition(Lib.ensureSingle(bar, 'path'), opts, makeOnCompleteCallback); + sel .style('vector-effect', 'non-scaling-stroke') - .attr('d', isBlank ? 'M0,0Z' : 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z') + .attr('d', '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(hasTransition(opts)) { + var styleFns = Drawing.makePointStyleFns(trace); + Drawing.singlePointStyle(di, sel, trace, styleFns, gd); + } + + appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback); if(plotinfo.layerClipId) { Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar); @@ -69210,10 +69958,10 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts) { }); // error bars are on the top - Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo); + Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo, opts); } -function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) { +function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) { var xa = plotinfo.xaxis; var ya = plotinfo.yaxis; @@ -69225,7 +69973,6 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) { .text(text) .attr({ 'class': 'bartext bartext-' + textPosition, - transform: '', 'text-anchor': 'middle', // prohibit tex interpretation until we can handle // tex and regular text together @@ -69241,7 +69988,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) { var trace = calcTrace[0].trace; var isHorizontal = (trace.orientation === 'h'); - var text = getText(calcTrace, i, xa, ya); + var text = getText(fullLayout, calcTrace, i, xa, ya); textPosition = getTextPosition(trace, i); // compute text position @@ -69338,9 +70085,12 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) { (textPosition === 'outside') ? outsideTextFont : insideTextFont); + var currentTransform = textSelection.attr('transform'); + textSelection.attr('transform', ''); textBB = Drawing.bBox(textSelection.node()), textWidth = textBB.width, textHeight = textBB.height; + textSelection.attr('transform', currentTransform); if(textWidth <= 0 || textHeight <= 0) { textSelection.remove(); @@ -69355,32 +70105,32 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) { trace.constraintext === 'both' || trace.constraintext === 'outside'; - transform = getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, { + transform = Lib.getTextTransform(toMoveOutsideBar(x0, x1, y0, y1, textBB, { isHorizontal: isHorizontal, constrained: constrained, angle: trace.textangle - }); + })); } else { constrained = trace.constraintext === 'both' || trace.constraintext === 'inside'; - transform = getTransformToMoveInsideBar(x0, x1, y0, y1, textBB, { + transform = Lib.getTextTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, { isHorizontal: isHorizontal, constrained: constrained, angle: trace.textangle, anchor: trace.insidetextanchor - }); + })); } - textSelection.attr('transform', transform); + transition(textSelection, opts, makeOnCompleteCallback).attr('transform', transform); } -function getRotationFromAngle(angle) { +function getRotateFromAngle(angle) { return (angle === 'auto') ? 0 : angle; } -function getTransformToMoveInsideBar(x0, x1, y0, y1, textBB, opts) { +function toMoveInsideBar(x0, x1, y0, y1, textBB, opts) { var isHorizontal = !!opts.isHorizontal; var constrained = !!opts.constrained; var angle = opts.angle || 0; @@ -69417,9 +70167,9 @@ function getTransformToMoveInsideBar(x0, x1, y0, y1, textBB, opts) { lx = tmp; } - var rotation = getRotationFromAngle(angle); - var absSin = Math.abs(Math.sin(Math.PI / 180 * rotation)); - var absCos = Math.abs(Math.cos(Math.PI / 180 * rotation)); + 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); @@ -69453,12 +70203,19 @@ function getTransformToMoveInsideBar(x0, x1, y0, y1, textBB, opts) { var textY = (textBB.top + textBB.bottom) / 2; // lastly apply auto rotation - if(isAutoRotated) rotation += 90; + if(isAutoRotated) rotate += 90; - return getTransform(textX, textY, targetX, targetY, scale, rotation); + return { + textX: textX, + textY: textY, + targetX: targetX, + targetY: targetY, + scale: scale, + rotate: rotate + }; } -function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, opts) { +function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) { var isHorizontal = !!opts.isHorizontal; var constrained = !!opts.constrained; var angle = opts.angle || 0; @@ -69477,7 +70234,7 @@ function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, opts) { textpad = (lx > 2 * TEXTPAD) ? TEXTPAD : 0; } - // compute rotation and scale + // compute rotate and scale var scale = 1; if(constrained) { scale = (isHorizontal) ? @@ -69485,9 +70242,9 @@ function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, opts) { Math.min(1, lx / textWidth); } - var rotation = getRotationFromAngle(angle); - var absSin = Math.abs(Math.sin(Math.PI / 180 * rotation)); - var absCos = Math.abs(Math.cos(Math.PI / 180 * rotation)); + 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); @@ -69506,39 +70263,27 @@ function getTransformToMoveOutsideBar(x0, x1, y0, y1, textBB, opts) { var textX = (textBB.left + textBB.right) / 2; var textY = (textBB.top + textBB.bottom) / 2; - return getTransform(textX, textY, targetX, targetY, scale, rotation); + return { + textX: textX, + textY: textY, + targetX: targetX, + targetY: targetY, + scale: scale, + rotate: rotate + }; } -function getTransform(textX, textY, targetX, targetY, scale, rotation) { - var transformScale; - var transformRotate; - var transformTranslate; - - if(scale < 1) transformScale = 'scale(' + scale + ') '; - else { - scale = 1; - transformScale = ''; - } - - transformRotate = (rotation) ? - 'rotate(' + rotation + ' ' + 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) { +function getText(fullLayout, calcTrace, index, xa, ya) { var trace = calcTrace[0].trace; + var texttemplate = trace.texttemplate; var value; - if(!trace.textinfo) { - value = helpers.getValue(trace.text, index); - } else { + if(texttemplate) { + value = calcTexttemplate(fullLayout, calcTrace, index, xa, ya); + } else if(trace.textinfo) { value = calcTextinfo(calcTrace, index, xa, ya); + } else { + value = helpers.getValue(trace.text, index); } return helpers.coerceString(attributeText, value); @@ -69549,6 +70294,76 @@ function getTextPosition(trace, index) { return helpers.coerceEnumerated(attributeTextPosition, value); } +function calcTexttemplate(fullLayout, calcTrace, index, xa, ya) { + var trace = calcTrace[0].trace; + var texttemplate = Lib.castOption(trace, index, 'texttemplate'); + if(!texttemplate) return ''; + var isWaterfall = (trace.type === 'waterfall'); + var isFunnel = (trace.type === 'funnel'); + + var pLetter, pAxis; + var vLetter, vAxis; + if(trace.orientation === 'h') { + pLetter = 'y'; + pAxis = ya; + vLetter = 'x'; + vAxis = xa; + } else { + pLetter = 'x'; + pAxis = xa; + vLetter = 'y'; + vAxis = ya; + } + + function formatLabel(u) { + return tickText(pAxis, u, true).text; + } + + function formatNumber(v) { + return tickText(vAxis, +v, true).text; + } + + var cdi = calcTrace[index]; + var obj = {}; + + obj.label = cdi.p; + obj.labelLabel = obj[pLetter + 'Label'] = formatLabel(cdi.p); + + var tx = Lib.castOption(trace, cdi.i, 'text'); + if(tx === 0 || tx) obj.text = tx; + + obj.value = cdi.s; + obj.valueLabel = obj[vLetter + 'Label'] = formatNumber(cdi.s); + + var pt = {}; + appendArrayPointValue(pt, trace, cdi.i); + + if(isWaterfall) { + obj.delta = +cdi.rawS || cdi.s; + obj.deltaLabel = formatNumber(obj.delta); + obj.final = cdi.v; + obj.finalLabel = formatNumber(obj.final); + obj.initial = obj.final - obj.delta; + obj.initialLabel = formatNumber(obj.initial); + } + + if(isFunnel) { + obj.value = cdi.s; + obj.valueLabel = formatNumber(obj.value); + + obj.percentInitial = cdi.begR; + obj.percentInitialLabel = Lib.formatPercent(cdi.begR); + obj.percentPrevious = cdi.difR; + obj.percentPreviousLabel = Lib.formatPercent(cdi.difR); + obj.percentTotal = cdi.sumR; + obj.percenTotalLabel = Lib.formatPercent(cdi.sumR); + } + + var customdata = Lib.castOption(trace, cdi.i, 'customdata'); + if(customdata) obj.customdata = customdata; + return Lib.texttemplateString(texttemplate, obj, fullLayout._d3locale, pt, obj, trace._meta || {}); +} + function calcTextinfo(calcTrace, index, xa, ya) { var trace = calcTrace[0].trace; var isHorizontal = (trace.orientation === 'h'); @@ -69625,11 +70440,11 @@ function calcTextinfo(calcTrace, index, xa, ya) { module.exports = { plot: plot, - getTransformToMoveInsideBar: getTransformToMoveInsideBar, - getTransformToMoveOutsideBar: getTransformToMoveOutsideBar + toMoveInsideBar: toMoveInsideBar, + toMoveOutsideBar: toMoveOutsideBar }; -},{"../../components/color":51,"../../components/drawing":72,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../registry":256,"./attributes":266,"./helpers":271,"./style":279,"d3":16,"fast-isnumeric":18}],277:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../components/fx/helpers":86,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../registry":258,"./attributes":268,"./constants":270,"./helpers":274,"./style":282,"d3":16,"fast-isnumeric":18}],280:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69693,7 +70508,7 @@ function getCentroid(d, xa, ya, isHorizontal, isFunnel) { } } -},{}],278:[function(_dereq_,module,exports){ +},{}],281:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69804,7 +70619,7 @@ Sieve.prototype.getLabel = function getLabel(position, value) { return prefix + label; }; -},{"../../constants/numerical":149,"../../lib":168}],279:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169}],282:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -69827,8 +70642,8 @@ var attributeInsideTextFont = attributes.insidetextfont; var attributeOutsideTextFont = attributes.outsidetextfont; var helpers = _dereq_('./helpers'); -function style(gd, cd) { - var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.barlayer').selectAll('g.trace'); +function style(gd) { + var s = d3.select(gd).selectAll('g.barlayer').selectAll('g.trace'); var barcount = s.size(); var fullLayout = gd._fullLayout; @@ -69869,16 +70684,14 @@ function styleTextPoints(sel, trace, gd) { }); } -function styleOnSelect(gd, cd) { - var s = cd[0].node3; +function styleOnSelect(gd, cd, sel) { var trace = cd[0].trace; if(trace.selectedpoints) { - stylePointsInSelectionMode(s, trace, gd); + stylePointsInSelectionMode(sel, trace, gd); } else { - stylePoints(s, trace, gd); - - Registry.getComponentMethod('errorbars', 'style')(s); + stylePoints(sel, trace, gd); + Registry.getComponentMethod('errorbars', 'style')(sel); } } @@ -69983,7 +70796,7 @@ module.exports = { getBarColor: getBarColor }; -},{"../../components/color":51,"../../components/drawing":72,"../../lib":168,"../../registry":256,"./attributes":266,"./helpers":271,"d3":16}],280:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../lib":169,"../../registry":258,"./attributes":268,"./helpers":274,"d3":16}],283:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70021,7 +70834,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, default coerce('unselected.marker.color'); }; -},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62}],281:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62}],284:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70035,7 +70848,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, default var scatterAttrs = _dereq_('../scatter/attributes'); var barAttrs = _dereq_('../bar/attributes'); var colorAttrs = _dereq_('../../components/color/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var extendFlat = _dereq_('../../lib/extend').extendFlat; var scatterMarkerAttrs = scatterAttrs.marker; @@ -70233,7 +71046,7 @@ module.exports = { } }; -},{"../../components/color/attributes":50,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../bar/attributes":266,"../scatter/attributes":365}],282:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"../scatter/attributes":377}],285:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70305,6 +71118,9 @@ module.exports = function calc(gd, trace) { Lib.identity : function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); }; + var minLowerNotch = Infinity; + var maxUpperNotch = -Infinity; + // build calcdata trace items, one item per distinct position for(i = 0; i < pLen; i++) { if(ptsPerBin[i].length > 0) { @@ -70359,6 +71175,8 @@ module.exports = function calc(gd, trace) { var mci = 1.57 * iqr / Math.sqrt(bvLen); cdi.ln = cdi.med - mci; cdi.un = cdi.med + mci; + minLowerNotch = Math.min(minLowerNotch, cdi.ln); + maxUpperNotch = Math.max(maxUpperNotch, cdi.un); cdi.pts2 = pts.filter(ptFilterFn); @@ -70367,8 +71185,11 @@ module.exports = function calc(gd, trace) { } calcSelection(cd, trace); - var extremes = Axes.findExtremes(valAxis, val, {padded: true}); - trace._extremes[valAxis._id] = extremes; + + trace._extremes[valAxis._id] = Axes.findExtremes(valAxis, + trace.notched ? val.concat([minLowerNotch, maxUpperNotch]) : val, + {padded: true} + ); if(cd.length > 0) { cd[0].t = { @@ -70482,7 +71303,7 @@ function sortByVal(a, b) { return a.v - b.v; } function extractVal(o) { return o.v; } -},{"../../lib":168,"../../plots/cartesian/axes":212,"fast-isnumeric":18}],283:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"fast-isnumeric":18}],286:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70545,7 +71366,7 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { for(i = 0; i < boxList.length; i++) { calcTrace = calcdata[boxList[i]]; for(j = 0; j < calcTrace.length; j++) { - pointList.push(calcTrace[j].pos); + pointList.push(posAxis.c2l(calcTrace[j].pos, true)); shownPts += (calcTrace[j].pts2 || []).length; } } @@ -70698,6 +71519,7 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { padded: padded, vpadminus: vpadminus, vpadplus: vpadplus, + vpadLinearized: true, // N.B. SVG px-space positive/negative ppadminus: {x: ppadminus, y: ppadplus}[axLetter], ppadplus: {x: ppadplus, y: ppadminus}[axLetter], @@ -70710,7 +71532,7 @@ module.exports = { setPositionOffset: setPositionOffset }; -},{"../../lib":168,"../../plots/cartesian/axes":212,"../../plots/cartesian/axis_ids":215}],284:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216}],287:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70854,7 +71676,7 @@ module.exports = { handlePointsDefaults: handlePointsDefaults }; -},{"../../components/color":51,"../../lib":168,"../../registry":256,"../bar/defaults":270,"./attributes":281}],285:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../registry":258,"../bar/defaults":272,"./attributes":284}],288:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70879,7 +71701,7 @@ module.exports = function eventData(out, pt) { return out; }; -},{}],286:[function(_dereq_,module,exports){ +},{}],289:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -70942,7 +71764,7 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) { var boxDelta = t.bdPos; var boxDeltaPos, boxDeltaNeg; var posAcceptance = t.wHover; - var shiftPos = function(di) { return di.pos + t.bPos - pVal; }; + var shiftPos = function(di) { return pAxis.c2l(di.pos) + t.bPos - pAxis.c2l(pVal); }; if(isViolin && trace.side !== 'both') { if(trace.side === 'positive') { @@ -71033,7 +71855,7 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) { // box plots: each "point" gets many labels var usedVals = {}; - var attrs = ['med', 'min', 'q1', 'q3', 'max']; + var attrs = ['med', 'q1', 'q3', 'min', 'max']; if(trace.boxmean || (trace.meanline || {}).visible) { attrs.push('mean'); @@ -71053,6 +71875,7 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) { 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); @@ -71163,7 +71986,7 @@ module.exports = { hoverOnPoints: hoverOnPoints }; -},{"../../components/color":51,"../../components/fx":90,"../../lib":168,"../../plots/cartesian/axes":212}],287:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],290:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71198,7 +72021,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"./attributes":281,"./calc":282,"./cross_trace_calc":283,"./defaults":284,"./event_data":285,"./hover":286,"./layout_attributes":288,"./layout_defaults":289,"./plot":290,"./select":291,"./style":292}],288:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":284,"./calc":285,"./cross_trace_calc":286,"./defaults":287,"./event_data":288,"./hover":289,"./layout_attributes":291,"./layout_defaults":292,"./plot":293,"./select":294,"./style":295}],291:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71239,7 +72062,7 @@ module.exports = { } }; -},{}],289:[function(_dereq_,module,exports){ +},{}],292:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71285,7 +72108,7 @@ module.exports = { _supply: _supply }; -},{"../../lib":168,"../../registry":256,"./layout_attributes":288}],290:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"./layout_attributes":291}],293:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71314,7 +72137,6 @@ function plot(gd, plotinfo, cdbox, boxLayer) { var cd0 = cd[0]; var t = cd0.t; var trace = cd0.trace; - if(!plotinfo.isRangePlot) cd0.node3 = plotGroup; // whisker width t.wdPos = t.bdPos * trace.whiskerwidth; @@ -71375,14 +72197,14 @@ function plotBoxAndWhiskers(sel, axes, trace, t) { 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 lcenter = posAxis.c2l(d.pos + bPos, true); + var posc = posAxis.l2p(lcenter) + bPosPxOffset; + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; + var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; + var posw0 = posAxis.l2p(lcenter - wdPos) + bPosPxOffset; + var posw1 = posAxis.l2p(lcenter + wdPos) + bPosPxOffset; + var posm0 = posAxis.l2p(lcenter - bdPos0 * nw) + bPosPxOffset; + var posm1 = posAxis.l2p(lcenter + bdPos1 * nw) + bPosPxOffset; var q1 = valAxis.c2p(d.q1, true); var q3 = valAxis.c2p(d.q3, true); // make sure median isn't identical to either of the @@ -71577,9 +72399,10 @@ function plotBoxMean(sel, axes, trace, t) { 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 lcenter = posAxis.c2l(d.pos + bPos, true); + var posc = posAxis.l2p(lcenter) + bPosPxOffset; + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; + var pos1 = posAxis.l2p(lcenter + bdPos1) + 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); @@ -71609,7 +72432,7 @@ module.exports = { plotBoxMean: plotBoxMean }; -},{"../../components/drawing":72,"../../lib":168,"d3":16}],291:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"d3":16}],294:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71658,7 +72481,7 @@ module.exports = function selectPoints(searchInfo, selectionTester) { return selection; }; -},{}],292:[function(_dereq_,module,exports){ +},{}],295:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71673,8 +72496,8 @@ var d3 = _dereq_('d3'); var Color = _dereq_('../../components/color'); var Drawing = _dereq_('../../components/drawing'); -function style(gd, cd) { - var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.boxes'); +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; }); @@ -71716,10 +72539,9 @@ function style(gd, cd) { }); } -function styleOnSelect(gd, cd) { - var s = cd[0].node3; +function styleOnSelect(gd, cd, sel) { var trace = cd[0].trace; - var pts = s.selectAll('path.point'); + var pts = sel.selectAll('path.point'); if(trace.selectedpoints) { Drawing.selectedPointStyle(pts, trace); @@ -71733,7 +72555,7 @@ module.exports = { styleOnSelect: styleOnSelect }; -},{"../../components/color":51,"../../components/drawing":72,"d3":16}],293:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"d3":16}],296:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71755,6 +72577,8 @@ 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({ @@ -71772,8 +72596,10 @@ module.exports = extendFlat({ ytype: heatmapAttrs.ytype, zhoverformat: heatmapAttrs.zhoverformat, hovertemplate: heatmapAttrs.hovertemplate, - - connectgaps: heatmapAttrs.connectgaps, + hoverongaps: heatmapAttrs.hoverongaps, + connectgaps: extendFlat({}, heatmapAttrs.connectgaps, { + + }), fillcolor: { valType: 'color', @@ -71895,9 +72721,13 @@ module.exports = extendFlat({ editType: 'style+colorbars', }), - width: extendFlat({}, scatterLineAttrs.width, { - editType: 'style+colorbars' - }), + width: { + valType: 'number', + min: 0, + + editType: 'style+colorbars', + + }, dash: dash, smoothing: extendFlat({}, scatterLineAttrs.smoothing, { @@ -71912,7 +72742,7 @@ module.exports = extendFlat({ }) ); -},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../constants/filter_ops":147,"../../lib/extend":162,"../../plots/font_attributes":238,"../heatmap/attributes":315,"../scatter/attributes":365}],294:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../constants/docs":146,"../../constants/filter_ops":147,"../../lib/extend":164,"../../plots/font_attributes":239,"../heatmap/attributes":318,"../scatter/attributes":377}],297:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71965,7 +72795,7 @@ module.exports = function calc(gd, trace) { return cd; }; -},{"../../components/colorscale":63,"../heatmap/calc":316,"./end_plus":304,"./set_contours":312}],295:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../heatmap/calc":319,"./end_plus":307,"./set_contours":315}],298:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -71976,63 +72806,86 @@ module.exports = function calc(gd, trace) { 'use strict'; -module.exports = function(pathinfo, operation, perimeter, trace) { - // Abandon all hope, ye who enter here. - var i, v1, v2; +module.exports = function(pathinfo, contours) { var pi0 = pathinfo[0]; - var na = pi0.x.length; - var nb = pi0.y.length; var z = pi0.z; - var contours = trace.contours; + var i; - var boundaryMax = -Infinity; - var boundaryMin = Infinity; + switch(contours.type) { + case 'levels': + // Why (just) use z[0][0] and z[0][1]? + // + // N.B. using boundaryMin instead of edgeVal2 here makes the + // `contour_scatter` mock fail + var edgeVal2 = Math.min(z[0][0], z[0][1]); - for(i = 0; i < 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; + for(i = 0; i < pathinfo.length; i++) { + var pi = pathinfo[i]; + pi.prefixBoundary = !pi.edgepaths.length && + (edgeVal2 > pi.level || pi.starts.length && edgeVal2 === pi.level); } break; - case '<': - if(contours.value < boundaryMin) { - pi0.prefixBoundary = true; + case 'constraint': + // after convertToConstraints, pathinfo has length=0 + pi0.prefixBoundary = false; + + // joinAllPaths does enough already when edgepaths are present + if(pi0.edgepaths.length) return; + + var na = pi0.x.length; + var nb = pi0.y.length; + var boundaryMax = -Infinity; + var boundaryMin = Infinity; + + for(i = 0; i < nb; i++) { + boundaryMin = Math.min(boundaryMin, z[i][0]); + boundaryMin = Math.min(boundaryMin, z[i][na - 1]); + boundaryMax = Math.max(boundaryMax, z[i][0]); + boundaryMax = Math.max(boundaryMax, z[i][na - 1]); } - break; - case '[]': - v1 = Math.min.apply(null, contours.value); - v2 = Math.max.apply(null, contours.value); - if(v2 < boundaryMin || v1 > boundaryMax) { - pi0.prefixBoundary = true; + 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]); } - break; - case '][': - v1 = Math.min.apply(null, contours.value); - v2 = Math.max.apply(null, contours.value); - if(v1 < boundaryMin && v2 > boundaryMax) { - pi0.prefixBoundary = true; + + var contoursValue = contours.value; + var v1, v2; + + switch(contours._operation) { + case '>': + if(contoursValue > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + case '<': + if(contoursValue < boundaryMin || + (pi0.starts.length && contoursValue === boundaryMin)) { + pi0.prefixBoundary = true; + } + break; + case '[]': + v1 = Math.min(contoursValue[0], contoursValue[1]); + v2 = Math.max(contoursValue[0], contoursValue[1]); + if(v2 < boundaryMin || v1 > boundaryMax || + (pi0.starts.length && v2 === boundaryMin)) { + pi0.prefixBoundary = true; + } + break; + case '][': + v1 = Math.min(contoursValue[0], contoursValue[1]); + v2 = Math.max(contoursValue[0], contoursValue[1]); + if(v1 < boundaryMin && v2 > boundaryMax) { + pi0.prefixBoundary = true; + } + break; } break; } }; -},{}],296:[function(_dereq_,module,exports){ +},{}],299:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72081,7 +72934,7 @@ module.exports = { calc: calc }; -},{"../../components/colorscale":63,"./end_plus":304,"./make_color_map":309}],297:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"./end_plus":307,"./make_color_map":312}],300:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72159,7 +73012,7 @@ module.exports = { } }; -},{}],298:[function(_dereq_,module,exports){ +},{}],301:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72253,7 +73106,7 @@ function handleConstraintValueDefaults(coerce, contours) { } } -},{"../../components/color":51,"../../constants/filter_ops":147,"./label_defaults":308,"fast-isnumeric":18}],299:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../constants/filter_ops":147,"./label_defaults":311,"fast-isnumeric":18}],302:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72332,7 +73185,7 @@ function makeInequalitySettings(operation) { }; } -},{"../../constants/filter_ops":147,"fast-isnumeric":18}],300:[function(_dereq_,module,exports){ +},{"../../constants/filter_ops":147,"fast-isnumeric":18}],303:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72361,7 +73214,7 @@ module.exports = function handleContourDefaults(traceIn, traceOut, coerce, coerc if(autoContour || !contourSize) coerce('ncontours'); }; -},{}],301:[function(_dereq_,module,exports){ +},{}],304:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72378,6 +73231,8 @@ var Lib = _dereq_('../../lib'); // need weird range loops and flipped contours instead of the usual format. This function // does some weird manipulation of the extracted pathinfo data such that it magically // draws contours correctly *as* constraints. +// +// ** I do not know which "weird range loops" the comment above is referring to. module.exports = function(pathinfo, operation) { var i, pi0, pi1; @@ -72393,18 +73248,20 @@ module.exports = function(pathinfo, operation) { 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. + // In this case there should be exactly one contour levels in pathinfo. + // We flip all of the data. This will draw the contour as closed. pi0 = pathinfo[0]; for(i = 0; i < pi0.edgepaths.length; i++) { pi0.edgepaths[i] = op0(pi0.edgepaths[i]); } - for(i = 0; i < pi0.paths.length; i++) { pi0.paths[i] = op0(pi0.paths[i]); } + for(i = 0; i < pi0.starts.length; i++) { + pi0.starts[i] = op0(pi0.starts[i]); + } + return pathinfo; case '][': var tmp = op0; @@ -72418,19 +73275,22 @@ module.exports = function(pathinfo, operation) { 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. + // In this case there should be exactly two contour levels in pathinfo. + // - We concatenate the info into one pathinfo. + // - We must also flip all of the data in the `[]` case. + // This will draw the contours as closed. pi0 = copyPathinfo(pathinfo[0]); pi1 = copyPathinfo(pathinfo[1]); for(i = 0; i < pi0.edgepaths.length; i++) { pi0.edgepaths[i] = op0(pi0.edgepaths[i]); } - for(i = 0; i < pi0.paths.length; i++) { pi0.paths[i] = op0(pi0.paths[i]); } + for(i = 0; i < pi0.starts.length; i++) { + pi0.starts[i] = op0(pi0.starts[i]); + } while(pi1.edgepaths.length) { pi0.edgepaths.push(op1(pi1.edgepaths.shift())); @@ -72438,6 +73298,10 @@ module.exports = function(pathinfo, operation) { while(pi1.paths.length) { pi0.paths.push(op1(pi1.paths.shift())); } + while(pi1.starts.length) { + pi0.starts.push(op1(pi1.starts.shift())); + } + return [pi0]; } }; @@ -72445,11 +73309,12 @@ module.exports = function(pathinfo, operation) { function copyPathinfo(pi) { return Lib.extendFlat({}, pi, { edgepaths: Lib.extendDeep([], pi.edgepaths), - paths: Lib.extendDeep([], pi.paths) + paths: Lib.extendDeep([], pi.paths), + starts: Lib.extendDeep([], pi.starts) }); } -},{"../../lib":168}],302:[function(_dereq_,module,exports){ +},{"../../lib":169}],305:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72487,6 +73352,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('text'); coerce('hovertext'); coerce('hovertemplate'); + coerce('hoverongaps'); var isConstraint = (coerce('contours.type') === 'constraint'); coerce('connectgaps', Lib.isArray1D(traceOut.z)); @@ -72499,7 +73365,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } }; -},{"../../lib":168,"../heatmap/xyz_defaults":329,"./attributes":293,"./constraint_defaults":298,"./contours_defaults":300,"./style_defaults":314}],303:[function(_dereq_,module,exports){ +},{"../../lib":169,"../heatmap/xyz_defaults":332,"./attributes":296,"./constraint_defaults":301,"./contours_defaults":303,"./style_defaults":317}],306:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72563,7 +73429,7 @@ module.exports = function emptyPathinfo(contours, plotinfo, cd0) { return pathinfo; }; -},{"../../lib":168,"./constraint_mapping":299,"./end_plus":304}],304:[function(_dereq_,module,exports){ +},{"../../lib":169,"./constraint_mapping":302,"./end_plus":307}],307:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72583,7 +73449,7 @@ module.exports = function endPlus(contours) { return contours.end + contours.size / 1e6; }; -},{}],305:[function(_dereq_,module,exports){ +},{}],308:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72639,15 +73505,15 @@ function ptDist(pt1, pt2) { } function makePath(pi, loc, edgeflag, xtol, ytol) { - var startLocStr = loc.join(','); - var locStr = startLocStr; + var locStr = loc.join(','); var mi = pi.crossings[locStr]; - var marchStep = startStep(mi, edgeflag, loc); + var marchStep = getStartStep(mi, edgeflag, loc); // start by going backward a half step and finding the crossing point var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])]; - var startStepStr = marchStep.join(','); var m = pi.z.length; var n = pi.z[0].length; + var startLoc = loc.slice(); + var startStep = marchStep.slice(); var cnt; // now follow the path @@ -72669,14 +73535,16 @@ function makePath(pi, loc, edgeflag, xtol, ytol) { pts.push(getInterpPx(pi, loc, marchStep)); loc[0] += marchStep[0]; loc[1] += marchStep[1]; + locStr = loc.join(','); // don't include the same point multiple times if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop(); - 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); + + var closedLoop = loc[0] === startLoc[0] && loc[1] === startLoc[1] && + marchStep[0] === startStep[0] && marchStep[1] === startStep[1]; // have we completed a loop, or reached an edge? if((closedLoop) || (edgeflag && atEdge)) break; @@ -72770,7 +73638,7 @@ function makePath(pi, loc, edgeflag, xtol, ytol) { } else { if(!edgeflag) { Lib.log('Unclosed interior contour?', - pi.level, startLocStr, pts.join('L')); + pi.level, startLoc.join(','), pts.join('L')); } // edge path - does it start where an existing edge path ends, or vice versa? @@ -72820,7 +73688,7 @@ function makePath(pi, loc, edgeflag, xtol, ytol) { // special function to get the marching step of the // first point in the path (leading to loc) -function startStep(mi, edgeflag, loc) { +function getStartStep(mi, edgeflag, loc) { var dx = 0; var dy = 0; if(mi > 20 && edgeflag) { @@ -72875,7 +73743,7 @@ function getInterpPx(pi, loc, step) { } } -},{"../../lib":168,"./constants":297}],306:[function(_dereq_,module,exports){ +},{"../../lib":169,"./constants":300}],309:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72910,7 +73778,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay return hoverData; }; -},{"../../components/color":51,"../heatmap/hover":322}],307:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../heatmap/hover":325}],310:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72939,7 +73807,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"./attributes":293,"./calc":294,"./colorbar":296,"./defaults":302,"./hover":306,"./plot":311,"./style":313}],308:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":296,"./calc":297,"./colorbar":299,"./defaults":305,"./hover":309,"./plot":314,"./style":316}],311:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -72969,7 +73837,7 @@ module.exports = function handleLabelDefaults(coerce, layout, lineColor, opts) { if(opts.hasHover !== false) coerce('zhoverformat'); }; -},{"../../lib":168}],309:[function(_dereq_,module,exports){ +},{"../../lib":169}],312:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73053,7 +73921,7 @@ module.exports = function makeColorMap(trace) { ); }; -},{"../../components/colorscale":63,"./end_plus":304,"d3":16}],310:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"./end_plus":307,"d3":16}],313:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73145,7 +74013,7 @@ function getMarchingIndex(val, corners) { return (mi === 15) ? 0 : mi; } -},{"./constants":297}],311:[function(_dereq_,module,exports){ +},{"./constants":300}],314:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73161,6 +74029,7 @@ var d3 = _dereq_('d3'); var Lib = _dereq_('../../lib'); var Drawing = _dereq_('../../components/drawing'); +var Colorscale = _dereq_('../../components/colorscale'); var svgTextUtils = _dereq_('../../lib/svg_text_utils'); var Axes = _dereq_('../../plots/cartesian/axes'); var setConvert = _dereq_('../../plots/cartesian/set_convert'); @@ -73211,8 +74080,8 @@ exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) { var fillPathinfo = pathinfo; if(contours.type === 'constraint') { + // N.B. this also mutates pathinfo fillPathinfo = convertToConstraints(pathinfo, contours._operation); - closeBoundaries(fillPathinfo, contours._operation, perimeter, trace); } // draw everything @@ -73236,10 +74105,17 @@ function makeBackground(plotgroup, perimeter, contours) { } function makeFills(plotgroup, pathinfo, perimeter, contours) { + var hasFills = contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '='); + var boundaryPath = 'M' + perimeter.join('L') + 'Z'; + + // fills prefixBoundary in pathinfo items + if(hasFills) { + closeBoundaries(pathinfo, contours); + } + var fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill'); - var fillitems = fillgroup.selectAll('path') - .data(contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '=') ? pathinfo : []); + var fillitems = fillgroup.selectAll('path').data(hasFills ? pathinfo : []); fillitems.enter().append('path'); fillitems.exit().remove(); fillitems.each(function(pi) { @@ -73248,30 +74124,21 @@ function makeFills(plotgroup, pathinfo, perimeter, contours) { // 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); + var fullpath = (pi.prefixBoundary ? boundaryPath : '') + + joinAllPaths(pi, perimeter); - if(!fullpath) d3.select(this).remove(); - else d3.select(this).attr('d', fullpath).style('stroke', 'none'); + 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 fullpath = ''; var i = 0; var startsleft = pi.edgepaths.map(function(v, i) { return i; }); var newloop = true; @@ -73386,7 +74253,7 @@ function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours) { // invalidate the getTextLocation cache in case paths changed Lib.clearLocationCache(); - var contourFormat = exports.labelFormatter(contours, cd0.t.cb, gd._fullLayout); + var contourFormat = exports.labelFormatter(gd, cd0); var dummyText = Drawing.tester.append('text') .attr('data-notex', 1) @@ -73538,13 +74405,18 @@ exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) { return lineClip; }; -exports.labelFormatter = function(contours, colorbar, fullLayout) { +exports.labelFormatter = function(gd, cd0) { + var fullLayout = gd._fullLayout; + var trace = cd0.trace; + var contours = trace.contours; + if(contours.labelformat) { return fullLayout._d3locale.numberFormat(contours.labelformat); } else { var formatAxis; - if(colorbar) { - formatAxis = colorbar.axis; + var cOpts = Colorscale.extractOpts(trace); + if(cOpts && cOpts.colorbar && cOpts.colorbar._axis) { + formatAxis = cOpts.colorbar._axis; } else { formatAxis = { type: 'linear', @@ -73760,17 +74632,18 @@ exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPath }; function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) { + var trace = cd0.trace; var clips = gd._fullLayout._clips; - var clipId = 'clip' + cd0.trace.uid; + var clipId = 'clip' + trace.uid; var clipPath = clips.selectAll('#' + clipId) - .data(cd0.trace.connectgaps ? [] : [0]); + .data(trace.connectgaps ? [] : [0]); clipPath.enter().append('clipPath') .classed('contourclip', true) .attr('id', clipId); clipPath.exit().remove(); - if(cd0.trace.connectgaps === false) { + if(trace.connectgaps === false) { var clipPathInfo = { // fraction of the way from missing to present point // to draw the boundary. @@ -73792,10 +74665,13 @@ function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) { makeCrossings([clipPathInfo]); findAllPaths([clipPathInfo]); - var fullpath = joinAllPaths(clipPathInfo, perimeter); + closeBoundaries([clipPathInfo], {type: 'levels'}); var path = Lib.ensureSingle(clipPath, 'path', ''); - path.attr('d', fullpath); + path.attr('d', + (clipPathInfo.prefixBoundary ? 'M' + perimeter.join('L') + 'Z' : '') + + joinAllPaths(clipPathInfo, perimeter) + ); } else clipId = null; Drawing.setClipUrl(plotGroup, clipId, gd); @@ -73821,7 +74697,7 @@ function makeClipMask(cd0) { return z; } -},{"../../components/drawing":72,"../../lib":168,"../../lib/svg_text_utils":189,"../../plots/cartesian/axes":212,"../../plots/cartesian/set_convert":230,"../heatmap/plot":326,"./close_boundaries":295,"./constants":297,"./convert_to_constraints":301,"./empty_pathinfo":303,"./find_all_paths":305,"./make_crossings":310,"d3":16}],312:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../../components/drawing":72,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/cartesian/axes":213,"../../plots/cartesian/set_convert":231,"../heatmap/plot":329,"./close_boundaries":298,"./constants":300,"./convert_to_constraints":304,"./empty_pathinfo":306,"./find_all_paths":308,"./make_crossings":313,"d3":16}],315:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -73925,7 +74801,7 @@ function autoContours(start, end, ncontours) { return dummyAx; } -},{"../../lib":168,"../../plots/cartesian/axes":212}],313:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213}],316:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74006,7 +74882,7 @@ module.exports = function style(gd) { heatmapStyle(gd); }; -},{"../../components/drawing":72,"../heatmap/style":327,"./make_color_map":309,"d3":16}],314:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../heatmap/style":330,"./make_color_map":312,"d3":16}],317:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74051,7 +74927,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, handleLabelDefaults(coerce, layout, lineColor, opts); }; -},{"../../components/colorscale/defaults":61,"./label_defaults":308}],315:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"./label_defaults":311}],318:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74063,8 +74939,9 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, 'use strict'; var scatterAttrs = _dereq_('../scatter/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var FORMAT_LINK = _dereq_('../../constants/docs').FORMAT_LINK; var extendFlat = _dereq_('../../lib/extend').extendFlat; @@ -74119,10 +74996,16 @@ module.exports = extendFlat({ editType: 'calc', + }, + hoverongaps: { + valType: 'boolean', + dflt: true, + + editType: 'none', + }, connectgaps: { valType: 'boolean', - dflt: false, editType: 'calc', @@ -74157,7 +75040,7 @@ module.exports = extendFlat({ colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); -},{"../../components/colorscale/attributes":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../scatter/attributes":365}],316:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../constants/docs":146,"../../lib/extend":164,"../../plots/template_attributes":253,"../scatter/attributes":377}],319:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74317,7 +75200,7 @@ module.exports = function calc(gd, trace) { return [cd0]; }; -},{"../../components/colorscale/calc":59,"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":256,"../histogram2d/calc":344,"./clean_2d_array":317,"./convert_column_xyz":319,"./find_empties":321,"./interp2d":324,"./make_bound_array":325}],317:[function(_dereq_,module,exports){ +},{"../../components/colorscale/calc":59,"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"../histogram2d/calc":347,"./clean_2d_array":320,"./convert_column_xyz":322,"./find_empties":324,"./interp2d":327,"./make_bound_array":328}],320:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74345,11 +75228,11 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { 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]; }; + 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]; }; + old2new = function(zOld, i, j) { return (zOld[i] || [])[j]; }; } var padOld2new = function(zOld, i, j) { @@ -74378,9 +75261,9 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { var xMap = axisMapping(xa); var yMap = axisMapping(ya); + if(ya && ya.type === 'category') rowlen = ya._categories.length; var zNew = new Array(rowlen); - if(ya && ya.type === 'category') rowlen = ya._categories.length; for(i = 0; i < rowlen; i++) { if(xa && xa.type === 'category') { collen = xa._categories.length; @@ -74394,7 +75277,7 @@ module.exports = function clean2dArray(zOld, trace, xa, ya) { return zNew; }; -},{"../../constants/numerical":149,"../../lib":168,"fast-isnumeric":18}],318:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"fast-isnumeric":18}],321:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74410,7 +75293,7 @@ module.exports = { max: 'zmax' }; -},{}],319:[function(_dereq_,module,exports){ +},{}],322:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74488,7 +75371,7 @@ module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, } }; -},{"../../constants/numerical":149,"../../lib":168}],320:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169}],323:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74525,12 +75408,13 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleStyleDefaults(traceIn, traceOut, coerce, layout); + coerce('hoverongaps'); coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false)); colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); }; -},{"../../components/colorscale/defaults":61,"../../lib":168,"./attributes":315,"./style_defaults":328,"./xyz_defaults":329}],321:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"../../lib":169,"./attributes":318,"./style_defaults":331,"./xyz_defaults":332}],324:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74635,7 +75519,7 @@ module.exports = function findEmpties(z) { return empties.sort(function(a, b) { return b[2] - a[2]; }); }; -},{"../../lib":168}],322:[function(_dereq_,module,exports){ +},{"../../lib":169}],325:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74729,6 +75613,8 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay var zVal = z[ny][nx]; if(zmask && !zmask[ny][nx]) zVal = undefined; + if(zVal === undefined && !trace.hoverongaps) return; + var text; if(Array.isArray(cd0.hovertext) && Array.isArray(cd0.hovertext[ny])) { text = cd0.hovertext[ny][nx]; @@ -74764,7 +75650,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay })]; }; -},{"../../components/colorscale":63,"../../components/fx":90,"../../lib":168,"../../plots/cartesian/axes":212}],323:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],326:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74793,7 +75679,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"./attributes":315,"./calc":316,"./colorbar":318,"./defaults":320,"./hover":322,"./plot":326,"./style":327}],324:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./attributes":318,"./calc":319,"./colorbar":321,"./defaults":323,"./hover":325,"./plot":329,"./style":330}],327:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -74926,7 +75812,7 @@ function iterateInterp2d(z, emptyPoints, overshoot) { return maxFractionalChange; } -},{"../../lib":168}],325:[function(_dereq_,module,exports){ +},{"../../lib":169}],328:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75014,7 +75900,7 @@ module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, return arrayOut; }; -},{"../../lib":168,"../../registry":256}],326:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],329:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75419,7 +76305,7 @@ function putColor(pixels, pxIndex, c) { pixels[pxIndex + 3] = Math.round(c[3] * 255); } -},{"../../components/colorscale":63,"../../constants/xmlns_namespaces":150,"../../lib":168,"../../registry":256,"d3":16,"tinycolor2":34}],327:[function(_dereq_,module,exports){ +},{"../../components/colorscale":63,"../../constants/xmlns_namespaces":150,"../../lib":169,"../../registry":258,"d3":16,"tinycolor2":34}],330:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75440,7 +76326,7 @@ module.exports = function style(gd) { }); }; -},{"d3":16}],328:[function(_dereq_,module,exports){ +},{"d3":16}],331:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75463,7 +76349,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce) { coerce('zhoverformat'); }; -},{}],329:[function(_dereq_,module,exports){ +},{}],332:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75560,7 +76446,7 @@ function isValidZ(z) { return (allRowsAreArrays && oneRowIsFilled && hasOneNumber); } -},{"../../lib":168,"../../registry":256,"fast-isnumeric":18}],330:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"fast-isnumeric":18}],333:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75572,7 +76458,7 @@ function isValidZ(z) { 'use strict'; var barAttrs = _dereq_('../bar/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var makeBinAttrs = _dereq_('./bin_attributes'); var constants = _dereq_('./constants'); var extendFlat = _dereq_('../../lib/extend').extendFlat; @@ -75701,7 +76587,7 @@ module.exports = { } }; -},{"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../bar/attributes":266,"./bin_attributes":332,"./constants":336}],331:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"./bin_attributes":335,"./constants":339}],334:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75726,7 +76612,7 @@ module.exports = function doAvg(size, counts) { return total; }; -},{}],332:[function(_dereq_,module,exports){ +},{}],335:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75761,7 +76647,7 @@ module.exports = function makeBinAttrs(axLetter, match) { }; }; -},{}],333:[function(_dereq_,module,exports){ +},{}],336:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -75835,7 +76721,7 @@ module.exports = { } }; -},{"fast-isnumeric":18}],334:[function(_dereq_,module,exports){ +},{"fast-isnumeric":18}],337:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76011,7 +76897,7 @@ function dateParts(v, pa, calendar) { return parts; } -},{"../../constants/numerical":149,"../../plots/cartesian/axes":212}],335:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../plots/cartesian/axes":213}],338:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76568,7 +77454,7 @@ module.exports = { calcAllAutoBins: calcAllAutoBins }; -},{"../../lib":168,"../../plots/cartesian/axes":212,"../../registry":256,"../bar/arrays_to_calcdata":265,"./average":331,"./bin_functions":333,"./bin_label_vals":334,"./norm_functions":342,"fast-isnumeric":18}],336:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../registry":258,"../bar/arrays_to_calcdata":267,"./average":334,"./bin_functions":336,"./bin_label_vals":337,"./norm_functions":345,"fast-isnumeric":18}],339:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76584,7 +77470,7 @@ module.exports = { eventDataKeys: ['binNumber'] }; -},{}],337:[function(_dereq_,module,exports){ +},{}],340:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76862,7 +77748,7 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { } }; -},{"../../lib":168,"../../plots/cartesian/axis_ids":215,"../../registry":256,"../bar/defaults":270}],338:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axis_ids":216,"../../registry":258,"../bar/defaults":272}],341:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76936,7 +77822,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'}); }; -},{"../../components/color":51,"../../lib":168,"../../registry":256,"../bar/style_defaults":280,"./attributes":330}],339:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../../registry":258,"../bar/style_defaults":283,"./attributes":333}],342:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -76985,7 +77871,7 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) { return out; }; -},{}],340:[function(_dereq_,module,exports){ +},{}],343:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77020,7 +77906,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { return pts; }; -},{"../../plots/cartesian/axes":212,"../bar/hover":272}],341:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/axes":213,"../bar/hover":275}],344:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77070,7 +77956,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"../bar/cross_trace_calc":269,"../bar/layout_attributes":274,"../bar/layout_defaults":275,"../bar/plot":276,"../bar/select":277,"../bar/style":279,"../scatter/marker_colorbar":382,"./attributes":330,"./calc":335,"./cross_trace_defaults":337,"./defaults":338,"./event_data":339,"./hover":340}],342:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../bar/cross_trace_calc":271,"../bar/layout_attributes":277,"../bar/layout_defaults":278,"../bar/plot":279,"../bar/select":280,"../bar/style":282,"../scatter/marker_colorbar":395,"./attributes":333,"./calc":338,"./cross_trace_defaults":340,"./defaults":341,"./event_data":342,"./hover":343}],345:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77105,7 +77991,7 @@ module.exports = { } }; -},{}],343:[function(_dereq_,module,exports){ +},{}],346:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77119,7 +78005,7 @@ module.exports = { var histogramAttrs = _dereq_('../histogram/attributes'); var makeBinAttrs = _dereq_('../histogram/bin_attributes'); var heatmapAttrs = _dereq_('../heatmap/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); var extendFlat = _dereq_('../../lib/extend').extendFlat; @@ -77171,7 +78057,7 @@ module.exports = extendFlat( colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); -},{"../../components/colorscale/attributes":58,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../heatmap/attributes":315,"../histogram/attributes":330,"../histogram/bin_attributes":332}],344:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../../plots/template_attributes":253,"../heatmap/attributes":318,"../histogram/attributes":333,"../histogram/bin_attributes":335}],347:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77390,7 +78276,7 @@ function getRanges(edges, uniqueVals, gapLow, gapHigh, ax, calendar) { return out; } -},{"../../lib":168,"../../plots/cartesian/axes":212,"../histogram/average":331,"../histogram/bin_functions":333,"../histogram/bin_label_vals":334,"../histogram/calc":335,"../histogram/norm_functions":342}],345:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../histogram/average":334,"../histogram/bin_functions":336,"../histogram/bin_label_vals":337,"../histogram/calc":338,"../histogram/norm_functions":345}],348:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77423,7 +78309,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertemplate'); }; -},{"../../components/colorscale/defaults":61,"../../lib":168,"../heatmap/style_defaults":328,"./attributes":343,"./sample_defaults":348}],346:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"../../lib":169,"../heatmap/style_defaults":331,"./attributes":346,"./sample_defaults":351}],349:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77457,7 +78343,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay return pts; }; -},{"../../plots/cartesian/axes":212,"../heatmap/hover":322}],347:[function(_dereq_,module,exports){ +},{"../../plots/cartesian/axes":213,"../heatmap/hover":325}],350:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77491,7 +78377,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"../heatmap/calc":316,"../heatmap/colorbar":318,"../heatmap/plot":326,"../heatmap/style":327,"../histogram/cross_trace_defaults":337,"../histogram/event_data":339,"./attributes":343,"./defaults":345,"./hover":346}],348:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../heatmap/calc":319,"../heatmap/colorbar":321,"../heatmap/plot":329,"../heatmap/style":330,"../histogram/cross_trace_defaults":340,"../histogram/event_data":342,"./attributes":346,"./defaults":348,"./hover":349}],351:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77536,7 +78422,7 @@ module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout coerce('autobiny'); }; -},{"../../lib":168,"../../registry":256}],349:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],352:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77575,7 +78461,16 @@ module.exports = extendFlat({ autocontour: contourAttrs.autocontour, ncontours: contourAttrs.ncontours, contours: contourAttrs.contours, - line: contourAttrs.line, + line: { + color: contourAttrs.line.color, + width: extendFlat({}, contourAttrs.line.width, { + dflt: 0.5, + + }), + dash: contourAttrs.line.dash, + smoothing: contourAttrs.line.smoothing, + editType: 'plot' + }, zhoverformat: histogram2dAttrs.zhoverformat, hovertemplate: histogram2dAttrs.hovertemplate }, @@ -77585,7 +78480,7 @@ module.exports = extendFlat({ }) ); -},{"../../components/colorscale/attributes":58,"../../lib/extend":162,"../contour/attributes":293,"../histogram2d/attributes":343}],350:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../contour/attributes":296,"../histogram2d/attributes":346}],353:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77622,7 +78517,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertemplate'); }; -},{"../../lib":168,"../contour/contours_defaults":300,"../contour/style_defaults":314,"../histogram2d/sample_defaults":348,"./attributes":349}],351:[function(_dereq_,module,exports){ +},{"../../lib":169,"../contour/contours_defaults":303,"../contour/style_defaults":317,"../histogram2d/sample_defaults":351,"./attributes":352}],354:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77654,7 +78549,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"../contour/calc":294,"../contour/colorbar":296,"../contour/hover":306,"../contour/plot":311,"../contour/style":313,"../histogram/cross_trace_defaults":337,"./attributes":349,"./defaults":350}],352:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../contour/calc":297,"../contour/colorbar":299,"../contour/hover":309,"../contour/plot":314,"../contour/style":316,"../histogram/cross_trace_defaults":340,"./attributes":352,"./defaults":353}],355:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77665,11 +78560,597 @@ module.exports = { 'use strict'; -var plotAttrs = _dereq_('../../plots/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var extendFlat = _dereq_('../../lib/extend').extendFlat; +var colormodel = _dereq_('./constants').colormodel; + +var cm = ['rgb', 'rgba', 'hsl', 'hsla']; +var zminDesc = []; +var zmaxDesc = []; +for(var i = 0; i < cm.length; i++) { + zminDesc.push('For the `' + cm[i] + '` colormodel, it is [' + colormodel[cm[i]].min.join(', ') + '].'); + zmaxDesc.push('For the `' + cm[i] + '` colormodel, it is [' + colormodel[cm[i]].max.join(', ') + '].'); +} + +module.exports = extendFlat({ + z: { + valType: 'data_array', + + editType: 'calc', + + }, + colormodel: { + valType: 'enumerated', + values: cm, + dflt: 'rgb', + + editType: 'calc', + + }, + zmin: { + valType: 'info_array', + items: [ + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'} + ], + + editType: 'calc', + + }, + zmax: { + valType: 'info_array', + items: [ + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'}, + {valType: 'number', editType: 'calc'} + ], + + editType: 'calc', + + }, + x0: { + valType: 'any', + dflt: 0, + + editType: 'calc+clearAxisTypes', + + }, + y0: { + valType: 'any', + dflt: 0, + + editType: 'calc+clearAxisTypes', + + }, + dx: { + valType: 'number', + dflt: 1, + + editType: 'calc', + + }, + dy: { + valType: 'number', + dflt: 1, + + editType: 'calc', + + }, + text: { + valType: 'data_array', + editType: 'plot', + + }, + hovertext: { + valType: 'data_array', + editType: 'plot', + + }, + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { + flags: ['x', 'y', 'z', 'color', 'name', 'text'], + dflt: 'x+y+z+text+name' + }), + hovertemplate: hovertemplateAttrs({}, { + keys: ['z', 'color', 'colormodel'] + }), + + transforms: undefined +}); + +},{"../../lib/extend":164,"../../plots/attributes":210,"../../plots/template_attributes":253,"./constants":357}],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 constants = _dereq_('./constants'); +var isNumeric = _dereq_('fast-isnumeric'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var maxRowLength = _dereq_('../../lib').maxRowLength; + +module.exports = function calc(gd, trace) { + var xa = Axes.getFromId(gd, trace.xaxis || 'x'); + var ya = Axes.getFromId(gd, trace.yaxis || 'y'); + + var x0 = xa.d2c(trace.x0) - trace.dx / 2; + var y0 = ya.d2c(trace.y0) - trace.dy / 2; + var h = trace.z.length; + var w = maxRowLength(trace.z); + + // Set axis range + var i; + var xrange = [x0, x0 + w * trace.dx]; + var yrange = [y0, y0 + h * trace.dy]; + if(xa && xa.type === 'log') for(i = 0; i < w; i++) xrange.push(x0 + i * trace.dx); + if(ya && ya.type === 'log') for(i = 0; i < h; i++) yrange.push(y0 + i * trace.dy); + trace._extremes[xa._id] = Axes.findExtremes(xa, xrange); + trace._extremes[ya._id] = Axes.findExtremes(ya, yrange); + trace._scaler = makeScaler(trace); + + var cd0 = { + x0: x0, + y0: y0, + z: trace.z, + w: w, + h: h + }; + return [cd0]; +}; + +function scale(zero, ratio, min, max) { + return function(c) { + return Lib.constrain((c - zero) * ratio, min, max); + }; +} + +function constrain(min, max) { + return function(c) { return Lib.constrain(c, min, max);}; +} + +// Generate a function to scale color components according to zmin/zmax and the colormodel +function makeScaler(trace) { + var colormodel = trace.colormodel; + var n = colormodel.length; + var cr = constants.colormodel[colormodel]; + + trace._sArray = []; + // Loop over all color components + for(var k = 0; k < n; k++) { + if(cr.min[k] !== trace.zmin[k] || cr.max[k] !== trace.zmax[k]) { + trace._sArray.push(scale( + trace.zmin[k], + (cr.max[k] - cr.min[k]) / (trace.zmax[k] - trace.zmin[k]), + cr.min[k], + cr.max[k] + )); + } else { + trace._sArray.push(constrain(cr.min[k], cr.max[k])); + } + } + + return function(pixel) { + var c = pixel.slice(0, n); + for(var k = 0; k < n; k++) { + var ck = c[k]; + if(!isNumeric(ck)) return false; + c[k] = trace._sArray[k](ck); + } + return c; + }; +} + +},{"../../lib":169,"../../plots/cartesian/axes":213,"./constants":357,"fast-isnumeric":18}],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'; + +module.exports = { + colormodel: { + rgb: { + min: [0, 0, 0], + max: [255, 255, 255], + fmt: function(c) {return c.slice(0, 3);}, + suffix: ['', '', ''] + }, + rgba: { + min: [0, 0, 0, 0], + max: [255, 255, 255, 1], + fmt: function(c) {return c.slice(0, 4);}, + suffix: ['', '', '', ''] + }, + hsl: { + min: [0, 0, 0], + max: [360, 100, 100], + fmt: function(c) { + var p = c.slice(0, 3); + p[1] = p[1] + '%'; + p[2] = p[2] + '%'; + return p; + }, + suffix: ['°', '%', '%'] + }, + hsla: { + min: [0, 0, 0, 0], + max: [360, 100, 100, 1], + fmt: function(c) { + var p = c.slice(0, 4); + p[1] = p[1] + '%'; + p[2] = p[2] + '%'; + return p; + }, + suffix: ['°', '%', '%', ''] + } + } +}; + +},{}],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'); +var attributes = _dereq_('./attributes'); +var constants = _dereq_('./constants'); + +module.exports = function supplyDefaults(traceIn, traceOut) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + var z = coerce('z'); + if(z === undefined || !z.length || !z[0] || !z[0].length) { + traceOut.visible = false; + return; + } + + coerce('x0'); + coerce('y0'); + coerce('dx'); + coerce('dy'); + var colormodel = coerce('colormodel'); + + coerce('zmin', constants.colormodel[colormodel].min); + coerce('zmax', constants.colormodel[colormodel].max); + + coerce('text'); + coerce('hovertext'); + coerce('hovertemplate'); + + traceOut._length = null; +}; + +},{"../../lib":169,"./attributes":355,"./constants":357}],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 = function eventData(out, pt) { + if('xVal' in pt) out.x = pt.xVal; + if('yVal' in pt) out.y = pt.yVal; + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + out.color = pt.color; + out.colormodel = pt.trace.colormodel; + return out; +}; + +},{}],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'; + +var Fx = _dereq_('../../components/fx'); +var Lib = _dereq_('../../lib'); +var constants = _dereq_('./constants'); + +module.exports = function hoverPoints(pointData, xval, yval) { + var cd0 = pointData.cd[0]; + var trace = cd0.trace; + var xa = pointData.xa; + var ya = pointData.ya; + + // Return early if not on image + if(Fx.inbox(xval - cd0.x0, xval - (cd0.x0 + cd0.w * trace.dx), 0) > 0 || + Fx.inbox(yval - cd0.y0, yval - (cd0.y0 + cd0.h * trace.dy), 0) > 0) { + return; + } + + // Find nearest pixel's index + var nx = Math.floor((xval - cd0.x0) / trace.dx); + var ny = Math.floor(Math.abs(yval - cd0.y0) / trace.dy); + + // return early if pixel is undefined + if(!cd0.z[ny][nx]) return; + + var hoverinfo = cd0.hi || trace.hoverinfo; + var fmtColor; + if(hoverinfo) { + var parts = hoverinfo.split('+'); + if(parts.indexOf('all') !== -1) parts = ['color']; + if(parts.indexOf('color') !== -1) fmtColor = true; + } + + var colormodel = trace.colormodel; + var dims = colormodel.length; + var c = trace._scaler(cd0.z[ny][nx]); + var s = constants.colormodel[colormodel].suffix; + + var colorstring = []; + if(trace.hovertemplate || fmtColor) { + colorstring.push('[' + [c[0] + s[0], c[1] + s[1], c[2] + s[2]].join(', ')); + if(dims === 4) colorstring.push(', ' + c[3] + s[3]); + colorstring.push(']'); + colorstring = colorstring.join(''); + pointData.extraText = colormodel.toUpperCase() + ': ' + colorstring; + } + + var text; + if(Array.isArray(trace.hovertext) && Array.isArray(trace.hovertext[ny])) { + text = trace.hovertext[ny][nx]; + } else if(Array.isArray(trace.text) && Array.isArray(trace.text[ny])) { + text = trace.text[ny][nx]; + } + + // TODO: for color model with 3 dims, display something useful for hovertemplate `%{color[3]}` + var py = ya.c2p(cd0.y0 + (ny + 0.5) * trace.dy); + var xVal = cd0.x0 + (nx + 0.5) * trace.dx; + var yVal = cd0.y0 + (ny + 0.5) * trace.dy; + var zLabel = '[' + cd0.z[ny][nx].slice(0, trace.colormodel.length).join(', ') + ']'; + return [Lib.extendFlat(pointData, { + index: [ny, nx], + x0: xa.c2p(cd0.x0 + nx * trace.dx), + x1: xa.c2p(cd0.x0 + (nx + 1) * trace.dx), + y0: py, + y1: py, + color: c, + xVal: xVal, + xLabelVal: xVal, + yVal: yVal, + yLabelVal: yVal, + zLabelVal: zLabel, + text: text, + hovertemplateLabels: { + 'zLabel': zLabel, + 'colorLabel': colorstring, + 'color[0]Label': c[0] + s[0], + 'color[1]Label': c[1] + s[1], + 'color[2]Label': c[2] + s[2], + 'color[3]Label': c[3] + s[3] + } + })]; +}; + +},{"../../components/fx":89,"../../lib":169,"./constants":357}],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'; + +module.exports = { + attributes: _dereq_('./attributes'), + supplyDefaults: _dereq_('./defaults'), + calc: _dereq_('./calc'), + plot: _dereq_('./plot'), + style: _dereq_('./style'), + hoverPoints: _dereq_('./hover'), + eventData: _dereq_('./event_data'), + + moduleType: 'trace', + name: 'image', + basePlotModule: _dereq_('../../plots/cartesian'), + categories: ['cartesian', 'svg', '2dMap', 'noSortingByValue'], + animatable: false, + meta: { + + } +}; + +},{"../../plots/cartesian":224,"./attributes":355,"./calc":356,"./defaults":358,"./event_data":359,"./hover":360,"./plot":362,"./style":363}],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 Lib = _dereq_('../../lib'); +var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces'); +var constants = _dereq_('./constants'); + +module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + Lib.makeTraceGroups(imageLayer, cdimage, 'im').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + + var z = cd0.z; + var x0 = cd0.x0; + var y0 = cd0.y0; + var w = cd0.w; + var h = cd0.h; + var dx = trace.dx; + var dy = trace.dy; + + var left, right, temp, top, bottom, i; + // in case of log of a negative + i = 0; + while(left === undefined && i < w) { + left = xa.c2p(x0 + i * dx); + i++; + } + i = w; + while(right === undefined && i > 0) { + right = xa.c2p(x0 + i * dx); + i--; + } + i = 0; + while(top === undefined && i < h) { + top = ya.c2p(y0 + i * dy); + i++; + } + i = h; + while(bottom === undefined && i > 0) { + bottom = ya.c2p(y0 + i * dy); + i--; + } + + if(right < left) { + temp = right; + right = left; + left = temp; + } + + if(bottom < top) { + temp = top; + top = bottom; + bottom = temp; + } + + // Reduce image size when zoomed in to save memory + var extra = 0.5; // half the axis size + left = Math.max(-extra * xa._length, left); + right = Math.min((1 + extra) * xa._length, right); + top = Math.max(-extra * ya._length, top); + bottom = Math.min((1 + extra) * ya._length, bottom); + var imageWidth = Math.round(right - left); + var imageHeight = Math.round(bottom - top); + + // if image is entirely off-screen, don't even draw it + var isOffScreen = (imageWidth <= 0 || imageHeight <= 0); + if(isOffScreen) { + var noImage = plotGroup.selectAll('image').data([]); + noImage.exit().remove(); + return; + } + + // Draw each pixel + var canvas = document.createElement('canvas'); + canvas.width = imageWidth; + canvas.height = imageHeight; + var context = canvas.getContext('2d'); + + var ipx = function(i) {return Lib.constrain(Math.round(xa.c2p(x0 + i * dx) - left), 0, imageWidth);}; + var jpx = function(j) {return Lib.constrain(Math.round(ya.c2p(y0 + j * dy) - top), 0, imageHeight);}; + + var fmt = constants.colormodel[trace.colormodel].fmt; + var c; + for(i = 0; i < cd0.w; i++) { + var ipx0 = ipx(i); var ipx1 = ipx(i + 1); + if(ipx1 === ipx0 || isNaN(ipx1) || isNaN(ipx0)) continue; + for(var j = 0; j < cd0.h; j++) { + var jpx0 = jpx(j); var jpx1 = jpx(j + 1); + if(jpx1 === jpx0 || isNaN(jpx1) || isNaN(jpx0) || !z[j][i]) continue; + c = trace._scaler(z[j][i]); + if(c) { + context.fillStyle = trace.colormodel + '(' + fmt(c).join(',') + ')'; + } else { + // Return a transparent pixel + context.fillStyle = 'rgba(0,0,0,0)'; + } + context.fillRect(ipx0, jpx0, ipx1 - ipx0, jpx1 - jpx0); + } + } + + 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') + }); + }); +}; + +},{"../../constants/xmlns_namespaces":150,"../../lib":169,"./constants":357,"d3":16}],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'); + +module.exports = function style(gd) { + d3.select(gd).selectAll('.im image') + .style('opacity', function(d) { + return d.trace.opacity; + }); +}; + +},{"d3":16}],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 baseAttrs = _dereq_('../../plots/attributes'); var domainAttrs = _dereq_('../../plots/domain').attributes; var fontAttrs = _dereq_('../../plots/font_attributes'); var colorAttrs = _dereq_('../../components/color/attributes'); -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; var extendFlat = _dereq_('../../lib/extend').extendFlat; @@ -77740,7 +79221,7 @@ module.exports = { text: { valType: 'data_array', - editType: 'calc', + editType: 'plot', }, hovertext: { @@ -77773,12 +79254,15 @@ module.exports = { editType: 'calc', }, - hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { flags: ['label', 'text', 'value', 'percent', 'name'] }), hovertemplate: hovertemplateAttrs({}, { keys: ['label', 'color', 'value', 'percent', 'text'] }), + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: ['label', 'color', 'value', 'percent', 'text'] + }), textposition: { valType: 'enumerated', @@ -77797,6 +79281,13 @@ module.exports = { outsidetextfont: extendFlat({}, textFontAttrs, { }), + automargin: { + valType: 'boolean', + dflt: false, + + editType: 'plot', + + }, title: { text: { @@ -77906,7 +79397,7 @@ module.exports = { } }; -},{"../../components/color/attributes":50,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/attributes":209,"../../plots/domain":237,"../../plots/font_attributes":238}],353:[function(_dereq_,module,exports){ +},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/domain":238,"../../plots/font_attributes":239,"../../plots/template_attributes":253}],365:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77917,27 +79408,19 @@ module.exports = { 'use strict'; -var Registry = _dereq_('../../registry'); -var getModuleCalcData = _dereq_('../../plots/get_data').getModuleCalcData; +var plots = _dereq_('../../plots/plots'); exports.name = 'pie'; -exports.plot = function(gd) { - var Pie = Registry.getModule('pie'); - var cdPie = getModuleCalcData(gd.calcdata, Pie)[0]; - Pie.plot(gd, cdPie); +exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { + plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback); }; 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.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout); }; -},{"../../plots/get_data":240,"../../registry":256}],354:[function(_dereq_,module,exports){ +},{"../../plots/plots":245}],366:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -77953,8 +79436,6 @@ 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 = {}; @@ -78033,32 +79514,6 @@ function calc(gd, trace) { // 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; } @@ -78149,7 +79604,7 @@ module.exports = { generateExtendedColors: generateExtendedColors }; -},{"../../components/color":51,"../../lib":168,"./helpers":357,"fast-isnumeric":18,"tinycolor2":34}],355:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"fast-isnumeric":18,"tinycolor2":34}],367:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78199,11 +79654,14 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout // 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'); + var textTemplate = coerce('texttemplate'); + var textInfo; + if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent'); + coerce('hovertext'); coerce('hovertemplate'); - if(textInfo && textInfo !== 'none') { + if(textTemplate || (textInfo && textInfo !== 'none')) { var textposition = coerce('textposition'); handleText(traceIn, traceOut, layout, coerce, textposition, { moduleHasSelected: false, @@ -78213,6 +79671,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout moduleHasTextangle: false, moduleHasInsideanchor: false }); + + var hasBoth = Array.isArray(textposition) || textposition === 'auto'; + var hasOutside = hasBoth || textposition === 'outside'; + if(hasOutside) { + coerce('automargin'); + } } handleDomainDefaults(traceOut, layout, coerce); @@ -78231,7 +79695,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('pull'); }; -},{"../../lib":168,"../../plots/domain":237,"../bar/defaults":270,"./attributes":352}],356:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/domain":238,"../bar/defaults":272,"./attributes":364}],368:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78280,7 +79744,7 @@ module.exports = function eventData(pt, trace) { return out; }; -},{"../../components/fx/helpers":86}],357:[function(_dereq_,module,exports){ +},{"../../components/fx/helpers":86}],369:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78313,7 +79777,7 @@ 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; + if(v || v === 0 || v === '') return v; } }; @@ -78322,7 +79786,7 @@ exports.castOption = function castOption(item, indices) { else if(item) return item; }; -},{"../../lib":168}],358:[function(_dereq_,module,exports){ +},{"../../lib":169}],370:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78355,7 +79819,7 @@ module.exports = { } }; -},{"./attributes":352,"./base_plot":353,"./calc":354,"./defaults":355,"./layout_attributes":359,"./layout_defaults":360,"./plot":361,"./style":362,"./style_one":363}],359:[function(_dereq_,module,exports){ +},{"./attributes":364,"./base_plot":365,"./calc":366,"./defaults":367,"./layout_attributes":371,"./layout_defaults":372,"./plot":373,"./style":374,"./style_one":375}],371:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78388,7 +79852,7 @@ module.exports = { } }; -},{}],360:[function(_dereq_,module,exports){ +},{}],372:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78413,7 +79877,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { coerce('extendpiecolors'); }; -},{"../../lib":168,"./layout_attributes":359}],361:[function(_dereq_,module,exports){ +},{"../../lib":169,"./layout_attributes":371}],373:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -78426,6 +79890,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { var d3 = _dereq_('d3'); +var Plots = _dereq_('../../plots/plots'); var Fx = _dereq_('../../components/fx'); var Color = _dereq_('../../components/color'); var Drawing = _dereq_('../../components/drawing'); @@ -78434,12 +79899,14 @@ var svgTextUtils = _dereq_('../../lib/svg_text_utils'); var helpers = _dereq_('./helpers'); var eventData = _dereq_('./event_data'); +var isValidTextValue = _dereq_('../../lib').isValidTextValue; function plot(gd, cdModule) { var fullLayout = gd._fullLayout; + var gs = fullLayout._size; prerenderTitles(cdModule, gd); - layoutAreas(cdModule, fullLayout._size); + layoutAreas(cdModule, gs); var plotGroups = Lib.makeTraceGroups(fullLayout._pielayer, cdModule, 'trace').each(function(cd) { var plotGroup = d3.select(this); @@ -78541,6 +80008,7 @@ function plot(gd, cdModule) { } // add text + formatSliceLabel(gd, pt, cd0); var textPosition = helpers.castOption(trace.textposition, pt.pts); var sliceTextGroup = sliceTop.selectAll('g.slicetext') .data(pt.text && (textPosition !== 'none') ? [0] : []); @@ -78642,7 +80110,7 @@ function plot(gd, cdModule) { if(trace.title.position === 'middle center') { transform = positionTitleInside(cd0); } else { - transform = positionTitleOutside(cd0, fullLayout._size); + transform = positionTitleOutside(cd0, gs); } titleText.attr('transform', @@ -78655,6 +80123,31 @@ function plot(gd, cdModule) { if(hasOutsideText) scootLabels(quadrants, trace); plotTextLines(slices, trace); + + if(hasOutsideText && trace.automargin) { + // TODO if we ever want to improve perf, + // we could reuse the textBB computed above together + // with the sliceText transform info + var traceBbox = Drawing.bBox(plotGroup.node()); + + var domain = trace.domain; + var vpw = gs.w * (domain.x[1] - domain.x[0]); + var vph = gs.h * (domain.y[1] - domain.y[0]); + var xgap = (0.5 * vpw - cd0.r) / gs.w; + var ygap = (0.5 * vph - cd0.r) / gs.h; + + Plots.autoMargin(gd, 'pie.' + trace.uid + '.automargin', { + xl: domain.x[0] - xgap, + xr: domain.x[1] + xgap, + yb: domain.y[0] - ygap, + yt: domain.y[1] + ygap, + l: Math.max(cd0.cx - cd0.r - traceBbox.left, 0), + r: Math.max(traceBbox.right - (cd0.cx + cd0.r), 0), + b: Math.max(traceBbox.bottom - (cd0.cy + cd0.r), 0), + t: Math.max(cd0.cy - cd0.r - traceBbox.top, 0), + pad: 5 + }); + } }); }); @@ -79357,8 +80850,63 @@ function setCoords(cd) { } } +function formatSliceLabel(gd, pt, cd0) { + var fullLayout = gd._fullLayout; + var trace = cd0.trace; + // look for textemplate + var texttemplate = trace.texttemplate; + + // now insert text + var textinfo = trace.textinfo; + if(!texttemplate && textinfo && textinfo !== 'none') { + var parts = textinfo.split('+'); + var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; }; + var hasLabel = hasFlag('label'); + var hasText = hasFlag('text'); + var hasValue = hasFlag('value'); + var hasPercent = hasFlag('percent'); + + var separators = fullLayout.separators; + var text; + + text = hasLabel ? [pt.label] : []; + if(hasText) { + var tx = helpers.getFirstFilled(trace.text, pt.pts); + if(isValidTextValue(tx)) text.push(tx); + } + if(hasValue) text.push(helpers.formatPieValue(pt.v, separators)); + if(hasPercent) text.push(helpers.formatPiePercent(pt.v / cd0.vTotal, separators)); + pt.text = text.join('
    '); + } + + function makeTemplateVariables(pt) { + return { + label: pt.label, + value: pt.v, + valueLabel: helpers.formatPieValue(pt.v, fullLayout.separators), + percent: pt.v / cd0.vTotal, + percentLabel: helpers.formatPiePercent(pt.v / cd0.vTotal, fullLayout.separators), + color: pt.color, + text: pt.text, + customdata: Lib.castOption(trace, pt.i, 'customdata') + }; + } + + if(texttemplate) { + var txt = Lib.castOption(trace, pt.i, 'texttemplate'); + if(!txt) { + pt.text = ''; + } else { + var obj = makeTemplateVariables(pt); + var ptTx = helpers.getFirstFilled(trace.text, pt.pts); + if(isValidTextValue(ptTx) || ptTx === '') obj.text = ptTx; + pt.text = Lib.texttemplateString(txt, obj, gd._fullLayout._d3locale, obj, trace._meta || {}); + } + } +} module.exports = { plot: plot, + formatSliceLabel: formatSliceLabel, transformInsideText: transformInsideText, determineInsideTextFont: determineInsideTextFont, positionTitleOutside: positionTitleOutside, @@ -79367,7 +80915,7 @@ module.exports = { attachFxHandlers: attachFxHandlers, }; -},{"../../components/color":51,"../../components/drawing":72,"../../components/fx":90,"../../lib":168,"../../lib/svg_text_utils":189,"./event_data":356,"./helpers":357,"d3":16}],362:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/drawing":72,"../../components/fx":89,"../../lib":169,"../../lib/svg_text_utils":190,"../../plots/plots":245,"./event_data":368,"./helpers":369,"d3":16}],374:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79396,7 +80944,7 @@ module.exports = function style(gd) { }); }; -},{"./style_one":363,"d3":16}],363:[function(_dereq_,module,exports){ +},{"./style_one":375,"d3":16}],375:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79420,7 +80968,7 @@ module.exports = function styleOne(s, pt, trace) { .call(Color.stroke, lineColor); }; -},{"../../components/color":51,"./helpers":357}],364:[function(_dereq_,module,exports){ +},{"../../components/color":51,"./helpers":369}],376:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79441,26 +80989,27 @@ 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.texttemplate, cd, 'txt'); Lib.mergeArray(trace.hovertext, cd, 'htx'); Lib.mergeArray(trace.customdata, cd, 'data'); Lib.mergeArray(trace.textposition, cd, 'tp'); if(trace.textfont) { - Lib.mergeArray(trace.textfont.size, cd, 'ts'); + 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.mergeArray(marker.size, cd, 'ms'); - Lib.mergeArray(marker.opacity, cd, 'mo'); + 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.mergeArray(markerLine.width, cd, 'mlw'); + Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw'); } var markerGradient = marker.gradient; @@ -79471,7 +81020,7 @@ module.exports = function arraysToCalcdata(cd, trace) { } }; -},{"../../lib":168}],365:[function(_dereq_,module,exports){ +},{"../../lib":169}],377:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -79482,7 +81031,8 @@ module.exports = function arraysToCalcdata(cd, trace) { 'use strict'; -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); var fontAttrs = _dereq_('../../plots/font_attributes'); var dash = _dereq_('../../components/drawing/attributes').dash; @@ -79576,6 +81126,10 @@ module.exports = { editType: 'calc', }, + + texttemplate: texttemplateAttrs({}, { + + }), hovertext: { valType: 'string', @@ -79880,7 +81434,7 @@ module.exports = { } }; -},{"../../components/colorscale/attributes":58,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/font_attributes":238,"./constants":369}],366:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../components/drawing":72,"../../components/drawing/attributes":71,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"./constants":381}],378:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80165,7 +81719,7 @@ module.exports = { getStackOpts: getStackOpts }; -},{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axes":212,"./arrays_to_calcdata":364,"./calc_selection":367,"./colorscale_calc":368,"./subtypes":388,"fast-isnumeric":18}],367:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"./arrays_to_calcdata":376,"./calc_selection":379,"./colorscale_calc":380,"./subtypes":401,"fast-isnumeric":18}],379:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80184,7 +81738,7 @@ module.exports = function calcSelection(cd, trace) { } }; -},{"../../lib":168}],368:[function(_dereq_,module,exports){ +},{"../../lib":169}],380:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80227,7 +81781,7 @@ module.exports = function calcMarkerColorscale(gd, trace) { } }; -},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"./subtypes":388}],369:[function(_dereq_,module,exports){ +},{"../../components/colorscale/calc":59,"../../components/colorscale/helpers":62,"./subtypes":401}],381:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80256,7 +81810,7 @@ module.exports = { eventDataKeys: [] }; -},{}],370:[function(_dereq_,module,exports){ +},{}],382:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80437,7 +81991,7 @@ function getInterp(calcTrace, index, position, posAttr) { return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]); } -},{"./calc":366}],371:[function(_dereq_,module,exports){ +},{"./calc":378}],383:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80476,7 +82030,7 @@ module.exports = function crossTraceDefaults(fullData) { } }; -},{}],372:[function(_dereq_,module,exports){ +},{}],384:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80531,6 +82085,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(subTypes.hasText(traceOut)) { + coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); } @@ -80565,7 +82120,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout Lib.coerceSelectionMarkerOpacity(traceOut, coerce); }; -},{"../../lib":168,"../../registry":256,"./attributes":365,"./constants":369,"./fillcolor_defaults":373,"./line_defaults":377,"./line_shape_defaults":379,"./marker_defaults":383,"./stack_defaults":386,"./subtypes":388,"./text_defaults":389,"./xy_defaults":390}],373:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258,"./attributes":377,"./constants":381,"./fillcolor_defaults":385,"./line_defaults":390,"./line_shape_defaults":392,"./marker_defaults":396,"./stack_defaults":399,"./subtypes":401,"./text_defaults":402,"./xy_defaults":403}],385:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80602,7 +82157,33 @@ module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coe )); }; -},{"../../components/color":51,"../../lib":168}],374:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169}],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 Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function formatLabels(cdi, trace, fullLayout) { + var labels = {}; + + var mockGd = {_fullLayout: fullLayout}; + var xa = Axes.getFromTrace(mockGd, trace, 'x'); + var ya = Axes.getFromTrace(mockGd, trace, 'y'); + + labels.xLabel = Axes.tickText(xa, cdi.x, true).text; + labels.yLabel = Axes.tickText(ya, cdi.y, true).text; + + return labels; +}; + +},{"../../plots/cartesian/axes":213}],387:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80651,7 +82232,7 @@ module.exports = function getTraceColor(trace, di) { } }; -},{"../../components/color":51,"./subtypes":388}],375:[function(_dereq_,module,exports){ +},{"../../components/color":51,"./subtypes":401}],388:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80846,7 +82427,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { } }; -},{"../../components/color":51,"../../components/fx":90,"../../lib":168,"../../registry":256,"./get_trace_color":374}],376:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/fx":89,"../../lib":169,"../../registry":258,"./get_trace_color":387}],389:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80873,6 +82454,7 @@ module.exports = { arraysToCalcdata: _dereq_('./arrays_to_calcdata'), plot: _dereq_('./plot'), colorbar: _dereq_('./marker_colorbar'), + formatLabels: _dereq_('./format_labels'), style: _dereq_('./style').style, styleOnSelect: _dereq_('./style').styleOnSelect, hoverPoints: _dereq_('./hover'), @@ -80891,7 +82473,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"./arrays_to_calcdata":364,"./attributes":365,"./calc":366,"./cross_trace_calc":370,"./cross_trace_defaults":371,"./defaults":372,"./hover":375,"./marker_colorbar":382,"./plot":384,"./select":385,"./style":387,"./subtypes":388}],377:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"./arrays_to_calcdata":376,"./attributes":377,"./calc":378,"./cross_trace_calc":382,"./cross_trace_defaults":383,"./defaults":384,"./format_labels":386,"./hover":388,"./marker_colorbar":395,"./plot":397,"./select":398,"./style":400,"./subtypes":401}],390:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80922,7 +82504,7 @@ module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, if(!(opts || {}).noDash) coerce('line.dash'); }; -},{"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"../../lib":168}],378:[function(_dereq_,module,exports){ +},{"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"../../lib":169}],391:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -80992,8 +82574,8 @@ module.exports = function linePoints(d, opts) { function getPt(index) { var di = d[index]; if(!di) return false; - var x = xa.c2p(di.x); - var y = ya.c2p(di.y); + var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x); + var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y); // if non-positive log values, set them VERY far off-screen // so the line looks essentially straight from the previous point. @@ -81383,7 +82965,7 @@ module.exports = function linePoints(d, opts) { return segments; }; -},{"../../constants/numerical":149,"../../lib":168,"./constants":369}],379:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"./constants":381}],392:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81402,7 +82984,7 @@ module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) { if(shape === 'spline') coerce('line.smoothing'); }; -},{}],380:[function(_dereq_,module,exports){ +},{}],393:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81492,7 +83074,7 @@ module.exports = function linkTraces(gd, plotinfo, cdscatter) { return cdscatterSorted; }; -},{}],381:[function(_dereq_,module,exports){ +},{}],394:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81534,7 +83116,7 @@ module.exports = function makeBubbleSizeFn(trace) { }; }; -},{"fast-isnumeric":18}],382:[function(_dereq_,module,exports){ +},{"fast-isnumeric":18}],395:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81552,7 +83134,7 @@ module.exports = { max: 'cmax' }; -},{}],383:[function(_dereq_,module,exports){ +},{}],396:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81633,7 +83215,7 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout } }; -},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"./subtypes":388}],384:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../components/colorscale/defaults":61,"../../components/colorscale/helpers":62,"./subtypes":401}],397:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -81788,7 +83370,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition if(ownFillDir !== 'x' && ownFillDir !== 'y') ownFillDir = ''; // store node for tweaking by selectPoints - if(!plotinfo.isRangePlot) cdscatter[0].node3 = tr; + cdscatter[0][plotinfo.isRangePlot ? 'nodeRangePlot3' : 'node3'] = tr; var prevRevpath = ''; var prevPolygons = []; @@ -82194,7 +83776,7 @@ function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) { }); } -},{"../../components/drawing":72,"../../lib":168,"../../lib/polygon":180,"../../registry":256,"./line_points":378,"./link_traces":380,"./subtypes":388,"d3":16}],385:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"../../lib/polygon":181,"../../registry":258,"./line_points":391,"./link_traces":393,"./subtypes":401,"d3":16}],398:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82248,7 +83830,7 @@ module.exports = function selectPoints(searchInfo, selectionTester) { return selection; }; -},{"./subtypes":388}],386:[function(_dereq_,module,exports){ +},{"./subtypes":401}],399:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82353,7 +83935,7 @@ module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) } }; -},{}],387:[function(_dereq_,module,exports){ +},{}],400:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82369,8 +83951,8 @@ var d3 = _dereq_('d3'); var Drawing = _dereq_('../../components/drawing'); var Registry = _dereq_('../../registry'); -function style(gd, cd) { - var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.scatter'); +function style(gd) { + var s = d3.select(gd).selectAll('g.trace.scatter'); s.style('opacity', function(d) { return d[0].trace.opacity; @@ -82405,16 +83987,15 @@ function styleText(sel, trace, gd) { Drawing.textPointStyle(sel.selectAll('text'), trace, gd); } -function styleOnSelect(gd, cd) { - var s = cd[0].node3; +function styleOnSelect(gd, cd, sel) { var trace = cd[0].trace; if(trace.selectedpoints) { - Drawing.selectedPointStyle(s.selectAll('path.point'), trace); - Drawing.selectedTextStyle(s.selectAll('text'), trace); + Drawing.selectedPointStyle(sel.selectAll('path.point'), trace); + Drawing.selectedTextStyle(sel.selectAll('text'), trace); } else { - stylePoints(s, trace, gd); - styleText(s, trace, gd); + stylePoints(sel, trace, gd); + styleText(sel, trace, gd); } } @@ -82425,7 +84006,7 @@ module.exports = { styleOnSelect: styleOnSelect }; -},{"../../components/drawing":72,"../../registry":256,"d3":16}],388:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../registry":258,"d3":16}],401:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82464,7 +84045,7 @@ module.exports = { } }; -},{"../../lib":168}],389:[function(_dereq_,module,exports){ +},{"../../lib":169}],402:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82494,7 +84075,7 @@ module.exports = function(traceIn, traceOut, layout, coerce, opts) { } }; -},{"../../lib":168}],390:[function(_dereq_,module,exports){ +},{"../../lib":169}],403:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82538,7 +84119,7 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { return len; }; -},{"../../lib":168,"../../registry":256}],391:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../registry":258}],404:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82549,9 +84130,10 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { 'use strict'; -var hovertemplateAttrs = _dereq_('../../components/fx/hovertemplate_attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; var scatterAttrs = _dereq_('../scatter/attributes'); -var plotAttrs = _dereq_('../../plots/attributes'); +var baseAttrs = _dereq_('../../plots/attributes'); var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); var dash = _dereq_('../../components/drawing/attributes').dash; @@ -82588,6 +84170,9 @@ module.exports = { mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}), text: extendFlat({}, scatterAttrs.text, { + }), + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: ['a', 'b', 'c', 'text'] }), hovertext: extendFlat({}, scatterAttrs.hovertext, { @@ -82635,14 +84220,14 @@ module.exports = { selected: scatterAttrs.selected, unselected: scatterAttrs.unselected, - hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { flags: ['a', 'b', 'c', 'text', 'name'] }), hoveron: scatterAttrs.hoveron, hovertemplate: hovertemplateAttrs(), }; -},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../components/fx/hovertemplate_attributes":89,"../../lib/extend":162,"../../plots/attributes":209,"../scatter/attributes":365}],392:[function(_dereq_,module,exports){ +},{"../../components/colorscale/attributes":58,"../../components/drawing/attributes":71,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/template_attributes":253,"../scatter/attributes":377}],405:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82723,7 +84308,7 @@ module.exports = function calc(gd, trace) { return cd; }; -},{"../scatter/arrays_to_calcdata":364,"../scatter/calc":366,"../scatter/calc_selection":367,"../scatter/colorscale_calc":368,"fast-isnumeric":18}],393:[function(_dereq_,module,exports){ +},{"../scatter/arrays_to_calcdata":376,"../scatter/calc":378,"../scatter/calc_selection":379,"../scatter/colorscale_calc":380,"fast-isnumeric":18}],406:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82801,6 +84386,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(subTypes.hasText(traceOut)) { + coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); } @@ -82826,7 +84412,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout Lib.coerceSelectionMarkerOpacity(traceOut, coerce); }; -},{"../../lib":168,"../scatter/constants":369,"../scatter/fillcolor_defaults":373,"../scatter/line_defaults":377,"../scatter/line_shape_defaults":379,"../scatter/marker_defaults":383,"../scatter/subtypes":388,"../scatter/text_defaults":389,"./attributes":391}],394:[function(_dereq_,module,exports){ +},{"../../lib":169,"../scatter/constants":381,"../scatter/fillcolor_defaults":385,"../scatter/line_defaults":390,"../scatter/line_shape_defaults":392,"../scatter/marker_defaults":396,"../scatter/subtypes":401,"../scatter/text_defaults":402,"./attributes":404}],407:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82858,7 +84444,7 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) { return out; }; -},{}],395:[function(_dereq_,module,exports){ +},{}],408:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82867,12 +84453,33 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) { * LICENSE file in the root directory of this source tree. */ +'use strict'; + +var Axes = _dereq_('../../plots/cartesian/axes'); + +module.exports = function formatLabels(cdi, trace, fullLayout) { + var labels = {}; + + var subplot = fullLayout[trace.subplot]._subplot; + labels.aLabel = Axes.tickText(subplot.aaxis, cdi.a, true).text; + labels.bLabel = Axes.tickText(subplot.baxis, cdi.b, true).text; + labels.cLabel = Axes.tickText(subplot.caxis, cdi.c, true).text; + + return labels; +}; + +},{"../../plots/cartesian/axes":213}],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 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); @@ -82901,6 +84508,8 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { } var cdi = newPointData.cd[newPointData.index]; + var trace = newPointData.trace; + var subplot = newPointData.subplot; newPointData.a = cdi.a; newPointData.b = cdi.b; @@ -82908,28 +84517,32 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { newPointData.xLabelVal = undefined; newPointData.yLabelVal = undefined; - // TODO: nice formatting, and label by axis title, for a, b, and c? - var trace = newPointData.trace; - var ternary = newPointData.subplot; + var fullLayout = {}; + fullLayout[trace.subplot] = {_subplot: subplot}; + var labels = trace._module.formatLabels(cdi, trace, fullLayout); + newPointData.aLabel = labels.aLabel; + newPointData.bLabel = labels.bLabel; + newPointData.cLabel = labels.cLabel; + var hoverinfo = cdi.hi || trace.hoverinfo; var text = []; function textPart(ax, val) { - text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); + 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, cdi.a); - if(parts.indexOf('b') !== -1) textPart(ternary.baxis, cdi.b); - if(parts.indexOf('c') !== -1) textPart(ternary.caxis, cdi.c); + if(parts.indexOf('a') !== -1) textPart(subplot.aaxis, newPointData.aLabel); + if(parts.indexOf('b') !== -1) textPart(subplot.baxis, newPointData.bLabel); + if(parts.indexOf('c') !== -1) textPart(subplot.caxis, newPointData.cLabel); } newPointData.extraText = text.join('
    '); newPointData.hovertemplate = trace.hovertemplate; return scatterPointData; }; -},{"../../plots/cartesian/axes":212,"../scatter/hover":375}],396:[function(_dereq_,module,exports){ +},{"../scatter/hover":388}],410:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82944,6 +84557,7 @@ module.exports = { attributes: _dereq_('./attributes'), supplyDefaults: _dereq_('./defaults'), colorbar: _dereq_('../scatter/marker_colorbar'), + formatLabels: _dereq_('./format_labels'), calc: _dereq_('./calc'), plot: _dereq_('./plot'), style: _dereq_('../scatter/style').style, @@ -82962,7 +84576,7 @@ module.exports = { } }; -},{"../../plots/ternary":252,"../scatter/marker_colorbar":382,"../scatter/select":385,"../scatter/style":387,"./attributes":391,"./calc":392,"./defaults":393,"./event_data":394,"./hover":395,"./plot":397}],397:[function(_dereq_,module,exports){ +},{"../../plots/ternary":254,"../scatter/marker_colorbar":395,"../scatter/select":398,"../scatter/style":400,"./attributes":404,"./calc":405,"./defaults":406,"./event_data":407,"./format_labels":408,"./hover":409,"./plot":411}],411:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -82995,7 +84609,7 @@ module.exports = function plot(gd, ternary, moduleCalcData) { scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer); }; -},{"../scatter/plot":384}],398:[function(_dereq_,module,exports){ +},{"../scatter/plot":397}],412:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83194,7 +84808,7 @@ module.exports = { } }; -},{"../../lib/extend":162,"../box/attributes":281}],399:[function(_dereq_,module,exports){ +},{"../../lib/extend":164,"../box/attributes":284}],413:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83370,7 +84984,7 @@ function calcSpan(trace, cdi, valAxis, bandwidth) { return spanOut; } -},{"../../constants/numerical":149,"../../lib":168,"../../plots/cartesian/axes":212,"../box/calc":282,"./helpers":402}],400:[function(_dereq_,module,exports){ +},{"../../constants/numerical":149,"../../lib":169,"../../plots/cartesian/axes":213,"../box/calc":285,"./helpers":416}],414:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83413,7 +85027,7 @@ module.exports = function crossTraceCalc(gd, plotinfo) { } }; -},{"../box/cross_trace_calc":283}],401:[function(_dereq_,module,exports){ +},{"../box/cross_trace_calc":286}],415:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83474,7 +85088,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout if(!meanLineVisible) traceOut.meanline = {visible: false}; }; -},{"../../components/color":51,"../../lib":168,"../box/defaults":284,"./attributes":398}],402:[function(_dereq_,module,exports){ +},{"../../components/color":51,"../../lib":169,"../box/defaults":287,"./attributes":412}],416:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83547,7 +85161,7 @@ exports.getKdeValue = function(calcItem, trace, valueDist) { exports.extractVal = function(o) { return o.v; }; -},{"../../lib":168}],403:[function(_dereq_,module,exports){ +},{"../../lib":169}],417:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83659,7 +85273,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay return closeData; }; -},{"../../lib":168,"../../plots/cartesian/axes":212,"../box/hover":286,"./helpers":402}],404:[function(_dereq_,module,exports){ +},{"../../lib":169,"../../plots/cartesian/axes":213,"../box/hover":289,"./helpers":416}],418:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83693,7 +85307,7 @@ module.exports = { } }; -},{"../../plots/cartesian":223,"../box/defaults":284,"../box/select":291,"../scatter/style":387,"./attributes":398,"./calc":399,"./cross_trace_calc":400,"./defaults":401,"./hover":403,"./layout_attributes":405,"./layout_defaults":406,"./plot":407,"./style":408}],405:[function(_dereq_,module,exports){ +},{"../../plots/cartesian":224,"../box/defaults":287,"../box/select":294,"../scatter/style":400,"./attributes":412,"./calc":413,"./cross_trace_calc":414,"./defaults":415,"./hover":417,"./layout_attributes":419,"./layout_defaults":420,"./plot":421,"./style":422}],419:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83719,7 +85333,7 @@ module.exports = { }) }; -},{"../../lib":168,"../box/layout_attributes":288}],406:[function(_dereq_,module,exports){ +},{"../../lib":169,"../box/layout_attributes":291}],420:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83741,7 +85355,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { boxLayoutDefaults._supply(layoutIn, layoutOut, fullData, coerce, 'violin'); }; -},{"../../lib":168,"../box/layout_defaults":289,"./layout_attributes":405}],407:[function(_dereq_,module,exports){ +},{"../../lib":169,"../box/layout_defaults":292,"./layout_attributes":419}],421:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83772,7 +85386,8 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { connectGaps: true, baseTolerance: 0.75, shape: 'spline', - simplify: true + simplify: true, + linearized: true }); return Drawing.smoothopen(segments[0], 1); } @@ -83782,7 +85397,6 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { var cd0 = cd[0]; var t = cd0.t; var trace = cd0.trace; - if(!plotinfo.isRangePlot) cd0.node3 = plotGroup; if(trace.visible !== true || t.empty) { plotGroup.remove(); @@ -83809,8 +85423,8 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { var pathSel = d3.select(this); var density = d.density; var len = density.length; - var posCenter = d.pos + bPos; - var posCenterPx = posAxis.c2p(posCenter); + var posCenter = posAxis.c2l(d.pos + bPos, true); + var posCenterPx = posAxis.l2p(posCenter); var scale; if(trace.width) { @@ -83830,7 +85444,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { for(i = 0; i < len; i++) { pt = pts[i] = {}; pt[t.posLetter] = posCenter + (density[i].v / scale); - pt[t.valLetter] = density[i].t; + pt[t.valLetter] = valAxis.c2l(density[i].t, true); } pathPos = makePath(pts); } @@ -83840,7 +85454,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { 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; + pt[t.valLetter] = valAxis.c2l(density[i].t, true); } pathNeg = makePath(pts); } @@ -83883,10 +85497,10 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { bPosPxOffset = 0; } else if(hasPositiveSide) { bdPosScaled = [0, bdPos * boxWidth / 2]; - bPosPxOffset = -boxLineWidth; + bPosPxOffset = boxLineWidth * {x: 1, y: -1}[t.posLetter]; } else { bdPosScaled = [bdPos * boxWidth / 2, 0]; - bPosPxOffset = boxLineWidth; + bPosPxOffset = boxLineWidth * {x: -1, y: 1}[t.posLetter]; } // inner box @@ -83931,7 +85545,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { }); }; -},{"../../components/drawing":72,"../../lib":168,"../box/plot":290,"../scatter/line_points":378,"./helpers":402,"d3":16}],408:[function(_dereq_,module,exports){ +},{"../../components/drawing":72,"../../lib":169,"../box/plot":293,"../scatter/line_points":391,"./helpers":416,"d3":16}],422:[function(_dereq_,module,exports){ /** * Copyright 2012-2019, Plotly, Inc. * All rights reserved. @@ -83946,8 +85560,8 @@ var d3 = _dereq_('d3'); var Color = _dereq_('../../components/color'); var stylePoints = _dereq_('../scatter/style').stylePoints; -module.exports = function style(gd, cd) { - var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.violins'); +module.exports = function style(gd) { + var s = d3.select(gd).selectAll('g.trace.violins'); s.style('opacity', function(d) { return d[0].trace.opacity; }); @@ -83986,5 +85600,5 @@ module.exports = function style(gd, cd) { }); }; -},{"../../components/color":51,"../scatter/style":387,"d3":16}]},{},[11])(11) +},{"../../components/color":51,"../scatter/style":400,"d3":16}]},{},[11])(11) }); diff --git a/static/babybuddy/js/graph.js.gz b/static/babybuddy/js/graph.js.gz index f7fa75b6..9da51fa4 100644 Binary files a/static/babybuddy/js/graph.js.gz and b/static/babybuddy/js/graph.js.gz differ diff --git a/static/babybuddy/js/vendor.3aed05379df2.js b/static/babybuddy/js/vendor.3aed05379df2.js deleted file mode 100644 index 7bec0dc7..00000000 --- a/static/babybuddy/js/vendor.3aed05379df2.js +++ /dev/null @@ -1,25030 +0,0 @@ -/*! - * jQuery JavaScript Library v3.4.1 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2019-05-01T21:04Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var document = window.document; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - -var isFunction = function isFunction( obj ) { - - // Support: Chrome <=57, Firefox <=52 - // In some browsers, typeof returns "function" for HTML elements - // (i.e., `typeof document.createElement( "object" ) === "function"`). - // We don't want to classify *any* DOM node as a function. - return typeof obj === "function" && typeof obj.nodeType !== "number"; - }; - - -var isWindow = function isWindow( obj ) { - return obj != null && obj === obj.window; - }; - - - - - var preservedScriptAttributes = { - type: true, - src: true, - nonce: true, - noModule: true - }; - - function DOMEval( code, node, doc ) { - doc = doc || document; - - var i, val, - script = doc.createElement( "script" ); - - script.text = code; - if ( node ) { - for ( i in preservedScriptAttributes ) { - - // Support: Firefox 64+, Edge 18+ - // Some browsers don't support the "nonce" property on scripts. - // On the other hand, just using `getAttribute` is not enough as - // the `nonce` attribute is reset to an empty string whenever it - // becomes browsing-context connected. - // See https://github.com/whatwg/html/issues/2369 - // See https://html.spec.whatwg.org/#nonce-attributes - // The `node.getAttribute` check was added for the sake of - // `jQuery.globalEval` so that it can fake a nonce-containing node - // via an object. - val = node[ i ] || node.getAttribute && node.getAttribute( i ); - if ( val ) { - script.setAttribute( i, val ); - } - } - } - doc.head.appendChild( script ).parentNode.removeChild( script ); - } - - -function toType( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; -} -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var - version = "3.4.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android <=4.0 only - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - copy = options[ name ]; - - // Prevent Object.prototype pollution - // Prevent never-ending loop - if ( name === "__proto__" || target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - src = target[ name ]; - - // Ensure proper type for the source value - if ( copyIsArray && !Array.isArray( src ) ) { - clone = []; - } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { - clone = {}; - } else { - clone = src; - } - copyIsArray = false; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - // Evaluates a script in a global context - globalEval: function( code, options ) { - DOMEval( code, { nonce: options && options.nonce } ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - // Support: Android <=4.0 only - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), -function( i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -} ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = toType( obj ); - - if ( isFunction( obj ) || isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.4 - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://js.foundation/ - * - * Date: 2019-04-08 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - nonnativeSelectorCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - rdescend = new RegExp( whitespace + "|>" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rhtml = /HTML$/i, - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }, - - inDisabledFieldset = addCombinator( - function( elem ) { - return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; - }, - { dir: "parentNode", next: "legend" } - ); - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - - // ID selector - if ( (m = match[1]) ) { - - // Document context - if ( nodeType === 9 ) { - if ( (elem = context.getElementById( m )) ) { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( newContext && (elem = newContext.getElementById( m )) && - contains( context, elem ) && - elem.id === m ) { - - results.push( elem ); - return results; - } - } - - // Type selector - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( (m = match[3]) && support.getElementsByClassName && - context.getElementsByClassName ) { - - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( support.qsa && - !nonnativeSelectorCache[ selector + " " ] && - (!rbuggyQSA || !rbuggyQSA.test( selector )) && - - // Support: IE 8 only - // Exclude object elements - (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) { - - newSelector = selector; - newContext = context; - - // qSA considers elements outside a scoping root when evaluating child or - // descendant combinators, which is not what we want. - // In such cases, we work around the behavior by prefixing every selector in the - // list with an ID selector referencing the scope context. - // Thanks to Andrew Dupont for this technique. - if ( nodeType === 1 && rdescend.test( selector ) ) { - - // Capture the context ID, setting it first if necessary - if ( (nid = context.getAttribute( "id" )) ) { - nid = nid.replace( rcssescape, fcssescape ); - } else { - context.setAttribute( "id", (nid = expando) ); - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[i] = "#" + nid + " " + toSelector( groups[i] ); - } - newSelector = groups.join( "," ); - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - } - - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - nonnativeSelectorCache( selector, true ); - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement("fieldset"); - - try { - return !!fn( el ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - // release memory in IE - el = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11 - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - /* jshint -W018 */ - elem.isDisabled !== !disabled && - inDisabledFieldset( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - var namespace = elem.namespaceURI, - docElem = (elem.ownerDocument || elem).documentElement; - - // Support: IE <=8 - // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes - // https://bugs.jquery.com/ticket/4833 - return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); - - // Support: IE 9-11, Edge - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - if ( preferredDoc !== document && - (subWindow = document.defaultView) && subWindow.top !== subWindow ) { - - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert(function( el ) { - el.className = "i"; - return !el.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( el ) { - el.appendChild( document.createComment("") ); - return !el.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - }); - - // ID filter and find - if ( support.getById ) { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( (elem = elems[i++]) ) { - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( el ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll("[msallowcapture^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push("~="); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push(".#.+[+~]"); - } - }); - - assert(function( el ) { - el.innerHTML = "" + - ""; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement("input"); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll(":enabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll(":disabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( el ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === document ? -1 : - b === document ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - if ( support.matchesSelector && documentIsHTML && - !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch (e) { - nonnativeSelectorCache( expr, true ); - } - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.escape = function( sel ) { - return (sel + "").replace( rcssescape, fcssescape ); -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) { - - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - // Use previously-cached element index if available - if ( useCache ) { - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - uniqueCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - // Don't keep the element (issue #299) - input[0] = null; - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? - argument + length : - argument > length ? - length : - argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { - elem = elem[ dir ] || elem; - } else if ( (oldCache = uniqueCache[ key ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context === document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - if ( !context && elem.ownerDocument !== document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context || document, xml) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( el ) { - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( el ) { - el.innerHTML = ""; - return el.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( el ) { - el.innerHTML = ""; - el.firstChild.setAttribute( "value", "" ); - return el.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( el ) { - return el.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; - -// Deprecated -jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; -jQuery.escapeSelector = Sizzle.escape; - - - - -var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; -}; - - -var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; -}; - - -var rneedsContext = jQuery.expr.match.needsContext; - - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -}; -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Filtered directly for both simple and complex selectors - return jQuery.filter( qualifier, elements, not ); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); -}; - -jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -} ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } -} ); - -function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( typeof elem.contentDocument !== "undefined" ) { - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -} ); -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones -function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && toType( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -function Identity( v ) { - return v; -} -function Thrower( ex ) { - throw ex; -} - -function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } -} - -jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.stackTrace ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the stack, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // rejected_handlers.disable - // fulfilled_handlers.disable - tuples[ 3 - i ][ 3 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock, - - // progress_handlers.lock - tuples[ 0 ][ 3 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the master Deferred - master = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - master.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( master.state() === "pending" || - isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return master.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); - } - - return master.promise(); - } -} ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -jQuery.Deferred.exceptionHook = function( error, stack ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); - } -}; - - - - -jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); -}; - - - - -// The deferred used on DOM ready -var readyList = jQuery.Deferred(); - -jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; -}; - -jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } -} ); - -jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method -function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); -} - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon -if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - -} else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); -} - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( toType( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; -}; - - -// Matches dashed string for camelizing -var rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g; - -// Used by camelCase as callback to replace() -function fcamelCase( all, letter ) { - return letter.toUpperCase(); -} - -// Convert dashed to camelCase; used by the css and data modules -// Support: IE <=9 - 11, Edge 12 - 15 -// Microsoft forgot to hump their vendor prefix (#9572) -function camelCase( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); -} -var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - - - -function Data() { - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; - -Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( camelCase ); - } else { - key = camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } -}; -var dataPriv = new Data(); - -var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - -function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; -} - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } -} ); - -jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } -} ); - - -jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } -} ); - -jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -} ); -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var documentElement = document.documentElement; - - - - var isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ); - }, - composed = { composed: true }; - - // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only - // Check attachment across shadow DOM boundaries when possible (gh-3504) - // Support: iOS 10.0-10.2 only - // Early iOS 10 versions support `attachShadow` but not `getRootNode`, - // leading to errors. We need to check for `getRootNode`. - if ( documentElement.getRootNode ) { - isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ) || - elem.getRootNode( composed ) === elem.ownerDocument; - }; - } -var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - isAttached( elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - -var swap = function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - - - -function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, scale, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = elem.nodeType && - ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Support: Firefox <=54 - // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) - initial = initial / 2; - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - while ( maxIterations-- ) { - - // Evaluate and update our best guess (doubling guesses that zero out). - // Finish if the scale equals or crosses 1 (making the old*new product non-positive). - jQuery.style( elem, prop, initialInUnit + unit ); - if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { - maxIterations = 0; - } - initialInUnit = initialInUnit / scale; - - } - - initialInUnit = initialInUnit * 2; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; -} - - -var defaultDisplayMap = {}; - -function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; -} - -function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; -} - -jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } -} ); -var rcheckableType = ( /^(?:checkbox|radio)$/i ); - -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); - -var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - - - -// We have to close these tags to support XHTML (#13200) -var wrapMap = { - - // Support: IE <=9 only - option: [ 1, "" ], - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting or other required elements. - thead: [ 1, "", "
    " ], - col: [ 2, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - _default: [ 0, "", "" ] -}; - -// Support: IE <=9 only -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (#15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (#12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (#11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; -} )(); - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -// Support: IE <=9 - 11+ -// focus() and blur() are asynchronous, except when they are no-op. -// So expect focus to be synchronous when the element is already active, -// and blur to be synchronous when the element is not already active. -// (focus and blur are always synchronous in other supported browsers, -// this just defines when we can count on it). -function expectSync( elem, type ) { - return ( elem === safeActiveElement() ) === ( type === "focus" ); -} - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = {}; - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - // Make a writable jQuery.Event from the native event object - var event = jQuery.event.fix( nativeEvent ); - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", returnTrue ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. -function leverageNative( el, type, expectSync ) { - - // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add - if ( !expectSync ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var notAsync, result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - // Saved data should be false in such cases, but might be a leftover capture object - // from an async native handler (gh-4350) - if ( !saved.length ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - // Support: IE <=9 - 11+ - // focus() and blur() are asynchronous - notAsync = expectSync( this, type ); - this[ type ](); - result = dataPriv.get( this, type ); - if ( saved !== result || notAsync ) { - dataPriv.set( this, type, false ); - } else { - result = {}; - } - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - return result.value; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering the - // native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved.length ) { - - // ...and capture the result - dataPriv.set( this, type, { - value: jQuery.event.trigger( - - // Support: IE <=9 - 11+ - // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), - saved.slice( 1 ), - this - ) - } ); - - // Abort handling of the native event - event.stopImmediatePropagation(); - } - } - } ); -} - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (#504, #13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - - which: function( event ) { - var button = event.button; - - // Add which for key events - if ( event.which == null && rkeyEvent.test( event.type ) ) { - return event.charCode != null ? event.charCode : event.keyCode; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { - if ( button & 1 ) { - return 1; - } - - if ( button & 2 ) { - return 3; - } - - if ( button & 4 ) { - return 2; - } - - return 0; - } - - return event.which; - } -}, jQuery.event.addProp ); - -jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, expectSync ); - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - delegateType: delegateType - }; -} ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - /* eslint-disable max-len */ - - // See https://github.com/eslint/eslint/issues/3229 - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, - - /* eslint-enable */ - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.access( src ); - pdataCur = dataPriv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - } ); - } - } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html.replace( rxhtmlTag, "<$1>" ); - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (#15098, #14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - -var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (#8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, #12537) - // .css('--customProperty) (#3144) - if ( computed ) { - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined -function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property -function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rcustomProp = /^--/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - -function setPositiveNumber( elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - if ( box === "margin" ) { - delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta; -} - -function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - // Support: IE 9-11 only - // Also use offsetWidth/offsetHeight for when box sizing is unreliable - // We use getClientRects() to check for hidden/disconnected. - // In those cases, the computed value can be trusted to be border-box - if ( ( !support.boxSizingReliable() && isBorderBox || - val === "auto" || - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "gridArea": true, - "gridColumn": true, - "gridColumnEnd": true, - "gridColumnStart": true, - "gridRow": true, - "gridRowEnd": true, - "gridRowStart": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (#7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug #9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (#7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue && type !== false ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - // Use proper attribute retrieval(#12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classes = classesToArray( value ); - - if ( classes.length ) { - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classes = classesToArray( value ); - - if ( classes.length ) { - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) > -1 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - return this.each( function() { - var className, i, self, classNames; - - if ( isValidValue ) { - - // Toggle individual class names - i = 0; - self = jQuery( this ); - classNames = classesToArray( value ); - - while ( ( className = classNames[ i++ ] ) ) { - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (#14686, #14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion - - -support.focusin = "onfocusin" in window; - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} -var location = window.location; - -var nonce = Date.now(); - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; -}; - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ) - .filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ) - .map( function( i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (#10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket #12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - - -jQuery._evalUrl = function( url, options ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (#11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options ); - } - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // #1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see #8605, #14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // #14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( "