From 1cfb852e87968655922446731a2e6d2137459dad Mon Sep 17 00:00:00 2001 From: "Christopher C. Wells" Date: Sat, 9 Nov 2019 05:46:40 -0800 Subject: [PATCH] Update NPM dependencies and static files --- package-lock.json | 516 +- package.json | 6 +- static/babybuddy/js/graph.a7dcc3322745.js | 85457 ++++++++++++++++ static/babybuddy/js/graph.a7dcc3322745.js.gz | Bin 0 -> 660253 bytes static/babybuddy/js/vendor.06e5507afbe2.js | 25042 +++++ static/babybuddy/js/vendor.06e5507afbe2.js.gz | Bin 0 -> 179061 bytes static/babybuddy/js/vendor.js | 40 +- static/babybuddy/js/vendor.js.gz | Bin 179002 -> 179061 bytes static/staticfiles.json | 2 +- 9 files changed, 110833 insertions(+), 230 deletions(-) create mode 100644 static/babybuddy/js/graph.a7dcc3322745.js create mode 100644 static/babybuddy/js/graph.a7dcc3322745.js.gz create mode 100644 static/babybuddy/js/vendor.06e5507afbe2.js create mode 100644 static/babybuddy/js/vendor.06e5507afbe2.js.gz diff --git a/package-lock.json b/package-lock.json index 8b424ea5..f35f2044 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,28 +96,28 @@ "dev": true }, "@nodelib/fs.scandir": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.1.tgz", - "integrity": "sha512-NT/skIZjgotDSiXs0WqYhgcuBKhUMgfekCmCGtkUAiLqZdOnrdjmZr9wRl3ll64J9NF79uZ4fk16Dx0yMc/Xbg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.1", + "@nodelib/fs.stat": "2.0.3", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.1.tgz", - "integrity": "sha512-+RqhBlLn6YRBGOIoVYthsG0J9dfpO79eJyN7BYBkZJtfqrBwf2KK+rD/M/yjZR6WBmIhAgOV7S60eCgaSWtbFw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.2.tgz", - "integrity": "sha512-J/DR3+W12uCzAJkw7niXDcqcKBg6+5G5Q/ZpThpGNzAUz70eOR6RV4XnnSN01qHZiVl0eavoxJsBypQoKsV2QQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.1", + "@nodelib/fs.scandir": "2.1.3", "fastq": "^1.6.0" } }, @@ -203,9 +203,9 @@ "dev": true }, "@types/node": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.2.tgz", - "integrity": "sha512-gojym4tX0FWeV2gsW4Xmzo5wxGjXGm550oVUII7f7G5o4BV6c7DBdiG1RRQd+y1bvqRyYtPfMK85UM95vsapqQ==", + "version": "12.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz", + "integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==", "dev": true }, "a-big-triangle": { @@ -278,6 +278,24 @@ "robust-orientation": "^1.1.3" } }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, "ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", @@ -557,9 +575,9 @@ } }, "array-normalize": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.3.tgz", - "integrity": "sha1-c/uDf0gW7BkVHTxejYU6RZDOAb0=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz", + "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==", "dev": true, "requires": { "array-bounds": "^1.0.0" @@ -944,9 +962,9 @@ "dev": true }, "acorn-jsx": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", - "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", "dev": true }, "chalk": { @@ -1211,6 +1229,12 @@ "uniq": "^1.0.1" } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", @@ -1553,8 +1577,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "country-regex": { "version": "1.1.0", @@ -1780,9 +1803,9 @@ "dev": true }, "d3-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.3.0.tgz", - "integrity": "sha512-NHODMBlj59xPAwl2BDiO2Mog6V+PrGRtBfWKqKRrs9MCqlSkIEb0Z/SfY7jW29ReHTDC/j+vwXhnZcXI3+3fbg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.0.tgz", + "integrity": "sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg==", "dev": true }, "d3-dispatch": { @@ -1967,22 +1990,25 @@ "dev": true }, "del": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-5.0.0.tgz", - "integrity": "sha512-TfU3nUY0WDIhN18eq+pgpbLY9AfL5RfiE9czKaTSolc6aK7qASXfDErvYgjV1UqCR4sNXDoxO0/idPmhDUt2Sg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", + "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", "dev": true, "requires": { - "globby": "^10.0.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "rimraf": "^2.6.3" + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0" }, "dependencies": { "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1993,10 +2019,16 @@ "path-is-absolute": "^1.0.0" } }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", "dev": true, "requires": { "glob": "^7.1.3" @@ -2151,9 +2183,9 @@ } }, "earcut": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.1.5.tgz", - "integrity": "sha512-QFWC7ywTVLtvRAJTVp8ugsuuGQ5mVqNmJ1cRYeLrSHgP3nycr2RHTJob9OtM0v8ujuoKN0NY1a93J/omeTL1PA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.1.tgz", + "integrity": "sha512-5jIMi2RB3HtGPHcYd9Yyl0cczo84y+48lgKPxMijliNQaKAHEZJbdzLmKmdxG/mCdS/YD9DQ1gihL8mxzR0F9w==", "dev": true }, "ecc-jsbn": { @@ -2209,23 +2241,27 @@ } }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", + "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", "dev": true, "requires": { "es-to-primitive": "^1.2.0", "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-keys": "^1.0.12" + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -2424,12 +2460,6 @@ } } }, - "esm": { - "version": "3.0.84", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.0.84.tgz", - "integrity": "sha512-SzSGoZc17S7P+12R9cg21Bdb7eybX25RnIeRZ80xZs+VZ3kdQKzqTp2k4hZJjR7p9l0186TTXSgrxzlMDBktlw==", - "dev": true - }, "espree": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", @@ -2533,6 +2563,23 @@ "homedir-polyfill": "^1.0.1" } }, + "ext": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.2.0.tgz", + "integrity": "sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2676,16 +2723,15 @@ "dev": true }, "fast-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.0.4.tgz", - "integrity": "sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", + "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", "dev": true, "requires": { - "@nodelib/fs.stat": "^2.0.1", - "@nodelib/fs.walk": "^1.2.1", - "glob-parent": "^5.0.0", - "is-glob": "^4.0.1", - "merge2": "^1.2.3", + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", "micromatch": "^4.0.2" }, "dependencies": { @@ -2708,9 +2754,9 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -3731,16 +3777,23 @@ } }, "gl-cone3d": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/gl-cone3d/-/gl-cone3d-1.3.1.tgz", - "integrity": "sha512-ftw75smsDy5nU94susUNimXo8H40BEOVjaFrjT387vP4fJqkSVpzVK7jGrPA8/nSrFCOIQ0msWNYL9MMqQ3hjg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/gl-cone3d/-/gl-cone3d-1.5.1.tgz", + "integrity": "sha512-R8m2lPfVN5ip/IPzykvMNgUUGWTkp9rMuCrVknKIkhjH+gaQeGfwF3+WrB0kwq3FRWvlYWcfdvabv37sZ2rKYA==", "dev": true, "requires": { + "colormap": "^2.3.1", + "gl-buffer": "^2.1.2", + "gl-mat4": "^1.2.0", "gl-shader": "^4.2.1", + "gl-texture2d": "^2.1.0", + "gl-vao": "^1.3.0", "gl-vec3": "^1.1.3", "glsl-inverse": "^1.0.0", "glsl-out-of-range": "^1.0.4", - "glslify": "^7.0.0" + "glsl-specular-cook-torrance": "^2.0.1", + "glslify": "^7.0.0", + "ndarray": "^1.0.18" } }, "gl-constants": { @@ -3905,9 +3958,9 @@ } }, "gl-plot3d": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.2.2.tgz", - "integrity": "sha512-is8RoDVUEbUM7kJ2qjhKJlfGLECH3ML9pTCW1V7ylUdmUACmcZ4lzJrQr/NIRkHC5WcUNOp3QJKPjBND3ngZ2A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.3.0.tgz", + "integrity": "sha512-qg269QiLpaw16d2D5Gz9fa8vsLcA8kbX/cv1u9S7BsH6jD9qGYxsY8iWJ8ea9/68WhPS5En2kUavkXINkmHsOQ==", "dev": true, "requires": { "3d-view": "^2.0.0", @@ -4030,14 +4083,17 @@ } }, "gl-streamtube3d": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/gl-streamtube3d/-/gl-streamtube3d-1.2.1.tgz", - "integrity": "sha512-1aj1noU+jJirl5IwFXk29eDx1nO7PQk4r0UZK3My56J3vDSfRR+IbMq2YBhBkjfCWsKY1nc9ESD8t9EcqZY91w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gl-streamtube3d/-/gl-streamtube3d-1.4.0.tgz", + "integrity": "sha512-WgRtdB77uFCN1lBZ6ogz7VTK4J8WwW5DGHvyB3LaBSZF3t5lf/KWeXPgm+xnNINlOy4JqJIgny+CtzwTHAk3Ew==", "dev": true, "requires": { - "gl-vec3": "^1.0.0", + "gl-cone3d": "^1.5.0", + "gl-vec3": "^1.1.3", + "gl-vec4": "^1.0.1", "glsl-inverse": "^1.0.0", "glsl-out-of-range": "^1.0.4", + "glsl-specular-cook-torrance": "^2.0.1", "glslify": "^7.0.0" } }, @@ -4094,14 +4150,36 @@ }, "dependencies": { "es5-ext": { - "version": "0.10.50", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", - "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "version": "0.10.52", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", + "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", "dev": true, "requires": { "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "^1.0.0" + "es6-symbol": "~3.1.2", + "next-tick": "~1.0.0" + }, + "dependencies": { + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + } } }, "es6-weak-map": { @@ -4276,9 +4354,9 @@ }, "dependencies": { "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -4290,9 +4368,9 @@ } }, "ignore": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.2.tgz", - "integrity": "sha512-vdqWBp7MyzdmHkkRWV5nY+PfGRbYbahfuvsBCh277tq+w9zyNi7h5CYJCK0kmzti9kU+O/cB7sE8HvKv6aXAKQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", "dev": true } } @@ -5037,8 +5115,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -5384,23 +5461,11 @@ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true }, "is-plain-obj": { "version": "1.1.0", @@ -5504,8 +5569,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -5522,8 +5586,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jquery": { "version": "3.4.1", @@ -5794,9 +5857,9 @@ } }, "magic-string": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", - "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.4.tgz", + "integrity": "sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw==", "dev": true, "requires": { "sourcemap-codec": "^1.4.4" @@ -5868,9 +5931,9 @@ } }, "mapbox-gl": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.1.1.tgz", - "integrity": "sha512-i57kASg8J/U/lJzBePyqTP2ImKUcx8FkHyCjb3ssWYaBBXHUeZ4STGXXfU9u1AQU9170PjDIJLubUUB1vLLSBQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.3.2.tgz", + "integrity": "sha512-6Ro7GbTMWxcbc836m6rbBNkesgTncbE1yXWeuHlr89esSqaItKr0+ntOu8rZie3fv+GtitkbODysXzIGCA7G+w==", "dev": true, "requires": { "@mapbox/geojson-rewind": "^0.4.0", @@ -5884,7 +5947,6 @@ "@mapbox/whoots-js": "^3.1.0", "csscolorparser": "~1.0.2", "earcut": "^2.1.5", - "esm": "~3.0.84", "geojson-vt": "^3.2.1", "gl-matrix": "^3.0.0", "grid-index": "^1.1.0", @@ -6040,9 +6102,9 @@ "dev": true }, "merge2": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", - "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", "dev": true }, "micromatch": { @@ -6742,10 +6804,13 @@ } }, "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } }, "pad-left": { "version": "1.0.2", @@ -6881,9 +6946,9 @@ } }, "pbf": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.0.tgz", - "integrity": "sha512-98Eh7rsJNJF/Im6XYMLaOW3cLnNyedlOd6hu3iWMD5I7FZGgpw8yN3vQBrmLbLodu7G784Irb9Qsv2yFrxSAGw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", "dev": true, "requires": { "ieee754": "^1.1.12", @@ -6922,9 +6987,9 @@ "dev": true }, "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", + "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", "dev": true }, "pify": { @@ -6974,37 +7039,18 @@ } }, "plexer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/plexer/-/plexer-1.0.2.tgz", - "integrity": "sha512-YvKSr441x/P9tNuNHUyDWGUSnzsCNXox425j14/3+YcwMyVnJs11qiz/ZWti/y+UNhCFu4SkDSelHYY5hXpifQ==", - "dev": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/plexer/-/plexer-1.0.3.tgz", + "integrity": "sha512-U8iVMgA9Asku2az1HCoYUywL54hypBQ0d2svuDb3aO8MsuvhQKz/I3db2WtZcRQWe42N/3E8Pru/F/WvzIh6UA==", "requires": { - "debug": "2.6.1", "isstream": "^0.1.2", "readable-stream": "^2.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.1.tgz", - "integrity": "sha1-eYVQkLosTjEVzH2HaUkdWPBJE1E=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } } }, "plotly.js": { - "version": "1.49.4", - "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.49.4.tgz", - "integrity": "sha512-yOcA1GKLY6vsGLWYoGa/jWTPXcHPTyTWwkgAhopNtCgxTVexXzKfnnGQ2SslL//+TkpNX8CpCzC88HQVV4p42Q==", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.51.1.tgz", + "integrity": "sha512-EOAP6hne7e8RyH2fo0dyTuRKiAO04TVYECK65EMrK8jnP5AqSbHIypnroNCm/nfYj3sn5DTl+GX5XsJKpyM4Aw==", "dev": true, "requires": { "@plotly/d3-sankey": "0.7.2", @@ -7014,6 +7060,7 @@ "alpha-shape": "^1.0.0", "canvas-fit": "^1.5.0", "color-normalize": "^1.5.0", + "color-rgba": "^2.1.1", "convex-hull": "^1.0.3", "country-regex": "^1.1.0", "d3": "^3.5.12", @@ -7023,7 +7070,7 @@ "delaunay-triangulate": "^1.1.6", "es6-promise": "^3.0.2", "fast-isnumeric": "^1.1.3", - "gl-cone3d": "^1.3.1", + "gl-cone3d": "^1.5.1", "gl-contour2d": "^1.1.6", "gl-error3d": "^1.0.15", "gl-heatmap2d": "^1.0.5", @@ -7031,18 +7078,18 @@ "gl-mat4": "^1.2.0", "gl-mesh3d": "^2.1.1", "gl-plot2d": "^1.4.2", - "gl-plot3d": "^2.2.2", + "gl-plot3d": "^2.3.0", "gl-pointcloud2d": "^1.0.2", "gl-scatter3d": "^1.2.2", "gl-select-box": "^1.0.3", "gl-spikes2d": "^1.0.2", - "gl-streamtube3d": "^1.2.1", + "gl-streamtube3d": "^1.4.0", "gl-surface3d": "^1.4.6", "gl-text": "^1.1.8", "glslify": "^7.0.0", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", - "mapbox-gl": "1.1.1", + "mapbox-gl": "1.3.2", "matrix-camera-controller": "^2.1.3", "mouse-change": "^1.4.0", "mouse-event-offset": "^3.0.2", @@ -7050,13 +7097,13 @@ "ndarray": "^1.0.18", "ndarray-fill": "^1.0.2", "ndarray-homography": "^1.0.0", - "point-cluster": "^3.1.4", + "point-cluster": "^3.1.8", "polybooljs": "^1.2.0", "regl": "^1.3.11", "regl-error2d": "^2.0.8", - "regl-line2d": "^3.0.14", - "regl-scatter2d": "^3.1.5", - "regl-splom": "^1.0.7", + "regl-line2d": "^3.0.15", + "regl-scatter2d": "^3.1.7", + "regl-splom": "^1.0.8", "right-now": "^1.0.0", "robust-orientation": "^1.1.3", "sane-topojson": "^4.0.0", @@ -7128,21 +7175,23 @@ "dev": true }, "point-cluster": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/point-cluster/-/point-cluster-3.1.5.tgz", - "integrity": "sha512-KpVtB1mXDlo6yzv80MA6oUq+1519CMeeUd4PPluM4ZlAQgHi/qeBrLY2G53RLy41kas7XvKol0FM98MSrjNH7Q==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/point-cluster/-/point-cluster-3.1.8.tgz", + "integrity": "sha512-7klIr45dpMeZuqjIK9+qBg3m2IhyZJNJkdqjJFw0Olq75FM8ojrTMjClVUrMjNYRVqtwztxCHH71Fyjhg+YwyQ==", "dev": true, "requires": { "array-bounds": "^1.0.1", - "array-normalize": "^1.1.3", + "array-normalize": "^1.1.4", "binary-search-bounds": "^2.0.4", "bubleify": "^1.1.0", "clamp": "^1.0.1", + "defined": "^1.0.0", "dtype": "^2.0.0", - "flatten-vertex-data": "^1.0.0", + "flatten-vertex-data": "^1.0.2", "is-obj": "^1.0.1", "math-log2": "^1.0.1", - "parse-rect": "^1.2.0" + "parse-rect": "^1.2.0", + "pick-by-alias": "^1.2.0" } }, "point-in-big-polygon": { @@ -7181,9 +7230,9 @@ } }, "popper.js": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", - "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz", + "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw==", "dev": true }, "posix-character-classes": { @@ -7294,9 +7343,9 @@ }, "dependencies": { "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", "dev": true } } @@ -7438,7 +7487,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7452,8 +7500,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" } } }, @@ -7558,9 +7605,9 @@ "dev": true }, "regexpu-core": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.5.tgz", - "integrity": "sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", + "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", "dev": true, "requires": { "regenerate": "^1.4.0", @@ -7572,9 +7619,9 @@ } }, "regjsgen": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", - "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", "dev": true }, "regjsparser": { @@ -7587,9 +7634,9 @@ } }, "regl": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.11.tgz", - "integrity": "sha512-tmt6CRhRqbcsYDWNwv+iG7GGOXdgoOBC7lKzoPMgnzpt3WKBQ3c8i7AxgbvTRZzty29hrW92fAJeZkPFQehfWA==", + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/regl/-/regl-1.3.13.tgz", + "integrity": "sha512-TTiCabJbbUykCL4otjqOvKqDFJhvJOT7xB51JxcDeSHGrEJl1zz4RthPcoOogqfuR3ECN4Te790DfHCXzli5WQ==", "dev": true }, "regl-error2d": { @@ -7609,13 +7656,13 @@ } }, "regl-line2d": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.0.14.tgz", - "integrity": "sha512-F5Ru1Bugi6Xkk2JJ4EuzAybuL99CtnAr6VIrJVJdsaFzWmI9GfPFtwbNZROeOrXXX7yElyc0HQsQDJaNpSeWmg==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.0.15.tgz", + "integrity": "sha512-RuQbg9iZ6MyuInG8izF6zjQ/2g4qL6sg1egiuFalWzaGSvuve/IWBsIcqKTlwpiEsRt9b4cHu9NYs2fLt1gYJw==", "dev": true, "requires": { "array-bounds": "^1.0.1", - "array-normalize": "^1.1.3", + "array-normalize": "^1.1.4", "bubleify": "^1.2.0", "color-normalize": "^1.5.0", "earcut": "^2.1.5", @@ -7629,14 +7676,36 @@ }, "dependencies": { "es5-ext": { - "version": "0.10.50", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", - "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "version": "0.10.52", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", + "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", "dev": true, "requires": { "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "^1.0.0" + "es6-symbol": "~3.1.2", + "next-tick": "~1.0.0" + }, + "dependencies": { + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + } } }, "es6-weak-map": { @@ -7654,9 +7723,9 @@ } }, "regl-scatter2d": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.1.5.tgz", - "integrity": "sha512-VCmASgrNIQXzDxmTLpLA4MAlbi+kdjKcVR9XugmFCTnWY7zytIhuMyIoPxinpaejGXzsC0Lq5oKvOnWFMQFGng==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.1.7.tgz", + "integrity": "sha512-FWw1hMsQrV3Y0zMU8YOytGjwSBuV3V58t8GR/mhlSL2S04jXLK1m2eAa/rDP3SpvMDkdVEr744PPDeHwsZVUhA==", "dev": true, "requires": { "array-range": "^1.0.1", @@ -7672,15 +7741,15 @@ "object-assign": "^4.1.1", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", - "point-cluster": "^3.1.5", + "point-cluster": "^3.1.8", "to-float32": "^1.0.1", "update-diff": "^1.1.0" } }, "regl-splom": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.7.tgz", - "integrity": "sha512-17ltp68/pCMFOU2gSIIFRTRMmsCRpDFzPUF9jkZhT8IDimI83jkU/2nP4vAHTIfd+HZ/Ip+eFrNx2aKV9FMDwQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.8.tgz", + "integrity": "sha512-4GQTgcArCbGLsXhgalWVBxeW7OXllnu+Gvil/4SbQQmtiqLCl+xgF79pISKY9mLXTlobxiX7cVKdjGjp25559A==", "dev": true, "requires": { "array-bounds": "^1.0.1", @@ -7692,7 +7761,7 @@ "left-pad": "^1.3.0", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", - "point-cluster": "^3.1.4", + "point-cluster": "^3.1.8", "raf": "^3.4.1", "regl-scatter2d": "^3.1.2" } @@ -8048,8 +8117,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -8794,11 +8862,30 @@ "function-bind": "^1.0.2" } }, + "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==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "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==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -9025,9 +9112,9 @@ }, "dependencies": { "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -9360,6 +9447,12 @@ "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q=", "dev": true }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -9619,8 +9712,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-copy": { "version": "1.1.1", diff --git a/package.json b/package.json index 513f1b85..4a0b6bfa 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "devDependencies": { "bootstrap": "4.3.1", - "del": "^5.0.0", + "del": "^5.1.0", "font-awesome": "^4.7.0", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", @@ -21,8 +21,8 @@ "gulp-uglify": "^3.0.2", "jquery": "^3.4.1", "moment": "^2.24.0", - "plotly.js": "^1.49.4", - "popper.js": "^1.15.0", + "plotly.js": "^1.51.1", + "popper.js": "^1.16.0", "pump": "^3.0.0", "tempusdominus-bootstrap-4": "^5.1.2", "tempusdominus-core": "^5.0.3" diff --git a/static/babybuddy/js/graph.a7dcc3322745.js b/static/babybuddy/js/graph.a7dcc3322745.js new file mode 100644 index 00000000..891482bb --- /dev/null +++ b/static/babybuddy/js/graph.a7dcc3322745.js @@ -0,0 +1,85457 @@ +/** +* plotly.js (cartesian) v1.51.1 +* 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":275}],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":289}],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":309}],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":325}],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":343}],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":349}],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":353}],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":360}],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_('./image'), + _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,"./image":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":369}],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":407}],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":415}],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":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. +* +* This 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":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":169,"../../plot_api/plot_template":203,"../../registry":258}],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":169,"../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":192,"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":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. +* +* This 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; + } + + if(annotationIsOffscreen) { + annTextGroupInner.remove(); + return; + } + + var xShift = 0; + var yShift = 0; + + if(options.align !== 'left') { + xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1); + } + if(options.valign !== 'top') { + yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1); + } + + if(hasMathjax) { + mathjaxGroup.select('svg').attr({ + x: borderfull + xShift - 1, + y: borderfull + yShift + }) + .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd); + } else { + var texty = borderfull + yShift - anntextBB.top; + var textx = borderfull + xShift - anntextBB.left; + + annText.call(svgTextUtils.positionText, textx, texty) + .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd); + } + + annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull, + annWidth, annHeight); + + annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2, + outerWidth - borderwidth, outerHeight - borderwidth); + + annTextGroupInner.call(Drawing.setTranslate, + Math.round(annPosPx.x.text - outerWidth / 2), + Math.round(annPosPx.y.text - outerHeight / 2)); + + /* + * rotate text and background + * we already calculated the text center position *as rotated* + * because we needed that for autoranging anyway, so now whether + * we have an arrow or not, we rotate about the text center. + */ + annTextGroup.attr({transform: 'rotate(' + textangle + ',' + + annPosPx.x.text + ',' + annPosPx.y.text + ')'}); + + /* + * add the arrow + * uses options[arrowwidth,arrowcolor,arrowhead] for styling + * dx and dy are normally zero, but when you are dragging the textbox + * while the head stays put, dx and dy are the pixel offsets + */ + var drawArrow = function(dx, dy) { + annGroup + .selectAll('.annotation-arrow-g') + .remove(); + + var headX = annPosPx.x.head; + var headY = annPosPx.y.head; + var tailX = annPosPx.x.tail + dx; + var tailY = annPosPx.y.tail + dy; + var textX = annPosPx.x.text + dx; + var textY = annPosPx.y.text + dy; + + // find the edge of the text box, where we'll start the arrow: + // create transform matrix to rotate the text box corners + var transform = Lib.rotationXYMatrix(textangle, textX, textY); + var applyTransform = Lib.apply2DTransform(transform); + var applyTransform2 = Lib.apply2DTransform2(transform); + + // calculate and transform bounding box + var width = +annTextBG.attr('width'); + var height = +annTextBG.attr('height'); + var xLeft = textX - 0.5 * width; + var xRight = xLeft + width; + var yTop = textY - 0.5 * height; + var yBottom = yTop + height; + var edges = [ + [xLeft, yTop, xLeft, yBottom], + [xLeft, yBottom, xRight, yBottom], + [xRight, yBottom, xRight, yTop], + [xRight, yTop, xLeft, yTop] + ].map(applyTransform2); + + // Remove the line if it ends inside the box. Use ray + // casting for rotated boxes: see which edges intersect a + // line from the arrowhead to far away and reduce with xor + // to get the parity of the number of intersections. + if(edges.reduce(function(a, x) { + return a ^ + !!Lib.segmentsIntersect(headX, headY, headX + 1e6, headY + 1e6, + x[0], x[1], x[2], x[3]); + }, false)) { + // no line or arrow - so quit drawArrow now + return; + } + + edges.forEach(function(x) { + var p = Lib.segmentsIntersect(tailX, tailY, headX, headY, + x[0], x[1], x[2], x[3]); + if(p) { + tailX = p.x; + tailY = p.y; + } + }); + + var strokewidth = options.arrowwidth; + var arrowColor = options.arrowcolor; + var arrowSide = options.arrowside; + + var arrowGroup = annGroup.append('g') + .style({opacity: Color.opacity(arrowColor)}) + .classed('annotation-arrow-g', true); + + var arrow = arrowGroup.append('path') + .attr('d', 'M' + tailX + ',' + tailY + 'L' + headX + ',' + headY) + .style('stroke-width', strokewidth + 'px') + .call(Color.stroke, Color.rgb(arrowColor)); + + drawArrowHead(arrow, arrowSide, options); + + // the arrow dragger is a small square right at the head, then a line to the tail, + // all expanded by a stroke width of 6px plus the arrow line width + if(edits.annotationPosition && arrow.node().parentNode && !subplotId) { + var arrowDragHeadX = headX; + var arrowDragHeadY = headY; + if(options.standoff) { + var arrowLength = Math.sqrt(Math.pow(headX - tailX, 2) + Math.pow(headY - tailY, 2)); + arrowDragHeadX += options.standoff * (tailX - headX) / arrowLength; + arrowDragHeadY += options.standoff * (tailY - headY) / arrowLength; + } + var arrowDrag = arrowGroup.append('path') + .classed('annotation-arrow', true) + .classed('anndrag', true) + .classed('cursor-move', true) + .attr({ + d: 'M3,3H-3V-3H3ZM0,0L' + (tailX - arrowDragHeadX) + ',' + (tailY - arrowDragHeadY), + transform: '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":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. +* +* This 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":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. +* +* This 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":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":169,"../../plots/cartesian/axes":213}],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":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. +* +* This 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":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":169,"../../registry":258,"./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":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. +* +* 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":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. +* +* This 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; + + // stash mocked axis for contour label formatting + var ax = opts._axis = mockColorBarAxis(gd, opts, zrange); + + // position can't go in through supplyDefaults + // because that restricts it to [0,1] + ax.position = opts.x + xpadFrac + thickFrac; + + if(['top', 'bottom'].indexOf(titleSide) !== -1) { + ax.title.side = titleSide; + ax.titlex = opts.x + xpadFrac; + ax.titley = yBottomFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac); + } + + if(line.color && opts.tickmode === 'auto') { + ax.tickmode = 'linear'; + ax.tick0 = levelsIn.start; + var dtick = levelsIn.size; + // expand if too many contours, so we don't get too many ticks + var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1; + var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick); + if(dtFactor > 1) { + var dtexp = Math.pow(10, Math.floor(Math.log(dtFactor) / Math.LN10)); + dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]); + // if the contours are at round multiples, reset tick0 + // so they're still at round multiples. Otherwise, + // keep the first label on the first contour level + if((Math.abs(levelsIn.start) / levelsIn.size + 1e-6) % 1 < 2e-6) { + ax.tick0 = 0; + } + } + ax.dtick = dtick; + } + + // set domain after init, because we may want to + // allow it outside [0,1] + ax.domain = [ + yBottomFrac + ypadFrac, + yBottomFrac + lenFrac - ypadFrac + ]; + + ax.setScale(); + + g.attr('transform', '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":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. +* +* This 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}],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":184,"../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":169,"./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 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":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":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. +* +* This 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, colorKey) { + var container = containerStr ? + Lib.nestedProperty(trace, containerStr).get() || {} : + trace; + var color = container[colorKey || 'color']; + + var isArrayWithOneNumber = false; + if(Lib.isArrayOrTypedArray(color)) { + for(var i = 0; i < color.length; i++) { + if(isNumeric(color[i])) { + isArrayWithOneNumber = true; + break; + } + } + } + + return ( + Lib.isPlainObject(container) && ( + isArrayWithOneNumber || + container.showscale === true || + (isNumeric(container.cmin) && isNumeric(container.cmax)) || + isValidScale(container.colorscale) || + Lib.isPlainObject(container.colorbar) + ) + ); +} + +var constantAttrs = ['showscale', 'autocolorscale', 'colorscale', 'reversescale', 'colorbar']; +var letterAttrs = ['min', 'max', 'mid', 'auto']; + +/** + * Extract 'c' / 'z', trace / color axis colorscale options + * + * Note that it would be nice to replace all z* with c* equivalents in 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":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":164,"./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":169,"../../plot_api/plot_template":203,"./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":169}],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 dragElement = module.exports = {}; + +dragElement.align = _dereq_('./align'); +dragElement.getCursor = _dereq_('./cursor'); + +var unhover = _dereq_('./unhover'); +dragElement.unhover = unhover.wrapped; +dragElement.unhoverRaw = unhover.raw; + +/** + * Abstracts click & drag interactions + * + * During the interaction, a "coverSlip" element - a transparent + * div covering the whole page - is created, which has two key effects: + * - Lets you drag beyond the boundaries of the plot itself without + * dropping (but if you drag all the way out of the browser window the + * interaction will end) + * - Freezes the cursor: whatever mouse cursor the drag element had when the + * interaction started gets copied to the coverSlip for use until mouseup + * + * If the user executes a drag bigger than MINDRAG, callbacks will fire as: + * prepFn, moveFn (1 or more times), doneFn + * If the user does not drag enough, prepFn and clickFn will fire. + * + * Note: If you cancel contextmenu, clickFn will fire even with a right click + * (unlike native events) so you'll get a `plotly_click` event. Cancel context eg: + * gd.addEventListener('contextmenu', function(e) { e.preventDefault(); }); + * TODO: we should probably turn this into a `config` parameter, so we can fix it + * such that if you *don't* cancel contextmenu, we can prevent partial drags, which + * put you in a weird state. + * + * If the user clicks multiple times quickly, clickFn will fire each time + * but numClicks will increase to help you recognize doubleclicks. + * + * @param {object} options with keys: + * element (required) the DOM element to drag + * prepFn (optional) function(event, startX, startY) + * executed on mousedown + * startX and startY are the clientX and clientY pixel position + * of the mousedown event + * moveFn (optional) function(dx, dy) + * executed on move, ONLY after we've exceeded MINDRAG + * (we keep executing moveFn if you move back to where you started) + * dx and dy are the net pixel offset of the drag, + * dragged is true/false, has the mouse moved enough to + * constitute a drag + * doneFn (optional) function(e) + * executed on mouseup, ONLY if we exceeded MINDRAG (so you can be + * sure that moveFn has been called at least once) + * numClicks is how many clicks we've registered within + * a doubleclick time + * e is the original mouseup event + * clickFn (optional) function(numClicks, e) + * executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn + * has not been called at all) + * numClicks is how many clicks we've registered within + * a doubleclick time + * e is the original mousedown event + * clampFn (optional, function(dx, dy) return [dx2, dy2]) + * Provide custom clamping function for small displacements. + * By default, clamping is done using `minDrag` to x and y displacements + * independently. + */ +dragElement.init = function init(options) { + var gd = options.gd; + var numClicks = 1; + var doubleClickDelay = gd._context.doubleClickDelay; + var element = options.element; + + var startX, + startY, + newMouseDownTime, + cursor, + dragCover, + initialEvent, + initialTarget, + rightClick; + + if(!gd._mouseDownTime) gd._mouseDownTime = 0; + + element.style.pointerEvents = 'all'; + + element.onmousedown = onStart; + + if(!supportsPassive) { + element.ontouchstart = onStart; + } else { + if(element._ontouchstart) { + element.removeEventListener('touchstart', element._ontouchstart); + } + element._ontouchstart = onStart; + element.addEventListener('touchstart', onStart, {passive: false}); + } + + function _clampFn(dx, dy, minDrag) { + if(Math.abs(dx) < minDrag) dx = 0; + if(Math.abs(dy) < minDrag) dy = 0; + return [dx, dy]; + } + + var clampFn = options.clampFn || _clampFn; + + function onStart(e) { + // make dragging and dragged into properties of gd + // so that others can look at and modify them + gd._dragged = false; + gd._dragging = true; + var offset = pointerOffset(e); + startX = offset[0]; + startY = offset[1]; + initialTarget = e.target; + initialEvent = e; + rightClick = e.buttons === 2 || e.ctrlKey; + + // fix Fx.hover for touch events + if(typeof e.clientX === 'undefined' && typeof e.clientY === 'undefined') { + e.clientX = startX; + e.clientY = startY; + } + + newMouseDownTime = (new Date()).getTime(); + if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) { + // in a click train + numClicks += 1; + } else { + // new click train + numClicks = 1; + gd._mouseDownTime = newMouseDownTime; + } + + if(options.prepFn) options.prepFn(e, startX, startY); + + if(hasHover && !rightClick) { + dragCover = coverSlip(); + dragCover.style.cursor = window.getComputedStyle(element).cursor; + } else if(!hasHover) { + // document acts as a dragcover for mobile, bc we can't create dragcover dynamically + dragCover = document; + cursor = window.getComputedStyle(document.documentElement).cursor; + document.documentElement.style.cursor = window.getComputedStyle(element).cursor; + } + + document.addEventListener('mouseup', onDone); + document.addEventListener('touchend', onDone); + + if(options.dragmode !== false) { + e.preventDefault(); + document.addEventListener('mousemove', onMove); + document.addEventListener('touchmove', onMove, {passive: false}); + } + + return; + } + + function onMove(e) { + e.preventDefault(); + + var offset = pointerOffset(e); + var minDrag = options.minDrag || constants.MINDRAG; + var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag); + var dx = dxdy[0]; + var dy = dxdy[1]; + + if(dx || dy) { + gd._dragged = true; + dragElement.unhover(gd); + } + + 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":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/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":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 = {}; +var appendArrayPointValue = _dereq_('../fx/helpers').appendArrayPointValue; + +// ----------------------------------------------------- +// 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', (d.isBlank ? 0 : lineWidth) + 'px'); + + var markerGradient = marker.gradient; + + var gradientType = d.mgt; + if(gradientType) perPointGradient = true; + else gradientType = markerGradient && markerGradient.type; + + // for legend - arrays will propagate through here, but we don't need + // to treat it as per-point. + if(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, inLegend) { + 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; + s.each(function(d) { + var p = d3.select(this); + var text = Lib.extractOption(d, trace, template ? 'txt' : 'tx', template ? 'texttemplate' : '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 || {}); + } + + 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":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. +* +* 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":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. +* +* This 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":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":399,"../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":164,"../../plots/font_attributes":239,"./layout_attributes":90}],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":169,"../../registry":258}],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":258,"./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":169,"./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":169}],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]; + + if(plots[spId]) { + // 'cartesian' case + supportsCompare = true; + xaArray[i] = plots[spId].xaxis; + yaArray[i] = plots[spId].yaxis; + } else if(fullLayout[spId] && fullLayout[spId]._subplot) { + // other subplot types + var _subplot = fullLayout[spId]._subplot; + xaArray[i] = _subplot.xaxis; + yaArray[i] = _subplot.yaxis; + } else { + Lib.warn('Unrecognized subplot: ' + spId); + return; + } + } + + var hovermode = evt.hovermode || fullLayout.hovermode; + + if(hovermode && !supportsCompare) hovermode = 'closest'; + + if(['x', 'y', 'closest'].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(gd, spikePoints, spikelineOpts); + } + } + return result; + } + + if(hasCartesian) { + if(spikesChanged(oldspikepoints)) { + createSpikelines(gd, 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); + var commonLabelFont = { + family: commonLabelOpts.font.family || fontFamily, + size: commonLabelOpts.font.size || fontSize, + color: commonLabelOpts.font.color || contrastColor + }; + + lpath.style({ + fill: commonBgColor, + stroke: commonStroke + }); + + ltext.text(t0) + .call(Drawing.font, commonLabelFont) + .call(svgTextUtils.positionText, 0, 0) + .call(svgTextUtils.convertToTspans, gd); + + label.attr('transform', ''); + + var tbb = ltext.node().getBoundingClientRect(); + var lx, ly; + + if(hovermode === 'x') { + var topsign = xa.side === 'top' ? '-' : ''; + + ltext.attr('text-anchor', 'middle') + .call(svgTextUtils.positionText, 0, (xa.side === 'top' ? + (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) : + (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD))); + + lx = xa._offset + (c0.x0 + c0.x1) / 2; + ly = ya._offset + (xa.side === 'top' ? 0 : ya._length); + + var halfWidth = tbb.width / 2 + HOVERTEXTPAD; + + if(lx < halfWidth) { + lx = halfWidth; + + lpath.attr('d', 'M-' + (halfWidth - HOVERARROWSIZE) + ',0' + + 'L-' + (halfWidth - HOVERARROWSIZE * 2) + ',' + topsign + HOVERARROWSIZE + + 'H' + (HOVERTEXTPAD + tbb.width / 2) + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + halfWidth + + 'V' + topsign + HOVERARROWSIZE + + 'Z'); + } else if(lx > (fullLayout.width - halfWidth)) { + lx = fullLayout.width - halfWidth; + + lpath.attr('d', 'M' + (halfWidth - HOVERARROWSIZE) + ',0' + + 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + halfWidth + + 'V' + topsign + HOVERARROWSIZE + + 'H' + (halfWidth - HOVERARROWSIZE * 2) + 'Z'); + } else { + lpath.attr('d', 'M0,0' + + 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE + + 'H' + (HOVERTEXTPAD + tbb.width / 2) + + 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) + + 'H-' + (HOVERTEXTPAD + tbb.width / 2) + + 'V' + topsign + HOVERARROWSIZE + + 'H-' + HOVERARROWSIZE + 'Z'); + } + } else { + var anchor; + var sgn; + var leftsign; + if(ya.side === 'right') { + anchor = 'start'; + sgn = 1; + leftsign = ''; + lx = xa._offset + xa._length; + } else { + anchor = 'end'; + sgn = -1; + leftsign = '-'; + lx = xa._offset; + } + + ly = ya._offset + (c0.y0 + c0.y1) / 2; + + ltext.attr('text-anchor', anchor); + + lpath.attr('d', 'M0,0' + + 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE + + 'V' + (HOVERTEXTPAD + tbb.height / 2) + + 'h' + leftsign + (HOVERTEXTPAD * 2 + tbb.width) + + 'V-' + (HOVERTEXTPAD + tbb.height / 2) + + 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z'); + + var halfHeight = tbb.height / 2; + var lty = outerTop - tbb.top - halfHeight; + var clipId = 'clip' + fullLayout._uid + 'commonlabel' + ya._id; + var clipPath; + + if(lx < (tbb.width + 2 * HOVERTEXTPAD + HOVERARROWSIZE)) { + clipPath = 'M-' + (HOVERARROWSIZE + HOVERTEXTPAD) + '-' + halfHeight + + 'h-' + (tbb.width - HOVERTEXTPAD) + + 'V' + halfHeight + + 'h' + (tbb.width - HOVERTEXTPAD) + 'Z'; + + var ltx = tbb.width - lx + HOVERTEXTPAD; + svgTextUtils.positionText(ltext, ltx, lty); + + // shift each line (except the longest) so that start-of-line + // is always visible + if(anchor === 'end') { + ltext.selectAll('tspan').each(function() { + var s = d3.select(this); + var dummy = Drawing.tester.append('text') + .text(s.text()) + .call(Drawing.font, commonLabelFont); + var dummyBB = dummy.node().getBoundingClientRect(); + if(dummyBB.width < 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) { + 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(gd, closestPoints, opts) { + var container = opts.container; + var fullLayout = opts.fullLayout; + var gs = fullLayout._size; + var evt = opts.event; + var showY = !!closestPoints.hLinePoint; + var showX = !!closestPoints.vLinePoint; + + var xa, ya; + + // Remove old spikeline items + container.selectAll('.spikeline').remove(); + + if(!(showX || showY)) return; + + var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor); + + // Horizontal line (to y-axis) + if(showY) { + var hLinePoint = closestPoints.hLinePoint; + var hLinePointX, hLinePointY; + + xa = hLinePoint && hLinePoint.xa; + ya = hLinePoint && hLinePoint.ya; + var ySnap = ya.spikesnap; + + if(ySnap === 'cursor') { + hLinePointX = evt.pointerX; + hLinePointY = evt.pointerY; + } else { + hLinePointX = xa._offset + hLinePoint.x; + hLinePointY = ya._offset + hLinePoint.y; + } + var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ? + Color.contrast(contrastColor) : hLinePoint.color; + var yMode = ya.spikemode; + var yThickness = ya.spikethickness; + var yColor = ya.spikecolor || dfltHLineColor; + var xEdge = Axes.getPxPosition(gd, ya); + var xBase, xEndSpike; + + if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) { + if(yMode.indexOf('toaxis') !== -1) { + xBase = xEdge; + xEndSpike = hLinePointX; + } + if(yMode.indexOf('across') !== -1) { + var xAcross0 = ya._counterDomainMin; + var xAcross1 = ya._counterDomainMax; + if(ya.anchor === 'free') { + xAcross0 = Math.min(xAcross0, ya.position); + xAcross1 = Math.max(xAcross1, ya.position); + } + xBase = gs.l + xAcross0 * gs.w; + xEndSpike = gs.l + xAcross1 * gs.w; + } + + // Foreground horizontal line (to y-axis) + container.insert('line', ':first-child') + .attr({ + x1: xBase, + x2: xEndSpike, + y1: hLinePointY, + y2: hLinePointY, + 'stroke-width': yThickness, + stroke: yColor, + 'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness) + }) + .classed('spikeline', true) + .classed('crisp', true); + + // Background horizontal Line (to y-axis) + container.insert('line', ':first-child') + .attr({ + x1: xBase, + x2: xEndSpike, + y1: hLinePointY, + y2: hLinePointY, + 'stroke-width': yThickness + 2, + stroke: contrastColor + }) + .classed('spikeline', true) + .classed('crisp', true); + } + // Y axis marker + if(yMode.indexOf('marker') !== -1) { + container.insert('circle', ':first-child') + .attr({ + cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness), + cy: hLinePointY, + r: yThickness, + fill: yColor + }) + .classed('spikeline', true); + } + } + + if(showX) { + var vLinePoint = closestPoints.vLinePoint; + var vLinePointX, vLinePointY; + + xa = vLinePoint && vLinePoint.xa; + ya = vLinePoint && vLinePoint.ya; + var xSnap = xa.spikesnap; + + if(xSnap === 'cursor') { + vLinePointX = evt.pointerX; + vLinePointY = evt.pointerY; + } else { + vLinePointX = xa._offset + vLinePoint.x; + vLinePointY = ya._offset + vLinePoint.y; + } + var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ? + Color.contrast(contrastColor) : vLinePoint.color; + var xMode = xa.spikemode; + var xThickness = xa.spikethickness; + var xColor = xa.spikecolor || dfltVLineColor; + var yEdge = Axes.getPxPosition(gd, xa); + var yBase, yEndSpike; + + if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) { + if(xMode.indexOf('toaxis') !== -1) { + yBase = yEdge; + yEndSpike = vLinePointY; + } + if(xMode.indexOf('across') !== -1) { + var yAcross0 = xa._counterDomainMin; + var yAcross1 = xa._counterDomainMax; + if(xa.anchor === 'free') { + yAcross0 = Math.min(yAcross0, xa.position); + yAcross1 = Math.max(yAcross1, xa.position); + } + yBase = gs.t + (1 - yAcross1) * gs.h; + yEndSpike = gs.t + (1 - yAcross0) * gs.h; + } + + // Foreground vertical line (to x-axis) + container.insert('line', ':first-child') + .attr({ + x1: vLinePointX, + x2: vLinePointX, + y1: yBase, + y2: yEndSpike, + 'stroke-width': xThickness, + stroke: xColor, + 'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness) + }) + .classed('spikeline', true) + .classed('crisp', true); + + // Background vertical line (to x-axis) + container.insert('line', ':first-child') + .attr({ + x1: vLinePointX, + x2: vLinePointX, + y1: yBase, + y2: yEndSpike, + 'stroke-width': xThickness + 2, + stroke: contrastColor + }) + .classed('spikeline', true) + .classed('crisp', true); + } + + // X axis marker + if(xMode.indexOf('marker') !== -1) { + container.insert('circle', ':first-child') + .attr({ + cx: vLinePointX, + cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness), + r: xThickness, + fill: xColor + }) + .classed('spikeline', true); + } + } +} + +function hoverChanged(gd, evt, oldhoverdata) { + // don't emit any events if nothing changed + if(!oldhoverdata || oldhoverdata.length !== gd._hoverdata.length) return true; + + for(var i = oldhoverdata.length - 1; i >= 0; i--) { + var oldPt = oldhoverdata[i]; + var newPt = gd._hoverdata[i]; + + if(oldPt.curveNumber !== newPt.curveNumber || + String(oldPt.pointNumber) !== String(newPt.pointNumber) || + String(oldPt.pointNumbers) !== String(newPt.pointNumbers) + ) { + return true; + } + } + return false; +} + +function spikesChanged(gd, oldspikepoints) { + // don't relayout the plot because of new spikelines if spikelines points didn't change + if(!oldspikepoints) return true; + if(oldspikepoints.vLinePoint !== gd._spikepoints.vLinePoint || + oldspikepoints.hLinePoint !== gd._spikepoints.hLinePoint + ) return true; + return false; +} + +function plainText(s, len) { + return svgTextUtils.plainText(s || '', { + len: len, + allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em'] + }); +} + +},{"../../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. +* +* This 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}],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":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. +* +* This 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":84}],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":88,"./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":18}],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._imgSrc === d.source) { + return; + } + + thisImage.attr('xmlns', xmlnsNamespaces.svg); + + if(d.source && d.source.slice(0, 5) === 'data:') { + thisImage.attr('xlink:href', d.source); + this._imgSrc = d.source; + } else { + var imagePromise = new Promise(function(resolve) { + var img = new Image(); + this.img = img; + + // If not set, a `tainted canvas` error is thrown + img.setAttribute('crossOrigin', 'anonymous'); + img.onerror = errorHandler; + img.onload = function() { + var canvas = document.createElement('canvas'); + canvas.width = this.width; + canvas.height = this.height; + + var ctx = canvas.getContext('2d'); + ctx.drawImage(this, 0, 0); + + var dataURL = canvas.toDataURL('image/png'); + + thisImage.attr('xlink:href', dataURL); + + // resolve promise in onload handler instead of on 'load' to support IE11 + // see https://github.com/plotly/plotly.js/issues/1685 + // for more details + resolve(); + }; + + thisImage.on('error', errorHandler); + + img.src = d.source; + this._imgSrc = d.source; + + function errorHandler() { + thisImage.remove(); + resolve(); + } + }.bind(this)); + + gd._promises.push(imagePromise); + } + } + + function applyAttributes(d) { + var thisImage = d3.select(this); + + // Axes if specified + var xa = Axes.getFromId(gd, d.xref); + var ya = Axes.getFromId(gd, d.yref); + + var 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":72,"d3":16}],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, + + editType: 'legend', + + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'left', + + editType: 'legend', + + }, + y: { + valType: 'number', + min: -2, + max: 3, + + editType: 'legend', + + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + + editType: 'legend', + + }, + uirevision: { + valType: 'any', + + editType: 'none', + + }, + valign: { + valType: 'enumerated', + values: ['top', 'middle', 'bottom'], + dflt: 'middle', + + editType: 'legend', + + }, + editType: 'legend' +}; + +},{"../../plots/font_attributes":239,"../color/attributes":50}],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, + 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 +}; + +},{}],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'; + + 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); + + 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 { + // 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); + if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap'); + + coerce('itemsizing'); + + coerce('itemclick'); + coerce('itemdoubleclick'); + + coerce('x', defaultX); + coerce('xanchor'); + 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":258,"./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(); + return Plots.autoMargin(gd, 'legend'); + } + + var legend = Lib.ensureSingle(fullLayout._infolayer, 'g', 'legend', function(s) { + s.attr('pointer-events', 'all'); + }); + + var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) { + s.append('rect'); + }); + + var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) { + s.attr('shape-rendering', 'crispEdges'); + }); + bg.call(Color.stroke, 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(constants.scrollBarEnterAttrs) + .call(Color.fill, constants.scrollBarColor); + }); + + var groups = scrollBox.selectAll('g.groups').data(legendData); + groups.enter().append('g').attr('class', 'groups'); + groups.exit().remove(); + + var traces = groups.selectAll('g.traces').data(Lib.identity); + traces.enter().append('g').attr('class', 'traces'); + traces.exit().remove(); + + traces.style('opacity', function(d) { + var trace = d[0].trace; + if(Registry.traceIs(trace, 'pie-like')) { + return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1; + } else { + return trace.visible === 'legendonly' ? 0.5 : 1; + } + }) + .each(function() { d3.select(this).call(drawTexts, gd); }) + .call(style, gd) + .each(function() { d3.select(this).call(setupTraceToggle, gd); }); + + Lib.syncOrAsync([ + Plots.previousPromises, + function() { return computeLegendDimensions(gd, groups, traces); }, + function() { + // IF expandMargin return a Promise (which is truthy), + // we're under a doAutoMargin redraw, so we don't have to + // draw the remaining pieces below + if(expandMargin(gd)) return; + + var gs = fullLayout._size; + var bw = opts.borderwidth; + + 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(fullLayout.margin.autoexpand) { + var lx0 = lx; + var ly0 = ly; + + lx = Lib.constrain(lx, 0, fullLayout.width - opts._width); + ly = Lib.constrain(ly, 0, fullLayout.height - opts._effHeight); + + if(lx !== lx0) { + Lib.log('Constrain legend.x to make legend fit inside graph'); + } + if(ly !== ly0) { + Lib.log('Constrain legend.y to make legend fit inside graph'); + } + } + + // Set size and position of all the elements that make up a legend: + // legend, background and border, scroll box and scroll bar + Drawing.setTranslate(legend, lx, ly); + + // to be safe, remove previous listeners + scrollBar.on('.drag', null); + legend.on('wheel', null); + + if(opts._height <= opts._maxHeight || gd._context.staticPlot) { + // if scrollbar should not be shown. + bg.attr({ + width: opts._width - bw, + height: opts._effHeight - bw, + x: bw / 2, + y: bw / 2 + }); + + Drawing.setTranslate(scrollBox, 0, 0); + + clipPath.select('rect').attr({ + width: opts._width - 2 * bw, + height: opts._effHeight - 2 * bw, + x: bw, + y: bw + }); + + Drawing.setClipUrl(scrollBox, clipId, gd); + + Drawing.setRect(scrollBar, 0, 0, 0, 0); + delete opts._scrollY; + } else { + var scrollBarHeight = Math.max(constants.scrollBarMinHeight, + opts._effHeight * opts._effHeight / opts._height); + var scrollBarYMax = opts._effHeight - + scrollBarHeight - + 2 * constants.scrollBarMargin; + var scrollBoxYMax = opts._height - opts._effHeight; + 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: opts._width - + 2 * bw + + constants.scrollBarWidth + + constants.scrollBarMargin, + height: opts._effHeight - bw, + x: bw / 2, + y: bw / 2 + }); + + clipPath.select('rect').attr({ + width: opts._width - + 2 * bw + + constants.scrollBarWidth + + constants.scrollBarMargin, + 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), + 0, scrollBoxYMax); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) { + d3.event.preventDefault(); + } + }); + + var eventY0, eventY1, scrollBoxY0; + + var getScrollBarDragY = function(scrollBoxY0, eventY0, eventY1) { + var y = ((eventY1 - eventY0) / scrollRatio) + scrollBoxY0; + return Lib.constrain(y, 0, scrollBoxYMax); + }; + + var getNaturalDragY = function(scrollBoxY0, eventY0, eventY1) { + var y = ((eventY0 - eventY1) / scrollRatio) + scrollBoxY0; + return Lib.constrain(y, 0, scrollBoxYMax); + }; + + // scroll legend by dragging scrollBAR + var scrollBarDrag = d3.behavior.drag() + .on('dragstart', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchstart') { + eventY0 = e.changedTouches[0].clientY; + } else { + eventY0 = e.clientY; + } + scrollBoxY0 = scrollBoxY; + }) + .on('drag', function() { + var e = d3.event.sourceEvent; + if(e.buttons === 2 || e.ctrlKey) return; + if(e.type === 'touchmove') { + eventY1 = e.changedTouches[0].clientY; + } else { + eventY1 = e.clientY; + } + scrollBoxY = getScrollBarDragY(scrollBoxY0, eventY0, eventY1); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + }); + scrollBar.call(scrollBarDrag); + + // scroll legend by touch-dragging scrollBOX + var scrollBoxTouchDrag = d3.behavior.drag() + .on('dragstart', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchstart') { + eventY0 = e.changedTouches[0].clientY; + scrollBoxY0 = scrollBoxY; + } + }) + .on('drag', function() { + var e = d3.event.sourceEvent; + if(e.type === 'touchmove') { + eventY1 = e.changedTouches[0].clientY; + scrollBoxY = getNaturalDragY(scrollBoxY0, eventY0, eventY1); + scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio); + } + }); + scrollBox.call(scrollBoxTouchDrag); + } + + function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) { + opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY; + Drawing.setTranslate(scrollBox, 0, -scrollBoxY); + + Drawing.setRect( + scrollBar, + opts._width, + constants.scrollBarMargin + scrollBoxY * scrollRatio, + constants.scrollBarWidth, + scrollBarHeight + ); + clipPath.select('rect').attr('y', bw + scrollBoxY); + } + + if(gd._context.edits.legendPosition) { + var xf, yf, x0, y0; + + legend.classed('cursor-move', true); + + dragElement.init({ + element: legend.node(), + gd: gd, + prepFn: function() { + var transform = Drawing.getTranslate(legend); + x0 = transform.x; + y0 = transform.y; + }, + moveFn: function(dx, dy) { + var newX = x0 + dx; + var newY = y0 + dy; + + Drawing.setTranslate(legend, newX, newY); + + xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, 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) { + 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) { + 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, maxNameLength) : name); + + svgTextUtils.positionText(textEl, constants.textGap, 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, maxNameLength)) + .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.textGap, textY); + } + + legendItem.lineHeight = lineHeight; + legendItem.height = Math.max(height, 16) + 3; + 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 gs = fullLayout._size; + var isVertical = helpers.isVertical(opts); + var isGrouped = helpers.isGrouped(opts); + + var bw = opts.borderwidth; + var bw2 = 2 * bw; + var textGap = constants.textGap; + var itemGap = constants.itemGap; + var endPad = 2 * (bw + itemGap); + + 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(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); + }); + opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap; + } + } else { + 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; + + // - 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) { + var w = d[0].width + textGap; + maxItemWidth = Math.max(maxItemWidth, w); + combinedItemWidth += w; + }); + + toggleRectWidth = null; + var maxRowWidth = 0; + + if(isGrouped) { + var maxGroupHeightInRow = 0; + var groupOffsetX = 0; + var groupOffsetY = 0; + groups.each(function() { + var maxWidthInGroup = 0; + var offsetY = 0; + d3.select(this).selectAll('g.traces').each(function(d) { + var h = d[0].height; + Drawing.setTranslate(this, 0, itemGap + bw + h / 2 + offsetY); + offsetY += h; + maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width); + }); + maxGroupHeightInRow = Math.max(maxGroupHeightInRow, offsetY); + + var next = maxWidthInGroup + itemGap; + + if((next + bw + groupOffsetX) > opts._maxWidth) { + maxRowWidth = Math.max(maxRowWidth, groupOffsetX); + groupOffsetX = 0; + groupOffsetY += maxGroupHeightInRow + opts.tracegroupgap; + maxGroupHeightInRow = offsetY; + } + + Drawing.setTranslate(this, groupOffsetX, groupOffsetY); + + groupOffsetX += next; + }); + + opts._width = Math.max(maxRowWidth, groupOffsetX) + bw; + opts._height = groupOffsetY + maxGroupHeightInRow + endPad; + } else { + var nTraces = traces.size(); + var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < opts._maxWidth; + + var maxItemHeightInRow = 0; + var offsetX = 0; + var offsetY = 0; + var rowWidth = 0; + traces.each(function(d) { + var h = d[0].height; + var w = textGap + d[0].width; + var next = (oneRowLegend ? w : maxItemWidth) + itemGap; + + if((next + bw + offsetX) > 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; + } + } + } + + opts._width = Math.ceil(opts._width); + opts._height = Math.ceil(opts._height); + + opts._effHeight = Math.min(opts._height, opts._maxHeight); + + var edits = gd._context.edits; + var isEditable = edits.legendText || edits.legendPosition; + traces.each(function(d) { + var traceToggle = d3.select(this).select('.legendtoggle'); + var h = d[0].height; + var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width)); + if(!isVertical) w += itemGap / 2; + Drawing.setRect(traceToggle, 0, -h / 2, w, h); + }); +} + +function expandMargin(gd) { + var fullLayout = gd._fullLayout; + var opts = fullLayout.legend; + var xanchor = getXanchor(opts); + var yanchor = getYanchor(opts); + + 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._effHeight * (FROM_BR[yanchor]), + t: opts._effHeight * (FROM_TL[yanchor]) + }); +} + +function getXanchor(opts) { + return Lib.isRightAnchor(opts) ? 'right' : + Lib.isCenterAnchor(opts) ? 'center' : + 'left'; +} + +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. +* +* This 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 maxNameLength = 0; + var i, j; + + function addOneItem(legendGroup, legendItem) { + // each '' legend group is treated as a separate group + if(legendGroup === '' || !helpers.isGrouped(opts)) { + // TODO: check this against fullData legendgroups? + var uniqueGroup = '~~i' + lgroupi; + lgroups.push(uniqueGroup); + lgroupToTraces[uniqueGroup] = [[legendItem]]; + lgroupi++; + } else if(lgroups.indexOf(legendGroup) === -1) { + lgroups.push(legendGroup); + hasOneNonBlankGroup = true; + lgroupToTraces[legendGroup] = [[legendItem]]; + } else { + lgroupToTraces[legendGroup].push([legendItem]); + } + } + + // build an { legendgroup: [cd0, cd0], ... } object + for(i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var cd0 = cd[0]; + var trace = cd0.trace; + var lgroup = trace.legendgroup; + + if(!trace.visible || !trace.showlegend) continue; + + if(Registry.traceIs(trace, 'pie-like')) { + if(!slicesShown[lgroup]) slicesShown[lgroup] = {}; + + for(j = 0; j < cd.length; j++) { + var labelj = cd[j].label; + + if(!slicesShown[lgroup][labelj]) { + addOneItem(lgroup, { + label: labelj, + color: cd[j].color, + i: cd[j].i, + trace: trace, + pts: cd[j].pts + }); + + slicesShown[lgroup][labelj] = true; + maxNameLength = Math.max(maxNameLength, (labelj || '').length); + } + } + } else { + addOneItem(lgroup, cd0); + maxNameLength = Math.max(maxNameLength, (trace.name || '').length); + } + } + + // won't draw a legend in this case + if(!lgroups.length) return []; + + // 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; + } + + // 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":258,"./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":258}],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, true); + } + + 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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 = fullLayout._cartesianSpikesEnabled; + + var ax, i; + + if(astr === 'zoom') { + var mag = (val === 'in') ? 0.5 : 2; + var r0 = (1 + mag) / 2; + var r1 = (1 - mag) / 2; + var axName; + + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + + if(!ax.fixedrange) { + axName = ax._name; + if(val === 'auto') { + aobj[axName + '.autorange'] = true; + } else if(val === 'reset') { + if(ax._rangeInitial === undefined) { + aobj[axName + '.autorange'] = true; + } else { + var rangeInitial = ax._rangeInitial.slice(); + aobj[axName + '.range[0]'] = rangeInitial[0]; + aobj[axName + '.range[1]'] = rangeInitial[1]; + } + + // N.B. "reset" also resets showspikes + if(ax._showSpikeInitial !== undefined) { + aobj[axName + '.showspikes'] = ax._showSpikeInitial; + if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) { + allSpikesEnabled = 'off'; + } + } + } else { + var rangeNow = [ + ax.r2l(ax.range[0]), + ax.r2l(ax.range[1]), + ]; + + var rangeNew = [ + r0 * rangeNow[0] + r1 * rangeNow[1], + r0 * rangeNow[1] + r1 * rangeNow[0] + ]; + + aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]); + aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]); + } + } + } + } else { + // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y' + if(astr === 'hovermode' && (val === 'x' || val === 'y')) { + val = fullLayout._isHoriz ? 'y' : 'x'; + button.setAttribute('data-val', val); + } + + aobj[astr] = val; + } + + fullLayout._cartesianSpikesEnabled = allSpikesEnabled; + + Registry.call('_guiRelayout', gd, aobj); +} + +modeBarButtons.zoom3d = { + name: 'zoom3d', + 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 camera = sceneId + '.camera'; + var aspectratio = sceneId + '.aspectratio'; + var scene = fullLayout[sceneId]._scene; + var didUpdate; + + if(attr === 'resetLastSave') { + 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[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; + } + } + + 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; + var allSpikesEnabled = fullLayout._cartesianSpikesEnabled; + + fullLayout._cartesianSpikesEnabled = allSpikesEnabled === 'on' ? 'off' : 'on'; + Registry.call('_guiRelayout', gd, setSpikelineVisibility(gd)); + } +}; + +function setSpikelineVisibility(gd) { + var fullLayout = gd._fullLayout; + var areSpikesOn = fullLayout._cartesianSpikesEnabled === 'on'; + var axList = axisIds.list(gd, null, true); + var aobj = {}; + + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + aobj[ax._name + '.showspikes'] = areSpikesOn ? true : ax._showSpikeInitial; + } + + return aobj; +} + +modeBarButtons.resetViewMapbox = { + name: 'resetViewMapbox', + 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":258}],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":258,"../../traces/scatter/subtypes":399,"./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":16,"fast-isnumeric":18}],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":50}],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":51,"./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":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. +* +* This 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}],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":50}],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; + + opts._width = gs.w * (domain[1] - domain[0]); + + var x = Math.round(gs.l + (gs.w * domain[0])); + + var y = Math.round( + gs.t + gs.h * (1 - axisOpts._counterDomainMin) + + (axisOpts.side === 'bottom' ? axisOpts._depth : 0) + + opts._offsetShift + constants.extraPad + ); + + rangeSlider.attr('transform', '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":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. +* +* This 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 svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var constants = _dereq_('./constants'); +var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING; +var name = constants.name; + +function isVisible(ax) { + var rangeSlider = ax && ax[name]; + return rangeSlider && rangeSlider.visible; +} +exports.isVisible = isVisible; + +exports.makeData = function(fullLayout) { + var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true); + var margin = fullLayout.margin; + var rangeSliderData = []; + + if(!fullLayout._has('gl2d')) { + for(var i = 0; i < axes.length; i++) { + var ax = axes[i]; + + if(isVisible(ax)) { + rangeSliderData.push(ax); + + var opts = ax[name]; + opts._id = name + ax._id; + opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness; + opts._offsetShift = Math.floor(opts.borderwidth / 2); + } + } + } + + fullLayout._rangeSliderData = rangeSliderData; +}; + +exports.autoMarginOpts = function(gd, ax) { + var fullLayout = gd._fullLayout; + var opts = ax[name]; + var axLetter = ax._id.charAt(0); + + var bottomDepth = 0; + var titleHeight = 0; + if(ax.side === 'bottom') { + bottomDepth = ax._depth; + if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { + // as in rangeslider/draw.js + titleHeight = 1.5 * ax.title.font.size + 10 + opts._offsetShift; + // multi-line extra bump + var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length; + titleHeight += extraLines * ax.title.font.size * LINE_SPACING; + } + } + + return { + x: 0, + y: ax._counterDomainMin, + l: 0, + r: 0, + t: 0, + b: opts._height + bottomDepth + Math.max(fullLayout.margin.b, titleHeight), + pad: constants.extraPad + opts._offsetShift * 2 + }; +}; + +},{"../../constants/alignment":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. +* +* This 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":376,"../annotations/attributes":36,"../drawing/attributes":71}],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":258,"../color":51,"../dragelement":69,"../drawing":72,"./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":51,"../drawing":72,"./constants":134,"d3":16}],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'); + +var OPPOSITE_SIDE = _dereq_('../../constants/alignment').OPPOSITE_SIDE; +var numStripRE = / [XY][0-9]* /; + +/** + * Titles - (re)draw titles on the axes and plot: + * @param {DOM element} gd - the graphDiv + * @param {string} titleClass - the css class of this title + * @param {object} options - how and what to draw + * propContainer - the layout object containing `title` and `titlefont` + * attributes that apply to this title + * propName - the full name of the title property (for Plotly.relayout) + * [traceIndex] - include only if this property applies to one trace + * (such as a colorbar title) - then editing pipes to Plotly.restyle + * instead of Plotly.relayout + * placeholder - placeholder text for an empty editable title + * [avoid] {object} - include if this title should move to avoid other elements + * selection - d3 selection of elements to avoid + * side - which direction to move if there is a conflict + * [offsetLeft] - if these elements are subject to a translation + * wrt the title element + * [offsetTop] + * attributes {object} - position and alignment attributes + * x - pixels + * y - pixels + * text-anchor - start|middle|end + * transform {object} - how to transform the title after positioning + * rotate - degrees + * offset - shift up/down in the rotated frame (unused?) + * containerGroup - if an svg element already exists to hold this + * title, include here. Otherwise it will go in fullLayout._infolayer + * _meta {object (optional} - meta key-value to for title with + * Lib.templateString, default to fullLayout._meta, if not provided + * + * @return {selection} d3 selection of title container group + */ +function draw(gd, titleClass, options) { + var cont = options.propContainer; + var prop = options.propName; + var placeholder = options.placeholder; + var traceIndex = options.traceIndex; + var avoid = options.avoid || {}; + var attributes = options.attributes; + var transform = options.transform; + var group = options.containerGroup; + + var fullLayout = gd._fullLayout; + + var opacity = 1; + var isplaceholder = false; + var title = cont.title; + var txt = (title && title.text ? title.text : '').trim(); + + var font = title && title.font ? title.font : {}; + var fontFamily = font.family; + var fontSize = font.size; + var fontColor = font.color; + + // only make this title editable if we positively identify its property + // as one that has editing enabled. + var editAttr; + if(prop === 'title.text') editAttr = 'titleText'; + else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText'; + else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText'; + var editable = gd._context.edits[editAttr]; + + if(txt === '') opacity = 0; + // look for placeholder text while stripping out numbers from eg X2, Y3 + // this is just for backward compatibility with the old version that had + // "Click to enter X2 title" and may have gotten saved in some old plots, + // we don't want this to show up when these are displayed. + else if(txt.replace(numStripRE, ' % ') === placeholder.replace(numStripRE, ' % ')) { + opacity = 0.2; + isplaceholder = true; + if(!editable) txt = ''; + } + + if(options._meta) { + txt = Lib.templateString(txt, options._meta); + } else if(fullLayout._meta) { + txt = Lib.templateString(txt, fullLayout._meta); + } + + var elShouldExist = txt || editable; + + if(!group) { + group = Lib.ensureSingle(fullLayout._infolayer, 'g', 'g-' + titleClass); + } + + var el = group.selectAll('text') + .data(elShouldExist ? [0] : []); + el.enter().append('text'); + el.text(txt) + // this is hacky, but convertToTspans uses the class + // to determine whether to rotate mathJax... + // so we need to clear out any old class and put the + // correct one (only relevant for colorbars, at least + // for now) - ie don't use .classed + .attr('class', titleClass); + el.exit().remove(); + + if(!elShouldExist) return group; + + function titleLayout(titleEl) { + Lib.syncOrAsync([drawTitle, scootTitle], titleEl); + } + + function drawTitle(titleEl) { + var transformVal; + + if(transform) { + transformVal = ''; + if(transform.rotate) { + transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')'; + } + if(transform.offset) { + transformVal += '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 backside = OPPOSITE_SIDE[avoid.side]; + var shiftSign = (avoid.side === 'left' || avoid.side === 'top') ? -1 : 1; + var pad = isNumeric(avoid.pad) ? avoid.pad : 2; + + var titlebb = Drawing.bBox(titleGroup.node()); + var paperbb = { + left: 0, + top: 0, + right: fullLayout.width, + bottom: fullLayout.height + }; + + var maxshift = avoid.maxShift || + shiftSign * (paperbb[avoid.side] - titlebb[avoid.side]); + var shift = 0; + + // Prevent the title going off the paper + if(maxshift < 0) { + shift = maxshift; + } else { + // so we don't have to offset each avoided element, + // give the title the opposite offset + var offsetLeft = avoid.offsetLeft || 0; + var offsetTop = avoid.offsetTop || 0; + titlebb.left -= offsetLeft; + titlebb.right -= offsetLeft; + titlebb.top -= offsetTop; + titlebb.bottom -= offsetTop; + + // iterate over a set of elements (avoid.selection) + // to avoid collisions with + avoid.selection.each(function() { + var avoidbb = Drawing.bBox(this); + + if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) { + shift = Math.max(shift, shiftSign * ( + avoidbb[avoid.side] - titlebb[backside]) + pad); + } + }); + shift = Math.min(maxshift, shift); + } + + if(shift > 0 || maxshift < 0) { + var shiftTemplate = { + left: [-shift, 0], + right: [shift, 0], + top: [0, -shift], + bottom: [0, shift] + }[avoid.side]; + titleGroup.attr('transform', '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; +} + +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. +* +* This 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'} + ], + + }, + args2: { + valType: 'info_array', + + freeLength: true, + items: [ + {valType: 'any'}, + {valType: 'any'}, + {valType: 'any'} + ], + + }, + label: { + valType: 'string', + + dflt: '', + + }, + execute: { + valType: 'boolean', + + dflt: true, + + } +}); + +module.exports = overrideAll(templatedArray('updatemenu', { + _arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/], + + visible: { + valType: 'boolean', + + + }, + + type: { + valType: 'enumerated', + values: ['dropdown', 'buttons'], + dflt: 'dropdown', + + + }, + + direction: { + valType: 'enumerated', + values: ['left', 'right', 'up', 'down'], + dflt: 'down', + + + }, + + active: { + valType: 'integer', + + min: -1, + dflt: 0, + + }, + + showactive: { + valType: 'boolean', + + dflt: true, + + }, + + buttons: buttonsAttrs, + + x: { + valType: 'number', + min: -2, + max: 3, + dflt: -0.05, + + + }, + xanchor: { + valType: 'enumerated', + values: ['auto', 'left', 'center', 'right'], + dflt: 'right', + + + }, + y: { + valType: 'number', + min: -2, + max: 3, + dflt: 1, + + + }, + yanchor: { + valType: 'enumerated', + values: ['auto', 'top', 'middle', 'bottom'], + dflt: 'top', + + + }, + + pad: extendFlat(padAttrs({editType: 'arraydraw'}), { + + }), + + font: fontAttrs({ + + }), + + bgcolor: { + valType: 'color', + + + }, + bordercolor: { + valType: 'color', + dflt: colorAttrs.borderLine, + + + }, + borderwidth: { + valType: 'number', + min: 0, + dflt: 1, + + editType: 'arraydraw', + + } +}), 'arraydraw', 'from-root'); + +},{"../../lib/extend":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. +* +* 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('args2'); + 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; + + if(buttonOpts.execute) { + if(buttonOpts.args2 && menuOpts.active === buttonIndex) { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2); + } else { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args); + } + } + + gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active}); + }); + + button.on('mouseover', function() { + button.call(styleOnMouseOver); + }); + + button.on('mouseout', function() { + button.call(styleOnMouseOut, menuOpts); + buttons.call(styleButtons, menuOpts); + }); + }); + + buttons.call(styleButtons, menuOpts); + + if(isVertical) { + scrollBoxPosition.w = Math.max(dims.openWidth, dims.headerWidth); + scrollBoxPosition.h = posOpts.y - scrollBoxPosition.t; + } else { + scrollBoxPosition.w = posOpts.x - scrollBoxPosition.l; + scrollBoxPosition.h = Math.max(dims.openHeight, dims.headerHeight); + } + + scrollBoxPosition.direction = menuOpts.direction; + + if(scrollBox) { + if(buttons.size()) { + drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, scrollBoxPosition); + } else { + hideScrollBox(scrollBox); + } + } +} + +function drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, position) { + // enable the scrollbox + var direction = menuOpts.direction; + var isVertical = (direction === 'up' || direction === 'down'); + var dims = menuOpts._dims; + + var active = menuOpts.active; + var translateX, translateY; + var i; + if(isVertical) { + translateY = 0; + for(i = 0; i < active; i++) { + translateY += dims.heights[i] + constants.gapButton; + } + } else { + translateX = 0; + for(i = 0; i < active; i++) { + translateX += dims.widths[i] + constants.gapButton; + } + } + + scrollBox.enable(position, translateX, translateY); + + if(scrollBox.hbar) { + scrollBox.hbar + .attr('opacity', '0') + .transition() + .attr('opacity', '1'); + } + + if(scrollBox.vbar) { + scrollBox.vbar + .attr('opacity', '0') + .transition() + .attr('opacity', '1'); + } +} + +function hideScrollBox(scrollBox) { + var hasHBar = !!scrollBox.hbar; + var hasVBar = !!scrollBox.vbar; + + if(hasHBar) { + scrollBox.hbar + .transition() + .attr('opacity', '0') + .each('end', function() { + hasHBar = false; + if(!hasVBar) scrollBox.disable(); + }); + } + + if(hasVBar) { + scrollBox.vbar + .transition() + .attr('opacity', '0') + .each('end', function() { + hasVBar = false; + if(!hasHBar) scrollBox.disable(); + }); + } +} + +function drawItem(item, menuOpts, itemOpts, gd) { + item.call(drawItemRect, menuOpts) + .call(drawItemText, menuOpts, itemOpts, gd); +} + +function drawItemRect(item, menuOpts) { + var rect = Lib.ensureSingle(item, 'rect', constants.itemRectClassName, function(s) { + s.attr({ + rx: constants.rx, + ry: constants.ry, + 'shape-rendering': 'crispEdges' + }); + }); + + rect.call(Color.stroke, menuOpts.bordercolor) + .call(Color.fill, menuOpts.bgcolor) + .style('stroke-width', menuOpts.borderwidth + 'px'); +} + +function drawItemText(item, menuOpts, itemOpts, gd) { + var text = Lib.ensureSingle(item, 'text', constants.itemTextClassName, function(s) { + s.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":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. +* +* 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":51,"../drawing":72,"d3":16}],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.51.1'; + +// 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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":18}],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":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. +* +* This 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":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. +* +* 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}],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(lib.isTypedArray(v)) { + objOut[k] = v.subarray(0, arrayLen); + } else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]); + else objOut[k] = v; + } + + keys = Object.keys(obj2); + for(i = 0; i < keys.length; i++) { + k = keys[i]; + v = obj2[k]; + if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') { + objOut[k] = v; + } + } + + return objOut; +}; + +lib.titleCase = function(s) { + return s.charAt(0).toUpperCase() + s.substr(1); +}; + +lib.containsAny = function(s, fragments) { + for(var i = 0; i < fragments.length; i++) { + if(s.indexOf(fragments[i]) !== -1) return true; + } + return false; +}; + +lib.isIE = function() { + return typeof window.navigator.msSaveBlob !== 'undefined'; +}; + +var IS_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 hovertemplateWarnings = { + max: 10, + count: 0, + name: 'hovertemplate' +}; +lib.hovertemplateString = function() { + return templateFormatString.apply(hovertemplateWarnings, arguments); +}; + +var texttemplateWarnings = { + max: 10, + count: 0, + name: 'texttemplate' +}; +lib.texttemplateString = function() { + return templateFormatString.apply(texttemplateWarnings, arguments); +}; + +var TEMPLATE_STRING_FORMAT_SEPARATOR = /^[:|\|]/; +/** + * Substitute values from an object into a string and optionally formats them using d3-format, + * or fallback to associated labels. + * + * Examples: + * Lib.hovertemplateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf' + * Lib.hovertemplateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf' + * Lib.hovertemplateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00' + * + * @param {string} input string containing %{...:...} template strings + * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'} + * @param {obj} d3 locale + * @param {obj} data objects containing substitution values + * + * @return {string} templated string + */ +function templateFormatString(string, labels, d3locale) { + var opts = this; + var args = arguments; + if(!labels) labels = {}; + // Not all that useful, but cache nestedProperty instantiation + // just in case it speeds things up *slightly*: + var getterCache = {}; + + return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, key, format) { + 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; + } + + 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 && opts) { + if(opts.count < opts.max) { + lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!'); + value = match; + } + + if(opts.count === opts.max) { + lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed'); + } + opts.count++; + + return match; + } + + if(format) { + var fmt; + if(format[0] === ':') { + fmt = d3locale ? d3locale.numberFormat : d3.format; + value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value); + } + + if(format[0] === '|') { + fmt = d3locale ? d3locale.timeFormat.utc : d3.time.format.utc; + var ms = lib.dateTime2ms(value); + value = lib.formatDate(ms, format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''), false, fmt); + } + } 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; +}; + +/** + * @param {number} ratio + * @param {number} n (number of decimal places) + */ +lib.formatPercent = function(ratio, n) { + n = n || 0; + var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%'; + for(var i = 0; i < n; i++) { + if(str.indexOf('.') !== -1) { + str = str.replace('0%', '%'); + str = str.replace('.%', '%'); + } + } + return str; +}; + +lib.isHidden = function(gd) { + var display = window.getComputedStyle(gd).display; + return !display || display === 'none'; +}; + +lib.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. +* +* 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":258}],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":16}],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":18}],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":16,"fast-isnumeric":18}],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":18}],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":18}],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 = exports.NEWLINES = /(\r\n?|\n)/g; + +var SPLIT_TAGS = /(<[^<>]*>)/; + +var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i; + +var BR_TAG = //i; +exports.BR_TAG_ALL = //gi; + +/* + * style and href: pull them out of either single or double quotes. Also + * - target: (_blank|_self|_parent|_top|framename) + * note that you can't use target to get a popup but if you use popup, + * a `framename` will be passed along as the name of the popup window. + * per the spec, cannot contain whitespace. + * for backward compatibility we default to '_blank' + * - popup: a custom one for us to enable popup (new window) links. String + * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550' + * note that at least in Chrome, you need to give at least one property + * in this string or the page will open in a new tab anyway. We follow this + * convention and will not make a popup if this string is empty. + * per the spec, cannot contain whitespace. + * + * Because we hack in other attributes with style (sub & sup), drop any trailing + * semicolon in user-supplied styles so we can consistently append the tag-dependent style + * + * These are for tag attributes; Chrome anyway will convert entities in + * attribute values, but not in attribute names + * you can test this by for example: + * > p = document.createElement('p') + * > p.innerHTML = 'Hi' + * > p.innerHTML + * <- 'Hi' + */ +var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i; +var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i; +var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i; +var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i; + +// dedicated matcher for these quoted regexes, that can return their results +// in two different places +function getQuotedMatch(_str, re) { + if(!_str) return null; + var match = _str.match(re); + var result = match && (match[3] || match[4]); + return result && convertEntities(result); +} + +var COLORMATCH = /(^|;)\s*color:/; + +/** + * Strip string of tags + * + * @param {string} _str : input string + * @param {object} opts : + * - len {number} max length of output string + * - allowedTags {array} list of pseudo-html tags to NOT strip + * @return {string} + */ +exports.plainText = function(_str, opts) { + opts = opts || {}; + + var len = (opts.len !== undefined && opts.len !== -1) ? opts.len : Infinity; + var allowedTags = opts.allowedTags !== undefined ? opts.allowedTags : ['br']; + + var ellipsis = '...'; + var eLen = ellipsis.length; + + var oldParts = _str.split(SPLIT_TAGS); + var newParts = []; + var prevTag = ''; + var l = 0; + + for(var i = 0; i < oldParts.length; i++) { + var p = oldParts[i]; + var match = p.match(ONE_TAG); + var tagType = match && match[2].toLowerCase(); + + if(tagType) { + // N.B. tags do not count towards string length + if(allowedTags.indexOf(tagType) !== -1) { + newParts.push(p); + prevTag = tagType; + } + } else { + var pLen = p.length; + + if((l + pLen) < len) { + newParts.push(p); + l += pLen; + } else if(l < len) { + var pLen2 = len - l; + + if(prevTag && (prevTag !== 'br' || pLen2 <= eLen || pLen <= eLen)) { + newParts.pop(); + } + + if(len > eLen) { + newParts.push(p.substr(0, pLen2 - eLen) + ellipsis); + } else { + newParts.push(p.substr(0, pLen2)); + } + break; + } + + prevTag = ''; + } + } + + return newParts.join(''); +}; + +/* + * N.B. HTML entities are listed without the leading '&' and trailing ';' + * https://www.freeformatter.com/html-entities.html + * + * FWIW if we wanted to support the full set, it has 2261 entries: + * https://www.w3.org/TR/html5/entities.json + * though I notice that some of these are duplicates and/or are missing ";" + * eg: "&", "&", "&", and "&" all map to "&" + * We no longer need to include numeric entities here, these are now handled + * by String.fromCodePoint/fromCharCode + * + * Anyway the only ones that are really important to allow are the HTML special + * chars <, >, and &, because these ones can trigger special processing if not + * replaced by the corresponding entity. + */ +var entityToUnicode = { + mu: 'μ', + amp: '&', + lt: '<', + gt: '>', + nbsp: ' ', + times: '×', + plusmn: '±', + deg: '°' +}; + +// NOTE: in general entities can contain uppercase too (so [a-zA-Z]) but all the +// ones we support use only lowercase. If we ever change that, update the regex. +var ENTITY_MATCH = /&(#\d+|#x[\da-fA-F]+|[a-z]+);/g; +function convertEntities(_str) { + return _str.replace(ENTITY_MATCH, function(fullMatch, innerMatch) { + var outChar; + if(innerMatch.charAt(0) === '#') { + // cannot use String.fromCodePoint in IE + outChar = fromCodePoint( + innerMatch.charAt(1) === 'x' ? + parseInt(innerMatch.substr(2), 16) : + parseInt(innerMatch.substr(1), 10) + ); + } else outChar = entityToUnicode[innerMatch]; + + // as in regular HTML, if we didn't decode the entity just + // leave the raw text in place. + return outChar || fullMatch; + }); +} +exports.convertEntities = convertEntities; + +function fromCodePoint(code) { + // Don't allow overflow. In Chrome this turns into � but I feel like it's + // more useful to just not convert it at all. + if(code > 0x10FFFF) return; + var stringFromCodePoint = String.fromCodePoint; + if(stringFromCodePoint) return stringFromCodePoint(code); + + // IE doesn't have String.fromCodePoint + // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint + var stringFromCharCode = String.fromCharCode; + if(code <= 0xFFFF) return stringFromCharCode(code); + return stringFromCharCode( + (code >> 10) + 0xD7C0, + (code % 0x400) + 0xDC00 + ); +} + +/* + * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these + * to containerNode + * + * @param {svg text element} containerNode: the node to insert this text into + * @param {string} str: the pseudo-html string to convert to svg + * + * @returns {bool}: does the result contain any links? We need to handle the text element + * somewhat differently if it does, so just keep track of this when it happens. + */ +function buildSVGText(containerNode, str) { + /* + * Normalize behavior between IE and others wrt newlines and whitespace:pre + * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746 + * Chrome and FF display \n, \r, or \r\n as a space in this mode. + * I feel like at some point we turned these into
but currently we don't so + * I'm just going to cement what we do now in Chrome and FF + */ + str = str.replace(NEWLINES, ' '); + + var hasLink = false; + + // as we're building the text, keep track of what elements we're nested inside + // nodeStack will be an array of {node, type, style, href, target, popup} + // where only type: 'a' gets the last 3 and node is only added when it's created + var nodeStack = []; + var currentNode; + var currentLine = -1; + + function newLine() { + currentLine++; + + var lineNode = document.createElementNS(xmlnsNamespaces.svg, 'tspan'); + d3.select(lineNode).attr({ + class: 'line', + dy: (currentLine * LINE_SPACING) + 'em' + }); + containerNode.appendChild(lineNode); + + currentNode = lineNode; + + var oldNodeStack = nodeStack; + nodeStack = [{node: lineNode}]; + + if(oldNodeStack.length > 1) { + for(var i = 1; i < oldNodeStack.length; i++) { + enterNode(oldNodeStack[i]); + } + } + } + + function enterNode(nodeSpec) { + var type = nodeSpec.type; + var nodeAttrs = {}; + var nodeType; + + if(type === 'a') { + nodeType = 'a'; + var target = nodeSpec.target; + var href = nodeSpec.href; + var popup = nodeSpec.popup; + if(href) { + nodeAttrs = { + 'xlink:xlink:show': (target === '_blank' || target.charAt(0) !== '_') ? 'new' : 'replace', + target: target, + 'xlink:xlink:href': href + }; + if(popup) { + // security: href and target are not inserted as code but + // as attributes. popup is, but limited to /[A-Za-z0-9_=,]/ + nodeAttrs.onclick = 'window.open(this.href.baseVal,this.target.baseVal,"' + + popup + '");return false;'; + } + } + } else nodeType = 'tspan'; + + if(nodeSpec.style) nodeAttrs.style = nodeSpec.style; + + var newNode = document.createElementNS(xmlnsNamespaces.svg, nodeType); + + if(type === 'sup' || type === 'sub') { + addTextNode(currentNode, ZERO_WIDTH_SPACE); + currentNode.appendChild(newNode); + + var resetter = document.createElementNS(xmlnsNamespaces.svg, 'tspan'); + addTextNode(resetter, ZERO_WIDTH_SPACE); + d3.select(resetter).attr('dy', RESET_DY[type]); + nodeAttrs.dy = SHIFT_DY[type]; + + currentNode.appendChild(newNode); + currentNode.appendChild(resetter); + } else { + currentNode.appendChild(newNode); + } + + d3.select(newNode).attr(nodeAttrs); + + currentNode = nodeSpec.node = newNode; + nodeStack.push(nodeSpec); + } + + function addTextNode(node, text) { + node.appendChild(document.createTextNode(text)); + } + + function exitNode(type) { + // A bare closing tag can't close the root node. If we encounter this it + // means there's an extra closing tag that can just be ignored: + if(nodeStack.length === 1) { + Lib.log('Ignoring unexpected end tag .', str); + return; + } + + var innerNode = nodeStack.pop(); + + if(type !== innerNode.type) { + Lib.log('Start tag <' + innerNode.type + '> doesnt match end tag <' + + type + '>. Pretending it did match.', str); + } + currentNode = nodeStack[nodeStack.length - 1].node; + } + + var hasLines = BR_TAG.test(str); + + if(hasLines) newLine(); + else { + currentNode = containerNode; + nodeStack = [{node: containerNode}]; + } + + var parts = str.split(SPLIT_TAGS); + for(var i = 0; i < parts.length; i++) { + var parti = parts[i]; + var match = parti.match(ONE_TAG); + var tagType = match && match[2].toLowerCase(); + var tagStyle = TAG_STYLES[tagType]; + + if(tagType === 'br') { + newLine(); + } else if(tagStyle === undefined) { + addTextNode(currentNode, convertEntities(parti)); + } else { + // tag - open or close + if(match[1]) { + exitNode(tagType); + } else { + var extra = match[4]; + + var nodeSpec = {type: tagType}; + + // now add style, from both the tag name and any extra css + // Most of the svg css that users will care about is just like html, + // but font color is different (uses fill). Let our users ignore this. + var css = getQuotedMatch(extra, STYLEMATCH); + if(css) { + css = css.replace(COLORMATCH, '$1 fill:'); + if(tagStyle) css += ';' + tagStyle; + } else if(tagStyle) css = tagStyle; + + if(css) nodeSpec.style = css; + + if(tagType === 'a') { + hasLink = true; + + var href = getQuotedMatch(extra, HREFMATCH); + + if(href) { + // check safe protocols + var dummyAnchor = document.createElement('a'); + dummyAnchor.href = href; + if(PROTOCOLS.indexOf(dummyAnchor.protocol) !== -1) { + // Decode href to allow both already encoded and not encoded + // URIs. Without decoding prior encoding, an already encoded + // URI would be encoded twice producing a semantically different URI. + nodeSpec.href = encodeURI(decodeURI(href)); + nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank'; + nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH); + } + } + } + + enterNode(nodeSpec); + } + } + } + + return hasLink; +} + +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":16}],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":18}],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":258}],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":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. +* +* This 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":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. +* +* This 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":258,"./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); + + // TODO can this be moved elsewhere? + if(fullLayout._has('pie')) { + var fullData = gd._fullData; + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + if(trace.type === 'pie' && trace.automargin) { + Plots.allowAutoMargin(gd, 'pie.' + trace.uid + '.automargin'); + } + } + } + + Plots.doAutoMargin(gd); + return Plots.previousPromises(gd); + } + + // in case the margins changed, draw margin pushers again + function marginPushersAgain() { + if(!Plots.didMarginChange(oldMargins, fullLayout._size)) return; + + return Lib.syncOrAsync([ + marginPushers, + subroutines.layoutStyles + ], gd); + } + + function positionAndAutorange() { + if(!recalc) { + doAutoRangeAndConstraints(); + return; + } + + // TODO: autosize extra for text markers and images + // see https://github.com/plotly/plotly.js/issues/1111 + return Lib.syncOrAsync([ + Registry.getComponentMethod('shapes', 'calcAutorange'), + Registry.getComponentMethod('annotations', 'calcAutorange'), + doAutoRangeAndConstraints + ], gd); + } + + function doAutoRangeAndConstraints() { + if(gd._transitioning) return; + + subroutines.doAutoRangeAndConstraints(gd); + + // store initial ranges *after* enforcing constraints, otherwise + // we will never look like we're at the initial ranges + if(graphWasEmpty) Axes.saveRangeInitial(gd); + + // this one is different from shapes/annotations calcAutorange + // the others incorporate those components into ax._extremes, + // this one actually sets the ranges in rangesliders. + Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd); + } + + // draw ticks, titles, and calculate axis scaling (._b, ._m) + function drawAxes() { + return Axes.draw(gd, graphWasEmpty ? '' : 'redraw'); + } + + var seq = [ + Plots.previousPromises, + addFrames, + drawFramework, + marginPushers, + marginPushersAgain + ]; + + if(hasCartesian) seq.push(positionAndAutorange); + + seq.push(subroutines.layoutStyles); + if(hasCartesian) seq.push(drawAxes); + + 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 & Treemap traces + + // below this you must be in editable: true mode + // TODO: I still put name and title with `trace.uirevision` + // reasonable or should these be `editrevision`? + // Also applies to axis titles up in the layout section + + // "name" also includes transform.styles + {pattern: /(^|value\.)name$/}, + // including nested colorbar attributes (ie marker.colorbar) + {pattern: /colorbar\.title\.text$/}, + {pattern: /colorbar\.(x|y)$/, attr: 'editrevision'} +]; + +function findUIPattern(key, patternSpecs) { + for(var i = 0; i < patternSpecs.length; i++) { + var spec = patternSpecs[i]; + var match = key.match(spec.pattern); + if(match) { + return {head: match[1], attr: spec.attr}; + } + } +} + +// We're finding the new uirevision before supplyDefaults, so do the +// inheritance manually. Note that only `undefined` inherits - other +// falsy values are returned. +function getNewRev(revAttr, container) { + var newRev = nestedProperty(container, revAttr).get(); + if(newRev !== undefined) return newRev; + + var parts = revAttr.split('.'); + parts.pop(); + while(parts.length > 1) { + parts.pop(); + newRev = nestedProperty(container, parts.join('.') + '.uirevision').get(); + if(newRev !== undefined) return newRev; + } + + return container.uirevision; +} + +function getFullTraceIndexFromUid(uid, fullData) { + for(var i = 0; i < fullData.length; i++) { + if(fullData[i]._fullInput.uid === uid) return i; + } + return -1; +} + +function getTraceIndexFromUid(uid, data, tracei) { + for(var i = 0; i < data.length; i++) { + if(data[i].uid === uid) return i; + } + // fall back on trace order, but only if user didn't provide a uid for that trace + return (!data[tracei] || data[tracei].uid) ? -1 : tracei; +} + +function valsMatch(v1, v2) { + var v1IsObj = Lib.isPlainObject(v1); + var v1IsArray = Array.isArray(v1); + if(v1IsObj || v1IsArray) { + return ( + (v1IsObj && Lib.isPlainObject(v2)) || + (v1IsArray && Array.isArray(v2)) + ) && JSON.stringify(v1) === JSON.stringify(v2); + } + return v1 === v2; +} + +function applyUIRevisions(data, layout, oldFullData, oldFullLayout) { + var layoutPreGUI = oldFullLayout._preGUI; + var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal; + var bothInheritAutorange = []; + var newRangeAccepted = {}; + for(key in layoutPreGUI) { + match = findUIPattern(key, layoutUIControlPatterns); + if(match) { + revAttr = match.attr || (match.head + '.uirevision'); + oldRev = nestedProperty(oldFullLayout, revAttr).get(); + newRev = oldRev && getNewRev(revAttr, layout); + if(newRev && (newRev === oldRev)) { + preGUIVal = layoutPreGUI[key]; + if(preGUIVal === null) preGUIVal = undefined; + newNP = nestedProperty(layout, key); + newVal = newNP.get(); + if(valsMatch(newVal, preGUIVal)) { + if(newVal === undefined && key.substr(key.length - 9) === 'autorange') { + bothInheritAutorange.push(key.substr(0, key.length - 10)); + } + newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get())); + continue; + } + } + } else { + Lib.warn('unrecognized GUI edit: ' + key); + } + // if we got this far, the new value was accepted as the new starting + // point (either because it changed or revision changed) + // so remove it from _preGUI for next time. + delete layoutPreGUI[key]; + + if(key.substr(key.length - 8, 6) === 'range[') { + newRangeAccepted[key.substr(0, key.length - 9)] = 1; + } + } + + // Special logic for `autorange`, since it interacts with `range`: + // If the new figure's matching `range` was kept, and `autorange` + // wasn't supplied explicitly in either the original or the new figure, + // we shouldn't alter that - but we may just have done that, so fix it. + for(var i = 0; i < bothInheritAutorange.length; i++) { + var axAttr = bothInheritAutorange[i]; + if(newRangeAccepted[axAttr]) { + var newAx = nestedProperty(layout, axAttr).get(); + if(newAx) delete newAx.autorange; + } + } + + // Now traces - try to match them up by uid (in case we added/deleted in + // the middle), then fall back on index. + var allTracePreGUI = oldFullLayout._tracePreGUI; + for(var uid in allTracePreGUI) { + var tracePreGUI = allTracePreGUI[uid]; + var newTrace = null; + var fullInput; + for(key in tracePreGUI) { + // wait until we know we have preGUI values to look for traces + // but if we don't find both, stop looking at this uid + if(!newTrace) { + var fulli = getFullTraceIndexFromUid(uid, oldFullData); + if(fulli < 0) { + // Somehow we didn't even have this trace in oldFullData... + // I guess this could happen with `deleteTraces` or something + delete allTracePreGUI[uid]; + break; + } + var fullTrace = oldFullData[fulli]; + fullInput = fullTrace._fullInput; + + var newTracei = getTraceIndexFromUid(uid, data, fullInput.index); + if(newTracei < 0) { + // No match in new data + delete allTracePreGUI[uid]; + break; + } + newTrace = data[newTracei]; + } + + match = findUIPattern(key, traceUIControlPatterns); + if(match) { + if(match.attr) { + oldRev = nestedProperty(oldFullLayout, match.attr).get(); + newRev = oldRev && getNewRev(match.attr, layout); + } else { + oldRev = fullInput.uirevision; + // inheritance for trace.uirevision is simple, just layout.uirevision + newRev = newTrace.uirevision; + if(newRev === undefined) newRev = layout.uirevision; + } + + if(newRev && newRev === oldRev) { + preGUIVal = tracePreGUI[key]; + if(preGUIVal === null) preGUIVal = undefined; + newNP = nestedProperty(newTrace, key); + newVal = newNP.get(); + if(valsMatch(newVal, preGUIVal)) { + newNP.set(undefinedToNull(nestedProperty(fullInput, key).get())); + continue; + } + } + } else { + Lib.warn('unrecognized GUI edit: ' + key + ' in trace uid ' + uid); + } + delete tracePreGUI[key]; + } + } +} + +/** + * Plotly.react: + * A plot/update method that takes the full plot state (same API as plot/newPlot) + * and diffs to determine the minimal update pathway + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * @param {array of objects} data + * array of traces, containing the data and display information for each trace + * @param {object} layout + * object describing the overall display of the plot, + * all the stuff that doesn't pertain to any individual trace + * @param {object} config + * configuration options (see ./plot_config.js for more info) + * + * OR + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + * @param {object} figure + * object containing `data`, `layout`, `config`, and `frames` members + * + */ +function react(gd, data, layout, config) { + var frames, plotDone; + + function addFrames() { return exports.addFrames(gd, frames); } + + gd = Lib.getGraphDiv(gd); + helpers.clearPromiseQueue(gd); + + var oldFullData = gd._fullData; + var oldFullLayout = gd._fullLayout; + + // you can use this as the initial draw as well as to update + if(!Lib.isPlotDiv(gd) || !oldFullData || !oldFullLayout) { + plotDone = exports.newPlot(gd, data, layout, config); + } else { + if(Lib.isPlainObject(data)) { + var obj = data; + data = obj.data; + layout = obj.layout; + config = obj.config; + frames = obj.frames; + } + + var configChanged = false; + // assume that if there's a config at all, we're reacting to it too, + // and completely replace the previous config + if(config) { + var oldConfig = Lib.extendDeep({}, gd._context); + gd._context = undefined; + setPlotContext(gd, config); + configChanged = diffConfig(oldConfig, gd._context); + } + + gd.data = data || []; + helpers.cleanData(gd.data); + gd.layout = layout || {}; + helpers.cleanLayout(gd.layout); + + applyUIRevisions(gd.data, gd.layout, oldFullData, oldFullLayout); + + // "true" skips updating calcdata and remapping arrays from calcTransforms, + // which supplyDefaults usually does at the end, but we may need to NOT do + // if the diff (which we haven't determined yet) says we'll recalc + Plots.supplyDefaults(gd, {skipUpdateCalc: true}); + + var newFullData = gd._fullData; + var newFullLayout = gd._fullLayout; + var immutable = newFullLayout.datarevision === undefined; + var transition = newFullLayout.transition; + + var relayoutFlags = diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition); + var newDataRevision = relayoutFlags.newDataRevision; + var restyleFlags = diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision); + + // TODO: how to translate this part of relayout to Plotly.react? + // // Setting width or height to null must reset the graph's width / height + // // back to its initial value as computed during the first pass in Plots.plotAutoSize. + // // + // // To do so, we must manually set them back here using the _initialAutoSize cache. + // if(['width', 'height'].indexOf(ai) !== -1 && vi === null) { + // fullLayout[ai] = gd._initialAutoSize[ai]; + // } + + if(updateAutosize(gd)) relayoutFlags.layoutReplot = true; + + // clear calcdata 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 treemap layer for the whole plot + fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true); + + // single sunburst layer for the whole plot + fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true); + + // single indicator layer for the whole plot + fullLayout._indicatorlayer = fullLayout._toppaper.append('g').classed('indicatorlayer', true); + + // fill in image server scrape-svg + fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true); + + // lastly upper shapes, info (legend, annotations) and hover layers go on top + // these are in a different svg element normally, but get collapsed into a single + // svg when exporting (after inserting 3D) + // upper shapes/images are only those drawn above the whole plot, including subplots + var layerAbove = fullLayout._toppaper.append('g') + .classed('layer-above', true); + fullLayout._imageUpperLayer = layerAbove.append('g') + .classed('imagelayer', true); + fullLayout._shapeUpperLayer = layerAbove.append('g') + .classed('shapelayer', true); + + fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true); + fullLayout._menulayer = fullLayout._toppaper.append('g').classed('menulayer', true); + fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true); + fullLayout._hoverlayer = fullLayout._hoverpaper.append('g').classed('hoverlayer', true); + + // Make the modebar container + fullLayout._modebardiv + .classed('modebar-container', true) + .style('position', 'absolute') + .style('top', '0px') + .style('right', '0px'); + + gd.emit('plotly_framework'); +} + +exports.animate = animate; +exports.addFrames = addFrames; +exports.deleteFrames = deleteFrames; + +exports.addTraces = addTraces; +exports.deleteTraces = deleteTraces; +exports.extendTraces = extendTraces; +exports.moveTraces = moveTraces; +exports.prependTraces = prependTraces; + +exports.newPlot = newPlot; +exports.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":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. +* +* 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":258,"./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 Plots.previousPromises(gd); + } + + function getLinePosition(ax, counterAx, side) { + var lwHalf = ax._lw / 2; + + if(ax._id.charAt(0) === 'x') { + if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1); + else if(side === 'top') return counterAx._offset - pad - lwHalf; + return counterAx._offset + counterAx._length + pad + lwHalf; + } + + if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1); + else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf; + return counterAx._offset - pad - lwHalf; + } + + // some preparation of axis position info + for(i = 0; i < axList.length; i++) { + ax = axList[i]; + + var counterAx = ax._anchorAxis; + + // clear axis line positions, to be set in the subplot loop below + ax._linepositions = {}; + + // stash crispRounded linewidth so we don't need to pass gd all over the place + ax._lw = Drawing.crispRound(gd, ax.linewidth, 1); + + // figure out the main axis line and main mirror line position. + // it's easier to follow the logic if we handle these separately from + // ax._linepositions, which are only used by mirror=allticks + // for non-main-subplot ticks, and mirror=all(ticks)? for zero line + // hiding logic + ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side); + ax._mainMirrorPosition = (ax.mirror && counterAx) ? + getLinePosition(ax, counterAx, + alignmentConstants.OPPOSITE_SIDE[ax.side]) : null; + } + + // figure out which backgrounds we need to draw, + // and in which layers to put them + var lowerBackgroundIDs = []; + var backgroundIds = []; + var lowerDomains = []; + // no need to draw background when paper and plot color are the same color, + // activate mode just for large splom (which benefit the most from this + // optimization), but this could apply to all cartesian subplots. + var noNeedForBg = ( + Color.opacity(fullLayout.paper_bgcolor) === 1 && + Color.opacity(fullLayout.plot_bgcolor) === 1 && + fullLayout.paper_bgcolor === fullLayout.plot_bgcolor + ); + + for(subplot in fullLayout._plots) { + plotinfo = fullLayout._plots[subplot]; + + if(plotinfo.mainplot) { + // mainplot is a reference to the main plot this one is overlaid on + // so if it exists, this is an overlaid plot and we don't need to + // give it its own background + if(plotinfo.bg) { + plotinfo.bg.remove(); + } + plotinfo.bg = undefined; + } else { + var xDomain = plotinfo.xaxis.domain; + var yDomain = plotinfo.yaxis.domain; + var plotgroup = plotinfo.plotgroup; + + if(overlappingDomain(xDomain, yDomain, lowerDomains)) { + var pgNode = plotgroup.node(); + var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg'); + pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]); + backgroundIds.push(subplot); + } else { + plotgroup.select('rect.bg').remove(); + lowerDomains.push([xDomain, yDomain]); + if(!noNeedForBg) { + lowerBackgroundIDs.push(subplot); + backgroundIds.push(subplot); + } + } + } + } + + // now create all the lower-layer backgrounds at once now that + // we have the list of subplots that need them + var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg') + .data(lowerBackgroundIDs); + + lowerBackgrounds.enter().append('rect') + .classed('bg', true); + + lowerBackgrounds.exit().remove(); + + lowerBackgrounds.each(function(subplot) { + fullLayout._plots[subplot].bg = d3.select(this); + }); + + // style all backgrounds + for(i = 0; i < backgroundIds.length; i++) { + plotinfo = fullLayout._plots[backgroundIds[i]]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + if(plotinfo.bg) { + plotinfo.bg + .call(Drawing.setRect, + xa._offset - pad, ya._offset - pad, + xa._length + 2 * pad, ya._length + 2 * pad) + .call(Color.fill, fullLayout.plot_bgcolor) + .style('stroke-width', 0); + } + } + + if(!fullLayout._hasOnlyLargeSploms) { + for(subplot in fullLayout._plots) { + plotinfo = fullLayout._plots[subplot]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + // Clip so that data only shows up on the plot area. + var clipId = plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot'; + + var plotClip = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) { + s.classed('plotclip', true) + .append('rect'); + }); + + plotinfo.clipRect = plotClip.select('rect').attr({ + width: xa._length, + height: ya._length + }); + + Drawing.setTranslate(plotinfo.plot, xa._offset, ya._offset); + + var plotClipId; + var layerClipId; + + if(plotinfo._hasClipOnAxisFalse) { + plotClipId = null; + layerClipId = clipId; + } else { + plotClipId = clipId; + layerClipId = null; + } + + Drawing.setClipUrl(plotinfo.plot, plotClipId, gd); + + // stash layer clipId value (null or same as clipId) + // to DRY up Drawing.setClipUrl calls on trace-module and trace layers + // downstream + plotinfo.layerClipId = layerClipId; + } + } + + var xLinesXLeft, xLinesXRight, xLinesYBottom, xLinesYTop, + leftYLineWidth, rightYLineWidth; + var yLinesYBottom, yLinesYTop, yLinesXLeft, yLinesXRight, + connectYBottom, connectYTop; + var extraSubplot; + + function xLinePath(y) { + return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight; + } + + function xLinePathFree(y) { + return 'M' + xa._offset + ',' + y + 'h' + xa._length; + } + + function yLinePath(x) { + return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom; + } + + function yLinePathFree(x) { + return 'M' + x + ',' + ya._offset + 'v' + ya._length; + } + + function mainPath(ax, pathFn, pathFnFree) { + if(!ax.showline || subplot !== ax._mainSubplot) return ''; + if(!ax._anchorAxis) return pathFnFree(ax._mainLinePosition); + var out = pathFn(ax._mainLinePosition); + if(ax.mirror) out += pathFn(ax._mainMirrorPosition); + return out; + } + + for(subplot in fullLayout._plots) { + plotinfo = fullLayout._plots[subplot]; + xa = plotinfo.xaxis; + ya = plotinfo.yaxis; + + /* + * x lines get longer where they meet y lines, to make a crisp corner. + * The x lines get the padding (margin.pad) plus the y line width to + * fill up the corner nicely. Free x lines are excluded - they always + * span exactly the data area of the plot + * + * | XXXXX + * | XXXXX + * | + * +------ + * x1 + * ----- + * x2 + */ + var xPath = 'M0,0'; + if(shouldShowLinesOrTicks(xa, subplot)) { + leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList); + xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0); + rightYLineWidth = findCounterAxisLineWidth(xa, 'right', ya, axList); + xLinesXRight = xa._offset + xa._length + (rightYLineWidth ? (pad + rightYLineWidth) : 0); + xLinesYBottom = getLinePosition(xa, ya, 'bottom'); + xLinesYTop = getLinePosition(xa, ya, 'top'); + + // save axis line positions for extra ticks to reference + // each subplot that gets ticks from "allticks" gets an entry: + // [left or bottom, right or top] + extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot); + if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) { + xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop]; + } + + xPath = mainPath(xa, xLinePath, xLinePathFree); + if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) { + xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop); + } + + plotinfo.xlines + .style('stroke-width', xa._lw + 'px') + .call(Color.stroke, xa.showline ? + xa.linecolor : 'rgba(0,0,0,0)'); + } + plotinfo.xlines.attr('d', xPath); + + /* + * y lines that meet x axes get longer only by margin.pad, because + * the x axes fill in the corner space. Free y axes, like free x axes, + * always span exactly the data area of the plot + * + * | | XXXX + * y2| y1| XXXX + * | | XXXX + * | + * +----- + */ + var yPath = 'M0,0'; + if(shouldShowLinesOrTicks(ya, subplot)) { + connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList); + yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0); + connectYTop = findCounterAxisLineWidth(ya, 'top', xa, axList); + yLinesYTop = ya._offset - (connectYTop ? pad : 0); + yLinesXLeft = getLinePosition(ya, xa, 'left'); + yLinesXRight = getLinePosition(ya, xa, 'right'); + + extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot); + if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) { + ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight]; + } + + yPath = mainPath(ya, yLinePath, yLinePathFree); + if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) { + yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight); + } + + plotinfo.ylines + .style('stroke-width', ya._lw + 'px') + .call(Color.stroke, ya.showline ? + ya.linecolor : 'rgba(0,0,0,0)'); + } + plotinfo.ylines.attr('d', yPath); + } + + Axes.makeClipPaths(gd); + + return Plots.previousPromises(gd); +} + +function shouldShowLinesOrTicks(ax, subplot) { + return (ax.ticks || ax.showline) && + (subplot === ax._mainSubplot || ax.mirror === 'all' || ax.mirror === 'allticks'); +} + +/* + * should we draw a line on counterAx at this side of ax? + * It's assumed that counterAx is known to overlay the subplot we're working on + * but it may not be its main axis. + */ +function shouldShowLineThisSide(ax, side, counterAx) { + // does counterAx get a line at all? + if(!counterAx.showline || !counterAx._lw) return false; + + // are we drawing *all* lines for counterAx? + if(counterAx.mirror === 'all' || counterAx.mirror === 'allticks') return true; + + var anchorAx = counterAx._anchorAxis; + + // is this a free axis? free axes can only have a subplot side-line with all(ticks)? mirroring + if(!anchorAx) return false; + + // in order to handle cases where the user forgot to anchor this axis correctly + // (because its default anchor has the same domain on the relevant end) + // check whether the relevant position is the same. + var sideIndex = alignmentConstants.FROM_BL[side]; + if(counterAx.side === side) { + return anchorAx.domain[sideIndex] === ax.domain[sideIndex]; + } + return counterAx.mirror && anchorAx.domain[1 - sideIndex] === ax.domain[1 - sideIndex]; +} + +/* + * Is there another axis intersecting `side` end of `ax`? + * First look at `counterAx` (the axis for this subplot), + * then at all other potential counteraxes on or overlaying this subplot. + * Take the line width from the first one that has a line. + */ +function findCounterAxisLineWidth(ax, side, counterAx, axList) { + if(shouldShowLineThisSide(ax, side, counterAx)) { + return counterAx._lw; + } + for(var i = 0; i < axList.length; i++) { + var axi = axList[i]; + if(axi._mainAxis === counterAx._mainAxis && shouldShowLineThisSide(ax, side, axi)) { + return axi._lw; + } + } + return 0; +} + +exports.drawMainTitle = function(gd) { + var fullLayout = gd._fullLayout; + + var textAnchor = getMainTitleTextAnchor(fullLayout); + var dy = getMainTitleDy(fullLayout); + + Titles.draw(gd, 'gtitle', { + propContainer: fullLayout, + propName: 'title.text', + placeholder: fullLayout._dfltTitle.plot, + attributes: { + x: getMainTitleX(fullLayout, textAnchor), + y: getMainTitleY(fullLayout, dy), + 'text-anchor': textAnchor, + dy: dy + } + }); +}; + +function getMainTitleX(fullLayout, textAnchor) { + var title = fullLayout.title; + var gs = fullLayout._size; + var hPadShift = 0; + + if(textAnchor === SVG_TEXT_ANCHOR_START) { + hPadShift = title.pad.l; + } else if(textAnchor === SVG_TEXT_ANCHOR_END) { + hPadShift = -title.pad.r; + } + + switch(title.xref) { + case 'paper': + return gs.l + gs.w * title.x + hPadShift; + case 'container': + default: + return fullLayout.width * title.x + hPadShift; + } +} + +function getMainTitleY(fullLayout, dy) { + var title = fullLayout.title; + var gs = fullLayout._size; + var vPadShift = 0; + + if(dy === '0em' || !dy) { + vPadShift = -title.pad.b; + } else if(dy === alignmentConstants.CAP_SHIFT + 'em') { + vPadShift = title.pad.t; + } + + if(title.y === 'auto') { + return gs.t / 2; + } else { + switch(title.yref) { + case 'paper': + return gs.t + gs.h - gs.h * title.y + vPadShift; + case 'container': + default: + return fullLayout.height - fullLayout.height * title.y + vPadShift; + } + } +} + +function getMainTitleTextAnchor(fullLayout) { + var title = fullLayout.title; + + var textAnchor = SVG_TEXT_ANCHOR_MIDDLE; + if(Lib.isRightAnchor(title)) { + textAnchor = SVG_TEXT_ANCHOR_END; + } else if(Lib.isLeftAnchor(title)) { + textAnchor = SVG_TEXT_ANCHOR_START; + } + + return textAnchor; +} + +function getMainTitleDy(fullLayout) { + var title = fullLayout.title; + + var dy = '0em'; + if(Lib.isTopAnchor(title)) { + dy = alignmentConstants.CAP_SHIFT + 'em'; + } else if(Lib.isMiddleAnchor(title)) { + dy = alignmentConstants.MID_SHIFT + 'em'; + } + + return dy; +} + +exports.doTraceStyle = function(gd) { + var calcdata = gd.calcdata; + var editStyleCalls = []; + var i; + + for(i = 0; i < calcdata.length; i++) { + var cd = calcdata[i]; + var cd0 = cd[0] || {}; + var trace = cd0.trace || {}; + var _module = trace._module || {}; + + // See if we need to do arraysToCalcdata + // call it regardless of what change we made, in case + // supplyDefaults brought in an array that was already + // in gd.data but not in gd._fullData previously + var arraysToCalcdata = _module.arraysToCalcdata; + if(arraysToCalcdata) arraysToCalcdata(cd, trace); + + var editStyle = _module.editStyle; + if(editStyle) editStyleCalls.push({fn: editStyle, cd0: cd0}); + } + + if(editStyleCalls.length) { + for(i = 0; i < editStyleCalls.length; i++) { + var edit = editStyleCalls[i]; + edit.fn(gd, edit.cd0); + } + clearGlCanvases(gd); + exports.redrawReglTraces(gd); + } + + Plots.style(gd); + Registry.getComponentMethod('legend', 'draw')(gd); + + return Plots.previousPromises(gd); +}; + +exports.doColorBars = function(gd) { + Registry.getComponentMethod('colorbar', 'draw')(gd); + return Plots.previousPromises(gd); +}; + +// force plot() to redo the layout and replot with the modified layout +exports.layoutReplot = function(gd) { + var layout = gd.layout; + gd.layout = undefined; + return Registry.call('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; + + scene.setViewport(sceneLayout); + } +}; + +exports.drawData = function(gd) { + var fullLayout = gd._fullLayout; + + clearGlCanvases(gd); + + // loop over the base plot modules present on graph + var basePlotModules = fullLayout._basePlotModules; + for(var i = 0; i < basePlotModules.length; i++) { + basePlotModules[i].plot(gd); + } + + exports.redrawReglTraces(gd); + + // styling separate from drawing + Plots.style(gd); + + // draw components that can be drawn on axes, + // and that do not push the margins + Registry.getComponentMethod('shapes', 'draw')(gd); + Registry.getComponentMethod('annotations', 'draw')(gd); + Registry.getComponentMethod('images', 'draw')(gd); + + // Mark the first render as complete + fullLayout._replotting = false; + + return Plots.previousPromises(gd); +}; + +// Draw (or redraw) all regl-based traces in one go, +// useful during drag and selection where buffers of targeted traces are updated, +// but all traces need to be redrawn following clearGlCanvases. +// +// Note that _module.plot for regl trace does NOT draw things +// on the canvas, they only update the buffers. +// Drawing is perform here. +// +// TODO try adding per-subplot option using gl.SCISSOR_TEST for +// non-overlaying, disjoint subplots. +// +// TODO try to include parcoords in here. +// https://github.com/plotly/plotly.js/issues/3069 +exports.redrawReglTraces = function(gd) { + var fullLayout = gd._fullLayout; + + if(fullLayout._has('regl')) { + var fullData = gd._fullData; + var cartesianIds = []; + var polarIds = []; + var i, sp; + + if(fullLayout._hasOnlyLargeSploms) { + fullLayout._splomGrid.draw(); + } + + // N.B. + // - Loop over fullData (not _splomScenes) to preserve splom trace-to-trace ordering + // - Fill list if subplot ids (instead of fullLayout._subplots) to handle cases where all traces + // of a given module are `visible !== true` + for(i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + + if(trace.visible === true && trace._length !== 0) { + if(trace.type === 'splom') { + fullLayout._splomScenes[trace.uid].draw(); + } else if(trace.type === 'scattergl') { + Lib.pushUnique(cartesianIds, trace.xaxis + trace.yaxis); + } else if(trace.type === 'scatterpolargl') { + Lib.pushUnique(polarIds, trace.subplot); + } + } + } + + for(i = 0; i < cartesianIds.length; i++) { + sp = fullLayout._plots[cartesianIds[i]]; + if(sp._scene) sp._scene.draw(); + } + + for(i = 0; i < polarIds.length; i++) { + sp = fullLayout[polarIds[i]]._subplot; + if(sp._scene) sp._scene.draw(); + } + } +}; + +exports.doAutoRangeAndConstraints = function(gd) { + var 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) { + // 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":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. +* +* This 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":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. +* +* This 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":81}],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 + * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus) + * is linearized (for log scale axes) + * + * @return {object} + * - min {array of objects} + * - max {array of objects} + * each object item has fields: + * - val {number} + * - pad {number} + * - extrappad {number} + * - opts {object}: a ref to the passed "options" object + */ +function findExtremes(ax, data, opts) { + if(!opts) opts = {}; + if(!ax._m) ax.setScale(); + + var minArray = []; + var maxArray = []; + + var len = data.length; + var extrapad = opts.padded || false; + var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-'); + var isLog = ax.type === 'log'; + var hasArrayOption = false; + var vpadLinearized = opts.vpadLinearized || false; + var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax; + + function makePadAccessor(item) { + if(Array.isArray(item)) { + hasArrayOption = true; + return function(i) { return Math.max(Number(item[i]||0), 0); }; + } else { + var v = Math.max(Number(item||0), 0); + return function() { return v; }; + } + } + + var ppadplus = makePadAccessor((ax._m > 0 ? + opts.ppadplus : opts.ppadminus) || opts.ppad || 0); + var ppadminus = makePadAccessor((ax._m > 0 ? + opts.ppadminus : opts.ppadplus) || opts.ppad || 0); + var vpadplus = makePadAccessor(opts.vpadplus || opts.vpad); + var vpadminus = makePadAccessor(opts.vpadminus || opts.vpad); + + if(!hasArrayOption) { + // with no arrays other than `data` we don't need to consider + // every point, only the extreme data points + vmin = Infinity; + vmax = -Infinity; + + if(isLog) { + for(i = 0; i < len; i++) { + v = data[i]; + // data is not linearized yet so we still have to filter out negative logs + if(v < vmin && v > 0) vmin = v; + if(v > vmax && v < FP_SAFE) vmax = v; + } + } else { + for(i = 0; i < len; i++) { + v = data[i]; + if(v < vmin && v > -FP_SAFE) vmin = v; + if(v > vmax && v < FP_SAFE) vmax = v; + } + } + + data = [vmin, vmax]; + len = 2; + } + + var collapseOpts = {tozero: tozero, extrapad: extrapad}; + + function addItem(i) { + di = data[i]; + if(!isNumeric(di)) return; + ppadiplus = ppadplus(i); + ppadiminus = ppadminus(i); + + if(vpadLinearized) { + dmin = ax.c2l(di) - vpadminus(i); + dmax = ax.c2l(di) + vpadplus(i); + } else { + vmin = di - vpadminus(i); + vmax = di + vpadplus(i); + // special case for log axes: if vpad makes this object span + // more than an order of mag, clip it to one order. This is so + // we don't have non-positive errors or absurdly large lower + // range due to rounding errors + if(isLog && vmin < vmax / 10) vmin = vmax / 10; + + dmin = ax.c2l(vmin); + dmax = ax.c2l(vmax); + } + + if(tozero) { + dmin = Math.min(0, dmin); + dmax = Math.max(0, dmax); + } + if(goodNumber(dmin)) { + collapseMinArray(minArray, dmin, ppadiminus, collapseOpts); + } + if(goodNumber(dmax)) { + collapseMaxArray(maxArray, dmax, ppadiplus, collapseOpts); + } + } + + // For efficiency covering monotonic or near-monotonic data, + // check a few points at both ends first and then sweep + // through the middle + var iMax = Math.min(6, len); + for(i = 0; i < iMax; i++) addItem(i); + for(i = len - 1; i >= iMax; i--) addItem(i); + + return { + min: minArray, + max: maxArray, + opts: opts + }; +} + +function collapseMinArray(array, newVal, newPad, opts) { + collapseArray(array, newVal, newPad, opts, lessOrEqual); +} + +function collapseMaxArray(array, newVal, newPad, opts) { + collapseArray(array, newVal, newPad, opts, greaterOrEqual); +} + +/** + * collapseArray + * + * Takes items from 'array' and compares them to 'newVal', 'newPad'. + * + * @param {array} array: + * current set of min or max extremes + * @param {number} newVal: + * new value to compare against + * @param {number} newPad: + * pad value associated with 'newVal' + * @param {object} opts: + * - tozero {boolean} + * - extrapad {number} + * @param {function} atLeastAsExtreme: + * comparison function, use + * - lessOrEqual for min 'array' and + * - greaterOrEqual for max 'array' + * + * In practice, 'array' is either + * - 'extremes[ax._id].min' or + * - 'extremes[ax._id].max + * found in traces and layout items that affect autorange. + * + * Since we don't yet know the relationship between pixels and values + * (that's what we're trying to figure out!) AND we don't yet know how + * many pixels `extrapad` represents (it's going to be 5% of the length, + * but we don't want to have to redo calc just because length changed) + * two point must satisfy three criteria simultaneously for one to supersede the other: + * - at least as extreme a `val` + * - at least as big a `pad` + * - an unpadded point cannot supersede a padded point, but any other combination can + * + * Then: + * - If the item supersedes the new point, set includeThis false + * - If the new pt supersedes the item, delete it from 'array' + */ +function collapseArray(array, newVal, newPad, opts, atLeastAsExtreme) { + var tozero = opts.tozero; + var extrapad = opts.extrapad; + var includeThis = true; + + for(var j = 0; j < array.length && includeThis; j++) { + var v = array[j]; + if(atLeastAsExtreme(v.val, newVal) && v.pad >= newPad && (v.extrapad || !extrapad)) { + includeThis = false; + break; + } else if(atLeastAsExtreme(newVal, v.val) && v.pad <= newPad && (extrapad || !v.extrapad)) { + array.splice(j, 1); + j--; + } + } + if(includeThis) { + var clipAtZero = (tozero && newVal === 0); + array.push({ + val: newVal, + pad: clipAtZero ? 0 : newPad, + extrapad: clipAtZero ? false : extrapad + }); + } +} + +// In order to stop overflow errors, don't consider points +// too close to the limits of js floating point +function goodNumber(v) { + return isNumeric(v) && Math.abs(v) < FP_SAFE; +} + +function lessOrEqual(v0, v1) { return v0 <= v1; } +function greaterOrEqual(v0, v1) { return v0 >= v1; } + +},{"../../constants/numerical":149,"../../lib":169,"../../registry":258,"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 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 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 = {}; + +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) + * + * Depends on: + * - ax._mainSubplot (from linkSubplots) + * - ax._mainAxis + * - ax._anchorAxis + * - ax._subplotsWith + * - ax._counterDomainMin, ax._counterDomainMax (optionally, from linkSubplots) + * - ax._tickAngles (on redraw only, old value relinked during supplyDefaults) + * - ax._mainLinePosition (from lsInner) + * - ax._mainMirrorPosition + * - ax._linepositions + * + * Fills in: + * - ax._vals: + * - ax._gridVals: + * - ax._selections: + * - ax._tickAngles: + * - ax._depth (when required only): + * - and calls ax.setScale + */ +axes.drawOne = function(gd, ax, opts) { + opts = opts || {}; + + var i, sp, plotinfo; + + ax.setScale(); + + var fullLayout = gd._fullLayout; + var axId = ax._id; + var axLetter = axId.charAt(0); + var counterLetter = axes.counterLetter(axId); + var mainLinePosition = ax._mainLinePosition; + var mainMirrorPosition = ax._mainMirrorPosition; + var mainPlotinfo = fullLayout._plots[ax._mainSubplot]; + var mainAxLayer = mainPlotinfo[axLetter + 'axislayer']; + + var vals = ax._vals = axes.calcTicks(ax); + + // Add a couple of axis properties that should cause us to recreate + // elements. Used in d3 data function. + var axInfo = [ax.mirror, mainLinePosition, mainMirrorPosition].join('_'); + for(i = 0; i < vals.length; i++) { + vals[i].axInfo = axInfo; + } + + // stash selections to avoid DOM queries e.g. + // - stash tickLabels selection, so that drawTitle can use it to scoot title + ax._selections = {}; + // stash tick angle (including the computed 'auto' values) per tick-label class + // linkup 'previous' tick angles on redraws + if(ax._tickAngles) ax._prevTickAngles = ax._tickAngles; + ax._tickAngles = {}; + // measure [in px] between axis position and outward-most part of bounding box + // (touching either the tick label or ticks) + // depth can be expansive to compute, so we only do so when required + ax._depth = null; + + // calcLabelLevelBbox can be expensive, + // so make sure to not call it twice during the same Axes.drawOne call + // by stashing label-level bounding boxes per tick-label class + var llbboxes = {}; + function getLabelLevelBbox(suffix) { + var cls = axId + (suffix || 'tick'); + if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls); + return llbboxes[cls]; + } + + if(!ax.visible) return; + + var 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) { + var subplotsWithAx = ax._subplotsWith; + + // keep track of which subplots (by main counter axis) we've already + // drawn grids for, so we don't overdraw overlaying subplots + var finishedGrids = {}; + + for(i = 0; i < subplotsWithAx.length; i++) { + sp = subplotsWithAx[i]; + plotinfo = fullLayout._plots[sp]; + + var counterAxis = plotinfo[counterLetter + 'axis']; + var mainCounterID = counterAxis._mainAxis._id; + if(finishedGrids[mainCounterID]) continue; + finishedGrids[mainCounterID] = 1; + + var gridPath = axLetter === 'x' ? + 'M0,' + counterAxis._offset + 'v' + counterAxis._length : + 'M' + counterAxis._offset + ',0h' + counterAxis._length; + + axes.drawGrid(gd, ax, { + vals: gridVals, + counterAxis: counterAxis, + layer: plotinfo.gridlayer.select('.' + axId), + path: gridPath, + transFn: 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 pad = {x: 2, y: 10}[axLetter]; + + seq.push(function() { + var bboxKey = {x: 'height', y: 'width'}[axLetter]; + var standoff = getLabelLevelBbox()[bboxKey] + pad + + (ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0); + + return axes.drawLabels(gd, ax, { + vals: getSecondaryLabelVals(ax, vals), + layer: mainAxLayer, + cls: axId + 'tick2', + repositionOnUpdate: true, + secondary: true, + transFn: transFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition + standoff * tickSigns[4]) + }); + }); + + seq.push(function() { + ax._depth = tickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition); + + return drawDividers(gd, ax, { + vals: dividerVals, + layer: mainAxLayer, + path: axes.makeTickPath(ax, mainLinePosition, tickSigns[4], ax._depth), + transFn: transFn + }); + }); + } else if(ax.title.hasOwnProperty('standoff')) { + seq.push(function() { + ax._depth = tickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition); + }); + } + + var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax); + + seq.push(function() { + var s = ax.side.charAt(0); + var sMirror = OPPOSITE_SIDE[ax.side].charAt(0); + var pos = axes.getPxPosition(gd, ax); + var outsideTickLen = ax.ticks === 'outside' ? ax.ticklen : 0; + var llbbox; + + var push; + var mirrorPush; + var rangeSliderPush; + + if(ax.automargin || hasRangeSlider) { + if(ax.type === 'multicategory') { + llbbox = getLabelLevelBbox('tick2'); + } else { + llbbox = getLabelLevelBbox(); + if(axLetter === 'x' && s === 'b') { + ax._depth = Math.max(llbbox.width > 0 ? llbbox.bottom - pos : 0, outsideTickLen); + } + } + } + + if(ax.automargin) { + push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + var domainIndices = [0, 1]; + + if(axLetter === 'x') { + if(s === 'b') { + push[s] = ax._depth; + } else { + push[s] = ax._depth = Math.max(llbbox.width > 0 ? pos - llbbox.top : 0, outsideTickLen); + domainIndices.reverse(); + } + + if(llbbox.width > 0) { + var rExtra = llbbox.right - (ax._offset + ax._length); + if(rExtra > 0) { + push.xr = 1; + push.r = rExtra; + } + var lExtra = ax._offset - llbbox.left; + if(lExtra > 0) { + push.xl = 0; + push.l = lExtra; + } + } + } else { + if(s === 'l') { + push[s] = ax._depth = Math.max(llbbox.height > 0 ? pos - llbbox.left : 0, outsideTickLen); + } else { + push[s] = ax._depth = Math.max(llbbox.height > 0 ? llbbox.right - pos : 0, outsideTickLen); + domainIndices.reverse(); + } + + if(llbbox.height > 0) { + var bExtra = llbbox.bottom - (ax._offset + ax._length); + if(bExtra > 0) { + push.yb = 0; + push.b = bExtra; + } + var tExtra = ax._offset - llbbox.top; + if(tExtra > 0) { + push.yt = 1; + push.t = tExtra; + } + } + } + + push[counterLetter] = ax.anchor === 'free' ? + ax.position : + ax._anchorAxis.domain[domainIndices[0]]; + + if(ax.title.text !== fullLayout._dfltTitle[axLetter]) { + push[s] += approxTitleDepth(ax) + (ax.title.standoff || 0); + } + + if(ax.mirror && ax.anchor !== 'free') { + mirrorPush = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0}; + + mirrorPush[sMirror] = ax.linewidth; + if(ax.mirror && ax.mirror !== true) mirrorPush[sMirror] += outsideTickLen; + + if(ax.mirror === true || ax.mirror === 'ticks') { + mirrorPush[counterLetter] = ax._anchorAxis.domain[domainIndices[1]]; + } else if(ax.mirror === 'all' || ax.mirror === 'allticks') { + mirrorPush[counterLetter] = [ax._counterDomainMin, ax._counterDomainMax][domainIndices[1]]; + } + } + } + + if(hasRangeSlider) { + rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax); + } + + Plots.autoMargin(gd, axAutoMarginID(ax), push); + Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush); + Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush); + }); + + if(!opts.skipTitle && + !(hasRangeSlider && ax.side === 'bottom') + ) { + seq.push(function() { return drawTitle(gd, ax); }); + } + + return Lib.syncOrAsync(seq); +}; + +function getBoundaryVals(ax, vals) { + var out = []; + var i; + + // boundaryVals are never used for labels; + // no need to worry about the other tickTextObj keys + var _push = function(d, bndIndex) { + var xb = d.xbnd[bndIndex]; + if(xb !== null) { + out.push(Lib.extendFlat({}, d, {x: xb})); + } + }; + + if(vals.length) { + for(i = 0; i < vals.length; i++) { + _push(vals[i], 0); + } + _push(vals[i - 1], 1); + } + + return out; +} + +function getSecondaryLabelVals(ax, vals) { + var out = []; + var lookup = {}; + + for(var i = 0; i < vals.length; i++) { + var d = vals[i]; + if(lookup[d.text2]) { + lookup[d.text2].push(d.x); + } else { + lookup[d.text2] = [d.x]; + } + } + + for(var k in lookup) { + out.push(tickTextObj(ax, Lib.interp(lookup[k], 0.5), k)); + } + + return out; +} + +function getDividerVals(ax, vals) { + var out = []; + var i, current; + + // 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 calcLabelLevelBbox(ax, cls) { + var top, bottom; + var left, right; + + if(ax._selections[cls].size()) { + top = Infinity; + bottom = -Infinity; + left = Infinity; + right = -Infinity; + ax._selections[cls].each(function() { + var thisLabel = selectTickLabel(this); + // Use parent node , to make Drawing.bBox + // retrieve a bbox computed with transform info + // + // To improve perf, it would be nice to use `thisLabel.node()` + // (like in fixLabelOverlaps) instead and use Axes.getPxPosition + // together with the makeLabelFns outputs and `tickangle` + // to compute one bbox per (tick value x tick style) + var bb = Drawing.bBox(thisLabel.node().parentNode); + top = Math.min(top, bb.top); + bottom = Math.max(bottom, bb.bottom); + left = Math.min(left, bb.left); + right = Math.max(right, bb.right); + }); + } else { + top = 0; + bottom = 0; + left = 0; + right = 0; + } + + return { + top: top, + bottom: bottom, + left: left, + right: right, + height: bottom - top, + width: right - left + }; +} + +/** + * Which direction do the 'ax.side' values, and free ticks go? + * + * @param {object} ax (full) axis object + * - {string} _id (starting with 'x' or 'y') + * - {string} side + * - {string} ticks + * @return {array} all entries are either -1 or 1 + * - [0]: sign for top/right ticks (i.e. negative SVG direction) + * - [1]: sign for bottom/left ticks (i.e. positive SVG direction) + * - [2]: sign for ticks corresponding to 'ax.side' + * - [3]: sign for ticks mirroring 'ax.side' + * - [4]: sign of arrow starting at axis pointing towards margin + */ +axes.getTickSigns = function(ax) { + var axLetter = ax._id.charAt(0); + var sideOpposite = {x: 'top', y: 'right'}[axLetter]; + var main = ax.side === sideOpposite ? 1 : -1; + var out = [-1, 1, main, -main]; + // then we flip if outside XOR y axis + if((ax.ticks !== 'inside') === (axLetter === 'x')) { + out = out.map(function(v) { return -v; }); + } + // independent of `ticks`; do not flip this one + if(ax.side) { + out.push({l: -1, t: -1, r: 1, b: 1}[ax.side.charAt(0)]); + } + return out; +}; + +/** + * Make axis translate transform function + * + * @param {object} ax (full) axis object + * - {string} _id + * - {number} _offset + * - {fn} l2p + * @return {fn} function of calcTicks items + */ +axes.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} sgn tick sign + * @param {number (optional)} len tick length + * @return {string} + */ +axes.makeTickPath = function(ax, shift, sgn, len) { + len = len !== undefined ? len : ax.ticklen; + + var axLetter = ax._id.charAt(0); + var pad = (ax.linewidth || 1) / 2; + + return axLetter === 'x' ? + 'M0,' + (shift + pad * sgn) + 'v' + (len * sgn) : + 'M' + (shift + pad * sgn) + ',0h' + (len * sgn); +}; + +/** + * Make axis tick label x, y and anchor functions + * + * @param {object} ax (full) axis object + * - {string} _id + * - {string} ticks + * - {number} ticklen + * - {string} side + * - {number} linewidth + * - {number} tickfont.size + * - {boolean} showline + * @param {number} shift + * @param {number} angle [in degrees] ... + * @return {object} + * - {fn} xFn + * - {fn} yFn + * - {fn} anchorFn + * - {fn} heightFn + * - {number} labelStandoff (gap parallel to ticks) + * - {number} labelShift (gap perpendicular to ticks) + */ +axes.makeLabelFns = function(ax, shift, angle) { + var 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 + * - {object} (optional)} _prevTickAngles + * @param {object} opts + * - {array of object} vals (calcTicks output-like) + * - {d3 selection} layer + * - {string (optional)} cls (node className) + * - {boolean} repositionOnUpdate (set to true to reposition update selection) + * - {boolean} secondary + * - {fn} transFn + * - {object} labelFns + * + {fn} xFn + * + {fn} yFn + * + {fn} anchorFn + * + {fn} heightFn + */ +axes.drawLabels = function(gd, ax, opts) { + opts = opts || {}; + + var fullLayout = gd._fullLayout; + var axId = ax._id; + var axLetter = axId.charAt(0); + var cls = opts.cls || axId + 'tick'; + var vals = opts.vals; + var labelFns = opts.labelFns; + var tickAngle = opts.secondary ? 0 : ax.tickangle; + var prevAngle = (ax._prevTickAngles || {})[cls]; + + var tickLabels = opts.layer.selectAll('g.' + cls) + .data(ax.showticklabels ? vals : [], tickDataFn); + + var labelsReady = []; + + tickLabels.enter().append('g') + .classed(cls, 1) + .append('text') + // only so tex has predictable alignment that we can + // alter later + .attr('text-anchor', 'middle') + .each(function(d) { + var thisLabel = d3.select(this); + var newPromise = gd._promises.length; + + thisLabel + .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d)) + .call(Drawing.font, d.font, d.fontSize, d.fontColor) + .text(d.text) + .call(svgTextUtils.convertToTspans, gd); + + if(gd._promises[newPromise]) { + // if we have an async label, we'll deal with that + // all here so take it out of gd._promises and + // instead position the label and promise this in + // labelsReady + labelsReady.push(gd._promises.pop().then(function() { + positionLabels(thisLabel, tickAngle); + })); + } else { + // sync label: just position it now. + positionLabels(thisLabel, tickAngle); + } + }); + + 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, (prevAngle + 1) ? prevAngle : tickAngle); + + function allLabelsReady() { + return labelsReady.length && Promise.all(labelsReady); + } + + var autoangle = null; + + function fixLabelOverlaps() { + positionLabels(tickLabels, tickAngle); + + // check for auto-angling if x labels overlap + // don't auto-angle at all for log axes with + // base and digit format + if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) && + (ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D') + ) { + autoangle = 0; + + var maxFontSize = 0; + var lbbArray = []; + var i; + + tickLabels.each(function(d) { + maxFontSize = Math.max(maxFontSize, d.fontSize); + + var x = ax.l2p(d.x); + var thisLabel = selectTickLabel(this); + var bb = Drawing.bBox(thisLabel.node()); + + lbbArray.push({ + // ignore about y, just deal with x overlaps + top: 0, + bottom: 10, + height: 10, + left: x - bb.width / 2, + // impose a 2px gap + right: x + bb.width / 2 + 2, + width: bb.width + 2 + }); + }); + + if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) { + var gap = 2; + if(ax.ticks) gap += ax.tickwidth / 2; + + // TODO should secondary labels also fall into this fix-overlap regime? + + for(i = 0; i < lbbArray.length; i++) { + var xbnd = vals[i].xbnd; + var lbb = lbbArray[i]; + if( + (xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) || + (xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap) + ) { + autoangle = 90; + break; + } + } + } else { + var vLen = vals.length; + var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1); + var 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._selections) { + ax._selections[cls] = tickLabels; + } + + var seq = [allLabelsReady]; + + // N.B. during auto-margin redraws, if the axis fixed its label overlaps + // by rotating 90 degrees, do not attempt to re-fix its label overlaps + // as this can lead to infinite redraw loops! + if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) { + autoangle = 90; + seq.push(function() { + positionLabels(tickLabels, prevAngle); + }); + } else { + seq.push(fixLabelOverlaps); + } + + // save current tick angle for future redraws + if(ax._tickAngles) { + seq.push(function() { + ax._tickAngles[cls] = autoangle === null ? + (isNumeric(tickAngle) ? tickAngle : 0) : + autoangle; + }); + } + + var done = Lib.syncOrAsync(seq); + if(done && done.then) gd._promises.push(done); + return done; +}; + +/** + * Draw axis dividers + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} showdividers + * - {number} dividerwidth + * - {string} dividercolor + * @param {object} opts + * - {array of object} vals (calcTicks output-like) + * - {d3 selection} layer + * - {fn} path + * - {fn} transFn + */ +function drawDividers(gd, ax, opts) { + var cls = ax._id + 'divider'; + var vals = opts.vals; + + var dividers = opts.layer.selectAll('path.' + cls) + .data(vals, tickDataFn); + + dividers.exit().remove(); + + dividers.enter().insert('path', ':first-child') + .classed(cls, 1) + .classed('crisp', 1) + .call(Color.stroke, ax.dividercolor) + .style('stroke-width', Drawing.crispRound(gd, ax.dividerwidth, 1) + 'px'); + + dividers + .attr('transform', opts.transFn) + .attr('d', opts.path); +} + +/** + * Get axis position in px, that is the distance for the graph's + * top (left) edge for x (y) axes. + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} side + * if anchored: + * - {object} _anchorAxis + * Otherwise: + * - {number} position + * @return {number} + */ +axes.getPxPosition = function(gd, ax) { + var gs = gd._fullLayout._size; + var axLetter = ax._id.charAt(0); + var side = ax.side; + var anchorAxis; + + if(ax.anchor !== 'free') { + anchorAxis = ax._anchorAxis; + } else if(axLetter === 'x') { + anchorAxis = { + _offset: gs.t + (1 - (ax.position || 0)) * gs.h, + _length: 0 + }; + } else if(axLetter === 'y') { + anchorAxis = { + _offset: gs.l + (ax.position || 0) * gs.w, + _length: 0 + }; + } + + if(side === 'top' || side === 'left') { + return anchorAxis._offset; + } else if(side === 'bottom' || side === 'right') { + return anchorAxis._offset + anchorAxis._length; + } +}; + +/** + * Approximate axis title depth (w/o computing its bounding box) + * + * @param {object} ax (full) axis object + * - {string} title.text + * - {number} title.font.size + * - {number} title.standoff + * @return {number} (in px) + */ +function approxTitleDepth(ax) { + var fontSize = ax.title.font.size; + var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length; + if(ax.title.hasOwnProperty('standoff')) { + return extraLines ? + fontSize * (CAP_SHIFT + (extraLines * LINE_SPACING)) : + fontSize * CAP_SHIFT; + } else { + return extraLines ? + fontSize * (extraLines + 1) * LINE_SPACING : + fontSize; + } +} + +/** + * Draw axis title, compute default standoff if necessary + * + * @param {DOM element} gd + * @param {object} ax (full) axis object + * - {string} _id + * - {string} _name + * - {string} side + * - {number} title.font.size + * - {object} _selections + * + * - {number} _depth + * - {number} title.standoff + * OR + * - {number} linewidth + * - {boolean} showticklabels + */ +function drawTitle(gd, ax) { + var fullLayout = gd._fullLayout; + var axId = ax._id; + var axLetter = axId.charAt(0); + var fontSize = ax.title.font.size; + + var titleStandoff; + + if(ax.title.hasOwnProperty('standoff')) { + titleStandoff = ax._depth + ax.title.standoff + approxTitleDepth(ax); + } else { + 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 pos = axes.getPxPosition(gd, ax); + var transform, x, y; + + if(axLetter === 'x') { + x = ax._offset + ax._length / 2; + y = (ax.side === 'top') ? pos - titleStandoff : pos + titleStandoff; + } else { + y = ax._offset + ax._length / 2; + x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; + transform = {rotate: '-90', offset: 0}; + } + + var avoid; + + if(ax.type !== 'multicategory') { + var tickLabels = ax._selections[ax._id + 'tick']; + + avoid = { + selection: tickLabels, + side: ax.side + }; + + if(tickLabels && tickLabels.node() && tickLabels.node().parentNode) { + var translation = Drawing.getTranslate(tickLabels.node().parentNode); + avoid.offsetLeft = translation.x; + avoid.offsetTop = translation.y; + } + + if(ax.title.hasOwnProperty('standoff')) { + avoid.pad = 0; + } + } + + return Titles.draw(gd, axId + 'title', { + propContainer: ax, + propName: ax._name + '.title.text', + placeholder: fullLayout._dfltTitle[axLetter], + avoid: avoid, + transform: transform, + attributes: {x: x, y: y, 'text-anchor': 'middle'} + }); +} + +axes.shouldShowZeroLine = function(gd, ax, counterAxis) { + var rng = Lib.simpleMap(ax.range, ax.r2l); + return ( + (rng[0] * rng[1] <= 0) && + ax.zeroline && + (ax.type === 'linear' || ax.type === '-') && + ( + clipEnds(ax, 0) || + !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) || + hasBarsOrFill(gd, ax) + ) + ); +}; + +axes.clipEnds = function(ax, vals) { + return vals.filter(function(d) { return clipEnds(ax, d.x); }); +}; + +function clipEnds(ax, l) { + var p = ax.l2p(l); + return (p > 1 && p < ax._length - 1); +} + +function anyCounterAxLineAtZero(gd, ax, counterAxis, rng) { + var mainCounterAxis = counterAxis._mainAxis; + if(!mainCounterAxis) return; + + var fullLayout = gd._fullLayout; + var axLetter = ax._id.charAt(0); + var counterLetter = axes.counterLetter(ax._id); + + var zeroPosition = ax._offset + ( + ((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ? + 0 : ax._length + ); + + function lineNearZero(ax2) { + if(!ax2.showline || !ax2.linewidth) return false; + var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1); + + function closeEnough(pos2) { + return typeof pos2 === 'number' && Math.abs(pos2 - zeroPosition) < tolerance; + } + + if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) { + return true; + } + var linePositions = ax2._linepositions || {}; + for(var k in linePositions) { + if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) { + return true; + } + } + } + + var plotinfo = fullLayout._plots[counterAxis._mainSubplot]; + if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) { + return lineNearZero(counterAxis, zeroPosition); + } + + var counterLetterAxes = axes.list(gd, counterLetter); + for(var i = 0; i < counterLetterAxes.length; i++) { + var counterAxis2 = counterLetterAxes[i]; + if( + counterAxis2._mainAxis === mainCounterAxis && + lineNearZero(counterAxis2, zeroPosition) + ) { + return true; + } + } +} + +function hasBarsOrFill(gd, ax) { + var fullData = gd._fullData; + var subplot = ax._mainSubplot; + var axLetter = ax._id.charAt(0); + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + + if(trace.visible === true && (trace.xaxis + trace.yaxis) === subplot) { + if( + Registry.traceIs(trace, 'bar-like') && + trace.orientation === {x: 'h', y: 'v'}[axLetter] + ) return true; + + if( + trace.fill && + trace.fill.charAt(trace.fill.length - 1) === axLetter + ) return true; + } + } + return false; +} + +function selectTickLabel(gTick) { + var s = d3.select(gTick); + var mj = s.select('.text-math-group'); + return mj.empty() ? s.select('text') : mj; +} + +/** + * Find all margin pushers for 2D axes and reserve them for later use + * Both label and rangeslider automargin calculations happen later so + * we need to explicitly allow their ids in order to not delete them. + * + * TODO: can we pull the actual automargin calls forward to avoid this hack? + * We're probably also doing multiple redraws in this case, would be faster + * if we can just do the whole calculation ahead of time and draw once. + */ +axes.allowAutoMargin = function(gd) { + var axList = axes.list(gd, '', true); + for(var i = 0; i < axList.length; i++) { + var ax = axList[i]; + if(ax.automargin) { + Plots.allowAutoMargin(gd, axAutoMarginID(ax)); + if(ax.mirror) { + Plots.allowAutoMargin(gd, axMirrorAutoMarginID(ax)); + } + } + if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) { + Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax)); + } + } +}; + +function axAutoMarginID(ax) { return ax._id + '.automargin'; } +function axMirrorAutoMarginID(ax) { return axAutoMarginID(ax) + '.mirror'; } +function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; } + +// swap all the presentation attributes of the axes showing these traces +axes.swap = function(gd, traces) { + var axGroups = makeAxisGroups(gd, traces); + + for(var i = 0; i < axGroups.length; i++) { + swapAxisGroup(gd, axGroups[i].x, axGroups[i].y); + } +}; + +function makeAxisGroups(gd, traces) { + var groups = []; + var i, j; + + for(i = 0; i < traces.length; i++) { + var groupsi = []; + var xi = gd._fullData[traces[i]].xaxis; + var yi = gd._fullData[traces[i]].yaxis; + if(!xi || !yi) continue; // not a 2D cartesian trace? + + for(j = 0; j < groups.length; j++) { + if(groups[j].x.indexOf(xi) !== -1 || groups[j].y.indexOf(yi) !== -1) { + groupsi.push(j); + } + } + + if(!groupsi.length) { + groups.push({x: [xi], y: [yi]}); + continue; + } + + var group0 = groups[groupsi[0]]; + var groupj; + + if(groupsi.length > 1) { + for(j = 1; j < groupsi.length; j++) { + groupj = groups[groupsi[j]]; + mergeAxisGroups(group0.x, groupj.x); + mergeAxisGroups(group0.y, groupj.y); + } + } + mergeAxisGroups(group0.x, [xi]); + mergeAxisGroups(group0.y, [yi]); + } + + return groups; +} + +function mergeAxisGroups(intoSet, fromSet) { + for(var i = 0; i < fromSet.length; i++) { + if(intoSet.indexOf(fromSet[i]) === -1) intoSet.push(fromSet[i]); + } +} + +function swapAxisGroup(gd, xIds, yIds) { + var xFullAxes = []; + var yFullAxes = []; + var layout = gd.layout; + var i, j; + + for(i = 0; i < xIds.length; i++) xFullAxes.push(axes.getFromId(gd, xIds[i])); + for(i = 0; i < yIds.length; i++) yFullAxes.push(axes.getFromId(gd, yIds[i])); + + var allAxKeys = Object.keys(axAttrs); + + var noSwapAttrs = [ + 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType' + ]; + var numericTypes = ['linear', 'log']; + + for(i = 0; i < allAxKeys.length; i++) { + var keyi = allAxKeys[i]; + var xVal = xFullAxes[0][keyi]; + var yVal = yFullAxes[0][keyi]; + var allEqual = true; + var coerceLinearX = false; + var coerceLinearY = false; + if(keyi.charAt(0) === '_' || typeof xVal === 'function' || + noSwapAttrs.indexOf(keyi) !== -1) { + continue; + } + for(j = 1; j < xFullAxes.length && allEqual; j++) { + var xVali = xFullAxes[j][keyi]; + if(keyi === 'type' && numericTypes.indexOf(xVal) !== -1 && + numericTypes.indexOf(xVali) !== -1 && xVal !== xVali) { + // type is special - if we find a mixture of linear and log, + // coerce them all to linear on flipping + coerceLinearX = true; + } else if(xVali !== xVal) allEqual = false; + } + for(j = 1; j < yFullAxes.length && allEqual; j++) { + var yVali = yFullAxes[j][keyi]; + if(keyi === 'type' && numericTypes.indexOf(yVal) !== -1 && + numericTypes.indexOf(yVali) !== -1 && yVal !== yVali) { + // type is special - if we find a mixture of linear and log, + // coerce them all to linear on flipping + coerceLinearY = true; + } else if(yFullAxes[j][keyi] !== yVal) allEqual = false; + } + if(allEqual) { + if(coerceLinearX) layout[xFullAxes[0]._name].type = 'linear'; + if(coerceLinearY) layout[yFullAxes[0]._name].type = 'linear'; + swapAxisAttrs(layout, keyi, xFullAxes, yFullAxes, gd._fullLayout._dfltTitle); + } + } + + // now swap x&y for any annotations anchored to these x & y + for(i = 0; i < gd._fullLayout.annotations.length; i++) { + var ann = gd._fullLayout.annotations[i]; + if(xIds.indexOf(ann.xref) !== -1 && + yIds.indexOf(ann.yref) !== -1) { + Lib.swapAttrs(layout.annotations[i], ['?']); + } + } +} + +function swapAxisAttrs(layout, key, xFullAxes, yFullAxes, dfltTitle) { + // in case the value is the default for either axis, + // look at the first axis in each list and see if + // this key's value is undefined + var np = Lib.nestedProperty; + var xVal = np(layout[xFullAxes[0]._name], key).get(); + var yVal = np(layout[yFullAxes[0]._name], key).get(); + var i; + + if(key === 'title') { + // special handling of placeholder titles + if(xVal && xVal.text === dfltTitle.x) { + xVal.text = dfltTitle.y; + } + if(yVal && yVal.text === dfltTitle.y) { + yVal.text = dfltTitle.x; + } + } + + for(i = 0; i < xFullAxes.length; i++) { + np(layout, xFullAxes[i]._name + '.' + key).set(yVal); + } + for(i = 0; i < yFullAxes.length; i++) { + np(layout, yFullAxes[i]._name + '.' + key).set(xVal); + } +} + +function isAngular(ax) { + return ax._id === 'angularaxis'; +} + +},{"../../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. +* +* This 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":18}],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":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. +* +* This 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":258,"./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":18}],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: [ + 'imagelayer', + 'heatmaplayer', + 'contourcarpetlayer', 'contourlayer', + 'funnellayer', 'waterfalllayer', 'barlayer', + 'carpetlayer', + 'violinlayer', + 'boxlayer', + 'ohlclayer', + 'scattercarpetlayer', 'scatterlayer' + ], + + clipOnAxisFalseQuery: [ + '.scatterlayer', + '.barlayer', + '.funnellayer', + '.waterfalllayer' + ], + + layerValue2layerClass: { + 'above traces': 'above', + 'below traces': 'below' + } +}; + +},{"../../lib/regex":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, 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; + 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', constrainDflt); + 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 && + !(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', scaleanchorDflt); + } + + 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() { + 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":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. +* +* This 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) && gd._fullLayout._plots[subplot]) { + Fx.hover(gd, evt, subplot); + } + }; + + Fx.hover(gd, evt, subplot); + + // Note that we have *not* used the cached fullLayout variable here + // since that may be outdated when this is called as a callback later on + gd._fullLayout._lasthover = maindrag; + gd._fullLayout._hoversubplot = subplot; + }; + + /* + * IMPORTANT: + * We must check for the presence of the drag cover here. + * If we don't, a 'mouseout' event is triggered on the + * maindrag before each 'click' event, which has the effect + * of clearing the hoverdata; thus, cancelling the click event. + */ + maindrag.onmouseout = function(evt) { + if(gd._dragging) return; + + // When the mouse leaves this maindrag, unset the hovered subplot. + // This may cause problems if it leaves the subplot directly *onto* + // another subplot, but that's a tiny corner case at the moment. + gd._fullLayout._hoversubplot = null; + + dragElement.unhover(gd, evt); + }; + + // corner draggers + if(gd._context.showAxisDragHandles) { + makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE, + DRAGGERSIZE, DRAGGERSIZE, 'n', 'w'); + makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE, + DRAGGERSIZE, DRAGGERSIZE, 'n', 'e'); + makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length, + DRAGGERSIZE, DRAGGERSIZE, 's', 'w'); + makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length, + DRAGGERSIZE, DRAGGERSIZE, 's', 'e'); + } + } + if(gd._context.showAxisDragHandles) { + // x axis draggers - if you have overlaid plots, + // these drag each axis separately + if(subplot === xa._mainSubplot) { + // the y position of the main x axis line + var y0 = xa._mainLinePosition; + if(xa.side === 'top') y0 -= DRAGGERSIZE; + makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0, + xa._length * 0.8, DRAGGERSIZE, '', 'ew'); + makeDragBox(gd, plotinfo, xa._offset, y0, + xa._length * 0.1, DRAGGERSIZE, '', 'w'); + makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0, + xa._length * 0.1, DRAGGERSIZE, '', 'e'); + } + // y axis draggers + if(subplot === ya._mainSubplot) { + // the x position of the main y axis line + var x0 = ya._mainLinePosition; + if(ya.side !== 'right') x0 -= DRAGGERSIZE; + makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1, + DRAGGERSIZE, ya._length * 0.8, 'ns', ''); + makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9, + DRAGGERSIZE, ya._length * 0.1, 's', ''); + makeDragBox(gd, plotinfo, x0, ya._offset, + DRAGGERSIZE, ya._length * 0.1, 'n', ''); + } + } + }); + + // In case you mousemove over some hovertext, send it to Fx.hover too + // we do this so that we can put the hover text in front of everything, + // but still be able to interact with everything as if it isn't there + var hoverLayer = fullLayout._hoverlayer.node(); + + hoverLayer.onmousemove = function(evt) { + evt.target = gd._fullLayout._lasthover; + Fx.hover(gd, evt, fullLayout._hoversubplot); + }; + + hoverLayer.onclick = function(evt) { + evt.target = gd._fullLayout._lasthover; + Fx.click(gd, evt); + }; + + // also delegate mousedowns... TODO: does this actually work? + hoverLayer.onmousedown = function(evt) { + gd._fullLayout._lasthover.onmousedown(evt); + }; + + exports.updateFx(gd); +}; + +// Minimal set of update needed on 'modebar' edits. +// We only need to update the cursor style. +// +// Note that changing the axis configuration and/or the fixedrange attribute +// should trigger a full initInteractions. +exports.updateFx = function(gd) { + var fullLayout = gd._fullLayout; + var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair'; + setCursor(fullLayout._draggers, cursor); +}; + +},{"../../components/dragelement":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. +* +* This 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":258}],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":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. +* +* This 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', + + }), + standoff: { + valType: 'number', + + min: 0, + editType: 'ticks', + + }, + editType: 'ticks' + }, + type: { + valType: 'enumerated', + // '-' means we haven't yet run autotype or couldn't find any data + // it gets turned into linear in gd._fullLayout but not copied back + // to gd.data like the others are. + values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'], + dflt: '-', + + editType: 'calc', + // we forget when an axis has been autotyped, just writing the auto + // value back to the input - so it doesn't make sense to template this. + // Note: we do NOT prohibit this in `coerce`, so if someone enters a + // type in the template explicitly it will be honored as the default. + _noTemplating: true, + + }, + 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,"../../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 yaMustNotReverse = {}; + var yaMayReverse = {}; + var axHasImage = {}; + var outerTicks = {}; + var noGrids = {}; + var i, j; + + // look for axes in the data + for(i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + if(!traceIs(trace, 'cartesian') && !traceIs(trace, 'gl2d')) continue; + + var xaName; + if(trace.xaxis) { + xaName = id2name(trace.xaxis); + appendList(ax2traces, xaName, trace); + } else if(trace.xaxes) { + for(j = 0; j < trace.xaxes.length; j++) { + appendList(ax2traces, id2name(trace.xaxes[j]), trace); + } + } + + var yaName; + if(trace.yaxis) { + yaName = id2name(trace.yaxis); + appendList(ax2traces, yaName, trace); + } else if(trace.yaxes) { + for(j = 0; j < trace.yaxes.length; j++) { + appendList(ax2traces, id2name(trace.yaxes[j]), trace); + } + } + + // logic for funnels + if(trace.type === 'funnel') { + if(trace.orientation === 'h') { + if(xaName) xaMayHide[xaName] = true; + if(yaName) yaMayReverse[yaName] = true; + } else { + if(yaName) yaMayHide[yaName] = true; + } + } else if(trace.type === 'image') { + if(yaName) axHasImage[yaName] = true; + if(xaName) axHasImage[xaName] = true; + } else { + if(yaName) { + yaMustDisplay[yaName] = true; + yaMustNotReverse[yaName] = true; + } + + if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) { + if(xaName) xaMustDisplay[xaName] = true; + } + } + + // Two things trigger axis visibility: + // 1. is not carpet + // 2. carpet that's not cheater + + // The above check for definitely-not-cheater is not adequate. This + // second list tracks which axes *could* be a cheater so that the + // full condition triggering hiding is: + // *could* be a cheater and *is not definitely visible* + if(trace.type === 'carpet' && trace._cheater) { + if(xaName) xaMayHide[xaName] = true; + } + + // check for default formatting tweaks + if(traceIs(trace, '2dMap')) { + outerTicks[xaName] = true; + outerTicks[yaName] = true; + } + + if(traceIs(trace, 'oriented')) { + var positionAxis = trace.orientation === 'h' ? yaName : xaName; + noGrids[positionAxis] = true; + } + } + + var subplots = layoutOut._subplots; + var xIds = subplots.xaxis; + var yIds = subplots.yaxis; + var xNames = Lib.simpleMap(xIds, id2name); + var yNames = Lib.simpleMap(yIds, id2name); + var axNames = xNames.concat(yNames); + + // plot_bgcolor only makes sense if there's a (2D) plot! + // TODO: bgcolor for each subplot, to inherit from the main one + var plotBgColor = Color.background; + if(xIds.length && yIds.length) { + plotBgColor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor'); + } + + var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor); + + 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' && + ( + (!yaMustNotReverse[axName] && yaMayReverse[axName]) || + axHasImage[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 + }); + + coerce('title.standoff'); + + 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]; + + 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++) { + 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":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. +* +* This 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":169,"tinycolor2":34}],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":18}],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":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. +* +* This 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(ax.rangemode === 'tozero' || ax.rangemode === 'nonnegative') { + dflt[0] = 0; + } + + if(!range || range.length !== 2) { + Lib.nestedProperty(ax, rangeAttr).set(dflt); + return; + } + + if(ax.type === 'date' && !ax.autorange) { + // check if milliseconds or js date objects are provided for range + // and convert to date strings + range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar); + range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar); + } + + for(i = 0; i < 2; i++) { + if(ax.type === 'date') { + if(!Lib.isDateTime(range[i], ax.calendar)) { + ax[rangeAttr] = dflt; + break; + } + + if(ax.r2l(range[0]) === ax.r2l(range[1])) { + // split by +/- 1 second + var linCenter = Lib.constrain(ax.r2l(range[0]), + Lib.MIN_MS + 1000, Lib.MAX_MS - 1000); + range[0] = ax.l2r(linCenter - 1000); + range[1] = ax.l2r(linCenter + 1000); + break; + } + } else { + if(!isNumeric(range[i])) { + if(isNumeric(range[1 - i])) { + range[i] = range[1 - i] * (i ? 10 : 0.1); + } else { + ax[rangeAttr] = dflt; + break; + } + } + + if(range[i] < -FP_SAFE) range[i] = -FP_SAFE; + else if(range[i] > FP_SAFE) range[i] = FP_SAFE; + + if(range[0] === range[1]) { + // somewhat arbitrary: split by 1 or 1ppm, whichever is bigger + var inc = Math.max(1, Math.abs(range[0] * 1e-6)); + range[0] -= inc; + range[1] += inc; + } + } + } + }; + + // set scaling to pixels + ax.setScale = function(usePrivateRange) { + var gs = fullLayout._size; + + // make sure we have a domain (pull it in from the axis + // this one is overlaying if necessary) + if(ax.overlaying) { + var ax2 = axisIds.getFromId({ _fullLayout: fullLayout }, ax.overlaying); + ax.domain = ax2.domain; + } + + // While transitions are 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":16,"fast-isnumeric":18}],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 Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); +var Axes = _dereq_('./axes'); + +/** + * transitionAxes + * + * transition axes from one set of ranges to another, using a svg + * transformations, similar to during panning. + * + * @param {DOM element | object} gd + * @param {array} edits : array of 'edits', each item with + * - plotinfo {object} subplot object + * - xr0 {array} initial x-range + * - xr1 {array} end x-range + * - yr0 {array} initial y-range + * - yr1 {array} end y-range + * @param {object} transitionOpts + * @param {function} makeOnCompleteCallback + */ +module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnCompleteCallback) { + var fullLayout = gd._fullLayout; + + // special case for redraw:false Plotly.animate that relies on this + // to update axis-referenced layout components + if(edits.length === 0) { + Axes.redrawComponents(gd); + return; + } + + function unsetSubplotTransform(subplot) { + var xa = subplot.xaxis; + var ya = subplot.yaxis; + + fullLayout._defs.select('#' + subplot.clipId + '> rect') + .call(Drawing.setTranslate, 0, 0) + .call(Drawing.setScale, 1, 1); + + subplot.plot + .call(Drawing.setTranslate, xa._offset, ya._offset) + .call(Drawing.setScale, 1, 1); + + var traceGroups = subplot.plot.selectAll('.scatterlayer .trace'); + + // This is specifically directed at scatter traces, applying an inverse + // scale to individual points to counteract the scale of the trace + // as a whole: + traceGroups.selectAll('.point') + .call(Drawing.setPointGroupScale, 1, 1); + traceGroups.selectAll('.textpoint') + .call(Drawing.setTextPointsScale, 1, 1); + traceGroups + .call(Drawing.hideOutsideRangePoints, subplot); + } + + function updateSubplot(edit, progress) { + var plotinfo = edit.plotinfo; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var xlen = xa._length; + var ylen = ya._length; + var editX = !!edit.xr1; + var editY = !!edit.yr1; + var viewBox = []; + + if(editX) { + var xr0 = Lib.simpleMap(edit.xr0, xa.r2l); + var xr1 = Lib.simpleMap(edit.xr1, xa.r2l); + var dx0 = xr0[1] - xr0[0]; + var dx1 = xr1[1] - xr1[0]; + viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen; + viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0); + xa.range[0] = xa.l2r(xr0[0] * (1 - progress) + progress * xr1[0]); + xa.range[1] = xa.l2r(xr0[1] * (1 - progress) + progress * xr1[1]); + } else { + viewBox[0] = 0; + viewBox[2] = xlen; + } + + if(editY) { + var yr0 = Lib.simpleMap(edit.yr0, ya.r2l); + var yr1 = Lib.simpleMap(edit.yr1, ya.r2l); + var dy0 = yr0[1] - yr0[0]; + var dy1 = yr1[1] - yr1[0]; + viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen; + viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0); + ya.range[0] = xa.l2r(yr0[0] * (1 - progress) + progress * yr1[0]); + ya.range[1] = ya.l2r(yr0[1] * (1 - progress) + progress * yr1[1]); + } else { + viewBox[1] = 0; + viewBox[3] = ylen; + } + + Axes.drawOne(gd, xa, {skipTitle: true}); + Axes.drawOne(gd, ya, {skipTitle: true}); + Axes.redrawComponents(gd, [xa._id, ya._id]); + + var xScaleFactor = editX ? xlen / viewBox[2] : 1; + var yScaleFactor = editY ? ylen / viewBox[3] : 1; + var clipDx = editX ? viewBox[0] : 0; + var clipDy = editY ? viewBox[1] : 0; + var fracDx = editX ? (viewBox[0] / viewBox[2] * xlen) : 0; + var fracDy = editY ? (viewBox[1] / viewBox[3] * ylen) : 0; + var plotDx = xa._offset - fracDx; + var plotDy = ya._offset - fracDy; + + plotinfo.clipRect + .call(Drawing.setTranslate, clipDx, clipDy) + .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor); + + plotinfo.plot + .call(Drawing.setTranslate, plotDx, plotDy) + .call(Drawing.setScale, xScaleFactor, yScaleFactor); + + // apply an inverse scale to individual points to counteract + // the scale of the trace group. + Drawing.setPointGroupScale(plotinfo.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor); + Drawing.setTextPointsScale(plotinfo.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor); + } + + var onComplete; + if(makeOnCompleteCallback) { + // This module makes the choice whether or not it notifies Plotly.transition + // about completion: + onComplete = makeOnCompleteCallback(); + } + + function transitionComplete() { + var aobj = {}; + + for(var i = 0; i < edits.length; i++) { + var edit = edits[i]; + var xa = edit.plotinfo.xaxis; + var ya = edit.plotinfo.yaxis; + if(edit.xr1) aobj[xa._name + '.range'] = edit.xr1.slice(); + if(edit.yr1) aobj[ya._name + '.range'] = edit.yr1.slice(); + } + + // Signal that this transition has completed: + onComplete && onComplete(); + + return Registry.call('relayout', gd, aobj).then(function() { + for(var i = 0; i < edits.length; i++) { + unsetSubplotTransform(edits[i].plotinfo); + } + }); + } + + function transitionInterrupt() { + var aobj = {}; + + for(var i = 0; i < edits.length; i++) { + var edit = edits[i]; + var xa = edit.plotinfo.xaxis; + var ya = edit.plotinfo.yaxis; + if(edit.xr0) aobj[xa._name + '.range'] = edit.xr0.slice(); + if(edit.yr0) aobj[ya._name + '.range'] = edit.yr0.slice(); + } + + return Registry.call('relayout', gd, aobj).then(function() { + for(var i = 0; i < edits.length; i++) { + unsetSubplotTransform(edits[i].plotinfo); + } + }); + } + + var t1, t2, raf; + var easeFn = d3.ease(transitionOpts.easing); + + gd._transitionData._interruptCallbacks.push(function() { + window.cancelAnimationFrame(raf); + raf = null; + return transitionInterrupt(); + }); + + function doFrame() { + t2 = Date.now(); + + var tInterp = Math.min(1, (t2 - t1) / transitionOpts.duration); + var progress = easeFn(tInterp); + + for(var i = 0; i < edits.length; i++) { + updateSubplot(edits[i], progress); + } + + if(t2 - t1 > transitionOpts.duration) { + transitionComplete(); + raf = window.cancelAnimationFrame(doFrame); + } else { + raf = window.requestAnimationFrame(doFrame); + } + } + + t1 = Date.now(); + raf = window.requestAnimationFrame(doFrame); + + return Promise.resolve(); +}; + +},{"../../components/drawing":72,"../../lib":169,"../../registry":258,"./axes":213,"d3":16}],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":258,"./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":258}],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; + } + } + + 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":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":258,"./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":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. +* +* 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 getModuleCalcData = _dereq_('../plots/get_data').getModuleCalcData; + +var relinkPrivateKeys = Lib.relinkPrivateKeys; +var _ = Lib._; + +var plots = module.exports = {}; + +// Expose registry methods on Plots for backward-compatibility +Lib.extendFlat(plots, Registry); + +plots.attributes = _dereq_('./attributes'); +plots.attributes.type.values = plots.allTypes; +plots.fontAttrs = _dereq_('./font_attributes'); +plots.layoutAttributes = _dereq_('./layout_attributes'); + +// TODO make this a plot attribute? +plots.fontWeight = 'normal'; + +var transformsRegistry = plots.transformsRegistry; + +var commandModule = _dereq_('./command'); +plots.executeAPICommand = commandModule.executeAPICommand; +plots.computeAPICommandBindings = commandModule.computeAPICommandBindings; +plots.manageCommandObserver = commandModule.manageCommandObserver; +plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings; + +// in some cases the browser doesn't seem to know how big +// the text is at first, so it needs to draw it, +// then wait a little, then draw it again +plots.redrawText = function(gd) { + gd = Lib.getGraphDiv(gd); + + 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); + + // find "full" domain span of counter axes, + // this loop can be costly, so only compute it when required + if(ax._counterAxes.length && ( + (ax.spikemode && ax.spikemode.indexOf('across') !== -1) || + (ax.automargin && ax.mirror && ax.anchor !== 'free') || + Registry.getComponentMethod('rangeslider', 'isVisible')(ax) + )) { + var min = 1; + var max = 0; + for(j = 0; j < ax._counterAxes.length; j++) { + var ax2 = axisIDs.getFromId(mockGd, ax._counterAxes[j]); + min = Math.min(min, ax2.domain[0]); + max = Math.max(max, ax2.domain[1]); + } + if(min < max) { + ax._counterDomainMin = min; + ax._counterDomainMax = max; + } + } + } +}; + +function findMainSubplot(ax, fullLayout) { + var mockGd = {_fullLayout: fullLayout}; + + var isX = ax._id.charAt(0) === 'x'; + var anchorAx = ax._mainAxis._anchorAxis; + var mainSubplotID = ''; + var nextBestMainSubplotID = ''; + var anchorID = ''; + + // First try the main ID with the anchor + if(anchorAx) { + anchorID = anchorAx._mainAxis._id; + mainSubplotID = isX ? (ax._id + anchorID) : (anchorID + ax._id); + } + + // Then look for a subplot with the counteraxis overlaying the anchor + // If that fails just use the first subplot including this axis + if(!mainSubplotID || !fullLayout._plots[mainSubplotID]) { + mainSubplotID = ''; + + var counterIDs = ax._counterAxes; + for(var j = 0; j < counterIDs.length; j++) { + var counterPart = counterIDs[j]; + var id = isX ? (ax._id + counterPart) : (counterPart + ax._id); + if(!nextBestMainSubplotID) nextBestMainSubplotID = id; + var counterAx = axisIDs.getFromId(mockGd, counterPart); + if(anchorID && counterAx.overlaying === anchorID) { + mainSubplotID = id; + break; + } + } + } + + return mainSubplotID || nextBestMainSubplotID; +} + +// This function clears any trace attributes with valType: color and +// no set dflt filed in the plot schema. This is needed because groupby (which +// is the only transform for which this currently applies) supplies parent +// trace defaults, then expanded trace defaults. The result is that `null` +// colors are default-supplied and inherited as a color instead of a null. +// The result is that expanded trace default colors have no effect, with +// the final result that groups are indistinguishable. This function clears +// those colors so that individual groupby groups get unique colors. +plots.clearExpandedTraceDefaultColors = function(trace) { + var colorAttrs, path, i; + + // This uses weird closure state in order to satisfy the linter rule + // that we can't create functions in a loop. + function locateColorAttrs(attr, attrName, attrs, level) { + path[level] = attrName; + path.length = level + 1; + if(attr.valType === 'color' && attr.dflt === undefined) { + colorAttrs.push(path.join('.')); + } + } + + path = []; + + // Get the cached colorAttrs: + colorAttrs = trace._module._colorAttrs; + + // Or else compute and cache the colorAttrs on the module: + if(!colorAttrs) { + trace._module._colorAttrs = colorAttrs = []; + PlotSchema.crawl( + trace._module.attributes, + locateColorAttrs + ); + } + + for(i = 0; i < colorAttrs.length; i++) { + var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]); + + if(!origprop.get()) { + Lib.nestedProperty(trace, colorAttrs[i]).set(null); + } + } +}; + + +plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) { + var modules = fullLayout._modules; + var visibleModules = fullLayout._visibleModules; + var basePlotModules = fullLayout._basePlotModules; + var cnt = 0; + var colorCnt = 0; + + var i, fullTrace, trace; + + fullLayout._transformModules = []; + + function pushModule(fullTrace) { + dataOut.push(fullTrace); + + var _module = fullTrace._module; + if(!_module) return; + + Lib.pushUnique(modules, _module); + if(fullTrace.visible === true) Lib.pushUnique(visibleModules, _module); + Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule); + cnt++; + + // TODO: do we really want color not to increment for explicitly invisible traces? + // This logic is weird, but matches previous behavior: traces that you explicitly + // set to visible:false do not increment the color, but traces WE determine to be + // empty or invalid (and thus set to visible:false) DO increment color. + // I kind of think we should just let all traces increment color, visible or not. + // see mock: axes-autotype-empty vs. a test of restyling visible: false that + // I can't find right now... + if(fullTrace._input.visible !== false) colorCnt++; + } + + var carpetIndex = {}; + var carpetDependents = []; + var dataTemplate = (layout.template || {}).data || {}; + var templater = Template.traceTemplater(dataTemplate); + + for(i = 0; i < dataIn.length; i++) { + trace = dataIn[i]; + + // reuse uid we may have pulled out of oldFullData + // Note: templater supplies trace type + fullTrace = templater.newTrace(trace); + fullTrace.uid = fullLayout._traceUids[i]; + plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i); + + fullTrace.index = i; + fullTrace._input = trace; + fullTrace._expandedIndex = cnt; + + if(fullTrace.transforms && fullTrace.transforms.length) { + var sdInvisible = trace.visible !== false && fullTrace.visible === false; + + var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout); + + for(var j = 0; j < expandedTraces.length; j++) { + var expandedTrace = expandedTraces[j]; + + // No further templating during transforms. + var fullExpandedTrace = { + _template: fullTrace._template, + type: fullTrace.type, + // set uid using parent uid and expanded index + // to promote consistency between update calls + uid: fullTrace.uid + j + }; + + // If the first supplyDefaults created `visible: false`, + // clear it before running supplyDefaults a second time, + // because sometimes there are items we still want to coerce + // inside trace modules before determining that the trace is + // again `visible: false`, for example partial visibilities + // in `splom` traces. + if(sdInvisible && expandedTrace.visible === false) { + delete expandedTrace.visible; + } + + plots.supplyTraceDefaults(expandedTrace, fullExpandedTrace, cnt, fullLayout, i); + + // relink private (i.e. underscore) keys expanded trace to full expanded trace so + // that transform supply-default methods can set _ keys for future use. + relinkPrivateKeys(fullExpandedTrace, expandedTrace); + + // add info about parent data trace + fullExpandedTrace.index = i; + fullExpandedTrace._input = trace; + fullExpandedTrace._fullInput = fullTrace; + + // add info about the expanded data + fullExpandedTrace._expandedIndex = cnt; + fullExpandedTrace._expandedInput = expandedTrace; + + pushModule(fullExpandedTrace); + } + } else { + // add identify refs for consistency with transformed traces + fullTrace._fullInput = fullTrace; + fullTrace._expandedInput = fullTrace; + + pushModule(fullTrace); + } + + if(Registry.traceIs(fullTrace, 'carpetAxis')) { + carpetIndex[fullTrace.carpet] = fullTrace; + } + + if(Registry.traceIs(fullTrace, 'carpetDependent')) { + carpetDependents.push(i); + } + } + + for(i = 0; i < carpetDependents.length; i++) { + fullTrace = dataOut[carpetDependents[i]]; + + if(!fullTrace.visible) continue; + + var carpetAxis = carpetIndex[fullTrace.carpet]; + fullTrace._carpet = carpetAxis; + + if(!carpetAxis || !carpetAxis.visible) { + fullTrace.visible = false; + continue; + } + + fullTrace.xaxis = carpetAxis.xaxis; + fullTrace.yaxis = carpetAxis.yaxis; + } +}; + +plots.supplyAnimationDefaults = function(opts) { + opts = opts || {}; + var i; + var optsOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt); + } + + coerce('mode'); + coerce('direction'); + coerce('fromcurrent'); + + if(Array.isArray(opts.frame)) { + optsOut.frame = []; + for(i = 0; i < opts.frame.length; i++) { + optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {}); + } + } else { + optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {}); + } + + if(Array.isArray(opts.transition)) { + optsOut.transition = []; + for(i = 0; i < opts.transition.length; i++) { + optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {}); + } + } else { + optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {}); + } + + return optsOut; +}; + +plots.supplyAnimationFrameDefaults = function(opts) { + var optsOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt); + } + + coerce('duration'); + coerce('redraw'); + + return optsOut; +}; + +plots.supplyAnimationTransitionDefaults = function(opts) { + var optsOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt); + } + + coerce('duration'); + coerce('easing'); + + return optsOut; +}; + +plots.supplyFrameDefaults = function(frameIn) { + var frameOut = {}; + + function coerce(attr, dflt) { + return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt); + } + + coerce('group'); + coerce('name'); + coerce('traces'); + coerce('baseframe'); + coerce('data'); + coerce('layout'); + + return frameOut; +}; + +plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, traceInIndex) { + var colorway = layout.colorway || Color.defaults; + var defaultColor = colorway[colorIndex % colorway.length]; + + var i; + + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt); + } + + var visible = coerce('visible'); + + coerce('type'); + coerce('name', layout._traceWord + ' ' + traceInIndex); + + coerce('uirevision', layout.uirevision); + + // we want even invisible traces to make their would-be subplots visible + // so coerce the subplot id(s) now no matter what + var _module = plots.getModule(traceOut); + + traceOut._module = _module; + if(_module) { + var basePlotModule = _module.basePlotModule; + var subplotAttr = basePlotModule.attr; + var subplotAttrs = basePlotModule.attributes; + if(subplotAttr && subplotAttrs) { + var subplots = layout._subplots; + var subplotId = ''; + + // 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; + } + + // 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) { + 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; + var yt = o.yt !== undefined ? o.yt : o.y; + var yb = o.yb !== undefined ? o.yb : o.y; + + pushMargin[id] = { + l: {val: xl, size: o.l + pad}, + r: {val: xr, size: o.r + pad}, + b: {val: yb, size: o.b + pad}, + t: {val: yt, size: o.t + pad} + }; + pushMarginIds[id] = 1; + } + + if(!fullLayout._replotting) { + return plots.doAutoMargin(gd); + } + } +}; + +plots.doAutoMargin = function(gd) { + var fullLayout = gd._fullLayout; + 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; + } + + // 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.'); + } + } +}; + +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 = null; + var yr1 = null; + var editX = null; + var editY = null; + + if(Array.isArray(newLayout[xa._name + '.range'])) { + xr1 = newLayout[xa._name + '.range'].slice(); + } else if(Array.isArray((newLayout[xa._name] || {}).range)) { + xr1 = newLayout[xa._name].range.slice(); + } + if(Array.isArray(newLayout[ya._name + '.range'])) { + yr1 = newLayout[ya._name + '.range'].slice(); + } else if(Array.isArray((newLayout[ya._name] || {}).range)) { + yr1 = newLayout[ya._name].range.slice(); + } + + if(xr0 && xr1 && + (xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) + ) { + editX = {xr0: xr0, xr1: xr1}; + } + if(yr0 && yr1 && + (ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) + ) { + editY = {yr0: yr0, yr1: yr1}; + } + + if(editX || editY) { + axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY)); + } + } + } + + return Promise.resolve(); + }; + + opts.runFn = function(makeCallback) { + var traceTransitionOpts; + var basePlotModules = gd._fullLayout._basePlotModules; + var hasAxisTransition = axEdits.length; + var i; + + if(layout) { + for(i = 0; i < basePlotModules.length; i++) { + if(basePlotModules[i].transitionAxes) { + basePlotModules[i].transitionAxes(gd, axEdits, transitionOpts, makeCallback); + } + } + } + + // Here handle the exception that we refuse to animate scales and axes at the same + // time. In other words, if there's an axis transition, then set the data transition + // to instantaneous. + if(hasAxisTransition) { + traceTransitionOpts = Lib.extendFlat({}, transitionOpts); + traceTransitionOpts.duration = 0; + // This means do not transition cartesian traces, + // this happens on layout-only (e.g. axis range) animations + delete transitionedTraces.cartesian; + } else { + traceTransitionOpts = transitionOpts; + } + + // Note that we pass a callback to *create* the callback that must be invoked on completion. + // This is since not all traces know about transitions, so it greatly simplifies matters if + // the trace is responsible for creating a callback, if needed, and then executing it when + // the time is right. + for(var n in transitionedTraces) { + var traceIndices = transitionedTraces[n]; + var _module = gd._fullData[traceIndices[0]]._module; + _module.basePlotModule.plot(gd, traceIndices, traceTransitionOpts, makeCallback); + } + }; + + return _transition(gd, transitionOpts, opts); +}; + +/** + * Transition to a set of new data and layout properties from Plotly.react + * + * @param {DOM element} gd + * @param {object} restyleFlags + * - anim {'all'|'some'} + * @param {object} relayoutFlags + * - anim {'all'|'some'} + * @param {object} oldFullLayout : old (pre Plotly.react) fullLayout + */ +plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLayout) { + var fullLayout = gd._fullLayout; + var transitionOpts = fullLayout.transition; + var opts = {}; + var axEdits = []; + + opts.prepareFn = function() { + var subplots = fullLayout._plots; + + // no need to redraw at end of transition, + // if all changes are animatable + opts.redraw = false; + if(restyleFlags.anim === 'some') opts.redraw = true; + if(relayoutFlags.anim === 'some') opts.redraw = true; + + for(var k in subplots) { + var plotinfo = subplots[k]; + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + var xr0 = oldFullLayout[xa._name].range.slice(); + var yr0 = oldFullLayout[ya._name].range.slice(); + var xr1 = xa.range.slice(); + var yr1 = ya.range.slice(); + + xa.setScale(); + ya.setScale(); + + var editX = null; + var editY = null; + + if(xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) { + editX = {xr0: xr0, xr1: xr1}; + } + if(ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) { + editY = {yr0: yr0, yr1: yr1}; + } + + if(editX || editY) { + axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY)); + } + } + + return Promise.resolve(); + }; + + opts.runFn = function(makeCallback) { + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + var basePlotModules = fullLayout._basePlotModules; + + var axisTransitionOpts; + var traceTransitionOpts; + var transitionedTraces; + + var allTraceIndices = []; + for(var i = 0; i < fullData.length; i++) { + allTraceIndices.push(i); + } + + function transitionAxes() { + 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; + setTimeout(transitionAxes, transitionOpts.duration); + transitionTraces(); + } else { + axisTransitionOpts = transitionOpts; + transitionedTraces = null; + traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0}); + setTimeout(transitionTraces, axisTransitionOpts.duration); + transitionAxes(); + } + } else if(axEdits.length) { + axisTransitionOpts = transitionOpts; + transitionAxes(); + } else if(restyleFlags.anim) { + transitionedTraces = allTraceIndices; + traceTransitionOpts = transitionOpts; + transitionTraces(); + } + }; + + return _transition(gd, transitionOpts, opts); +}; + +/** + * trace/layout transition wrapper that works + * for transitions initiated by Plotly.animate and Plotly.react. + * + * @param {DOM element} gd + * @param {object} transitionOpts + * @param {object} opts + * - redraw {boolean} + * - prepareFn {function} *should return a Promise* + * - runFn {function} ran inside executeTransitions + */ +function _transition(gd, transitionOpts, opts) { + var aborted = false; + + function executeCallbacks(list) { + var p = Promise.resolve(); + if(!list) return p; + while(list.length) { + p = p.then((list.shift())); + } + return p; + } + + function flushCallbacks(list) { + if(!list) return; + while(list.length) { + list.shift(); + } + } + + function executeTransitions() { + gd.emit('plotly_transitioning', []); + + return new Promise(function(resolve) { + // This flag is used to disabled things like autorange: + gd._transitioning = true; + + // When instantaneous updates are coming through quickly, it's too much to simply disable + // all interaction, so store this flag so we can disambiguate whether mouse interactions + // should be fully disabled or not: + if(transitionOpts.duration > 0) { + gd._transitioningWithDuration = true; + } + + // If another transition is triggered, this callback will be executed simply because it's + // in the interruptCallbacks queue. If this transition completes, it will instead flush + // that queue and forget about this callback. + gd._transitionData._interruptCallbacks.push(function() { + aborted = true; + }); + + if(opts.redraw) { + gd._transitionData._interruptCallbacks.push(function() { + return Registry.call('redraw', gd); + }); + } + + // Emit this and make sure it happens last: + gd._transitionData._interruptCallbacks.push(function() { + gd.emit('plotly_transitioninterrupted', []); + }); + + // Construct callbacks that are executed on transition end. This ensures the d3 transitions + // are *complete* before anything else is done. + var numCallbacks = 0; + var numCompleted = 0; + function makeCallback() { + numCallbacks++; + return function() { + numCompleted++; + // When all are complete, perform a redraw: + if(!aborted && numCompleted === numCallbacks) { + completeTransition(resolve); + } + }; + } + + opts.runFn(makeCallback); + + // If nothing else creates a callback, then this will trigger the completion in the next tick: + setTimeout(makeCallback()); + }); + } + + function completeTransition(callback) { + // This a simple workaround for tests which purge the graph before animations + // have completed. That's not a very common case, so this is the simplest + // fix. + if(!gd._transitionData) return; + + flushCallbacks(gd._transitionData._interruptCallbacks); + + return Promise.resolve().then(function() { + if(opts.redraw) { + return Registry.call('redraw', gd); + } + }).then(function() { + // Set transitioning false again once the redraw has occurred. This is used, for example, + // to prevent the trailing redraw from autoranging: + gd._transitioning = false; + gd._transitioningWithDuration = false; + + gd.emit('plotly_transitioned', []); + }).then(callback); + } + + function interruptPreviousTransitions() { + // Fail-safe against purged plot: + if(!gd._transitionData) return; + + // If a transition is interrupted, set this to false. At the moment, the only thing that would + // interrupt a transition is another transition, so that it will momentarily be set to true + // again, but this determines whether autorange or dragbox work, so it's for the sake of + // cleanliness: + gd._transitioning = false; + + return executeCallbacks(gd._transitionData._interruptCallbacks); + } + + var seq = [ + plots.previousPromises, + interruptPreviousTransitions, + opts.prepareFn, + plots.rehover, + executeTransitions + ]; + + var transitionStarting = Lib.syncOrAsync(seq, gd); + + if(!transitionStarting || !transitionStarting.then) { + transitionStarting = Promise.resolve(); + } + + return transitionStarting.then(function() { return gd; }); +} + +plots.doCalcdata = function(gd, traces) { + var axList = axisIDs.list(gd); + var fullData = gd._fullData; + var fullLayout = gd._fullLayout; + + var trace, _module, i, j; + + // XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without + // *all* needing doCalcdata: + var calcdata = new Array(fullData.length); + var oldCalcdata = (gd.calcdata || []).slice(); + gd.calcdata = calcdata; + + // extra helper variables + + // how many box/violins plots do we have (in case they're grouped) + fullLayout._numBoxes = 0; + fullLayout._numViolins = 0; + + // initialize violin per-scale-group stats container + fullLayout._violinScaleGroupStats = {}; + + // for calculating avg luminosity of heatmaps + gd._hmpixcount = 0; + gd._hmlumcount = 0; + + // for sharing colors across pies / sunbursts / treemap / funnelarea (and for legend) + fullLayout._piecolormap = {}; + fullLayout._sunburstcolormap = {}; + fullLayout._treemapcolormap = {}; + 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; +}; + +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. +* +* This 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":376}],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":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. +*/ + +/* 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":169,"./micropolar":249,"./undo_manager":251,"d3":16}],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 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. +* +* This 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":255,"./layout_defaults":256,"./ternary":257}],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 colorAttrs = _dereq_('../../components/color/attributes'); +var domainAttrs = _dereq_('../domain').attributes; +var axesAttrs = _dereq_('../cartesian/layout_attributes'); + +var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll; +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var ternaryAxesAttrs = { + title: { + text: axesAttrs.title.text, + font: axesAttrs.title.font + // TODO does standoff here make sense? + }, + color: axesAttrs.color, + // ticks + tickmode: axesAttrs.tickmode, + nticks: extendFlat({}, axesAttrs.nticks, {dflt: 6, min: 1}), + tick0: axesAttrs.tick0, + dtick: axesAttrs.dtick, + tickvals: axesAttrs.tickvals, + ticktext: axesAttrs.ticktext, + ticks: axesAttrs.ticks, + ticklen: axesAttrs.ticklen, + tickwidth: axesAttrs.tickwidth, + tickcolor: axesAttrs.tickcolor, + showticklabels: axesAttrs.showticklabels, + showtickprefix: axesAttrs.showtickprefix, + tickprefix: axesAttrs.tickprefix, + showticksuffix: axesAttrs.showticksuffix, + ticksuffix: axesAttrs.ticksuffix, + showexponent: axesAttrs.showexponent, + exponentformat: axesAttrs.exponentformat, + 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":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. +* +* This 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":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. +* +* This 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":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. +* +* This 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}],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 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":258}],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 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":261,"./helpers":262}],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 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":262}],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 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":258}],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 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":259,"./download":260,"./helpers":262,"./svgtoimg":264,"./toimage":265,"./tosvg":266}],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 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":262,"events":15}],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 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":258,"./cloneplot":259,"./helpers":262,"./svgtoimg":264,"./tosvg":266,"events":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 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":169,"d3":16}],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 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}],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 scatterAttrs = _dereq_('../scatter/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'); + +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, + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: constants.eventDataKeys + }), + 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,"../../lib/extend":164,"../../plots/font_attributes":239,"../../plots/template_attributes":253,"../scatter/attributes":376,"./constants":270}],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 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":213,"../scatter/calc_selection":378,"./arrays_to_calcdata":267}],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'; + +module.exports = { + TEXTPAD: 3, // padding in pixels around text + eventDataKeys: [] +}; + +},{}],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 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":258,"./sieve.js":280,"fast-isnumeric":18}],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 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'); + + coerce('texttemplate'); + } + + if(hasInside) { + if(moduleHasInsideanchor) coerce('insidetextanchor'); + } +} + +module.exports = { + supplyDefaults: supplyDefaults, + crossTraceDefaults: crossTraceDefaults, + handleGroupingDefaults: handleGroupingDefaults, + 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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":18,"tinycolor2":34}],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'; + +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":51,"../../components/fx":89,"../../lib":169,"../../registry":258,"./helpers":273}],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 = { + 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'], + animatable: true, + meta: { + + } +}; + +},{"../../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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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', + + } +}; + +},{}],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 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":258,"./layout_attributes":276}],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'; + +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 constants = _dereq_('./constants'); +var attributes = _dereq_('./attributes'); + +var attributeText = attributes.text; +var attributeTextPosition = attributes.textposition; + +var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue; + +var TEXTPAD = constants.TEXTPAD; + +function keyFunc(d) {return d.id;} +function getKeyFunc(trace) { + if(trace.ids) { + return keyFunc; + } +} + +function dirSign(a, b) { + return (a < b) ? 1 : -1; +} + +function getXY(di, xa, ya, isHorizontal) { + var s = []; + var p = []; + + var sAxis = isHorizontal ? xa : ya; + var pAxis = isHorizontal ? ya : xa; + + s[0] = sAxis.c2p(di.s0, true); + p[0] = pAxis.c2p(di.p0, true); + + s[1] = sAxis.c2p(di.s1, true); + p[1] = pAxis.c2p(di.p1, true); + + return isHorizontal ? [s, p] : [p, s]; +} + +function transition(selection, 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; + + 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 keyFunc = getKeyFunc(trace); + var bars = pointGroup.selectAll('g.point').data(Lib.identity, keyFunc); + + bars.enter().append('g') + .classed('point', true); + + bars.exit().remove(); + + bars.each(function(di, i) { + var bar = d3.select(this); + + // now display the bar + // clipped xf/yf (2nd arg true): non-positive + // log values go off-screen by plotwidth + // so you see them continue if you drag the plot + var xy = getXY(di, xa, ya, isHorizontal); + + var x0 = xy[0][0]; + var x1 = xy[0][1]; + var y0 = xy[1][0]; + var y1 = xy[1][1]; + + 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 && !isBlank) { + if(isHorizontal) { + x0 -= dirSign(x0, x1) * adjustPixel; + x1 += dirSign(x0, x1) * adjustPixel; + } else { + y0 -= dirSign(y0, y1) * adjustPixel; + y1 += dirSign(y0, y1) * adjustPixel; + } + } + + var lw; + var mc; + + if(trace.type === 'waterfall') { + if(!isBlank) { + var cont = trace[di.dir].marker; + lw = cont.line.width; + mc = cont.color; + } + } else { + lw = helpers.getLineWidth(trace, di); + mc = di.mc || trace.marker.color; + } + + 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); + } + + var sel = transition(Lib.ensureSingle(bar, 'path'), opts, makeOnCompleteCallback); + sel + .style('vector-effect', 'non-scaling-stroke') + .attr('d', 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z') + .call(Drawing.setClipUrl, plotinfo.layerClipId, gd); + + 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); + } + }); + + // lastly, clip points groups of `cliponaxis !== false` traces + // on `plotinfo._hasClipOnAxisFalse === true` subplots + var hasClipOnAxisFalse = trace.cliponaxis === false; + Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId, gd); + }); + + // error bars are on the top + Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo, opts); +} + +function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + var fullLayout = gd._fullLayout; + var textPosition; + + function appendTextNode(bar, text, textFont) { + var textSelection = Lib.ensureSingle(bar, 'text') + .text(text) + .attr({ + 'class': 'bartext bartext-' + textPosition, + 'text-anchor': 'middle', + // prohibit tex interpretation until we can handle + // tex and regular text together + 'data-notex': 1 + }) + .call(Drawing.font, textFont) + .call(svgTextUtils.convertToTspans, gd); + + return textSelection; + } + + // get trace attributes + var trace = calcTrace[0].trace; + var isHorizontal = (trace.orientation === 'h'); + + var text = getText(fullLayout, 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); + + var currentTransform = textSelection.attr('transform'); + textSelection.attr('transform', ''); + textBB = Drawing.bBox(textSelection.node()), + textWidth = textBB.width, + textHeight = textBB.height; + textSelection.attr('transform', currentTransform); + + if(textWidth <= 0 || textHeight <= 0) { + textSelection.remove(); + return; + } + } + + // compute text transform + var transform, constrained; + if(textPosition === 'outside') { + constrained = + trace.constraintext === 'both' || + trace.constraintext === 'outside'; + + 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 = Lib.getTextTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, { + isHorizontal: isHorizontal, + constrained: constrained, + angle: trace.textangle, + anchor: trace.insidetextanchor + })); + } + + transition(textSelection, opts, makeOnCompleteCallback).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 getText(fullLayout, calcTrace, index, xa, ya) { + var trace = calcTrace[0].trace; + var texttemplate = trace.texttemplate; + + var value; + 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); +} + +function getTextPosition(trace, index) { + var value = helpers.getValue(trace.textposition, 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 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 cdi = calcTrace[index]; + var obj = {}; + + obj.label = cdi.p; + obj.labelLabel = 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); + + 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'); + 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, + toMoveInsideBar: toMoveInsideBar, + 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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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]; + } + } +} + +},{}],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'; + +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}],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 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":51,"../../components/drawing":72,"../../lib":169,"../../registry":258,"./attributes":268,"./helpers":273,"d3":16}],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 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}],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 scatterAttrs = _dereq_('../scatter/attributes'); +var barAttrs = _dereq_('../bar/attributes'); +var colorAttrs = _dereq_('../../components/color/attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var scatterMarkerAttrs = scatterAttrs.marker; +var scatterMarkerLineAttrs = scatterMarkerAttrs.line; + +module.exports = { + y: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + + }, + x: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + + }, + x0: { + valType: 'any', + + editType: 'calc+clearAxisTypes', + + }, + y0: { + valType: 'any', + + editType: 'calc+clearAxisTypes', + + }, + 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,"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"../scatter/attributes":376}],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 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":18}],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 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(posAxis.c2l(calcTrace[j].pos, true)); + shownPts += (calcTrace[j].pts2 || []).length; + } + } + + if(!pointList.length) return; + + // box plots - update dPos based on multiple traces + var boxdv = Lib.distinctVals(pointList); + var dPos0 = boxdv.minDiff / 2; + + // check for forced minimum dtick + Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true); + + var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes'; + var numTotal = fullLayout[numKey]; + var group = fullLayout[traceType + 'mode'] === 'group' && numTotal > 1; + var groupFraction = 1 - fullLayout[traceType + 'gap']; + var groupGapFraction = 1 - fullLayout[traceType + 'groupgap']; + + for(i = 0; i < boxList.length; i++) { + calcTrace = calcdata[boxList[i]]; + + var trace = calcTrace[0].trace; + var t = calcTrace[0].t; + var width = trace.width; + var side = trace.side; + + // position coordinate delta + var dPos; + // box half width; + var bdPos; + // box center offset + var bPos; + // half-width within which to accept hover for this box/violin + // always split the distance to the closest box/violin + var wHover; + + if(width) { + dPos = bdPos = wHover = width / 2; + bPos = 0; + } else { + dPos = dPos0; + + if(group) { + var groupId = getAxisGroup(fullLayout, posAxis._id) + trace.orientation; + var alignmentGroups = fullLayout._alignmentOpts[groupId] || {}; + var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {}; + var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length; + var num = nOffsetGroups || numTotal; + var shift = nOffsetGroups ? trace._offsetIndex : t.num; + + bdPos = dPos * groupFraction * groupGapFraction / num; + bPos = 2 * dPos * (-0.5 + (shift + 0.5) / num) * groupFraction; + wHover = dPos * groupFraction / num; + } else { + bdPos = dPos * groupFraction * groupGapFraction; + bPos = 0; + wHover = dPos; + } + } + t.dPos = dPos; + t.bPos = bPos; + t.bdPos = bdPos; + t.wHover = wHover; + + // box/violin-only value-space push value + var pushplus; + var pushminus; + // edge of box/violin + var edge = bPos + bdPos; + var edgeplus; + var edgeminus; + // value-space padding + var vpadplus; + var vpadminus; + // pixel-space padding + var ppadplus; + var ppadminus; + // do we add 5% of both sides (more logic for points beyond box/violin below) + var padded = Boolean(width); + // does this trace show points? + var hasPts = (trace.boxpoints || trace.points) && (shownPts > 0); + + if(side === 'positive') { + pushplus = dPos * (width ? 1 : 0.5); + edgeplus = edge; + pushminus = edgeplus = bPos; + } else if(side === 'negative') { + pushplus = edgeplus = bPos; + pushminus = dPos * (width ? 1 : 0.5); + edgeminus = edge; + } else { + pushplus = pushminus = dPos; + edgeplus = edgeminus = edge; + } + + if(hasPts) { + var pointpos = trace.pointpos; + var jitter = trace.jitter; + var ms = trace.marker.size / 2; + + var pp = 0; + if((pointpos + jitter) >= 0) { + pp = edge * (pointpos + jitter); + if(pp > pushplus) { + // (++) beyond plus-value, use pp + padded = true; + ppadplus = ms; + vpadplus = pp; + } else if(pp > edgeplus) { + // (+), use push-value (it's bigger), but add px-pad + ppadplus = ms; + vpadplus = pushplus; + } + } + if(pp <= pushplus) { + // (->) fallback to push value + vpadplus = pushplus; + } + + var pm = 0; + if((pointpos - jitter) <= 0) { + pm = -edge * (pointpos - jitter); + if(pm > pushminus) { + // (--) beyond plus-value, use pp + padded = true; + ppadminus = ms; + vpadminus = pm; + } else if(pm > edgeminus) { + // (-), use push-value (it's bigger), but add px-pad + ppadminus = ms; + vpadminus = pushminus; + } + } + if(pm <= pushminus) { + // (<-) fallback to push value + vpadminus = pushminus; + } + } else { + vpadplus = pushplus; + vpadminus = pushminus; + } + + var pos = new Array(calcTrace.length); + for(j = 0; j < calcTrace.length; j++) { + pos[j] = calcTrace[j].pos; + } + + trace._extremes[axId] = Axes.findExtremes(posAxis, pos, { + padded: padded, + vpadminus: vpadminus, + vpadplus: vpadplus, + vpadLinearized: true, + // N.B. SVG px-space positive/negative + ppadminus: {x: ppadminus, y: ppadplus}[axLetter], + ppadplus: {x: ppadplus, y: ppadminus}[axLetter], + }); + } +} + +module.exports = { + crossTraceCalc: crossTraceCalc, + setPositionOffset: setPositionOffset +}; + +},{"../../lib":169,"../../plots/cartesian/axes":213,"../../plots/cartesian/axis_ids":216}],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 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":169,"../../registry":258,"../bar/defaults":272,"./attributes":283}],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 = 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; +}; + +},{}],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'; + +var Axes = _dereq_('../../plots/cartesian/axes'); +var Lib = _dereq_('../../lib'); +var Fx = _dereq_('../../components/fx'); +var Color = _dereq_('../../components/color'); +var fillText = Lib.fillText; + +function hoverPoints(pointData, xval, yval, hovermode) { + var cd = pointData.cd; + var trace = cd[0].trace; + var hoveron = trace.hoveron; + var closeBoxData = []; + var closePtData; + + if(hoveron.indexOf('boxes') !== -1) { + closeBoxData = closeBoxData.concat(hoverOnBoxes(pointData, xval, yval, hovermode)); + } + + if(hoveron.indexOf('points') !== -1) { + closePtData = hoverOnPoints(pointData, xval, yval); + } + + // If there's a point in range and hoveron has points, show the best single point only. + // If hoveron has boxes and there's no point in range (or hoveron doesn't have points), show the box stats. + if(hovermode === 'closest') { + if(closePtData) return [closePtData]; + return closeBoxData; + } + + // Otherwise in compare mode, allow a point AND the box stats to be labeled + // If there are multiple boxes in range (ie boxmode = 'overlay') we'll see stats for all of them. + if(closePtData) { + closeBoxData.push(closePtData); + return closeBoxData; + } + return closeBoxData; +} + +function hoverOnBoxes(pointData, xval, yval, hovermode) { + var cd = pointData.cd; + var xa = pointData.xa; + var ya = pointData.ya; + var trace = cd[0].trace; + var t = cd[0].t; + var isViolin = trace.type === 'violin'; + var closeBoxData = []; + + var pLetter, vLetter, pAxis, vAxis, vVal, pVal, dx, dy, dPos, + hoverPseudoDistance, spikePseudoDistance; + + var boxDelta = t.bdPos; + var boxDeltaPos, boxDeltaNeg; + var posAcceptance = t.wHover; + var shiftPos = function(di) { return pAxis.c2l(di.pos) + t.bPos - pAxis.c2l(pVal); }; + + if(isViolin && trace.side !== 'both') { + if(trace.side === 'positive') { + dPos = function(di) { + var pos = shiftPos(di); + return Fx.inbox(pos, pos + posAcceptance, hoverPseudoDistance); + }; + boxDeltaPos = boxDelta; + boxDeltaNeg = 0; + } + if(trace.side === 'negative') { + dPos = function(di) { + var pos = shiftPos(di); + return Fx.inbox(pos - posAcceptance, pos, hoverPseudoDistance); + }; + boxDeltaPos = 0; + boxDeltaNeg = boxDelta; + } + } else { + dPos = function(di) { + var pos = shiftPos(di); + return Fx.inbox(pos - posAcceptance, pos + posAcceptance, hoverPseudoDistance); + }; + boxDeltaPos = boxDeltaNeg = boxDelta; + } + + var dVal; + + if(isViolin) { + dVal = function(di) { + return Fx.inbox(di.span[0] - vVal, di.span[1] - vVal, hoverPseudoDistance); + }; + } else { + dVal = function(di) { + return Fx.inbox(di.min - vVal, di.max - vVal, hoverPseudoDistance); + }; + } + + if(trace.orientation === 'h') { + vVal = xval; + pVal = yval; + dx = dVal; + dy = dPos; + pLetter = 'y'; + pAxis = ya; + vLetter = 'x'; + vAxis = xa; + } else { + vVal = yval; + pVal = xval; + dx = dPos; + dy = dVal; + pLetter = 'x'; + pAxis = xa; + vLetter = 'y'; + vAxis = ya; + } + + // if two boxes are overlaying, let the narrowest one win + var pseudoDistance = Math.min(1, boxDelta / Math.abs(pAxis.r2c(pAxis.range[1]) - pAxis.r2c(pAxis.range[0]))); + hoverPseudoDistance = pointData.maxHoverDistance - pseudoDistance; + spikePseudoDistance = pointData.maxSpikeDistance - pseudoDistance; + + function dxy(di) { return (dx(di) + dy(di)) / 2; } + var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); + Fx.getClosest(cd, distfn, pointData); + + // skip the rest (for this trace) if we didn't find a close point + // and create the item(s) in closedata for this point + if(pointData.index === false) return []; + + var di = cd[pointData.index]; + var lc = trace.line.color; + var mc = (trace.marker || {}).color; + + if(Color.opacity(lc) && trace.line.width) pointData.color = lc; + else if(Color.opacity(mc) && trace.boxpoints) pointData.color = mc; + else pointData.color = trace.fillcolor; + + pointData[pLetter + '0'] = pAxis.c2p(di.pos + t.bPos - boxDeltaNeg, true); + pointData[pLetter + '1'] = pAxis.c2p(di.pos + t.bPos + boxDeltaPos, true); + + pointData[pLetter + 'LabelVal'] = di.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":51,"../../components/fx":89,"../../lib":169,"../../plots/cartesian/axes":213}],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 = { + 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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', + + } +}; + +},{}],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 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":258,"./layout_attributes":290}],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 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 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 + // 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 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); + + 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":169,"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'; + +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; +}; + +},{}],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 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":51,"../../components/drawing":72,"d3":16}],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 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, + hoverongaps: heatmapAttrs.hoverongaps, + connectgaps: extendFlat({}, heatmapAttrs.connectgaps, { + + }), + + fillcolor: { + valType: 'color', + + editType: 'calc', + + }, + + autocontour: { + valType: 'boolean', + dflt: true, + + editType: 'calc', + impliedEdits: { + 'contours.start': undefined, + 'contours.end': undefined, + 'contours.size': undefined + }, + + }, + ncontours: { + valType: 'integer', + dflt: 15, + min: 1, + + editType: 'calc', + + }, + + contours: { + type: { + valType: 'enumerated', + values: ['levels', 'constraint'], + dflt: 'levels', + + editType: 'calc', + + }, + start: { + valType: 'number', + dflt: null, + + editType: 'plot', + impliedEdits: {'^autocontour': false}, + + }, + end: { + valType: 'number', + dflt: null, + + editType: 'plot', + impliedEdits: {'^autocontour': false}, + + }, + size: { + valType: 'number', + dflt: null, + min: 0, + + editType: 'plot', + impliedEdits: {'^autocontour': false}, + + }, + coloring: { + valType: 'enumerated', + values: ['fill', 'heatmap', 'lines', 'none'], + dflt: 'fill', + + editType: 'calc', + + }, + showlines: { + valType: 'boolean', + dflt: true, + + editType: 'plot', + + }, + showlabels: { + valType: 'boolean', + dflt: false, + + editType: 'plot', + + }, + labelfont: fontAttrs({ + editType: 'plot', + colorEditType: 'style', + + }), + labelformat: { + valType: 'string', + dflt: '', + + editType: 'plot', + + }, + operation: { + valType: 'enumerated', + values: [].concat(COMPARISON_OPS2).concat(INTERVAL_OPS), + + dflt: '=', + editType: 'calc', + + }, + value: { + valType: 'any', + dflt: 0, + + editType: 'calc', + + }, + editType: 'calc', + impliedEdits: {'autocontour': false} + }, + + line: { + color: extendFlat({}, scatterLineAttrs.color, { + editType: 'style+colorbars', + + }), + width: { + valType: 'number', + min: 0, + + editType: 'style+colorbars', + + }, + dash: dash, + smoothing: extendFlat({}, scatterLineAttrs.smoothing, { + + }), + editType: 'plot' + } +}, + colorScaleAttrs('', { + cLetter: 'z', + autoColorDflt: false, + editTypeOverride: 'calc' + }) +); + +},{"../../components/colorscale/attributes":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":318,"./end_plus":306,"./set_contours":314}],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 = function(pathinfo, contours) { + var pi0 = pathinfo[0]; + var z = pi0.z; + var i; + + switch(contours.type) { + case 'levels': + // Why (just) use z[0][0] and z[0][1]? + // + // N.B. using boundaryMin instead of edgeVal2 here makes the + // `contour_scatter` mock fail + var edgeVal2 = Math.min(z[0][0], z[0][1]); + + for(i = 0; i < pathinfo.length; i++) { + var pi = pathinfo[i]; + pi.prefixBoundary = !pi.edgepaths.length && + (edgeVal2 > pi.level || pi.starts.length && edgeVal2 === pi.level); + } + break; + case 'constraint': + // after convertToConstraints, pathinfo has length=0 + pi0.prefixBoundary = false; + + // joinAllPaths does enough already when edgepaths are present + if(pi0.edgepaths.length) return; + + var na = pi0.x.length; + var nb = pi0.y.length; + var boundaryMax = -Infinity; + var boundaryMin = Infinity; + + for(i = 0; i < nb; i++) { + boundaryMin = Math.min(boundaryMin, z[i][0]); + boundaryMin = Math.min(boundaryMin, z[i][na - 1]); + boundaryMax = Math.max(boundaryMax, z[i][0]); + boundaryMax = Math.max(boundaryMax, z[i][na - 1]); + } + for(i = 1; i < na - 1; i++) { + boundaryMin = Math.min(boundaryMin, z[0][i]); + boundaryMin = Math.min(boundaryMin, z[nb - 1][i]); + boundaryMax = Math.max(boundaryMax, z[0][i]); + boundaryMax = Math.max(boundaryMax, z[nb - 1][i]); + } + + var contoursValue = contours.value; + var v1, v2; + + switch(contours._operation) { + case '>': + if(contoursValue > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + case '<': + if(contoursValue < boundaryMin || + (pi0.starts.length && contoursValue === boundaryMin)) { + pi0.prefixBoundary = true; + } + break; + case '[]': + v1 = Math.min(contoursValue[0], contoursValue[1]); + v2 = Math.max(contoursValue[0], contoursValue[1]); + if(v2 < boundaryMin || v1 > boundaryMax || + (pi0.starts.length && v2 === boundaryMin)) { + pi0.prefixBoundary = true; + } + break; + case '][': + v1 = Math.min(contoursValue[0], contoursValue[1]); + v2 = Math.max(contoursValue[0], contoursValue[1]); + if(v1 < boundaryMin && v2 > boundaryMax) { + pi0.prefixBoundary = true; + } + break; + } + break; + } +}; + +},{}],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 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":306,"./make_color_map":311}],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'; +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 + } +}; + +},{}],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 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":310,"fast-isnumeric":18}],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 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}],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'; + +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'); +}; + +},{}],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'); + +// The contour extraction is great, except it totally fails for constraints because we +// need weird range loops and flipped contours instead of the usual format. This function +// does some weird manipulation of the extracted pathinfo data such that it magically +// draws contours correctly *as* constraints. +// +// ** I do not know which "weird range loops" the comment above is referring to. +module.exports = function(pathinfo, operation) { + var i, pi0, pi1; + + var op0 = function(arr) { return arr.reverse(); }; + var op1 = function(arr) { return arr; }; + + switch(operation) { + case '=': + case '<': + return pathinfo; + case '>': + if(pathinfo.length !== 1) { + Lib.warn('Contour data invalid for the specified inequality operation.'); + } + + // In this case there should be exactly one contour levels in pathinfo. + // We flip all of the data. This will draw the contour as closed. + pi0 = pathinfo[0]; + + for(i = 0; i < pi0.edgepaths.length; i++) { + pi0.edgepaths[i] = op0(pi0.edgepaths[i]); + } + for(i = 0; i < pi0.paths.length; i++) { + pi0.paths[i] = op0(pi0.paths[i]); + } + for(i = 0; i < pi0.starts.length; i++) { + pi0.starts[i] = op0(pi0.starts[i]); + } + + return pathinfo; + case '][': + var tmp = op0; + op0 = op1; + op1 = tmp; + // It's a nice rule, except this definitely *is* what's intended here. + /* eslint-disable: no-fallthrough */ + case '[]': + /* eslint-enable: no-fallthrough */ + if(pathinfo.length !== 2) { + Lib.warn('Contour data invalid for the specified inequality range operation.'); + } + + // In this case there should be exactly two contour levels in pathinfo. + // - We concatenate the info into one pathinfo. + // - We must also flip all of the data in the `[]` case. + // This will draw the contours as closed. + pi0 = copyPathinfo(pathinfo[0]); + pi1 = copyPathinfo(pathinfo[1]); + + for(i = 0; i < pi0.edgepaths.length; i++) { + pi0.edgepaths[i] = op0(pi0.edgepaths[i]); + } + for(i = 0; i < pi0.paths.length; i++) { + pi0.paths[i] = op0(pi0.paths[i]); + } + for(i = 0; i < pi0.starts.length; i++) { + pi0.starts[i] = op0(pi0.starts[i]); + } + + while(pi1.edgepaths.length) { + pi0.edgepaths.push(op1(pi1.edgepaths.shift())); + } + while(pi1.paths.length) { + pi0.paths.push(op1(pi1.paths.shift())); + } + while(pi1.starts.length) { + pi0.starts.push(op1(pi1.starts.shift())); + } + + return [pi0]; + } +}; + +function copyPathinfo(pi) { + return Lib.extendFlat({}, pi, { + edgepaths: Lib.extendDeep([], pi.edgepaths), + paths: Lib.extendDeep([], pi.paths), + starts: Lib.extendDeep([], pi.starts) + }); +} + +},{"../../lib":169}],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 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'); + coerce('hoverongaps'); + + var isConstraint = (coerce('contours.type') === 'constraint'); + coerce('connectgaps', Lib.isArray1D(traceOut.z)); + + if(isConstraint) { + handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor); + } else { + handleContoursDefaults(traceIn, traceOut, coerce, coerce2); + handleStyleDefaults(traceIn, traceOut, coerce, layout); + } +}; + +},{"../../lib":169,"../heatmap/xyz_defaults":331,"./attributes":295,"./constraint_defaults":300,"./contours_defaults":302,"./style_defaults":316}],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 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":301,"./end_plus":306}],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'; + +/* + * 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; +}; + +},{}],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 Lib = _dereq_('../../lib'); +var constants = _dereq_('./constants'); + +module.exports = function findAllPaths(pathinfo, xtol, ytol) { + var cnt, + startLoc, + i, + pi, + j; + + // Default just passes these values through as they were before: + xtol = xtol || 0.01; + ytol = ytol || 0.01; + + for(i = 0; i < pathinfo.length; i++) { + pi = pathinfo[i]; + + for(j = 0; j < pi.starts.length; j++) { + startLoc = pi.starts[j]; + makePath(pi, startLoc, 'edge', xtol, ytol); + } + + cnt = 0; + while(Object.keys(pi.crossings).length && cnt < 10000) { + cnt++; + startLoc = Object.keys(pi.crossings)[0].split(',').map(Number); + makePath(pi, startLoc, undefined, xtol, ytol); + } + if(cnt === 10000) Lib.log('Infinite loop in contour?'); + } +}; + +function equalPts(pt1, pt2, xtol, ytol) { + return Math.abs(pt1[0] - pt2[0]) < xtol && + Math.abs(pt1[1] - pt2[1]) < ytol; +} + +// distance in index units - uses the 3rd and 4th items in points +function ptDist(pt1, pt2) { + var dx = pt1[2] - pt2[2]; + var dy = pt1[3] - pt2[3]; + return Math.sqrt(dx * dx + dy * dy); +} + +function makePath(pi, loc, edgeflag, xtol, ytol) { + var locStr = loc.join(','); + var mi = pi.crossings[locStr]; + var marchStep = getStartStep(mi, edgeflag, loc); + // start by going backward a half step and finding the crossing point + var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])]; + var m = pi.z.length; + var n = pi.z[0].length; + var startLoc = loc.slice(); + var startStep = marchStep.slice(); + var cnt; + + // now follow the path + for(cnt = 0; cnt < 10000; cnt++) { // just to avoid infinite loops + if(mi > 20) { + mi = constants.CHOOSESADDLE[mi][(marchStep[0] || marchStep[1]) < 0 ? 0 : 1]; + pi.crossings[locStr] = constants.SADDLEREMAINDER[mi]; + } else { + delete pi.crossings[locStr]; + } + + marchStep = constants.NEWDELTA[mi]; + if(!marchStep) { + Lib.log('Found bad marching index:', mi, loc, pi.level); + break; + } + + // find the crossing a half step forward, and then take the full step + pts.push(getInterpPx(pi, loc, marchStep)); + loc[0] += marchStep[0]; + loc[1] += marchStep[1]; + locStr = loc.join(','); + + // don't include the same point multiple times + if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop(); + + var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) || + (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2)); + + var closedLoop = loc[0] === startLoc[0] && loc[1] === startLoc[1] && + marchStep[0] === startStep[0] && marchStep[1] === startStep[1]; + + // have we completed a loop, or reached an edge? + if((closedLoop) || (edgeflag && atEdge)) break; + + mi = pi.crossings[locStr]; + } + + if(cnt === 10000) { + Lib.log('Infinite loop in contour?'); + } + var closedpath = equalPts(pts[0], pts[pts.length - 1], xtol, ytol); + var totaldist = 0; + var distThresholdFactor = 0.2 * pi.smoothing; + var alldists = []; + var cropstart = 0; + var distgroup, cnt2, cnt3, newpt, ptcnt, ptavg, thisdist, + i, j, edgepathi, edgepathj; + + /* + * Check for points that are too close together (<1/5 the average dist + * *in grid index units* (important for log axes and nonuniform grids), + * less if less smoothed) and just take the center (or avg of center 2). + * This cuts down on funny behavior when a point is very close to a + * contour level. + */ + for(cnt = 1; cnt < pts.length; cnt++) { + thisdist = ptDist(pts[cnt], pts[cnt - 1]); + totaldist += thisdist; + alldists.push(thisdist); + } + + var distThreshold = totaldist / alldists.length * distThresholdFactor; + + function getpt(i) { return pts[i % pts.length]; } + + for(cnt = pts.length - 2; cnt >= cropstart; cnt--) { + distgroup = alldists[cnt]; + if(distgroup < distThreshold) { + cnt3 = 0; + for(cnt2 = cnt - 1; cnt2 >= cropstart; cnt2--) { + if(distgroup + alldists[cnt2] < distThreshold) { + distgroup += alldists[cnt2]; + } else break; + } + + // closed path with close points wrapping around the boundary? + if(closedpath && cnt === pts.length - 2) { + for(cnt3 = 0; cnt3 < cnt2; cnt3++) { + if(distgroup + alldists[cnt3] < distThreshold) { + distgroup += alldists[cnt3]; + } else break; + } + } + ptcnt = cnt - cnt2 + cnt3 + 1; + ptavg = Math.floor((cnt + cnt2 + cnt3 + 2) / 2); + + // either endpoint included: keep the endpoint + if(!closedpath && cnt === pts.length - 2) newpt = pts[pts.length - 1]; + else if(!closedpath && cnt2 === -1) newpt = pts[0]; + + // odd # of points - just take the central one + else if(ptcnt % 2) newpt = getpt(ptavg); + + // even # of pts - average central two + else { + newpt = [(getpt(ptavg)[0] + getpt(ptavg + 1)[0]) / 2, + (getpt(ptavg)[1] + getpt(ptavg + 1)[1]) / 2]; + } + + pts.splice(cnt2 + 1, cnt - cnt2 + 1, newpt); + cnt = cnt2 + 1; + if(cnt3) cropstart = cnt3; + if(closedpath) { + if(cnt === pts.length - 2) pts[cnt3] = pts[pts.length - 1]; + else if(cnt === 0) pts[pts.length - 1] = pts[0]; + } + } + } + pts.splice(0, cropstart); + + // done with the index parts - remove them so path generation works right + // because it depends on only having [xpx, ypx] + for(cnt = 0; cnt < pts.length; cnt++) pts[cnt].length = 2; + + // don't return single-point paths (ie all points were the same + // so they got deleted?) + if(pts.length < 2) return; + else if(closedpath) { + pts.pop(); + pi.paths.push(pts); + } else { + if(!edgeflag) { + Lib.log('Unclosed interior contour?', + pi.level, startLoc.join(','), pts.join('L')); + } + + // edge path - does it start where an existing edge path ends, or vice versa? + var merged = false; + for(i = 0; i < pi.edgepaths.length; i++) { + edgepathi = pi.edgepaths[i]; + if(!merged && equalPts(edgepathi[0], pts[pts.length - 1], xtol, ytol)) { + pts.pop(); + merged = true; + + // now does it ALSO meet the end of another (or the same) path? + var doublemerged = false; + for(j = 0; j < pi.edgepaths.length; j++) { + edgepathj = pi.edgepaths[j]; + if(equalPts(edgepathj[edgepathj.length - 1], pts[0], xtol, ytol)) { + doublemerged = true; + pts.shift(); + pi.edgepaths.splice(i, 1); + if(j === i) { + // the path is now closed + pi.paths.push(pts.concat(edgepathj)); + } else { + if(j > i) j--; + pi.edgepaths[j] = edgepathj.concat(pts, edgepathi); + } + break; + } + } + if(!doublemerged) { + pi.edgepaths[i] = pts.concat(edgepathi); + } + } + } + for(i = 0; i < pi.edgepaths.length; i++) { + if(merged) break; + edgepathi = pi.edgepaths[i]; + if(equalPts(edgepathi[edgepathi.length - 1], pts[0], xtol, ytol)) { + pts.shift(); + pi.edgepaths[i] = edgepathi.concat(pts); + merged = true; + } + } + + if(!merged) pi.edgepaths.push(pts); + } +} + +// special function to get the marching step of the +// first point in the path (leading to loc) +function getStartStep(mi, edgeflag, loc) { + var dx = 0; + var dy = 0; + if(mi > 20 && edgeflag) { + // these saddles start at +/- x + if(mi === 208 || mi === 1114) { + // if we're starting at the left side, we must be going right + dx = loc[0] === 0 ? 1 : -1; + } else { + // if we're starting at the bottom, we must be going up + dy = loc[1] === 0 ? 1 : -1; + } + } else if(constants.BOTTOMSTART.indexOf(mi) !== -1) dy = 1; + else if(constants.LEFTSTART.indexOf(mi) !== -1) dx = 1; + else if(constants.TOPSTART.indexOf(mi) !== -1) dy = -1; + else dx = -1; + return [dx, dy]; +} + +/* + * Find the pixel coordinates of a particular crossing + * + * @param {object} pi: the pathinfo object at this level + * @param {array} loc: the grid index [x, y] of the crossing + * @param {array} step: the direction [dx, dy] we're moving on the grid + * + * @return {array} [xpx, ypx, xi, yi]: the first two are the pixel location, + * the next two are the interpolated grid indices, which we use for + * distance calculations to delete points that are too close together. + * This is important when the grid is nonuniform (and most dramatically when + * we're on log axes and include invalid (0 or negative) values. + * It's crucial to delete these extra two before turning an array of these + * points into a path, because those routines require length-2 points. + */ +function getInterpPx(pi, loc, step) { + var locx = loc[0] + Math.max(step[0], 0); + var locy = loc[1] + Math.max(step[1], 0); + var zxy = pi.z[locy][locx]; + var xa = pi.xaxis; + var ya = pi.yaxis; + + if(step[1]) { + var dx = (pi.level - zxy) / (pi.z[locy][locx + 1] - zxy); + + return [xa.c2p((1 - dx) * pi.x[locx] + dx * pi.x[locx + 1], true), + ya.c2p(pi.y[locy], true), + locx + dx, locy]; + } else { + var dy = (pi.level - zxy) / (pi.z[locy + 1][locx] - zxy); + return [xa.c2p(pi.x[locx], true), + ya.c2p((1 - dy) * pi.y[locy] + dy * pi.y[locy + 1], true), + locx, locy + dy]; + } +} + +},{"../../lib":169,"./constants":299}],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 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":324}],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'; + +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":295,"./calc":296,"./colorbar":298,"./defaults":304,"./hover":308,"./plot":313,"./style":315}],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 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}],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 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":306,"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 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":299}],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 Lib = _dereq_('../../lib'); +var Drawing = _dereq_('../../components/drawing'); +var Colorscale = _dereq_('../../components/colorscale'); +var svgTextUtils = _dereq_('../../lib/svg_text_utils'); +var Axes = _dereq_('../../plots/cartesian/axes'); +var setConvert = _dereq_('../../plots/cartesian/set_convert'); + +var heatmapPlot = _dereq_('../heatmap/plot'); +var makeCrossings = _dereq_('./make_crossings'); +var findAllPaths = _dereq_('./find_all_paths'); +var emptyPathinfo = _dereq_('./empty_pathinfo'); +var convertToConstraints = _dereq_('./convert_to_constraints'); +var closeBoundaries = _dereq_('./close_boundaries'); +var constants = _dereq_('./constants'); +var costConstants = constants.LABELOPTIMIZER; + +exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) { + var xa = plotinfo.xaxis; + var ya = plotinfo.yaxis; + + Lib.makeTraceGroups(contourLayer, cdcontours, 'contour').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + var x = cd0.x; + var y = cd0.y; + var contours = trace.contours; + var pathinfo = emptyPathinfo(contours, plotinfo, cd0); + + // use a heatmap to fill - draw it behind the lines + var heatmapColoringLayer = Lib.ensureSingle(plotGroup, 'g', 'heatmapcoloring'); + var cdheatmaps = []; + if(contours.coloring === 'heatmap') { + cdheatmaps = [cd]; + } + heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer); + + makeCrossings(pathinfo); + findAllPaths(pathinfo); + + var leftedge = xa.c2p(x[0], true); + var rightedge = xa.c2p(x[x.length - 1], true); + var bottomedge = ya.c2p(y[0], true); + var topedge = ya.c2p(y[y.length - 1], true); + var perimeter = [ + [leftedge, topedge], + [rightedge, topedge], + [rightedge, bottomedge], + [leftedge, bottomedge] + ]; + + var fillPathinfo = pathinfo; + if(contours.type === 'constraint') { + // N.B. this also mutates pathinfo + fillPathinfo = convertToConstraints(pathinfo, contours._operation); + } + + // draw everything + makeBackground(plotGroup, perimeter, contours); + makeFills(plotGroup, fillPathinfo, perimeter, contours); + makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours); + clipGaps(plotGroup, plotinfo, gd, cd0, perimeter); + }); +}; + +function makeBackground(plotgroup, perimeter, contours) { + var bggroup = Lib.ensureSingle(plotgroup, 'g', 'contourbg'); + + var bgfill = bggroup.selectAll('path') + .data(contours.coloring === 'fill' ? [0] : []); + bgfill.enter().append('path'); + bgfill.exit().remove(); + bgfill + .attr('d', 'M' + perimeter.join('L') + 'Z') + .style('stroke', 'none'); +} + +function makeFills(plotgroup, pathinfo, perimeter, contours) { + var hasFills = contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '='); + var boundaryPath = 'M' + perimeter.join('L') + 'Z'; + + // fills prefixBoundary in pathinfo items + if(hasFills) { + closeBoundaries(pathinfo, contours); + } + + var fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill'); + + var fillitems = fillgroup.selectAll('path').data(hasFills ? pathinfo : []); + fillitems.enter().append('path'); + fillitems.exit().remove(); + fillitems.each(function(pi) { + // join all paths for this level together into a single path + // first follow clockwise around the perimeter to close any open paths + // if the whole perimeter is above this level, start with a path + // enclosing the whole thing. With all that, the parity should mean + // that we always fill everything above the contour, nothing below + var fullpath = (pi.prefixBoundary ? boundaryPath : '') + + joinAllPaths(pi, perimeter); + + if(!fullpath) { + d3.select(this).remove(); + } else { + d3.select(this) + .attr('d', fullpath) + .style('stroke', 'none'); + } + }); +} + +function joinAllPaths(pi, perimeter) { + var fullpath = ''; + var i = 0; + var startsleft = pi.edgepaths.map(function(v, i) { return i; }); + var newloop = true; + var endpt; + var newendpt; + var cnt; + var nexti; + var possiblei; + var addpath; + + function istop(pt) { return Math.abs(pt[1] - perimeter[0][1]) < 0.01; } + function isbottom(pt) { return Math.abs(pt[1] - perimeter[2][1]) < 0.01; } + function isleft(pt) { return Math.abs(pt[0] - perimeter[0][0]) < 0.01; } + function isright(pt) { return Math.abs(pt[0] - perimeter[2][0]) < 0.01; } + + while(startsleft.length) { + addpath = Drawing.smoothopen(pi.edgepaths[i], pi.smoothing); + fullpath += newloop ? addpath : addpath.replace(/^M/, 'L'); + startsleft.splice(startsleft.indexOf(i), 1); + endpt = pi.edgepaths[i][pi.edgepaths[i].length - 1]; + nexti = -1; + + // now loop through sides, moving our endpoint until we find a new start + for(cnt = 0; cnt < 4; cnt++) { // just to prevent infinite loops + if(!endpt) { + Lib.log('Missing end?', i, pi); + break; + } + + if(istop(endpt) && !isright(endpt)) newendpt = perimeter[1]; // right top + else if(isleft(endpt)) newendpt = perimeter[0]; // left top + else if(isbottom(endpt)) newendpt = perimeter[3]; // right bottom + else if(isright(endpt)) newendpt = perimeter[2]; // left bottom + + for(possiblei = 0; possiblei < pi.edgepaths.length; possiblei++) { + var ptNew = pi.edgepaths[possiblei][0]; + // is ptNew on the (horz. or vert.) segment from endpt to newendpt? + if(Math.abs(endpt[0] - newendpt[0]) < 0.01) { + if(Math.abs(endpt[0] - ptNew[0]) < 0.01 && + (ptNew[1] - endpt[1]) * (newendpt[1] - ptNew[1]) >= 0) { + newendpt = ptNew; + nexti = possiblei; + } + } else if(Math.abs(endpt[1] - newendpt[1]) < 0.01) { + if(Math.abs(endpt[1] - ptNew[1]) < 0.01 && + (ptNew[0] - endpt[0]) * (newendpt[0] - ptNew[0]) >= 0) { + newendpt = ptNew; + nexti = possiblei; + } + } else { + Lib.log('endpt to newendpt is not vert. or horz.', + endpt, newendpt, ptNew); + } + } + + endpt = newendpt; + + if(nexti >= 0) break; + fullpath += 'L' + newendpt; + } + + if(nexti === pi.edgepaths.length) { + Lib.log('unclosed perimeter path'); + break; + } + + i = nexti; + + // if we closed back on a loop we already included, + // close it and start a new loop + newloop = (startsleft.indexOf(i) === -1); + if(newloop) { + i = startsleft[0]; + fullpath += 'Z'; + } + } + + // finally add the interior paths + for(i = 0; i < pi.paths.length; i++) { + fullpath += Drawing.smoothclosed(pi.paths[i], pi.smoothing); + } + + return fullpath; +} + +function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours) { + var lineContainer = Lib.ensureSingle(plotgroup, 'g', 'contourlines'); + var showLines = contours.showlines !== false; + var showLabels = contours.showlabels; + var clipLinesForLabels = showLines && showLabels; + + // Even if we're not going to show lines, we need to create them + // if we're showing labels, because the fill paths include the perimeter + // so can't be used to position the labels correctly. + // In this case we'll remove the lines after making the labels. + var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo); + + var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid); + + var labelGroup = plotgroup.selectAll('g.contourlabels') + .data(showLabels ? [0] : []); + + labelGroup.exit().remove(); + + labelGroup.enter().append('g') + .classed('contourlabels', true); + + if(showLabels) { + var labelClipPathData = []; + var labelData = []; + + // invalidate the getTextLocation cache in case paths changed + Lib.clearLocationCache(); + + var contourFormat = exports.labelFormatter(gd, cd0); + + var dummyText = Drawing.tester.append('text') + .attr('data-notex', 1) + .call(Drawing.font, contours.labelfont); + + var xa = pathinfo[0].xaxis; + var ya = pathinfo[0].yaxis; + var xLen = xa._length; + var yLen = ya._length; + var xRng = xa.range; + var yRng = ya.range; + var xMin = Lib.aggNums(Math.min, null, cd0.x); + var xMax = Lib.aggNums(Math.max, null, cd0.x); + var yMin = Lib.aggNums(Math.min, null, cd0.y); + var yMax = Lib.aggNums(Math.max, null, cd0.y); + var x0 = Math.max(xa.c2p(xMin, true), 0); + var x1 = Math.min(xa.c2p(xMax, true), xLen); + var y0 = Math.max(ya.c2p(yMax, true), 0); + var y1 = Math.min(ya.c2p(yMin, true), yLen); + + // visible bounds of the contour trace (and the midpoints, to + // help with cost calculations) + var bounds = {}; + + if(xRng[0] < xRng[1]) { + bounds.left = x0; + bounds.right = x1; + } else { + bounds.left = x1; + bounds.right = x0; + } + + if(yRng[0] < yRng[1]) { + bounds.top = y0; + bounds.bottom = y1; + } else { + bounds.top = y1; + bounds.bottom = y0; + } + + bounds.middle = (bounds.top + bounds.bottom) / 2; + bounds.center = (bounds.left + bounds.right) / 2; + + labelClipPathData.push([ + [bounds.left, bounds.top], + [bounds.right, bounds.top], + [bounds.right, bounds.bottom], + [bounds.left, bounds.bottom] + ]); + + var plotDiagonal = Math.sqrt(xLen * xLen + yLen * yLen); + + // the path length to use to scale the number of labels to draw: + var normLength = constants.LABELDISTANCE * plotDiagonal / + Math.max(1, pathinfo.length / constants.LABELINCREASE); + + linegroup.each(function(d) { + var textOpts = exports.calcTextOpts(d.level, contourFormat, dummyText, gd); + + d3.select(this).selectAll('path').each(function() { + var path = this; + var pathBounds = Lib.getVisibleSegment(path, bounds, textOpts.height / 2); + if(!pathBounds) return; + + if(pathBounds.len < (textOpts.width + textOpts.height) * constants.LABELMIN) return; + + var maxLabels = Math.min(Math.ceil(pathBounds.len / normLength), + constants.LABELMAX); + + for(var i = 0; i < maxLabels; i++) { + var loc = exports.findBestTextLocation(path, pathBounds, textOpts, + labelData, bounds); + + if(!loc) break; + + exports.addLabelData(loc, textOpts, labelData, labelClipPathData); + } + }); + }); + + dummyText.remove(); + + exports.drawLabels(labelGroup, labelData, gd, lineClip, + clipLinesForLabels ? labelClipPathData : null); + } + + if(showLabels && !showLines) linegroup.remove(); +} + +exports.createLines = function(lineContainer, makeLines, pathinfo) { + var smoothing = pathinfo[0].smoothing; + + var linegroup = lineContainer.selectAll('g.contourlevel') + .data(makeLines ? pathinfo : []); + + linegroup.exit().remove(); + linegroup.enter().append('g') + .classed('contourlevel', true); + + if(makeLines) { + // pedgepaths / ppaths are used by contourcarpet, for the paths transformed from a/b to x/y + // edgepaths / paths are used by contour since it's in x/y from the start + var opencontourlines = linegroup.selectAll('path.openline') + .data(function(d) { return d.pedgepaths || d.edgepaths; }); + + opencontourlines.exit().remove(); + opencontourlines.enter().append('path') + .classed('openline', true); + + opencontourlines + .attr('d', function(d) { + return Drawing.smoothopen(d, smoothing); + }) + .style('stroke-miterlimit', 1) + .style('vector-effect', 'non-scaling-stroke'); + + var closedcontourlines = linegroup.selectAll('path.closedline') + .data(function(d) { return d.ppaths || d.paths; }); + + closedcontourlines.exit().remove(); + closedcontourlines.enter().append('path') + .classed('closedline', true); + + closedcontourlines + .attr('d', function(d) { + return Drawing.smoothclosed(d, smoothing); + }) + .style('stroke-miterlimit', 1) + .style('vector-effect', 'non-scaling-stroke'); + } + + return linegroup; +}; + +exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) { + var clips = gd._fullLayout._clips; + var clipId = clipLinesForLabels ? ('clipline' + uid) : null; + + var lineClip = clips.selectAll('#' + clipId) + .data(clipLinesForLabels ? [0] : []); + lineClip.exit().remove(); + + lineClip.enter().append('clipPath') + .classed('contourlineclip', true) + .attr('id', clipId); + + Drawing.setClipUrl(lineContainer, clipId, gd); + + return lineClip; +}; + +exports.labelFormatter = function(gd, cd0) { + var fullLayout = gd._fullLayout; + var trace = cd0.trace; + var contours = trace.contours; + + if(contours.labelformat) { + return fullLayout._d3locale.numberFormat(contours.labelformat); + } else { + var formatAxis; + var cOpts = Colorscale.extractOpts(trace); + if(cOpts && cOpts.colorbar && cOpts.colorbar._axis) { + formatAxis = cOpts.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 trace = cd0.trace; + var clips = gd._fullLayout._clips; + var clipId = 'clip' + trace.uid; + + var clipPath = clips.selectAll('#' + clipId) + .data(trace.connectgaps ? [] : [0]); + clipPath.enter().append('clipPath') + .classed('contourclip', true) + .attr('id', clipId); + clipPath.exit().remove(); + + if(trace.connectgaps === false) { + var clipPathInfo = { + // fraction of the way from missing to present point + // to draw the boundary. + // if you make this 1 (or 1-epsilon) then a point in + // a sea of missing data will disappear entirely. + level: 0.9, + crossings: {}, + starts: [], + edgepaths: [], + paths: [], + xaxis: plotinfo.xaxis, + yaxis: plotinfo.yaxis, + x: cd0.x, + y: cd0.y, + // 0 = no data, 1 = data + z: makeClipMask(cd0), + smoothing: 0 + }; + + makeCrossings([clipPathInfo]); + findAllPaths([clipPathInfo]); + closeBoundaries([clipPathInfo], {type: 'levels'}); + + var path = Lib.ensureSingle(clipPath, 'path', ''); + path.attr('d', + (clipPathInfo.prefixBoundary ? 'M' + perimeter.join('L') + 'Z' : '') + + joinAllPaths(clipPathInfo, perimeter) + ); + } else clipId = null; + + Drawing.setClipUrl(plotGroup, clipId, gd); +} + +function makeClipMask(cd0) { + var empties = cd0.trace._emptypoints; + var z = []; + var m = cd0.z.length; + var n = cd0.z[0].length; + var i; + var row = []; + var emptyPoint; + + for(i = 0; i < n; i++) row.push(1); + for(i = 0; i < m; i++) z.push(row.slice()); + for(i = 0; i < empties.length; i++) { + emptyPoint = empties[i]; + z[emptyPoint[0]][emptyPoint[1]] = 0; + } + // save this mask to determine whether to show this data in hover + cd0.zmask = z; + return z; +} + +},{"../../components/colorscale":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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}],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 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":329,"./make_color_map":311,"d3":16}],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 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":310}],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 scatterAttrs = _dereq_('../scatter/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; + +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', + + }, + hoverongaps: { + valType: 'boolean', + dflt: true, + + editType: 'none', + + }, + connectgaps: { + valType: 'boolean', + + editType: 'calc', + + }, + xgap: { + valType: 'number', + dflt: 0, + min: 0, + + editType: 'plot', + + }, + ygap: { + valType: 'number', + dflt: 0, + min: 0, + + editType: 'plot', + + }, + zhoverformat: { + valType: 'string', + dflt: '', + + editType: 'none', + + }, + hovertemplate: hovertemplateAttrs() +}, { + transforms: undefined +}, + 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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":18}],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'; + +module.exports = { + min: 'zmin', + max: 'zmax' +}; + +},{}],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 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}],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 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('hoverongaps'); + coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false)); + + 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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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}],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 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; + + if(zVal === undefined && !trace.hoverongaps) return; + + var text; + if(Array.isArray(cd0.hovertext) && Array.isArray(cd0.hovertext[ny])) { + text = cd0.hovertext[ny][nx]; + } else if(Array.isArray(cd0.text) && Array.isArray(cd0.text[ny])) { + text = cd0.text[ny][nx]; + } + + // dummy axis for formatting the z value + var cOpts = extractOpts(trace); + var dummyAx = { + type: 'linear', + range: [cOpts.min, cOpts.max], + hoverformat: zhoverformat, + _separators: xa._separators, + _numFormat: xa._numFormat + }; + var zLabel = Axes.tickText(dummyAx, zVal, 'hover').text; + + return [Lib.extendFlat(pointData, { + index: [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":89,"../../lib":169,"../../plots/cartesian/axes":213}],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'; + +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":317,"./calc":318,"./colorbar":320,"./defaults":322,"./hover":324,"./plot":328,"./style":329}],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 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}],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 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":258}],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'); +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":169,"../../registry":258,"d3":16,"tinycolor2":34}],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 d3 = _dereq_('d3'); + +module.exports = function style(gd) { + d3.select(gd).selectAll('.hm image') + .style('opacity', function(d) { + return d.trace.opacity; + }); +}; + +},{"d3":16}],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'; + +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'); +}; + +},{}],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 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":258,"fast-isnumeric":18}],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'; + +var barAttrs = _dereq_('../bar/attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var makeBinAttrs = _dereq_('./bin_attributes'); +var constants = _dereq_('./constants'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = { + x: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + + }, + y: { + valType: 'data_array', + editType: 'calc+clearAxisTypes', + + }, + + text: extendFlat({}, barAttrs.text, { + + }), + hovertext: extendFlat({}, barAttrs.hovertext, { + + }), + orientation: barAttrs.orientation, + + histfunc: { + valType: 'enumerated', + values: ['count', 'sum', 'avg', 'min', 'max'], + + dflt: 'count', + editType: 'calc', + + }, + histnorm: { + valType: 'enumerated', + values: ['', 'percent', 'probability', 'density', 'probability density'], + dflt: '', + + editType: 'calc', + + }, + + cumulative: { + enabled: { + valType: 'boolean', + dflt: false, + + editType: 'calc', + + }, + + direction: { + valType: 'enumerated', + values: ['increasing', 'decreasing'], + dflt: 'increasing', + + editType: 'calc', + + }, + + currentbin: { + valType: 'enumerated', + values: ['include', 'exclude', 'half'], + dflt: 'include', + + editType: 'calc', + + }, + editType: 'calc' + }, + nbinsx: { + valType: 'integer', + min: 0, + dflt: 0, + + editType: 'calc', + + }, + xbins: makeBinAttrs('x', true), + + nbinsy: { + valType: 'integer', + min: 0, + dflt: 0, + + editType: 'calc', + + }, + ybins: makeBinAttrs('y', true), + autobinx: { + valType: 'boolean', + dflt: null, + + editType: 'calc', + + }, + autobiny: { + valType: 'boolean', + dflt: null, + + editType: 'calc', + + }, + + bingroup: { + valType: 'string', + + dflt: '', + editType: 'calc', + + }, + + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + + marker: barAttrs.marker, + + offsetgroup: barAttrs.offsetgroup, + alignmentgroup: barAttrs.alignmentgroup, + + selected: barAttrs.selected, + unselected: barAttrs.unselected, + + _deprecated: { + bardir: barAttrs._deprecated.bardir + } +}; + +},{"../../lib/extend":164,"../../plots/template_attributes":253,"../bar/attributes":268,"./bin_attributes":334,"./constants":338}],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 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; +}; + +},{}],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'; + +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' + }; +}; + +},{}],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'); + + +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}],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 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}],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 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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'] +}; + +},{}],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 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":258,"../bar/defaults":272}],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 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":169,"../../registry":258,"../bar/style_defaults":282,"./attributes":332}],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'; + +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; +}; + +},{}],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'; + +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":274}],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'; + +/** + * 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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; + } +}; + +},{}],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 histogramAttrs = _dereq_('../histogram/attributes'); +var makeBinAttrs = _dereq_('../histogram/bin_attributes'); +var heatmapAttrs = _dereq_('../heatmap/attributes'); +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +module.exports = extendFlat( + { + x: histogramAttrs.x, + y: histogramAttrs.y, + + z: { + valType: 'data_array', + editType: 'calc', + + }, + marker: { + color: { + valType: 'data_array', + editType: 'calc', + + }, + editType: 'calc' + }, + + histnorm: histogramAttrs.histnorm, + histfunc: histogramAttrs.histfunc, + nbinsx: histogramAttrs.nbinsx, + xbins: makeBinAttrs('x'), + nbinsy: histogramAttrs.nbinsy, + ybins: makeBinAttrs('y'), + autobinx: histogramAttrs.autobinx, + autobiny: histogramAttrs.autobiny, + + bingroup: extendFlat({}, histogramAttrs.bingroup, { + + }), + xbingroup: extendFlat({}, histogramAttrs.bingroup, { + + }), + ybingroup: extendFlat({}, histogramAttrs.bingroup, { + + }), + + xgap: heatmapAttrs.xgap, + ygap: heatmapAttrs.ygap, + zsmooth: heatmapAttrs.zsmooth, + zhoverformat: heatmapAttrs.zhoverformat, + hovertemplate: hovertemplateAttrs({}, {keys: 'z'}) + }, + 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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":333,"../histogram/bin_functions":335,"../histogram/bin_label_vals":336,"../histogram/calc":337,"../histogram/norm_functions":344}],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 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":169,"../heatmap/style_defaults":330,"./attributes":345,"./sample_defaults":350}],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 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":324}],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'; + +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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":258}],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 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: { + 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 +}, + colorScaleAttrs('', { + cLetter: 'z', + editTypeOverride: 'calc' + }) +); + +},{"../../components/colorscale/attributes":58,"../../lib/extend":164,"../contour/attributes":295,"../histogram2d/attributes":345}],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 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":302,"../contour/style_defaults":316,"../histogram2d/sample_defaults":350,"./attributes":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'; + +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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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 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({}, plotAttrs.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":356}],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 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":356,"fast-isnumeric":18}],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'; + +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: ['°', '%', '%', ''] + } + } +}; + +},{}],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'); +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":354,"./constants":356}],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 = 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; +}; + +},{}],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'; + +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":356}],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 = { + 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":354,"./calc":355,"./defaults":357,"./event_data":358,"./hover":359,"./plot":361,"./style":362}],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 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":356,"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'); + +module.exports = function style(gd) { + d3.select(gd).selectAll('.im image') + .style('opacity', function(d) { + return d.trace.opacity; + }); +}; + +},{"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 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_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; + +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +var textFontAttrs = fontAttrs({ + editType: 'plot', + arrayOk: true, + colorEditType: 'plot', + +}); + +module.exports = { + labels: { + valType: 'data_array', + editType: 'calc', + + }, + // equivalent of x0 and dx, if label is missing + label0: { + valType: 'number', + + dflt: 0, + editType: 'calc', + + }, + dlabel: { + valType: 'number', + + dflt: 1, + editType: 'calc', + + }, + + values: { + valType: 'data_array', + editType: 'calc', + + }, + + marker: { + colors: { + valType: 'data_array', // TODO 'color_array' ? + editType: 'calc', + + }, + + line: { + color: { + valType: 'color', + + dflt: colorAttrs.defaultLine, + arrayOk: true, + editType: 'style', + + }, + width: { + valType: 'number', + + min: 0, + dflt: 0, + arrayOk: true, + editType: 'style', + + }, + editType: 'calc' + }, + editType: 'calc' + }, + + text: { + valType: 'data_array', + editType: 'plot', + + }, + hovertext: { + valType: 'string', + + dflt: '', + arrayOk: true, + editType: 'style', + + }, + +// 'see eg:' +// 'https://www.e-education.psu.edu/natureofgeoinfo/sites/www.e-education.psu.edu.natureofgeoinfo/files/image/hisp_pies.gif', +// '(this example involves a map too - may someday be a whole trace type', +// 'of its own. but the point is the size of the whole pie is important.)' + scalegroup: { + valType: 'string', + + dflt: '', + editType: 'calc', + + }, + + // labels (legend is handled by plots.attributes.showlegend and layout.hiddenlabels) + textinfo: { + valType: 'flaglist', + + flags: ['label', 'text', 'value', 'percent'], + extras: ['none'], + editType: 'calc', + + }, + hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + flags: ['label', 'text', 'value', 'percent', 'name'] + }), + hovertemplate: hovertemplateAttrs({}, { + keys: ['label', 'color', 'value', 'percent', 'text'] + }), + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: ['label', 'color', 'value', 'percent', 'text'] + }), + textposition: { + valType: 'enumerated', + + values: ['inside', 'outside', 'auto', 'none'], + dflt: 'auto', + arrayOk: true, + editType: 'plot', + + }, + textfont: extendFlat({}, textFontAttrs, { + + }), + insidetextfont: extendFlat({}, textFontAttrs, { + + }), + outsidetextfont: extendFlat({}, textFontAttrs, { + + }), + automargin: { + valType: 'boolean', + dflt: false, + + editType: 'plot', + + }, + + title: { + text: { + valType: 'string', + dflt: '', + + editType: 'plot', + + }, + font: extendFlat({}, textFontAttrs, { + + }), + position: { + valType: 'enumerated', + values: [ + 'top left', 'top center', 'top right', + 'middle center', + 'bottom left', 'bottom center', 'bottom right' + ], + + editType: 'plot', + + }, + + editType: 'plot' + }, + + // position and shape + domain: domainAttrs({name: 'pie', trace: true, editType: 'calc'}), + + hole: { + valType: 'number', + + min: 0, + max: 1, + dflt: 0, + editType: 'calc', + + }, + + // ordering and direction + sort: { + valType: 'boolean', + + dflt: true, + editType: 'calc', + + }, + direction: { + /** + * there are two common conventions, both of which place the first + * (largest, if sorted) slice with its left edge at 12 o'clock but + * succeeding slices follow either cw or ccw from there. + * + * see http://visage.co/data-visualization-101-pie-charts/ + */ + valType: 'enumerated', + values: ['clockwise', 'counterclockwise'], + + dflt: 'counterclockwise', + editType: 'calc', + + }, + rotation: { + valType: 'number', + + min: -360, + max: 360, + dflt: 0, + editType: 'calc', + + }, + + pull: { + valType: 'number', + + min: 0, + max: 1, + dflt: 0, + arrayOk: true, + editType: 'calc', + + }, + + _deprecated: { + title: { + valType: 'string', + dflt: '', + + editType: 'calc', + + }, + titlefont: extendFlat({}, textFontAttrs, { + + }), + titleposition: { + valType: 'enumerated', + values: [ + 'top left', 'top center', 'top right', + 'middle center', + 'bottom left', 'bottom center', 'bottom right' + ], + + editType: 'calc', + + } + } +}; + +},{"../../components/color/attributes":50,"../../lib/extend":164,"../../plots/attributes":210,"../../plots/domain":238,"../../plots/font_attributes":239,"../../plots/template_attributes":253}],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 plots = _dereq_('../../plots/plots'); + +exports.name = 'pie'; + +exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { + plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback); +}; + +exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { + plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout); +}; + +},{"../../plots/plots":245}],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 isNumeric = _dereq_('fast-isnumeric'); +var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray; +var tinycolor = _dereq_('tinycolor2'); + +var Color = _dereq_('../../components/color'); + +var extendedColorWayList = {}; + +function calc(gd, trace) { + var cd = []; + + var fullLayout = gd._fullLayout; + var hiddenLabels = fullLayout.hiddenlabels || []; + + var labels = trace.labels; + var colors = trace.marker.colors || []; + var vals = trace.values; + var 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; + + 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":169,"fast-isnumeric":18,"tinycolor2":34}],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 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 textTemplate = coerce('texttemplate'); + var textInfo; + if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent'); + + coerce('hovertext'); + coerce('hovertemplate'); + + if(textTemplate || (textInfo && textInfo !== 'none')) { + var textposition = coerce('textposition'); + handleText(traceIn, traceOut, layout, coerce, textposition, { + moduleHasSelected: false, + moduleHasUnselected: false, + moduleHasConstrain: false, + moduleHasCliponaxis: false, + moduleHasTextangle: false, + moduleHasInsideanchor: false + }); + + var hasBoth = Array.isArray(textposition) || textposition === 'auto'; + var hasOutside = hasBoth || textposition === 'outside'; + if(hasOutside) { + coerce('automargin'); + } + } + + 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":272,"./attributes":363}],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 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}],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'); + +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 || v === '') return v; + } +}; + +exports.castOption = function castOption(item, indices) { + if(Array.isArray(item)) return exports.getFirstFilled(item, indices); + else if(item) return item; +}; + +},{"../../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'; + +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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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', + + } +}; + +},{}],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 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":370}],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 d3 = _dereq_('d3'); + +var Plots = _dereq_('../../plots/plots'); +var Fx = _dereq_('../../components/fx'); +var Color = _dereq_('../../components/color'); +var Drawing = _dereq_('../../components/drawing'); +var Lib = _dereq_('../../lib'); +var 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, gs); + + var plotGroups = Lib.makeTraceGroups(fullLayout._pielayer, cdModule, 'trace').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var trace = cd0.trace; + + setCoords(cd); + + // TODO: miter might look better but can sometimes cause problems + // maybe miter with a small-ish stroke-miterlimit? + plotGroup.attr('stroke-linejoin', 'round'); + + plotGroup.each(function() { + var slices = d3.select(this).selectAll('g.slice').data(cd); + + slices.enter().append('g') + .classed('slice', true); + slices.exit().remove(); + + var quadrants = [ + [[], []], // y<0: x<0, x>=0 + [[], []] // y>=0: x<0, x>=0 + ]; + var hasOutsideText = false; + + slices.each(function(pt) { + if(pt.hidden) { + d3.select(this).selectAll('path,g').remove(); + return; + } + + // to have consistent event data compared to other traces + pt.pointNumber = pt.i; + pt.curveNumber = trace.index; + + quadrants[pt.pxmid[1] < 0 ? 0 : 1][pt.pxmid[0] < 0 ? 0 : 1].push(pt); + + var cx = cd0.cx; + var cy = cd0.cy; + var sliceTop = d3.select(this); + var slicePath = sliceTop.selectAll('path.surface').data([pt]); + + slicePath.enter().append('path') + .classed('surface', true) + .style({'pointer-events': 'all'}); + + sliceTop.call(attachFxHandlers, gd, cd); + + if(trace.pull) { + var pull = +helpers.castOption(trace.pull, pt.pts) || 0; + if(pull > 0) { + cx += pull * pt.pxmid[0]; + cy += pull * pt.pxmid[1]; + } + } + + pt.cxFinal = cx; + pt.cyFinal = cy; + + function arc(start, finish, cw, scale) { + var dx = scale * (finish[0] - start[0]); + var dy = scale * (finish[1] - start[1]); + + return 'a' + + (scale * cd0.r) + ',' + (scale * cd0.r) + ' 0 ' + + pt.largeArc + (cw ? ' 1 ' : ' 0 ') + dx + ',' + dy; + } + + var hole = trace.hole; + if(pt.v === cd0.vTotal) { // 100% fails bcs arc start and end are identical + var outerCircle = 'M' + (cx + pt.px0[0]) + ',' + (cy + pt.px0[1]) + + arc(pt.px0, pt.pxmid, true, 1) + + arc(pt.pxmid, pt.px0, true, 1) + 'Z'; + if(hole) { + slicePath.attr('d', + 'M' + (cx + hole * pt.px0[0]) + ',' + (cy + hole * pt.px0[1]) + + arc(pt.px0, pt.pxmid, false, hole) + + arc(pt.pxmid, pt.px0, false, hole) + + 'Z' + outerCircle); + } else slicePath.attr('d', outerCircle); + } else { + var outerArc = arc(pt.px0, pt.px1, true, 1); + + if(hole) { + var rim = 1 - hole; + slicePath.attr('d', + 'M' + (cx + hole * pt.px1[0]) + ',' + (cy + hole * pt.px1[1]) + + arc(pt.px1, pt.px0, false, hole) + + 'l' + (rim * pt.px0[0]) + ',' + (rim * pt.px0[1]) + + outerArc + + 'Z'); + } else { + slicePath.attr('d', + 'M' + cx + ',' + cy + + 'l' + pt.px0[0] + ',' + pt.px0[1] + + outerArc + + 'Z'); + } + } + + // add text + formatSliceLabel(gd, pt, cd0); + var textPosition = helpers.castOption(trace.textposition, pt.pts); + var sliceTextGroup = sliceTop.selectAll('g.slicetext') + .data(pt.text && (textPosition !== 'none') ? [0] : []); + + sliceTextGroup.enter().append('g') + .classed('slicetext', true); + sliceTextGroup.exit().remove(); + + sliceTextGroup.each(function() { + var sliceText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) { + // prohibit tex interpretation until we can handle + // tex and regular text together + s.attr('data-notex', 1); + }); + + 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, gs); + } + + 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); + + if(hasOutsideText && trace.automargin) { + // TODO if we ever want to improve perf, + // we could reuse the textBB computed above together + // with the sliceText transform info + var traceBbox = Drawing.bBox(plotGroup.node()); + + var domain = trace.domain; + var vpw = gs.w * (domain.x[1] - domain.x[0]); + var vph = gs.h * (domain.y[1] - domain.y[0]); + var xgap = (0.5 * vpw - cd0.r) / gs.w; + var ygap = (0.5 * vph - cd0.r) / gs.h; + + Plots.autoMargin(gd, 'pie.' + trace.uid + '.automargin', { + xl: domain.x[0] - xgap, + xr: domain.x[1] + xgap, + yb: domain.y[0] - ygap, + yt: domain.y[1] + ygap, + l: Math.max(cd0.cx - cd0.r - traceBbox.left, 0), + r: Math.max(traceBbox.right - (cd0.cx + cd0.r), 0), + b: Math.max(traceBbox.bottom - (cd0.cy + cd0.r), 0), + t: Math.max(cd0.cy - cd0.r - traceBbox.top, 0), + pad: 5 + }); + } + }); + }); + + // This is for a bug in Chrome (as of 2015-07-22, and does not affect FF) + // if insidetextfont and outsidetextfont are different sizes, sometimes the size + // of an "em" gets taken from the wrong element at first so lines are + // spaced wrong. You just have to tell it to try again later and it gets fixed. + // I have no idea why we haven't seen this in other contexts. Also, sometimes + // it gets the initial draw correct but on redraw it gets confused. + setTimeout(function() { + plotGroups.selectAll('tspan').each(function() { + var s = d3.select(this); + if(s.attr('dy')) s.attr('dy', s.attr('dy')); + }); + }, 0); +} + +// TODO add support for transition +function plotTextLines(slices, trace) { + slices.each(function(pt) { + var sliceTop = d3.select(this); + + if(!pt.labelExtraX && !pt.labelExtraY) { + sliceTop.select('path.textline').remove(); + return; + } + + // first move the text to its new location + var sliceText = sliceTop.select('g.slicetext text'); + + 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); + } +} + +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, + prerenderTitles: prerenderTitles, + layoutAreas: layoutAreas, + 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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":374,"d3":16}],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 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":368}],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'); + + +// arrayOk attributes, merge them into calcdata array +module.exports = function arraysToCalcdata(cd, trace) { + // so each point knows which index it originally came from + for(var i = 0; i < cd.length; i++) cd[i].i = i; + + Lib.mergeArray(trace.text, cd, 'tx'); + Lib.mergeArray(trace.texttemplate, cd, 'txt'); + Lib.mergeArray(trace.hovertext, cd, 'htx'); + Lib.mergeArray(trace.customdata, cd, 'data'); + Lib.mergeArray(trace.textposition, cd, 'tp'); + if(trace.textfont) { + Lib.mergeArrayCastPositive(trace.textfont.size, cd, 'ts'); + Lib.mergeArray(trace.textfont.color, cd, 'tc'); + Lib.mergeArray(trace.textfont.family, cd, 'tf'); + } + + var marker = trace.marker; + if(marker) { + Lib.mergeArrayCastPositive(marker.size, cd, 'ms'); + Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo'); + Lib.mergeArray(marker.symbol, cd, 'mx'); + Lib.mergeArray(marker.color, cd, 'mc'); + + var markerLine = marker.line; + if(marker.line) { + Lib.mergeArray(markerLine.color, cd, 'mlc'); + Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw'); + } + + var markerGradient = marker.gradient; + if(markerGradient && markerGradient.type !== 'none') { + Lib.mergeArray(markerGradient.type, cd, 'mgt'); + Lib.mergeArray(markerGradient.color, cd, 'mgc'); + } + } +}; + +},{"../../lib":169}],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 texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; +var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var colorScaleAttrs = _dereq_('../../components/colorscale/attributes'); +var fontAttrs = _dereq_('../../plots/font_attributes'); +var dash = _dereq_('../../components/drawing/attributes').dash; + +var Drawing = _dereq_('../../components/drawing'); +var constants = _dereq_('./constants'); +var extendFlat = _dereq_('../../lib/extend').extendFlat; + +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', + + }, + + texttemplate: texttemplateAttrs({}, { + + }), + hovertext: { + valType: 'string', + + dflt: '', + arrayOk: true, + editType: 'style', + + }, + mode: { + valType: 'flaglist', + flags: ['lines', 'markers', 'text'], + extras: ['none'], + + editType: 'calc', + + }, + hoveron: { + valType: 'flaglist', + flags: ['points', 'fills'], + + editType: 'style', + + }, + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + line: { + color: { + valType: 'color', + + editType: 'style', + anim: true, + + }, + width: { + valType: 'number', + min: 0, + dflt: 2, + + editType: 'style', + anim: true, + + }, + shape: { + valType: 'enumerated', + values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'], + dflt: 'linear', + + editType: 'plot', + + }, + smoothing: { + valType: 'number', + min: 0, + max: 1.3, + dflt: 1, + + editType: 'plot', + + }, + dash: extendFlat({}, dash, {editType: 'style'}), + simplify: { + valType: 'boolean', + dflt: true, + + editType: 'plot', + + }, + editType: 'plot' + }, + + connectgaps: { + valType: 'boolean', + dflt: false, + + editType: 'calc', + + }, + cliponaxis: { + valType: 'boolean', + dflt: true, + + editType: 'plot', + + }, + + fill: { + valType: 'enumerated', + values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'], + + editType: 'calc', + + }, + fillcolor: { + valType: 'color', + + editType: 'style', + anim: true, + + }, + marker: extendFlat({ + symbol: { + valType: 'enumerated', + values: Drawing.symbolList, + dflt: 'circle', + arrayOk: true, + + editType: 'style', + + }, + opacity: { + valType: 'number', + min: 0, + max: 1, + arrayOk: true, + + editType: 'style', + anim: true, + + }, + size: { + valType: 'number', + min: 0, + dflt: 6, + arrayOk: true, + + editType: 'calc', + anim: true, + + }, + maxdisplayed: { + valType: 'number', + min: 0, + dflt: 0, + + editType: 'plot', + + }, + sizeref: { + valType: 'number', + dflt: 1, + + editType: 'calc', + + }, + sizemin: { + valType: 'number', + min: 0, + dflt: 0, + + editType: 'calc', + + }, + sizemode: { + valType: 'enumerated', + values: ['diameter', 'area'], + dflt: 'diameter', + + editType: 'calc', + + }, + + line: extendFlat({ + width: { + valType: 'number', + min: 0, + arrayOk: true, + + editType: 'style', + anim: true, + + }, + editType: 'calc' + }, + colorScaleAttrs('marker.line', {anim: true}) + ), + gradient: { + type: { + valType: 'enumerated', + values: ['radial', 'horizontal', 'vertical', 'none'], + arrayOk: true, + dflt: 'none', + + editType: 'calc', + + }, + color: { + valType: 'color', + arrayOk: true, + + editType: 'calc', + + }, + editType: 'calc' + }, + editType: 'calc' + }, + colorScaleAttrs('marker', {anim: true}) + ), + selected: { + marker: { + opacity: { + valType: 'number', + min: 0, + max: 1, + + editType: 'style', + + }, + color: { + valType: 'color', + + editType: 'style', + + }, + size: { + valType: 'number', + min: 0, + + editType: 'style', + + }, + editType: 'style' + }, + textfont: { + color: { + valType: 'color', + + editType: 'style', + + }, + editType: 'style' + }, + editType: 'style' + }, + unselected: { + marker: { + opacity: { + valType: 'number', + min: 0, + max: 1, + + editType: 'style', + + }, + color: { + valType: 'color', + + editType: 'style', + + }, + size: { + valType: 'number', + min: 0, + + editType: 'style', + + }, + editType: 'style' + }, + textfont: { + color: { + valType: 'color', + + editType: 'style', + + }, + editType: 'style' + }, + editType: 'style' + }, + + textposition: { + valType: 'enumerated', + values: [ + 'top left', 'top center', 'top right', + 'middle left', 'middle center', 'middle right', + 'bottom left', 'bottom center', 'bottom right' + ], + dflt: 'middle center', + arrayOk: true, + + editType: 'calc', + + }, + textfont: fontAttrs({ + editType: 'calc', + colorEditType: 'style', + arrayOk: true, + + }), + + r: { + valType: 'data_array', + editType: 'calc', + + }, + t: { + valType: 'data_array', + editType: 'calc', + + } +}; + +},{"../../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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":375,"./calc_selection":378,"./colorscale_calc":379,"./subtypes":399,"fast-isnumeric":18}],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 Lib = _dereq_('../../lib'); + +module.exports = function calcSelection(cd, trace) { + if(Lib.isArrayOrTypedArray(trace.selectedpoints)) { + Lib.tagSelected(cd, trace); + } +}; + +},{"../../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 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":399}],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'; + +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: [] +}; + +},{}],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 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":377}],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'; + + +// 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; + } + } + } + } +}; + +},{}],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 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)) { + coerce('texttemplate'); + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); + coerce('marker.maxdisplayed'); + dfltHoverOn.push('points'); + } + + // It's possible for this default to be changed by a later trace. + // We handle that case in some hacky code inside handleStackDefaults. + coerce('fill', stackGroupOpts ? stackGroupOpts.fillDflt : 'none'); + if(traceOut.fill !== 'none') { + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce); + } + + var lineColor = (traceOut.line || {}).color; + var markerColor = (traceOut.marker || {}).color; + + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + coerce('hoveron', dfltHoverOn.join('+') || 'points'); + if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults'); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'y'}); + errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'x', inherit: 'y'}); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +}; + +},{"../../lib":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":169}],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 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":399}],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 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":89,"../../lib":169,"../../registry":258,"./get_trace_color":385}],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 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":169}],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 numConstants = _dereq_('../../constants/numerical'); +var BADNUM = numConstants.BADNUM; +var LOG_CLIP = numConstants.LOG_CLIP; +var LOG_CLIP_PLUS = LOG_CLIP + 0.5; +var LOG_CLIP_MINUS = LOG_CLIP - 0.5; +var Lib = _dereq_('../../lib'); +var segmentsIntersect = Lib.segmentsIntersect; +var constrain = Lib.constrain; +var constants = _dereq_('./constants'); + + +module.exports = function linePoints(d, opts) { + var xa = opts.xaxis; + var ya = opts.yaxis; + var xLog = xa.type === 'log'; + var yLog = ya.type === 'log'; + var xLen = xa._length; + var yLen = ya._length; + var connectGaps = opts.connectGaps; + var baseTolerance = opts.baseTolerance; + var shape = opts.shape; + var linear = shape === 'linear'; + var fill = opts.fill && opts.fill !== 'none'; + var segments = []; + var minTolerance = constants.minTolerance; + var len = d.length; + var pts = new Array(len); + var pti = 0; + + var i; + + // pt variables are pixel coordinates [x,y] of one point + // these four are the outputs of clustering on a line + var clusterStartPt, clusterEndPt, clusterHighPt, clusterLowPt; + + // "this" is the next point we're considering adding to the cluster + var thisPt; + + // did we encounter the high point first, then a low point, or vice versa? + var clusterHighFirst; + + // the first two points in the cluster determine its unit vector + // so the second is always in the "High" direction + var clusterUnitVector; + + // the pixel delta from clusterStartPt + var thisVector; + + // val variables are (signed) pixel distances along the cluster vector + var clusterRefDist, clusterHighVal, clusterLowVal, thisVal; + + // deviation variables are (signed) pixel distances normal to the cluster vector + var clusterMinDeviation, clusterMaxDeviation, thisDeviation; + + // turn one calcdata point into pixel coordinates + function getPt(index) { + var di = d[index]; + if(!di) return false; + var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x); + var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y); + + // if non-positive log values, set them VERY far off-screen + // so the line looks essentially straight from the previous point. + if(x === BADNUM) { + if(xLog) x = xa.c2p(di.x, true); + if(x === BADNUM) return false; + // If BOTH were bad log values, make the line follow a constant + // exponent rather than a constant slope + if(yLog && y === BADNUM) { + x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) / + (ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS))); + } + x *= 1000; + } + if(y === BADNUM) { + if(yLog) y = ya.c2p(di.y, true); + if(y === BADNUM) return false; + y *= 1000; + } + return [x, y]; + } + + function crossesViewport(xFrac0, yFrac0, xFrac1, yFrac1) { + var dx = xFrac1 - xFrac0; + var dy = yFrac1 - yFrac0; + var dx0 = 0.5 - xFrac0; + var dy0 = 0.5 - yFrac0; + var norm2 = dx * dx + dy * dy; + var dot = dx * dx0 + dy * dy0; + if(dot > 0 && dot < norm2) { + var cross = dx0 * dy - dy0 * dx; + if(cross * cross < norm2) return true; + } + } + + var latestXFrac, latestYFrac; + // if we're off-screen, increase tolerance over baseTolerance + function getTolerance(pt, nextPt) { + var xFrac = pt[0] / xLen; + var yFrac = pt[1] / yLen; + var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1); + if(offScreenFraction && (latestXFrac !== undefined) && + crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac) + ) { + offScreenFraction = 0; + } + if(offScreenFraction && nextPt && + crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen) + ) { + offScreenFraction = 0; + } + + return (1 + constants.toleranceGrowth * offScreenFraction) * baseTolerance; + } + + function ptDist(pt1, pt2) { + var dx = pt1[0] - pt2[0]; + var dy = pt1[1] - pt2[1]; + return Math.sqrt(dx * dx + dy * dy); + } + + // last bit of filtering: clip paths that are VERY far off-screen + // so we don't get near the browser's hard limit (+/- 2^29 px in Chrome and FF) + + var maxScreensAway = constants.maxScreensAway; + + // find the intersections between the segment from pt1 to pt2 + // and the large rectangle maxScreensAway around the viewport + // if one of pt1 and pt2 is inside and the other outside, there + // will be only one intersection. + // if both are outside there will be 0 or 2 intersections + // (or 1 if it's right at a corner - we'll treat that like 0) + // returns an array of intersection pts + var xEdge0 = -xLen * maxScreensAway; + var xEdge1 = xLen * (1 + maxScreensAway); + var yEdge0 = -yLen * maxScreensAway; + var yEdge1 = yLen * (1 + maxScreensAway); + var edges = [ + [xEdge0, yEdge0, xEdge1, yEdge0], + [xEdge1, yEdge0, xEdge1, yEdge1], + [xEdge1, yEdge1, xEdge0, yEdge1], + [xEdge0, yEdge1, xEdge0, yEdge0] + ]; + var xEdge, yEdge, lastXEdge, lastYEdge, lastFarPt, edgePt; + + // for linear line shape, edge intersections should be linearly interpolated + // spline uses this too, which isn't precisely correct but is actually pretty + // good, because Catmull-Rom weights far-away points less in creating the curvature + function getLinearEdgeIntersections(pt1, pt2) { + var out = []; + var ptCount = 0; + for(var i = 0; i < 4; i++) { + var edge = edges[i]; + var ptInt = segmentsIntersect( + pt1[0], pt1[1], pt2[0], pt2[1], + edge[0], edge[1], edge[2], edge[3] + ); + if(ptInt && (!ptCount || + Math.abs(ptInt.x - out[0][0]) > 1 || + Math.abs(ptInt.y - out[0][1]) > 1 + )) { + ptInt = [ptInt.x, ptInt.y]; + // if we have 2 intersections, make sure the closest one to pt1 comes first + if(ptCount && ptDist(ptInt, pt1) < ptDist(out[0], pt1)) out.unshift(ptInt); + else out.push(ptInt); + ptCount++; + } + } + return out; + } + + function onlyConstrainedPoint(pt) { + if(pt[0] < xEdge0 || pt[0] > xEdge1 || pt[1] < yEdge0 || pt[1] > yEdge1) { + return [constrain(pt[0], xEdge0, xEdge1), constrain(pt[1], yEdge0, yEdge1)]; + } + } + + function sameEdge(pt1, pt2) { + if(pt1[0] === pt2[0] && (pt1[0] === xEdge0 || pt1[0] === xEdge1)) return true; + if(pt1[1] === pt2[1] && (pt1[1] === yEdge0 || pt1[1] === yEdge1)) return true; + } + + // for line shapes hv and vh, movement in the two dimensions is decoupled, + // so all we need to do is constrain each dimension independently + function getHVEdgeIntersections(pt1, pt2) { + var out = []; + var ptInt1 = onlyConstrainedPoint(pt1); + var ptInt2 = onlyConstrainedPoint(pt2); + if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out; + + if(ptInt1) out.push(ptInt1); + if(ptInt2) out.push(ptInt2); + return out; + } + + // hvh and vhv we sometimes have to move one of the intersection points + // out BEYOND the clipping rect, by a maximum of a factor of 2, so that + // the midpoint line is drawn in the right place + function getABAEdgeIntersections(dim, limit0, limit1) { + return function(pt1, pt2) { + var ptInt1 = onlyConstrainedPoint(pt1); + var ptInt2 = onlyConstrainedPoint(pt2); + + var out = []; + if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out; + + if(ptInt1) out.push(ptInt1); + if(ptInt2) out.push(ptInt2); + + var midShift = 2 * Lib.constrain((pt1[dim] + pt2[dim]) / 2, limit0, limit1) - + ((ptInt1 || pt1)[dim] + (ptInt2 || pt2)[dim]); + if(midShift) { + var ptToAlter; + if(ptInt1 && ptInt2) { + ptToAlter = (midShift > 0 === ptInt1[dim] > ptInt2[dim]) ? ptInt1 : ptInt2; + } else ptToAlter = ptInt1 || ptInt2; + + ptToAlter[dim] += midShift; + } + + return out; + }; + } + + var getEdgeIntersections; + if(shape === 'linear' || shape === 'spline') { + getEdgeIntersections = getLinearEdgeIntersections; + } else if(shape === 'hv' || shape === 'vh') { + getEdgeIntersections = getHVEdgeIntersections; + } else if(shape === 'hvh') getEdgeIntersections = getABAEdgeIntersections(0, xEdge0, xEdge1); + else if(shape === 'vhv') getEdgeIntersections = getABAEdgeIntersections(1, yEdge0, yEdge1); + + // a segment pt1->pt2 entirely outside the nearby region: + // find the corner it gets closest to touching + function getClosestCorner(pt1, pt2) { + var dx = pt2[0] - pt1[0]; + var m = (pt2[1] - pt1[1]) / dx; + var b = (pt1[1] * pt2[0] - pt2[1] * pt1[0]) / dx; + + if(b > 0) return [m > 0 ? xEdge0 : xEdge1, yEdge1]; + else return [m > 0 ? xEdge1 : xEdge0, yEdge0]; + } + + function updateEdge(pt) { + var x = pt[0]; + var y = pt[1]; + var xSame = x === pts[pti - 1][0]; + var ySame = y === pts[pti - 1][1]; + // duplicate point? + if(xSame && ySame) return; + if(pti > 1) { + // backtracking along an edge? + var xSame2 = x === pts[pti - 2][0]; + var ySame2 = y === pts[pti - 2][1]; + if(xSame && (x === xEdge0 || x === xEdge1) && xSame2) { + if(ySame2) pti--; // backtracking exactly - drop prev pt and don't add + else pts[pti - 1] = pt; // not exact: replace the prev pt + } else if(ySame && (y === yEdge0 || y === yEdge1) && ySame2) { + if(xSame2) pti--; + else pts[pti - 1] = pt; + } else pts[pti++] = pt; + } else pts[pti++] = pt; + } + + function updateEdgesForReentry(pt) { + // if we're outside the nearby region and going back in, + // we may need to loop around a corner point + if(pts[pti - 1][0] !== pt[0] && pts[pti - 1][1] !== pt[1]) { + updateEdge([lastXEdge, lastYEdge]); + } + updateEdge(pt); + lastFarPt = null; + lastXEdge = lastYEdge = 0; + } + + function addPt(pt) { + latestXFrac = pt[0] / xLen; + latestYFrac = pt[1] / yLen; + // Are we more than maxScreensAway off-screen any direction? + // if so, clip to this box, but in such a way that on-screen + // drawing is unchanged + xEdge = (pt[0] < xEdge0) ? xEdge0 : (pt[0] > xEdge1) ? xEdge1 : 0; + yEdge = (pt[1] < yEdge0) ? yEdge0 : (pt[1] > yEdge1) ? yEdge1 : 0; + if(xEdge || yEdge) { + if(!pti) { + // to get fills right - if first point is far, push it toward the + // screen in whichever direction(s) are far + + pts[pti++] = [xEdge || pt[0], yEdge || pt[1]]; + } else if(lastFarPt) { + // both this point and the last are outside the nearby region + // check if we're crossing the nearby region + var intersections = getEdgeIntersections(lastFarPt, pt); + if(intersections.length > 1) { + updateEdgesForReentry(intersections[0]); + pts[pti++] = intersections[1]; + } + } else { + // we're leaving the nearby region - add the point where we left it + + edgePt = getEdgeIntersections(pts[pti - 1], pt)[0]; + pts[pti++] = edgePt; + } + + var lastPt = pts[pti - 1]; + if(xEdge && yEdge && (lastPt[0] !== xEdge || lastPt[1] !== yEdge)) { + // we've gone out beyond a new corner: add the corner too + // so that the next point will take the right winding + if(lastFarPt) { + if(lastXEdge !== xEdge && lastYEdge !== yEdge) { + if(lastXEdge && lastYEdge) { + // we've gone around to an opposite corner - we + // need to add the correct extra corner + // in order to get the right winding + updateEdge(getClosestCorner(lastFarPt, pt)); + } else { + // we're coming from a far edge - the extra corner + // we need is determined uniquely by the sectors + updateEdge([lastXEdge || xEdge, lastYEdge || yEdge]); + } + } else if(lastXEdge && lastYEdge) { + updateEdge([lastXEdge, lastYEdge]); + } + } + updateEdge([xEdge, yEdge]); + } else if((lastXEdge - xEdge) && (lastYEdge - yEdge)) { + // we're coming from an edge or far corner to an edge - again the + // extra corner we need is uniquely determined by the sectors + updateEdge([xEdge || lastXEdge, yEdge || lastYEdge]); + } + lastFarPt = pt; + lastXEdge = xEdge; + lastYEdge = yEdge; + } else { + if(lastFarPt) { + // this point is in range but the previous wasn't: add its entry pt first + updateEdgesForReentry(getEdgeIntersections(lastFarPt, pt)[0]); + } + + pts[pti++] = pt; + } + } + + // loop over ALL points in this trace + for(i = 0; i < len; i++) { + clusterStartPt = getPt(i); + if(!clusterStartPt) continue; + + pti = 0; + lastFarPt = null; + addPt(clusterStartPt); + + // loop over one segment of the trace + for(i++; i < len; i++) { + clusterHighPt = getPt(i); + if(!clusterHighPt) { + if(connectGaps) continue; + else break; + } + + // can't decimate if nonlinear line shape + // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again + // but spline would be verrry awkward to decimate + if(!linear || !opts.simplify) { + addPt(clusterHighPt); + continue; + } + + var nextPt = getPt(i + 1); + + clusterRefDist = ptDist(clusterHighPt, clusterStartPt); + + // #3147 - always include the very first and last points for fills + if(!(fill && (pti === 0 || pti === len - 1)) && + clusterRefDist < getTolerance(clusterHighPt, nextPt) * minTolerance) continue; + + clusterUnitVector = [ + (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist, + (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist + ]; + + clusterLowPt = clusterStartPt; + clusterHighVal = clusterRefDist; + clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0; + clusterHighFirst = false; + clusterEndPt = clusterHighPt; + + // loop over one cluster of points that collapse onto one line + for(i++; i < d.length; i++) { + thisPt = nextPt; + nextPt = getPt(i + 1); + if(!thisPt) { + if(connectGaps) continue; + else break; + } + thisVector = [ + thisPt[0] - clusterStartPt[0], + thisPt[1] - clusterStartPt[1] + ]; + // cross product (or dot with normal to the cluster vector) + thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0]; + clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation); + clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation); + + if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt, nextPt)) break; + + clusterEndPt = thisPt; + thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1]; + + if(thisVal > clusterHighVal) { + clusterHighVal = thisVal; + clusterHighPt = thisPt; + clusterHighFirst = false; + } else if(thisVal < clusterLowVal) { + clusterLowVal = thisVal; + clusterLowPt = thisPt; + clusterHighFirst = true; + } + } + + // insert this cluster into pts + // we've already inserted the start pt, now check if we have high and low pts + if(clusterHighFirst) { + addPt(clusterHighPt); + if(clusterEndPt !== clusterLowPt) addPt(clusterLowPt); + } else { + if(clusterLowPt !== clusterStartPt) addPt(clusterLowPt); + if(clusterEndPt !== clusterHighPt) addPt(clusterHighPt); + } + // and finally insert the end pt + addPt(clusterEndPt); + + // have we reached the end of this segment? + if(i >= d.length || !thisPt) break; + + // otherwise we have an out-of-cluster point to insert as next clusterStartPt + addPt(thisPt); + clusterStartPt = thisPt; + } + + // to get fills right - repeat what we did at the start + if(lastFarPt) updateEdge([lastXEdge || lastFarPt[0], lastYEdge || lastFarPt[1]]); + + segments.push(pts.slice(0, pti)); + } + + return segments; +}; + +},{"../../constants/numerical":149,"../../lib":169,"./constants":380}],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'; + + +// common to 'scatter' and 'scatterternary' +module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) { + var shape = coerce('line.shape'); + if(shape === 'spline') coerce('line.smoothing'); +}; + +},{}],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 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; +}; + +},{}],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'); + + +// 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}],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'; + +module.exports = { + container: 'marker', + min: 'cmin', + max: 'cmax' +}; + +},{}],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 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":399}],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 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":72,"../../lib":169,"../../lib/polygon":181,"../../registry":258,"./line_points":389,"./link_traces":391,"./subtypes":399,"d3":16}],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 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":399}],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 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; + } +}; + +},{}],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 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":72,"../../registry":258,"d3":16}],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'); + +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}],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'); + +/* + * 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}],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 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":258}],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 hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs; +var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs; +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, { + + }), + texttemplate: texttemplateAttrs({editType: 'plot'}, { + keys: ['a', 'b', 'c', 'text'] + }), + hovertext: extendFlat({}, scatterAttrs.hovertext, { + + }), + line: { + color: scatterLineAttrs.color, + width: scatterLineAttrs.width, + dash: dash, + shape: extendFlat({}, scatterLineAttrs.shape, + {values: ['linear', 'spline']}), + smoothing: scatterLineAttrs.smoothing, + editType: 'calc' + }, + connectgaps: scatterAttrs.connectgaps, + cliponaxis: scatterAttrs.cliponaxis, + fill: extendFlat({}, scatterAttrs.fill, { + values: ['none', 'toself', 'tonext'], + dflt: 'none', + + }), + fillcolor: scatterAttrs.fillcolor, + marker: extendFlat({ + symbol: scatterMarkerAttrs.symbol, + opacity: scatterMarkerAttrs.opacity, + maxdisplayed: scatterMarkerAttrs.maxdisplayed, + size: scatterMarkerAttrs.size, + sizeref: scatterMarkerAttrs.sizeref, + sizemin: scatterMarkerAttrs.sizemin, + sizemode: scatterMarkerAttrs.sizemode, + line: extendFlat({ + width: scatterMarkerLineAttrs.width, + editType: 'calc' + }, + colorScaleAttrs('marker.line') + ), + gradient: scatterMarkerAttrs.gradient, + editType: 'calc' + }, + colorScaleAttrs('marker') + ), + + textfont: scatterAttrs.textfont, + textposition: scatterAttrs.textposition, + + selected: scatterAttrs.selected, + unselected: scatterAttrs.unselected, + + hoverinfo: extendFlat({}, plotAttrs.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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":375,"../scatter/calc":377,"../scatter/calc_selection":378,"../scatter/colorscale_calc":379,"fast-isnumeric":18}],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 constants = _dereq_('../scatter/constants'); +var subTypes = _dereq_('../scatter/subtypes'); +var handleMarkerDefaults = _dereq_('../scatter/marker_defaults'); +var handleLineDefaults = _dereq_('../scatter/line_defaults'); +var handleLineShapeDefaults = _dereq_('../scatter/line_shape_defaults'); +var handleTextDefaults = _dereq_('../scatter/text_defaults'); +var handleFillColorDefaults = _dereq_('../scatter/fillcolor_defaults'); + +var attributes = _dereq_('./attributes'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var a = coerce('a'); + var b = coerce('b'); + var c = coerce('c'); + var len; + + // allow any one array to be missing, len is the minimum length of those + // present. Note that after coerce data_array's are either Arrays (which + // are truthy even if empty) or undefined. As in scatter, an empty array + // is different from undefined, because it can signify that this data is + // not known yet but expected in the future + if(a) { + len = a.length; + if(b) { + len = Math.min(len, b.length); + if(c) len = Math.min(len, c.length); + } else if(c) len = Math.min(len, c.length); + else len = 0; + } else if(b && c) { + len = Math.min(b.length, c.length); + } + + if(!len) { + traceOut.visible = false; + return; + } + + traceOut._length = len; + + coerce('sum'); + + coerce('text'); + coerce('hovertext'); + if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + + var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; + coerce('mode', defaultMode); + + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); + handleLineShapeDefaults(traceIn, traceOut, coerce); + coerce('connectgaps'); + } + + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + + if(subTypes.hasText(traceOut)) { + coerce('texttemplate'); + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + var dfltHoverOn = []; + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('cliponaxis'); + coerce('marker.maxdisplayed'); + dfltHoverOn.push('points'); + } + + coerce('fill'); + if(traceOut.fill !== 'none') { + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce); + } + + if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') { + dfltHoverOn.push('fills'); + } + coerce('hoveron', dfltHoverOn.join('+') || 'points'); + + Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +}; + +},{"../../lib":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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; +}; + +},{}],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 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":386}],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'; + +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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":395}],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 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":283}],410:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":284,"./helpers":413}],411:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":285}],412:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":169,"../box/defaults":286,"./attributes":409}],413:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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}],414:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":288,"./helpers":413}],415:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* 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":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){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":290}],417:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":291,"./layout_attributes":416}],418:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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, + linearized: true + }); + return Drawing.smoothopen(segments[0], 1); + } + + Lib.makeTraceGroups(violinLayer, cdViolins, 'trace violins').each(function(cd) { + var plotGroup = d3.select(this); + var cd0 = cd[0]; + var t = cd0.t; + var trace = cd0.trace; + + if(trace.visible !== true || t.empty) { + plotGroup.remove(); + return; + } + + var bPos = t.bPos; + var bdPos = t.bdPos; + var valAxis = plotinfo[t.valLetter + 'axis']; + var posAxis = plotinfo[t.posLetter + 'axis']; + var hasBothSides = trace.side === 'both'; + var hasPositiveSide = hasBothSides || trace.side === 'positive'; + var hasNegativeSide = hasBothSides || trace.side === 'negative'; + + var violins = plotGroup.selectAll('path.violin').data(Lib.identity); + + violins.enter().append('path') + .style('vector-effect', 'non-scaling-stroke') + .attr('class', 'violin'); + + violins.exit().remove(); + + violins.each(function(d) { + var pathSel = d3.select(this); + var density = d.density; + var len = density.length; + var posCenter = posAxis.c2l(d.pos + bPos, true); + var posCenterPx = posAxis.l2p(posCenter); + + var scale; + if(trace.width) { + scale = t.maxKDE / bdPos; + } else { + var groupStats = fullLayout._violinScaleGroupStats[trace.scalegroup]; + scale = trace.scalemode === 'count' ? + (groupStats.maxKDE / bdPos) * (groupStats.maxCount / d.pts.length) : + groupStats.maxKDE / bdPos; + } + + var pathPos, pathNeg, path; + var i, k, pts, pt; + + if(hasPositiveSide) { + pts = new Array(len); + for(i = 0; i < len; i++) { + pt = pts[i] = {}; + pt[t.posLetter] = posCenter + (density[i].v / scale); + pt[t.valLetter] = valAxis.c2l(density[i].t, true); + } + pathPos = makePath(pts); + } + + if(hasNegativeSide) { + pts = new Array(len); + for(k = 0, i = len - 1; k < len; k++, i--) { + pt = pts[k] = {}; + pt[t.posLetter] = posCenter - (density[i].v / scale); + pt[t.valLetter] = valAxis.c2l(density[i].t, true); + } + pathNeg = makePath(pts); + } + + if(hasBothSides) { + path = pathPos + 'L' + pathNeg.substr(1) + 'Z'; + } else { + var startPt = [posCenterPx, valAxis.c2p(density[0].t)]; + var endPt = [posCenterPx, valAxis.c2p(density[len - 1].t)]; + + if(trace.orientation === 'h') { + startPt.reverse(); + endPt.reverse(); + } + + if(hasPositiveSide) { + path = 'M' + startPt + 'L' + pathPos.substr(1) + 'L' + endPt; + } else { + path = 'M' + endPt + 'L' + pathNeg.substr(1) + 'L' + startPt; + } + } + pathSel.attr('d', path); + + // save a few things used in getPositionOnKdePath, getKdeValue + // on hover and for meanline draw block below + d.posCenterPx = posCenterPx; + d.posDensityScale = scale * bdPos; + d.path = pathSel.node(); + d.pathLength = d.path.getTotalLength() / (hasBothSides ? 2 : 1); + }); + + var boxAttrs = trace.box; + var boxWidth = boxAttrs.width; + var boxLineWidth = (boxAttrs.line || {}).width; + var bdPosScaled; + var bPosPxOffset; + + if(hasBothSides) { + bdPosScaled = bdPos * boxWidth; + bPosPxOffset = 0; + } else if(hasPositiveSide) { + bdPosScaled = [0, bdPos * boxWidth / 2]; + bPosPxOffset = boxLineWidth * {x: 1, y: -1}[t.posLetter]; + } else { + bdPosScaled = [bdPos * boxWidth / 2, 0]; + bPosPxOffset = boxLineWidth * {x: -1, y: 1}[t.posLetter]; + } + + // inner box + boxPlot.plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, { + bPos: bPos, + bdPos: bdPosScaled, + bPosPxOffset: bPosPxOffset + }); + + // meanline insider box + boxPlot.plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, { + bPos: bPos, + bdPos: bdPosScaled, + bPosPxOffset: bPosPxOffset + }); + + var fn; + if(!trace.box.visible && trace.meanline.visible) { + fn = Lib.identity; + } + + // N.B. use different class name than boxPlot.plotBoxMean, + // to avoid selectAll conflict + var meanPaths = plotGroup.selectAll('path.meanline').data(fn || []); + meanPaths.enter().append('path') + .attr('class', 'meanline') + .style('fill', 'none') + .style('vector-effect', 'non-scaling-stroke'); + meanPaths.exit().remove(); + meanPaths.each(function(d) { + var v = valAxis.c2p(d.mean, true); + var p = helpers.getPositionOnKdePath(d, trace, v); + + d3.select(this).attr('d', + trace.orientation === 'h' ? + 'M' + v + ',' + p[0] + 'V' + p[1] : + 'M' + p[0] + ',' + v + 'H' + p[1] + ); + }); + + boxPlot.plotPoints(plotGroup, {x: xa, y: ya}, trace, t); + }); +}; + +},{"../../components/drawing":72,"../../lib":169,"../box/plot":292,"../scatter/line_points":389,"./helpers":413,"d3":16}],419:[function(_dereq_,module,exports){ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This 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":51,"../scatter/style":398,"d3":16}]},{},[11])(11) +}); diff --git a/static/babybuddy/js/graph.a7dcc3322745.js.gz b/static/babybuddy/js/graph.a7dcc3322745.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..27c21c23f74e282da75ae3526cfbdc128f3f776a GIT binary patch literal 660253 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

2oyYhzrKA8TJ-zRChx*iPQJXx` z(DCiCJW!f!Llz&7@Tej2re45u9_l3ib33n{gO0+youX$X3i!m5VTdTR*|F22QiFtk zZ>Esik49QCCwd*$)u}*{T6ma_d5-NDGzW#wqe8$K)9QmthfndLAkhsBsM zFdV5fc59(DVNStpJxwPCWX*NskabIe&*Qf7>1uub<5AOq<=7H0{p+#0w^wVUzSSwo zo%gwiBr|r7cB#c22jMrIYBBNzHYd=;!)$@McAcNp^fPhvG<02EGL_Tk^ketBJOHn5 zn(jai|XUGOKZ7e_O97Vu+~T+vi!Qi7UF`h_e|) z$;o^(uhVt2bdh@T>jH6cpnue}uVt$N{TF$^ruROZ37oe5p6V0iN6;VEqECOy5!oHy z@4i-};{5K*wfQvAR6WM)nme(D-8DDAf#Wr8Sl*3V+w3l#*sdoWym1rWwnM~r7xQtt z9FLop%_pt^y?Kd7G8@J-*hS=W*Ur!E<-9o~CXCw2hhq_lm(y`xIZDAl&!E*H2&K{M z_=&HmrpRC!L_T+TaltYwJASiOc68-AK9^0>l~s~S1nh%d7*r&Ds#%Gw$+SY9Jh!*S zL~tM|jb0}SUbyN!_goUD!6ckMHL5&Jk?M3;-F;u^Tqa70?kC?ap}rfX(BF#oHw0+! z(MA{P^jzOrYHliu}lQ&AaPFl+c({f20>(x~H z<=jkFisYu{e8NV+8gQ+*OiOa6!jh+=&h3r)pf6TuvwZ5^D)hst*|7B(P1a!~8)@Cv z+OXq86}XyD3_OqR7WW(al33DCZFW7MHzhIMQ6Q;x!!S<~v{h$p$7-=fY;D#kMuyOs z+!QC};SlzzIBoLHS~?u(n<`uu`4viM`64aGn@UzAL>~CDYkvOJT-wM{j`*|*wmQuN zb~;|DU)4o%rhiYUd-%C==ui@%Fnr1B6d;<1Xo}W)0xVZq(T55c^dM-@4?E5ILbkB< zo%RY}aQSfLMHe<_OD~$Re+<*KrS{TRR$R7h0!M6+B;Z!n3yD8O!_n{ z8>k9e^jaxXl?f{WI4f3p*782H^{vslsh7fD!nccZR(mHlUA5QmpCVHVz5Yu7&##q^ z-ks0cP>FgqB@U?7y9P6Gsq8Zo${t)406h?AwJD!EsCh0_O#(%riFg|RrP!8QlQiJBkX7{Kf1hO!;HG5Guo4&lk7 z5Iu3&HFcV}G5BJ?d&8JmA)<~v`c_nc*u7D=+oJcdU;kqNk80s&ZXIF~It}BOH1Bhw z{&5~JtH0{f#Gk&Kr?ZQP#bvxg2HVKKQ%rS;r}069&Dz;2Vxwj)zvA|3fjyWTy#1U6 zqWls@2NJq|H;cA9=&?{(7(2DY}o3<5u?sM4z|S>0)i=JJ7(k^7NMb98#?W|Jl_sN-T&DaPNKDO#xT`U9mUMEbE(Ck=^rO48}6kp7fMC% zq_h_@?h|RlQtJr^sGf8GTFO3Yn4gvNTucX5ukXgr$ z4vtaUcE*0lI4J1=x%-{X_sfGn>9X_;yTS&$R z7?N!h$}}*>h4>P3O3v9zxE+#u!b)xk#z-g1Jk!`(!inyrRh47OE1H!H{zHK9cU(@- z^>6%DpY>}zs)K|?6V`zCOypZ9FfI$R`kQsL`Qq>o>t@rGPkP5h(btI=_)4V*uL~(U zarjP|rA>jVmR4GoGHK>_36@)3o*(C{#h(|&xIs3f5^tc)c(X(qGo00AJmmuvoeW2L zq^{wTx40;W1(mY8ZR^6gp|UAtM)dT>qc^X=zyILri|?qS`QCRwNYRInUO#&F=)v*B zr_VPv;Tqi2cIVX6p&pk*tf0ssIW}~K$jk^xdW#BzA(;_E!SpR_AYK>m^T*Sz08rtL z=q%pAx#URojrfdf zpH}f@E4u?I9bWbE@b?rJ`e)_TH7{#d#NX3N0Y(JWsi3l2;=rx}l`!oj;z58uO%eb> z&A&&ge(3mV$=!6-QrvU;)=WS?Ni1N;G6Yu8<|Ce*EVca7ew9CN4nyjLYB8xq7eVZZ zy55twEj{#(5nj2yPvJZ7S5W?Q>T_$hYS}spVO1ZO-EGN`MVxl5Q7JIgmT^JiiCNQVx0iD5- z5H15%AAxh*EhGkCb<_i9W8LBGTn?nyYW^K4SKZ{og>qG73vBdP8q!71E`ul*f_p_o z80nM&Dn9x$k&*GJA=JLVX6k1cda`NThA$CERL7Jx81H9Da-EJxd@5kkJV8=Ow;}Apdky%}NkVopR8|tn7dR}r$ll4+u z5I9LkahAyGh@DD_yiIQ<(>WjT{8A$pU*%RJC~wKkyk`FEPAX_PAH!o6&Z_R=7b?J2 z+8-Ux+F5_)c&&qrto4Vr<1uOz?yWjFmD%StKT|_}OoJcDLWLb*HxaC#gq>_yzCv~q z*so*X@`7m?K>SB^tn5@uRcl6K07gK$zbM_jhBn_~*}9BE@N>w<2A^$Yg=Ra0Y(`73&s?qrZ?P;nqiw`llI)M!)G=OH9Sr&W%i)sFZfj#b5X)mK#MQ zD+`QhZyhaT8|)$`cmoK-bFoR~KAtNt6LxEdF;ms2G5 zcr1Dwt*x<35Xui-g?W&(3f*sO`L-ulbfyd2$UxGL5FwoGpZ58|H=!{oWOEq$+&6Fh zg6mqrb-bX^8_O{Iup$jVt#qo$)-av-;H=leoV5BwsMKj$V`jv>yxYeM{@HTwDBXcu z9g}Xcvk4h4(MKm4#1br*EfjoI*AoTQG@27Bzfw0To)#Bu@Gsb@{V-aJQ{t;+yMf;X zE*t6mkDIh2Wegm9X3&Qpje6i&W#-A~ucbp@WtG>DOxEf6yi8bR9 z{g+u*tR-U-ayh63n2WQU@|JfuX}8-Mkacp}QQ@xs{-GmFLqBjyC&vcYzB)h>jw`&! z_x_chn|;TbMYLz*%#`9c^w84lM+9+Nj^(=?`^k7@%4)gXT_dOuG@vMcW8?<`vQU{< zV+~i=K@@-6#>YM$hdbl(8n|Rc`MknoH15$yFdo5oq(+E*AN4T~qe}+JSJe!WD{R_D zayR+ZTjzQOtHHbAm634(QI*$oYGYJo%F?eGUT^zpsUFtExtaIMni`d9#V)q>2oY36ugD>ImzK`E+ z=No#il~Q28^jWdq4NTZnGFl&FJeqM0z8y?K2*q=_dQTZ8bF6 zvwYLpiKg{M|Jyz-iWZ$;ABc1O#)JQ!xJ!Mz4 zbp9@%%QGX?Gio<7eC_;;F4r+>EA&I8ItN$9XfgEHKRy5U<+F#69={IKlddPja*WKe zqPjP9$nRNykN91uN5L25zKwr*k>}YnmU2bw9DFSwmWzfjp!(=1*iNgq_C&whNBD=f z9Td@JIe+BM{yTTE5S)kk8NsksOzx~`TlJSvHjYxqH0eZu_NSctOg})UinzS*z>0e1 zET6VcJF$isw+6j=7h^Pd$o5BO z6&|>3&1x3h2_;{f1Y!k z01bkU39YExbp8+VbVqaHx53>?A&j_)X*yI#HHiJ01*J!2l59`>SFSTN3V{<3j1D#1HVGGC})phdk+P(Z0cwH*lzuI|KQA+ z|IO13Uc^tQXQi=1!8^`o4Y`OqF3;boqAiubS>D1=XnnjB6K5Z^*mhj3hM0_5CuG4K6giSyxrhPj*;zbx~lEnZ2|ZP7s& z2%&;&(7Eor$p~*dsIN1xv?fkraygymBN98)IR|)Q%Y6wQSnJP1?0|&8!7(ir!dq>g zv@{9eQN^nv45P=oXDvO;$PzAoIn%74ywGkk0z5fKa5;&MikZr@^W4ODvaL!nF({%^ zFq#*2r|yRoP}cCcAuMO^!1!?Y!Ap6cvf-6YU@kUR-Q?ut%*0LP@~1rKr!hPsB!$ek z3Qn`7bzmx(@0az1-(;6 zH{XMq%Rv|>+FTSFQc=|Xx&jmnhT`6Q;-oTQ#RDk$KN`I1xbHo&MjWV{3H0ODci*-b zqt2#E!Wht^hCnrQ#F&*KG0mlQlW5Ch9D3^ApmauDoftx9O z8wgjJ0u$QSLe-wA#gvVm`7pzKqzM>Gn>@o%J{7hq3j!QG52%H&$uP(DB8fUQdq{*Yy1i-gcP#`2>>}$C>l>=R3MK>!Dn^DB@cYVS6jd>vlgvEI>X z)u!Xol8w^DCX{%O_+;sm6XF$X3hWbLYGv*Rv~Xud?3`oW*Olv~+gW2jO-dQjP3S1g zRf;QL41Pxp0yp$q%OU#97Kcz@m1ACLwqxNKvlgDVLOo` zgklp&TtZL~VUI<>#kNEFIf;kt0fog!rC@6_r=vv)i=TTTj}xYI&;0B=T-F;I@w$Cz z_znz&4xi2f%BLA^O~#`Z1QOz@?gU}jBfWa@L6VR0YyW;t;*Nv;(NUbQUawO2@xsNh zjnr%o(=6a93B!rEDc8zkchASwmvgvah@*ltgUTYq@2!ZXD%kBa-g7+24Wy??$;HR! z4w%M$Zs!u~hOV#=V}14dp{+gy(_?0*xk;K7GgZ=0Gg+f;Cbf}uCO62ip_!i#Q^^O6 z^t?@dboo1js&5BwE0N;Ce&8od1Pcu;l);x(Z!0A=vuF(CIG*b(Wv(Z)0HQmXIl;GZ zbwgr3SUKPI%81h{5#5eFH{E<|BOx=Uz&a>2vS~7|PviZoISrvRgyKEb2^seCDo{$2^g&Z;f zho+)I^q8G_nL+(Lo8GW%akTvLxsP5TJrk45)&04pPdg#3{l;)pZaG4s_iT+HV%i*C z)ZstpqgpS$Qaj9aZMJ>3WF|Lc+F`F)X(!v`vdC)P3j5J1Wkmh-nf3( z#bSLWpCC)QH$0W{-0+gqQ>|;>#<;EVi1fs=qox_J+1~k}w7DsOnf)@tm)-MZ?Q~nS zzIV@$61l-Om~A*wTK_$`?uk&LidxGXu`?BU8!OT}i?fCo{$&s zz#`W|+2CxO&S7FPUt7;xB?36N&ZuZaqwUosDuX?EHk#baIfc%I?nH4f`JgAB2JD{- zX3oB&8rk;(5!Lrb)usHyv#Yt)H^*)Fpi0}*=aLIk$EI{LF7w3c{an;WZns&>vsw`2 zeN2LHo1=la)bm=^jOj=^F<%^x_7T!x6v~dYxaWIAkiZ9Zv!O}-HnjC#PUGe6dtGBQ zwTEzZ?dQ6%*Gxkt+o-7)^e?Dr`&2pyEK!s=ec`V@=z6K86bt(h1^2XisMIre8M>FU z*aztrT}I08ZIjqbNo?&xqGu0uxWMWvC|5xi+Y6N;#_sy3U|W$xwQ4q|{MQ-4-C^8g z+zq(#Ky@D5DjYD&0M1eXxqkCEV_^4!;IxiDVloPj4(^1H>hu$oSxTWA^|;$SjJzfz zHG89`NmGZ3YfRaQVNOOLJttiMqh_J0<|af}7SYz7T<~1g@$45_N|_p(Yk>R}T5VXN zRj@*vTU?006t6)KH0M#zs37QSI&69p|Wu?{Nj-&i$xgIPKUCo|u(v z0`+U5z`M{?n2!#d@32CwH(ljKZ>J`!PM6_#A|bZS5%av|R?E58bml?W`+5axk-g z`9LfR$A@WNVM!~?zpye)SBPy45>GwB(Q~GYkQ*~M=K}~^@%j6Jwk5akrQ?#NV*x*E z)RuT1P3IchLZ^YFTa+-%;z!m|zykd7yqp|^D@ph3`W*+5Yd~f_kB%Sxc>MD5QMFp&39?may02}Pg z60Sxj;#HazOFyrmGA(gj=yYTf$&CF1hHDOgzf{}0Zp1n%u)!7!w7mmItHSRc{YkvC z4A!Zi=vOeR7pypur!N|N0rS{sH;`>&Yd6@+*6W4jt@Apem1f16xlw_=PjyjVO?B*Y z4tvM}dL_y9MLOMc(ko2Mi6q37Z4TS4ftn|Ww+{NB)2ULGLhdER>YLaOchT|n`tP?;=Y%atofP0<~6VK z=h9&ln;1*IlD;gAt%iGH5V5dC)Syrur+p@+{j&G$WP8y-P$-h*tG_sl08lgl7 z(20JD*&F~Q&tEK3$)*5{Y~mTIQxnI!m`GO!;}b`dnQ6$k{<&xKD7)N^*(_FDxy`7V zyLxskTzK|-(EeWJV7*w;ch}mdE>LrkS#1tEoosA2iSJ7Wq%j*_K1df6;t9Q1`bB@ z%PVT&(TWqK^zS%<114p;xM0g4hnQwav?QOXKb1pM^hF&!=@fd~x!P$$>n*I~i%|Do?1J`F&5cqDpAI!CeY@ZnA!HsBw4;L+1VI8&J> zyiW(U){D~}I_>r4wg-L|e%8ZL^9zUpJ7R>5#%1NF==g1j2OaxsOM858rJ>oglp`es z*pAXzNEKXHQNzCa8&Je{vILBO0yX*&_gs^N+K?RZ;Se40p5eYfs=E!t+CZ}h4K%we zz4{QP+fj)1^(T*iZ8eas7Y)GRYMx)RY)3m1{!X!tEMnx#r_zI!=(wnkGk-3|5tI{3Pwj}CnZ zhYaD6AsjMM(2D)|Z}wbr|;LaJRmM_1AuV+ZXQZC3C48%=zA64MHb=3<-plz_KBYz)H4AkttYHl(PNt(Fv~mS zE#-&s(*b`{yhw#_xY((c22g0Fp#xqk7dtlN!=i#haSW8r$K0XCXZPBDyWA1cKh*km zp!#;;_wC81zU?|Ydn%#{$O7uc`>#B)!|SE);j?B^=r%Rx#?>N%^uthtIK6v)zU6>i zt*P~d%WzaU+f$FVxS~=`FXKu%ws(5RYv{aucCi&+9M#0<=*5xVht;SeVoEeLkhj^Y zduDakDbLP&lGYPBQmCY)ry=lAdzn<-Jur5wHYqNLO9EIrQwVp-*qmbl20*3VT> z@?7-*BcHd}29^ErDj1FzHa?FO@XR)j`IS4N?p!YP*U*Xf+x63|ZO^YQBus2|U}|&8 zezdhtbt6If9PM$C^rcT=*i>Rip9g)=9VD%ESe4`DB9AyY;Fg0*^bYpve^D0*zB1ZW zfc+b&$X87TB(uV%60P?=zBy@8p@RlT3nx?Ga9&?|B4k}M&)d+gU$_yCi_K z0?J9i&FNBvwEDOUpk-YZh;;5}{FZ1xsZRGo-G{9Hf6`U*_4zjz)A4>+b|7`@x0|IN~w5TYoZ^eqYGJT#95bP9O~h{gNW$@&UUxt=6If_YhHb- zSN#ATCLKsI6;3?BB=B(cIP?Gr*ju0U-rkdT2~)^5hwDwj9=Jf7WXlN&t7bzXsq=#K zprsNO0##K0JH&$*k^l=7@)YlAFmX0A0Z7?Q!cORzglEN^PS_l4LQN37Dk_4;eoRz2 zL7p?}fp1J3q&@uyh-#mI}@ruMzid zs`4_)1is7H(mxXkOJiE)*Q74=!?Z0bSQvtB&5*1~Wp~l>J9Hv>T7t%oSlIE_axO0pWV<&AS`JwryP~{3dDcT`yq#J z0Rhj;*}Tx#lLU$@5a}tZ%O}i*>DQ9$n>M%{zUxlhGM5)JNf#3C8Yp-|KY1#h^Snt? zOEuhCRaT#F1OrPkCn$O9*$#28U&lPh@vXtl9X^Mlx;a$z2&une{$jL_@K*57N}9j~ zF;97|^4xQ>Ot2;fVma5uMZAPRD%7&YBC1{+b_3yIeqpqCY6rf^6VrlKE$G^7YOvc;#S4#!Z^VeXJW zsncMzxZYH4-B``y@NUvn+uDkB>c;b*n6EKAKbQkKG1R-s&r8~Ss9~N6anW0M6~`KK zh}}X!m@C=X9N>k>@5o9VYV4O~F?!dJM%Cg`e*3Mn`T)z$QdcY$6eF(3 z+oCbAVngL`0=tjR1}D|k(Z*sD5;0!xL#*NTwuTpoe04WjZ*Es@c=3WCn%ovt?Ivr@ zke$fVV}^8PHWYWsx+=s+E5^p*V zbdpf$pms~gy(#D25FQgE_MBq6WD!f3)$)U8JrAt-uf%QMuR?e_xBwf+5#z&9a1zed zHc~2)r&#pEs>hIStF*|v$y>Vy&0zYloc|$}IHcIo*wR!TDjhIyNuEE}mEJ0;2siHl ze={p@CRLtKByJ5)#@U;UDf9l&zse zwDa!XRAIGGG{kf;r&?dkSN5%bv;#g5ie;usvj{1lLn+~{Qa3fU--pPb<3$?|`kqMv zwI7N^+Ygx)``ifANRgH@6q?0| ztcGSA0(@w08(amK9#LN%cJ=}lnfWefqCH;;XTI{T)zd@eM_ZQFai%Et8Cw!3DXVS` z`XrlN6Ig8{Zi=hD$ph|r2YK1l!qZMEX9r&DA~TxQ^n4EC_(T;Q6l~&776QvO!yYgp z{Lj}$-N%5e^vMB52te-~`;SR-ti3G4BjGo9gTr7n&R^|#X%ubIBc{LNv>Jpq2IzqN z!$u?DBS^!cvcwD;`SerY2P?t_heK;CG)iX+Oaq5^lH+7L(%Hb5!TWbhca^iy`E+F5e;m zY;QR|Q^ABYCt0#nla5Zzcs!d#+}%$5&d<@^qSHIi1AERaasiR#+Lh?x<8| zXUHSpBu|c?KT8(rd8NXfl`t!qDcu#Tc&H0bbO|BzJ4z-)KO%Yhbq+@gsL9@AH`Ve3=mGUi`Swf5Dm()0`w z+Z#f~<1nRT)}M2sUZV3MzCryz)4+6l!Dks+o*`qRRXT<_sgar#K!T)w(sMURr`E@t zLA1O$xo(Xx^F%{2$3|1VHbM~=fU_N`F{yCZ>gmW!SRrGG5B^@#g3WcihB?mV=Nmk< zXhPb5YeCx6rAYeYIN5qS>G^Lhge?1Dm2EUco7mdHrJ6@4ujdP$POQo?->?ce`LDla zr$~N$g=+zIST*O_d48UoyP3b_+HR5#Ban)52MUe!(?_CDQrd0kS&3L%Nz0KesAISK zH~%&?*93oSw|n>gmfieYAOChv{;NkFxf#5pSN)r}EVj>cl|w_Tw?P-cG5)e`yUREn zS0q;jpz59ouv72s@1RGHW%EpyQ$ zIl*9jx=`<3vao8bfAikoI!E_9 zzs2-aTPs$-;kLalme-9H*0zE_npMFjb;l0-XqzzT5z6AtsJ(fPgOP4tLjJXh z#k%9x(-;HCFI~52ib*sM=Zu)0?X+&Y+gfV(R@U6I-CK8J^-i0dPiu41j3n{rs8@)B zj!bLUg=NHtU{QbQ{{2S3e~bQ~&Pq@vFREMVxHz9;H@Pu5`0P%%_MPt~B;QwsB45^v zur=eGsWE4yVt4j+v2ih^=g+@#o^Mr`=WjsLZ5aji1N+1Tb7S!NZGLsO zx^vizF*xYgx|MCb^AjXED!nYW>#U-d+nQb7*BoiB!CrF|I8#^7>A}{#1Yq(ZPE1-4;@v#kGmT@{!*Z`UgtgKWh zmtt!;b;F#qm68|soR!?uQUaLk3r8Cg7dADl!SEuQOS^}rUCG4iHCs<-tE=Hk-Ybh2 ztgG4+`sgeAF!vIpVm?Cr7t2ego{{I7W4k|(**|dfIAc6M_l?F~jSw55Q)zI({rk>? z{h9330poz0@9B4Sp4wP*pK7{I{IylP{F7l-zbw;vIOK!hd&u_Zlb-$kgY)}|{n^;} ztVky%V)uBRKfF%oGq3YVyw|_IZ3vm5^x1ybK0fOOpPc8${*UHmRRz7jeVyKq!rt%q zKdbj%iXPTSdbn+*RLRWN$j-{hc$BCTY7$2=68*uc-E->v(5ruN-yg<+J*k&-x^=h_ z+p7T6pIryjt2(B?Om2y9U-ip;(H1AJI^sFGRBOx~w9?glRWDi9O7Pst7(Xw&;R!$r`Lp?YaxdNj0MQ~;(sS{ zEIzspo_=s})7xnWx5$zBX#sY={NGGE8_{!3>`e0zHD6r^H3#(_;Pc(Ev2ClZ5B0#V zk^?*H!yVU1RU7ER{=ddZ?TB1<@Pjkv-t|U$RU7HSztBh>>B$}4fg`@`I?`!k= zoYsiTaDbIVuThNBx!I}Qv$Y+V3hVn@cKvb`_hSHx!O7FZGR6B97YTI@Y>+!)sQy*eI{<0DFoftO0JiP8+^Th5o zvQlr`0=F9pldx+_ede;&JI;dk%QSLZzd`f*Fzl$|7!gRj?{o8X5|aOQPE+fM%=P(h ztXU>#cgf|8&$qH7v-@R&GN!HT45aH^P8YVH-pLeqz)Mp+Vl6E#9XfSeQ6X#Bxr}Mj4)^~(o7-kHCKtzMq_@kY*5(m_d zzxv%LXTRmn%l^)@5!SorEO7M=6QFS4TcqQ0Z31rp8XiZ|#E!?p=u8C{Noy-D`gfyi zM}FI;!_|YmDzZ8w`0UpVwvpJ@JE7%w80HA7F6F> z`+wb}in!OXe!lwGUq2sj+Vrb&R$Bt%(cOD=*u(XYi4~x zv7mZr+4ozci{ow<)w>W~cYY}D<`YP37Q6; z(Hm9yxHQ{;-uS_@bHJO{@5r#%QMLZXr5xv?S z(W`4BI(A`b(H!0x@)ys@t_I0TYnIb!IPpqLd#~d+^yB!8*nzFnkM35ro=kF}=64!42PKZfM$g~ycc<<( zpiYYEvii+IGveb=#dF)KM?LU-n6tC-!uJUIn8r7Z>-X;g2O*f}XYuwCSK)kz4s2;( zVpGPN-7xmwt!+d|ezWHHzh)624ND#F`(F(=*5PqEkAvh-I{|E%{)=Mt4p}OH75yE4 zdaR>(Uk!cx6BM`!zP|JAcHD znnUzfFC;{X8;-*dUc#X{Q1+Dy!{Qe81GJzhO5T^S%0Vg6N_A_L&KG%Aq|;j|mC`ky z6~lUkaWOQFBfiUnaxz;k@<+%<;FZ2Lk}q#KQ32Pf2_Yu@Du}Udg9j7I#E>;hKrd(z_G{ka`H4Iv~;CU{S{IK#dJj*Ypo-- zFt(S|{62kYn{lcNUFrq(b=A67oi*yLTalyyUYy;{K+w}JEy8l{kwQ6RYT-gBg2XpC zGjKtUji`f#%kud_&q>loRWnC4H79)fNs;)mH7?Ivr+rR(`0}icw)>D+JIN%LE1phOf$tan!cUUG z37-&bNg`oO=y;V=D0y3O{xrmEvkQ>}_PHy7!4s~)B+m$~G-=Xeq$v{ONAHNCrt>@{ z(8Pd|0+2ygZrS6P4_^)>xydlqaejs-7h#r+mh-Bd7vr^r(RSf>T|kI9jLcPwi;`38 zjo1C;DKhWjWsZpID$5*)Q%|md1Vr|^N+sc6Rt{WnuO-W0v5;&j!`viEL3I5Tdqt^& zv@@MjLkM}50CV>ufH*T=rqZ}nk``*1kIO6I{g-8$(dlS8&%LVmY183;N?`dZg0zo| z_o)QtQ;8O*$!t#3Sj@%}!&x;@UC`TiF0}L<3FD~N)I>B|&Jvv?qnsuAH1nFLb&I8l zWqhSl9-=tS?y$(5DYNr}DCW!Qq8KMvxeF=FM+H$CCk#ajmT4xPrLFLnB{I%R40aPm zO%i!&a+A>WI$upd@|cwe;0uQEPlQbbu>j2`D+_;gIBuJ&olYZoh0VL(61O$GSO9)* zE8CFNG*bt05rFtKF+B-Ej8)t_ zbOza=V#!|&_e)n#fqlm7n!=6A}cRr<7X_}K4<+P`e z4`}MWO6Q6JnGIG}C*+5DdBoNK?UX z(3na(4NH~N?D$2)v)8rP@@7J>jYik{&6o2>c2&0;rLOo~XlUKt>geLN(p%|FvVw^y zp=RFMj_roj*2$;jD4wrI`Ajz2PD6%q-T^EqJi5iqp3QXP6y)b$vHSDB*SO8O6fH}` z28OqLmcG}Eq|`8?W_-R?DRz@dhk72V%QDPc2qx^FNsgS1lwKFied4A*Y->GS{LRhr zmF2G+E_($91MkEd|6o+-U;JBj{#&ZfHz~q7|3W^Qsl+l7_4=E1R>+TqDrdT2#d2SD zZr^j!W^q$A%TK3DgZgqwG7RkpDV-Z0;95+!7f+Wu{P_j)a@23P9FzU6E9K50Bp zc@R|(kD?_!V}Qslicx~rH&ZO7E&H1xAYf`k@4a$fS3bCx8?!Z%(_4?j+>M}#==)0bVnb3Xj5@oq-QE~=2 z3$@{YR%z?4WNksv`DhSq1JLO-BbvAmb5y3r^@wY7rxbI(zI5Gvb0x&SW0DET^-V-@OoQeP+69b zbCSYTbA8oJ-!V#RD6`(Z%vzOE1AG7bkIXIn{#s~wAHl;q9OL^9gNzq?e~m&KrA-E? z^4eH(O=jhk4L|uKlEn;^oQj)8ZLU;}F7iq0nk7jdq0mrCr4<_Jne=6gVcPVC*4d(z zaDCD53hwP};4Y=M#z0paS0$|}-&wah(fa4(uzpkZdT1BH!*VtouOA9mr_npBP_g<7 zP48*z4X*^Zp*j|x<_8<>coaOa39lGiopp4iPc{H_zN*yRIws8}HodcQV1N2%BIY$; zt#VR2VdZ zbOm0|SG)Cm7504P_k7jd^HoF7SCO8te4v%DXxO%&+x-KH!#^5t6n9^<0NA*0|f?+D!+2hvurY_D~gGnJ)o|=k8Anp9{%r7iwWD$ z=p?SDP1tDoZ~EDCE@i)QQ$SPCgT$6uB(o)(6lRVop>w~&YaVo@uYuSChwHQ@sai2& zQzeC8`u}M=lI(cuAOzE%FmohzOS0C% zpd%|PI^e8{SA64K-i4V;feP1b@spMFqLXBwzgQn8$v@mn~@++NDXm0LddUKfjtjfy9;>tiw5t7xgK}?a?LS7DC znC!zI^&N#Rhl|_Q2%+2kRgH_plt)~2eQ}Ml@w<2)3bML!zgW)`43_168WJ>q?9XXB zN&@2g$|T-xxKvdgH4(krhsjM#*GnpkT+=LpU8anVb$C297P-EvQ735b#l?%13heY8Wo3nd0w(` z*vrq(=m#GU%V`hUaA`^QB+=<6D262|!ZZx2W@cfO&70U$ZCLW7x&4h?>sN9xuX;`* zR9KbX5td?utxu$yXvy515uPwVK~;q#RW!1{7~y9#lUOw|iPbhHvEF^*udcx)R?SRe zg=HWea}+?yJrUdnEa!E0V<4tu8(D`LDzTZBu$fDf2?-rAFGgmnD0SGcp@*?$d(DTn5NLa zv#vl{`t*i!#ZMDlS#4TeByx=c_>SMs&x#gv+8-Ux+8GHWR(ZJ|P(}4#9Y6Ki3?0-C zF^(ND`on%OFlA)d75d9A4dZf@swCn<{263~Pnt~HsEhc1*Xe+ZQ~q3epFLALoK6K# zXq=D0<=GiiKOM3Qbmu(br>=W+o~6|yfop(w)*g{)%dptA8Uuqjivix?XChs!aaX*C zpkl7j!;j&xYU}PpaK)X0uWm%-3W&5b93pa6d7nd@#9&OTrZN54Jf^UP&0|tWb*dP6 zLo}$JZVYaJCHhFEz`8N`;;^e?fHd3|e!2f|{h^~j4 zNxv7$gpTOd(ZfWIjlEu7qt~mC>-Ea*wYznx<;tIK4DK9sZ7`lM5B{iE^k?OCp|6gk zF8L_%#=lEvl{Ic`o~6TlT(5m=oUVySdbRpoVT=j_z+N3kO$2VqcdBR$Hs9V_;O^Dy z@LWaf2(GDvzV}tRZm@ZL)sudzJR7u(U8CH=38F_P^RTqKJ{ z72$h4Hx2`jh@lY~<>Rd4SPHX3T@QMZ6Vl4gSFy+^@D>B{4~ddWj{ zxJ^2yk1mXVs&XY{e%W`0kjCI9)T`dOfDWP2dmdhEk8#!72;?w(p3c~Y5J#^(5ql@p zyN~`j@C25+_A@izCCn0-i-H81W|Hf~t_~T7wmCb@}o?CpBbIi8EBUNp#ucSkxu(GtBbWYcW2`47Zv}t@VtXD zFZmMze#-Eif;*Ykcy_SZ3~&EYH^be1cZ7p_VluzdqVeV}kw){5EmZQ$MHjdJkXqaw zGM!G#h3K&!!g<$WZRh#ofi`G-o-Z!Stle^oLjhY(AE1R+hugpg%6($@KdV|-${>GHmkuTtW0#1XP0 zqgrAyQY?}y-ddKjna~fk%9e?JP5%mPv(^bsu~7@{K4;_f97ZXuB_twYh!q;50nYQ@ zd@1yyHa}K-irozmP@FI4IcqF$vQ^4Ww+#tSKtwdmVa>$zN8L>RQV=?p3ns9xyvBE? zwn@%0b(Yh4J}%NBQG#!BSDSxNS@j{1bMi^C;7NrOA*K{B+8mQ_yne$iw-9E810N0i zIqz~@B05!2Yjk>cRC-=9TnAAJ^{2l`=R|5QvZ+EymsB=*MMwO+rj(DBX`3N+YJR-V zXnir}>S9gA2RC%Y6-w#%5*JxKJ`ND1XXD;t!J(us%6ah~9w%ebjAL3C z#aM}z+$?8FW=KEXfAQV3M}y?^{jT9+<`zI%KqcB!Ng9%t7j#fGHD<*sA4@$NnG_(Y zw7e3r_sPp29=*E%>eb7?yngyuG}iShLV2HY_>h!?R10iV@%Yh?$KT(7I7n`H{lP<0 zvBe(0d~qz`4^%<(nhw49=LJ08t8`lRXts;9mL2b;m==@egpf4JllG*_M;+n-WCrCJh+) zEf!;hDH~nnww0$e!Eb ztW@@><>&LfH23dROtd$Iz4HPAB*!FJ0BZ_rb#g+KN39ppEx1uN zxPy)pIohfPgj%I5C)S=k1;LTTPTnoHjunfj!x&tXF3hd-v()*S>a4vwKiBK{CyL6% zXXVCDic^^I@z0-6O{CV%5r*nl(Dh(KANK0Mv^Els^hc5V=vo)+1giB-s-uoA$CL=D z$frb3diIsTmCj}?w15>5&$~o8QjFJ7(3#xb7^1a?QO}<>&!@YF~DC z={-&7!vdn}D1WSn;4_}^bDQ=69HUqC-x`0{&cekv<*Pp~>EH`jeeH~};>&E^unhy- z7Er3YshCwt^15Z{6F9H>o!aJ;n<-VbL#fde1|8+954hH^`w?#8G z7fc-Cc{PN1*IV6TY$_<#A$o~YT*Eb3T$BXmi_u}WS(NoyFN|D(S}0KlsnBGZDjD? zk~g3fgXSPvvnpR^Wv@Q(&XJ2vDJw*NM*Qh&fXLR`77U692`p(qU>fLHNVC~~3zggt z>G&#(~TE9BLMcS&6=> zFoHUPn!VpkZ%mnbco2l8`PELRs+3ry5|!M&{BayfbRbehqbs%imZ$|2U}a#o$9>_jvLGmt?9*dk4DgRtE-;6 zk6Xi_#+%pk7evroiDX)O2e_JY11nM+}M4SNcQm zk7@}6TB~STe~Mb`5awFSt>ZA)(Xzp`Q!A-zl=TbJaC#IJTB|~9TWDP?^qzHF*t~+< zHPn1BwM-drvNX34KM_9pFvUu+PM!ue1wQ(try*=S_Zd*7GWBU;M>xKtC-tLu6OR^k ziF;~j;*?qtOVq-ZJwd$M&mSw5u|2ZghRYt~Qia;z7cMIQ8j=5)ZX7NM218Ttto zKiwSU*hVXcvRT6A;7%}~FVW>n&lL8ZZZVJINXC&>5%x2(EW`erHZ}$c z{_U2QCCkSpZ}O7k#V)ORuB7`4M|r42#hgXtymU@S#H0-~Gm`!214o|Vx4hao^1qR| zMAp)xkQD>}$Je*Fu+=j6-ubJ&B6#=3;_b}CX4TgV{1MLj90gquSLr<6m@o3RIKLYRY9NxGP}ddY2S??|mi?YA^~^7H97ro=k%CN+nr zBGIf!K*t%8VTXkH=jqG{>P1!|6{*hDYy9>G$e<>_^(VF)zwn zn=b$9Pf6{qds-mDzMk|+4I@(2dpN0O6 z?Vhf>Q@F5=?t_f&d3ye6oKK)XsX;o=<;%zCFWu22f=xH_@hlJBsnkmi&e|&lX-?H^ zef9mZHdL@g6dS7Uo`mC?@^t{jh9OL#&Z>N2bhF6cA|B1_MC=o0+)FXN;?UZvUwwIX zq&~lSb^qbh`!C>|%L>QupOSo|YTZf>zTB5l&^m_$JTXcLMDy}z(Z5~fSjPhD@%;zK zFJBRy1F9&>%VOHLEhv0Y_o0;%iD^clX7hqCG@t;SD|16!W$*xb?8ryRLm!rl3;M*y z0;)hIjlX{K^zreJdJvUt04NOL}@k_6MZb_io7x^T(+`E zIPW44uAIsX`rqGIvsHIJTb*?I6E5^lKO{Y6h-Wr_IRYRB9?9p^@&@aW~na zNCKLqt!$u@8>{sVo=mLniVvEE-1BkY%%zPR2$~(3_E$+8$sEOWUFLx4hA)}aZS_iz zG$}ysv~MgY(~cd3E@vUB$zO7-EWkmQCczCQXx);NF|=lyv)v)3nlI#1aSb>2mdaWN^_`o5UcQqF%_=F`!-zo$&})6rs? z!uAB>mok#iU_wq?6mjlLux=wni&1*toL{xKg&)W_VnIJG*=&!694!E!XX>Cv*#C}( zaPhFXY}@d0_@b>^Z8S#T>yV9NInIyAYM(yrx+yr^Ps-}ny?3xlfYkW5_5Vo8-yQe* z7$TBo8G;zN^Mrf;d`033a8H7pO=qEe6^MKRUqU3KS|u5U**1d?c$H4& z)H6FLv^lH&Zn8S)CTsf3Zx1>)=M%3)=JMupm$&WtyDqGdBv~T8VPj0wA=?hCp9!}5 z`(FLU#wo3FC~wmoJoQ+4Rce`?#U%qzoVmT;!2#x647o+R=KR&VX5UmLSv4Kshw~RF z|KK6oPS z#@h7zE2WLiF0Wz4P!J0d;C7CnGt3aVrfCP3>5tikF4~On=PoybqN+t4sbn_X7K2p) zdv>p#*N}^1tr(O`8{$E@1u`Te4|+4MTu2UH@tv)Tn`wP`{$K~H(N;N}{JJ9IWHziq z11a;)%78$Cta6qThZHK-a|mkIo7*|;J+%dgOz_GfWVKXmx6^W(E-KZvr6`USX|pF+=@2TRWS$n& zS~pli&Z+$gfyA!Gf(0lS21mt0a-S;;6B-$>Ei-A@|9QrYVCYX>IvvrT$ybjy=ep>V zRd!h0A7+$1X(?}$Qdiuf73)HTQUm7Lq|dbVA#RMv5TlXQ8mfjFIt@cCUg>kW(pSf- zzNeub(^mK-+~v&6w&BWEJ@pm~2)eE{N`>~-*AiJEo^r^m@7S%`kor8fyUB@tI;|P! zi{zl}25iCpB%Rr~Jm!7GM<7hB!*MrE1(Qlgj{kkNpq~-l_<=4~`);ijTkJT`k`2_a zap2WB*j{7pwXqJ`SjTF3ZLANr*O0x>>zV9+KHOTVb)?k$TB-MuQrK19GQ7)rB_wf% zEy4WCzrQluq+8Cu?fM#Rk`2w;MSAiy8N!AKhpELH@dN$L8kTXp(4E)aKwqvmHn+Ha zTQ806DJ+Y3^^&z-n3r{wJyWq!e8kDy`V`c#XI|Ek2QPJDn5+sBlTKdB(A*JooXytt zu;CtX-kT7?s%|aMj5DN5!xbbTMyuuD69hzzY$|U=!9tLNuj`FpZS#AzwzV6NI}+~$ zM#QWvh>@d>Mcj(kSS?=31N=`U}3&6?4-r>ck0_ZKehiF z7TZ23|9dAHI1M|!!D;8^%<$ zyRP|$X_p7cMy2_et&xW+b9v3SZ5iXD_!DvKH*p;%x;rulM zoU_{cj-9PBiQC`Osj{5>WNpU9x1U7|^Uij2&xTMVp}=mN)WxAqEs$czA;FaX9DT3> z)WQ&W-K2=!TQ15O`jZKetWU@9TghSF457^hd%+Mp$=B|{V%LmWU#XPuCT$l7J#HGo zb$yW00Hzzk?uU1i-lLCq9>u3J&QA@H`fv-r@s)(7R&3;bxnpO{*0tW!*C&xh$1h+OSmGcb8pYR^`ZY_gwlPI1l|Pt>?B40PARH3dCu^xgk}4V{9!k z*b5XhJ1Sl{3#5*1Q5n~|)@t3d>r0)HSWz|ONWiSGa5Ndl2T51qAXwSIBFRBU>9&-8JSoT#6t0fTIeN@c`g<9layNAk|`d(#D3Um=NP zDyKy!kIVyo0~LUzPwkeuo@zP#N;?n2(Mxav9jhKZ)O2v6RI#mL_n`U_npGG)Iu3;< z6NCB~NcXd%;m&zhAR9`Z*j36C4c<715%V9Vw0nhJsJjG@^Ql`=SwJ{Re8l+1HMF2I z9XFHDwh}7yO@*KBc<Aa#;l8M9k3}FAFbbasE@DG`Lx~o^K_n%%JV6m7tD$yv5B@FbTKffaV?-~fr*Xq zBCdEGpTs^xxU{UmythVIwkWcY1hmodARD31);?*$Y`=wcE#~W~@kO=wZM07h@7S;qom{=c5bT08bdyZTz0L(|+>2tQO<7QE2{*JG_*W zGgDVI9B00}ID{_XO~XoU8w-k+dwQclml0lRsv&R)s59g9sje(52})5w2v`_xTB^O5 zb!EZ{cKZ?AbMh6bv(K9$24g>2{bPN4nM%Mv89tlGE6Z3<8^}jO6LAIBlQfj6cFa^o zmERijr)f8$+Jsv0dl&WbY(R1D>GJ@3L4TLsh>rc{bKFei0`}^ zwBv8vm0L?p)A#fIvM86;Gqpn+iU1*gucAg>gXUn9Bb6)d7;4ssfb-9hP}K4JK{BOf zU^{*pdQljmhiXAcTSUc{msJL9+Gcm{%3A0ugU9KsbuX1&Swbu_7G`7=n z;R<0dp@r409IHs1t`NY-L=!)ZZ6bu;m7;{C(&@mE6AUbj)k7k-)bpMpd^U5ATM2^W z1<}vE3*O9=n`34OwR1i807*>mhh$I$siYe9@HOv5#uiuJ){RX$RH?%vPN>$2Dt+2_ zOd9xjbz1Z79c8g|Wi-TTB*KI9-Nx@^WBOM0u9&r&qB>Z{(TDmTy*P^Jw$Z*yL=)zy z#QyRZjldd!k&sxBJL-_~jJ1EU6$ z1Bpf1$Mv^IEs0@!r)m6{DZJkFHBXegdU;Qptp@g@yh_@h8B|B_Ha^>Mft1sx?MSxa zd7gTq2eM39Lq7lUA3wnckL}j^s!Lf~%0?i}oetyjYnskTAQW@Wq$(CmzaxrQ{+amE z13G)=nZ8l_#0C=#aw*JKv}4UywbX?HjBrBO18*iic;JWtq=e+vY~@ddWW(X_`Yb?H z`9`@+MipPJwF`S~V6UUF<@>q;_QsO~>+k34ayKXR=Z^5f($g=?Vj*^1N*u0a+X=x~ zZ)z=O5fvZ|oXn*d7Z4eY6%~n{FD;}pgfvN~&;v^;pSaZSAFev$xDoHE!sCFTvqo*pQN$l(~EV*X|4^?noidchHAOqW-A@D06)KpEdlzrw#C+5 ztWM3%^RwFlZ6hB9YBfW`I-Mazf!ozk=jv2|ig(Qw){zRnkME`O#}%HTg>c+fs|_cJ zc*G0^W}kt#ZmcSz{-`c@F@jF$4D2<&-mUReIX93D{=|ggY^XMR**-G0FbG)V; zUU+gG;J)cb{nPhS^A55G@s*ith6}6O^ml;vF--(!(nh%Rm4;H>d1#{y^OW4XrCrZK@Ah3vznuR+h=qk zt}0y@-xklpt*p6cEvnv8Q%`Dv`3$&tT5H6sE{GDQtg;ZoW|joQH5lE(cDiCG9C2vK zaw|`(!+EIQ)u{r_bw8*UR~O8cx+J1irO|kBcy>NvvB06|hCzO>g4u)~ou1Z46E^o; z9TbW{F+a)%P*ugOOo9L_+8{)4?Pk*IRw3(pk-DV1GJLxVqZ7%|rKimIL=D2?aVjQ} zY+eWzq)>VYTNl$_YU*}EAhF$}Y5EqoWkQkVzd(Fudz{rx{Wb)TE^76rth0qxqJk6Y z`1nfydA>~N)Fb7yrDElsvpvH+gl#v=3$|ihtL=)1Z!&3S@HN1BKo|GrdFni$yz~GD zzFNH-7JlvGVBjms>oX@Evg8CCRE=o9wLB9x9J2-z8xtoaKi|?9Dh{jXI|07}K6&(i zR@nNIX6jXQuP)(?$Dt+YG=Te5Y$;h#O>s%>1x!drcE_s}}#QRBF?zFDgs( zMHwZlQ`aze=C4Ll6`vEQ$qgl?+z|b+&Wj2-jGCcncy**e!J-rhVH)v&53;k0OrSmdHg#87v(`;lEqhfq`88`x*}?0Umaube7zGSkn+Dii zYu#LHrNaOq#{#9>6GmWC@M zEsMBE3IAlkeu(C(K{6!pk@U>J8}Z~L)>p}~gBL!$X2fTYxRY{XaB*gX)5@(y%*4$E zCltL+lcpNyayK9yb1GeRxA37h3}P*?nYTsOF57o9dDGQeQ3bYDXl(4X@%eOnix=@L z2i@kXwN0m8Q*CVR#yafAdRsU6&pnH{WPo%1HMH$9)GBOPT}b5g;Bu$n0AxUKUJ#%y;OL;2$iBKqZj#N*TmJ^Y3TwGX1Ub}Qob|k3og(qLcj(Nxacgdd;+7ZWv#6xvGvz^c! z@cG6$p|+yy0owVtGuTv5n$iZcN!qjwq>;t*S`UOV@nYu z_7n2_^r8<-@$)lrM+Ee=v|S6X2dzykp|9C2x_~MfK1}pZ z=^WGXxYUM4-OXS{KjNHHokm!NqVvIVbc-VMkF1Tb-Ed!hU%T`5RzlF`%{z%sKCX$F zePlOZy%}GQ$!x=B>CoB_ztwzt$XOBRp+m4t*6z6QNB_f4qpgi+VaK8%ob2zqH(Ma} z-P!rJ!~U!%01KXo@wKsjbKq>)m5q~xwVLZ&dpOP}6#o>^^o+q~2z>(UL7vOTlwncX83T~%54tgNieFU2x;{0^PN#CI22 zW+lj%<}v~Yy=y)-%je(3-mRSj0;F9>Io}v}%QmekQcFNnLlIL+41rX}Yt;AFp!`=( zU+^XyW_eUUSa!S10dtxZQOP)?*Dy}8X%@^yR2mQ5>ut4wY2lVq9~DV=s-x%#_>88{ z;PCZKGdxd;Yl={FTzF{=v>+3QNn8CMB)d9kuY*=?UVWyzCaRT3wX#&J7*+cm!$mL}GLEI}si@cQnp}(AaaXQRvqn#)9ap*d(6A_l|2DkN2Oz%i2ou(n%!h7abS) z>wTmhKg4u_dZo+mj^htcaiMoao5}reI;Qq6^jVC4I^v7XInS=#oWDeE`{QCpH|ik} z=kl_NUTaue<1}x_y|c1~0^TstL<6x|6*{JKePl2Se&OBj7JHj3Y3w>0)$%DB|HTjD zb>5cTS{~X~4!VvYRh#OK;?_CMoYwgV+QH6NN4Liz4N2c*Q#I$uUvo6pHy({2n@8iv zTa3nK^Jx62N2AVEEs>3T;S{Ga7!g-+{sJ2p=SRn;H z&DLayqN}anf3+{1Bfy4-gv>Y&=Thm^gg%&6{DeN+;i^ z_*MXmhlKSk8?(tHI$)z$%_J-OAIetAS&B2@=lC9IoqryoDfo`DEzy>5`E0SoS48CK zchA;$lP5pC#0Lb${Rw^qZF12!D!1O)CTSBgO_8fdm91!+&bE7H15AkUa1^ z9pm(+i8{$*Y=IwRtJ>PROocYOcD{02r~N1CZ%37uka=B_yg#q*yI-#$5cclh?@_urkoeg55Z zbh5ht6~Qfj-9Gy7^1t65-|Kw!-}m{;eHjv`fs+)Pi3lXptq_u=>p9_V2^064bF}bL zuMQ$-m4ul)2r>3_4)dW7S1%5$Cn-8S-O_bGBc!5$F`zsadh$L2A>*CP;JlNI5fKpv zq{^ZpEiHnv@nI5H2Z+vGTujb2x@~@$kI@jFO%gOEWO2sQ8daWsJ98ET`jOvO>`DZE z`M@bm{Sig!cf246vasVh@XQb-d=s8#0~6tr0h{<+mHJbH!M~)(gpss>&9QwdaXB_- zZQ0VR0Z}}%RE}jLrncV^2zGllpsOfwO0u~Co{+RUPLthL^Ykf>iStRJuhqM$I+PBJ zML^-{Q}#V|GX5Xaf_H;!y)L^ldPlFgBx^@m}7#E4GrQ-b@jDpVl*%x*MS#D;nFLMHy{Pc{{n z%vii8Sk7ag6l8X96|GxFDwj*km7Q_3!tGN9cVW{Z3vxiwFckN(#^TZqP&;?XQ8QXb zuj@e+yy^4>l>K`ENI-8q$_VR)Zj6WZVV{5^SK06d2wG#Rla~s7`}tW-j5`scbrLtl z@fR|Ub8pDU0w+RVj4;K+k%;3=7`k<4kPD2X5x@faWgq*k9C{x#N7!w}&2Ok0<2u%Y zA!!*5EwS<(Da9!gKyTF|nFPC}#u|xkMR3G_ye)pky82n0y!T>Z-3OvM42;bK=z zqg2a#Ua-Rkf)s`)p0~g{&AqwaiJi+F6iM-0mV=*6r|s6HkDLnq3|a!V69qGWd%<+0 z!|E_O(vc7a`F@5XGw^T@s|j=^xzUtREMbOqU0vFeF*<8lFr7ZzI2wooCx-(m>Tmj~ z>a3Oa@l@})n}51U>105g&1_y?EVFkMJEX0qrG@Eu-Jsi?swuD!EBlDZowa{yVNi*W zS&z;YjIg-3Shu9E{MxS{$DpZxHAc<#qiSzioad*gn@Lu?HM#M|rC09e|!_teOuo-o)C z6ePk2=paJr9gT5}5MiBr#B^|vw$qG`Pna|Ls-3NhnnrwE&?ALAH)jfC-ES<)f&DWh zL)HPWmjt>VEnF%oGLFNp-e{vG%@8^Lj`|eQ2GPsg?N~mY4&BN1nojdHbY?BSN@y1l zlsgKM4iSV@nzmKwLu!53AM#iM8J@APo!t&8GR^@0|HdLPFsMsCEDlQy$tM*;45N(v zhc^NY;JgG$VMg23U;HaVVBrpKBitIsfJg>jg!@43s)4XBF%S=8`t&J23T1}RBM8+&I8@2HUr&`v>|%Xwv9mF!y$vMj2PAM!;AjcbK?7T zlHpd=5HD)iC$;O6>i93JQ^rNofGLn4#bh%1!2hykhASJ;1+j99#3o^o3@KMUkTGk< zrf2%nf*qjq1ncu!9Rf5>5w6A*j|oB4FO%aSv^eA~uedxef!966X>Hp^`O|{9<*n`j zaz&@3^=cD=^LwTc1nh9*s8=;+tPBl^4`M z+r64>Ki;NufA=M33oATDJR<2Rd8G!|&VTI!XezbUn)uWu;f>fWr~co6{$6am^y?=2 zmn|k^uxX?N-uM6XPP<(TZa00s`0$0Z)$CVkV_SrVGv#C+enhM_Y-wuqjab1;nXxJKQn zI^ipQ!fz=l@gIQ{vF%>veQLT_yJa^nTVCg}Ey8=NZjZFiy#~~6(JPiLVm0?}S@nyJ zegCm(A4cUTqsMjYkL$|krWUvQG z>xVQrd9^&(whV3;rr}=Vjj(t0MoqU=4cOk-Vw!5@VQjrQ7S&kR@W^wfXB+8D=}ioI z3-X(&<0^>AEIhYsM}+PngYaeOfcwp-oxR&eo%B z!3I}Tjyc*vg&)BV5qIttXFbdsqlSSq8RSO6y2bRTnxQ^YEVWMi$?QvL{Z@FtZFL%S zX|Z7~oAhe20^^I>40oBn$fJvKwlq3IPwN=_AFBsS{!eE^-Yt{{TeO!RsOYb)ukMkl zQKZxY{7SOWW|tnVX+s4YuH#&!Bb}pK==laH-g9rP);Dqq_m)=_q zJj`_iiJa}1N{I>6wD90wfVCr~Qf`C)LLS2$y~8?hQ(2(wmXJJ1KG1mUqOh%a26H_5 zHFcefG|ROledTvFm)d`dl#nuX3%r7QM}V+{<^Q^z>Uj~pRp*)tMc`tWUT>iSqaSF2 z*rr2$Z6uc|1{rI0oC6#9n*uQ)^CPm;Bll!}~|d;4~YhXpU5zCE0R`0GsI& zl=S&df6KN4;(QxF$|hd)zlLP5xV|7uJPzP-FRK9(@=mcD))5 z!sh{LCd{wJ(`k+Qf<oQl$KIj%CmUf{{Snp4U0PRup>r!%a<@a#19pRS^_ua0`?{?ca z8tM%uuSpgWk3Ht~X46`VzJ@Z=-<;l;DVRDw+ac75fk`%wS{w;i+x&N>0hNFt!#AZe zth_#UW6dJ0dmC}kjBrJ|Dh7ROtIsZ|E7@6F7-a{RH8Bm-`@!~k< z$FKvps;P=@&QnXK20GM)`xsCCfIJ&)!tsBcRH*I2twGBP)vn>D*xi1gI<;-aJ+!IK zkmts2R%3;YR)2nKs|gV&HP4V}uWUQQ&$(uMH$K>oWuWWGfhM`i`$i3& zHrhadV2dgxU*hV8kUpYLW|?c&Yh-v$9*qXHyN9yD zvuBqi(&Sd6XaSL&C3Q=}2I7v3fYexJ)8q|7q*v=`M%wMW_?pSl08Ks+6~8)Ne%(3< zDOJssDov@{NJ$`oQv~i|m^AjIV z1#_gZ+-t}3k(tcRgJ!CK6j>gR%=+>oN9aSR?XSUZu$DS4cO&C6!qRk@FdcRV#rTr0 z*&L^LqVpuz3oTc%VDNh#?3jjgW_Jz7$tsgJ;$7qv4TXLEvgJHoRvpqg$>Otgo=!4l zkXBsHg-s@k#whThj{*k03prs7*Pn2wfR$rJoW%kqoD#8UN9Ve2&Z&i1@06D%7+_%} z(zn00NktH&^sGkaMn?)5 zKfsV*Qyz+6oK#<1uDV25w`B$`8D;IzA4jMgD5_02p`8~y zD+ubHUXW<%5q7-U3{+dVPNO0a)le$|g}N(_Uqz&M?^)Es&}$(Z{Nah{evcZM{{BXOJlOk@(ME>o zU1lNUl`_~G1S#kGDRST*79OkSa3yf9<#U4>?~cz=fpOD+DYy>~p3f=P``gU@P3Qk6 zYd~+R$fVne57kavPW~+y!0i@+nZR9vQtl^J6-6bdChEs_2nzRe*5p_i|6^HrR$8U(!Kuvn$j(BN}}%_`bfsP*L% z4o!`s($iFWE|sr?U|)Rq%*RKWR39X)I{wH)`XvTECFy@^YY~O(IWUIP=e@#noFg-I7YL zc8>k>ugpw~@=*^*TjSTL98%L`zBZP5g&}A=dZ>Hm^A=$P;0TQwE9%j8vBxyq?2zEF zWx2^_BLE&@wgO;I+3NTiH+)DZ*$#nw zoyusXd1;@%F&x>7!XI8d68ejMO?+_ODl>Wq)Y;I zWCaRstzztVHo znOY$5O=`#=N9Jeih7SHS6=r^_(|AS_eDkxY3Ev+*P=^~2RG*xPF&QD{snav{>?q7axA*v+}HQ+ZbZ>Q)MIIFLo4f z&kc-`&iPSDUN?AKPD!LcqP}}baOwJGiEu6H8jWiEBbbCItM;Iy-#Fad%lU-!Qd|yK z1pLS7L8owOv9dj&Msxr{un^Beq>(?&W4Uf^$$KBmy#9Q+ulXP`4 z8z!+pp)WbKnn#otTXJi3pQ_uoEwcKGHk zu-zCI@nV+G!|Fl$A6g25tC5S{#diQZZpR*L7sKZPFQ&kaH z*;Y##=Ks`0n5HEN1DU#Cf-{(|ywCy>tFnv!HgC)^Nlu;SXWmUdAU-4fx4f8*7Y80V zxH%lY!^v+?mJ6Rbz~ZCu0g&Xx44|}hZPMWXG^SrK+4F*Ss}oE|)eAL zU^SU~!9rtBl@y!rr zBWwr4OR`xPFtPJb={(#|oMwMY|L=I&Jv2MRpSe zW^*lzxCTXNBZ`X=pF(FQ#52jKa}^|-DY>dT`t%GI*2>l3dk{c&`O6?z z%OJ;P5ZZN!A^M6893tpA?f`_rteN2l42_hNdZUzK}a$KxWCHLv1yI=%vq_@tnXY-NMr ze>2~joBgI+%)+sfe6TuS(w&^|3teS3OhWH@HdFWg%E%tNEF&ao`Is)!lyr5TPqX0c zq9<5=--gkfhmQwrTAJcz-j!e|m>)_yq+Xh#`iFr1+(kgKIJKsP?*JB-Th)vw!;WkE za52kFC+NyRmERAkC+LVr0#8YVMc@* zg8PF@Alzfx6Ih8TyMU-jKlfA^Q>B&*+#d!yn31|)`wNva?fIFIB}LdK`)6L+=%*Ts zkQ>L`rM>Xpm1Ph7lH3hl(0j)w{RwtpGB}zH+zU#O5Bo6L3nHUASCGX(@5q7LkOM97 zFZR@J1peTpJ^vn}`tk=MM!;**xaXweAxsYTrdh{Se|_lUbJTUZYK;I@9Cg$SgRjny)=HXjWB%%cKh9@s z1pwlR%S8j<+)B;~UC|a=+wvj8zc_HO3VK26&Vw*qGe`{Pksh-x;(1tdSkNC~sFrJi z(7iRzeD2e~4HKDm^Z*)%1MjPRyn6I>MpOH;y-*eR(h;7t7$T&fOS%jGe` z+$xRd#P9deWVy1&mq(Y3H6&@Iz)SI`(4dbK-qUDl zl$o6xEC!997uaR-78~jH2qRDBl=d8<$V-m=TZ-uHZKYQ+iDP<~R~xN-IWdS(yyygld} zD-IjV_Zp00tu2x_7L0x74@MpOP2EsX{$F7r>c^qC%{V9y(=ZG*);*gHp|Kp)z*xc-+FIuFn8YIrVn+XfpKFW+M*Mr*ti#N zfUWu>`T%t5Ebletu0ERf!#IT;*Tz1e8jrp9xSI%sd}}sB_5{SR=r0l4gIyUjhz4=qp|*~0^-R)FQ#4SSlzyiyxd<>C`%i{{gM;~m&&0s0JWcFE5u^) zgA-8I9kES~m<@A0A{!d&6Z{ps$6P8mt5I66-YmG>7_RIJDMZom)|ZZ(s(SVqs0b&#PFt23{l0Cf)wqOS}WD`Uk_9Tyg|tz3;_r6P^APHR+PKK06BI^XNv za^`sD>Qb*5u{#6aq)mu0%1jtk2)A)V2u8MLDwgQT!KQGO1==IGZF`d^hm^(Jn_M`2 z$P}Z7F&2Nu{s*P>Q={%gJ&)@0dZ2lOF5G6|pg~u;olK>(AbEyF3hdPvsA4r#x`rxJ zAXY=Q7b_qkUzFDqb&w-JXdg5JVNwbwHdVx6;0Az0AOS>huznH#7HboQNJo`It@xPH zLsa50d9uz_s3H2I5GWXM$yDQbh^(3C)9{V<$L%oR^^ONcu%V@uZD|#>v}$f?#n4dE zmR7o@m2PPjwA4g-WJ9lM{xM6=vvf5>n_9N`Qg~hpXv0L0VRaa*=r!No@nAWEjx!9y z&l`{&Y>vd>iu_#LbjE5~C$o&~XJIW#P&TC@Psr?2E6uD|E1HJDfuDYl^9bb_Hi5T3 z;Y9GYrUiJ^yuKmJJexFwnpl*G zM~D%DcU08`xSs>Z>BUffw`dzwT;Dw7{37fAKmR+iwR;|>Gvsb2w$~z?rwDgTMvr2W zv}u88o8T*?>&p*KQSf}mMtnnzi?NMOwrebz zuY=W7$<=v^XQv2?`2hVcYV2)feRzG=f+D+HtxWd$&%4pnSK0 zlMLNZN(yJIq`x1O(+vg~l-JO00+BlLQ#q7oB!KFq^uR#)mD>Vxn9_t{ao|KxMD5?5 zJ5F2s=ZI-Wf4g~qisZc%c}1MgZc?RI{Cdln5ycevrye8*vs3}jBEXz|ld~0I03r`; zbBkb?Q8)Zp!A&8q81=0tXnD6bFu9N?Z76jGcMZI~>Y=%NN5*G-AB2B4qlI{+je{N* z`Kc>@yccYX!lDY&7=zLLGQm>ue^q17Ds`)_-p%PT8^bT)WR!EqCIAC7a1OfcP@w5R z4=I`tlrSW;f@b{bpeEmYH{bgWF0O6`&h1GKLQ`#3O`$6G*hHqYXVcXS%%@k@DxX)h zM*3)dm@@2ATaZ#q%Y_C>zDoA`4?X2-5QohvF-&}--ju!OlR{6K4Wn#w9G=5z3QVdL z*;e|Smsj^s=d&1+&Iq`r*3Z#(Oo?HysXXl{jHx}x52EU#m)A{yh^3Ly1B z$2#w5nU@QT;_R=Fr|IZpxISL}XMIH12fp+)uaP*U&Tl=dQSPWLEZj;5bF;iL6XoyWV038QsKJS9Jt<33D!Vz&T~y{Q+%FKdMGODTk3)(hd6j%a<^AcWWE^o8)X62jNDLil>45PrF(5Po@E zA^fsl2*0cq!Y}KE@XIZQ@EyHw)ow-M@8A2>-E0 z2(Oxj@U<_5S1g2AaUp!YQ3zk(R0v<+SO~9_SXae`@JdK=&K7MH!q>MF!YeE9uD2G# zD=Xx#w-v(IHxRGA$+}22(KbScx8m}^$msa$_nB1IEg@Tnp%g&>htDFjFcS2 zOEwDR_~@69q&U9*J;g0~bkqu68#AHURn-e_C~#HHf}1aHt+y1%*PkqouN`rG{qH4? z|7Ue?no{gn`N5oQJI|BQ9lfb^*yw_P4U;j3; zxO^y~hp=bo_%1#sIRs?PF1`y;0-A{nzZ7;F&1N~`V^j>lP5zqN-*d!-%#(nmgEEQy zt=)QXASdJgiJxE`OW4)ZxEs*}AHJVNXl?R2f->a)Bg;B&M}$q4T`_2jY?SNbj<9%0 zst@A*W%m8W>@;J`Un=W-^}v3VDFc@#S)TLpx>u=8#Yx?gOxa9FFwq+8J=d@HT0C>B z49bU1t|7h!$wdV1qL1vn8LS|5U#b)o9J{cIV0!E+uOIGkb%|ff8+4qHkq$36x!Rms zgD&y3i9F(ZY1Uy)vX}sm#(6Uho4mv09yF_dWGxze*-c=L>ycC?T)StRmjkGD#SBB) zA^u~z4I$xV{t z-@C*8JwqR4%N$O>3O+vkf5kb;XIozV9UVI&%1ZW53oPoyJv8$_VRMh#=+Q!?p<@u-M>40@Y(fo_lqxvN5*dU z1Ooy7^Q6m>N4nX1Q7mC~{<3rb-o2fBaIsxf?6k8#*xT>HKVNo}AE3X8gZOej>O-C< z(OtMH_2#_om?dOi9R#nV?QKi^k5=&wz;I{6VZ(OU(pV@f9 zk@`|)GD}(M2X54Z*iOau`Q;yVeU2Z<vGe&rE z+lYKOElyLzLWYrXoluSw6)({ChKNK9#sCvsbJpoxNQb9H^Qd`H$)sIU_v@x|z4LUy7KQ0H<&(XNnV zmAaG>lk@CL0?0v3VQth(M<~oQuMl90WX6q}ZjcmDm&H|yJZW6?tp%B(%hvmO`OO2< z!kT73j^D~f9?!SX0tiG2s-xO8r8W|@7aJs6pvFRtF}O8XMzdpON=)X)IG*3G?J|&7 zgzi#pD<`Ifx=nS7k$Ez?=d~3Sk*7AAyb=k|cu}r?O~Km0S!%j!Q}RTK2I$=sdbU}K z!&3j{-0nIkbq$w>6Fw@2iH@N~Kd&9o=a>oRc0E__RH~(o*NISQ7e@q$<$ENy1OJ4- zS%k-}2tOV~<$2AE;rGKpzE>wG{P;8e2oB)8L%NOld>uaTiO=4_0;&7@>HuQbmxDj5 z*k$Iz6b>UQxZ&y>ozbN?U1j4~v)N?}Yxub+ zDEz`Aor~7PU#OxaSIS~}n$s8L8>{q`)VQSn2>Zy?VqL_Ro~FxIyf4OW;L|#Tp}$(> zXsLY%!M}6&1*6Xf7uQpfu-?d(3Hbcwvq;d{!@byiJE?#%t5=KW?8x?gGw#TuD z+Rs~zl77{WS!G9kC&DEbU1qkOdk=rrHu@!;9YQ>d&pNuadQvcZd5Az6|6bltT9YqnwN@d2XSTY0zoLR0M4%f*MW!i*%YOSAQd3 z+C=aHlIF3>E-jQsp*~JLP?#iwT;MrpBnzmB{xJM|?3EMSAB z9XcO_$VT_dmbE2r(Zg0b3`29Mpx~vBe?fTi96|_33jJ0|L659B6MFu|a#ZbOc9gEwgC5i^-^eh7EdNF0%6{;ll^tK% z%E3I+-PLTrvc2DHf5dv{&G^jUE-#i`o458X_%t)>U^_oUlE&&>q1yc%_sqcmRrl z$U)pJ5k+h$O@#^H0V;?Oy&ZD7McS=(g%B=Q8;+o}?@bcS@Smal>x zLrmQb1wfFCm8IIyvGF7LgVFxzntgWw{axF2kQVJcU1T<1cRr@**A#Xw8*3;{jK`8Z zeg5s6x6iey7u>bAbiO0BGV@~I(^qa~Dl$sR8hb=Ah_f=AUJ^c~sZdzM=RarL+)ngF zytL|W2D}4R6o@g1*(?BZgT5K{5q}_{~~G;FsJjhe8+=? z93ucjTm6tIu-{I2vPT^T%i>;>0R0pl( zu9Gc4w{WdeWbXGmcjf1QKrJVtHFdZCBRWLCX=m84?841c^ zZVa3(3k1q+ZUyzLBWtA&26b`npKNYh@j56V@v@;+ZwNk3-(~vq?CP1e4U=n?52;QV zlKDordA;IQ#a(+9YJLh?a__Y|8Gp9AW?aO&8kvrp7Zd){a^~8BN+aU;Owq5CJx8ah zf-hRtCbs#&>*Rq4t#HTNkhpi{vc6+y*Hu5lC0a48cUt{z`=Z1<1<2<}Y|df`iF85^ zVSQd$i>Ky<6w_`__rrlREpI5?xHC0$y_77_1UP>rN+_2-aXa)j=y&Pjr=arWaWd%d zCm@;i_YVB*@8j7Y=nR9+5xYhy!mrUr5r^4eIJr%8vhCpN$+kiu!u45=l=zfyDD9RG%nR3TLsCV0{ z#h;+Tw!P%ZC0Z-w0jT0T!WPjyPDo~Ojc6HaVx@CC>h2I@jg|U?^7^_Mxqk_KrasqwI)*j$|;}1oc#MQMhq@qq{;B z?lI7SlQZ(b0`#JfDoF61Y9u#ySJTbyZh%f68D8;Nw+`#g5mqF}-~dO4R98^l6-_=q z2puH}5!8yPZF4K{kl8f3&NfM`=VVo%Jo)zU`P&Wh>Q$C}#E^Yg_`}p=A2Tt4YkHaH zQ#7>`cE;R{n+U6Xa>vdkFCcs}DOMU^8IB9=DeJ{?yS$@4XwEH77{uN?KAVZio!be` zr9H|dms4L4>ZRD4GwHw=40JyH850I{`bdj7xB)Uhx`bQ8}4cUfAY%8v`P zCmtBRNai&7u^8rBRG^AQGmdCS+iFKfIE$2~F53QLS+9e-NgudLAD~HkzLO7Os~Tjb zI6i@yTI8o{Jowf8ae|szU{QF^0yE;oU9#6uziLorC{4y2^~_sjoiqp(Of$&MqYG*H z+}?_0z&pIYsTihYjB!jYO~^hZ62L09)9`Zaz+BgWS5n(;iIT$D4tPh@VeLfAG!%gD zwblMPf(nnr7zaV>hro|t{?AaW8mXDoyb~o=H;&q0C!fcnoku`>ViRq~Cr-iXG4!`N zSH-#VP2fe>amynw^1@A&BsW%+=t7!>t(C?>3xHIbnqYY_TWJ_6x2yZ@H$>B{Vl*p{ zX2mpF;I#y+R(OGBn5xp@_vVv4D#cw%9BRdeJChd0un{lmJy$iyKQN5A`AFsMkIvKO zlT~}*%?9DBm^K^*V(PIMOfjc`SR?r7TMU@%nxS9`4GZ#B@0!@49n1+7o+ z-Xj(5UHC-O3J}LR4+=B>gqLB<|hUqa`hsNb48$?sn#WOBsKi;(BLR`9ly{Q ztA8=e!1&4k5`F;{A2+SX-5PH^%b3iO?M}9Ng-VX z12v*sG8%EB;*M|?lYc;^|KP-YY~^2B<)G`Unq}MfZrFAs_Cx7id>Lyt{?>}tPXNAQ zYnYu4m^Qy)wxNALymmlsLaZA5(8z>^VZhM&)iptvggSQUf2g+*c1>3ilO-8+J%&TXEk#n{;>Fv7( zp-JJ|ceAux`7E7e1`_ZxFZ0uBjS19gQDDsOn3)orGPzcX!YGN}&Jb2FV!y;&fQB|+ zZAk`-8;qki)&RTkmUhOc8JrsApnFUS!)?Fb(j3rhz@U5U8mr&CMwrQ4 zO#+?;Yza#CAcP+Q8*w!{j~a!lOrJI-#UdT$s~Zo|CSy(Iw0^rGtXU#8UgDoLTh;GZ z)tZIZPqMrmp(EvTlcD9Ou8HasT8f6N(7ahb9y30*(wjy-6+X40>t%L!n^rm!Sc{@6 zdYB2}5vN^I9D8|FChVS1^>#DyujpX)`*g52E8!M-2oIkm#i6kF@DCxZ#iX^o{GRc8 zrHthw`5Kw5cvVc^Qkd|lbJ(glnHG~13M5XZiTCgAkRcz1vVT?-%Q39JtMhEGzmc^O zKaM1)+3G6G5FT(sW=9BBmoAY2+#SgD10N02DPDgNVV$J55n&PIhzU^=*=Cg|#;!mL~{XGn6k4?Ln=d1D_ z28z9yj-fF~Q8ym0fMt0xV>%g+0eQm{ONORERZu$NlOSo2hz7&`am}zEY5pk~{XKN` zzrvJ!{t*hc$Ri(?J8X^)Vb~`aJA87^xQnCJ1;(C0V|_9Vqg}vUg~w!0aOFt%pBMQm zvt!OLLG*K!!ouYtF43Bt=Q(L3MQrvGs!&m6IAA>iq33IiM}JRuOc@7J|GQKz{+74W9Zqzr=4Sl2EUB@i`Fv_fYY|oCmWI0ws;k1g_s?o^vl57tawmY-n^a z%IcQz1<#hn4DlxM)lML~!}yokio0a2>}VC@|NdHLyb56%>lZfFrV`)Ts0Ha2S}eh| z;T|Xm6H{kVJVH^UaW{33FwDGKQW|WWE3=Ci8*;zF)7aU_gk_(uyLzO$dO)-aBVChP z$Z>-=Tm(%vR6!d8p3LCe=~_mHr^O+SS8-&c1XRul4J0iMjJXBSE=4OAjahLQhO8GD z2mJkDC;EWfKgoj!TtiHhftmjSj_!P24~@X})Bz*#zJH{0IoefA9F=M+1DUrO}0fA#&|V9WO}GnY=o>}+0OWLv0s>l1Cq~}ua`58K!0(=3x7BhR6g6sy| zQ+YDrl+q!JDwbfKKF86R7dBYK;Tf+*sDl7nX zHSR!l9JB>Pvb)`^orBF~0#?!0702TD_{Y3cnA3WKd=ySSO{eZwn2qU z-jNPO3aM9(_fixlMNpy}-iZOic2HAx^syNOObH_a;LC+Q{aU{Y6rJ-nw^+G89{f~- z|8|8R9FD0vkYb+F!izlWORJmQxx@LTP^VIjC#-Ii7w&{q;4Lp!h*G`zpR4$1 z(o)AhR*{dWwfDX%m8yg1HyOV!MjvrWB|_L7irGr8LzxrTph4k1u9qeE1Njk-B&DD+ z&zW#PI8C7qDQeB?Z4_0_0SZ^Tew&@?2(H{!_z+tp+}Ez)=Bg1wf$VKS_f~JDa?mkAbt6{l!Ij-quZn6wfkT9OR;3CNyo>C+*in zRm(08qNwn8kv7JSYUYoq>H2b;U{DS~{FelG`se?|x^ZLOsCb}Z9Gfe(lW%t(u4mJE z`8^yD<$^rsgTh85d7rYL%uU`6=wH}0Sn=|75=_ItzIs%?ikq@~ z{hjp9(GeK@VJbC<6$$(biV6lyKt3tilm1>Uo7t8yWTV@#qO@xYM{b?iB`koTI3DIA z`>$)*!nmu+9bLB0cnV_>qk@P|0GOT=8m?Ex6~eS(|8t7G!FLr{w-;H7p18UjD%?tv zt@Co#ud9KW0A6mC_a0n|P_qj?hOMC0gRI#=MejUW!{w@C{oJUSo#yj&6}#)2Yk6v0 z!nWn}idBLr7_C^t$kRz0KbXw8mx#N*^&v5-F~F-gD7=nNhMtQ)T8rS14c3k@-$9Xn zKHj#vJN{-iYo)H<+5%#yH`~h9Qm7kCd(ZT?9NQH{4Qw3-wt;X%@y0n0&HQ36B#y7Q zTNUcc;hTB(Z>?X`%YAWnR%ZBEEK%2KHId%hFESQIdV8pI@XOJ$>lI7aZn5ha)O+PO z8CbJJ=lzBj^INW8WOIv|;d|v8nryC1`ZRgTD%#maiH{CiJI&8Txb6S?_En2r)cM@R zIN$D#2WlZA?ZA-}1&NnR$!eoC;a5*e#ZM$szFUrH$&UN#auNk>jmnm$GLFXFA`t+_ z!IK*L<>kbyT~~%@kN(?Q4L?*S>Nps~U=szkPV@Q4;W?^*yS5hxK^faylWPMCxRDut z00BKOmF6u`zuHCu1(~s!US@UX<$R`NNuIO_Kc@C^KKM7TzI{m{nQ`tx@JRlF(ljm_ zIG8Te@*hdIToy~d6)lUad4zFHU^!S@Mhx{`l;s=PfNYbK&Wm|9D=x~I!8n#K=3K`k zuImNe{Filkr$ssz0$;n1YqRAY_JM#0Mi&(2ZM4kNRiq$ZNpJWC2im!;(T4}7SFX)WYi zsEtA)`Q`Jyy*lb;mL=z_)uJ5Uzdy-W=NG7wnbG}YTB-l^Ka}_LvIIHp{@&+bJgg&y z%yCvMGeq7`^Jy9Papm5`_SwSp4_84`d2PV!>B5UW+iD>NV5uQR+{FyJBV*cRADJY87(Z7dPt*q zm=Wc?P(SzWo|Yi{>mwLd{&C!A;l~?aQ&jcAmaU*{<(Kt`n>8BI>Qw4(oFJ#VU1p~- z-OJMLBR@2?Y1UwBmNc+1QM>r-gxg1hY9|SyXYh|H<$4qAzM@L=R%xmg=C34Go*;z9 z>D&3zEsStmtlr{{)2g*CcE?q|8lCf+fdTRv3tg4j zUUwMY{ndzrV3v@m)(Y8)@=S^l)fT_CbRVf`NVP;a3!rXi;7)_n>I(00Yp8;cw62Hz zA2)@U?NI&KOosWpv3 z9e-UcZe9mbGcQ246Mecsw>v8EvY~SzGP25`He*O~ov+${lwF6EpCKfX0>3aqymM;n zTc{8kAcL^zagY31w(D4%e)AfwDr7npa7_{mH_%iHtTLh76$I+Ex>g$|GS>xzfhKIL zCPgVYC!KP}~eUHevK*gjM z@JaossNrL2GR@L*MK{wb6Xb;VGIWa7*rXqmp3FPJO}l(Ex4Qdi`KGO=`a$-e`I$!M zAP^5{0PwLz>+r8KW23vgmZ_9q~%4lL--RiRW&1nZvDlS~+FCT_30<33JNP zin!fxW(Uy%(Y+kaS--=!*=1YqG`YWh`y3Q5Er2pic`c|!*GFoKAK*Pv!^N^*>KZqq{@F{8jskw|dj`T)T(Z=h*50S;Z`3RDw@xV{Ex{b*@hs?Db6+V{hMc z(0=dmtYdS6O==Y#D<+P8`VOllgD# zFs`%Qehk8VHwh*mic5Eg4>8{4-eB;btCSHF%h9r!PM@aBzp)JUdDs2?I-kE#+T#6z zm-dXszt(?!G5F%?lh0ay(R4Y%4f~n*{W-D|5|v;HF4sgjSys4_waywoXdU%HGt>L` z6J`k5<;B`MDAU?V%4&96OxabLzu-G|(oUyWX+_SMYa14Ubt{ManlfN%k znTa_8!wkzkhb_cWb;%f&E21p0n6sR`x6AY5Y7Q4NN$TDeI%?lTvo=m91Oe)LH(3=| z>2l1D=~smg!X`ixb{xek1)admnxB*rny1+)z0mIGXrs&M2DpG6G`ze_XQ%n(A|t6(w{614(yP_}Kdq+A%YZy5(boDtQX&C(NahsvI0UCPwj% zKKIEY_a5dH88j8M`HDGAH#*qzM8gJUpM9|yXy{{hx|(hEsqD*=yN%8B|Fy(rv@6M@q(zlUE;L{9G4l-7Ro9O6cyssz? z&fy4Mvk;7bR;^FS;azDk8g5ZO8(J;G2^UrplsYDb)-%IN!30>9g&^Ux~Kc2VTz=TYS1)H51Zzp?K z_2}_BTb&oS+cA+1!}G^3Y9;D)TzXpPY#579wW{D~ z5~(u%J?CjUgMw?@@SnEyn6@R}*Em!AyUWYQnj*;pP_6W4iDn87%+Pv4E06eQXWFgc z;)1IdU~=+P}i>tEP#N;{^3c zCl{E<1>AUs``A+fP{N7b;Yyr|05gXm%(NM?lMP7D)&#}mHu{y9-#yyJ*@$4#5G4d#5vO`0cdAWE#o@8aqv&TK1;KH4@iSPH}6e>Puyn_4U zG9+xAMQ&7N=)4m@A2FkLiz)W_-sBKwACujrwOBhyGwM;KST~Lbb&Io}7EuD}a;pJV zG}7g!aW%(CKk{rNEK|b~ij;gNE}zaaAo-aB{ODnjdw0_3blALPb!s_*qQ%L9Nhuuf zYH^IFX;~Tz6tf(3xu|8S(Li%FLdxkhiW7CCaLjXzdYi8*MV>Qk(=1KOEf;N;N>mF? zgBFQd9M`GQ$47%>ZuG&<%@mw*pJ|818*LxeLd@L+ufgE*50jz1u|@B=imap5S5Toe z7A`LGk+=ylgJy?*w>yS)G3~4oa zxJb%sK7xxA{-0521=|p( z<=^mMev4Xh5Kj3ZGePFHSGcyCM@Q`C#p=B3bbS&WSCa2DX)-RJm}3#B;iz{JP?2yr z4J3$o9MMl2kWs;!pfx{=@US3pH->A-a#Lj4 zld?Yrt`7gXO73x~b<)#P^AU(mKk-GoO=8lq&U6y-<0hgN6J0UUa~)Wo3XwCUApJg0 zIy*ai5%c z@VVHNqY`oFrD5?%RX!=@i`nysp%Vw;f#xBfg6`ElzhH$3k=lBuYgG~)%&~@qRwCUQ zIZ+V2^kkVX&RdMrOMW+{ z>Ms_s;kgY%pPm*$5XuOd6HsM%DcoCKb@0PaGN>>=)1^a4czdJ0=&)?Ap{aP-DqZQljH5Mh7#)`GSvIYemuF-$>BPUZQJNNFtTGk3WdJ9nCl&1Ol~|b zh@5=$v1uwrGk-_}qjX)OB|$Hwdd9!@i;emRAf%-!We@3#bda_%iJ zGzl(J|cE zoMP=Xr|y_*qQQRikGu}vcWbD@xwCo99KrGHOgl2MLD!{L1J<6;5dudc*Li0GuG8KE z^%At-V#QA@6|_QgK;D^HngyC)f&10QC*^&d?AB(8%qGr!3q0E|OA(~c3at|6@t%{4$`63-73daJ4 z!TaNwO;to>+6bcM@E%wGubpm}Gu1B({9n5b*Ll*H_Te~NrSC0IWFFZ;+!BYab!!Pm zIADFBZZS1cY{)Y~fo>85RM&go+-5ZdY&N5Ta2_Q-sL9hauuW_T*R% zYRbSB_njUR*bI~R5_e4$?*`PHK-usr*>0Hw&OfskI9d@tR! z60Ym}{Cx^k)9G*1Q}nw_U`yIaz|tsP>? z67Rt@I1ZMpZTJtZTqe%@f?wC^$+~t!W+K)R@kXw$Y2WF&d6Z;*74+&5OJ*f%Re{xG za?KvB6SAYU8!2sI!2_wMkI=Yo81dh5uum^mD_Dv&-uoztLN`~->0b~#%m?}0G*h!W zbX#{Y_kP2P+WYj0LKULVIyZ2S-}tDyj(oGa7FMCl=58y9(4_Tzj`8NF=8Fu?*dLs2 z+-z&t9rpi{4gWjt+1S34$Mdar)vqoDUf*QD6>Dd|iQUi~HOqoD->&JA334U%Ahe&l zFo+CIOLPm>V9%D{MiJJm>}gU&MG*HkUPc}1%iKC>?6#)#xB7a$xd~lR4sLeWC)-l* zTHCyUVDCyrH#+y0YL0aTm%1mY^;tKm;Q!VziB;^jpq2`~t7qeOYjm+Ji)D{kLzjGR zViPXx3H$jxU)3lu)Pu)R*D^20S-a*Qh=ENf<9Hg}sTSYPhnxALc)*{RXT@>`OJ6U} zN%#WNz-H*_&XDP^>vL9a&Z>sf#CwM0?DT958*W@}>fb!OLQ8LGB*q*(*pwGJKt*}0 z8mQ-uPeoWG{rkStKw^!7ljPzcgT^)&h9!uPzFqnBt1Nw#6?VgXRJD}fYs&iNmwzbA zRU_q^Ss54eZ0q#~yK{XOIzCn2wnL z^NLk;=4jzmbZ+Yb-Mp(N^`lyEFxX5SOQ!cduek@=W}g+f!5Z2S^G>pUmUR{=DFaWqhB zBtvBDmZ*aDK?46NETEPM$Vf>ibl8#DLAJ#woF*Du%+K=4P`=K09n;}&boD3tS2to> zJoLm`C(5MpK0P73t&TImH0W#s$!D|4rwxm5XUV52ld)c~3NB`}3^+BgV5a5wV~WMt zZv*PgCzH%z`jY`P`;;m3bar*<>T0dKm8DhV*9dvc8m3H+mQ!C3hzy#(v$;N=`~0Jx z1q<(tw%jq;cvGW<`|LV48e_ZpzQMyyTeg~@Np7q6ok-zl?O4M*FtS;?)Gd!@vF+Jx z`)}MSPp8jrK4sDi&7U~c2}$^loxhum>@`MNblfn=EVDf!z9-m~C4`qmhgBWi%x7b> zJ4A#vdfYHCf5@{}7_0kHvc=BxND~V)+nKnZB>fz2MJdNwt-CDYAd$DUX^15-OV=2m z=oR@%B*NIpeic^EDlD(<&|}$cW)GSNW8MRgXU#H;KSKPU>EYvm_TziELZkdPMdD3l zv4FE+Kc7!yM(h(bl!jF63PHb&VKfy~w^IDkGp>y-r9u<-{Q8Cn+h*Bg0j|eLS$s|O zO#k(50)3jh;ksd8XIq9M=ZIN+tnfZ~=+!Vd(?JOF@JshbI-K>j`5k^~b3dhhy@D?i zHcw_ZVG_60v+kGV6_o2?aah9nTDD8xr?qTT*&QPRX?3i`6knpbHcZ?Oh@`0Ijg?Wd zS!hzI3&4FrU8yNQkCmr)WD>T=UMfYs0di6!2^TJ$f|y`PGC0knJ9S*bpS^g%!k|Sp zkA1kFULs0JgyAp)<5UIR6C-i47&9W$FV}H1SHq&t)ACT?@7sp9_Ki5LTAe06t;(bV z^-9*KH4;cyCCAAh)Dq7XQ*BTMBOLKlo#bBf z8V!pHE9ojz;X@O&94qF)7b+G+iN~Tpae-#9_b`+k`#{sFgT4y0yeui!$T9&|M{qw& zn0~ZY$ydo$4(myU07k(9;Sv<`Ka%?_8J(xlHw>_iy~T8nGO$k6V#Ii1gc`+eja?mx z08W{U*@9W|c}mn~MY$3L3BUg;XXkGOc;aT1&;;H7;!Jvf0%h3-)#WIa;6^gVP! zDbttgJrT@Lwn7(dEk_I&DfS+KZcDBt$nB^Tc(lWw5_mL#ZJp1aatmX3+`sVW`&TFc34+8%W2|8)YlPSGhkbIC{yEy3!Z0{wAAD;10;`3-Tq4jnA;x$Ry& zf>OQO>(E!hoJJg?Tyv=ya$3p98+&n>wg|xqGm8MAJX-AN@p}NPj{0ey7}CD zggJU7j~vUpsVewA!M-_UloBtvL)R<=GXfxS8OwgUhMtjpgq}tB5*KH}KnJL2W9e}R z<0RVVySS>Pr%%NT+3C|_?UWH~(}Wx}1xKES+R}Lc>uJlXlNm7LJXf_}CqF{k@~y z`i71^3L`Ns7RzFt&ls}{#3@y^3K<9=7;Bv58C)mgsFP#poOOUEdT;X0u|l!F!oihd*Y}^bzTKEs&Z1*!ECb48&O! zc2i~E+29gwllPNH$~)q~QRu<`Ajm&&&xm#~ShkZ%eG~6b^YIT9bQ`N^lhKLFBO9sS z$k|BCagCPUrs#j@Uik#BOS)GTh>8Q0g4*imBW*};h(1yfb`CKL>=zRII@$Ay?FGaX z{GaXt@V6fNc9N6(eS-6&aCLZ1OksKD@H=1W>nE+;MX^HI37Bzqed=NKVutFsBKlR4&Z3$ zr&nFGw0GG|%4y_oa_*fwK|kQaaUM(7eK(ULP0qNnz8AgYlo1ZC-l)5AXaZR+G|G0% zmIDUh=eND!-RoWu46E@6T_dx&Qv-t;(@i!^00>~Y_5yW~8f!FEb&Xe}JLPs6ZNy$D zN_-=}B}eiWeCnr=Pg(ZzDmz=fIrI6;+GSR2%dFOtzi_CQfaY8L93$#5t+i!Z4pEBp z63~2_RRv74BKe-4lw8(G?7`s}UMO%c)-A6#*dea?NE~u8g%KZ$I*Sn^b{!)3IwAPfHt0IazTT-|tP_gaDO#!3 zWZ9nYUunLtVY?i`#h5+1jWKg0zr$_TV=-YPfzMoR&8-n;A{djm#g*vqR-9Iqq*OlX zU(VlB%;`Z0OG6BfW3~UZF~|EbN0hDCS^Mhc{&KGM7=!rRS5a4;a%FUIn&QTK8`i?s zvcQ%{k}DDrhr?r+`%&{irh6F*1Y#X}116WEOzMh5J1QzP8QR5Hi>%5|?uxGM#I1VQ ztc`IyVC4JwlV+QWsX>%W6RrJRmRUEU?w*l-(4V_5<@m@q?~6z-n#R~b?ly`Oo2Nky z^j7bC4U1HE$oUnWLZ|z?k7znW`Dz;>wtnDgo*RU;N8a$(jINeK8#VZ}Bo8(ApGMpx zJJOKj74z(^2(r+Q@O8Qw?&df9gZo) zR_Eo`{81mQF&1>t#2(ttP(Mykgpg-!=SB>p_$_#a9>hi--oPRXhdX#Pe$e1*WUngQ zmfL1U-JEgAq21!33vCYU2kG7t);5>b$dodm)~eSc~;eZC62G zoI7$2CyTT=$!vH=L56^=tNq9 z?rF57qKOPEW5$*>Mwx*jCiSHO0C0bF1jLvNaq5=UN6si3Dr7JKX#1VYen&ij9eZpC8E2ZX60o{&L{PPoaM? zX3E35m?lB# zAzAP@nK9w7KX>nrA7CFTP|Jh=#7^B5MdcF$V7Rdzyuc39sD&{=QqZpVmqPZKH)c0`^NqIN<;9J6{<1sV=PE|heV=CI3*&siek5SEXF``pDm|K~-f$GY{u{#T9$&T1$<6kDa2 zL(Owrl4`rQ8IN(kkL*w$AID1OGH#{ylG3$n1bH!X9U8hYV5HMvng77^?}{zE zF!rd|F+s4YV^QI|ffTn^66@4NZbX2~i+qYN56Lfbnup2JXbb`q{Bzt*`u#pMqB#Aa z&0f%MR>auVTevC4?g(`jIwJ+xbyE=zU^F%!R=#@_Wbi`@*bQskn?w(q0{GGf2`%6v zQGIV0wg4*>Oy-DNdE7}NA7h>rIJ_Ttct7Cqj?KV+a0k{I3N*z2z@zxVxKqZo6S1}( zaq;6iorL6S%DbT6$C+piRg1a}IGGg9o!d)4>htD0UtXVwCPw*>)#m~(sA<8UJ9S>) zaZr=`fvb%*tTY485hCx&Hldre)g+RMy0&lc+#m*Tj8jCW4t7%~cv{-7h1NT-wv|4M zxuWn36lWndL8#|C)O49y`&7kkb=V~(B%8QRKnu>qB?Zj%rGtEq=6Z0O?VX|Z)p%MZ zn3KdG`lIwk2h&%#S|JVabvhQh%36HHSpT2?lx%VQI)Sjz{_6=gv+cA_E<}&%I{5;Y zOBI-?GX7z~MS%++$57@~2=sV7VSV9H#QQ2>a2x#!odIjGN`QHU?LJKx@D)-1l_*D3 zh|#n2Gx-_)R!(tn<0E%eH+09p_1HOwl{b8YUcJL!!- z@tyFu)uip*z+uF(>(tRNL$7HQ0@pPJ6&@P3#RN1#ElLD3e9-_l-6XZCPH}3o>141| zwj|_@H|Q7&k?}^Dr+t6cop+avj zv?|KW0$)~$_Y(7Ibx?~~aNN?GmO0nzfW|(qP z`8zXm=m?h1mH{Lm$HF+u9DR}w+tSj=NG>-*l~4zp1bT5Z9B#yn5EmiUrNf#Fl2&7M z*$Wu{lU4Lj*+tthOA3)m5G3z#f;{{n$GBsy30O znt!cSm6JEOXo$JvM_q-nAWqfy%=AiAN;nkw@sj2fEiX=2Ai83k`yGGV z*j8>rbrk&x6)7RKtI00FprY~1kXX|DI@X($M;jN_q9~bena@WV z13;)SZ@NZvt?TS;9OHOYK1{)Un13f`d}R*|wX8Sy*-nCQJG1TBx)s+cvSO-Waj4a( zgGq-CTliIoeJ23#f}EPyM%LR8{{6T_%G*~~KtUfsG2; z&Or=`y6$G7E_QX-3U#96pgB!a-=TEPq=wzvFs#=xp>Q+8Zc8>7zR6meJkddJd*$3y z#?TjN3j>A+>spJiv5A|bcOl~R`~VCNBy%<1ir~_n2~dm9J>0dJ;=iBA~E+(Nme>T)gu_ zM(>Wl!;qwN5U+^NlPzl!%8Z*-Ase`5P4#a|k%~eosd=S;!fIFa#acIqv=PW~56cX< zSi`hrf_tPQ*l==hz(C&4wzc*G+gR=O?N2?BlN6Q$-5V4RG-Ji}U8rKX8X!y;Mnt|s z*?Z2{51mI@g8J0)Rsu6qfUrGZXNms@4Bi}~)!Di3P zXWYsz{ArwisDC7$A4E#LWOd^>y1Z|>9!)!32Fwzpp1CN4H6^140G7TzVZ2>+lVx7> zAaALAw4~rmLN?;CM#WIGCt4u2bL)7VQ@V=R?qN9_H)m^&!M$%em9S2~CH6tUdN4KB z0j|d}y&`iw9k{jjceZ3l0_?eIeru%rTMldeksk4KxXGD#gR^7<@AU49TTb{E=Q6$Z z83sVM;AV|I=2bl0;;jCJT^-xBmK2(tf65NN#aP>hm;taObn{8rLRbqdw3f@VY2&%t z(7ay%8DH5BGHflJXibs7WEI{<-jID6>LtrICv*;)%%W`JP3BQR5{lXUM<0%3+SX}Q zi|>Dd<#QA-exwyW`SG!-&wEg)oiH@Sgfrj5e3Ir6^{L8y1))SQrejpdWdFQ?#xi0+ zm0Zk?UQ<$|vQDWrUexo|v=Sj#P-uXfW_Z3&vXxS4BHk&iUzcE7sxFmOH7bYF(I_7y z)Dgmj;=}k#Lza#?kSn)BsaFxzv6K|?{kL4C%hL3b08$xSlp&jhMT2cLcg=F2kKqEc zvnCdiw<$i#r4!o|v}`a}upzvq7{0O8u0`nfUr~dSmT#`+Kj3JuFsvXB{0drcjc@VD zs)DY{9qFyizM#1T4)me+bn*%wk@cDx-dpjTjo7`m&aNCTi^J;}c=&z@j=={Rcs%?= z7SBdkYJa7Tf24$i)^K`tacL=fi4| zfgFbzb;TT}u*D{~_)0XiE@%p#kS5>>Z2SBakphR(X(TnDTgv~dyjU!z70;TA2<{e9 zrwbLJ5@=`U=70}|9I_!Nl^IjVG9A%~yTdP$?U*{yrfiQs07?9@?>Y|A0{QbF)!c0S zKpK*b9~k4Y?LCP$S*$6iPA`x03)7hhH`>cWCgr9<<;5v3*)pt)^&j`k5iEjic^}_1 zF{36;Q~g7p)fQaj8Lx*EjO(FF!N+H5xq8Ex>`g_D(-fSYx`1l)vv-Flub;gA%k#G< z?_U1>Ia++~AD9#Zwf)=6Zw_BT!ovfLTxGSlAHJ8p%YWuoAdK@$^$pJrowwy%6`kS+KGlI}t59UF-XcgY}>D<6;|s*h9$+8|~XtkNQb z^%#2xmrdiReo38nv17OM7uWY_&t2RL|3q;L%NWMIC4xpTgfvfirIiSO(b)3EB8A%u zhJ78I7;$~|(XaQC+*$47;NPCFkOamhl0G`Fiwo_$xXU%6>%)boJ#W#eNYuejc;x@@ z+m>@TRRAGvYl9EbuoF64K*pS<$f)r=UKNWWB9XJN@DK!->mzR4@j(RIW%)5%TFfj( zhJcD6;(tWzdx=4ge9-aT^Xl5qKM15_&}|Vkj;wJvX?0qi8q2dKT@P^viMksaz`@eu zDaPEbNIY;dt2`P@mp9DXCui+;r%xt6W1tg+AJCDS1pP3SuU<&oHxzT1TecMQ-ngY) zLA25;j>8j5LfQNciotZ4#ev+#E&P(TJ*uPs|yKAN>th z^%5@S(=tf<_F_KIBHy2;%gD!Koz;;*AjmKcM33+zV~Gb@sVl+fXJnQiet+}g)w3Xy zmn9#b1mvYz@1z;`(L_V_Z+UOKUYKaPKp$x5yT9lvNO96aVy}4rX@L&)9^76k-|-QW@v_;rEd!{A@{(9ku<9?eNsW%)*D>+5eJ zCXmfdu$s(aVlS9suOhZ}weO#HtlbJ}Uhsf(Kc8pmYL+fkis}S8*t)3xH3ogU1Pb3m zq{Fx-tJ>bFSull9Q28aa36EOQ&IV&h?7qHBn+Dgc@6d2DuInX5C#`hWrK+Q{MErRM z{>KO$bUUNt26It%5W>>rGV z59n2ob;{#7%~D3jEvG2nP;{$laivDBi<(%@0(at>){Y0pj7SN@-_b`{*Q^cK=Co$Q z;Qp-RXVJ7QmIVmlnTRk!npia;-1y#p3!ldDuLmvfBu8oc!AAL8$xxKpM{;6`Ngl2^ zw9zBAX-1)~aNDx}7o;~#e$mk>uC9Ia!!egWa*HFp!!O!p9&%iFj%!78CGkd= zl{$U4URQPFN$U#Q5iG5!P#jrWm@iyfT-sk+PM8ViMOe;jXKqHx|xJX7R8XTmk ziX^jsZYo8EN#Kw<{1zU|wQ#nYNT7<32y!CAG4-`i~7jfTidUA+fou; zqnsC(iin1_!k^wl<<|GG=%omFMoX#PABwRWVd4nuyA4 zVAppxIc+(;f)bl9c|6KB-Wh_}S86_B@f#Qz^CjHf`c5Y(e692E1r+vHjajITy`^1{hlwcw~dgIkJhXj&rhUZc9n!{cuJ z5ie55it8Yn=5;<$Q^3)?%+Bzcpf(+D1A;Gbc>>*(r^ZW&8bU4i0btnOa6qs|1IWx_MOH#iQid$9rC z++^JG^}BI}yQ%dgZPg9pYT--{aw8ppa;wEnZ^3(m+T>9{{)`Pnx7|Ifvp9vbbe2!6 z1`^(I)n9MY;1j?)6dC`@-dCd7qUNDRo;i- zA1S*yBxFJkhYelp%OpLc7_2bAI|8Q9T>9CQS6%u+)*hbWocBiO`E*?SxFb5FlmfBH zBA;S=;i7i5RVGL!SC=uZY zlQfLW5ipQ=)!(T*uW_pIB^#=Q*?-_`7D?=ntO?Z*4h&qg9!ThjFNPID+w%;1BX1w( z>~z_xVzcFx;|ASu0;x516NrhT%ILK_;W+nm2)HO`$2 zOT$-C`&M$LYiZe+v>HDWOOx#z;GS2&owtnKGLDW~`Fw=Wl4xrMBmBDedAIlZkDtF7 z{JjOo*imamMJeg?E_{AP8D8{s`Z#lnL!KXdT>3b2U#YL}^G13-Og?Vlo8=97X6-9E zF-gvAhdZ`2)Cl|hV}lOUr+jO+<#m9~X;?wgo58WP{J-qI`F|Tnawz^g|BA8mc>y2+ z5J6gA8(giU%WJ*piYQ-U_?RIu1jhm~2nHlEtL67^|JKn*^)Um0ls8W7eJ>U@?OT-p@o9Wl=SD9`G*Yipo#^IVoye;lkziYEI;1O2-&3t|R-zvW>PR+W zp5ddXdH7d9;f5?PzZOuvOo}U-hV{~q{-8YzllEs$_r6JLgGHaFiL+HG0CmkbFMlSbzjC< zS2MFh%cFYC@1xBH2JB62XmKQqw*C=+lt z&QrXPF5v3C$Pj4)TWS}!)IVa&Z7X4HywiO0Q;ZcRMqjH@LQa*14;T6+9T|jG|Bs1S z=?~l1JdNqrCc<{%+V-8FPmoSuR&ZLWdlGAW@vn(7kK1b46i6fgTQ-cFiTjQQLu%RF zFx&D3CAC^NA(dUH+t^x@-f@YQ^u1s~mdi53Gz(T=)O%M;@Kp#Bcp$Nu4)0G`9o8s5)Z!O}C)AIZ!wQYASegh8i zt8R4kT#0Cg;IpS0n&(+-y_SjGV>ZHfX5d?E`nS2ZX=7`wyK9RauurEm@)_uFLSM8w zul(=R6*aRV*fIzZH*pk>5&*yW{Ry>w;BK!tD@DWIWc8=_)T@jB8;>>0$}YES)_-z>|DjZW<5c z1*&C22(*mhF601U!d{#xVn0#8QI+x}T|Bv5E*VW2!!*G^{wP7@qrl|?~y()BK+1)V>)2koXV-0GS;D^$s)bV zm({Upf_kJ4w@6gCwe)tr;b5}1Ta*Nf({(Kx#?ehuD>D^mrhaPz$8no<%4mQBeVU)E zS?QqtiUrKB6dn)DH*42yXOhqyoOH8LlG0jq#=Od2rD^QkVJ^_bHIKjWa0k8)< z?w;%~ix(H^#9AAwkV@*aa!H`wF|?P%NpPVqSl*L4!xRc`h$V?K_8^KfQHnAPq?mh>8;1LatH)RCy>_|-dvFq8iNXJCsoYWeDF>fU2he$`2YXVkR8F(930hthX z{s{K)toQcyUv1qRo*~>9L*|xn9lFRC%9VgSNuFYaUhJi60X^5CAevH+LdA;SsJ=w)c#iPSo8ewS zXv8ZH+4wRSe@oLpK=AfP>Ewj+G=x? zNg3q{l`$>QRnQJdPej4zF!n@A4D%l9y(G>1!9^Z?9Xbb<;DpY>9NgHB@2CDJe?MoW1oF`#Z7Ef zGd48>a*F^`0m@!1lf&iZI4_U~Mfl7(FF$O;Wk`(ld)zh;PtoyV^2--9>IC<}A7KFS z>x>cZ=%al4YmM@6MRBngEFOQ|Vm$v26-58>I7bcMBAsA+3K1Od3U@0UCVoCGR?aw8 zep4;F$NE1{XJPm0;}*Sto6a`uJYbIW9lmbcZ`SYZMYaup-=gk;q`^`?do?Dts}EZ-^JOt+zyyM+i%703Ja;tJx1H z^XX-In<2cpT*k*h1=|h+QxB~l1tlno591=AZ9k6ma#_BC#PwdkGNRf@}b&HW7&2>iVL`# zW@{$I1KMIjRMlI}MUA|m8t{^#o#f?r=$MZ$auq$qt<(+SrhY0%+`HRMrk~AR7t*k^ zD;eliUFsjf=lt8iO%6B3NncPFm*lBG<0kQ&O=ZSkLryM z+p%3{2iwJ5O^efC{OrxEml&Aik=9rCr|`4QncLY|$auF9_;h+W&p%}4^BHD9@#43h z7_FuQBvMUd#MRs;Qs>#hJ1B7o{~``oB78WZDM28zpZq4laFs(0X)9qD_&(PjP}Ipu zCwUN*54gNnQ=YC~!^SwMO`*XP)s!zFrF6>#MhOFg6dLKOf0nN@kQ!!_Xm%Iw17Yc( z%&0qYKY3-hh=)RLkbhX~BZUxddvsJ#!>!}myka-flb+jPM%|DC47*BrUnt$#+Dxx^ z>0zTep0nIemlD61I$8U{#=WE3N4v+=If*J7k)RpNAcAFZT$jEq`uV|w*8%+MIq}`(PGokshj8}bU%6n z-M_n9sT^f!QT0ibOV0qy{($f?cZ6m)X@KJ^h@FCN;#<2_IyrML3FL+!GELG+Vg1giCIZi~#kJr5!_Tx$?4`yrS6F>7(u3E+s zZWn4twXlu;2@%J4zUcW&IQ0(UxNjVu1-irdrhp_V0ZYvYbt91>UKCgP+A#)75H9-% z?fi|D8DgK;?s1u|(NlojcK?c|G5Dd~z+T*WUMd4Y+nYanA?;I4-?$``Di2(g_+ju7 z${Dz3CC4xW_?H)bF(32(bS(jRVazq(5KW-jK#5L`2s?H;C-Iq&9-yXceXG8lA2e3X zs;s6alr&f62;DWcxzG!Htj8QE6ReuUn*hxhHXOejtEp+c>mkMPLg0p>IO}QPq63hG zhE1+&CR4!mR|o6yR2$%_Ho)_@4eX5?%{G89VQWIAZ~G}ZFsObU}p6qhfu`w-VpcWLy`vML1ziyZOfV5BL{!?(C*4z4mxy}7 zjYTF3?*~-hQ@e&(+-rtLcN}J{iO;oDL(KCLwtg&{-XKtToPeG7^d(2U0?Gjp+xA4# zdIsffrZ_`8_*9o0w#4{rR>Vy-py0P*qX8Xhu+FIIKbQPw>@#Y{K(bq81p527_pF*b zRs2-m)EuLx?^3>q48be@-y(l7D%z5<>n4o!x2+fVTNZcqAj%1l(D?9Uek3Pw3T4Pap?9n7-pLscz!y5CFgDV)QE zvrZ}`4wm(7Ip_1x?+hl-F;A~VFZz4 zO*nM=C!*NX@4z6BoIxG;=a=PqTk3U0u{Ou))S_9{Vpy%OT-8>tB9(9ITy=ni(EVB~ zl-`g+$$B!t%~5-|UY}<(?cff76|;=p-pDOGNhu#75Y=oMz9ZVr6gmK!qW>-Dhv>PK z_HJ>L3!bygAfn7?XLC`*y8+gEt{DDPMp+M)+=}Uxx8ORl*=&%Xch(zykmTIeqT<2M zre-2q(y}#b>}t2_h4)?&B6r`b1ny=L&nvx1=M?O-VcB^%kQ!sEGGaw`tu5bv>r&n1 zCFfa;QbMV*JKMO%zQ(iQ{Hw~;+Vpz~Xk)evXIaiZmy6V<&1y)6 zLMbK1j^`BMY5O$-Gtgh+vNYdGG<2Z)Cvi2mYJTNwF8blQm~Z?3`9{Bg+TcWP%$U!I zgNOUwnz%)EQENUQ?hU?lf#|>R{fmduphpyH=UQ zGFl+@0TyAfi&Hk({asGkokA(8fLMnrLurVhCUBOnzgb);0>#jf%S^{ha#t2UQvQd7 zcmyT^s_u`3)eFINy}-anC@Roox46L;Ws7QE((R;0F>yxc1>W+^U6i7!mGrO1#euyq zaVaP}**M{lMdpr5Sm_r>eCm6#p7?S61{ezm03uNS$dDA&#*Hgwjxi=|)tB2yODYJ9 z84dx`>kTV30Y<(4nqE=CgVAxj)V@c4pg3;e1i@hMX6P70ExJPM?Y@9!AG}qKt=>if@&i)GPi7=l9uSmXg>IKrgw-t6*-d0la%5)I%+%!TH96#R3o5Jck6(C6%;X_wL85to#zJEw=M z_EE#=L3M?|#tk!-yZfXM{2*Js(ce4dEZZA&XrJUW-&L6&%JIR$-9ATL`#A15r=CKn z9#66B`C)@_UR=<#8`-GYcLl*C5a6#WRqT<89H>Ug3E*jWqq^m}A2zBZ5pZ`Rv85!> z^)Mc0v$2Zy6vO35;!>Clt%w?u%uw-c#w4^6r09C|B?(kuIng8(o~TwZGdF&{Rqw(5cy}g z!Ssbe;^1S8Mo(8SR3Tu@Qx$oV1D;OPQ&_qr9W4sD8z3PW%M`0icV^ake>(P5<`zmk zb{zW>2G5Heva_htWX`ca6RmlD^6jJ6^hG~3#vDZRPRTmgQm}jxOK!QyH=^a&1wZd@y-Z8PIX^0 zG8BiY`9|qdP*BH*wIr{FDE)j!SvJgtPTy7OpRRKa?6`Jb4YvVt&5gf9eiXOep}2|M zc+Zo!dyC}YO6nQ1A;ywxHy3?KQ6OyYd!uv$#fLQ zOHmLp4;^Qg!x&UI5u@Ub(!NGUaH}k(ckl5>-`xoq(&a_QWR1c6F|K+#MVSQU1f~PE z$S{8n9d~_PWbT$PTczk$KRM)Oz?URAGP2bun=8vveg!(sMkzk~sxTm07mE8=mXmHkt9WqVKgZyO|@n$ceJDr zLnw@>S-;&@{Vbp`u_?Q@3!P4c!`(#Lnq}&noCdl-Rxc7!J_q`11&Xnme7cRygNrz- zyz!MJx`F(-vxep)wK_4=l8I8t^SYz{9Crk(&5izLk)34=$~j^KO^}8fGb?K-JHu}$*k&Q)h7N<@t)atsUz!7q_qEADUcQDC8k3!f z;KAixigc zij@qU#^@>Dd+4vXt_cC=x~Ti!*DM#U__C6OYCKvN#=|E0$+NB0fa`22Lu_~qnd_IvB)sO91Qw3lCpg$ z7KF4Sy73KdKw`J>mL!>xai(-inb!t$3`i&c3kP6%J>qptPd055yDJDgy&dB5r(`}Fu-5}X8 z0o@p~SxZ+iEE^_5Ter~g)PxG#mbREyPj;edvqO1ghb7)P?CY)z9(n0?o|Ap(*weC_ zcZ%-NQcSl;8DPY_;AK9R6rh&bv&7F)kzvqv1W{srd-}eJ;-a*kD$cR)M=N-l@@x=n z_)Cx>YS7&WkVFrx#~RrzEwkP!rWA^J$tl>r49-anUA0_mlltbM4Pcn*wBHG*rj)H*tY!@M$mLTu7~T&zTQt-|Af?K$qsW4>EZPz?1KlF2~6yTxvUqcUAxvhjnDhMSO~ArY+Sz1uB|oHXS9XjUaUTOcQ9G$CANpj za#aobjTZrr^69{cRo9;9gkZ`)WqtnSoO4rM$%U~z!ynP#|L*qJ`Az73_Q83BK@`<9 zAdSk!;T%qoyk7@qSlDuGVj{OU{I0Kvd|YHp?7VNJ7;7eW_cHh#h93Kh#A#5q{F}Pp z`1brc%BOP>*B8qt8AzmA+w`jIF>@4XUPCKo7(lxAX6Rs$f{|&Z(>O+lvKtjrf-WA@ zR#gfFWv(V=5WClmDB{Tc9Xd`dI?w8^mdtnc%qd-Ah2>^V|*yf z;R@5)e8Qe+f-*BUa<+cR_ySU_o6gkSpQ9ehi;NtcmB+iDcm*1rc@WK7;<`u9hb(s> z`=OuaCZq~o9jI5sGAtDtnk1L}Z**1;l>rIY;kU^&K?gBfCVKC|ZC;`q2PWoROdOLw zrN*`WXz3Yl*^bb^ZTJ4!v62&m;d_?BvfF9!?0Utjx=nBCrzI5b55s)s^{C!yqUN9? zG;jvkhv}LltH!<`uP)Cq*Ec@L<5OarQNqy726bZ-kQQGIOr3c`mEwBvD{`mg!!t@~ zOQavz0xCnuaRC&LkA+D2!0)tfC^%dql9nGS!8<<0Dm9QanLAlJRA!j4C+L-~bKuc9 zmv@r!zIs8|M;Eh{Yz%yf`q*-8z9YlfdTDv zp6rgu&HkDO7D5c2dnpyA7y(qL8>cUDWx`Lwqx@>}`Vf!0^W}0reDL7<`nrF;4_CLz zgT39|-3K?7%y!gpJik6{Q=?r@8ZNdbnPIx5gf`Gm(CI%p+U+L0U2i$N zV*cXqYZ08Z9Z>+#FDA}K2FJ2V-$^+m8p7ZHK4^7a;_}o2rfj)LXC>xR_t|R%sezTq zErM9|Jf9vK-)DhCzsx4-s8TymV@5VSx}1`PvP~ab7b$#SwFk*A{J$RlgLfCdPjG?h zwBr61w1kpTsi}1BC=Ehbpdjx5)#O=$k8u1B25SNTe7h(v4rzxwb!OniYJ!0b?-G>JkzXUgyGp7syd#c@Q~H}+->qmf?dNoRJc%)=pgrL{u}QgRABX? zM+JN35Z-l~mO8zLPm@^n7Ew(70dE{N)v)lA!xNd+XxGYJ1zp=I^TX6Uk9J4UcZ0ocrrdc|ZEXzK3tkKG<8-@jzdNmoZz=5la0_xh)@zaM> z(Q~z2XG-kb<=wxZd-q{$hF9OHE$q}-Vb|KGiMmnKG^YngzZ{cNB;EyG+S%R`+>+nF zEW}auEWc6=`zCpRp7C>8j^36kU1vB0C#TbN_Q4c1=x2}Cl2{}PdOe)bmKjUaHIuKGv+SCY(L0!)g-{)wcIxz>yg>egU2LVU0aNDK!}QPc zMY*IFYApyDb7)H=>mCHpK=#2%(X8#NiZ&AKAubcQr%A&z?19_(qS9~n?N;B!su6F| zHRb`v;6rg|qcEzAwNP#s{n!oVf_f{B@}qE>Rl4mg!*YuwdhT{Go{dqUtu&y>8zH(Qk%8hiU_3uuBY^D6b+@bg>zt*9(b4!q(aDkhAGh#to*|hH<`B zHL;_9H5F|UWX^fKxr`_e`xe-? z{aa7cDxHna(fn>>^0ZWmF3#lJ%7;};B{Rgp2c)YCNt7XP+t=#9X^yH-Fh|Q`&g^D9 zCP^Bo2}s}~wvD;q_Ed%^&Cni5LT*}zprh$@qHHo_1E!*u-)(!2^{2S_FHKnLTTHmH zP~5B!TjD=07I0|ZWsJ)I>rSKOUXZ?a8^R8F?(Qe!kR;H%&Oo6k@%eCXci#>D>d7_$ zvCk1?eIe%a;X{sUy8nBW6Jw{2w&K79Ec2qw=_)l($4PpMf!ye5&Q$imae+}cnI*kV z$CyfKcdu(u0CnUSmlsxx4}_Jp-OwQ9W>DsTQ@0AJ!WZ2inF{xJyFuJ39A&ad$9c|o z49iLv75%9z(7sR3$O#DAzr-nC78I%kpAm54T4HCnOe8&$1F-Vr3zkIo9&!WYbn!ud z8|-T8Bm(;4_j=J-=jinm=Qehi&;ak%KHmK5!1XwG0o@Fq>Wj9Dc2s-mA(iThxtE`) z6)%n3nq>`cMBLa#i4D&@K=YYUHiVz$?&}V>O`l%Ox>j-U2KCufrPo$_4k4b4yh3S~sD5cVkwg)YSh7?~bce}6+d5WO0x!7|g@c01H^@EIMS)w~pNm}QjZ ztV?G?B<&MwbQGNJc2{<$D;9(QR>%UgBx^U^L=*Mx*v_dK{W_dk*URVe0%%a9(?YZU zEz~}0X@=%6SB=Br-bt{hb)rdVsV}Xrr|M4rlI&XJ90ldSz5%Cx22;b~OyBa4_F5_u zFC4lTXqupqxwhyUpb`KB2|$lB#VmWX0N&L^&PUV;AUc*$Go}M9+qPm&+ajk`F8>x9 zY4e}xV*!wXwZ;6L9#72R5~O;Lzy;vHX!ae#moA^g(-gfLhfnqUjvGAM5xcqd8&mTpuir63 zJzBFpQ0XBm;!np4;;c;&pVh@TL8~}DH5i6i|4kJpAkh>H3<1uIx>(s&o~hv8hf5ecWeMG|93H{S)*g4cISmu%GFHuQt$==vhDa0=uW&sgQJUdrM?4KB@X*@RF!_@3{KV9RW*|Pg4du#e(Xh4 zh-K5*=t5N|HzBCFS;2K`fUy+h1#2+a!%wDT_+_6&=7+ymkvSG$VT(i$6cGWJrB2GF z|86XQKU<{NXb$CqjQOhuS|{a~R}+kA_5CuRmQg^oygb3%-pM7x=z6!xslhejjGf`L z*3+mUPDl*VJuuK_KrdcDKRJB+`00z+f2BNLKDNGu;5y&Fd-Lk#{mVGGDin}U-Zelz zd8adWY;cd@Y|1M6AEyU$p`q}EZPQNJETk1xDjSB4tGjg`Sb16S6E>l<3`V41sJBOy z0b70Vo?w_@A0P4C7wLygzxiSmDeRCHQ2sTmzJ=~;u5B9blTx>BA2TNsv3bRGn|4jq z1+G=T1XTPs-44siL0Kp)N0*C|e8VXsnxhNTpL!X39bo!Y5+&TTN3`tN)CF1YO6zNH z-*b_{cm`{@EzGKo5283sxdnDt#+QuEbxH%sMV%;d&3`|pB}Ji+PwpRB5^KAiF!4P? zFUsbH^`5BpJD^-u2UT=k9fi8~aqfjP`hu zX?3u)r3n!VCPR4_ErjR2DiSe;=YSK*m6UNoK1os|cMkK z##E#$zrCvZ7hxT@*RC(VCDORrY}V|U;C8C=4*JOrL)NC+XuBy1y<-cbRFgj{RmXIM z=&x`LOVM0y5LO!;+jQm8o2|QjlqMs#iv+1k#vw-A6WJQzz) zeK1?Ul=EJa%P3sw&(nDZpm<>+I|NL=mY4JA;|Xh}oh^$fxuV6?T43&WdqrHVg!r%-Rp$V5el1izo)EYJg6ZuiV9pyWONqmK%KfW z-l-VEBO7reLtQ0?hO{afykzpWpk96%E zhjhi5n5A-UkE4>_EW$_$*mHcqnb&F<<;-Pq82qkBgaz<@$f68gwNAfWZ!8t#?~>ft zOx^zIJY76qws*au*U1As6Y1);d-et*yNvFNyC(1I9~Cr*2ecdAiHP9K z^R+qW8!uCe{EZVZ!L|A>vHR}`OXKcJJw$rz&nD9-cj2;|PoMD{c#J4n{9ir3p~e`$YI$I@U|J_Wg!4xV?}`!~nG2Z*h!^mR;c?TRKtvw2k8ze!?2B#|>kQ2fCQ$T2cT|-xhc2}Z8 zxC9lJlX? zr#SBX9$le&LZNC0345C9((b8BeX8uI=}-;op4OuusdW`5rwb40$-4;9spbFzstTZ% zJ`^;lq2fx}wM-J{RdYd@N$unBJh4Td96lcoYz}(=2`+pZf#R=sKYhOY;FZq2@q9k~ zQ{;&bzXZ<;Sn{QIx}itBFaL;=E?@ELpIn+$!+c1`k{Bs3MPI3APXMEOMw2er`Bqh^ z*XSrsh~4E@qtiN0)@pQCO80|9`{^D2T|X#sWQMJ3tqwK1(!UPY))+W72FI}nbFS-Y z95?82=otM@jn>yIi9l{?ndu<)$TB44-n1?8pzV~~67m@;6g-9Y8VW+K~iF>zXjP9VPdyDpcxEU<-i|hz79``SoqkeIAR%XjK&OtZn zjeH{~V%%PSCvIB#4-Lcj7bxE2TgV1&dCK)IdQRsjHCEGMPu3vRgXB@-EvS2&KH?s9 z6}UCU0AWC$zYxsxPrpYCh#FTX{%rgmF6PAypX>QRS9Y1+qpl+K(Rp@}N{ z-&aTr@XY>kX;D)(CSz>h?r%KwZ$0> zzMv0K34c5!8vE+^YGq@*VF)o;>mS4=Lc{axE|xB%C<5) zB*Oz^iuV|ZQ(p+yp)hnz0jW&QE!>FeZYjr#UVM z-|Q|+iXx5%5%#G+KLfxKY`1h#^%V+>X#t3gGPBi@FA}3-@&Xy8g0N4#Ti_m8WK z<1cH&Q(E#yDhjVJvceJm|Myvk>F@L1e~V^iTL=ycT#ovsu#eSK2Yg3~{z7$;Z9%}P(<5O3&?f)G8z zFOT8=zNS(NAfguE)*9+CfU4niu732Ub7lfe=b9^V=S(ftx%xV~bLq-k#W(Z$W7yQ| z7PD8(=RMs0?y7E7J6*}znz%W$nBgC`Q%_Zrt;OUtZSQvJ|MkDg&aQjFV)DObashEMYEftag90Qfi$g*vxAlQ`TjvE)U%!y6 z_%0pim!%Oh`Qss3IcX#cN4A0#Rl8~ovN9(P8$tw;GCoKZG*8!wou!w6ds zpoH+Q(@E5Mkh5M2y)5}J_Oe{bgh4#W7uH|x#Y}0{k3~So6?=jkEEdGX0Bs|dzF-1q zCub`3PZk-)*MDaLjtDIps2+Db3vgf)m=~eEoM)84^Kxvcp?#57r?~u>kmZD8SfVtl zVmar)RkCz!dc`lYZ>MlJnnxD5o6h5GFos9QFaE~Og{eH5NlJ9<9E5O9e)Kd6T~`vg ztNVO|tM#T1^wgcF5|8`kjgC$2^-;Z{Y}nBaTIo9b;|>nNnk0-NwnK5g$G6xrl%w** z&G^{hpxxk{t&5C)!mPCMDmOR7Zt@xLkG%W=({J;{1|b|e9%^;1l>S*t^)2^r|3rT^ z^P)fax1@^MpDykjmgm}O`p&ru{zWvjv?2rp)5n|Lasg+#_@_e-w=O z@^M>nGT&Rl?bt;+zhkfBU6Nf@@q_E#CTJpcDmtY95>;Cux6Ru)zg;u5e7rYv#}v8P zGN!t#txily%g7+{ikrs>so4IrRgpj#%fMDW4i1{?wRi7! z7!%~0OV@m^s`^0Sk`Wc*DhHr>R}2Hz~@qtqYlV6a;y%2bFZp= zwv(>Mp{xT>A#rOD^akyO`zD5lE)iJ@H5c?Cwzv0(+xf5TcD|92zLDGc`ruI*2;A=x zMF+P%d8$cgtzhq51fJdIB7i=QD#mpbeQ%-uNhoxlJ@x1hsS>9tb_mV$PgyQePU-akX&txWlI&r=wefXbZ zt(2T5sGv16btUO4EYVssr(}`{)tD^7|8YI5(u$WWO;pewf!gg(h`O%apq2mz4kk(c z{lL2-eP9nr6&WTk-^^WER`@Euq4l&zbN;o5iL5rBf$&6=KJYCk)=H?c^gfF*6yg8aAt|4=7lf>vXtbSf1AMw7_<@0kwgd3v4TFDe8?*r({0~X;6zAX6Zkj>!LiISC5W4+-aojkLs1Z- z%=AXbB=bZd?Q=3I$=ArnBTIUTi8SWByLp}_P#3+H+6srQ=GXHaQQWNQC3`ZJ)`iKQ zukHiyv;@yNIzd5C8duL=A8elLe-)3k|#7W5zx9`bN#uq{p$VrCvRW5t)NntwId5 zyN2#MZjEaY!-Z31F}av~Zm$m_d==Kk)~;Wk_!6m_dZjcXy zW;u;+TrcF5iJ>#$07QCYsNks)=yW`O3gYkE6!ZCRRs|b)r!6$Lsq_jigJrBjH;}*T zP#miF@ok#@E8qK~Eqh!HnCRu4)FO$}eu&T*Q1r|yOr&7H0cN$H$_c|0_yG4?WS2ta zTaUOa_eW&r+;&O3luRg{JG!$k{R<1fN|I_WAAR7AG8ibSkXU}ftoO_G0}$jg!Pp{n z$sv z$tL$Kg+Ht`O-I>zF&+CU(eQU4Dr)M>2O{`K32V;dLOv$}X}*crO+^VOUF z^g>7IY?f4}l3RN58R6sdCj&B?=J3x?-YuG_4}lbP&*?0W0?mq_pctLcCQk_q1*_`x z(YN2l2iK(^C<(7Vn=MxX=_V8i;X2j?KM3vv$myRFKYJvV;r$VQgI>>_Xp9EB52xmH zoCXY>oZ)jKF9K?dU=ir=SMay!-rBmCb6xZMxop%1p3_*{QJugzo07)+ex}z3W}lZK zl#)m|2kPyrzfw3*S$ppZManOe2_pKQhhC9-pADnTWTamWgfZJe6jFB!VAay!=W#WG z)9z9$-w5tIbUH&2TYB9nggxD_GmN`*KV5zL?*FQ@uROdyA?YYCw6@opTW!+q27L_3 zItJ;VO+R9Lg*fd6Ui@^0uuaiFjj9fa1`GJsYl}`qg*a9TCK|^cy8J*t0=BIV*RZyZ zW+L9b3@V}Odn4&Ku9|Y0;P>+&+^A)*Ds%64+<8+J9+UQip}ARu?YunAVcCUFzz1u}zth z8mgo#qiMU&XCT7#l_}=qS$;tY=)YZ{);%iKtPj5%k@dC3$&ngTS9kPS#(Q;_=F?rx zgfM%4y~nmEO70W3*Bp9h(?t%}b+_v%rJTid@hJtxmS~;WJyE>h(O5P^+k=U4ItTOePdmC>UB(rGeG>-uh!UOB2vCND#_I z7iJiLaRLd~+EE*Dhs-l-*ciHv`-Y8H>eo&~Kj!%GNxpbvxU}cX%Hh}?XtdE5kNGlL zNIl2QFNb#XKpF=?j2bmxXW^HZub?f&i`+L?wve7`-nH!0c6gW?VNqPXV3vs8 zXjQFF)Rs^A7&%h7HLP7{_$QDYdp)}-MjyV-C$Qa3%q^KRWtbctyQdW^C1THkI>uX6 z^|+)6IriZWe#T$x*lr2h1)eZCj>D)pPS!X^``N_7ZB}4w6(-XII~vCjMH6#rVlpAr z1;zJ;DM~0H7lzY?|4!+@3*vt}4Sk^`kl6~VA|F>jq9!wTQTr-6rlf#ip?Pg2ZH3Fz zv@B*hHo5jD-_!u=HB8*DG_0>(S6LxgS~)Yk|1qZfv@-77cDe!+JLKtNHe~>DB6rXA zupYc_53W+OP+UBPQ={FHfwz%M9^zo+=c)$0a=`;uhI}ws_k27=uCwFX2`Iidw10ME zp&VDKC)e`l%KNOA8RwnjY(liv@* zykDdva+y@{L8zvs03hI_-QzxebeEAn@!kL_fPYn-*z3`wq}DGOHxy;(GWge|=py;m zm)^)GYh%53RF$nXRJXVy`pV1S&OK4}Y-baFQd8 zCn{b2niKWl0ku*lXy!snD@BdT--_bmZW?4)yLx*P_SKqo%Tc=|@nzX&X&Un(y4`G2 zcgnY;h(Trjn#l-A{(0&ue{WCj|0AZ^bn|x8ta8Y);-iQ8K_WB#gi7zUK=Jf}+Q4?x z%e=~#szht(e7ej>ERs0BZJq(&w(B3L&+o|ZbZX>C3smxnxT8kucdGgB!^*L+YZ&sY zRt=-AoHt!K;HuUqR=cxmvqjc~uV1H$vq&)=`aytqljAOqSL(&>*#M*9$8$Ld{_<_U z(xI!I1Tcaa)J-+|5HB*ltU?fuiRP~wZ@*XoZLQX1h-hGIU$5ymDI%`PO6!U9=D?eo zXVomd$VcJqz*Z5jERI(ALGnc>)DhN0?i4s;$ zij>k!B*Ug&qX}hguQwOeHtBV`TGwm4xiKiC-Wc2PY8z%SvW0?8<3tqf)@(JG3;Rq? z#x?Bj)bnXKyy1?#7KirSThds}u6xaT`Sawhn84U58OKI&8+wbGkAI>OXsW9n!A(=> z94ABwlzjD7O~jnfhkJWp{G$>|=@=)U4^>ayBvU##3-ovYt1z^%Of9vy|9cJ8bF1(( zBo2z?hcz)}oK$1F-X?KnD5j6dlo{fWq&{nP`fhej@^j~B1UCx7fs?fS2afpAPFGH1 zZ71XbY-UQnek6hzD7}mX1Q_|)rD93o-cm%=H_ZW3^EdQIm+Ocz(tpwFRO?dJKU1md zKPEx}E4`kQn|Ka;*3Y|c-TbX7EYFb)&naI@&(pHqnoRe`Ehog;mLb&RVbp!AZD-+X z1FDJ89i(luW@~=u$96 zyv&wMwIgxvWx{A-A%c#I&&cQA$^1;m!FMA4vvGE8c5`$QV$0;vf8aVyD&IUxJB&K3 zsL$b%9zqaS1C6^%WauJh1087nUtCp$I|}U|=z9ig|G^q_718PiTx9YdCK~4O&&lcK z#XQ)IRHRcByQA%sa<1)U!+rAZtqStbrOB|u;!y8K}_id1ofV=ijyK?N}Vm8O%+hGWZsEg>m zWKT=bZsfxa5`*3)5ojz&!xw)92#?1eOvC8gcaPM~dF-e2%m-H=n=*`Tp8U?q9VDO%dg_7RrpR$1vE)Rg z=jk>j;p0Lh;SBX!Tk%n+E?{ZV0_ZO`ahW}tEtLDcK5S|%+^IVd{QR|-8E|hPG6U{W z-ayqb{@z~~Gxgyg*Q&z;tyL_{^=s|HnR^P16a+q{Jqd zMKaC+XD0IqEYH)W$)a&N$3F0W>+(N)>fik?#z{=n*~$CwBrgxSQ*Tax(V4||oQTHp z++^A8@*-QL%WNzxOQHA@H{=loEJrJ84ujm)Pk_gMDl_%{*4fPdaVhd>;kL+r$M9u-5LnTHS7@?SF&(9RIz|uQOCC)KSl!HJUvO@tr+7?E<~H3FieP-EfW$p zW+wDV_&#k$IwM1p-?5f`w_rD;d@-UpHCij%nzNyI3s+CFo!fVgg1p|UccPK86oj$;P|pXDi5}|A$*yj&u&i1$7oA8oxo~g9X__a zckp>rDv_ed5a7g*oAhXd<&zVHufJO-2Y0gSby{~@qA2JNS?)MDStTFgh4zar@_^Jz zbDt0Q|HMz1ae)!azm!jrPSKwRBzphp4^>%QRkmrGWRY)U)7Qkc%CcMLJ>KX&gXH6v z9K06QUTplXpRa^+m;g%=Sm85MRY#ux``?q#bUVV%-LnCSytw(8qL%RlgaP0*uwxca zU;8Jhlg>Ef)W;joR21zRPLYU8*GitNq6o?MymR=uh=!)UTJ7o%^o>->PctkOB;V*# zKEacr{=It;!8W)pwtLdow3?#*5XI!YN$Y zYLk_%?8;V+Yf*V?QN>oa8r&A!Rx4X=wXzkiY_)D>fBr|U?9bb-tTHQ>783OoqB^U+ zWC1P)M(PhO%y>J|eAqRAd2@G`PRqHu_%C@+8JfYbIkna@+SFEEukn3QF%dHIY^UTET7c9p@AI3`1qpG8D$+>@$B zxg7#0D2(lnG>n}QffCp{l-~*H^>MUBfApsW-gbHpJ1ivaWEkUQi5nsCh-czB769~8 zXF*6Olh>CQWm`X^c9Yp9+r?v7IW&xJKt9#j_*{op<_ zdyCU5;;Ydt+uv6hEL0f9*f|`NYS^8B%_PPdUM`+tC=pxU5rU``Le;41WKdO(C#!;V z1W-E&nifMNM+(}S107R9@|k0zBWt^>j8AB=6{Ke;V|P2E^R^B;!Ez9|Sqw`Igajy( z|I$MIpT$U`tzXd$3nJtCN4DCr+Z0y#Gg4Vck=m6E{Hg%{g5=gwG-m*A^ory6zdT|j zn(m2pLc#GG1L4>u4b4UN19OqD-1p#pz7v1-ySNkkm*To-U$eP~$%CU;FTeZY-=4ld z{P*Ld|7ZWc_h8c9F@(yH|L;G_?(uyr(2u#-=lSS7VJ#XNmti0fKJoQdOBY$PO4=YF zR~@B_)wPM*aE2HSwS+%YRYJ+gx@fjm%;yEp;Ic?kT^q@IQA`;sX)}&-k(IO7lAn+P z{5oBX6Jt%#*LB~&dG=<=lX#J(vl2ZCU=2C{99Eu=m8uI)rmEnSbtXodMyEvKKCe1; z{4|GFVfsH**jJ32zA)i&Fwetmnw2HKl`rTRM4(d@?HvACVRCbPT?gWY^NU{B7g@_p zSW1%M<&@Mylm(J{IxhPCe#fpB2t-nG_6mONs+TXG*pn znjpOMm2qlOxLWEjs~Vbs@FmPvw48>3rMq5U&5}ru@ZbKYe_Ex zGTE=JHvGk`&zcft8IlZ@sfefNS9LUUyhF9R2AjC8tqzTr#y}gzSW5s_5x`0UYViYP zupAd2gq6T$0{6xdxd64Q)^Tn2?7ir-fa7rqzOBcs!Me$V*6L$7HeN zRda=kTRS&HJA4qA>)y(X4qcRc3oj$#9(;5YaIw(~>VQ3D@3J$k zf#o%U53wD@-Rzn{!u+#w(VU6h^&xrJtVNG&s&xbJNKx&=_fJ%x#&86c>;t7chnYvm z*~+U!lOV45BuT$7h!4G01jcSujy4HrQ`9OY`1^w6?>n`knOij)-3b}lu`bu%Q=0w+ zRmiA3M#XJ42wo^?TdJvD#*-rA9^Iwr$9oSCqNM{W{nJD5IRFDLr$CEvrOBts4}1Mx zP6IzJFt`EA4HLK%=*+>V*?D@E!!GHO{Whjxn-;|f5E9@=fj0^tIor+SU8KYt5B$p{ z*F}i|u3>N?0VEi;fK_&WeQgVjd7<&vlV9QeCM}CuicDJ_(1v~39u_=?l` z(4}V%{Cru#@be2ihT*24WMdd9XBR5$MTip7<&#+fcTVh7KlJ$nCbH}CkKNWm6mHr5Y)$`JXq_hCny8qg9_W9! zbI=3Za#qZ;Hwz4{91%l6^xH+bV>KWNkp8X)tz#KU zYMpks?qY|QQ=2J*U3Z@^W18Uo-QVr?U8h6}FV;F1hKY^v$uIg8*2t@3u2Be}B3Z|s z7mTkXQpodhz6@|RIUI;uY9>T7$FM;i^~3mP=%i*fQ7ClSl3&4Q4A=?D#U*LcOBg^# z^f4K$FvQBD3$3Sla*!g5(_er*N~}!+ILJv#myXgI>|$~)Vv0hk4eNCgIw>%j^sGll z)cJ@6LJ+yoGi(feJDFwISY3nm1_%LrCqJu5_RE0LFY$8tfHI#`yQdkuh{3f9zoG4d z>R>k^2{21OWL45eMRpx=#>D?x=gc=~ z39SBb)iWyL@ph>PU=Z)9V<Y*HImO@cMQ_OeShpNctT&)CfZ#eK_u0h=btjMxQ$Z$=ada z)LKCX(^&Ts4UGq|_ylZpSV0CuBoU5lUl+-jLZMc$w!Br~Ji6p8CVW9V!;2DL<|u?j z`(^q*`>A88K%-EKI}Yrv$>{B(xX8<_Y*Rh~Kf0jl8Qu4-Zz<0jZ5_=UlnH2l#&25* z?-}zjcbbh-Jh^gKD9}b8o#Xwqi@n!FqYEwc9Jvl7q=n-#xH|)g0CgSX{L03c73(V~ zgVX-?Xb_J+r<@DKKH=*3`LWFD5J``qIg7t9&B`LCV|=MMCawD7tR|#PCddqX$$wqK zWqVd!^Ie;{3r<|VbYCxVn#TJ{KF*7aQf6v5HBg3UMv1@h!Ldq~`9)?FrmEMds6o1b z-svw!;fjGq$VARns!~W@2IaXU)d#zgN!q2Lp*X;{zbYO(aV1ZIo@{y_S)G;aW#yG^ z(XwSmbuzCP={=w-Vl7TD?>ikkb~ug36jKac*nAi9aO!B_5yVn=2dQxgdS0gt14{Z9 zHxMayU{m=MgBy%gEF^ex>P^^w%0=8fwbRb-`OX;E;)!@L2SGuZXB5hG>N z$76Vwtda*w#dpO~adDc@=o}7JO{s19JO$>G&MHc*fyyM&OV8=J3GbyA%j?%KY*}&%If>KF{k0hZdfoqMQ4uSI=*-0>&aQN_y1UjZ^ ziAOfpFEVBF{XL((iSTX(NFv~VdPnee_Fti7QFCOkWS-aHTwk>Buvv=nk8SaBD z0N2b>)_y=I(1QuSzvz_>vop0_Ul*EptBKLAf5Vx;Py+FJ)MbvLnhfNS&5jL<3ER(x zS&Bo-5eqMe6{8_>1$p)2^^Y%}z5fm)gNevz@cFyvFaG-7d-dHuLQ;BUd|?}F7GdTz z=2RhGCF^Te%{B89x2s>t8k>^Cy##+N7+45SOiT<3H|KZe>MoQEZF4Q}+P8XlM+w7R zx;Ep}Js=S%`?f$%<1Rlu(E}9x-%2YXE2;jYfRyN00RMLl|Ce!A=GXM!L;CNJ^xx;w zMt6DsVrE5Bq7`KsT2dwzMMdR~lf$RZpGEE%`Nq6Soyx7V+MA0PzX@kSe_s$?P~}xB zTUZBOr9pVg`QFN{L(uOFN);2dFyE`1p+yVAPtEk4}f=HJes+A>`%kqHRDS-5au5uDX-0q~d62P{cX3$Emc z>c(F3o4HnHXy(#KL-V#6g^6|_>vzAHUBLB{?}?9+%Q;$tKXZ{AJ{F(4(B?!jfNzq~ zA_H{z15t85*pd=r8SYn;sJy1)N8cwzb%vd3EKl@SUl*HzeGb`6Zy^t5$F&w<9-*~1 zluAk#zc#1zX@whk$wzCJ{HK*S|5Mr722Ia;W$D*7O zl4uCv5Jwf>LgVkN%(k>jFVK;&B1OH4oiE6FUla>sW9Uk+} z4b4JCJ<+Cp;5`aR>9~Kz4w~2>x#d0QQ@tL@E`be$oZEAthi;iA&swkM8s(aj*nq#` z6EgBsxEbSh45W*UUzl>3zv7IA?yg_IER@9KxQHaEHV>`GV7i`g@+2X8^;H*8Sbd^! zz)cige=zxO#CidUynwOrQmKb7oQswZc$(rnHtO6!+{@FcweEuPFl{E+aQ#*Tiuu8!)Mq;i4m9oHLE%iYL(LUM88J2QDJ)BO$N}b3jRFz zeV~cT)s}NDNel|11K_SlC<4}%p90u-OG`ko#VBAL{imc4^q(33m;Q6zq9=&fb!(?) zYjdmpaz2GSL#x;797*%JxZ2fRWPHA5Zm-PV5)L9&QXe#x3I{aB(ZuoM^RJaLNvb_{ z{LU(*RXx(mAgw)Q4w^z?)F>$YX82z3Yc=MnjF28>TofL~J##mcdED(lu{f%FG_^vF zi-y9hH1($Ll=pfwSks%jW4vxDU>5!io%3h?nkLV)!b7zS<<9jDO*4WX1kle3TAG_i zSL(W^`j0_%+t_YRW3p?mUEsGss}v)bOzQRBbBUJ4L za>VRASC<^M7>JghZAw}XjaFF_qhkyf}UuxR*R)(*@5 zf@S`Sk^OEJPRnbYmdanTlAyyWKP2e2D>kdC{yL~`8{4gEtbWCq2^ub&viE8-Di&~- z&kL3$*!#!Yc1A?P10hYKz1k0Xd6`b10{;{bUQq=narrETCA$Ko`mN!8hUVMpBA-_6 zTJ1C07s!)BZ$>~MK-NaJ{qafgYZXh@@i7X$@mhFCxGj*(m48R)bu=K`=%}X_~EK`GP%s( zWsJ4O6{Ut5M2PQcwcnin((ZKP6J#`ho`S7u5@LAFsyBhay6m5VZn3igwF%04ziwO{+hP)fGGjR^0J4g!taUl-w_<_Oq>WIq;8^xh@ zFl$|C12MWT%9Aa!U6`(d%@ru~nm;mtFtF z*HxmegF2v}LnQ++%O1=zUgw!@9^B?May1UzogtAt{k@hI$C-KMWBr-2R&DybhU6M& z(`u)i=8}{sgEey*sz|DVnajZ@a~VIE!?_%Yxg6Z`SPSQJAm(z=aMZ;%vYG?DhqwPf zJ==YHg?@9^Zp|>?=r?$Mzt8hAiv)~?`iC|3ORis<`enTSk2UqLx&Ae82dIA?um8NJ ze#Z4PQ$O1c6}`_2RKK9YG@r#(JNU-Tx}Ct-msL7vN~P2DR5~iyRhdpvJ;dIi2CG)~ z>mBZ1b7dfnqG5HAa;1@ksmJFNMdYaTi^*Bqv7 zQYN<~n!&$5j_u0Vc2mAyv(wo|%}09}MSEz`9@cMlHDk0fdc42vQ_T87{uuV=$Kx17 z`_b~VAJ-4^d5HFT6zy}1_WAz^(S6hUu0$`#{rA5o4MH8az(QI(uA29HqhTZLJ}+Mv z%Ua9N&wR^Iv5&|5$uDr^((`6AzPz|l4hZO-m@f13tU?gtgr_-aTf^YuW|mwFDe0KR zFul=bMhUx?Y5Rx*2l(B2KJ!CzkmVU!85z4Y@3=6{gCq&H+Z~T)n=JV2Y&(oe5gK*; z)7jZsV9&9sXMiH?T~FswhAWnkz(spmlty|bI+ zG1vY3TKIg6Zub}Yn4{Z7hHq*3n`lncBTupo#Ixf_qs;-jgNs8nv4A_-orBoK{j8oq zy$)2HPh9m6nz(8=kU)Q(iTgR6I6r)myf81&q(eK1d|NCI&(p>HkhZ`(Vt>f;u})WR zj4-m&&$qz&vyZbuj}Zu3@tewMQIy&izx4{eAE@(ibmKilwuUa5T@CPa6d=Yd&U;^7 z?VW#>Tzz%Ech&nUIsfXaw+9~+_;KFbOa8qTxgA+nX%AJ?)7l%`YW;Apo}`Q0jrQw} zMte1Y5$?g*;NOi$raQH%_Jd4u>IU-lCt5Zbz|~%IzSTg^YX-8$xTNWUOFk8DaAe0_ z#*Opft)E9dBXstU4V^DsQ{5^8RRvIWo*lxOW1T~XpU%W{t7^3zuUJ1}51B))(!L3? z+_9DKKT6zjp~fs$zpAazBQ^+{{IhC&v_6ramhjaQ2c+wzSMS^@{IOV5XxtF-=fPS> zonkv5og(g@Gl_V+pWw|^dhmeg55f_U1^xx6{ek1sWaH^sxAIjgDSWj)>Ywq^VR%Pn ze^Gd-f1Y!0jai)r>vsNdB-1D(d3>OFkil4zNKK2cnc_M?E_h_Y~gLN0BMxP8q+A$ zppH0q{kEkM9V3*bgW4fin+&;l0L;@(KWEsHrpjVu~&aZ#xjo%z#f?{g=hO0t^_x`PA@8JCqA1<0-f zvGr+UfdE8Stwz2evu0^q+9EKcHG!6S+av+^@WL#6U{wfajgCEDA}|FYl?IVB$lW#` z>XF<&U3&zmZ~ktli|K31??FM1Jnu%TqoY{+IdVMN@~zHz6Y2|&1ST))b5&pM=Z0#t zNHvpXz^fMym}M?8CYzWioK2M=Ma<=#^X^1!akzOgsmHO~A%Y%8S@B%uCZdP;$HwuA zajAC03lecpSv3Y|@2=J6-K?6sNsr!-jpx)h-K^@m=~=m(F5bT%>L4Wb{zcP-?Oy9A z?>1>Rj!9z`_+Hjy{TTMl(2@9MBSsuyI-Pqz_3u4UK_h$(*0h+kTQ4x2F~x5}m6FaI zg;Bdyf*oXYdiO?k;%ZcKx89dMIT>{^XKk41V zCP1-0*~~a({hv+!jkoazdvYrRua83OMDv7_ufEzMqjytWS|`RTe0<1=De}@0^#VQ-dPdA+b7TGzxogrZ7WDN@k>RA=qO5S|z5e2yo_4m*VcEYzUj zXDVu{X$0pnTU?adu)uT?)(*#{GPO~(SIjep;hM;m=L}yfp!dc_uAjWY0F690yyH;T zGH$67q5PE_=#B~+0az{phZrOES{Y}~IC(+aWG^7>D*XT&P5=Z|y`1G3<&zT{paTor zT!1pyZy2T1F3wFXn}kK+XwJxkU!qAYq*=UDv)$Dk0v3avrnc|wh(pBkw@z!tELF*)h)CX86 zxx#di<~(bipmS)_s#VlrD}e4m$DEPfvxbk+?qNYw2ZNSKYjkA~Tj&>b?xfWTCMI~% zlO1TxDywQ{W2%RN4M`P#bnJu)+AbZdNpyNvES{$Tx=y?TqRy@W6&=wp#;e;TI1O>c zlG(<@oH8om+%?Im-=9biVsp<~YA7+)G)rglR#G1MIFF5kRL8wt$1uATylxjl=F}DdLFm|UQrC=XL*2KphTIHe)AN9Wau(AgArWNk6O+)~ zVs0iOJh$v3)Ff=mr9TwKL;|I!%Ryu3g0ejmvw`ta2=@$Twm0U;sVz% z>joG#wyDB)U9+&x6yw68=naJML`Tm+nF;eH&D1-1qT+y&83r5A&dE#(YZWllm=GyvaZLrf$JC0`Gj6{Rb+P&j70wrpaA!#|9y@I&?W{lPCxcGcmW ztILAqb__rN2|wEmOM^Yy2G}+#lXhtb?EfxyKx6})6*D5r+9XE^a1!Pdo~Pr+YET}j z#fj#hW!W5OD4Jx5L0X-@1IyLX4{VW-vuI_bxx@evC#1&#ETW1^tTcnO0oS>)`vxj9 z2(1(yQ-OQ-eVB11rOQ%s*p)7}vXggKB>a-~ECMZnIt&D#* zU8+>(ZlOwOevvKDi*X!nMv0WLM*=fz#fHfd`tf0SKU6p@Y_yIR&oX^xH)V>j^&y|P zj=NsBEQ+w>vXkjzQpUT8DJ4!QhII_b8Elf5bldi)05eG8Ae{T)M}ato(sX?mJX<0IqfU+ZRuR^u)RvQ;*MeeenCYUd_;#EL!J4k&#J1&d#m)|rmbWVHq_ z%at*-7x`?M?E193Thj(@>7m%!T2#np_|SZ-ou;+hlo=UC{jzmjJAMHjr@3sl;S9tF zK3GFht8K9N_QZ!2s_w6wXTLc(lZNgQ)YivX`y-wjdP5?qkpy_tk-6y=uH+-M^^u$0 z+r7C_vX8P|xz(znyn-u+u^I8-5&0?+yF@7>!VVd-dA!v>c6zH%FagT~&#~4;J|6RN z!SOC56F`vl9io5f&Pi82*{Sj6BK5zmnIWf%7`+Rcf4EyyPaFXmU9;;u5S=YKW3Cj_ zw*&1n>b`n)s~>@y#?j?zKI-MO9v#Uw9d{;mgqhZ6%kVe}S@Ak{T+=C+E&Fq5?L9+! z+j16O23(+L?L#JLjZWvx9Q?R(8+kYL0i!rgst z*NIwCt5|&6i6oTo*U#n-N>M_n>Z06PT=Y@BfLJKGK&uic`Ad z8O~sqlR5n<7QB|#dZtW~GG%p%z|r@XR-Vo;myZ`&N^lm2zV}inP=Ls| zFrd5#ho5`o6k);M7nh^+I@l!^${KjUM1b+L1j8L10mIJ{jQag{QLL)yfaX}z5_*Nw z1LKbp;;&{FJt$`$=}Qbsl`Glt2b8}&Fjdqe*-S4z&eeArlKU`uXvyK(n^zwBAictm zB{~yQ7M~Ar!K0i&aKrte>)|sS9pO4E-UWz=O1Cw;e1QRTP^56_glp)0J*E%&m^TN$ zS>h8HClSVb%&T-U$vHca{rO`2u0XB4#wsuYALTUV*D6!YIgMDe?H~DU*xxmbT+ax8 z(nx?IQLy85!J8WKVS+`b8u;UFI{s;xd?Bg;M=q|Fc#8ipUrDm@BvYz1D1Sr6SW_$s zr}co_ucda?%ZK(0pYK%B{iHw#{`aSR*0%oXG#h<2{#tA$B@9upjjztqvyqrRd@LO2 z$aP0L8WGho=@@B1x`g&!iZT9iQ)?WDQylrUN~Reu=6TA@4;3?*Z8%#R<3(+3c)1`G zI>1~C;?ixKj=z|Z=>JtscWoC}z)JDS5;rnjzZV~fro7&pT*}TMR3=GG0Si|@42#Uc z#7o>3DLKvNTO zFGUpk(t_XAQtydRnt0_mO)B9xmsafeIIXahZfWI9q!y=4Nbm?TJ{^yG9$+xm`2T9-`gf-xvH^SKfPQziCzTpA6TKJ^20o&wwmb3lH<4-t0)mdH8#MJ!>+srg#w)dY)9*PqkR0 zUvq1po)G%PNHHh&p;t9(?JJ5 z>G8H%?nD0?{_D-9G2LvMr&v9ym1Ut3|7&j`j1R+**|&4mc5iA_*JG7L(zk*zFW|6M@LFxH8Xy=o#KLbI}(3nWM+UvbARN z04A6^r!!A6`-OF9f|I39vB&5W{PSn-vNCCfFZb{J0rIi@C_nBiO=8F5MOPVb0u<1J zW_T~o@bEf^1Me)mCPsj#_;n#NMlV|><)%^xTdHPdN3_qQ_%qt;s0PrSS5IEwemhI2 zrD~jP>THQx-In^Ijla>p*9^=t-nHmY8a9^ioFa%dHnh>a4H&!WSpBZzzQ!mYd)D$CdPyV~Z7JmmAJRSae>R;y z(V2sNyh?+@xFY4sj-w?*G1a?@HVdVwJpDSb#M|GmQHFl`d%Y-(_oWCv-Zzprsf}ul z=Dm18t_^#p~xMhi@N0eewFQc(Dnv^(6$? z`S#tLS0?~KK)=85U&g^zp@4kyt^xANyMv~y6E0fY2EZdjP2=1Wc)M5uQQcqIoddmV z56y?Rg;%M^wB(G9DKA{w+@uw{2O{!4eT5r(4RBwrvkjWoO)*5E;WaW=;G|EHam)mS z%6l6Ml%xOJVQ3Gyza+cKHwlQA$uK!GBB1Jxj_e!Fx^9BsHg!O`Zi((wW@4QT9W#Dj z_f&dV-MBptr&+q-iB>&-Ghh0lpKU=O4Vf?Cj`il0Sk}V#HERI%A}!hkbkhu@wbxgy zOQ83vD3^&ftX6*6TIC0n3G6XP%_ui46xzn2TcW$5qPLgjIT8takuZzwk+zZhr*!YS ztqH0*7sch|Tq07=D(I^UJhv>+BfGv=xk$U{>*3jbOz7d|8o45eA*WTo1Zv70XH&<& zZeP)N8%Nh^B6j!n$&Go-EeP^4{+INRSZuvj-zkU8bOD3U&)_yoFBhdTzS(ieP3|Cc zKiTzeo_0sm^Nzh&mbV#_zjwRIhphTm&7Zqvxw&okt=q60z}m%23%FB1&$cL><8O17 zMRJ_O+?EvQKcD#;v9Tp|^79!9KQc)!7 z)$1q2_|@LIu9okQA6>(2MExG#!25my#e`I0AwQo{#&uz#= zo@06GBSc6F!q1WS(OGY_$cTfo_(@p$$zg%x5uac<7&rnpL%A%wF|_(EiHkk88x#19x=iXD zG%+19!{W>&lJyq52v_nc^J)LgYF}I~iAxB=5$!aeFw)JHzS1dxtEnSN1Lmb06Wo^* zJiC#&pb#Df?upLrZYwExO}nB3njjU$oZL)cAAiV{rhDWD*S$%Pw>Jy-PYSUl6Ap*Jw-{qRs)a#5J!Rs(_XS9x71s*v?aOS-fS+p z2o%Y(KmbAkJlM^y<@r z4oUh=JN3ZtrcoR#49X&-Y^_&M-__MHexAdd1+1`WU5+^%7f~h*wuH6qqux+CC(2{4ij(miyJ1$FnndyGU7-&8O35rRw4e?87K)uyU9BM!l zi>Tp>zDNHJ}eP7#r1$rTKtY5N`;buYws7e5Gu7WV>h`3^BCbCl#}hGtzD zuCxC9rd3Ru+h+%dAKFPT(2eOL*mEQ5(9&AIpR{gKj#=Xinmn3&2VIso>d-1&<#)3c zIoC@5oZ5)mmX~M6*^K-qT?|vW%ame_4Zp%0!C28g`|Y}XA13Lz8t!bXWMWz*kvDJf zrS&UxlL05prkT7}h=mJF@|8+<-n(rHa zLRDA!6Kff&`9M4_TD95qpLpDc74ELu-Kg6g4`$Ad9Vk<)@w30OO=#5>IYeIraiGU) zzwGyeCKCDlInDdR%N1~DPE=_j=cJigtLuMVPeoFsq)16GicHyHLK>V`PKXQDWhqOU z%GD>TY>Eye7z_w5nUjTjN?WpjFa!mP|^cbA7d z@VZnMjDHVCp+&Np_DKp_B zcm`-XKD&OVec7Hl$RIE7@FF?2>yu35tC~;P7K&WkWRkpe!bLtFcSxUb#dgPJBKr1;$6gmgePs z%>6zdmGnWYCG((Yi zu7vut7^+Idtj-Z8T4|(aPGj84JxiGVP~Acg?WSeq?C`iLwiilC!nn5<@5;%14pWHb z!WkpGsy=}+iKd&=MP2!&=7E7+o=h*<+aAUS^9eM^vQ&Oow+L-DEG7RtN#|51_b$Cu zRiU7&G<1Yol^2y}u9<0{R?*V55=s;{orr#ZX;08$3*&5M&H|UJBn?CQ1KGfJ#AvgfOgU?3JaEf1)R1VSQ~kti#q)a)ZAF~Ts&kn2O;wLnXm|T8@A46*U;hHP=wTPS?2B-u*X;nhyCrAS~PRCj%vG4$1x({~vJ_s8g+kuC& z%M>1+%evD+(QJi4IQmqS;hz6^rm?J4qay@DmA$#gBOnvwRbR=b6)Ow;i$^-9j{P>Z zYfttLDmj{J?`0#WZkBuf<$|CUOe0CN?z*l(Tqt+^!a(qwVGbFrx%0`DYPWI)G zu9Rsdo6^D3jY$Z0d1}XuZk3CKEJYpP@`!RvIYE~R%Qws;`wcloU4Gp6c1ID>dmE1R zAv|xc>enGqOL|rhL4iJ3EX0PBy>X>-$~r*Y$G%H}PTV6_L8(gJXT{VbN-ooIu9G~x z_%ci84SBL?K%c+j>s74^sm^^jl-l{8HK$@U^a@9u2i~1Cs>4&Se}6ktEE7TDeKw0Ho)Sy0Nx6X(4BgqqIk;oFD2VQ7Jb#Eh2yT~) z1YBWqO(5>*VdSc>%9UjgN)w();c?aZp5tJM_Ioq3ikCD<0T*+WzaO*h@mj zLkv&?jbEzwlgjL>5+L#9xf<#dqEk}_Ib~Ot)ckLBR~dhGUw%_9_#pr~CcyVLZofTV z&v81p8o=j^s~neaicdo*ba8bT7<47d$yvGT8li>JqsZ2yozo%!i7yoEwQx0~W_p9c zY^I;-vM*4Kv0oM~eVDwHJJp7&O7yPE)mB)s%4N<81C;5e1bJLO z`_3_%Oy}qEm~Oq1dVesHI&6k<)YP)fnit|e_(D-Q$e|aDLLt2V=kXv!hm!aTT;RC_ zl38Sa1HVD{uFjz!Ld~U$&*L{^K0MJmF$?KzoCsa8YM^EGKw#u0Z!d*%tDbvI;aWJ% zrm`ys54B46ShsdG7dMQi?~PTaeb7HG5xK%D>dM`VO+l%oTt~n<_NHa_3$#i`Rv3e4 z08Mz9u=O`j-@=kjeJ4(TGDF0;o0 zfuhXLmJYeHM|-JYjwEUd>ZT$Dpe*-|ZiB7jtOXhzI)yczqw8d$-4|)wGMZ*{tkhIp z71ROf7{{yi@ckZa#-x3gSS_%Rx0S95)jDb>=Y-lTGd7?6ASGCOlT_*j z$*}zZRkwIERw!IQRm*cat|_H+WkZqet<-{=J@2&%EWJ&6Og%v?8qbq>bWH~zbfE#k>CS24A5#9k@B z*mHi}Y&7z>@d8NrVY)OSX8doj86srxP5u&~k(!&M^&d#eOK{_x%qRq?H&9 zXgZrqs&|{vK=L;{Rp{}X7s^7&2#gX0IE4lPTyEIvj$NQU>m(?y-xhX2r zI`@gse=82nyIk!1Vj}v+_Bd>{V-)!}R>04RNylat)fA z0IbV|!e(iJ}pBALBW04pD_7%*@jszhbiSi=fq!7#vAkmoc74 z3F?Is5y88C>*{9Crk8Y)&Fk#O1`mOLWAP$-m}Umh!QBusQpxanSQf}~@mvE|V) z!9{yMg;~1DH=3>m>uIuhXO^rQ@aACelVz1-%lSas(;=*IH*{JNfgpG^3np-0?>VT2 z@2w0Pt+=*|x7CfW+OA|z_V5)nRSHIpmbl4Lxau}}%?M3QXK&`yS$rDHoc4=laDmaM z@}=R-1(Ksxf)>Yjh(C`28GB$@jrY^661ADCRzUV@lDX&Y=WsW*L;+4pRQX>-5EIWj~M>RQjAXfh*O!T9J}1v3#6t44pY#+SQ69 z$KA22klJ!p%pF7Uv?CT&J#e3{B2{lqvX)#XdblNQi|)xJ^7<$jEcCnf|x3M75~CdrD^o<0X_Z$N8fB?!B$9ctmXc?%Wpz*BxfmXH%&L5 zFuHI+OPl4x8tny>|6wAwbhY_Kjs7A~<-_34yB${ZnFZx@K{xc0SJN89pDCqUY4vWM z>Xm9Eo(Najw3=HgbDj9Pi&bv>D)sRF(Ln)ji)yr{dofpaai}qdl5JB%!alh>QNW?D z=qo)c;!0oCWD7XTHG0YT?sj?EMFBR0AyscQqYJ06~>mX zR2aGORXb2+vm@&Dw6#VQYWdbQ`avvVTtGYWp?E{k&SM_3{*scPq?3#3N01xDe?`O$ zXf;N|cTGmjY-&_v%0$Bg723sDXAe&2W!5@We8at0qEs){xLeEm1MU(*)o4{$mc#< z4nH;)*E6Gyg{@_ct}!u@;5R44D`r_dzcvof7u+Trlunh>Z{8xSi_MhlwWe~0Dpy~{ z#CztjUgmPaajjlCT3+V8T=c1cz$ZzG6;`zN1&OJD&iMZT2Y&F_P z5(!&^V4JrZ)@@G3bA|7iBlnAC8P9Ka+KQaO#utdBROc9xbkN*1f6 zbYct_VWHFtP9U(xn=K=+`($~1E-KK6)O`CWU(wB&wAp8_W7!0&X6%jNsy0G9Py^n) z-6E@;P-C?aYoYawH`TFOtHa7NtJ^E#6yuouchSV*yFbiWgtn%risM@1xpw^ZTiLDo z_GvX+8RvEiHaY81na<*2y11tK8SJXo2Kg0UqvnSLtX4{57~LT6rv#G8L^*iOqQ0|DLK`Yv8cN=>>0gz3hgP zyT1`y_?nOeo3L~O;<+E&fb-7>d+lDoUgGDr=-Lz^##*-yF~)xp5KMaFC4aEjZ}Tr@ zee_^&&?$Pztv?ubF(mesJNUhN%H1etMIgEu+t_ikiO;7P6D3ZPXb0Z&zgBUAZop&X z!g#MNtcJ9dEukJD`gVDU1glCOvO{%%eRoSB5ME6OQQ5?QGE#D{%k6HT6Q~*d5SZEd zU51(~bFVC-zV8tNHXYj8CvmoDq}fDrKEo69+vF5>E!V}ubCt8Hx8;poHm`O3{JB1x zwSLA%E|e{0jy!J6>o;#+zkBii*@t&8o<7^q0dv!-BqO1{fD~)?7L9C0hkyO)h`6=g zqleKJ^#qWmi{O2_fDvdTqPwOh#+8zyOyuILBxAt>Hfs+x{$m!;}h&omlV^D8)=y-3RQSW-y{m5223tu*PR~!La=+I1CCI|v*{%di0xL1 z0m7hBLn*&ZXC`Ee>P3Y{l$JE-2>|(-d;6JNlkZUO``I+c3Qu`lAbu%kB_`p;?BSZu z;Y3yS%g%N=2#}ThT0taWr~#&5_sKf zuTAz^42~GplvqUpXyy!GM4iH*5|Yb*(tLCU(~w&eZy62cRaDR_+mcSDbJ1UzGwx>J z-zS8s7fuU@;u_>-aU+MhAWO9o>hJw@c4X2m zn6}o|aP4d$sZonn*l8EiCUMH;BwvSOf6s{Vv$H^&Obnb{$ghm?7S$YDOs#{R6$gj->+vT4?87itSCs4om zCO@1;d?gNzcpnLz=|+gYI{jL2gc00|w^+;qXMjQ1T^`J)ZlD_=)QnyOlrGaOF$VCb zQ;e^~N4#oIj2YDOMO8tY@fZuqIUkd2i3ZBn_fv%hSm(|eQqiuVu8k_Wre_;b7suHP zpri{rB-m4K??05V=r?o9ZVG2khLF3;KNUlEqDy?={dMaOsp~tz_mE1royuuC*TKG} zSUa70rM~CbY^H#PudH%;YF;!)(^knm(e}9pw3L^T-|SMrDrASDGbK%;)2%uVPOp-- zZ$0#g!!2FJ1zbzQzcLcSz1){D%TL6GSpM|uE|;YWaiOmn!jr{{gqBWXddhR%sJdxe@s&nc$_K!Vhjgj$+&`@6ZC zeB^AtQS*l#zM_@owz;Fs9hX8YR^hCR1ef`UP(^xj&5t$uMmJA7nOx}Nqa!o7bTUC> z%7F#H&`<_=E%QJlFb>Pvd>tz_Q!dgOAaW~K%r#Y2Tr$5tsa9e_UQ`j<^^LaM0J7e1 z)QBcJD!LuFA)W0vre_vQupq!CzzFtKNX`y*BS)4AF<=Vwra|ZM+S<3A`+XE=8a`1% z*ZNqPpc#ib#$k}Rj!YgqlGEs~oqF_BH+PXD4|w9{nc9RNAEfr2;HJl{QdGKNwMoCSa_psoaV9w=7;*P;t~I$bQ_>Lo_7T)Gd$OX*lB^gI&n zWujt1T*gpYJQFtr8E#X;2-*{i@-{N!GkmQ9O79^pWfRdNXq~D#H&Kr&(Z5Hux>#jv z5mywoh1%AhG%XKQJ{A!jH(PO7142!qjY`Egiq=?88R+GGE{x4%!B%f!?vbW#fPG^Y z!N2M@Z?{mh;6ET4zhm0<85>y~aC>wk=$Ym~egD)}_B9dMLz3MfeW0xJDyJOoD!)WS zL*l1iPoYY%ho?2=j^2qRqh2k2jVNixWGgdA%kt4WQ)hIPuIuc>$1`<|h#C_^FlHW| z%ES*Q)aN;@!3fCZZFQct(z zVh5GQ_~h6drJwg@ll{0m+^~R;T+7eKHq-&yvg^t4_53kDs)wSq$K-1^oK6?~{YVY) z+1Q0IPt}h~g!*vE8+Lu*c+J*1zQTuH#ljAus!aihkV%2l$|HCkB4On zTg8|UazVXnm{L*6{GhrWZz0XzA0HRO59FJoTp5S@CGLf*7SR|c!$Ix~_-#51rqAW+ z*rI0wO0BHQ$J^DwOb!6aN19qUO8Vs)qy+GE;kVAxV{` zvr}s&doDbQ4|s3H*2R)>olcn*IS~;uck5pwW)@$$T6WIB#S=S!^)80cb>L7&LYC+# zMCyuxO1LZwTR{WETuy2>$4a6z(b`{8?>!56>}p5} z*HW9-Wbbf|1FPrqvhvKgVPUu@Fhyw`4z*>3$W;&P)d-Z(?Cggu<68{Xf@uNPs;fiKpH?iPXh6E z=)j<$_ljo?qU#)GA2!rjT1WL@F@>lP*A!zs4;4jeucVYl<|ruTic462rLLtOJgr%G z9kjx03EgZ{Q^apEy=L)Cg&3xED&OQXjcSPyWK$7{=9jH9p01xU2m@AV=DKbxkE$Pb zvs5L!sG41P>TbS!{`$ucZ(crr^6dHRmrtL)t!a&@d@yZ)eM=`n*Z6T-bZePl3D$*5rMXR|B8JEI{J?Om$n2emfoQ5xWptU5w3M zQFfpgSXE(9_2UNQwopSkNhisu4D)Iw4jEqM!|5`8EAPh@QCfvmSRB9Bt(TLKJ$@Ng zQre9?Ko|N6+WM~pK%)PL93p-t*a6s-3=7``*|2Z2cDW7aHACdHFRo+aKB^?3tz$Ai;d$vA7x4)byKQ}CK_L?@L`m^ zHquKBYuup7&KIaI7W5J=WhF}N@2pc+6xGD~16O&9(so$&Z1?x7t_TqdUUf<}$57yd ztwXr4)EsH4Llrq=t5|{dHkpjRqsu?6;wAZ&S~D*w6{pJGEY>tQMpkPDru_hyw$x$M zsv>O=)wpt!lpmO*erOmyW@j4ZojW?Ucs+G0s+Cqj8ud8bGS!M?8(VQ55Ze(Jjg@NX z@pL*);)x}LbHCh3hInHMH<9oOK>t=N5_O9R!phuSC6#%O?ki&4#>wJT2;Z`g>8xUr zn4^<1k*IkSs|N`uG2tQNMdbtgF$uMCLQpL#@Oxs^=NL0%5)R({EpIoTTvu-DUyy4u zkLsmU>iqv~P7-D;I49*UIxrsv36*e@%8}5mL0npO*GF6K29>KVbWrt=oq#cd(3_(t z`-5{z#1FNCb>I5WykPdX!)ojAhwa9$D0x1r{q5*)O?AAtj+DyummXm82*AZZs6lTc+M>H?BW*kQU?z0BNvemkd9l*E)f4S$tpf(v$tURuT&)9LAds+nNY{6}3hi~>-N6drv7ny2Q#Jv`iYve+*8n+DC3_a# zRMk{W^*2yE8hZm-u{p*~&2}`S%&_X(8^dI=mlJ!{vU1ACD!d4yw7+HOKM1o26OdJrIqEKl0*+pFIP7oqll`c zNr!Lw_F6SIU9uq;oij+cmCYJZRgYIyaY}-AHU_rEvdC_TRjw%xq0Fk{;<`WgZ1MV) z2NZxp8HCav``LsQdqO%{LHFKLX}4voHEjpP3XBy^2Q}Ja;d?xeCm)|PV8Gm#5U}PEDiQs6lea@i zS3FROE$&uiy%6kVB0L%oz)D$mMzF_aTj7=}d(_7_#|6l)yb-}BLw#xthKmQ*qbvfgt|Jx>C z2s3fw|69>Sfn5B2hqiyOa>aF)qvDQ|nQ&t@qHU4Oq;yPZeK5DSfb+)Vvg zyFzt{A-Lz~lWtIZSM5S*G_1hQQ4jYmcN})}Rk^w9oX9l_Q?Xpt5u=c> zhF+aX@qp)EC@83Mg_iRrm+z{Pf}XY74_dmF^DVURFSL=QtX^#?<*v7FN~0u03{^a_ zib|=}^-vM}7hXrcHt(N!(G16c)Qu=N|Ej!BHIf@Y1H6WxmdiVguMC}a<(IwI3D_Y7 zAbLHAe66sEhg6mLqU*|0p-B~g$Zt4LcG5Y%r(YO}^HAb(C~<5W&B5wBf`h7jt&5gv z#e!Lh7UYdYG09Fj))xEVx|ucQbC$Rg%o6VbzODo^*;6MeMtlL;^^{H9` z#<`~y-GvT!An^t9V1V~hK8VdU6IrY&%YNf)Ip7<(w}O$XE~Qypk;EJaYNRZ^6Ut!f zHI$*`)D_!5q{G-dkD05lDsb*7>iNsq_5Ee^gaz;X%Oi&65#yF_F4p>;*vNauIzr}i zomW`lU2gHRXw}k$mK_*rLk|{SaK%jtB9wbKoKMH&Z>Lvoxr~#Kr&nS3HokGOP1US4 zhc(*6ATeT&9R~zPP_{_se-XT3nPDBebeg$xwjM@5#@Cq+SfDHF`ajHomSJqjs>O)ZpjvGYAn$q0)2C$1yvIgT#AKcASuYOYG(+v|`cQu5qOi#@#w- zVX<>RK&MuA94_Y>QfLo*hVxL7pkwizf<55g4WYByRlf@^T1@8=mFOPv^cVK z;TVkSAUTlfa0y^a!cF6c)-Z@tHF+r^(~ESpjK`>FHzmB4Em)UjzE`H^ zx&q2V()og~WZF9KNEwHqz7;8rRFUdbUOW&@ruJEJ5UHk-eMsgg03N4EE&nn#)7HK} z)N!-d0tPG$dY?UQ_juLe3*|cnx_X}O;@|FYg*ta*I%piD9}hGVEaI6{5oU($yA^DC zQC3$yeU!VGy_I)Zm@5+?!)qiBh^6rA_Qn}vw-oW@nn07jFoZ_u%LN}qgL2Ef!+U^I z@C&a|uL{tjiyx+z`aj0@)C`UBzUkM;^h5*D?Pci0Joca_BwxUO&UrZ_IJ1%dG9B#~ ztHE+Mxy&%r1Pcc4g#rbqg1^|O8&AKk?>+Dx29>)w z+!}Y9=~dI6=M-qjmW76{0QXj7zlE)TX%~Pb{Ws9`O&;52pWW5x+*L(Fb5yWOKu(Hp z)8^6~&+;BWKU(}{ra4GiUp6yQ94!LLYt4orMczbf->P^DvC53`Hpu;_6Ge(}uiB*u zmBV#a!L^yvm?nz~T^k9zWQ(O^XB6gI$_1bn%}_Y(ib67gs^b;g0@O774yF$VS$cIJ zX-vk(Bvw2g|LI-4?=;$e>!q;vMg6RtQW;)Q@>~AC5{k8qm9apYy-SG-Scqsu51Teo zfq{Nfuyh;Je9aAq;zmQZdiXDkiR=rM1CO}$NN+mp6&|Uh61;K)~GGnR!_pLw$`T2Ll6qp0%9l3pPG8WzLPzowkAf8?F*?3AkmRH7UrW*PUdbp-FeuS$> z*KeSj+NwiS*~ue_ztVY(zvx?ZOtL_QJPKxBQ7uosIG+&zsWf@iMeS+vsJ8&hUkQbxaaI6C zS8H!Ya1(7Mh}*s)RB;2ZwiRD0WqH7|T*(4(**q%{(NFsuz*DJ3qVmhBTbwN=^C?*R zIESX1d9Xh64+$6dgJN72!Nm#1U@J)xU+Dp>Y34crQ_)t`apPL%idueC3wNl}jM%qA z_+Mt;x{{f44R(~byH#X|a#G__5H&ku;Jv%R#-V8%WB9VEszb2ZR;aqzdq1i3Z*`%lK-f?` z8jn?cZ8$q6lE_G2;H{FQOGc;AJr>uP;!rz(WI=pyPQqK!d?Y^nvXlt5^yE5szA2AO z;vXk^n56Lb_s~!zO5rZceJHvlw*~8X0KT|A%;|?N|6NoQOD7w=N-Q~37V!BQ37rBBlH z;EVUB6VTLEG~Qn=F1hj={82@3+N3?tEy&6v`o}*;#YK`r%b7EOo?@f4|M1v!+&$@myVB@xB#XW{a+xp##huT^}C*$8edbP>q1G(d6_U0fg$EauGSaP zoJQSY1DxjqO~pGi8t>_eHm~eKT3UH|CyTuBbxC2TdxHoaskJSsv84~55c^o)mE|&Q zTU}%=r{f5SMjZr_E?eT*&$M6Ze-RL*Waj!7bJ!i(+&dGz0 zo3iH;>m;n@fvobY_@MQDl=DL?y9n?^Zk^1kq`A*WA?f zs5`xL#rJD1Ch(rvH(_Y&#evq=kbYGNulwggk^r3%fxISa5}5^RJUf34@=f3gO-zxpKRbzHBBP$#ieNmRGK28q|gBSX=% z=J{cI6edaw8|_0$KC3St)nCo1m~OkppuA()6reG2xa%crUke<>=Gk#P4hs{?q0Pen zZUjK$Q1EKi))n6P3a%*b_JOZ>?wlt-cI_6=7nP#-XmPzdnNo2o)(#&wTR6OczNTK5 zCY##js96Fj`o=#J$jMMDDj*a$6pR8@k96cG<5l4n?~^(}(q{w-aTIhc)~9-0V1X=H zD5v5!lpdv@Au|HS$3&;<-Ijvn#=j-}q-v8fjHe7 zGanuYOMl)tSB)=sJF#jo%QCEer=u$1G#-&w7-xGQOKZGLIo!-(l`6LdtwElosJqd2 zylV-?A4-GMG|MNN5aDjxV!DXO$~R2QOV$oztaKw7D5M4okFVUHbc?kLqT`eJqdv)2 zPz72>yict#j3{~XTmMGqKSG+9I%_1iHIyhBO!4*ZPTrLy>0iOXE zK+f#$6x-QyunmObo_@HxtUcrxIJ(XaZMTBZkUU>Hg5uWZ7|}|vRH`xnaBMMk-}3Nv zAh(z(`2$(Ip*`}vn((pvP9vAkPEKrD)1vsArX45Q$WFUt@@67ChN+mSeH`-20ZLOwq&0H9&SM!tWmroOAJxDe=j4IayRaKl9Al`SVnLKU9r*lT%`~GR+=q z)^@|gBpp|gV0C`!=U+;`y=?CmY_&r)87~y(pDA^XI1>L(5fNMIuTaPs=c*pCcT28f zqH!$WE>J3CLp#8YHlaIKA}gpBhyq=%4tT`-ZnhE77VAiIJ@VIf<-;`B%x{oFqj{ETxKQ2PDEdh{ue4v%nSTG4 zgf(qNCeW|_iMTj}f5*RKW z;!Tk{@=VH<>;F+QDmlmn9R@$aj%3dbl|pGj$-z%Jwgz=Nk7s8p?77eN<`K}fw#7kS zo=2Gfx};0abaG|ax^`+UoLXlH$kmP-dDUxs#gt#BNUNI|791pNn!UwrA9Imv(7Z|y zo6e23`1x*I?Ta61JEDo{<_S}}x9l;{E?^*0UBZtoBf+Z1x##1(X!&x&WkOG zPsvRiINozN6fJNFH)=pOQ8jQ~(!e6nfR66Ax{aWWCA`G0S(}BHJ%_S3;rm z1^rx>MO7`E&`L&*v74=tRFJY7BMcqi!5*cZW`%&@pEU8`ESnT1PYb7-MzEF2v zb)QQ#d@M_v^(-nnl(`-+0^$n?Of<4sqfsXdq9;t|z~woBGj z19I})^3Av8nOP@t7EgrPg^EH|)ow)X8d&E1PSN8k4O>=~;Bp~0`mc4{S%leISkyG^ z0hBP|Ybkt76Z0Sl2^PE(#aKe=TgsmYfz7i|L*9sr&0rtFM~__m>09#T#yXSa@|A6j zspq5$c(Kb@T5$SJaR544b#CMvsZ2^~k?t`)nRxK^ZFZOLJN4|_+4?e5^{uyrJ+#8p+OZi>`owbpxfL`v=tvfXQQ0!;-! z5UOrDL(KsXv-f*+AYJeuX!JkUd58RRpO0&uKJsc4rhJ}^XS6C3+yORI?C2&|+6WOV zk8#2O;3E8O7SH2zM`P91l^tJKGGWwc)h>cna?7nKGCquz98kJ$P}j6+)fgsZPrH#F zn%Xq@n?&upi4Ziv{O`qx1&`Re;W(WUQAw8`wGDGH7W7%#acYKJIofj-@ytN$)P=@# z95C~A#BA?=o{~@MAg^cTr>FrpK>C^CU6SP8HD~iF@gUc;M5$x(Bux0^W>VBgDL`V;n4ZN9{|92{AL}#xWf@c?=`8YToUo z9c4du&k4%fgav^P((7hjo+TVb*30q)LZ)%=6^)f<0uN~wXRu`s{7MWK@2%3dP-;>R z-C3kdi?pdlpL(g9zHB*qHHz_4CsvpQ_1JqaKR}eBIgrl2oBmSylcP(;%Xu4*Qufg3 zS0P%7QrljtM$MH!?6h{iecY?$sPx4Nzh)$(y1}A3`oYtK0n|+6R52N_5!&nHUuYL^ zBJ1?#nGy-g%509K=q2KKAhNkhCF*_>&C)BP6KkuhFQVxEbSAYou5=kivdkOTIE(ZP zOi2}k#t&x&jqn4-iQThYewzP1V-sE-ZZGnJ^ueMR++1*UhdDMpXdTfAb6ljuoUEB& z-oj7Zg5juw^|$V=K=qb$;Z1dM%bX9Z%PH)E7-{;9SmIibLr0Eb;jCh3N+y`cIys-g zpN;`MB8^<7bN`;wUoB>Ftj9d_3=jDAw~%>hag2pbYo=#~%(MNV9=uy9s0{xU{n(z947qyiq`l{~ zZ*xxdzMAeI{}|m@6OykoWcmKX%u%!tuVAE?C>QIRrNk9fkYPJw7*&+mL3Fo9)gP0i zI+n!>CJMa=WIW;p@?Txie89+037+BSNnu7;i;R{G7>e~xZCG->onC0 zwVuluzlBd)s1g6|6?e594_Iq(n>L|J~bsvD3g z`RZl#HFs+vGB5}~+MKdQyFH3JbP4+;np^j|UssEL%c~lPogr3aDgprisOj#k4c1BT z)zLe%>@hg`ks9FFHa;*_tQ)r>1+5!Wu}zzCB=NkFPepSk4mefIHl-Hu8jiki!^L>4 zid4TPpW;rTz^@dPNs9!!C3!8MtapK_Rpt=~Se?ooO5y?5!Lt^aUvE?ljWGy$>DMa# zy_YV!{gU^imnUSI%6uL+z6G<53Cc@egiXrw@OqT<*H=WXf=e+!1Y*rQ%d7axU9b

(P>~`usDnp+;!|35h~z-i@j|d#SF|OLq+FMmK?s)LzTXzP7F{ z(@4%{G^CgvIP8|#OwVEkE-UOs2$NlW878~SW)IwO;0TJB>@*iLRB-T5vAF8y^>JxJ2@{k3)O(ug_{RDcE#~yYE>hNN2ng{ z>BK@??=^=I=;vG0PYKDw9EK*Zte(ErJvq(2iGAV62V(R_$%zvu;t%}J$ZO-9Jd}NL)i?Ik!{xUqXv!X?6!3RNk|F@CKhgZZk(JX(} zSabgtJ+s=2%VkRtrH0A?f`tER^cIcTn>)e+twWMud{j8*?SyX8r|6h1eU6i}_#&ly zA7iNomIUd_YIU`Vx$!n7kI~Qg`%GL;si2!hGxBhM6$Ppv%wU_xDPaz_M=C!6x881@ zZFTk|lI-4Y4#c;VK6ICjYH7h^-zji= zXG*N2@SFqIWtP5;T)|rFVk7APVBetT0OYAv$3cay=He;71l&vgR4Db+{u;o^J7^r_ z7=5(i*5w^LwXgRJviw`)oxo!A3H+SZ=3BYA+?@Phw%(8I6-&m8_@~>h`I;8)X5I0| z{8>P5XccGGCH0@X=xjtKauIv|OL3Z%XPL;A2wqm9H&a77LSg07Ib9;T1J$cKH&o|V zJ!|PJm@HS}w-7~Y(H8wc-|Dw-1aHPKj6eCy_<6_g9g%P$T`N9_d5{GDb~2rxm+N2sH&YL?SHeaLo2*do6Eb7-eG8hg zLe{QSh2dq#4sZe^XiJ5fooBUspLvkJ1aO?Q=YqDC1?#Um{&PmApxlAGk|ur%H1U%( zQKC2NquHWjYyrit5gD1Ctb6h`tjAcdj-~V0uyw)$bryv>3%ibOnBtPn} zx5t)`Km!#2ttU}0b$zOEcJLai*%W_nXyX{ttT1=6Oj-KBM;j4%yqd>DqdbXVH*6)h zm`0}wYpcf*!U2wQh2Vl+iNW(H^Xa)(RuS7>9fb!&qzmjcwn)x7=exJBUw!!Y<({op#2r||0?SC)vMSa_$d*EwSoB&oEz4fpN7hU` z<-4gnGlXJZae8#);}wu^RA(A8?|d5X?{pgNO{UQbO+#&zY8qR=ND<)Zdb*5$S!O!$ zJ$7!!^GUFG|M{QRhP*A#q`lcpT6eS|=gaXTosD^Du&fFcAKpEG@!fks%b=BS-dQ|8G5espuZ~if5Yn<}IM92?Bq>Vbhgn-V z7c0!I3cDzd%bQM4V6jG(kEa(&t;yA1y?B}rsM}Wqdj00j>vu2S6QsR(`mA_rl;U;a zm?(n+_Y7zU7{w#RzBk+pk?*$cf7w5pd+NIYTR^10*Kc1ve*b}v{r{i^(|~uHF3y&q z(9gF48cAQrsuf(aof!mZ2`z* zb4}#}Sy{I#|C`he1NZaSKYn=g^6`^r&tJcM`izLhc1sa?&*>H{WLcNDGVFw%CHHNZ z6m+b>d2tpmL|uX=o=!ZY-KWpqJ$w1=$@`}-Ug09{I6J-jubmF=^gAMsMlq3kMCZag z=6RZBSbiv;p?8p`{9O^Qn8FEsnCb>5AQ<@>5Vnus$KN*%5Mj42<7?2lpgvLk{=tE1 zp^gf0X!Gskr{DkZs;=vCz;t^JLbQnu)yu>(TI`$%V4)WsN2h5#S!h0z>ST+Q#&$WKe{|2)cSP|%{*I`~ z@41=gtH*!+K=;rO&!R0pMy+jz`--|lio!yXr5Sw(md!NQOeezIRBO#i za`_aJ`SvbxMDShKt~D3aKR?w9M{0?my?OoQ`JbQClJ$D6{?4G;FHGezK>Knk2O*>L z9nT4%iHeBF-wt6UpAJ8PyP@h$zs&29lNS@%ld?AYa1w@ag11lyTH`Lh8D$%T!Q$ ztPpi_N{l?6q->a`1g+Uw^q}#;85+UW5ATR6{1bs!T#`+n|f_tj`*5--tMOMBbrix_9(DQu$ z?EqH$cPj5D%JO4;%924w%;^LkFXZ*{=gI6mnlC3%o}105!z9aoHd~-85WKXT{dTjh z8k7d8%9O|@SaFT7VNY|Gm$#qs$)u|lf+_ABL9_!Ak))Yd-iF?N;cOX&@?2?D+k+F9B5H-H0 z{U^qn2&>0$Ug(j_bs}V(o@z46nWqVnvjm?;&zst94$tEG<3+V4-W@9+DmA@=hscM2 zc{LgNy|0`zO6%4Osch%_>sdlK3oX(s9+Sljqxv5IwcHF&_T1+0`7Ur`h_TI+&V-18 z1@UHy1}8p73N-JTQCWM&rL!&7P^G4z(LhIR)@siBrZA42PQY8L8D)91hder~@x1WX z$yG(Y_lEPw38E;kE)!3PI1pB0ZjIR~O^=+YE&62IZ)^O6}f}0;};9)v_oNtGr~= z_~9KhF>?sQ^kLMfR4eDNfG}yWkxT+ZF?kK_i5UaqvlKj(xk_FPB_2)zS)(k9YFxjZ z5S^P2Q$iG#<75$6q&0(IaJvz+bqxt1+?CAJ?4#+09X}T_rg8^~E_BK7uH0hYIm!#Z zo5Zv1Y`SoZXDZb$iO$~5veAc&xswB5`mbJL{VmRY~R5bBGGFSbFxq>`i7i38{aWxdA+~QZe|9ayz%s*gzTCumyxHj1751N(^ zPVUyAZDg064ozh|g`HD{sM~LSYBhESjc%{e-rim`cH0d?*!*v|gCxrN)Y#pQS`EUn zQLnvdw0nc7-KNxDv`zoo-9?{%j|bb)VDM=>q8~l_v)gD>R%1X_8vQN?(b(xM+MVr2 zXZKToCu&ihR@CV(c018_8~^RLqfUoX+YR->X${VANa0V7ejg)g^=WTuu&qXaFdhsV z)Nz{zy&df|x;vEJ=yV(1F8w`gbZCZ+HfnV+Od39Q)4xD6wb(prz~5pFz|?nE{pQM`(e7sL=&aHzbyM9yjL6VqS7Qy}eqyhkcqUtrDix;3+jQr%#P8 z7IT|mdqCT{P2X^SBg}4rdD2?Z3ZMeOo7RukduP$5Rohl;5%qCiyS!!n#sKyEyNw>! z1gE3bR^Mrn8(3%l#vl1tWJoD$hRz^uXs30y)8Dxmw9gv&Yz&aK-RshJ8 zwA%D1+NR2VI^i8mg%j{WT_CX=NlL^uR2Q`U{hc#F?w}(OOve;i1B_$9of9~pHG10} zf}!mOM$+AGsbagG-H4%mK!DzAbP3K0FVTei_{L-YM3{-zwomIu=>+Kg_63)Yx=5m* z3(iAVpHMX7x(jaT%fRQ?>Gb?~dUZFDgLFE@W1v1lb?srV#fyo@}4ttPI?F)ayQ2s=BB z%lbT9{9n%+_amJ$x=PhQNo9oEGNnik+nW5gHTfNxJpD0~RNudRG6*LnBC|UIe*oH!hJIW; zJW4vb0`Kx$fhcbWVFQ_C#Fch*dHt1ZbxbXk=jIq?5Qyw{Y3&%^RbO)CwmZ^S-iwTg zs$pH*yyE&~IbEvG_=B5Oohi^7V;!m2#x9}5KAk4(A5p-&gf8`|)2FyWbYGXrfBQ^ySUZ{s=Rz6wHM_jb!i$;Ps*d+L8R-T*RH_<>hsIMWig#XSLMTT zoTbAOkm=tJWC*861G;}0r_+w&w|wGqp*vn5^Z`&E|G1ku3`HxUWERxXtxy<27tzsU z?52f@HO8Xtki0G>_gk<$-|S*+yRXg1&Gb?KSZaQjc0z73rJgQiZr^yLfhfJS_kW_N=#cZi`R@~ce; zx3zQ8YtvVvH@87`)5rEujxJz^I3_+4U}3;Mf%yU&YJe0YmJ)Pon;9v}qo0HaIxVoh z9RbP?_<&vh*ajNw_S)b@yTovVWsSB$=IsclEuYxj264$mXLtMj(QY$y-=QLeU)rGT z2R;4-iAU7iU;si7A4p`Xmrn8k10wtdDBz>L+Z#6ebh9;fcR^dz-yY+&UbNjOl8-9) z@G$`4+{SMv$cZq-Z!jPj`7Tk4E#ghL&xn4YWurlK!Q0Tly8W}A9dN7YvAsPYdK>`N z-M(lKX$oL;F!`vr9rZioogOXz?nNK0Z5K=cK}?5uhV~Ha*ruhUMIaKlN3{F6LEH&g zNcw)(>Gg@j_%dMXjy}xsQgBCi@GdR?_I8Vwmw;n88UR+>*uP!k#fj(J0qv|l#$atD z+H2a1UaK_(^N#;h8r9hWL=rVizk9p-GsBhG8Q}O5Q#T}RwF7|&?oZ<0And_+qF*9F zBQXj+0y??_>5LL?B({L|qYHL^mmzx_GL_v1HPhMM)=d)>v|$?vu)zoXhqt) zG|%3){v0=Uag_p4+Z`TmpPB#>jn5q*pY0YHCCDluQtHa2dJuH&V&SQe2%MApo6)4O zA%jMDXE*8%J6&!k>a$d$qd$qsVX0ONr)(Dj2aLZ%Lv8oFLl&c9Yo%ePdVoA|c-sBD zkWAc>Oac(nCqN{?0AeC+HsFso$PuKHZuxuu-(-1mM6fbVX39ynKVp)B2-)0|<#X8|V?`8z|51D=8w~ z*M0t=e{|8%%v#8j#7JeF?a(n|OvneMAE~?xE)V#TG+hT9OS7SdFfd?w;f&iIi^fBG zTNq^XkIOQhsd9vRL>moNTmRz|M^x1gy1*6E?c;>f-!2bym(gKg{f80}&?wSS2*-kN zm%jY>We|2gy-4p0(?L?u?v5LZ-yMQl!dVy)vcVMt?1H6h4f21Ag>K{W=(KV9?D8it z00cC7BMOWs1UsG3J)p$uq-As&6tSkz(3mkZTfBVB^h`VNovT~hQ;N48DM={CK+CR?&!S(*o$e?fhSZw!|r`* zuw)0K;)ckIxu7juwFJtR5EUZML7$qWYYbSby`Xyuu&~?b7Ym|L0ER7E6o4Y`5V}Bn zVL(<7?3!2A-C;-dc$i3!yJUeIOG7j z-p+z<7upR1z1F%}K*A3Qq%IZ^b?&f~9bX8IEIN!pAfXyCR>2s1Ou^$u=QZM+mq-E{ zn&F8%a?29A22up1BvSi0q6D4tp;99$6iF2gN|ChqWgs+4vyXQKKA7&{jF=F4I>8r+ zp=lvlc5^o9T+kN=t!$cE1cX;4#1St)|<)os5_cOuxG`Y|vT9N$>39Isv~%P`yKkfoMcJ zy1fBWA~c&GW%VGB=i37mB9Xy^ZGIA=WhPp^7Lg1A~X>GY}>{z#%oT1Jb3ZcmW8tSU^L~fir6{2h|1#MI0J zNM{{{3h)lU;W9Z#NRGCFaTLvnPy>~xiT7CYM^_S!W3Wp%NQ--?+Z0%9cR;*i#P3Gn z%%a{vL}n4*X(?lXY18f5Zt?2@?%Y9-sT&X~z_B0&w;43L+yqFqHepd(BmfE!ZUY1~ z5jZ=WpF1^AE>zpCYJUQr9d^d@Bj^D z*vIL`D$%B4Nc~=hH<4{TaZttfVAuhwW731}0$MYgT(|#)I84;D?(BI;ZY4j?CnCd(WSAs zp+>`!0Q$EJnh;dy8DX4@HpH5(!LR524yLAVO!=>^823!@aNa_?H!2i=<@A%L1FBIM!}Us z>=0`7+VZ_kqy)q|J5&OH`+#7YOm}BY(<0Kg_X(OWgutndO=a2m+xGTgl3bR_vSEpU zHrygsPcZ9(cLErHvHjD*-&RaD*Fb{^*`T;+eQX?L)5>vp-yx_0}$dT+;T*G`aj z`#q~&uClIOf`?l-EOn&ityB6(C-duoTaJG3_h|vET+2R97Rme^9;(oq2@$8lf6nIc zX0YIPuePqED@otFR{6QQeN*|IZD>>OucG!=H;SPk%uT}s0te!b7h=J$eQwm9YKK&- z9(A_5M^;@G4{LSJuTjgYBpc<|X|gZc+yGeOvp$yTSRQEY^y+j*K%j1nd9ql}Cy|cR zhODMq7AqQZ^!LB30@Y#(C{I)nYFM(OuMX(O+KRejJ+EB<<^z1fvkwi?&RI3c&@g39 zt$}Vd)URcv_TY)hD<3;YRSpf^%DCO~s?g`RS>LY@R*v!gbQT(+N#0;(ukG&PB(1ot z*X1L;UN^#5?5{91NIU1Y;}kE5w;rko0oQ9F;9K?`7aF^rvla%NF)0UiJ`k)Nckqzu zJK%TeoRv+Qr=wS5m+hFuQL-_5o+5mnDc7WLCgVALAbOcdQ;olQp<_Y6c_DQcms3te za8IP&%Y(fnr=58`s?sNcfd&Ek1*bDREh!IY!lnk5?ofV}KCK$`i)v6O4^fS4sGANe z!YdTT93C$cV(lB`HJeRYGrG{|D&atbV{!ZR?k|60x9bh%*q2s&xe)K3-0b(wZ=39N zXgbN*JqIj|r6c94CY!|*ap=fDJF_&ZU;q_00uw($d06i>(nWF}#q)W5eG|psPDWX@ zM>n!K`#1(XWEj~)nrlidkW^Ol|Lu4>9VhYRhVMD+w0%cCKH>hV+@yEdc$?zmOfnDK zM|82u*Va*u4!!Igds%7s#9UjJRga5fO&GtIlu9sFi7RXuTRE6;a^m*U!)SK0i-CretdWNVL@YNhOiJ;oSb@QB}WlbD%v*3y$| z;2_rY;=t6-3g?-%>QUCtec9{dNEfa?q_!9(iubkn1kzyF=bTSV1)J+5sAY;yvb*@A$DA%iZFDZ4I?7fBgta%l}( zdGQT_bnXMG>Q`X&4uN~!{qbhufHvGV*JX4?AiuxzFc^4eBU}pt<^)W03*hHDz|R8! zKL^0f3jjZFo8(e}x2m?oRXu>$)CIt4&fqi$G-0&jxde`vaI;OBSa>`i*7?gjMR5?O z<@Cv9IbLS@x_&@vRVGgBL!U8y4zK7Rk(0#Xv6EFj>mmFo&>zI~kl_vCD2M(?idjjj zD3YiH_vSy89**pz#Rk8WN~{F^eKxUTB+pgeoKtfYCau|fA+4~_n>>wLq!b)ilSBrjdyzF^Y=>+j+M0TqRIXCPE6FI)B2U2jn3KY|x60WeY zSDv0IY4f_8*HrVmyrw3nAa0qQ-Y^o!s<~v9q7@i>Id{LuHVK&kSSO5Fq!U`T;^rvI z?_b;Vh1+A%yI-(F?OO2oC~~l5kMdpEh}oE1>%JLx@~>^qPoZo@2`L`j>@uod?RAN7 z6(#80(_ci*KnXMV^cP9Zg(Q2{cRMf57JKjXo zp7`U=n^B`4U09=d8ZTlr<_|&lWp|;Q)OGAq06!EdvUB4|yi>dIUlc zBhY-{fqcroX91Y)r~2jnah5IdcyWo?3d9nQuA`5W>7_VQHRYLT0}!cNFQ?OwnK)XF zC8U{L2rA-6o#O!G|a%nkxFFE z^^zsLC>rR@iK7jwM-wZY?)%qIU++aYxFfZC*>c7nF|m)|n22x;G|EG!iA^W|2#4`} zmMqvC<8qO}(c)r09eDz=o6HF%5=z6L+R^Xk98Ybj^g@PlrsAX!w^UxICIhZX=InYX$745*t0BBqz90i0oW_}i(Z+~^{ell^Z4rR^zx;?tRqKj3t`SYGr{$#?pRm)KaDAKyW*1R{0U?C91 zJ_z%}4Bw&X#)Z&8V2WQ?9S3#wVKpYW5dO_?{><+ZJq8L zQRfiK1QKd&4>8=P7m0o=ryLIYjK^_=79HcqaK1cQ2r+vJN?G$$Nh%z$iEXE+Cp=E6 z7jJt2)r+D_?+cMgh=9=bE*qg&`6dq) z5ZDfQtKY3Za5ojs=Na^OexDB98JM1o6Lzl-ZGK{$s$i-n*R^IIq+G++jQ!ygjl#?q zk$1~%DZF5^hcV{&O^10u)f~bj;yzlPksj1DvW!^RyXVq;d4~JUJcz zut>*5;S-b)OZRiF>51~6zJ3)=uy_QnB7?b!PDjyR#G%G%Aud$%20%Rj)A&g|xxiC@ z^>k$OSx_c@8D9%=a5`#!$P;q%El+EHI2{kUB?!qv>CG(tHK{t+OR!iH56`Lw8b<*( zXoMI{<60!2O`b>fH;z-GT+2tJWCjQD_+fi;zcDAxR@C18-9tZ(a|V$c;nA}g5lQA* zeh~hV;P=*aY=IwQ6A-}VgL-ln&lk^SU^YCc7zlC$Ji8z+`z1>a2}@R}Ho;q^9<699 z2aHiNP8P|EvH`f+ZvB4W4mP3}SOk+!uK8rzMNMMIkQK*-g5E>Ury~a13?3x!gSL0e zN4-R=ZlmX3cn1Ph=K7~g3!H67IFT- z7%$=v+<+I({io^DghqZiIvFnx4v!$GAR=!FQDmxR8I;B{KZ3I9^7O1}8d>pWI8CUP z@6;Ky-i$8lY(@AQ@;P+sUlK|n6P?G`$4T__1r2&3LFG7Ja<@ZxfhL;mMTc8rsPS;? z=;r5$lBOjhFNaYARi*~OEc``C3WuAD#>SE)^El*bkNz8_6ai;pFP|2?$}3y^Z$DIhy4uTfdvS758SGBHmJX zPPd;#fv;6oo(GXNpS2vWn)Vtt7nKmF^&K+-RjEYGZp|Bp8g=!f6-AM`hb=ZVGsz$I zJ3<4xRjNd-Od_e`Su%owWQUN=v#S|3k}Z!3msi{M$ZD`ttL=w_>6}!#l_o48Y+fXn zvxh~g!U5%zF0U74^F5)Zb1o`MnY?n>$07hrG7StGZ?N)<$F{-o-}IbM6d(IfI^*|k z-qJSHwEriE=$t482YCS?FX#m5+;#O;R4cFk*OAT*g6|sKaZ_B~M;B#li|hOJpSZXe z(Z5G+)`0jut+L2DRxh%?kt+)2W@mUB!Q<(%rby?+d?%yRgr5}Ysz&Vb3R_D`AiCN= zV#kcc=6;F9#>S}AkME~XxJye5MG1T47*|TrJMV=M(GE(vpEtSo)(m16eZADe8>qXU zOurq+laH*xa`A=tv%U3->Hdc!V#6`eP@s1Bmcac?Ecz^D@!s6Q=ZEZ;Wh(_4mZk4VA5sn;T}7Xkb$XL8lSTLt@H!r8F)BiKo`9{9e0FAPHhV5KB?`L!1& zzg3;$!k4fGx25P$#_{Pt0`-3RrN6q2zX>R z_f0Iq*NbL08>fU&9{JcL=W(6n%d>QxRGI7LHq7M_CXg=Bgp@3(JJ^&EcH9yiq*`*o zT$XfXXMPo>rHrnW#)VYTBjJ6KBXgx}W(; zna3iJg6*feHyW{s)S0(|IMi-TJu>sqjdD`kn!M2`hU)N=23;tX-8mA(#-!(TXim~( zq#DafQ`QpZUE*=^q{Bq4)CAP)dn*i$hJ(^dN`3~${6>tJf8!9*`GjB~AkUB&unh<^ zSaLPG;J)U%WRMR@y$`iC5E9>`rn}U7f#0y1qqV|;)_u*a5_LP}#OSFRs{uWpQHQ-w zgmUEA5M5KC^owb%+EXML3QWSCUW3IehR=_hC;up3Ll>`&(zc()@c2Tt;f|aetF5Zi zdCDqcrUhCfUiHFSaZ_R&P#FJYlFYgKYbdH8m=@f2@7ulZm#9-!y*h5&wdfmJEiIe0pbZ0X_zS(iiM&RCA@H&x3T6HPP(8=jsY z6tv1j;yhT|Tp|qu`)eR+dRZ9X1)1wXQTNO&?#XI~xEVAsIftG_> z*O1A;hJ6$|m~v|St0v9ud2eV6ibU;HfIL+DW}Vt|nJW5ifx0kd0BYSv3g4K8CivyA za(T#$(Cw#QI`5T=-41a_U+(bPEpU+pJo4Q3F8BB-i{do`$41(CXypRY(0ChUfKdLI0m-7l z)Q7XzzE!S!rd)y9OE#8KF({JYWOFo`&(~7%U4`Jvg%9QD*`BtAP_1EXf>-Zri09cc zt_mJq;2U*%SI%U!*_6-dX2r{@JJvNgrnX9?a}yNQxZeG7BL~F z`JvIyIbs{ISb;CIL|TO`*Wj^>ivg)(=Prp`g4%w@)^8t^>uk?BWPZ;o9Jwk=-O`b$ zN9tL)&gvX7J0o+bdfTt_UR2Givdv*F=fUW(O)nE3dM1@v9sfMOqH6NYQ`awxs%|82S;@C=|wt%A&jyz6>Bu82gNw(lrEdw^cOfM}5g)&i|!t@sf z&t|TqgEr*$;t}ORFeozdWm48L5c!TCumzit#wjD?i|8`85E_gl6;^~H_q7YM@hKf8 zKrHQUwn)cg-5R_B`rx}Rugyb6OVl1MiLSKrHpc~p`2b8`W!dECMA+jPNPJI26z z-IQ3E^Zommc%CXlaxJ=3H}enh^`}rZT@BQrb^RzDe*(-f*K}wm7d#u1>kA>TqSn)| zv|#TJ`X;lhGJT>>$((OII>vd`X5dcg-0h`u2N#1ozRw+#b#KID9#P=#&~9p_NKyaC zB#SS*Z_Ft7G%1*S9(V_&K3pu}PHLfM#%%gywId0BL(vD2{EEjkZD}uD@#Ol3>T^UFUL(bG z1iD?L%Ym?W#j|xSSkG z7|UeTMBuYz-ki?jVY;~HyH7ndg6GX<$q)|m^ySAVuFjSZ%rgti403Zi7b~L?J5^!A z@L^=?Nq4xWkCU_bBBfFAJG`)z?Au_eMoQgwX}Ct7l5dl=PwIg*jU1a8$;P% zE8`g^Pzi)C@J9Tz6#hA<7-WV-4@pjD$fOE^PjY3bR`lueRfS+^54NYQ3zgJ@ZDJ#iHAsJxSyQ#38{#Ln&lm`=D|=?T zXDK0=15$E0vZn7t%3-a}r{ez2?5SzNNnK8Kx)Vl07MbsY9J?*^WZM?)p(&(LoWg09 zk`*94rW&g4JLU7S6l*N=DvooBoVgL6-AzA0Jbte%@652k3ZKL&@SzZwF1aaLF8%*%I)t&$Z_!HKh3F$rmnJ4%z^3T9^4zBM2(n$QUR(|0ql-pKMV!$*(We_3lw33mkFa(>yi^Do0e zzehP@u1Mt`q8;nB6TeG#l}P^v>8#xep9T-=938a(;>=F}GTeH=CcOPWtTs)kZ9U+{LHJcIbn|ma)D@kp1?}%NIMp1lf{g zvwwX1)BAEh(rkgf-8bcYO0wDeFQ1pQDM_+lzI*%p$G^V_@-K5rV>7H^zm$J0bi=Bg)2fKSOkKe!i;pIQyu`87i`9`zz^ut*G z?|1yuYId3*#!rJEhtWZC)I99#4++gXPll9rN2sIJUW~U zR3v~;J&or`okldRjA!-BtKIke2WqeR0#`6|3E^n@7dW_RMV;uIUg$;k=WH<2tHtL@S+V<7UGyGtp&@=R0rSzS(`g z^ZI!Ir#FPN8m&W-htGC^;|!-jNUpm?}NrjfcW`h#0@_XT_L>h;0i(+7>~;PCZWP9iECrlV$eJMMC{EXCsUH zkH69FC90YR;%ytQd4iBJvnEX_0fz~j(n|}iV4g8O=4Ty?8q4VIy~;$C%t?L!(U7x` zbsAb+1%_%;D{KV5aEMeG8jepCb)No0|IrmoH|%$LGXMU^ABTH;!~Okkp6A~czwORW zv+qrWIxa9257to=yFUZrF>y1+;vKO2>8QE8|F-k(qh6n5+RYYco9D%Jvbi`N zZEbBm{*PQZIh}{yM|?X7KryonE64IzW}(D7A@$px!@*YU;+gEhO^JGhjs)1iG+@^B zX_`zBE{8@>%VlJ|$aap7xufs5BEI_h)`zE)^6}1HA0>!ut6MSl0~>=kM~IXDdJQNk z%z7GW&KmeMlJg8>(|JQ9MI>uXSFuOu<(vVy*Q5SMi%ezqwh<=_pJurkr4nX#Z(Ntn z5h59xBVj2xNYC35Y(?oINlSO>Ir*j+Atc(OKy)>anL+4Lf3xIF&KVO-$mu+$|9DQ| z<}jNwX77FML=8nB*oB72@_7*uUJ`WPHYiM5Km|LrhXNh@ng=z;G3xPLgIWw|Y>vR2 z73K|&lf*U?hlJW63{_IOk>(o&SWMUT+R~8*f$TqfzWMC=OM#kBpD^y=*>e!L zh9}B5mm`OVEFA^Mm0xlol{avjseQr~v|!0qVbmF!ZHyBG;N;u0tscR#T*6A63A@Xp zAwk;$Co!aYf(*GfcmN!!5(uB~X2alKc^-b^$RRDRtLT%K?=-!l5%Mu>{Lo;dp0kFX zC@&RII>bAtry+f8L(}#j_j)<67Ov8qqbOS}fN;J7EnNZP?6lyJzMoDu2M-=Rdh}o@ zn6CeLt4GiC!Q-fjt!0p=%W{#aa#y$8Z5cd4*U^;T7V(H4CkyCtaCgw^7(cOeIR5Dp zs>qXO(ckH?w!=|Zf)M}rGRc_@vPr{yF!*L`QyZIQ zmz{5WbXn1l`+aVb7V;H6`S;>aw3Y7`nY3-Sv$T)P%gZh}15*^^KKg_6WHJ{-8IQ-u z7in_I@}Ta6?x5dAy4UG9rqjV=vo!M+;VR_oEXbSExxiPD)xc)`vj%RaGE8@i?yD|7_8xiPPi%OG>!;J3*G8X zU3$mGtd=l3&r--A7=>WP(YtusA7P*Q2#XG5N@P)*pXOQ2pPgCgh z5`d-%$3d0ot_8^Pn&{2Wu@OY;DJ3)}o>ak6?{IJ;I)2z+Zfv<&P#P6)4to7ZE$LSd zdYaq4c<>02`>^I|Bt(-(cGBd_#5bP0{HA3+E3CSbf*y%wsPr;L+YGg4sHx}b=Un7r z(N71@gS%01u@y2Q#(`g(7Krc&-|LVpU;yW$1B%jhmd*a`NTtna5Z&jP%l;yDT)F$U zT$_Kj>YSPb_|)NT-tuY(VG?pg&eb;ty>W6x2bD*F6(HmlwKu~lbp1gq%!a~{s5K)d zC=6u{q@wD4B~QP3U_$Oav^2wbg(-J_&&*N%w*<;?`lRI`G;1EWPL4q5MTKf~gLksi zj+}C#;TV~~XKh@1k^qFf8aP#=IbnUWR`P7##5QEC1-g~QKL~2*`)b%WiZ->^Z8MxK_b$=Q3qi4;4z0TBzFCCys|NJ zV5C#KWcY98hPCps8RM%LR#%$w6Fov|kQ%h&By<-wb$4zM3PAecpTBB&SAmmtmd|Ov z>v6Mbm(59;Rvr7LjjF{6AcJX8YNk1D(_^m2ZfvC7B5$HyB4hc+UA2ScBHIH;eHZFC z$eXsqA-3AlxOKc77?xw{89?S&$APq-}H0-iZ9UZ|~-c6ZbMye+~e-X(5@K z!E=X3BO-tnoXFAlefak@x zvv7_w=+M*26%A@t(oj(W>8TI%0`E?bG)5%7lhA>G-X#viM206FycV1gz zQ6}-68+4bJ%C^I&9uZVc)Z#-iZbv`1qdnuw0~Mf9Pi3rRX62zp&R3!gyU-NxF4J11 z7M2&84;LTTL$aosI-pgX03Qd0!pBvT$f;*k5jp?_3D)qf_kXd=?^+v z{b;Z?eE69DziXOOHn(N3%B$chQ2+^tO1nr}9-I4xHhas9!<^!}(FDb@hO%-k%h{gC zV;AXxUyq1W1_f-@UYkY#fd`l{l?c91GYzOVPDUwspKLec#BUR75n2{)tx_7LbNos> z#8F-5*=4iev&CCJiR0q<9-#76pRKxn!VeOg6UW7kd=4)2#hx#+IWr|~=nKqI-a)iZ zmQyC-*z$r(Yw$wmcJeEV`QA2m6}@$p*Ed4j4Tv)Yv5bX-nemGq7d$_L6OT(r&sK>P zd3l~qgd}Cg%Ui=B7gBY>u?f$)K&)weauNS2Wes`6paZKWXWrcIX|w?UpEfBy6X7bP z1Pk1cBCcSD1wXR`>$*)A7rfbh^YY*TQb4W07sB-b?8iNe`bB(y@ZIOXYUPd-z>8;-1yitnL>!g^3#Z-LJyi&MNtH^W-k=Y1&b zpEClb$)hX-euaFzS!`!oV{Hn5;C5V_?%Iy5l11>or@h##op;+*ZQVpR>SX>!a3NubHXU zJ83q4`1+U5*VE3|G?b>jB<=Et{;m9X0Q!(9rf=RJL}$yGj&(tp|AGH3=rS%dtg$iU zVjvUz@*5L2HtW9S>OAU{#^SwYB^c)@1{G5*K;EQ9H~Kq%5RwjDJHc{)CShAtpa}fY z=rjW3Bih`SPhaZdlznt}DqiEnVVWtf1mJg3pAMH>V8Wse0$5Y3=A~OpYOia%7qsm+ zy>5FnTA}KTL5vXR1CuIXVE6CI)4 z)7co7?%-EK7{DaL2H0U6uF!2eI96KrM-*<}ZPUgT=4nt1qur9;Wwcg4fAjv0D}2>T zvk%RDrD3|)n)At$PrpFBkZNsvOt4!Ublz^?@Nqq7_NHAAXngi4FlJJsx$Z)BwC$^G zVFHCRxi}ejM6Et#p=Z9(E^nizYF<>i01Ggr>v<0und3kL;_`BnXOHG~ zFlRnccRfJcfArWmHOjlxaWXG0E^2LfamZukoMnSEj(yD!8KT2LsxXO>T0z)3i2Ers zzxh|tx{t~4(^VefD(7b=4QBuvw=uLyQzVtdA+;@*TXjw3VH3uTsI~7vKh!+^DH*LTf{+uv1Ad`CXPG_91mNK z+!?hYxuPm)oCnlMZYN1Wn3j>Pt4mW+_X_Y;DsE=1iY8bl8N0-alEorhgnmhs+hSMF z8kyAzf5jDjeEU%c?JI-51Yx2<18Nw6!Y&_+jFoA(Xge=x zAy4qGTd@ww*PmzCr;!kWGlQ`ZkH*B)wcJ$tb*i>sP$IP&w!)D^)}9JpH^2TcetL>% zzxIL-M&3M~5=Ogfo7pEdoHZ%}JUA_Eq+Q!X+audVK3ifpnd{BO;cniSg68SpZ!z+E z)Q%ptLRJ&{Ac}^cBh4!&u6le8vV9iXv z7Hr%Y=-a{yx)KuBnxRM@4)RbU^5XInHFsgY18u3w)YY(}B97G<^4Vh=J8CxeFv0x> zYd-%+WH)$2R7+H_mGhel-OvrC&nQ-%RhyQ<1MgKX@~eXS9?!t8Z`qSWkBR-4&)>dz z0dDo-!^iCiG8p;S{IP!@A9;t$O}lvoHwnIxFNUSgxbfDwvQG_tviSbXsHi*{nAR$B zaJUi2!|{n&yIn$~WjEBtlbViOuO;NFWuB{v(O#nz+TM$ZQUGuZVTiLKc&JH7+vMGp z8xFuW?2|thlLkhI9nt@`p{t;$4&CbI4EoLbj`bUvX0uPpO0M#P?Tz4>p4)?v#OiDt zxh7FJ))M$DzoFeX(Yu}Z2fNRIdcE@=HO}KP)MoLxn6VLPPKaU^oyCijm|n9iW@8a; z%WsCoG@{7lL^Y8Bt}5O^B^vTwqD2-7y9&%a*JaX-Qw6_F8Vh!qamlnVM_p$V)C3MG znPtFKEDM<~ISi=ZkZLYj8mL4RwN+DpWqc!qk~ps+)KoSC(Ip>d4B%QZq|$`%1dk&5q(6R_XMi)_w*A!U&- zMGzhRY#6R^R>6|dWiT;OTxaV8P*NFA46kkI$l7p`qge$-s!F6rpF|}Z zCYI13W}W3pOz48$iC8Z&upO}VACYz3C12g_vaIYb{uH_i9W zis7>czP#{EOY^KCgqE@hOl~GHf0LN82nI+U3%PBs-WTL#*|JC!j?rgg{^`2mTCp+@a_cpoE1DjK(m5VSo02P3v*g_*Wd7Z5`GbTVH8hk#ts@Akvj}oCYobiC6*)4lf`*F&+Uy4vDxV<^W4PQ3A$rW zlSfW@I)1<%#_$v}IHAj0Lv<%6#xUMmS_gm8Sm3Qm_n8)N=II&pa2yLlwIR_VWIAGO zQ?kre2ZA?YR^A)-(8GN)5&`Mm1nv2J=gph92Q5QU&(-@2UUO}D`xSrMIuc%8w?rCN zpv$(`w6R2dCs*+lv}|T2^!fk|p7LO&wR`O?B1xh#loCbbu-^i!>{W6SZS^C$4fwv_ zqlsoxizUv+;;S7U{P^+>d*(6}u{12MoR>4MWK5MdX)56VZ4SPTnlSTx%#+;G z^aq<;J^bx<4e{b`rffWRe)PoCT0)mhVJ|A7qwT>QJSxsRgKz05o4%<})3^3%Y~*6> z9@a;VBM1YM-Ke z4Q?YBW)-Da0D@Y)=d+xFFF_exrPJlqdx3VuTu&`^H$+AQodq_iGcO%acvoFqd4}t2 zT24bApC(`3t0<9`G{@vRndUX>aZ<(OR+JZay_G_!@n+k+QPu5#+QPw_Sd_H6pJQ8 z_irmzXqW3_RZ?HSuS?ysD@Ds_TkybFH=p8a>ff_^&t5KvDlv@oNk!zITakI9+NdZ)y|*OO<4KfmwO3Xqqk~KR!w*z zwkKZ`VQ7qUD8P|meUTqCEP+cH9%7c}uHoN~(~3mZTfA~SS9nZKXJP!Ba>x7}74R#P zK*ci^?L0=Yt|h;Sw-4n}RZ+Q7+^a*Esm10FnYU`g<&N>NX|}5Fn=9p}dS11pgVpjX z1=!UC-MVtuUMawFC4%lcRC!0uWFppKWkHW}a5cMYZNq9FivcN2a8l7@8HaW?Ygf=K zy63H7@3u9(ZT)UqJ6}fSV~_h!yiT`WHscVJDP?^DADttEPP`*Y>+23ns3+xC!F4zE z@!O_rv>ld}-iN`k*Oz|5N~UfOUP)O3xj?K((TdMUcnW>Yoy&58*r1c$$R& z&#ll$=y;2MJgV~o%3ePQe+sXkU*Pm}a*HERW1Fi+^j*6ROYQTy^>HPnIRH_Yjtas_3jkP&X@+gW+XXC*6p^sZ^C7j4?#BRue zV>I>IYIlro!t2GWWCM4UZW$=AGS+)3BpA4eY=pgd`OV4O9~dsl@|^Y|?3 zt`RmUdhq5Ps=w%z*{j^|znc~CRA;=$O?j;TKy4Hs1bRVZw5PU}l867mJ%jph5^o@5 z5zu-K-wP(ek`T{bCeAH3IGF@g-DFENUd3BBas%?1^1CxSz0DQ}8^=XXn-5Sk+vQPP zD2x~&%~m;eG@-F4M05R`cjk;h_)nu1j*_0I*+rr)S4R3SUIhtq-GM4!{dqXhi#;aL81-6Cni}BdGWnU0Z%#E8HQHX%QLeTRB;UCz?~UM5tWz?Nu3j+>6@May(tnMq}QO9ZL9aaK%OgQjT+ zaC8YwxL5~And*D`(>&7kh$S6+UMRS5;9a9!Ta;c5#5FkIM-QBUC*qEA(oIiaNZ`_O z7(njlN#85LoK^Ah}`yZZ;aeMVT#ip2XvzzRby&fZAo*=goRz z28}O=w_S-Y0c^4;us4;pDHpKN2yFV9C9rP$kakULVbt#(s`T0dM6B;gbL)Y=(z4#l z-OG3}Yc~8-qwd=q0k9Jo%h`7fPT>C#w#y(bvWW^{k7tN`9wiWMt{S3p;wn0I5{=?v_y$(eU>k_4O{kjg4MW%5SiQc3v7FJ z1fm3JW~m?F0Z{IRB&%wX?VgIV7POZ%yr$3)MJl2>A}@4A*4Z^04U~7342c($Rkv0H zhK`7iqvCi%HoC#e!1vg(DNdhoYpV4ztTj9U96q+bE8bEY)k1vRNfXUv0y#=cm|9tU>I$f zC7!G{f=TdMW4njkIXVzDD_EWbN-33PRNUadm}9D`^+u?CccHPQj0GTqzjaPye%J-0 zW!WVAO9TsTp2VXBjz?F1bEAx(8RnG%4}EsB#^^6LAssKWPre;7bP9D@1=|wFc}Pu)`Jxb;a7z2`zM8bL)cdrsWsHl&jO6*T~ z<=|LXN(XR_%{Omp*?3r(zzMg_q6G)s)0q985k_U#ypA4l zqpfNlI!}_Yia#wNt_w~ey9yQWx$M<^=iRP@?GQeMrOK}53`xOSJUjpb5%&7*Fk%-% zTz)_JNG4FP)h%HBdIAr!Cq~inBO3h()dvpGt7b!PHjp_QO}0?&H__WqSB>U#GZaan z+za~KJxOQuyz*^2Tp?!NbFoo!LqP$51N%}XOuMzRe|G!^{B*C8o3WSKDRI2Os7gdt z!5K7A4%k$aak`a3oFoA%S0meL3XXs}wt-V)uPL~g6`-9$VNAQf<3WBq3l6f~6PHx8 zYF=&Lq@cdpteabq1>V&%-tKvA)$QV4TBU6(H=NhCT;1NF^)=i4b0WUA5ll~(Tr@lF zQ8h|Be`k`)-)}q{V!y3ay!NvoP;mF$0C<6pI9plT{;ckdl^m?CN}i zZpucgp;-Kahj>E;4>)cpd0lmoXGufOZ<{de_k0?}B5&5JNmR{WW`&4^GMyg{<*-`_ z|HnpQ=TL{bYvRoX3DKGLeoGv z(7<&;9{Mim2C>+~dezr}=rz{>3QuZFm&;F?Ma!>C#$CRTjOC0=)Qs5u)#X0ZRb{lok@1gp^%~6#dp0z%iUmyE4>P9*WXaKcrM1AR;s~N$jVju7hv=y*G>i_-@1xT3 zOx;MK>Eda*m>Qm=-io#hbN&27lpm%?wVusc59Lz8P2$3V_%(nBFg6%jLvbt}AlBXv zhzwrS^BRo!Ub#uB+WDKC+m67xnn36r6&x4YMBB5;7=P@7P6E$ok>{YP@yEvkLSocR@T_BxeGeWIJ=C)eJ@S(1%lB* z3H)`I&G%tYmNaEV?WlmYZV^bK+Jb(z@6{l5skTmW@j{WRlw$2g?}omx*d+|Dr;)3J zFnnu#7O+^N?rI2{zsS;YvsY1nl&R9FBqZ}PxzX`~PzD6SM(veDNR3}j;^2i_a$S5Z zrJArwp2{Pg#~l7qE2yyL7vMR5QOg+O9UI0m_ycWwJ*;i|O~w$H{|jTsM_?JYJIm)x zp`EO39J@2Rcb6v|p$U}pG|Oh2xNSPp_~ywZJK3DFH_puzzWKYq=BjxG?-b+IM##~t zHeMFznLhI9qCHL6L#G_rcpCqPk7Z$=v7*KGZr7S0jnvVQwst}2kfzS2FCBV4IXhIu zT!jgJ6Vrqp%osZ@qi$&+AhR-Uv77;N_~-!cBae|wb|!=`C*p=d3dpOpR0|vPQF#Pj zcV-@MRn5%IB)?%QKfy143+`iZ%VE@Ycx0!a^Tf1;7sgIZ_+o5%mo9|aL6~znf=sF_Q-Af#?$8XM*M^PEVyzk@ z=$Yu?@bYP;pW_7_f(_LKmv0g^7U1wNr|e2Anh@5^b>%#nz5(iV2B%Pn0f9wtv+RIi z?mrXg=+ESE&`p&ldEh97kr8tvQT?#zg)%Y04>ZqUTQ6P<($N!4kUnOqkz8Lu4}GUp0fj$z$gH%;3>`kOW75F3JbV*(5sIM9+ma}=PT2) zW|!aY0SNn>i@L2f+Uxspkk4{=UKeI{6pMJ4pDJA?M<4O#_{T_EXYTvAd9;-JO1Y`ww0hl=O0PDgh_!s`IWL}v%Il-RF;60gNI zARz|eCkr)5b5Cfox(b4=29((!0P`EI@eO{vHrh_r=K~asc_@{ht86`jE>CBSL z++reD%4HGjY$8%u(O_kte!1wu>c9jub45d1?e4vM`~G0(&B4&Kq~@B8-C(DYjydF{ zw5qXZKWfmE&)2PwEbrsYF8ileoAzzvUFLOqf{>*|g8js$nupQnC>}=r=(!!^{W@#qsAalHgWC4b|D@epGdjz4u+m3 zjo=U^ju?0Jt8R^Ke#K`m4fXKX`}dK95;uF=pu*PfhtKb$Kchts%?Gi4A(_EO>#IUe z8NV|w(wtqvVl(jUV)#wv=!FM8Sy-=40U%>6dM#bMvQjR9=Aa<`Hqj-X1hwu2-~W(N zH*G~9$fd|D*3-zJ#}k-m$9~bL;EZ#sF3pfnp?uVxjCDuBt<3Gm!Imv^d(3c~C`eQk zb=#dU^K<#$z6rFQnA{FuYK2?{Bz4V3KY8-Rv21Ju;erH>@FQ#yi{1*Q(+jN2-`gP< zh*Bz@@MDH^j&-*IXT7q~Ot4AiNL+TULfMNH^$cLD*)-Cigr?E|*Qd&~j6w3XLml7z zS&jK8{cAZ;h^oz$8ne{%#9HDPmzj9<%9Ar@SqLwo+3$Qm;J40J=lcgO z7Q)JHsZ5TmHqmlMVP~ARg$c@T)6{@eik~mPSty1DC5>b8S zS7!!D41x+mX}B)fwti3u6;CF~gx5NM8g2vAM@n5=|0H4$EnW=rM0KC7-sdPoL$UR} z&V369LQf?2!;K9iYo2&ROAxwdY}f`UvNT1;aBo^E0-ve6HAacjPU5K<#*)x&56_Eb z^gYp9^rJ8-1yV|*3k;F3hO|S`eeSxTogLDO!{-D(uYFgBsfO#A5?KouFymA`BAg_} zB}k_VOqapA4nSX|)~`Yt7ERV2A(82)F9c${Fg-7zGop}OW^NYZR5eKrOO3F@e=lRB zS&2Qo>JRFUz>1SApnbLVpzau~IJpAelZiA=|8DTV_qp8-{OgyJM(N)T{`Wq&w}F4% z)Nqk4C+zvbEW1ucJpsX=yl9XA_o6VKfY{@aaR(&6G4f*IyZdw+e92;NwrxO*k5$J% zm_*y4gSI)?t@d3jJ}?y@+){B1`-*?CAZCu_~BK2LY>7YM@DhzLQLLHI3|d+o9b22bp~ca$1a#$S=NT{=DZh$NhZI;ig^YtIo4}qq6%JXJRg2@ zI*RH^L7IGyakg1zME$;t;owR?FzTPvG0Q@L@5L`KS`atZS)Qu)YQ|wS#D&}yfxHH1 zn4L`$Rq8Z;BstiF=U&!d8UJc4Wi{tKup%qs3C8$3nYHZz-p*v$g&D7R)A4BWT<|8y z*S#t0XC9OrV7Kr7LV0Wtjw9qX`h-`el4sk5T)-`;V>m#><9BfwPV2}Yi$dp(<;yM! z%=+_gv{`D&r`U6|03@jHbH_Hm800CJ7_4c?H}Dy&XXWMMB8FVB?bm?>nBg~$P7uaY z2t`CzJdX>D22bM1NK(0_f(=iTdV(v+v^*twe5U^NXviZSg3%=T<``*f2#wk@tIY$Tio zHh8j%=vEvr;Bwe4?QhHeKNk)COrqHX=}BSQY%CHNs*&+74XA+2Si#d?m$K zTD-({VVYe?^|oxzQ2yu>^lf`U*F!5h7TaoDrl-NA@Edl;t0z&y(bo_LMmTSpGQ&JS?*5HOCLvwz#tM zO^Gf~mbfE6M)RxCL-)`Vt;qYC1=eY92Gf>%T9|UI#qe1k=>$_l2X^*_C<%`$;t=Vsd6&&F>vbcFq)Gw^|FeRYw#m5K&sa^r0s0wv~S;Jol6NP{^ za?KGuK(l%PTjD@as-*dIkZt3k@X&|?5GZ3OvbdfTzQa?EgKHNC&vy=ysk8A0AW9^O zdG9c3qG)FWw;6-Y&jjr`K$3ehacPVmuWHZ`aLw80Ga(v0m_-xFdu|!`=q%3>s?)bw z1^f$}HnQ2UP$#Swydg8>8!j$fXH6IDQ`?^kiRS#~a)ndyaA1-u ze&@}SwYt9S>D16j0^4nY!M4u2qY&?gdC3FgTT`_A=WP5fF^$z7a5vxx(5@@%dS6jS zNH~R=N;;V{AjjN)kpN*(3sRtqFNb%9328I=)8pD?bC*XS9#e}^j?0v94?)lO(pl(J za?X(KT`mD+d*$|GTpm)~Z?WhRNm%&F{-WTOc94OG3XPgFdwpK4z12`UG-5f=I5Lj` z$l4PHs_P@ao5mk~KK%ruGhu|$fYE7D^<{4g$sAi|R$4j?111+~K^NzDyps@2@QxCZ zfz+Yqey@A^RFXUTIkawDY>O*xaYZfCrlLV?G`F|X_EyxM?(tJe@0jnf#VQ8?m;D&m z{22H6cohmK4BJGuibfJ{9l%B_XpnK6=Wi)P*Zn7cOZOG>1E~p@`<_9{aJW3Z>^luZ z$9-dAYW*%_#?A6SIc7VI>oqPG<-(@+9rs7BSgPuh_!^YamI~`_Ct%z5yuX^8NKQS# zA2J;|$rjFiaSHwuD*7ZRcuC!0s&F}~x5G8YORRLPJnso7UYnP53d zPVtsIH!84R14TwOORV7o{0wo^Pe}MR)C2?V1gsNQ{<`Atn3MI17Km+wEWOz-5XXln z-R9YuL&Y!Yg#F`B2Fh!lZ~5ckvO(G0)G?XFiB^*vT`MaI@2|$lT_So zu#*t;$jqkmCEoww6?I*h>*Zwa&Dd)l^0FTR9o3d_i)xELjXHgTvA(ZgTXH(xD}9?6 z{cBaswPW+$bj=IjC-v#6wl5eLV)k&wTLIb|vup{`v`+eXN)9|H2kR+=Y2a55o$?KB zX4=7)g?cL%L!oes?^lJJHM+C&+w^7;2Aj5ML%RRIK87`~pk1w7RS|gus>Q>8*EtFH z2JZZS8Hcwl2mkg@sY0_E?WY_nVXA!@6 z#i|KQTpOQvJMuM`9{>NoghOA#)_J?19XeuWm%{ArU^#tmTs{Z747IMHO%Jc$9q;eF zddW;5SHG*tQbyvDmy1ZMc0Nf@B&0{lcOZ6`^18DmC&c}3fzTzzDpa+QyyLlMVq~cD zDOn}s=SG<*tipN6R@2B-frl=r`NO$pSi$5R$CPD?-`%{<)F`so+1YyzjusRzvg1j1 zcDz82H<)zx>D?l|p!*O?!j%zGHqDyz?Jlxz$R4j_Se`T7$PZwMVU)nVN%pY$T zH`J+=2&(p9)K}=gkYaQ~mW!mWWiLmH(w>djUd}HiiBf>5`S;nSC4Bs1ewG$aXKM?4 zSx(8PZe>~3N84jHAX`rKe>CIAcye0KM4C1}$`U%}b?r-z>4Gj?0k}P3#QXKaOtWCf zFBg;#zr>)v$iOf2Dq}`WO)=#7C4Yo)B%PgR^~G(rUuU0vt`hFvix;1g#oKv2O8KnU zG*DmGt`N%#YZiyo4bDWYk-GKf471#04J<$4$UVuvS5vL8ia5h{IgBu9U9D zPA7%Im_ad(tG07dEnQPxtDl>T=EU6>(8}06D`fUVL^&Gz`zdl{cwUgn5ZUHl^tKfthTWha{6Z zPHVD}Xs(B`0llDzCL45v7*V5gA(O0&SB%+tkzaz-%;7{7lYn!6_^g72#K`r#aV zv(M6EU@vLEmY=262+Zf^S)RP7dt9KlMZbW7zT$>T zVQyICpTFE($K)$0f#)-LallrCgucqP%K7!R2Deok7*Y({2(#Uxnh_vwZOC3VRO`=uqL}Q^=-NwueI513DtwT7A4DW?&v&Q{8Xfq zyu7!_IEI;j?#S)e$!W2}Io!0Z8)TWnq64s|xSwe%yvHMTO?fZR(*D_eO<|j3iteT3 z@g!MO+RroAp>F!$|FrO5!<1Xi$onD zyysT(IaSB8NY0Yli<>jd7)FS~l`P&Pjf#1dOqMo>P(DR5seLJ$Jj-s-Z|LO()oe~! z?uFQ9m&%SQ)dTQ@wT*JNd0l1G+P76UHG}hlKyc`ZsRMPB?$|6G5h5sNQw_Jo3k(qy z4081T^CNZmW&I3Wv>f!IT%(+CZuPo=5kcMhBFgdN#eB~Kx*YEKLzVzCj~6zV*+0Vl z_>>&LIO_+Z-RF6kQv8%0v+36H8E4yS$K~@uZ^`sG*X%6gkR&Chm`Sz8e4E;^sN@`r z4YGR|c_xar+GjDFFd7NFD~YV zGaWHJO_tJ7JGP=#dcTOii#mpB`up!x>U+aOt!>3vCVIU^PhGpw(3bWjzPmZK%i^@u zxWr-qMNgxh5gZ0^Sa4Oq#M_1N z;nB5-(w%<}6|-K7e{vp`7k|!(P2u3^dPtMtAQP2&urO6n1HUSLjl_#~RX<>LOAcorT&HJ#e;xHz%SD!6jtOfE1xF;CTPjc`7$r`zruw1%MjEZc zbGE7L))>u-2BZcPng`n@H!d0)wiRoWjyPggb>sY$naZt4v!ce7-Y%}_ua(wzGz(n2 zi4rP|@70=gR3;^L;WyDgct4;PQprPZ%SvB{TscTL?B`lMsqEN4ssCn$4 zC0-PLF&Cg;1G|2x4@pAc#|%Si^FZM8uq~D4&%j47oZiy5pFkC2JIEVI9v-8ORb%av zn~rX>yN*TLVTuN3*R013n63(8yr}?Gu#(Nj(}qr|RZ_WH{eeaX@%u?gG7acGNpvL6_FdyL=GJ%d|mgD{-U`td>2RsH=1>V*(0-ajJ3y-F7 zlffST*~US%Z0(4p*#p~z50%&62SGLw z$74Bh2BH@|J~0=4Bh}HmA-1{b(f|71xu9y_bBeEYH;u2F7j%fM4I)=-g^?p&$(~=b zuO+?ZhwHM-Ow>BD=qK4#qM>C7x}ItB^GQm(`JPsXJ?y24s-=roI+W5yS+LCpChhYC z>DO(xdYZ)Zr>tEofgFunzqv*~oTpa_eDv$Sojl8~{3ownptv2K;G|gJ)N|${;a{RG zdcpLYF{{18iNuNMxWcR;9+wr&PHeIGreZO4VWlzEiCoi~7K?*ofpsKXASQe9d`Rby zl>s1V1-OW#f@wS#wPQHX5e4TwPpoN=iG2fTTFy~zfCu6th6}(P7+vfDIL2wzM2MW3 z#+mXc3ZplFd7e!)!#Jto51)jgE-)tc{>awQv>!taYNcoZ+H~Lw-eWsmn2-+LHoMWe ztHP{7u)X4T58rKVCDdu28Ih&+=eO=!76somcLbQ_axd7+7=1R4UmDx__Dy&5d)fe4 zwMrz$GdS~&i)>LMMlw~=V`tVv_=yS5gvw*76*!G;iNT#0&=eCUoY6z zjJDa48mE(tMkQ&j(m3^~)RZezR99w*QjC8t_-r&W6dn>`MRm^QEt{Mz%n+L6izE*Q zOZDaPBgaKQataEQJeszG&zN-Aptxg;n>?wV=$Qk@fc>6+D86 zM0ZIvwY9Ij?PtYlN&eoOqlLS`ljUjC9S``osAvZoCSJ2)4xYqjRflLqkLHb%3qL&( zcJiTapvk@Q%=)9kW!$30?1#}~{a(xqQXbIIT3Td*e`UHoSoL>C&mA*E7Yk?yoQjKd zkS6n}=w*g6(o_%$W_)Gis{C!J|3U0bRaCaZa-_UQDdWtEEk)U-Xg0 zHd=J$vi32PM^NZ<3VZAXF9>G0ArYa{ysI-|GiCiQ^|GHN(W|t0J6B5+by){oy3FIV zBnSfpd`L~SFc!~#S(9bWOqXy8J}-P+hxn3SEeFQ};9^}Lx5w<(4kYUKi3w^+$wvw64xs-~? z;BGpR=M?5LpxrJV@n#kL-j?XQI4qNTJ__B>PwwZ1)58}d)v0mzq8qCo6stGjrK1)d zq+bfv*zB9Dbd=?C-@MzU8B%>9Kf0ND8`wzP%)C{$?N<)e3hH~K) z>rEAL(-{3~NB?UzT+AoBijmSRFtn26VuW^rX9@x5vWEI6@O60`sIATu-TDn5 z20zifIk|M(7?Ze9xb37sO&fBF`)!m*hj@OIBw9bdUW@11;Te9qWe`$UhG1(1RW4wD zrl5cL=?E5{hrb@x-IxYuOPoF*GnpbGzi%}gyGsQ( zxjecEd$3I`{Y_bY+jve(`1ME6!D)&|WHZ%>(6cP+!tGIuAM;|aEdJe%CwcskP>K@O z6t|xpLATo#bJ;Wp&w5H%FJTztWQX1g9E4|?dx3uW$RX5Ys46c(mi4KEM&`=}lyD5c z)6|dvk1k~G*;xIr(jrM1qr6Dw%5_Puh!|n-DTTqqC=H+OLv~5<=zLact z8>`=ziS5(8{D&}YF6GS9vPGwJua{jTpU09Y}%F=dVScCU3B3hTdV0q5_% z-?C2mbrhKDEJYNHlGAzQ5fw~-1S;!IXH0f8m};wH?IMNjd$7$j*E8@E``xrW1~aH;MD}wl#T21dib*6#Yh@zz`$kcFA!#Xw7my z2b+%`ecStX@NiRx(2;j{M?+_KOv4>+*g+9`JK$G4`Pgf}_&YS)_7;#X2IKows$C*y^+ZbtrOan;t(_Bs@SaEt+fRy-LYL?9jR zgy5rTeXfkgkYKe8`z;=cZH(o>;V{4HKI=vZs39J`j88~-z8{-I!+sM@^{#VFl6I3W zE`_m)%u+0v!3qf{;P)h1Wi?eh{d!N~ZiGX1#cR(`9T##0D`Pu%b!zL1<;EU)tWDaY zpOTfuIlP26oPs&>b|iG*$8Bu_*OY&A5kRo_WepT?Du&|?(})xKtue!Ky|gOMl&A5P z@0PK=f^D}st&m(Uafi`REg0BX4sRBDKazbdkd!Wkw$-Gc)tD%v=se>l&icr5H1?!+7&KU-r+J|KPTj3y7jhT6h zF~Cl5D-1xMaNEwS&4auvqs5ldeYg#zsIfAqJoNl-M#>sZH?(qS3exN4 zsI@MR;;d5Cee{n4(5o}buWzOXV#}3Xi0P7gNAwbM){y~Q;ZR$wflY3U50p^>4_$=O zSlCZUPvyD_!ZrpkZW%V?u7r26IE-3Of0 z%Eq7KG6wbz%o-q1Wc8|WE|Y9W3X?jdw(Zb+VwvHlGhG_{x{}XBEpX(qBlY6pXNOyV z>)<02{~i*Bk2XbjhnW#+!JK@fHs`9)lbudC48Me_TwV#qNK)#SceV zQytZBLCm0sEQhiTSkJRhH4c9E&wD% zwlb?i&CN6l<{Ekl13V~`JprOnze8q9>W0m!Z6fnH*c01;-=uR&{>NX!Yw<;%Iql;!Um8s(Z?M3sqUWr*+tg6F^w%&+ST+<}^L2 z9>(<$wRf$ch%NOtiLwbYRX@M_4g>)MxrSkFy%MX`K9KkXR^>E8eG z!*wgbuesXp4D@S)S^Z0ZdM98lZSY;-^$sv=0d%`AOla7NJ_FVT47G8#Y{|O7oa=z8 zj472;KR9h^9#{=M>jOo&_v@C&d%8j{j*$THwJ-1K5))rEEMRj;r#%Ni*|uN!?| zcw~FW46kXZ@u77wtWJVKq?U+rE6ih;zB5Z@cqFU2B(jBVFXjaZ0!Egj$t{F4eI2Qn zW?eeYY`%GWz_jmt_0?A`Tb6weOVkB>^TOtptJd>H+Tjw_hNccmbWK!IbGDTYYD$9@ zDl5})v7Rf&3p$n?sBNiNHW|=03Q2B_1Nl^&9R@6YDC%49rRYb6E(z46Sx?l> z`c^4Iy9$wrYhR*|H*yn}-HO$Kqtx*eKtjS2SrKyE2!cl@k|^Z0mowq3L)+s!P9>!E zJvO{=bf}q2XvDbEXNO%b!Y*P#aH|nV&y&f7z;2RVq5=(Iz;;@2v7e}DoXFuZ+aj>F zo%8#qhBQr)umjL%nU2wsc++(DQfBPySDpOYZ*ymX{n5#>!L>Nb zCfOnhU|{r?6C9F!r@&d@(efWfbt%q5WVwmhhG zGYAjg4acqjwyR-rg^k;8?Eas@kA6^Dy@6{IoGCX1mr{HTMxWi$1cp`P@}&|?bcYQWtKd9K&V*5=K&FFRZJM!=DHY0y%K}Oa$z`qDEYTjWRIoBo_?^Tn z6aG1Rw@5Bh)_H8R+8u(!EOQfRV~VhzBSP3@XX%J?fT-v)z#{~q=0v90Ig)5w$aw9=5Dt1j2Z|8wxUvzlA zqbqyUwxHgQyj4`tD{)`SJ8E}?_SEeGy>d$1443=7W3UYAx?gg0_N#Ii8g&FBh{Al{R{ zzM#V^;K0Vzz}KTmI%g+Km|y;X0DeoMQIU@Sx5i>s*nhU#9p4Q9>G+QTe&as?>(-t9 zzX80vVBjA2Aj+Wl8x_jxu$a07@Vl_@0Q4>B->gp@Xr_cOG_Mf=AVRw6cFAn6{p zL03rNLiH{ZeS~h~M+xZ64n?fD5f{u73uIwkZLo~tkc{WmBAarkiiYrCm~uKC9I?|p zTABunO4-D2140m^x%D}c@4tZAwJ_p{X8SA6E^?602E?*V(q<*L z2o>JAcGl~&mGfb9g0OT&nRRAjiMYTcOJtnmI2dT zUw0gZ0UF~4?qEM0$8>Hj!<6v>ej2A5N9k+cCO5qWwd>j!AzZ~xY&5@+(H@I`cCFN% z#>MEox!LS)v_2dXeLzoy`>hX0nuzysxO0ijkqmnSh&Ru>SQaeXw z&b<*DFiq>N11oXYa2&L$WWvu;6bbADhFP(y}#vho2*8IXr zd}1w*Tb}WulPV5YlCTpw#9Orpj&7+A6ML*H&#_(zpN+GEPnmFc(}ZR6Qo&xvF9kp4 zhR_w`-{fje&nP+2IE{JpvyZ9-Pgz8BEr<&LJ^8){Nqr8`()G2$O1p)44KlG)1((h; z;~@a@?Ayn6nq_ndx+n2s{fMZH#g(6_d$(IvEnPl^#%gkR( zkh|g~30FKkBN5u9J7MyLL+d*vyJOl+0nmH_}@0<)hYBQ@XG2vHAer zTO1Ud3DS6$if4I<^)r4i5C7}K zA-FZI58Y<>e(UL`MlAuGY+VH|?%grX58c*NQ~O@+j{Day9O8MjOPZ(?TU&m9%?j3m;TJ98-PW+KP|7hct$z(EJUD9o z8&^DB+QW5q5PXY!-#QX7?>5%0fGRm1s12;d`GrBK30jOle98G&~7XKKctgJz`aZ9k}IX}JDw{6}b{*pSLm{A?Tdfm{6P$wX%d%w(J$!uq&Q8{G-EfaMKYPBXUq~>#G_9} zfq6~CFwHy1a*O5w$SUao_TSaDp_2v%2za9-5D60wAHE)n)**AgOluIhe*+P6vWXl}xtGv=-gK)r{#52Fe#MO#4 zyB$5~upcz*J4fz!yS;ENu~;T0=2|dbZ-(cwxB@zXgHYRu<*9i~9k>Z)`w(v??dphq zzj-y8)frl2?h?80t?pO8ei~i<9_T8EMuVWca|u?*CG)@8>q4`!Z|XP|Kt}8$aS+#q z;C_77XqD~Cjba8b9aCn;rFm!s&%V;t&6{u?D-PZ4wBMubX7j`NevAJ3ymj5$^dim* z2<;~lCdy9M>RD_D4X!KKZ+RT{E5o9y<#}Gg^YNASnZZfW-?}mj{C(7qp85m3PsRI3 zEXQ(M_rD~u0%m$P%go}|p8;K49X|x*L1(nhSACc8z}t5Zo&wiM>@CDwVNoo&>Q^2T zGTXj&gY4BIjPln$#202@_bFk}xW#J+HAw3*a5^IgFZbTP-Z^-Aynpb1_stJZd*bWQ zcv*Yxr9dh<+P<@>u-`{nVw_ix|5e1GtZtNmXeE;s(V z>B--qT$2_{6XM%ChjaWrjy7&h(8{d4h(&jB*^I~t3cC$Rpu z@oD3_6?Hn_M_P=N?$#7&PbBzF9)6Z0+VoL<<8?weH501JW4`{}?RKvtGZf?Lw4`c$ zZSQQeSY8vkSy*sr0v8NcY+XLNhEIuhW@G~sVQ(t zahckdfp)F>u-9|Bk!H&u!?mX;b)GS;ARGR3yqNKVKhX(I<16OZ-V?7P%ULmu^h<$1 zsNX=d7;KPQ1*5VenihP;cbOEIh-uB*;kB*5t=yYQLC=YQVIqEYI}bncUztYT*y?9d z2GLh<-|y`l9Pht;xAT7I;O%=xrhxqlP zzcsmy^E?}+j5sFo2|X*iYw>dp9Q;d2`u6tzIHta#hxsBMaTlxMU%P`-ZhJ-WWE#@^ zzxKP{BjAgW{_tN${BDN`)i@a=sX94w$`?1QgpeMj2vY0U)Nxc*gr${fr;%A#^6z(o{>E^yHlX zU%Kmjp@!^!S?14IjJ}-G?TSCDE%ir5%XpJ*nG%O%gO4DyMgpaF}OI zRuME?XV{A*{&b_j8Uy;mtKRSWk6hi&R;}@!>VDMF2X)f=dv&!#Yui?N`q%gFRcbsb z+vjHUsxAh$1(sQSixL3v2i>oAE93lX{{x|J5yI<31CJC)w&;QKp@D;-4vBL{+(^a^ zJsGnQ@gxy{8ecVP7RoFkS(7yxXu3vP?v()iGWg8lw{-CWk7)*;MA|LT_j} z2RAoQ#*NlZ+vbLM|9(k6pohFP%Wwm$N7x+W(`ivTS5lhpO5q9&Yvnam5dsXwr+7r` z&#rJ01P#+edc38PzSr*%wWXa}By}65(`H4}fFH4bb4wHY{R<}qfw@elyDUaBC8X)R zkhooIUCTkIOEKVUhwLv#7&E;&%?$?rvbAXo|EZ06-Dc-hIl@Z0Rgo)To2dfWDA^Hn z>BAuV=mx*{9+Kk6hKb`6Pv+-w7*{Srd~jG`rW4C|A&Ox>p~JuXA{S$Ze3ZdZJ>i{L zC$9H>H{3Lr9p_D2Q=Yk$x=<>$3U z|Ne=t5g~R-`A$+E*Hn9^)t*(VJ@cyF3%m}Xx0YFwevkH>f8W>MI?~Q|rP8yOSv6DX znO1t{D*3g((wV`;%(PH}aPC{Yb0S|MlT$mKR>6ugOjO~%og*S|_|I;1Gs8pY)^}{o zD+!Uuci%iumg7v~Yk1*r*g-KR?kDVZVbC-GS?>x&J#bYGpi}n-t*>v`dh=VViz;(o zh+87l*mk+;=A*~GM~`8RVba)X``6Kf!Q%&yAAK`;{K!(u;|$hGlZ0O>IebgQu_9px zFz@LdnMI>XmOF2?rXKr#jxXbtJYtDz!6UU;tkiIdD$5GCK|7qr7a0f75ep4@pCD`p zFlt(?3{lTVIzGvpcH|v1;T9cAJN}k$j^?jgGj!-{o7oKJZX6v)uW){Rq{E@Ze}oj<{dPt zyt9_T96817_>TQbw|D8W(joV!_&4Tow(@BAV}miM_M)aZQ(~;cE@cVDGphS&mRN!p626y(e1X`z?CFw2CDvukqNX~4auQlKbMwHDsmFK8VkCjB?RdD^c4Tc} zVDLHfCvmFIvyv1mF(Hr zKf&WPr{-aspjN;HM$fb9d})GpqkLCO1^35SrYdcQZG|PY*(_lIg1KFj&Pg!x{5_UvGC`NIqH2IY2}DPKM+F!S0E ztJ(DTTBDM|D>8Ad0gQMY-+>|t+eJJl&TQftFU1H4{|KEb9)zw77zr%2<*oC-e$f$@1l4b>BSMUlx%sdth)++M?aGQvY)EEwlNk4( zWLMz|V&SmiO|$nMo%MzcrL|ox|BEaCOR4-XTE4nFSSs5B&)n2^2SI4dUVVN>6p@#HVV{)5NuzjQa_+2}l5920dq$tm&S14%ra zB>ef1KXEhT>k+>E;P6QN#w0F|#XDh6gf23;{ZR(AOrMW>_ynH-4L|DhS2o4(0e*`% z96osn8r@z3&>{7qm#Q)Ek<3FnOR{M~B=Ugk^MH)&U?hCY3HdQ$nRIq6#0{l?!y}?c zeKJ0_gelT_Y)na~zoCTq5i&pgAV=kxE;NlWCiT? zcW0w6l|kePE`yjjd9t`j#*}jDe)#GBzA0 z6uO=p8&M*QS}l3Wcn)1KGH+DoR?}!}fBT!EG3e}!FrP|FS1r@?8b4#|5{OC`8PvjT zTu-q*=2c|+<51r|QsJmk=bEf(TmQq&`ca^@5t{cq)6UydSGU{;*K1h>&nK?`r#Nw? z1IO&fP~i9^(%BQiTgJ5@eebHJwTVEb(>VXc{8+p@*b&<$5grZ5C~=LAyU{+b9X2+@ zG*~n;6JaKwKP}g0JF=jtFqZ4e^0UkpW3hPJ@C+mlAH9b=pev_YauKPqdi_SS{&? zhoSb9mkt>b5E4A31Y>AzIb(l}Gbrg#GxbVhwn?W-IBCQ&?>GjX@%y7h9Sic~3EH|8 z|Bv#DEkT1Z@PrZKrWNdU#Vr8GdSZoum0J1pKBX|YSVN{3wnA@g-I#DEu=?6BL@G+@(w+FOphjM&+gn0BTq zjyf7hyn_jP4{#a*1Q}Y-7l&8GR<5XZaiDk7Tx_)Xk1DyDd)QvMQLMh|hoMX1*ID*y zIS*Y!QcpL9-&!ng(suA#J*1+D?Vwx5Uh2~|+PETi*=OX$OeI~%8r20usbSA4&=>u!bE{rjgSrXW=DbDuG8Y@A1vCu? z3kVRtgJJ{>8&91TUS(iOE(@Jp*JG4~BssdM3B@hd?>a}(a9RyU0aVJ3L; z-WoG+L0KAtSUk3`%&UYDA71x(hFsFCZg>MWy9sh?Zo+UA`Fc>$2j09*dG1c<-O#~+ z#=B6$wR5V>=x8l9bNz1{!A&%Ir*oH{(UFGeW|uGh8s$DYl+6n8-_?;h!OuH!IV#K_ zedcZmyJxaOtY>f(M)6D|n_ZqK`l2;{n;LmtvurBMZ+d$A6Frb;ZNm=XlO0bunJ5IC zE>J9N3a~x8w^1p^b3}bq3?e102{1uTC#Uo4W#&^T5ScLxGPA1)QJ6O+$KQY7(tIB0T zEDjEMS-H}Xr4V0rE9V}k)rL0`~R-KBG!+Hf##+m-SsxP zZ5qMrsct0tcz!C6jp6Nd=oh&H`L_-04^p?c@6Q){`0yrkWZiY%=E$QC3rIs1Hr95% z8%LJE*UEfZYBTs#%1MEi-X~{i4rz8@FvZ@h zh#Cwa4h?0N5leN6#F2X?U%u{oRBnr670>(mqnwMH5S7VEY@zcH}w zpf;YZs1WkQwyCX{T^3d)bO+5Dsyum-wJz$PL?}dw!@NGcU$%6SmTkPf`kTN_+n_C- zv-zwwO2aTb0DPj&7&5lQWNcsz;+KA6%2x~*?Gbdv>vR(8h$ zu(=T>`6Qhc9YO|dk}}IWAmB5o@@>l9G(Mda&$HQSdRDrfU_Nym&r|s^;vx+KQCnUw zH&qmaVP+Y{e)e>rJ9qvMrN_z1@=RRNWf`mae^~=GIUN@PZ><{#94R{$6yn~#4_#=p z(H|sQXM7+I8?WE~Fl^is3Dnw{-}Lq{t%_p5t%C4RwdYGbOX%ix#XJM@GEJcIw{4p( zKSZWk2AHaT$vX6IT!)``-oN=jbRiNr+WPmc#LM^Z-@gCfVJUcCYvwNYnD4U_R=Pm7 zwuv~UP7l5Hv$3W?sL@GgQ%=+o>rCNu*w~U(I`rEhqdH}WC%&7tQz#Rhii?!M1sY+r z&t*B&M#s?d6TkcB&q37e1GRq}-I2nj*7u^DO!K$j-d`W@v? z(^5gx&c<^xYfk^q^fnEY8K4MD!u~n$t%t|8pjh1yur$ufaOh3Ayz0V=!=!UHyu&pK zQ(V=^@}MmgrnvOFE)7K6i@xrTqD#Nm=S%;=pH)q8yyf5J<1PPz`2!oGP7tznj)Aeq z1HADBm5OdRQ(I$z70k52)2swH>UzO^xaws$P^2U3-cS9GH#tcq1*5rAd=B9PoYf&*&8}m^%JxOP=xWC`w__W6V ztJjo$z}j*0+CWS`^PB+d8JtXD?WtwrqJaMnCq(Br6dLW;N1W4VtHhXd0idZm!tKbh z_O`~1D9`v+1*SBYGwz_Jp(GDIs^)X7>@DwLX!vt<_#YSsnr>IycJUEwEVM$BJ$vH8 zfFCvPOyh~=)--GLL%w;|X0PEbd$J{poa@AyRYJ6FEwjh)_axEam+5$$y$#&6`$Jdz z6}>fDU18BT%b8(Jy2e?<>t6yl@!BsevbIu8ip(LH%YL+094!5 z5cfe0T1{9Iw5W~MBzZfN0B|nuPq>&SFj|8x5&*;va2y-x_C1n-rMB2`2uWLw?O@Ub zKF!i<@-j-CReOw^I5#bCMV&RyDDfUwVhX|7_2HkAop(>?)+Mx7491nf=Zoy}wK|a) zWs}!|+N2+y99qX>0&Huv)|ye;B6?SIBO+gQ?YQm&(?c#BSfesSXAWchIBL>g7#Bfd zmdIm*kAVsL^;;JXt>)}|_Jl*Z-Nne`n+Jn$AAbAIrr!}^x=!2~j62+LC!KYEjW6PS zv`FU#<0P}Dc4Y5)F#fv!7KZ4!ny{g!XW06ir2H1B!>R7;uhvb$UVN2Kms6~0Bn5|c z!IIIKuC?t;+=RqeO-X4-zah@m#4NePh{3K%;rfuwl2@g_$~*hpBnUZ;Ry<~tjFtIK zsCoD+D*r}*tTZcRl@!)i8c{P51%6CN!QKMKW&LHMJ{^owy98)YA~k=qn}*unMqRa| zmG4~HxyvQ;e`OVk;WyW0K;_HjSmzp(Ao3pX!Bu5ga(ivVJQL)XByaQ?Z9YI!Duj})qy>%uKCh2 zX|B9MmZ#tvaG<(oLY1O7POt5icVv1+ET!|J|CNT=yW7G>_u=rFy5ERaYXCZ1I*@ik z6QX)(kgHaRT!fFk#}IJfy$$wbGyXIy!PbU&OkXdSs7Bgg^QI zY4+MZ*Zo4{epUPCO2HigaW1!{#`o`bqs^%QZErgqALos-ycxIJQHQ^BxG|!y-chL4 za2VtU6)Q#WKEYsp%>@7XOU)E%8LKPo!;_17?BW2KmRqOkZ8>DCoLHdY)fZOT$CqR$U$nU3UAwTs2i`oW7@Lx0;z zX}M}OE!O+Cw8{u)f&2Nb@_XIY&I18^HkK$5n(DCAMV;q@5J>NJkj z&f1~k;aGUQvYOq?vKt-Ti&e~_GhN_=-wT~Se8%W~@M));d~IW*T!vng-~KW0KqM%6 zO5%F{D^-A7yeyr-!_tmw7)@vnQzp(=%n-+X&+R*vwR>amPTYK+G5k_?%r5DnGL%Yd zpCP-CPs{t?S@LBv-mUCowEJz*8sdE~L9k}o+x{`l=#4jNdeA?Izq>6zrjw(fJz8~( zw{_M@?k8>FWbOD^7-vZ@@gQf;==&JZGGh6$0hWMF;-)BMkW zSThp_$78TXPx}{w7C;jyNm)F6HHSTuJ;i$7#LOLAoqi;tTU8o+#u=!TNt>N;(=us= z0gxp6%{Il&%^uTV(cYH=D@kXRxieGnan98gv~b54Hc232 z9}!$l+=k@};8bYQ8MdUgv3;l!G>tPGEPK0&sf8C0N&;y@k#A7S7&ZOK=EY-njK!o5 zdh`~s!W$P!#F3JrgIlm7qw4ij=|Z4SYc@rE<4(|8FUoA9Q-(MJ z*MBSrI^^X`1e4?Z1ot1DuisE*5_ySZ+zIO1LLAn$HEoGC@Hf@!uFI!+4!-Fk>gqR# zohx*e6*?BuK|*%!E#}@4)Gmv*`PMP1Tp%3t$%6=TA$3gZN`Rjm)hLdXrqB52b3@OC zX8zhhBXAGZLIAafsb>SNWz^#Gd&Wo7;34ChWZ)5cHwoRLU^d4~Z z*xPRpI~$!pZFWM`QXv`f=h81Eq;?X&9OD+lpLT0^lHfUZPH_x0$|pAp+GrEH*bG!9 z1iDPl4s57I*s41%aHh3Wt?HbVi(cO}WTzg(&dl}gu9wQq_?=E9hs@5ygI3j+ zy`lur3S4Er{Q>aVv4E;Ue|+E*?Mt3GZKVvIMYEULjamV{h>_CgDq3Q+w>`RxyjKU>%2#A1XE*-t?P!Op2!R zc60)2O(ZVwA{z0)CW%5Ea=%N>`O>HAHSDxyF&FoW9$5R#u9y$aK2p%=F_+Q-lAQEP zhe;2UIph6yn#|S3tPq5-0Pu5*_@HZ##>>0fmff6jHRl7RWsW~Xl^X!=(o^|0vaPzT(F=%+xz?`~U!9EO?3w z?R!i3y#NmhnvA0SB+p*%J$w9wK7i?eC!#us?yU~}`BM2+Q;ukL1i+SNb(59OvH z`)qE1y}hN)_Pe6w!`O_kr1h9OVL+ybub-JS8rO$9NR79El88eY`^e-+d@)G!BsGvE z%;kb1d0@vSfpx6Etfnt*7t@*YoUSGk0X6cmAaknHQq$JPA`QnxhIq>k!4}0%kKiuDc^5!K+#r);d+kOX~^Y&nK z_lAd9_ZUUO;1-w$wIhjvRXYRvdgvBSOU%CJ_Jf5P$zEhvzZM;dw&3lzd*OcDRJI=~ z^>g-KBL;Yos5YCC1lYroZ6gn~CYs4#`r-}wScc|abC+FX>(BoQyZAqQmr+67Q#D9b zDJxRu7Q;gnF;@Y92=P~r{EP2yABAlUP0n_^-DJGsRM&hJJg+lfVI zvZs`)X=Ov`CO)UW%+5X8+SE=P*fk@H3LbWCtD-}4b-bu`+x7BxZvFbWMz!f6U`xJ8X!pYEr00|NG&|=x?;9`{565FMnQ(hc zpfxm^IyS91f$MwU5!|R|?5=Bb^t~LRiJegdZAfAfY_He?N&GOV}#3^=WCUF zu3iu197%`4nvmcX;E`dATjhMN2ZEU4Mr5dn3t|5?n#W(GS$U^&2QQ7Ou9u+WXq);H zf3@7h7z&Vfj^7hXBi0+?8iIR^CDL(YSvjURY^~L))ph;c^(t4}TI<0cU5zTLtcPmV z7*5=#4(^ziTcue=m5>EDr+n~&SyZ_k_S~MDmTcd~pw74Ly{XJNK`Jru9$p@Ro?#ov zO=iC6P|Q~GM&V;OcZ*z2Im_YJ%8TrWh z-;^0hx}Bs}TA-H_7F|nX@I5X=yJAcUpI?85mNY!eG~r*u)Y(&bTavBaX52Q)bNP=~ zi5g@CKd?Xpmi%A-kTx2diGYY0rxfd4c0=SNgM?fv#4@gd5I|uLJc4mHE7!sALbbvJ z>)8l=_lLC+h`9!RhV6jjCGS6Z{BZxFu7AT~O&kZ`&t=bsdcIh&m`=l67E^$BL;nNJ zEI;{6je~SBh9>9U_SV+cA!p;~1R&w)gi{jg^D?8AHnz?_vH<5vYf|8LRuHmjjh^M> ztmG(p2;b@G^n;MpmS)ABit6Z*F1C5KVm+KPy+Vd))78@vo<*5H#ce(p-mc}F4?TIf|BY|FC)Z<5xq z7_WT)8qX!DFS^E_q0p+j0pS`!s#Yt+IPrLnJL=6d&d+>8)@!C(SeLh+PS_H@=&Tp! zSK539r3`NHsHqtoL(V~d(*#BA9o?mhJgxwfLs(foi0q3pdx_~=H4fOkJDk5QZ@=AqaDVgoYMeKwawW>!5n|@f+yKmD0mj|; za+cEYaOAW$7Z5rkN7QY#o{RSHnibwWE4L$~dX18>)3uEWPJN;^jO%5m>g?AftNvx{ z4=Y5`LF3Vq#(zjAnZ|7nR&kmyyu|a24M#QG z%w4RaYDNdT)&kHpPX?l$P40B5D+1jXSHun!r4Tn?J18#BsF%^`K)lS+@CiBu_mZ|H ztp39t2~5#3#!gA=8fcY5?kYYeC8Jc$5Jjv+gC?bmu_@i&YjuplSe*ISJBU4@jg{A* zJDo;luidvOy#}i9>|R^DYq~r#Lb1BW>S-6zHB7qf>UEQ*>ZI2&__30!ptoUe?r~SS zuu8Ju4=w<6?RhywyG*4yO*A;s7pchsi9>=K^T~KCHCl{!T0*Qb86*lovM7LG*A5 zH>Z~7`69&Lm@P=E7Vpk#IVPdeW18{=L^t{E;xwOjlZ(?7dHDB=f6o?rWd%s|OM9Yj zUwm41+LZ1SrRs2<7*Y>oiA@1RWi|vY%H}1KCT5o1D^k%NJLn9?#z4*C7?3eoMY=F| za+Z|EY6t#?-2etjdq!t$>?=<}=gIgu>jcVx*}~Mo>m%NBsINn{fltK5jb@V&GalJR z%9bvLkBpchHG3HZ)TGYKsTW-XhA5b|>6kkX8Z9$6?3yN+O6;~!BPDJO<6=3ajhg97 z520|uZlXt2H(T_B%w`r0ivlrBae=~pBGbwnw4^P*-BL9;5ioTPIjxB}4pHU=No6_M# zjoIJDf^Uu%9MSec@}t}#G9MKaD~Bq-EM*Rqtb$X}a~Wa-Gpm%A6G$Ct=>i>;>1a-; z(-FJ;%(86k$F+JRu^YT=C5{YDZ|xG>r*gbNW1&6bHx)_D&J-~oSiqB%ElqzD=W^VP zoD}n4yWa1rXn|Zi0gXHSNzUUhz#!8kIQ;(Q& z>OPG&MUTWlD4F6KR`j*?$oUu&9cbNx^PmEWHIZ&Niy_)wcTlm(6nwv$YId* z6`Q}akwt1pCPp#WfOrD2DReLTktixPGnEQz54F?@ZfhIiO$Pzd&yE(o(*h?Qo)9Le zuQUb2yuJy}utZ6lU^c;#ART{5N1$itX^hriUp{}bw-?C%dQt>Nr(H8Nv&|&sZoi0! z^jT<7!G)f3>>-YOTkQEo+N4?|njp!Qu+UsC*0eLG&5}DNo@pJ|z^d!tx?oG*p~s(C zUG;Wl59-lkY4}Bs3nutZM9ocYKbbM?p_w12h#jw1q1rEw378IzPWuWYD1pg2ms*+D zVxD-6$C8ekielxX!?rrgTfrT6V@1P~LJpsNWQZGNI~rTbMna>}W74;iHnEW&z3n8M z2@2)+aTDDN)5H04P~&v|#I&rG*DusY5vtxfT%1nW_KM^HHoMB>y(62pG-^$Vo?Ibq zNNTA_7V(WZvQ@{$(#sq1THQ63LXp>;@(o^J#P+B!=s0*>EEbu%M;)be5|YMaZz~fW zhjcy%R+xH%NmUGU*}hD6q;qNq(QLu!4I;pZ_s%f`&Mq>~&eOcAS&{-O`1QhyA)k9{ zr*0GqI6d6F=k0#ji1gET@Tq=G3#u0xbe^hd;M}?PD2lZlSpC>(!4y9(&TXOUDF5Efx8SKB==QYY4MUo=(5U7 zHdMy0i!XC1Yf~taYl_T9HKs`;uJK?C;w59Wjq?Jyb7TE(wp1;KDm4=03QGqz{>@bI zmFw>1SQHa#y~cP;SkB9#D<0tvO!D(qLA^UgaZrVDWx$Ah_2n=-DpYf-84S}y_XPS{ ztEt1P(8cM?J*H3fibYd+z3a4fdATQfzR#xqxi<+uv|01Z_3on5UDxd3A`J=by5uKM z5oaoQFFC+xBEu`LXRz8H~vjho7)^p z#!J7P=8MOGh@^`9YNir3x~3u4!GfnLo|4%{hXk-@wB)e)&6o>?{|TTs~M@_G7{v(coJyz}8LD66TSB{eaDDezdCGp9CDi+}xz& z@oVbed3xT}UdhJpqK3bQ*CVvD2Y!1ye9=Ige+nWhmZlz?@=rmo<6ZqJlZFWwJ_^@e zX^Tx`DrT--Bups>E;C=6_`PFXlGNu|@6O zlhykiDw()UG{og2TCQ zULe#rt|2>xE_#839&?7+z9M77WzZiKyN#);GayoKFWJ5hl7$(}{93Bx@a`iLddL@ttB!OsD<#DhxG|h`Ab-3#-|2Zp zlr*b!;P*fUN%B_w*8J9B`|Izzx6H*mfoMA!fv@S$s$Si&$o=Z0QloF!~ zoR;HYNKyvmTId5EB(nKJbDF6pC2>B4M8H-f=&I0(ScrKq90+c}8QB8Gbfn(vti8Z1 z{>t6gRx1L|=z+YUy7^Qjbn22f+3}N$S#?1nmH6#(M2u_g`hxBvoXN${kZRXp;URKF)vD49rx_T8c@Z8d!ij&4#!F z>Q(cXJAc%8JJ0iB#jt3S4~=Kd-xXj_ISIOpgFDHxx~&)Sx1Ct&d$w!*a~4X))TqiD zj{SuX1d~AM{OzGx&c3H2RpH_MSlogUDb)|}B>*6^mFE$tz|`J3=rcl4p{WWeDVE5sCHXT3CvRV5;@U1%@ zl5IRC{78Pgl*GF=&-Qvt!rdGR*p(12alPO3A2R-RIxaTfWaA zAmofm+-9=>>hY^VqG2aDvnfZPq$ygkdSs3oh!aE!`0jZzo6|`uo2=}Nhub{E9LX-k zh(VP7-)*&18`yZw)JB%4G=r0;W*T!HcwqzoFs-PG@bw|^(D@&n(Uu&4&C-#ZLz^BA z3F)(K^SUDNHCBWrrA2Js;6ClA;~)+SR-UO%*Veadvv+L){@P?xbe43o9>3AU#tj=8 z1K?Ck4ymWOxzz14w2ZF1ggKINptn5Nva7 z!&y4zV{86v*6;V-?MV5cBiFO`60Vf)$xDBBO8t#ULiI;ks9DvxywbEi?Tk+1<*93* zePEo+dRIV3%crc96>=PZfIfV%iVo_bF4`bowCoQr!z*H6tag?d4Q23Qm>v5y|1F$a zuLMUJ$p^`A@HQ!B;K9`K>r!TF!$;5dCMZb5@;0Z@2_VnQ$gX5qIxj_k@EvnE zHU0%}WLm2dm;uW`+HPr!wWD4h5#;CQzNtA)TfjDza`vfRgHsLSDX1U$*P3 zuv1MKVcB;po=pv#C8y2Ru3zGdGulaVTUqICubV_mt{XgtO*UE-ZH#r|EW;FctXTVY z(*U2&Re{6NlWC+*V3|}f)f-puF!cftU3q$0s51~D5BLHRclLZ5RR05Qk!=0rCEu`s z=q7k?tXX?8woyS(ift?n$2*y^ICwlRSfY*OWgwvX?&F0TyP~4uDy>UZ-m-j?>0%o# z=X1WZHc=?-+EnvAxTxDrS3gT@)+|jv5Zv=Eh5rXEO)ljBA4~K8y=L+3>LvS!TtlAx z^x133KF5Lcw&>T6$1EM>j5x_U(K1l|X;_CmD{tQLg1g0) zcV5g7bFa(Fh#VcFwYFEG63MI$cUPMo!&KARbMxJ0(Ig)NMF5vWSHx@o%h!L*yCMru z2j8t@T1Q6`nsmf#hs}k6{yHu<;elT!d6sZ&rLO$h(ZAbU{eJZZ$b=a`$!^O`qT>GGOJ=98$R#YU(syIa>)f zoffkYYG#}sf8t{!Zoil)bI=-9F-P%C#w<}6CE3gfkF{V_yWDSGSNCqESYO^i)cSo& z4*UXSQ^*3C_uBl3GR|?u|K!IK;u`*xFHT<1iRnO37nZ_egqWw9{J*p;&;n0CR>spT zio{BRMoO;D+SP(KC{O!fr}y3Ar-N#~({exfL_Br0u#wYqS zetX#Y)20FsK6~-{`4d1n48&|nl*G$vPXp4~iafw=HmvG6uB!%WDm#MnVySSct~e=1 zg5{-?(ULWG@4d>;2Z zxO3RQX!RLh@0L0EObQ7yaZ8A)Ay<_kOz@Va8)k&3pyM&+Z&ylabz)UE&GM^F6Ocz+ zfn;bKnftwOo3dnY2;wQ;oC%EPTrqBzXd&1JvWpZ@lLNDf_;qR5`-k=tFsggCu3NRP zPvIBD#rm~QhTnb$nO{yx@XDt`NPmBR_Ws-_Ku=eI8JY>!4azXgTR_BPU(AldYm*2X zIwp_>uhNQ9Du3$Oq-J*seALYKZiSv(gPnkYAE?5Mf`%R7$$U=dFNTNp8^Gf50G$3H z-u3`RK)S!fO0E5GeZ;GKxM*)_yuV*sosjsw;86h*wyRh$o|l2sfZJBFCvCJ$jx=x( z4@0JzHx!NBrx4j9z4T#9f6x2&4;*au?}?x)6kbngKT{LmAa7p2o8j9#W3u4uswVmL zBOUbeGwdbrXakjqJN!conVqUhLL!MJ$Tah@iEkTLi-lm{_q`&-y8uT=-e-@jR*c;R z%ieI+6Y?lswIbNEj;I;ak;LYSWug>Z438g%$JB6jp?$j{@EvBs=(6 z3v(l>ys|k>2+jQ;8udr1eh!Q}<^NdaI`25lh#J7yn&OM;omRw|n!RQ1m3l^pN;s;h zgD0zY)bkdBFj%!yrNDlV=L{V(hc9RX7)`=*XOR`A##4u znThSU-3a7GEUtm=X5yr=?>r#-)mx<|AMhxm&gg8r4h0gB@ZOoAyxD2#ke+m{2>`O1 zXH^NYyRe?px%2o(b&{y_>N!H3jChk3jO;9(R~${@Ub|c(1jYG+czW@xU4QLj) z7}GE==jg?0zdlqvokq)YQJkW+Jmd6jJ}z60V@H*7bd9NK>3C3C1KT5U)ARCy3%2%v zDo4B0H%6r#N89s4B?`U95Q(stWP&EPp&9iLhij%p^rIouX|hs}@hHjC^^4v8+)ybz z?<3W0s$*W;l9Fmc?9IIF#^ zA>{dR`?W0<7V29p!u%^<{59gmPm1GXgm@056n$wSyu1|ESncH(eH zlOkPMr>G?R+cKSiK#!(KsxY5N$&i{_vcFfsk~yf{Qa^HdRNhjTOExMnj`yqNSi~C{ zuXE_upsqa1`7!}Ckatp?bKU0(3cx1<_!?)u?{;rkp$jM@-{){QVZ0HB5rBL^X%Vik zCm7INb}n4r1*|lV?F^`jV1>A-VAHZ$QTExmK0EF+3H?}Puv|^YUTKNjssm*(Q74C<)`AACFkvU*nK##Q;D&KDO;I@h!a_=Q=%FF&w4-)RL1A69g zV^Ud$|JvkK9L{Xudt-6YUB0dhq8J(K_al(E3jqVOou2?{VjnYMz&~HjpMV$Dq{%C& z9J9sCN+_U{~yTXQUCv8WY*82c$znugvqc8Z-^A!lV&;uLbl?DOp ztjMvA&>SXNlOtI!LL59Z7tzlG>XL_0;#{i5sckSL&8QmGo~m ze^`!u7FEmE<6OIyT#vJIO|Bv3N;#Lm9WopO?FtLyZjw9vTVjaI)0vn79B0gC`H;aI zBrx{*SPV)ZbmzdW!Ehy7U_s~68F}0Kj)O&4icEDcW{+?O^DLY4KD#(C z0$y&&ka$^E7KW)UH4n@ZX$~e}j{FPEk&a$oTIxGZYjVNBofBbYB4CW?1bv1@cA%sVn;mv0H5-(` zXTXdJ^*;%CJg|+U>J~8b8B*1_6FXvL?4|8o zf#rgv1IpMng%8Y+5EI3lr{yWkO@K_KxcaPsYxAIw)v-?gea7Lu8KEo(7ZY&=k_Ch2 z&yzMI<(FAUZ(HWaK3yruQf5cX35#Kj+AIu5!23UPz;al% z_QU&auFhX)HBn1Dtee;_aSIiDWZ>C=DpD4U$-?j*_}fbN9yIy=TjQjVj-jhBs~5)MV61>el8Q8=RxMVH=OuXVU}9 zbBN81LxoGXfXoK>QRUV`dj;Qn(nqo2j7pfYABLzx=UMuZgUK`VWQK`jnyb|hq5_k<9_nJX}o$cW#U?= zjf}RCxlk%P2mtt*?!qNG+uc!lw@W@#-R5%mFkkB@W=}I}=!Dx&C*Uy*DJ9@?^7JXw zGw3T&KxA{HtF%~Bj;g4jSnH%==P;1S#z=CEy~rm(J`?{a>xB(OS&cnmr+HA&`lg9F zQD@&uc>~XXfgQZ|(C<%QCdsG7biojl1U~{ZBhy4e-4Em;RseruxnMsH3@8I5)tAgPNq%1?<<( zRh8pS+_vc$u^3?JLeoSxX)u%bnMKZL+UN6>)d$5?tVsNIfIBi2J@dnGNG)tj&TQ8}B3$`SbWWLeJLl4Db;HutL%U)BjX~OY={hX$wE)&^B2FXJ{rnv2;TRrsf zm=Hg;phoO~oEnQfej1uC)cuUZ03Y)OISDChsW=o6L^#XRYeP*!2dl9%Qv90Di!c(U zGvMyv z>F%CP17Z697hFH7k_y*OcN!z={pF8pLHfHx-NEFbNZ2(&bMRidF0x%8nbwA*)0=?) z8q<XoTh%Y*tlogDC^%2}qNMp=38&`?p`G=Q`(3Mt>%`)CpHXJ;^W2o4 zwACntQ6V`TgN;oI+0RAr@^l^3BJpxDoh~Dy!5K+>J9u@|tcJyt!=p4|&OXX9U9D9g~hr)}Cl@ zP*pb4s^g{(^;sZu!fZ47ipF*ffLd=6h~>OCPwem(iY-$5oPr*R*@~)$A>oboi6hwQ zw15Myx{`TDSjEg!$(1E5zw>MNSGUghLP%_%#P_kO^% zEqjWw34_YFtF<@LSR{C9H!)>zB=M98Qx~LUZE@Hp;sbeF0p+lL-sMU8c|9L48qVKI zbX5I8_C_`YD7ev1^VI%cZhaHQSi%hg; zbHulSoY(%_GUZ<#g9OL-TDaC_gCFR|K;|b60zA;Z(VV#E=v_CDGw0 zFmf)&$rsyOd{M^9m|=~Zhk1U&9m*Vr*}3dw9KiBb0|AMChe(c7VTzf#0_GmdiG`-PZ`}z$9MoGoLs_DdGKj8ZJaxG)t76HJ3kVsRPHa>MqFKoG4o+fTN5tpUYdczByv(R- z<>DP$*kMs<8b%Ico|PMA1J}Z!S8fMZT3aT5Ww{(KU?7-mZ|)#?sojdw@wn4()F0BC z<`ahqb~damIz_ukv@*ETUe0Rsr5_W_AeMcvwgy|Q-`no8Rm^O2duxkTp-VQ*7Q1*e z-B?}v3o&pvt?kIFhnn~Xf3p=Nf?`^E09MDGdUjS|_yJ=BDWv~RgAnLw1lzGJkeb#e zVAhTeg9nTIRBP&6+8I_PTtgkMrfk^bPiU$JRnYSHsVMrK3eJXn;g9#joZCUl@RNr* z3d>P0_)DV%G*JfdfopT%%OlqEM5;9QBY=^GF<+p*HwpXrr|ltz{1vPO z9F5_5SiepC)11SF3$K)UxwdyyqN@&<0LQpRN!f?gWa;!7iP}c` zuIC8T!wI!_JTI2BRyC`wc)3_jG8(A7#GQRh&LADz_tLPM(+bZXhWnY9d}8H+UQXFw zWP#~ z2%|T)s{PR#U>bKLiFgLk6nW`-!>X2@Cj6v0-)H7f(Rfs+aX3=mB#zHio*~{G^r9TO znRWF<46TMkcGhZj0@7y2ou7~_)8mZYz5WvKDQ9S*vQC3h|Y4s8a;Z^fG%kt#2Akt#49xlkKetp^*tW@S90Eu*5H` z-n7}penE2>y>YN}*wxIQb;LVbUr#0^DjZK%vy;c!5uKCq?t5Y{(KtV!y<@D;eXZT2 zMu)8$vh%$&4!yulz~IU+pM|vI0f=sDW*UF}sYB4@&FPq7Q(RD=)z%rcUWdITof4o5d0!nR!G>SLYoDuCjmj7=N2NO-o{rz<8;P=~(6+4-LB zj@mUEG>Fu`PV$FTl@LyOM$I<1U=v-^|Y3?;MLbvgOU9<5To@6-L4lFO}%Zqqo z19|umo}7y}(6-SuxOkCIe}?7BX_hVOQ{u8do0T2^1}k(raSZ=M*qlPC%Ig;sc5FK#2EMOELGkLmP&jWt~7^dv4Z0|Bjhq$8J-u&8aw-O*88nSGTWYGqq*dMtI zaF{k(5g5U!0_?f^imn>NR-ot6+f2URjmCvG>j%d*##UIhnixpHi+MUN!FXwY z&jA%h;t2qcdf(9yL`JawlIn4oY&7%gj+8NRq|FrAOm@2Hfu5aZ^agskR;QVzuVG@+ z)Qds9-m5{Z>M@cII-%C-lhzen+t#6>3f$bb*WS;qLE6G*VqQDbAmV}xVqAj!v~}HD zZfT{~RN{Jsu;!UoJJQK;5~&PxyxGy2f3-B<4W@jAu#vacB=hTY*$I7~eQ_>O=4tt& z7>1Hil|`!2+<*7t;r^o^SzCAa(^r$l?x(N5_;zczvw19P$sha__dk7gEGc#*g%@-S z@S#u2Q98@Yr^TG=(k?5C#gYVIwgzsSVu|^@eLGnsp8RLi{NSfKMjF;JI!jS=5_m_y z_7rmOojDFf6@^CO_J&dPQ1ZwpP0s(FzzfLdwy|===8)%bVx~j5UoWfb@INO696PM*_itKzqjQR+K&J>#{$`~XE2dzIr zu=-PrPL>BW@7gV%ex26gVYn1S@_Jf+2<<#oV3Hjzyb4^}d->+6K;8EPF2{`-{amML zZJ5p6t&FYAi~eagUG8NISW02FNz!1DYjjv?OzWt&ZA5y%%oZkJTk_N_jn}l1oekMj z=GkLGbzGeENW7UX7i{B+*^lof7&guOZ@>1)%OVRj)U9DLhSj1uMvn8ds+vrge@>E_ zDflv~Nc)YY>Ku?RG=3)}*JaaI*M<{I!d4gA*%;)iq^oeA*=X+z%aY0 zWo6Y~gmF5K798**0|E;(8kDy5jyn`yXDHm!FV?B%@e3{P9Sygf?aEd~m!*Z99DOeb z5mK!xYbGiwS)I{v)Wl?9n1p@SV4tVXk<{G(;ku+;O&8P`G*(`x?N(Gu=N41_rKoeu#;bysqBOAJ*jz+F^qv7~az;HIhKRD@Vq^8)nepk3Iq|h)U>TI`tNXp<$1Td?=KwvE_`| zmE}f*KW}}hdlA*F;fsBLE=rDJF&XQp+-Bk3HD9EYe5BvfL83PL164(sl4dD*raon- zF&ewI0&i$UPTMGMa%p;%@13Z10%QH-v#0y-9{+5IM;f=)zmuKX=At8dTm5h86=*X~ z98Az$udN&!=mM2a+RUy@Tx+q}g6CV0RS*f^w6HG+57C&d4tms)aN0CgY7etfDz{tO z)%Pfcz;C~KviBrXZ>r_acBST9{jYbl=E(WqpS*eX?x$yu_kZL||0mc%-!6BywuUY? z!%w&l0zwt4MN4Q=jEV`HQvpb^XG3Nz=aaIZybyOvScWTzqPX&ZNYB!8G|y)X<5SJ3 zx`J_yKwxKVaZ!p_r*xP&Tc#2UU@jMX&z^ME86JoabC}Mv-G4SO*~)hT;FvN*4lm&t zeUfsZV1tnY^fjuP;5AGO+pw;n39zpwn_gL;d1J2*gh@pm0}9}j66NXusx(}HiDS;O z!UZW~NZ#J$Zh!se)&8qTubzVu9JJVy3^JkEDdN{@nokx5|7>*~bEC%rlsru2<&&Ra zt+D4BgnF+w+i&M@rw=~8oywVt=lAR9&-ULD8PSWYePcS+; z`t(jN20p!86thnpAPROe*3-gSO5WbVM?nuvBt|> zQHh}>XOASBzf1$;q|t1o-%Rw~(#C6G=JtC=T7qxNHozCt52YtI;gNj} zkxi?+&-^k~=1u9mdhQy$2sl6! zI_`hkmaFj(?kBi692cV{uI~E++5AK<^4qPM1aF{3{d_vj=0EPg03&PNc7x z!~OoYynT=U-@Zk>`xde8-3PZmwdi+?f44ed&>-^Tns&72UnHP)kBPZPSl&OM;qZlSf#$WkdKCG$a`)3QuYG1CvYuSP@Z*gtCEbI`Dh>ZK{^p zr|E^~&nJo-k4{gFBD3*6Rl$M+SZTD(mgAxa?*gtPwt<(g_BsD77850~{boF5=(hvY zP##ZI*NiAv{hs_m1l>y?vW{JNfIJ|cUBOWl0}_1cF~X;PHHf(T`>mBS`~Itt{)2jg z0m+lYdF#-tV>X%OGh*A|T>yjhaPkCI+$69*iB7jY8NW`&^_wTGSWh}J(wn%nW16G~ zo;f<)Td87R91?@{x77@5V2WJ}vVrc=bb%+KzshF1#8j~DR3a0k$53_oi%r(o&S8I1 zJO`@NqqMB)L^NAOSN*d8eP8C`xJYChU-B6UQkj(iq)}CDP;U_J<(4hUOrGCjmI%^S zY@8Ll19Bsv|jM_AB zZ*{evJ0MLvazs_~3tJUc8fmcy)g z2+>mEvmy2tip4~S09L`YzG{hSu}Zv2Yqw>*w@US6JDO*ivB8@6>F~6P4W(y_VCOa6 z{ht2x>?gGpg?d9#=~N3RfOaIqmKg^M;fU@#JKt`jwsWF1sFr+whh4_@-*AcFY}z6} zl;RDB1|6Ry&%jOcksgHD#9I!JDqD}2Gj_idOxR5*nC1YvEZODx)-H!L%Z>-ht*=P> z-M!V7AN=>>i`RI8PU44K?5cm7&d{`5U)@qq!fBD<0GrM2c}s)GChGs=h6K8ru2;J$ z9h=0@nnPETATH;|z66dLXRo2>+vKN{bdERL-4EqpurN4Z9*XW9C=u*On4hBejk1Xk zX{s`ksiI*zIR+h(FO$37_E#c}l9w1@O6HE_kFS#;2-|H4+ewhWMGXaLCbl zIefNtNMo#5M!<1OA9>y{{yNRsUCAixPM3qE^?(2GM*VpTHQiVGG+EHYU41yl!+m|2 z4of8bum44#87q-q{9pgGzJP80DHNvv^}p$xF=jyPzy4RN6D3jU$-swRvqKmJY5}5~ zAgmPyd$b&+yp{`+CaB0In=8n7}NO2w>*WvK(l5Q(7CPoDAW0pN_m0mFcHshF|x zQe|nq&z<(<<^Hq%pPjY9SM4v}jyFDiadGf=oc4|$_MRSYd_sK=H#)mebUE9BiYP2X zdBrJ-WEx`{;FsiC{$fG1q)#gv$F~;}p)C|fi0feH26v(^bu~EA7qQqwdhkb&#a0JK z>DiMYlNM?NFlowmX&D(Rh9K};3)dhqu@Be%4nK2Ke^Rcq*tyq@XeRqsl ztk4VgPm5pk$wa)ivuW?Idz<58RBry14LAQSJxllawqo;*aWvojf&Dw*NsOLyQwrRy z7g*^J-O?)Ep>KDC9ws?+>%#Bg2p!W8z7148k@M(~p}UWzM{?rU#p6Fe+Uiz5e@XAZ z-ZBXuZEfAq#d&EYFW=2)U?&Q|%KS4p&pt|}_g==^MmAgVTa=Afu(E>u0SitlE{OzWYz%C(L=6m)?i_NB)jLUmDO!mrg&qHSnSRW&8HCx< zN9&q=ev(Z^+Jr*36oJDsmcTzN=t1$$GF(ROl7SgblJG=qf^lbtiv@M?rzMAhWPLNR zUKv&zcb*c8z$vQ-GvlInTk1$cHPFzxMiP0AXbsc(kzJO@B)qf9(dp)l9l-SuiNc#C z$1byS^Uq&@YlvFK@tCi}#ZoGHJMAWK=ZtZM%P(vUsobtw)&D6qyl<%Z{L#qOg7!dr z2gW)Zt6azv`|falpZTgr3zln6MUyTp(blO9mGwy+iD%fkVj!??;N0C}Xe%`T<_&eM z1$d|@o|vn>ZqiCxzL%|Y#Z$nj8*W<^*G!%Q0?{jTuUnZ$Tr=x&ESWU?Gp(q6oEef> zXM22GYc97u__gG>DbFhS4|nAfpv!l9UA;@|%8kgSwS3@9U6v1#XTV0jl+KH+d`BT; z#k*f%fycm=*Gb=^sl{6Ru?OR7>KJOGbkJT}rt7_Yj(dBkSgPKgG{5mJutxFnY;08g zBC!}=3bT)2E4W#9?SqnPd8Bu~aKy|J<5uDnuB9t`e$xt$2`d-h92k~gSHniXYe7BVqBY+FGJta41_wT4 zYoM&{>MEgSR3-eWtJI?Im)t{5szH`&Y2C4LDGvdwxum@&B8kXDFg_>+B87IZCTiF$ z?bU4GTwd!%>m)zTRok_BKu3Q%aBua#dv~vUxEWU~n&Ijf)6pd772Wb|1KQY(d8YJX zH-)g6{6+`Hyt~y}<6$E;Bp!CFvuiMJcn55KcC=7`F$g0L7m}T{ysLG*=E*I)H?8?a zh4ayj`dyoXsaB_HS(nS14RmbY3~NiBc1%cr%zivh-K|-FRhXKWw(2Ze-2lAn%M4o7 zf584%t+^q6WwqVLX|i+-<_TQSjj`MTI<3mb-N3Sknz6oc28S(W? z^HlGyD@-!!3ERx(P25iCsXhyV$Y!m$0A^p?jc$PcNUE3!I0CPc(`w5q6Ub)gj19$P z7r7>?+|Ut&dnj>AMw5coxUfYLYcIUX9QYGg#HcB!e1*p*I*1(+D2*gQydRP1f<)?| z0Bdew0=-G-@A06)t`5M#U#6Lx{% zSm$Pc8FDMuv-z_?23c;lYJ3Pgr_Iq{L@q{c#rNQ&D%C_S!(3cn(daDnKGrrWwhOp6 zCak|my>&4?B>E&UqmNUJRUvqmOxqzQtdHW9FWWiimcm0l3|EMeYO13@Sc%4!G zff756Uf0X^IGoo7;c#HWD`!{GIv<0^4Yqm*!5z}Miq!+lhNgW_>=8K#Kv8B@Nm=@7 z`!ITJv20K+2%=Z&@oRKpV)v}6QIk)-@_WeRa(dUYF5~DCP48NC2wPz?+9NI-Mh zB$oAr)0N?*Wf>y{6LVuc3oRif6GppaJ8dx|1gjT6cN0~P)jUx2bz?*!<+7tZ8$%(J zGXZNa5rP2_1D$^IJQKaA6ej$fcZYtopGJ)FR52Rk=5;+ozBv90LOe8x|3a4jwF)9< z+67<3ZLaR1)!;eqP3;cT0_52)?;ZMjopW}W^`4ifu@#|wELD#u57uki=y&eaUH|}z zkg6O{<0D|LAUh2d)*MOw%e#||xN?~uIUj(sF+JlSEKg5Y4+ZJ0*#b3f8a|fo9ErZR zv3~NdG{yQISQ8_kimCcKd!tIjAIr@emv3+1d0CqPe2}1^uoFGr!aQF^_^)rC(INCF zwdmjrJc+FbeKgZx*KDXC!-B3sSY57$nXTO79L5#Y{_%1ojyq*`3P5B*Dt4TJa`)Gk z5NJX85)&m*wzgbC;ulY-H6t6)v3i*J1orH31qej79M`07v@&tx&N_EZZ7*+J?`^~e zDxwv;mRe0u`^p0Nmdc{2)BgO3t?s;NHTGo$J-`Fm;v*P<}tC?wsf4cm&(nf&-WujW5wi#CpP5=y+XSS7abdb!2KV853* z+zR{?-;E48hM{a=4I!Owvehtgq|~h|6*aQuRH>r5u_FyWG#+cvmw@N7UDvvx1HwgB zvZPc$2dSVlSXECx*zPfBp~jG%ig84%-6G(34KvJQh;YpqM1)Jftoz2Q5u7_s0}}v- z4q%Y&cTksg-BEzem*EWO!&FEXu_@*-P{i@`Uy)_P2m`xe%Kz|ELWBh;R-Te5502gB}){FO7XuX>=423UNoPI z*=H8(=!JdvqcmqOpiW_H(A8_{&Z`YzkFTbl)1m~6>a)uNOor<_aj>nHe2tz1na?h_ z+IdDR9pm?=Mqs{%n_LG2fDd8vKjS&=*$=L>czWY8wxFvIS79CSvO{k^_WAf z4-)tL_fV24VB)8V;lgJTsBCeA#3J1r^#S^gV}P8`7kz#+Hw22?*qkI!u2a4k%LH%hKva2H$_J-(Kz6) z`vfR1K$iF0aZ0m^R%W+~A$1(Nq2)kj zep*habw@jFf!U-&vE=gf(i;ewW=rtNQ6h!jp#uUJR#rc!V+ohm=At|P!1a%)wl+iU z&aE=Lt2+Eiv8bY79gi!Gu+lly`I5mQ4$(~O*#y<;J7&^Ox(@)gDuozjehfo4Oj^YU ze>y2rnq_+dBsibicdD56x{>I)-QQ#?cKcqk@bnx~h{V(Iv7G(lsm(3`?QF6<&ZoW1 zFnjYOias3`^~Cz2jv`i9YzsopQF@wBR>Wz&etOF9_%!!$PK|2JtmtDFZoQUFyoUqn zfj5D@7y@uTyItzrks3GwLY(4TgNU3VM^b>T3VVqavRvTQO`Y|{5&w7RPO?2CMqNzu zaU}g|I;SxjBnLe^r#6$nAm$>6!K456whz@sjmWg(^0GKYFcuJ?cVb;k;sJV?QUGar zSRhP$k6yNJtO0RW)xk(ukC{IMo<31HIB()sJ~N(Zs7|mFM?|4e$xXbB#&tPwb>oK| zdw?4E-x(1#2y7b8BQG|ESOd{7<*yBx?A7gJ`?3LW*xP|C>b0gV`0B0rpSLCdJzMiN zx9ETJR^6-z_7t7fL9S=7juo%+T;^GSyqvQ|d3$SJFa<}#Z0#MMx2MIj%oy{Z6`GNZ zDiHTEb7i5}rQyZWXj-EvgSY3a76*$aRD|rs?%>?KfaJ#O^BcOh8aii)3Jd0a&va62 zXPr_S7OC(@$Q(Wb!;05VP|%Ur_HMnqpWK{C(gg4#NShuteXwEv`YP zIW$V6jP{wa05>v0cv&)zk;Fvg@GeYkK0X>+nAbj%&` zfDDiqJSupSdma<$5kPA*%$qo75}>0gVGgvk{3yr7L3pwGM^72NFk`qVXd6BOXtyI| z!INBv4e*ekE>C${z*#*Qtgdn56P0E@Ju5U^Guq?khF^+VRhPD}2a0iF(DGe1gZY&& z0KBN4jb4=8*>axI6!T`CrPU9RnQY^|`*riKosYYI!@Efgm4Bqm!mW{meC&CHK~a7$ zc}!E3gpl*Lr)Rv$Sp(Z)FJ_A@u5-broQxHsbjV?f;MpkFBhW_{Qy4iH41EImhXEo$ z+9!4_uRGc72dKv64eE9HLr`+<4Uu;f`tGC;C3cZS)P+3Q!F>*z+)_Nj_is zshrb>HU1puJe6&CMeGTJzRVB%RHM7xk-g;Gt*xzS@C~STv2eqz>#aOku!@c!8yl7! zzJosuSauID-QEHFe7;YgX^h%3n7cJI@oW_O^hMCTIcuM-XZ?BNTefkEV)~fIy4raE zTXjeGza{rPgt(fLldZ$gll!hR8~)t)3zariFXDo(Q6lI$F>x@x6ct&j1jF2}(wD(< z?1a`bV`-u-ARk|MmD;7qAn#t`_86jJ2>V`CxeAidwIUT7m@+^KzcORu4^g!X?7-Ri zWQMuEW6MLN*=sI+UO$nJWo!hB@R6g`Z%gMiLW%upevzk}*1S zprj3KnZhL>PA1=@Ho}VzLmPnW@Wr5pRFm9_-GPlxj1Rd6$Y21m(OXqFBi2=QD!K}; zu`qG*PNFwU$vb~kDLF4+DnOMN|2zy`&$@bDI0pJ5WvfBT&v>%}06_1G<6~+Ezr$87 ze)Ut_UIKksdJMQkSk=2AfqI0x#Sll9VFok(sKwvrV?F47;K1TR60AJ7_l0rPlZRI<0DM|2b&Gr zcJmL~d*o*9VCzu(3|>- z#yfw+#aNs83V+70_XPlALZ_06xyXVBfZj1QkAX1Dxzk9T^`kDeST`Yvoeb_&t67=`ed9J_qfNYQ7hwgrlPQCNRV^@UISK>-o zNh&oZ5@8(^JT-i`TZiDk52I8;cT8ylIA{{rQr%5cB$RHj>|#ShdYB8mp|;)tYKO!&*ivsddx}YSpw_ zTBB%XX2frcF?avN7fAT}h2OJS&k(AxrqkrhVe(~`eEFXVt?X`X&C`NE-3-1gH0O_l zFJBD4-1GBfqx=-gm{#9Ue?k&JVvy1MuJ@kr?)Y-};Eq>0aUmS7P%;Q|FNf6@tX_DZdgI8qpOqee?lcV4~DqL8;^nZWveTK}ChQr-!nX5zNwQjjjgRxXo~q zqTyp_+U#O+y9Q(?PNAfSVvx2}_l3@-exn&NBedX9ft7Tlv z&Dem7j?LyTZx7mApANQr-yI%o(Lc94Zx7pV`|bWlr}N-Xn_k(cg5Voz##QwZUgbRR z{o46aI=H6Zy{FkNZVh(6kptkje^hPE^L$v@N+T+Hq zW`}NDk|ua(Zbs+nD0?;YP9$gPM2{m!lk^yX@g|NSTwPgCWXp4vw2+8j(s^RUCh$TGJ9}71IGCoa?vU6=5atf?H-xNa37&1EXI0)@j@@3jMOD7WEYDCM9h8?**a8Usw z#f@h!TI@2(j*jwC&TyS>ZonuSJ^pkefmZB<>2o9cC00XQbWZ?s9;J)+K?`utZ!+#< zaDHzztMuF$uOX=YuM|7Y$7!xzIdHX9aCpu>@Cxfzuv|^tH$)c^PS+8y6gN~lM(`R! zAsv%d%->6MnJFs{?#p~v#(=bPqdA!X$(X&dd6XaTqD zW7FBi5&riyOM2y>(HxmosOAfygt24O=e~Ikl{>p%R%Jbt181OfnM-$UGa7a-(Wu8xhe08do87 zavKkVEF_m5My(_->oJEV!qZtcpA*A+2){S)A-vGBqxvyh1#Vd2Yz~q$(ja6A+d)|l z@K&qpSr|HydG(u=xFban;LnD@n7~MLvLcQ*L%=?^7r;%HKqdc38vEgIoc@V^!qWyQ74K-r!I`Bu)&GO)=3_}5;*cy&46r~Fj3J%?#iNvz9*g|V%2+|2B&2rkvuJl-4%!w3YqK+kht!)a%D0(6dc2XgO_)(J%&CZ_9BIZw zLH8ClV@MEJtj?isWGd8GxsyoSzg-GzEh{)`w5~ZS=WzCuSCC6 zH}IM*Aj8jW6)FFkWH_Qf8^;rlNXp5|Je_XB+TmS3wz>Fzv62Ttv`NZ^LAtmen$|!S zFntEsdo8T5Z-mxywm|3X^9JnF_^)@}LG)Rmqt#b(oE}*du9;Y|C4XCHOIFLX0oRd4 zNk66mpJyLr(+g~q)cEb+IhSWib?jSGh5QYUFIpOHekAt$FC5H%pwG1Uu55EAC~aco zX6t2IP>O`g?dZ2ccLl#B4c(x61r4VSm4QSIQv>PD3OBq4)AMwNfuYb&G;-S)6d4R!OvJuKuToT4eXs|;39-cmz^1Z&58u@ZV-c3GudQ_8OVzSzJ zvMvZ1oA|abKCL>KsM}q&D?-;E<3b5;>i+sJTl(|}0<|-(26_&YLTjpv_nI*Bttf@@?Xv^d$SEl4_D%1b!;pw6#K+fhX z6omd|z66Cg^BB95Ew7P~!Qy4gnY`Gz#RR+7)xEq%`RoV%+fSkUE~+ z8As??LO9=2c9CfNcrvTQ$;)dG$)RKivHD)3hvra53kQX`R`5b_nIS?(!tTk@!MH*o zSdSgJ^dc+k2>0oQok0~L-d3ic3a}?v#5~*-Njl4-h^8*xJ+r?!LXGm7D-l8yRSH(L zN$?ob^O9BYPGOEmC;QWEaZ<EkcXbH%EE1ccqq5qGCI;8`!=M>2B590dp}*0 zuqF5*_c+eQ;^fzCUT|oJ`sYQ#ADgCuOWfe+082o$ze|ZY!Kr|S8+z-isjSVkcenB5 z*a8IAzv^HkaG{umy5Ohp+_&1an$4HQb+gkM?XTnFRNSrRaJ4#35s^e@lF{FY$o^Wd z)vA5XRP4c!x}MH4LbC;rVp~;tSD)hOTwI7Gz55CNo)+V*VLUn6&8yHeCA}eaHQJ_* zpUaT=#nF)kzxlutoqTbRde?44X>BZK<_G&Qk)ygtQO_$I6Rni+&1Y~_2wt4|MkFH8 zgf09VnW({DGV$9!Iv_4GT3jW8rI z7_o=%_5m!@wVDmkCD|&|(^UQHgaoh;B7l7$0qo9hva#`@iE9y^gAdR$Nh9%l!!+PF zYId+gFTSSV9}ah~1ODufZDUX`Ds_+mBNxTP4u2&-x9R7v@^k0#G9G*} zl!!hx(pk=g#un$@;yl}z9KbLWhY1Y=fJ^x0BxQ$AZP%{}f@o>%NvOkPp}(3KdVKb; z=~lbcM0f+UD4Upw5WiT@X(Z9=I*ER*GNanb$r&U*&##ohrfCX1WtUNS;Hyg^Tum2r&`}BCP_oLh}n;;p*&mD zH+igt#gf<+>=rs1_rw@P=@_O1hSt8oZQ=@A>W$4F@P9jgqa{cGBDtU3sZKNd?Ow9o z-`d%YezeEm7Vw8#F{#GVxtsKM))dm)x<)C2WF4KQuBg?T=B(B=XXOe~xbZa&TCHo) zYHfqOavUam%wSaZ1+fGW?B$S&Tg^$WVqLwZ zBK8e%MCNOGI$+u~Vw*3<2F@(GrFY+F=_DW907QI0AS&N#PnoXu)X~m_pLTw2>w=E1 z07R*Dka-O$weqSTs*=STu%>%%H{^=pkjtk^C;^YTcu+{b$!CupCi8 zWC`q|7C`e%j?y#my(6oBz{N^ibP$~?ddn-dN?^iP36b=0)v1OFvjR9wIpdOxts_{c zck5ZHR2f<+_-DMJ5bwSw{=tL|WAd!r0458ZKK#$^bOKkT;}cEDS0lh7$MuT3RoJ?9 z{hY?JnSpXfBe;r`QSHpp=WTQA^jRl~U_bHKG|JvW;TMN0Q&D zh~SZ3V4j`lb3QV+Tbu_-pfG~vzX8xjC860i*&x^aUY7yJvTRC>zv~hfM@MDGmC#=e z$iH4jS_=4!KqXyFar00!oRD;T9BU}-vICDsCDNY~jOEgCfet3}{=lnBJ+ilHBEG^7BPXiX?9(%J$hZ)#_RTK090{}^p`#xWIr@8k;f$N z50V#+7pFK5@CF{g2xYvhE3Q%dBpa~VRp}^-4Y7vCQ%exTMxUKx^Gc_H^gn_<7`EEu zf+M9C5*o0;^#fk8$rx|zr>2ZlN)T_a8_s4H!5$#k;I>_jc-d%-J-@(3*e(%9i_@%M zLm1(X^6zg^h?y0}HiuiKhXnn7muq9MtK@C{z_3sw03<$;M3n`Udvl-WGsKXKVJ=ru z^j8{9e=oW$`mAE#RsjP>DSa0Z`h*rkaZDT7s)gm$V(Kl{NpR(`#M-S@__n4N6sl6X zTFdvDQhegZzF!6n&4AYbZq-+OOGbNx86&dylGAK{tmdmuo(meN`MF6-!&TpWjTtD9 zVcsJ0oSKoB^{hOAYzk1yD8isRuP~5|o5EqsgGI?iuOaNWP@s zI;pCWsbdu}4WY&JO8TJPPytBM+Ne&qA&qvUO$YpvpioWFSu^I9q)nr5Kvt;N#yO+k z;qGWEdKj7M3PNHgIq+0GIR{p+7qB~Cw93Q*^*MfGTu5*i=g*#&6zNU%2+kQdMe8Pu zh}TZTpnv*Q-$U#oG_G)4-Q>GYWkt3+(fx<>di{}b1tOQ5%E!v{boQS=gON$aI@eIr z_)TmYB={JfXJU-P(2x+0w;w0A{w%=BXq}%ZMxo1|b3E2EQN00=BT2#;kRyv}y_wf! zVx8->dmV`jkv-Ry5aWI3QrL}NQ@@p?N$)-bQNRzH<~j8OUfJKNCx2o}($^Y89$CbT zE4i{uYWWpWT(d}nua`kpTzzXb@~eMko&3bMR*v|Z%Ec0e{maVjoMzF*HjX-ov$#!v zBfFrQIe8D>dD43eom(q5eO5k!Rg}O=39N#E4?GQUGlqJKDG}<*ziP#J90fKtExxld zLzUH`1-(yHoi)}u{jjP+tV!i{vs#x~p7nqwAoaN!VSf5K!a_rxtgawBjoE9ts4GvX zi>>Fn${yNVtzIrMj+V1Ey?(8)+tHMrYlftB9L8@|BRls~U9&Q4tg`)Sc9dU$4;NUG z9w%TMggoG@92iP0nSGm6lI~yd%NQ3m>1hJ!&%W^ijR{vS8a>CwtmWz31iQ_7eKix) zc_5v55WGE1)E}~DfaM6`>en90T<0_xa>SC#=J4~gAEw_gTb(4Hgc3|R!S3gheKmhG zUQ3n}eEvkBL-7$fH|(o^OWLE3S1U{p#4e2khJ8_L+xSk6b-X)(Rynwvt z=mfy<;}TvAo`PKtATv8DyMuf_tl zbg6FMi^h!LwUMo(>b`y+RaYD3yS%?Vl^%SY&uf+LtgG2t8ZKGHf)po)qx={exkAy> zMW0ii0$c|E`-O9EE;A_BX)cMoioaDEdI$l4Gl&GF7*{!cyVCHR&WsbswMvuR*Vxj4 zQvl-DlOWZTa@D34Q7mc0$2;rTK!>4|6!GkVF2EkIb!uf{>_T$Eo`(Yrb-@vWex|rf zp2IN$9itZzw&)vC-Z#z8&CnF5r^WR3oEZ8=J06B5=$LM0bh}sNadWd9ow2p-5vd6$ zKzLASWx1iH%k;oRj(9nt_IOin{ib&uO|d}jIzPSg}LpGt3-86 zCc49WrI@|gy;{G~yL8vt?rmx*M{|Z27wdO=n``}cUlLauow(GEM(cvNOe5Pm$|s0V zJ!p)nBfF8(lhoyW)I`*q6s8IqfGLSk*Fl)AZySY=H>d%xct1Xx|(P+K?UtvGiey{akyGDYiJQO12`eBD3>;Be(|17bKYYM zk)P#zhAjb`G2g(CW{mD^(=?al+AK+fzSUPEG5POyuQ^YY0C;PS!vsk>S4{_l&%f7H z%iSxpbGrXl_eXMMJ8Rn2&{+qE>kBs5N1lnTt%qfPADT58>x%EsmgPzN>U^A+`N+T( ze(>S2d##+j^aft%ANVv5ucXlw1nugTbmbcReMb%77sZ3@_zXvsx)X}ghrwaP^Fonr z5ibLUilT(Ay?=-mpg0>%<;d4caqH`=6^T=7)NTL$W?BDW)2ym_mQiB)Wy$fuT`G@cssdO$x zJ~w@JYLgI<#+awsGUFC$!DnlPM#Cn+#BKfF3BhfGQ#tAf$9?*<(*?BQ`L_JAcD}+= z?8PcTI_UG%0C|}6w{X97VfvX#u~?lDW_})N@Xn4Eq0ksI&L)`KJ(Ldr`kwA%}y-Mwl@n;^Qm|f*q9fP(OzNOl(TtxuJuZPe;WZ3OXJfS{)kdaMQTEWs6b2YQq{_ZAEU##Q(GW8Mhol zujS(+l>dnjLcis}GmOD#aGB;U;Yw!*SBD1NSQ^J2-=lS5DiK=GJCD*B1-0m}6S1cL zSJ#4_90Cge8S#BsUk`C%s1xyB8$t_kuINV6N)VJB$wLlSkrZh%Wek2y4|~oY-0)KK zUct_=8+`SZt-2X3{&HZ_9;()zH;7i4!9SQ)a&>%%%`KVRn4d5Kk>Fy+QV26vez=L9 zmSr)@+5Vc9&lh4!ccX4d;#&d+(z&-`^rSCd<_5yEbNMJ z=XK#knTW+5d!OR<&s96R&;)W+%vL{(-_gAUmM^o_yOn?vV`>SBs%@RT?R$wUhIKdF zHi_Eo=`%_1qmOjDE4_Do3+Bm74P&fh<(Jy3K)}1kRE6FvX#!))O$b*{|0P#6D6@j$ zXILb6H*d1}^r&d$lIVX<^^8&(%+^ZgD0FLO#|vq!Q|(e6!^CW!agV(ZJ4xhI-R;nD z#RfX7COQ|b*lDHKRn^9PC`cM`t-my|RHGsjOf}fWOvm|%aZuTAu~7SCGZFxh$9HFY z%z5|ytN}n*Hz4Z=KD6=6f&_jqD}O%l6NF(7Ar(bfi7gEQH~lw8?x`1YJnpk0^l3VC zF*e^pEOBVa`L0z2x$@T(A2p)ZkFQt=@-=S}0L4hvo2H^1TApD?4JyH)iKcPd`bO&> zS2t11RS0#WS5tLbsLOder@3b5Om;yGF`NrrGn(PKC`eLu&4CCH8|o$BCJq4>MKx-A(IZ#VO6YLi(+g`xP7US{kQ<7CczJ9 zBwdKc#vrM0TJZ6pan;&VFBSPh2+~g|{7;hh%l`L${bVAfC=fC`MHr_v=f!Z!y@#tgDspD&YXtb_n(Kbk?UIo&>Ht0lnv+-k0Yph)Ei=yu9y-u&!SZOzK{;edryw zIMK6G!fD{{(=s*w;sxUlQv&3)WDd zC6m5UHBNh-dhHfGe>#A9P3-7a(f1dg$*s{ULbY<6dcA9jcVLSbL|mp1y!%p7+!I|Y zBvfs_v?uE7v}XuNX$t0{K_x;QPn@KSqrj#%?2@j%*>=H{PWu%WULTv8G@uy<3A-i( z@z;mwCk#S59=ibQNYWMXM|Se!RRSdo8=OO1pn6?T!HOexLoHtp3%m(ys|u#EX|NjqDi&|pfPBG7t8$a zZ$0OnsycOP5R^4eX0!84ETX%rx~`|r^*QEEYbr`!&~BVt7kCtn;FV}9Z~&}ZM?!IG z%A;keb{Jzx->01IW^AGg9VP*F_HnpNHD|2R7Q_a6^`Jo<5T^w}>z9{L|Y9u59sN9#7tM){5g-;V>HZ8jssGPe~n zEB5twys$1f}F=NU53pOoZq!IDS>qntxP_ft{pAjGMJU2)`wqmaDn zrku_ZZomb4^%NH}(!SmF$mL4X@KPv;yydBNC40|1F4fZXN#Az)5gLqq9U)B?b;O2LcbgmkZz6?B*r4j$u zd`TMC!g;7)R}9*hWbY(S2kw#_#fkg_@Vk_~M$>y)SuH>-)+lH!wn3H1zC`ohk=@~9 zte+kl;!<50x^NdJud#vmlYPyAvt z<20Jf$!&-hJmgFTW{u3p!D6+mIfSus#h;g%@Bo;tqcj$KAnV>7Krg#YQCYFB9R_2F z<4t2xW-TnL4KW3LGV&8ajdfUO1f&q@E8}!AG6D*P&pH2I<3f#N)QUj-IO9T3TWrj<)H!WXP5I>d2_(| z7EF!fS>i$$AlQio3MK2YrQW-PtV6%6ps4)sB({&<^}0Md9PS6N`=M*;C`Ov}u4;J% zNg0{%`)9~FYyQixHbIS~&0{9gO%nZqcJQ>9_pl4P&sB~>M$03aNGbb`qs`DUGfcAC z+H!PVhX>3HK>~P)FNo)v1o=M(dH-bZce_KK5`cbm!N-5H_jLCkRSEj9dH6qmH%!w0 z4Kummho8id`Q|BSA^4yFRkIKfK&(dMr&&EN=QRb`St?6NM$f1;{q5J%0JOau>;4{^j2uU##jkVGq=!xLnv1{VXi`L-0-i zM3rjIO2B?^mgJ8=yh2JcS3G^C$6B7LsVL9XJI=E5miYxKqM!MahV_~!MT3XFtdRl3 za_)gI!*YIDOh5A_As~k3{1er`)#AcUAN>gW^VNa^0(^h?LEN9Pn0}^On;DkdFO~fC z?+sbQc74&z&g%)E6!$7#c( zopx5vja<0gv4vbp42wQ>Ft4M|$P0UZz1Heo_10ovJ=67rSLp~kD#foog-<$dsPR8O zG4$Ed74zT;J zTC`)*Y`CLQw+G`fsVp6EfqLXL5%w8i<tmmFPQrhVYP=Qs`xMMcSY{_qjKu6pO#8 zpQ?|#$7{Yz8btG1*AG^^_4bwWshrOzUjNM6Xhs~`BLR=J)TG*D=y$Wr99W*^z_1~G z+Rn$K>~BT%oJihRN7FUz z9^JV{igsR|0djjyGw&(W++Em^eMn6?_|CeMEtA6_yy; z%%D#H*Dgzro~5I5SPqNRVR3de6vVCKx|~5XOc^e>Z3c(pl#7og@ntwMyIAiroLBOj zpnr;Mh)gR_i}D-}VWPz!5BM{@$E`-cfKqvR4N;%HbxwzFDvDF-!e)IK-JloM^}lY? z3)z;Z{$porK8CTa{oWbA$IE9U2dE3OTm!XFUL1V;?d!L0t8l+|*Z>NOIOBeoOR@N# z)RF<%?I$8;kn&xDj<$J4gXZIN$V5~y+}OI&xB2<#8&oU-zx>nNZ@zw6Uwt{LreP0!fR2H^zeWe>qW&AsT4{#!M(!gP3fVT92ac!k{Z^?8QJ;~g& z8-D(v_U0~?ugjG2Jwi(4+!!g%u^Xe$bIoc@d%v;q#era%siojaB1x^;8n>e3?UyrN zKaef*(jC}QjbO(TBhJJKeB;~V1n8xcvlG+m5X-@d9&Mlk%fB)>v(WW;Wd=v7C-p!q z({WcE$DuXoN70`*ZCjsCZhbaajL(R&yuO1dB8S)jf^?OceS2B?s=yEehqE;615?1~ z3%Dd)tguOkta7G6Ibdcqp|OD)6NAI7GW(@Po)1)koQ$VNNOpTJv_Y6V!jQxhXz@=# zbIFJg`#jAQwmAE$n+YQ-ey(>cefWgb_l5wF`E6>Eg~gO1R=EXbj1SQayE!n;@pYFX zMK62>Xk^1ea)(EYrR^ZWiDhR2SXNagu@!$!ioF!Ly$F29z5j-CPxXUl0g6hkFxs(g zqNiN2MHlmsCQ&7B;`OZhfL315D%}t-%-1CYA=4C&_!$^z`H-@y{_wR(T-A9&{qTIs`H?dp`5yZsOL+;+S|YeFa&8 z={Db9Om}Svy19jjI&xqDmFT6Rh`~hX@v7_?Ma;1S!T*Qe*ILAp3WI^OrHA_Cyl+}k z%RY!IuA7!0W@go!i&_0M$XYl@d=K=7MYO-t&jij`+BOgZd8tj94Z5Lt_x#T5TRpqq zF6#T(h)5o~zt!0r6|dli^X+OH$KA@Vg4ZCskFbc|cXGPg=B^!D4niTfE*@Ql7$PIA zWloZH8zkRN{hD@dEf}{H0)YU$s+t$DJ!YMbGuBn=DvK3TY>sOl0$T{JA0iG#Pb2SGt5uJ#>htEbf> zT|M)PE3PmI$#2mA-xp7C41;HX$S2+&k+p9_GEp{gu)qrW93wI8JAj z*jq!IDzmXRA0QV~(fzsWGU<8N2-MQX-x#FSlE>hmN7v zlms_}JvojB?&7NW178{2+u4gD*yhpz#RzEcm5_@Vn<;5S4uxZK%Q5@lkU@CNh@$bf z58SOAQqP+v$$tmR1aKUTiPk>f`rtE_8^@i`p`N`ma$LQOc8S?RS2%p3JkRgrq94RT zdsbma4z?uH*|ni8rU&hFf_#nI!&fGKY;#?zCJ=AWJKmCT_`O#2)uF)D)#q zajzU%Uf%Sx+jr=SMwBF;l^AyDL)gj(vzJ{gouN(fh8c80^aKOBpF7A7GX9vv+sn*# zjx+P=dV}DoXllb&)=aJJ98#$TmBMJSI~`)+h3lp@6MKp55OY)-3V$ ziAT{=A=7(R#h#z@iT7@c_r@8$F!g+?eenu)QI27MclZqxDc94>YBG<#hj!$ln}XHp z!fG_`8F*Z@+x=?LG747Rn4cswwRW z>A+I|#Yl-}6TUFyZAq8Xx&mLQrZOftM3e}HL*?%H5%)Ebvf*8^ zs88)TK#Rdy+fT^AFn>eeQDNcR;Q|%_6D;A$usGqhz+XI=6ZQr< zIVZo_lVVy?;z=9z>A}TVu1dqV{}DrZSzjrp3ke13 zRwSxRYV~n7<)ohMt5Skq8j{8kfu=dYv|2m4#IMxL9?Jl#E%gzHIuJmmA?;j}l96Jw z@k-fX7^TL%8ig;MH4v|~InZw+$nzCgAO1Coyp!g~7TeX6r zfNnpy(P25R$sOjjD*hX26*M*5OeRf}rtJ2zJl#fnI5w;Kc{hley zU)Ywj1L;rg`sVz6+uTv0GKPMA0Wk;H{RAo0-hjX&IF{^);Pr-EUN7?BeA6g{Vd0R7 zp3m$&;ebc{)`9tP5GcX(Azgv_%P{X_0$?l3vqfVj&Ww(y!#2VG`)Ed=Si=(&-#F(p z-P%;5=Hs`sGv~lGUeOJOzOuKM1r@vvG9}peEE0MU)P{T=>^Inh(zYR9dUlsBZLUx= z6w}!`DTNs6knVAdIBOL{Vl;+cQXCe2R4sbM-!yyF#wU;59t`~nbPo-A&_6}Hzg(B`(Cal#04`9eB3 z(+%1^0laYQ(s7T?9$QS|ZXjtDou?tY(drM|rivgrjM9~RuK=@WqUn3Dn{gB}Sco9Y ze0ZVQ#B}FtNT(3x;Mrlh)vgF0U>I@yqx0#f8oH4a$7HxtEr{$%gCl{A2C6Xi?T$GRt z{wVFY;S9B45Rs~;%ma^_IYYYwG3VO8h zKh=iap0S+8tI40Hl`QU3jEb)*a11>%N6_>|x*8PYHxP74)a5FMo9%H~60JHXsl&Px z-Yi60-na%*^7ELBKjwfQ=8?D0kt=yTxn-tzTk0#!trI z$i2XN5Ox6kYEmxy@9&1e1%a`u<3LtiPhi>Ut(C|JzHGL^X(_H<*?fsbw~);$)&|RK~Hn0 z$@}Vr4!51jO=8C9Vq&KJs-y%w2}5)-Nt51Vp+BaWB9nf{PVa!0BoOHvS1i{vh=WtWTGvYGD{Px9{o zy59K`+NkqMb8S9xRU<&D>kCHAAhX@J7*AI`JWkt~OK@ENuZ!+f1E+#37;QdB)U_f$}DV5|T!o!Y4i$B1Yvs}##rwIoE)_9uh zL-Oogb5JbHQ6gYZkl`xV7L9g?M*!{}5QfF+qPd1Tca-yPAtxQP-sUPuW{nt3^c>?H z$zA)!3i1mjz%}ehzCaTZ&VhQgbXL@(~@^ z(`weT%8g;W7>{whA|_6zXos6QU0viHES0NScww(gXhuv2z|8xmryc3RI{js1=^TSa zHiH)yw~h6#7^f3%Sr^w^lZlKON78oS&;H>Ne0YZe$K4STlW!`g66s75cb|r&`!liN z(wX8gS)k88>n?3CmypBiSf~$4!xqvxsPmB+z5#Q~q1No`ec4+_`J@_|A9L4*r)1;} z+EK{T$KLLfC;we<89sWRA0)P{Vs z`z3Ug1V@syRvF>YzFoa>CFtwwkXLSMcf;w6=XnV#%m7p zXjWBmxm?cMy`7zldU?4z9T}pz!`9v%XR+R?+jdp8JGFW)Ko427+eC}(gbHX=T={3t0E;k4mc`ne#pxCrz&jxh4v*CpLlFi-e_7{xM@R;H^ z9v2m4pEEXWyEcX#E`MFOarRG+?14J;GPj5!YI1<&gYKPg*xck>IBrR)~ zd0v4iMt@D}aUk)n-H@tEWspHx{=)IzAm1hKn4e zYE3LeKa**P7D$2whLLB$^P<-)2sle4TPXLfw)!i43!>X5zD9!RUUu>sZe^v2;nqDw zIlVdP)sgqi(N^C9I>=!eIbv-0pexix?!8x8N9%TilNLLw5MsdA_K~^*?=VdT=pJZwk#OP}U##W55b0{HkE8!_C9-B}2vp(2cQ1#XCDR z=i#~)*PN|EoLm{S!?#F=@G!Np?Dp}f_=`;+Fc>g1P!_< zE^FA>e&~Jo^q-#mk2Orz@SY<-mH%YIp%|Z=do!1PGgv1QHS^BFXO{VH~cVXa-2 z9cVKz|GKKazpsa6oSVR1<(bXd&cJnjDQIq6N-1ooGc#sXPiLmX5mcq8R_5=>;BFrW zCefEwH7Ap(UzCKAL&C1#!+!UN;&M@)51j)S8RHktDKrUZlX^~>qkeevb(@$e;ko9V zL3?t(=v>=da!DMzn*NO9!!Uw=BoRS+v#j6LFfb~~aj-)X#njBdcZH-%9u(FT|<&|9bg z5UW$ddING8JZ*bj`4*(U_yb2fC-;$Hzmmp*M|WsS*wnXlR60{c$cLmt?qFrq3+wdd zQG=>KAEc)sR}=P(|9t#ieN#=|U{{gW%RKwzL_Gr+0!njTF2;q$Eve1EyA6>6D2OF# z5$^PM^T$ocv)5IQ2kc(y_Pb$WibQy`Xm20?IK zx$=UbV?sLM8nUZ1vQL7*d{=BO2n`8_5CCD*aX^rd4P|=lroy%~l%m8w+OlzLE0V~3 z#db6rQKB!x$Eufw1rG&y$zq&c7BxaYwD=Hb=z{;~U~SC%eIz38OM58f;wb3Rec|AK zvkD^R`whn6i~s5&tf6#SYL)M1=Y$-E8?gOVL;~ASd+Z~CX8XLfx(!4lNeswV+@WI* zHL1oDa3p;S9w+{@P&u4Y1@b+FC;F-34=yRlkTeZ@>QEfP%1e^H3HL_F*9cgWU9f$v1|ZESpQNs=xmxPz1e zl+3Q|%xc6dTtJN8sxQ%GFX)%tDb9mVxU0l7NM`MP4N6938PZ;^^U)W=?!<$5L2)Q5 z!q7s*Qwsa><c`Z6`Ued@BdVbhMfr1MHsyhS6T`pvSpt+kGr@M`#C)8 zc&^)$lH8QXznM&YUA6x?=D-EsaZq=m3IID57CEod`kA00)3c=TXMg5&(YX)$V~dPl z)YdcJP$|9z)V)VORP+5uo{m7UyElwNK9A_GRXq`eq?N+}vN3$^I~bOm zS)fO8D9C{fhJZZn+sN_qwy5%#6Hmt{Hk37J(z@IE(lr74^?T#`3OeOI;vu)sE={j> zqb?RO{H{bE`j9}s8O*VHSzjS9anJ!hhW6@Wvmr=hiL)R&o$F-Suyk%Kc5|>QU5p!! zTg&>Y(xE&YzhpQ`=9%j$>n{x>9`@>TIW?ovHP2TQq9_-D#Qy85UQn#Miv*$|uWXE% z%VakQBA;~FH0?OauDuxHGZG3F1PO?zyK=*G-HrVg$s=Sh*2~aSarYRiSU3>kq;TLI z;uLD$;!9W(HMFZ?EKL*|6Ai5+NXJHD=*s=~n8Uro#nJJM?fF@}8~PcTnTxuw0}$aa zLRp;2RWfNEcjOP|^>=uygGVpK&xDgkQiSW(g=D^6N}OHouLg=QA`F^8|B*Eo>JwKN zx@E`Zk=4fg$|#Na%Ds)$E=<15v+A;$jH|_4uedvA z2My+xk>i0zKr#Wfr0+;*1hE$2>tsoyM&nyjt)c%9KCe3*Qn07A;C#zir5V z(<02?^dEsT=6r3h1lMF4p~~_0BAws+UR<)b7mp?9R!yqXc+8#N*E0G&I}N!O&F<_? z8*}@De;-Lf?LyuqiTc|YJ9;qkmUnXpjRi8Gzx(9Lpwny7-ZlQKyBoTf`(B%4`?u)y zgL?fW+v`pU{I+7PxA{LMu6A|3gI++U`_id|-d~JC&by`x-_g_4r4>zY4trI+r_GCh zIn)FD^X{(0!<4#ATXmV;pX&%qXL~#a$<#ey!zypBaKfP7p9Ii4KR(8dE@PB+@VF!W>sQER;;D1ThupW7nT8tzi z{G|l4Qjhk4fj@2WLk%Cd8&}mlv70oFb+fi&K5g8h(lGs8wMb{NTRNZmy{mVXWaIE;S1S@lA2rozt#aHtf%+V zmoun>t;7G!E~G<$F(PI?C+{A_=7=0_s3fJ(wR;KpA1rtFuKp+>m_;V>KR>KZuk^wWrHK^mpejKaGQH5 zB{UlLzBmO(!5~lVC6P6C^56>K`kI)CC!yMtD<`md`hnpLL)76S8~thI5!CiK?sEsGd3H1k8XBtlN;$1Ih&AZgY=Z>}13e;Z@+aIjF=gHO zVF`9tvI_P%+1W1TdLH!>-(^Q-!l8tHu*R>j2x+n$K-QxluFk#^rH42cg zm$?kOwx;c}BjWb(n2wl@T27?jBkk-dgzCVhQ_-@I+1jf!9yi6J(L|94#AL%qO?@OiIP#h)g0EqNB&(A+tq< zovK!x|#fX(^|yRK|XBH=}1RPg`2bQzya=ob|V z7IfWC4!wD6Cy`^zI7gCU*dEU~)45P?@dzf`_xo29K2G)IXegYbyJw=4_~#cx)Rk>2o~|4$UI-qI1?E*F(tE&gWwn_expCb$0`C5;Rfc!kk6r|m!0BEa4=e3#xMTS7NTeC(J;iw%ZL&x=2I|i)wJXh@)E|+REFms3$dcA;K z8;(19;--cV+Ww>B%fGJ7=9*NDaz4LZUN*u^0=#!irIjO3m`eMwbPkgteT=SRef>UG~!>$D@8NH3>O0q z$SqvXomPi>Z+*D1NkDFZly^b@#Ghb4wlh(CJ8;?7(hq8mX%xqv82SY(RgFMm*_08s zo1)oH@+zDz+3p;qaScuC)Y)fIwza-&d()K|6)&Byw{$i@V$pjC18+A4cNTwgCU+hd z;W&J9^9dN2PZ<7#;{879Xq{R28%jMu7acbS>;yV?{Ao({jv2ftk4y@nyxWm|J9NnGIBaX~$^aDtB@k0kiJStebV zQ`9?nbJZ9oneUK_3S*+V%K}B6l4o-ZVoB6O#OqLe1-wi&K%<)0*tbkXPb)Kxb+aPo z@qE&hOUFD)GHft+#sB(##+Sckpw=(7;&RwDp36!O5Lx1K(i297M0| zlJ^+po<<6#HQnm=0}M4^gweq?nqZ>nD+DP60Ut4~j%(1ieKqL)dl_r2*q1^u%>RGK z>J`hOynOR-|7jR$DfE?e-nM?g`q_ljj`(aX9NfcT4|U6y@P3#UNV!*s4EG1H{fc)t z$M6ScDSgC~eIZ0r<;2Rs^XYTZsY+g0PDzypk~yC42jzrDMdg&N;C!lRg>YrbjX4`r zLK58Zi!e>z%JUsshv8bc@=5eq3o7bp5r>1+@TOgkjnVz$I3wnCuc}2G39K#}n!+3$ zv$am&;#TO8zFeAn{KydV?DHu7ceV^uFLZu4=zQ3F(tQYdm3_cnPH4ZO!WV!#63Q+5 zk$XlYS7S1wvZuav7vhPS!Jm~&#r&Ba_=FsR zcC|!pem^DQ08~J$zks&r6U~EB4evjJnqf~-mo^y+W}fX13)MvE^M`VpS_>2R>8DoX z?Sw|}k3zhi>{L#bJG2=vf@NioO;1b7%PXb%#K+WQL(gl&auo;Py=q79C7&yA5wloy zmsL3ix{(p`xydaS_QJx=IR}iJ?Kc^zOJ;>N5g`!oY+yOp+GcmfU_+yMvK-it4;j$P zh)6P|uvO}xZguDzbubqB)~U1BpDofmI`#Ds{J*elL$8~FixKki+SFmZoE8Pw>O*G@ z+LjJulkO-U%R=LNVGg}Obo$TLq5;fzT3uDtN)ggoa9XY`%@j$9QoGw8e-!vnE& z)Hm|A<8S195+P`GeNlfcKhY8Ak{xBfW^j9whaagCoQ_gj?p%mxUiFZ-0L6o#i_Ik7ap*B!IIq5jZPe$+>4x>o_cD#tq+|n&(^HGeqJr z?Xj?yB@+>P9R8RUzb!I_P3@l>dp;MEk>lX&5>yNgD%ia<4xfZ^d!g zVL6)n|2U5S$8r2UavW=J{tx0LJ^}~R-J{=t_xP1NVl7+oq?076x^ZFV|Q86A{$kDU`r?QbMXhw9?+M83H!f8n3an5}7iw!+iKH1L>L#LdhqvC6) zR^q07$VKGhJJ!rscU;9iOc!A)C-F*0;kJVPO6@)!2fMElWV?c{Z{52y8xduj(FJWt zf`EBye|6|kbFtBG=V_y)m>n^7NErey&Lx^+4={?Ix4wC1w zf36w}^y^v_hx=(c_Ye&9ER*jelbxB1?uWo%DU^haNv*?p>>ajfgWJQ|pEUxKfeznR z%SVHUN0QjHL{=IXPLc%8AV5=4wudTfJmo-85#2Wb0ec*@Nu=gOdlvZ1 zEqMXY#%5851Xew(Ox3pJ^n5t1l;nUj^1*qNplSqP^Ma89OpF&0}!Tb@^9@@C&HYAHE#!eAvXrosM>0)5n?s-`phpKodQP;zGh zv4FCkHk9a$;l^6VfG@@On#4f4L)Fr_b4{suInx=z5^^4SMFwEg8aIkjY7N7>{NB`u zH>ls-zhYyjC&zbSvxT-jYvyUkx*4*UbdxJH?TH+llOWVdA|QWTu8b~Z>`qR`v!PY4 z&IO}thDFEIJb*{?m+^YuD8u?F#wgD@#k`T_E$VxjqqKx#gCvP5eQkdYg@iA9L>Yg( z=%@Pw?duFp&N|!Q6I0(ELuDbq7R%I8AEdZ-Onw2AVIx{=HVJu6fXxhL8;uG99GW~Yz5_FpY+O!6aUQDo^ zV+3co_b7<5q zDQ;Y;{LI(G;%0D9V-yx~ZpFm)&2_ic;#)rri+6F8g&_fto)tggKko+jeBe+TIa>E2 zR|uavycrg^!{XgKy0xGS0KYYsNhJ~qib~IoH84w|c&Ft}*X*PQyXR$e(wmGX|DoOfk&1>)h zqHldu&bt{A-=i!Cl6xKVA6l^7SWnYE^pHDIQn&<2A|cbnTxLcPNy?xFX+=(=I$H}8R6LI>>}9Zll;opa_7b6=S(BQh z0_8fvSr2&j50c|UiT9{8p+gq-CY#wo!AP*)BafKl^&jg&HNrdBd2ABkQLp6`KboTr z2Gl9^o7GVT_aa&ULpa5v54}t;MI0LBH&4n+#=AAkQ6@u7+tEjQEmuk|ve21eXHi}* zLg1_xXO;CfchHKG7I|*Yaec<1&+z(e6S#bP#S#V|1Ja#V`PB>vhx8PdN`PtY?PrRhVZrUSBvs0eMA$s6#M z$SmjLj|Cz6`+py*%_TUDy!^KBy<(4u+RNbW|)R7s5lh;qjY zkWXbVb&q}55-%3Ce_bwS{ob2ORtxbqz3eCme^?SW4Pq_7!R#2Rp`m*{YzGcL~zoQL;DG8PPAr6JwVY zE(bt48}ERw-QWnA?a+c|xyWtOZ&tU&6DbpIE?`#n%sR*um56 z?_lq`=YiG!z2+q-b60JXG#Em~_7gu%)n7#p3cm}O{5RD+O515lGxMbp@dq6n$`ux9 z{Y6E22xO|13T(tnCJQbI-LNdp)D&&2VQ_{FMR`}xDP3?e zX(Uj-1wKc4Q?-`5_{hrOJMvP|fnZYjLjAt}xr(`FZe`rOkQEKB!gPwqm zlDwk4sSfoS%)cQ}kamj`f-y6KP;$5i7;1fKLqu7c#>tv`{=Z1+c7kY-SS8 zulVY)6PY<>G(pG%lMx2ll_ z%kVH4YT_ORj~y(5?p(Of+|F`x(vUhglmRAT6d8o6sOn%ps_{L!K+g!Lz`x_)Is=&6 z1G)iRp$E$)`S{ALo1@h;%;IPCz3_gZse8eyioUHn#McN1O876H#pr6Go>DKeX{qdN z3PwZ}h97*FXOO;Rr1w9oFZ!iQV2KH6F;P64%7b_jNOt%$&Pb7Ir|d7uKM1YD=L1#VJOJ(NXwokIEIo=A1NS%=~x z#-Bqo@2YCvlKAk>>Y(fu+AbLax;PdJuTsH5;z9a(0IxhQUN)xdk7v#Dv1k?8E%VC-su9G3f;a0RqP2zQ#UUjn6yAbbL?xPN_+}2F9z> z`qf2o!d*CFK(rI$olXY1c6rEqVuN!CEuti8F9~c6ZiHA-7+7mC57rnf-xaBS+hR_sqA|%QcCk;2YIzpk#K-B@ z(|4e~q3N@Bo7;m(r_1bv;N^#1u9+Lh-*VJGpQz}VhDpo2#d?JuqjJ5)DTVigLSmBE zIpdV8nR_`7o-3AxxB-{1@mSuNt?Ntks(kw$1yvD8q-*53BVX5X!~qq3X^t&YkCP?{ zDwWg}&F<=ee7yja;SPe$wEB8|!Mw(2TFlAtH4E$3z5)31#Y6?TUz`JLqQdTEf?EW^ygD z`#K3vlu`SXFyUt{TYMuGt!syEv!YrTbfHo47JCfk9-r5XcKO(YfJK7DC zH`c)hL*XIC4u3it9rL}1l>G3oBxX1qgqs~3;Q z&0}c%rQ}%&b^zA1z;_;~%jDXrWoG*RTZ@b2FRk$E7)7>OIyz>zv^M#hY1T-~V&(!G zsUaz!(l+HE<{D{BGGIB+nr1TQvwm8gL#Q`|fMGK&p;8&Ap85WgRRe(xgxp>TIO{=IW_T>d4SD=)X|4_&jAJ2=F6elB;o-n?HOh1nXn*a4>5v|RfS)c?ROz||a*M_{YqH7sv6G;1*XFj2 zX-bzgQ_?Mz#m!RTL^kuIWzMtfvk-|Nt^1T}s7`)u6&5L>Pfp6I zErE&XWhbqv@6Q7wDBvLG#Mh8Ih;yMx-ej#-4ZH<9Qm~+_SD+AVb*>|&7euf0$ydQ5 zNYZ9a2T5Bq8UMMAim_@Zd}g*etAIiy-WL|Y5pJ*LQY3oAD%4Zv>&@n&SP;W`HW!pE z*xcHu(D2LxGoC4XzNo67$s5N?4^kEvGYb+UUF8kaTO%H~4PCV-Qoeul%k5x~Ov@+C z+_RERg{wKkmQu8GuP2EsZx9H(*a<`PUtTjyH zivzREYVFC0jf1cD_G3mcQm0~9(gwMzNmTJC9~(BJ`=au?s{N6c+Dh25G^?XN6_1X& zhL&J*OqBSl4o$0Oh#TL$YcJjU-YZvM4b+#z_*&36LUccZIj$+o4&y4o^9S4WiZdu_ z?;1%6Q49CHY?{PM3X$IIB{HgtDr{6+(Piq`!qQJ4gK0iqj#lsZZ%XXnT?t1 zZ)F!OWDp({qHa4=X!(fTLDLR{Rx&7f#6-2BE9}PXm~C`D3$_XOg_h{v39B;UE}j)? z2k*y@y;;+X-9>fr-$5<}tAln_npea&5^_X-jZ-5SRHo+2T)M6yOF?_6{#Fz&&*{Q) zMnuum2~8K>CsN1pw4s*`=U1UdpW8p!Y_Mm@MOqIR&f#kS9@qqKdOf@}iip z_}e)8*u>?Gf0oT=ierDwA)r3#!WsjZk2MkRX_xOwQqL#wE9dScf0lwB@Pfo4Fh#%*xESvf3#TU(T*?^w+Z40#fnt6{5x53j>D%;EKYN+|v z?ca6KcwDkspcA@F&&}{hmxdIXf3Ek}9#>B`KT}`s4Wr3$Z;HpADGNIe!}DUd*il{G z^M4OI4*Gq7cIE(gjry8LeffY-g8JO z&)daW&)jG|{<3*wyO7xv`F6(K&=cRxhqMy*hR4_&y1{n9hQ?beUWi}=bzzAnjFahr zM6VFN?+rSS8VN@Cw_t8pr|8SJ1yN*Wsu%AjtMLVLUBDzHj<;mkMCkPeoFD;i(I6mC zVAbFu=-pmOt`%ZwyBy4w_NF!UBcd(xgx5z zE@!>LhtGBU>nB^y^?jh49kO{|Y0LX6m2+DO#e~w0?J(O|IY{Ozgm%aQvxT==;8IvEzTYP1!FLNDS8$C zq!K|xM~O1Xebn22YbM@lxTl{%_9B`U>SKEvPzQ5o`N~`q!mydcf$u}Xu@KfRO(qy> z$nQqOHs+3DkAD7^AEf&riNYy4YO0FRYuFCpKEm^W^9r z!h>j+J@P1=mb3DL;ep!`xe&Ux`AcFLvMSZiIZzGx-f^$ITeH|B@0p*@iFlu*XHIXi zW+U2Qn8rtxQJ#2K#nKM*DT-SMZ}a+{x`(>jw#1BpWGxa?&E#WIT}-xF4p+hW>jp8h zAMQ{};D3*@oCxctdiZ3wPJ<+kt~|ijuh_Xar$vMp$nz@e=*~S094)kWdSl}u#~3Lc z)kDS8oert-vAEYz3R^Ij=QStb=FbcX_zgHfB{7rmKQTiXbv5=D3lEo)OTtJ+}et4SCy`+jvZT^QCy_v_YUW zhmuUYhP1Qwfvb79l_NZ*Bx~c50Yz9Gd(4m!k4(%1c*W3L=K(y)U6;kE3q>2^<1%7RR$xyeJ9KcefuWM7WkrAzFwNL+}(HC zMsGK1^rpArhWMuZ$x!HOM#ezv|1dVEVPEh8kh;?v;Q=Us6HduCXQkmw3p^(Nz#QNKq$y~G1$Aaz1TI>U zffNPdTuMkPhPh?z67SYQG=ym$(F0Q^z`mKCHv}wTI5p)aX1MUH8lOJBb#S?(@N7iF z6RF8IIT7p-8=*pdkeM;?nh#$ z9xA&#pFnmiCZ7TAIE)-=3wTe)S%fkbb)~~6!(#U+tacZH3#dx~#vJ+;hpxwA?ZI^s zrL+wz?eRau$RfQb{|+NJhduds7{P|_*#Ld_+`0AdMPB{iHp~Y14}lfvin)9N0~N?8 zQAIBpxeIi|diyK0I$#$h1qAt6aqtB)$};jFN%%JVY8!g**r=Pgg82>B60W?~O`m7?s#+RC2t+pu2B)YeAAn8hc&k}p6)MWJ<%+yq72FIde`*G_?9+YBbj^ZZ z(H-iCi#vQ^dkf@ILs*UQ_P$4FVYD>#qv{z!>i{%%vFzQUBf;Cz(yT#H1Wn-*eoybu z4RgC!ya)V?ROV`U-m@>FC?x@yQDHdu2W6-b7Q^O}WQOk;3Q^ri2nglOtrqmC-@_0< z>wc6`4hw+=U~e8D5Mixnwy7QaKx+QOjxS4_=2?*-cc?IvAqU!#u%eQiD`JUiU8EAh zeb1=GVd%mJ4W2l+Azm-^7wS_rGJw!nMd2LIl37VZ)l5l}A%ao+SCMRD(Ou;G3f={6 zq%S#@P)d}o7ZOd4`Hy!?vWwGGyDj4qYzB#H_QvYDy=Sbeixw(fNbw#{HG8;bBf zg~e6Z^0)2-FGD>`%uzSrGD5WY6T@mdSZB}X#B<@&AQxD=+A-T@&~@DMO91)cREh^+h2t+uq6 zEz6in^W=Pr%-y)O4*$cgBy-HPyyXh4L!)>wym1G>T9{lQgJ@l^%X-PccOlAF9tbPJ z-r~mBpl@k3qo8oSE4!CjZA-y(3QSI4+zJCS=MLox}l`r;;lWIx$4jd!CkGZ zfp5(FFUmz0$jT7BMcIvF#jpSV5U{K=FM+^1x~CKi*8ed$SAK*48l)>8)Q3S`y`C@Y zY5j94*!(TjVUWTn$S^8+cs4NwDdtR_+*u{Fz!HZ2KNP!9o=jVnckw~AU_+n>_6_4Vu-jVLECvA}T8x=czcN90MnQR;0b@OUW>{aIEZy03%Y8=5t znMX4oG_UO;*NicYZeFjJfx~H9j>`DJd9d}SZT}!H8I+or0J%G>2 zvMLcI>9`+PjzF6YwP>zq4kwQskzZ0e#R()lm>68G_zD?K&`1K*#xDHhw`^izVg8_UalrBOMLMk;=byhtu*$W)`uL4*vD{&B3>S{POtC zm(ckB`{B*)&qq*t96s6p=cC6zG70PdczF2am))Zuj|R`71(uozKfHatzP6*bs@BWb z-yFR9wzJB(L1OXvivhS(EMF>SW1<+1Ne0a6Uc{&04!G%l6?(}8k+vOK;)1m24+efB zpB;|uViiaDhO~-EL-hcBDACk+jKdOt_;?UE3V>hnOF&kMC57V?>Z(OB_JC6a-;8_a z^OxdOSF@WnI{;X5yDX4bT89%Pa54Ab4!WRYyuk^J&X5;>o91HXlw+?8Y z@HLaDEoT{ zH~L#c7X1zJiZ%i(iqbGyCzW!>{$e~Kyb-kezvwGpl@Jc=sxBSi$G~Q@hw@|rw{O9g zs2SyA+hq=5e3_38gEZF|3L3R2)FwR?z9&IbcrdokJOa@PQNYd|%T!b|^Wcn(Cwl~7 z>hVh*Qa+3+@9Xhi0aA81@VRN>o>cKYPn(iFRR%2AHRt4*Bst`lm39+seGh!jd2D*} z&$93!Q#r~An0Wm!mRgWwM0>fYXYV|j2*jWmy6}}%bSuLPj-`}`!Mi^<7x?*ilU1AQ zFJ6r;UxDF2t;fDF<$GH8adE&yTj`zx@d0gMS$7lV6n8FMAkO0Bc(T7&T%G*Us3Wb< z8uXp{gaD`K0l2}wrUdee>2QDAQNVNp+~<{ZM>%nOz_gzXwNMnDk`>6uCe%^T@|_KrGcNPrGQx;d-ne4n9C z)N8Kg*NrtgPlhxjgV9fo5mkG0()Pff?);4kVdP3`O z-vt!5-Toi35jEV#pPIEep5^4h`ZIlOf>C2YE zMwCzW7?h;gOF^~Ga0k(K){UIO^XH*6f;mlrGd2g!xT-q=Gkz^t#>WK8*w6#{d+`Lg zuE&NHnG5T(sxL0d!v$Q?ornpJbXTcm?rPZRC$k=Xc!( zArvvdR+xrqBkpIhK-k&SOo+3B!yeQumW8cPaSr2p%0N)tW;$~XgEMPQo{$cB2LfGFlY3|m!#+I zi2Nj}nvnDqEINnRg4G4z0oI|^GFQQx<1B;`Urzw0Zak$!2+X88duKC0l@P&9_p2Gf z3}wtXs{5j+CLLZfs=UeXZnM>Q`td=UetGi+<&Jc0zO67T-o*t6m)f+2=GIzXS7sUz z3n|A0of&EG=_guUh~ZQK-fFb-XXd#Ueh#g9_- zP~H~Fh<3#OrzdIIeBE5Q5)6@qX=i*f`eIM`NPg5@WU%MX7tM5CJEl_52)COe!EtX8 z8^aBD*z6ybl%mS~GZG(-&N$|$FFi&)$7J)AfBq6TKxc^ZD>;{t$leJHU_(l{0rJVC zYs=aPmO+$R9L7nioQsNzw#BR7l=c<1&*{iV>w%v9q4FsUI8%Y)J>hR%zA#K(>!!0B!@yurMI&9T!RWATIt zp7V&_?=HEkOYR);f5q9*keTsB*QE=c$p`iWX+{5Bo`{WDf{n4bMU zW6AkAn_#^`+z$s#HEnJ?3b~*Dk)cK)KmaAy@zJ9V1~=g4SpY|DxBAEK&e?tHW`Y*W zRKBO4z;Rm!Ul=OhMYxUUPy{XNa@oBUnOSw-^+Qg?X11#t(V*DjpI?8w`(({TcQ&{i zHn_VLEP-_+W;l-! zwU+{lxWH_)GhSH6^qb0D;p2X<^c{qHNBZ)DzS|p+|BB}nAPWyIUC12xwO`_)|GJNK z5bcF>h07N1`)a+h^h+!pVSwi3@MK6cxr`^E74+$pxYFaJoGdL^Y$PFDk>f~=SW>0WS zovxPm>>cbI!(y1yBhy!c#5b~GwYYWNbBw+q=f`5sBRd<6ul&{tO4~++^E2h;q>p*b z?NScym%0w&uQ=r!eIpe8W3z0a;cg}qN+n6&8&2pJoF81B?9(erYaql_oZ?bJemGmX zzz9HHW5$Pe+;%F*V@LNBW@uNq}|0f(^-y*MY0`0IKRC_=jnVj|RQdI)my?Uf~aNVFMUC@$ZF(lYT7RjwE zlve@L{wN5iOtuI%6bHvJRYD~5bUhh5XuU=sVLG^ zpxEFaXAMQ5l;~k#cUSWf>>tAdjsZ)?8CdcRRABlHQ>LW%N!0oqItLH=K#bPvJQ`$+ z4m_~q;n5(;HFmGW5bWNrfaR_wC~0lOzsx%T6<_G55A9?r^=HjFj1ZUWIc0pwUW8VM{S+K2DLMv zwUrL#q$U>r*b6f-Lq%*&Y@kodhT76{r zdn7INb18}5e zu2U%083;Wo!uX6?nbcVt5zfh3GIFJy-nneshEP)M6Na|Vqdz|Cv&%|U(>=3w2lr;W zSTj@M{!PYyF?l-9!)a~Yrb7FCC`hDQ=3py?$$;1uvL1AML_NXNtJQE`*h4>BKUtK=>cD#i|-9c*xx%xepS&;GCTrfuuf38AGJ zNq{Dq{Lm-76#5f)=UM8WrDCW2CCNRqkkO3R#5-%h%sL5VXv~ zdr=R6a1dl4i1I~dtJW}sq+4;IX70ws;CiitgHOA`)AL*PW8w~t?Z>wSuy}Z6CY%4f zD+ERNh#Iz$5{*Q+5@G>AYh93@Afk|K1vH@)j0Wrcv|e&h-{ah|^QhQ7{$Vg1!{S&F zv;Pi2&%hX@@MLOiMh;THLTt25O+68&s{W~i8zcBB{k?TyW0|VK2R8yLlSO~#XTGTx zXBFkqd}a3G8OtVa6;)NqiTk~WI-}oIJOkaPhZVu7tJc1f2vyy90FB0F1=1>uvX=RF zxDfkEL;kC(;d>C-3|ZW17T!;=&0)0ZQ90P<;H&omxMdp((vS?oQ{$d1xcX=Y?uUho z;Mf%KTG;t605*%Tf&u5TQB4_yL zW+8WU7y4iX_Z36e1xQ2dp`(I18HRiE0kCqx0N268jT<`cgbx>L4Z=-lq0%y2_(3RY z!3Tone>enQZvT836kgcUy^wezrC0-v7qhrXEj1Zy0Gf#v}#?BlS4?8J*-_G*_VjX@U6e z1L-44fiN6qx$F5TF%pV$#e#LRPDI`4S_T_6F1J*Uu`_+Hk5SoF)eO-nzp z5HygIJH zSs-s{nd}N#2tHfpdI9KE3NeZ>mqM9UL+>QoB^#EFR$1Fj4meO5U#uWrX`;8b-3V&R z?z(-gCDD}i)OM(Hm(6uMuisVd#YcI_RT_t;b&x-j1(C3A+W718*&j!~y?^li zm+NEw063q|FGR9Y;okb`3{c;A^nBgyr8RAt3M=!sG`0|bi)^K7IX`V~WXwF! zUw&?y=`W|8J?NKt4Of|++??{(Gxe~!)RN!?tlf<*xpDrYTtNn#*H-h;`OPf|3E07i zP$iuLYmXci*n@y1huz3bfP|m1?Zc34QFU#R|1{$~q@;m!@AzGJvgJhB&5ko0Oe+cO zP($Gtrpb5~On*MXlhaA{i9Ls+2?9efYt2Zf1QtaX7Xd=iMKqFH$+7zPU+Ddhqk)w8 zhX)dtu_0tLylj6w{Biu*k4Ha_yy<~DBCX1$)k?#oI+yP0P7ejcvJL8;;j7%WRgFqs>n#P6>MkhLJs1^n#LvriND% z7dJ;#8nzMzIuZaP?TXAMaI$3CHZ!uim`)nPx0;M4cb@t7iQcUz&Mt8P|7hHlwWz*G zoTs!H=DxRzH@VST-Xm_9pP+-Eu#dIg8@wxIN37WZsP1SK;dY@7+@wO|tzf0^(tZs+ zyiXwwIUxQ-`YGKXo?Vv5d^O|Uf-$LwY_9Ug;FRO5V!dPgW%MD zD777h8pQT)(&disj(sk+dQF$d>$^LauHM@}J_!?sF+sXC;y@%G3vTsvm<$(!T!@H@ zhCy18Gl~-JiZKLWelFv^wY5F+$qDNt%I~dykEm16&D<1{zOD`@39y6gCVjO#EUupF zBX_m?s(tNJo`g1pt6in9MnQ5L0$^DXO67{d-%6T;ub8cnv4-@wLKiiu_35X%UOXK@ z^_bS+-k3U3cb+PzG;*r@U*Es}cEsRW^*QyI)-rzlbdcF_SG!d9>S=`e4YK;ft2c6v z+CFbLYc*{q&Uojm&P@wGA6Uvfd!aqI(7FPgzhZFRUv66J}^%C2=F>_OQh`$@%msbKB zGiC35{D6s>f8rWVNN40_OB0sT;$YVE_e(;~r7c+h(wi+&p@wAJRI-0QduE%;Iyt(v zbYOQp*^+KA<65L@IM^%QN@UxK#zyGL9iHgO#Fwqa4O8vhlzF@^p+GjZyX)3qjjNPx zU*B|?K(Jkc(u+UkM&2&|dElXU*kO2`PgU+DunJkV|Ah0$xlimq**pl?%_}*H#u#bq z_aP=+fH^z9=Rx8&cIC=BCU?XJfFywr-y_(@aQW|2+|>()b7;*aXpVs8M;#( z-pd@Y2#z|tczWZKW-zLpPeMTcXaeMt_ zD4CsWk5Jr-;K~K|A|F010(;!{TBfTICZsSe+dI?cA&J52J6k>u(BhN#iOq=UTarm&_M7#5lKij>`oHMVrv<@?|rp>f~xIw02dG zv%$4wa_RuQ$53%L*2{W|f6ZU$+|2L0(OW>QnrU^((;@N>W%|1;Z9Yzxk<1LM&y7Z- zZdjrVGmkCTC8u7=`6on<_bZL4Js~}uO%k{cBVp~%3D|{2Rld9LvenaU_nR8sA>d^x1D8mph*~ZJzpR6 zwMIJ0EAvxIoE1>?JCaViCT~MIL^agKSKlj=_%=GEl6wP3U1znX#w(AyV3m|!(NQ~9 zksykJYiTnXh+ncVv3{&}sqgKJm%1W=W!F^l<^2s)C3$Hs73Ml7Q%hp%PU#Tme!WPy zMy#_l*d^F)F5LYJ0q;TLk=uZqh(_zhqmANUx=zKW;u68!Tjqf{8oXE~Y02(#QNG=~ z<1R@Fu6;!)i}e9H=veQSDM40XaeD@0S8C5q6Lq1k$ z5&ZwSK0hZOW^6jRnAWpOxWQRHHM>N9;hktNR@`H>BRcfnr7R%p$KN(U_|2_o&>N_f?sB>tp92gc;LIQiP!zO zYR?w+sr^Pn*;Xd*6EZN&_0V@z@>sOX)%m%@X(lNPrvNaguoKSELo)^Scx9%(vtMSE zM#^%H^Uu|ShtehROE6iK}-*|G<05YarzGnTbCbh@4nMV3D=fRHZ!CNp zd2X9$*T}$9wmxnoiA*q)#e*U5L)}O9J5-U=o$37&?Hmea>Rzl4FX?RlcQla2eBw0l zV>fHsWfAPw=})8drAvB;*E;K+x|B>0r2zj33-FeZ0{o*h2@2PL;le8-zc~toZC6t< z<}z3msd`G=kCK3Th7v_gXF#HV>Lme^$LK$ZTgMGW?vj+CEQE`)BqspkQ3oiP;R&)U z@^pl0^C)ftTM{Q6{!if?sW;sWkYOpXOI~m}+AP8L2v@{UBJMfvR^N57t21gnOaa*$ zW7*R>{i!~>Gi>_4ln-4Z-S&|`2q>(hm9K5k$!HQp=g~D18>i%JL15n+6IGa87nT^H|tmQxqZ>Y;QDl2%ec6$425aYOGcUDV0zb7M}&jqFy$zg7p4Mx z5#7aEP7HNgOMRnqYokML*?Kx%EhVHos7I^nq^dy1*^iZFGsXl@n((ipk;DN?nAxv= zs8b}b28|AVmxt=xgJcRYFK=A`qtK4%{eINDAZ81llYVhI6yN~M04cNbwhuP94>$BG%islm%nW?myNTqC_MEzxsPe)!}58U zig$s{Ck^c$tP2`T@L|%py*Ntmqekjkcs+;h!H!=3>o%g7E^FI(4Tc7i5+2yRv*r+e8lEp&v`l{cR~#R#Z!4^xa2%w~9ZSBRrPdbV~+XpSXx0DM9D z;B%w}5d-ZoiOm%L(;9Vw!UemZYbUa>Wv?sHfJY(w2H%R{FrmftFyU#!^z$+x`i%E& zX7V@55*`IZ28)&rm5SrUsT+QjP~^XH!l@oy60I3CaeUFDLZM(P`AxF)cX1-44nz4|FgUG^IC3YSD zD(yv2;K498L6>!j@56Vx0W>X@x zIe=6Ch}|0^{*gksf0Hooj}prLal^SkB&7Sk>K_)l{gI-#e^mr`p2}x}xT7umkYMge zO}IV|`eVdGr>^xM5D`t>XGcKvxHJ4&Wo_Ot+Sw;>|ELnOQi#KJL?qyAc4G@19;|O0 zNoxK(F5$A%6Gwbq)M&JXhR- z_KIeGg#c>Oy_$J`0A`flxo_6ubr2g7tS^VpiOX4VBOfSM9fiqsqk2m=^V%f3`V6pH z{@9R&)ub9JCf+On!GwWLj^^1uPu0zL@d7zDmYQR1!q;gCAMpcTCLeuk*3pG*41DsTx zSlv3W1*HNHp@?ELtu$iTH!%C6f)n_8S6D=;q}8M;JH|un$pNs+yn#JalwDdw5usYX zG+TksmiCVdh9rk$Xr%3-+d5rHyAFEMDo{KSV+y^fWStD~BC<9GVfx$36Xn3O&*dgWw7>2Tr8*2~>Jf)o{#`d_K3)dtUmE-Fr7Bf=O!)cjhq zscB@KI6NVeN~rEmHy%xhCY28+T~XVATiTVONV$X6vGCEM`uO;zANLq-bc#Oo#KhC~ z!Ut%YSyR8vfU5=VkRe_wIoz zGqZtuT!Mf4_jF9jP1F|gPvTQ1D!P8mdN?dym7Qc{9|nAe?TuM(``&5vd-P!z);kic zgP(^e@WZS&5>sCjnnP*&XsCu1NgcfAIW}bzJa|10H)2YyGHYgObWyk=Kl`jR)9(WI z5yf$xMd>We=V)ER^W!Hv$C{<5;l2)>nSETfbva{9?Hu%ctr>2aoB@aast! zG4bs6yMXMfPzp_2Bcok=*({dZXZ7N2RWFOv5~c~*E}UzLq3bOyrtS$~M3Wv1~QBBaddj13&M*oqug*b!FuKJv>Hd$x0!U02Sc5BQA_SD>?U z*$DdS{y9vpm2zkzT<`62=rdP;C^d{Ua!knum1I$ zFZYlK6l$m^>?esoDF-JkW}JUh?8{&V$im&PZV(Ygu7np=Q`n4|gT$$5HKU~BoYoK_ zO)g@FLHuDFq7;R+FU*Gt!kgXDASFcMe|q+dYpQ)|@y|zk?)-V9kqu(;=O>299S_#_q)LPisj*5*22S_T*{(Itq453}Pt!W_R^(tfZn z#w-EH0U3%81d=;~n(e-Xht1QM;@~zE4>n4nT5?hrw&0U}NZio%db;sqr^;w@6og3H zzhHM;F5_4t3ifjzlxFa+X7SZz+c?T*BDGb5TdJck zgFt6OY=X`jC(T59Gs2##XJ1!XKuS(-e#xSUYSAKoSc`PpO+ztt1Le&_8bFL2cK{b5 zH`Ngfm&%3eIun^MZ*h$#GM~m~t?4+k_#cLykoX)Zy)I8gr@1^+z=W80zA($G z)81&2B5oL}FN$6{hrhIu^A@#@ThKzk+KJgjbh0DdR=~EPL$TJ2B0lD9?5qb5#_|we zf(};}JxQ+9#78IHn|5Q$u9T(E5zmk^XGr%Zmz`Y(V$cPcfSk9N*NsGEappRpRd}tJ z-A(QuPkhWmpKxijpdDm8|2B?NZkoZ;(GM z6IyC|?N9=8eswMT-^IgU+e4D~=F3EBFnBoeyz9ub=vExJ!n!5DXIAbuxVtymUBsxL zH@t_23})GyufjK|oEc$iTi`i~@_*kX5P_%3(*$K5hbL7{IPHf9% zX|j`}tCAoBqA(@_Zh*AJIQs6l*Q2{vKLAo5XU;v9JE@9ApwaJMy`H}n7BZ(x9IMEM z*?MosFZGTy2ab)-^og6jH4>kdy=|9Noa9Hv_UK0K=<5d3^9?%O;_~V=+{vq%UPAV6 z+rx;D#+HkUUv{i3yx-)D8e3*pxY$_5#(CV%O?9IO@7C?yeOsi4);8}t&=d}k<-$z3 zDFx#WO&AEodGRCshcd7Z;XbC{sLMc0!fzBPA8Dhib$Gl?xJNG==b6Duz|J4lm=*aw z{)wR_Z;Qr)8l-?NH&uk5C&1LA!{IBPBbPL0B3WQoS?FP2?Ht`Q>kKV^18Nx zI;c|R0i!}{l>8NVC;EX>;+RK`|LyX)hDkn}j*=dB0D#SVW8E}fIUH0CX(Ji>{{o9% zXpD7)={42r&gpT06#_G^G63c0B26whh{(j@T#VeF`BTq*Jndm;p7eMRJ?)*Tz9zGx zS=!lz`9S=6J(phB?98GT(?luB_AB;D`9dzGkmzx&x3t)(?aM_al}KM!ORs%20RbZm z`UlOdUNUT4+w_2a0953>hml{duKbZ3%z2z<19dZ2=;&w52Wn9z}|b+nz3NO z6F{60@N(u3g-{Oi^gl>hoBEpeqXXMnk1!x!Tt=z}V08u(Ipra?y+*BhbodwtoCe`Z)!F zn8ot3nY-q3&jMEm4rpkn=0H|AsLTv^2i4vwn=Qt31u|`5I!z;`w5^*6RZC zm-8H!R9sPDHPp4z#OB!}6LzkB@eym6HLa#&NzoX1ju8ZDW5fc1r0%$WM{0)xsZbkx zQmsmZwW>x@lZHU6agp!prqar8H&RPyxv2<;X*5>Cok{lA@fPIG@LwsW z1AD2?cB`SNt-SUly^DI)i*3*^JPFYiUEDo0h{$b4^8IsfYjsF2h9i~bPxsFD@5#_Q zpNDvdofSnZ+2lC{V4?ajY?w21Xj-Ssl-N8|rs%yV$$t5D^JBhmN7j9Pn@?7FIe=>t zI;NyoONKnAlzO$DLI{Vx%<~cpE80-vxnfcRne}DJggsDr}u?HuY9s0|ddol1U95-e|!FXOEX506)6Kbz!lGfR4&TDT`!~&-w z|I(CER(tJZ>9pceikr5o+XARd0$~b2^D^N$=KtH}s45TP;;C1QWt*PDFSms`V#kj| zcS7#WUM+N$dDa6z__*7qSr@$7cr~sx%Nto1^Uhn2z^SlBCFOtLs+H1V?||#_MzA7n z{eh?&>kxIhMiIFK+*cEb6s6EiOJLljdN*z{s*5feMvb51Ln}wKnMFh+G^y^5JQ-_7So^4b9%S# ze)NNYuZ=M|`%ydNp>^^@_saPSFuC%w$&FYjsCTACMw0cXC>bH=Snjk;Co3EHLtKJc zF^BX7ksai5QKmWJ=h9MSC|C;pY|sjreY@1F)MB=Uw_|rn<9-<>77VuYu)SL7;@JKf zFX@hQ?EMbExr^gcP=t(6l6I3P9S8pe^9?pjtuQHxH5h4a6I{svMPD#!w~Um->vFABG|OmI;*OMMtTFqQV#bfDM(a*lrTsl=_75ARWhnG zkU?9;lmwE;)KJC|)%k4MM5d$H=vf#kVF$Bs^qRJsU5O`l;o){ux86%GskUjz2N`DK zX)AQIt;yV<2y$WrjW!UY$c+vzDyHU*EyCeip&XZW!fi0By8p*OaVF!{015Uk|qv9PWn5_u>uDHZT70q zoQXi+Kg`BOT$vy<>A>e2T+PDVIDMO5MsI3f9yqMH90#zxyN4y}b=RBL<_L4qX&FE} zj@7HOL~S}M9@NTwJa~gM&5J`R7eIORxkBYBO^aVSc}MdxM!Zo}0)U{wj0EPnfSH_@ z#Z@s$OVHTi*-$^09KC@lZgwJ+1Z!&+SPYFCl*1&wglPCb{EiYokpGOTL>>Hpp!ZBV zS;CuQzMA25hyEQ)UP>4tYL)v9t=2rz9XdqeUVhOP)(e0|Q*(T5D zs*!q0!V@}|F;TcJmg~SVL+j4W9{HGF#dgDGJ}pY43wv9Iy!TxvhYQ(7yw5{SrwA{t z_-Fs3Rik|YX-8|cUaU$062m05IyX_zP<+aja-CD$2c2nfA#}H+%PHIxWmescCXicT zX1YwwWo)wccOs`omI(5}#OB9Py$)$?vZ~>dA?89!myySUuD5u@X`KS&_OVt0V`Lhl zr)pC9rYsEm7ON?pOw<`Imx#*<;ymjemN&&DZzCryo$YrMdktsGY*lPNViIC=U0k71 z7s+%)S4@14j_pRm1TUaQQSDFirb*e25Y#96LQ&%B6xH+)FT~7f0Ii}??GOtVl3A;1 zt6_41G(Oqe`yCv9){K)=%ZdZh$`xXXLyJwBr=QSXv6@~->sKd$Tv$;Vs%YfWB<@9; zNM15zM!Q6X58P=JLVX`cd0Db7zNh)slZGs4@`L+(w5-@$Ymkh+{oaP!=B=M?Y=39F z+vpXT64eop$?g0BtldfVo^%MN$00W#mtQmkiGto*Z*Vv*xH)F|70&3$Wel<$4u*zv zou_3w&7;1fnr}k1W10-XV;}|mQ#+vOos&J~1$ zvV#uvr4|aafglGxXeWss-YPLsv_&;lPH`>*SSVNGv`2B+CBeG$8!u{Z!f_X1V649O z_41J`TGnrPkNMk5HDvM{*5uV&R%Dme9xABY#yo03bv17rp05ZqbRS+S#*1ymOsO@E zaZ`uj!;Z#+8i<`?(rGyPQVQ(P)sO&$eOj653oQrv)f$(GxCoVJhKZVxd6+I5&zn9VHHKhxZ2;KYoJRcs`siA zVoos)r_H&bxCs;{a7$WWn{3>6H+oDeGrrtUkMreWx9Is^BHv-+gowK)`3N&{;_;5@ zkn9bPxg&!e@O`+{UCMmspf_sqy;1Ps`t3-^%m<{#UuAbzDQ>{EZo|#RSx!M>r2IFm z@tvWoo4EUz`J9m?2uPq_784~m*1i&0q~YMUoc(JBEUA=br_HMvk9%Jmv^HoteYlTJ z+7S3rph@-&%}yI<_te>M*Tml0{?~eYZq-0n>Mdt@5;?`U3b$>;S4ES~FN^64G)utE zp@33LzdoxZs+GJ_d=@$)Mg#O(FeU-e;d)W0oMQEj&KT3-pXFuH23)5Ng~J@FMMK9v z8iw6b-N3Kr7`@jtoqk}40Eiq;D^VZm?xq~zA=y+UK9!S=l)2I;E|TcAXyC8NGGf-T32cy zHbPxrMbsALOA;f$515umP!^=7SI#}l<3zU58<`?fJJH*bYzS`W;z`_a zdW!bi_AlD^@)uz(!&>g*_LsY_ih-46V-Nv~_pR*lncc?j&Y!;*hTeE(r$rnv{!||| zb?Er`wH>ACd9tGbUNg-v)llH}Lxdp{|e(m0) zTJE#?%qDBSi*TO5)v0W~Cw|R+K|TES2hL%5{+ic>sKSwb6uHq@PuR(l(gLAnc3(45 z!WvBR+2ftAqq^x}UPXK^_M@&jp$`nQ9FoNUls4C=R$YdKDjMcx)4=MB3B1Txwcci^ zJ-$-RWHwG7f*ZP5Dfpy6j3p?bnduL<4Ag8{8d8LyeG^V@UZ9Ny?V-{D%31^r%QQDC zx;yC$6`gB#93y6ut>z0>w3362`m$Mof1sib#yHMa@rFl~XAS_8qJpY3Km)CvQzUu{ zY@%&su*GMt4SL3{I4cwzzSODHlxz%ch!IprvkkdKOZ_Mex@w9prMeR&>RXR|fAeb2 zC8C%?831|iKH1-R?`?C-Z9Y(&50$;<@s%81t6CNZQT&Rm@OObF3F`bl*@GUzpZ~eF zKMdaV1%XJuX_3Ev?L2Qk*RB4159G8frm|3@RRitJ{^E~{Qxnzgyu#f!5w>EIf+n`0 zSuyv???YeKxGUy{OhH~;4EiVBpqiMJ#W(POY(y&APzbOimdhEWWJZKKzhY7j z3MC(T(V-D?ytM`0CG(VOA`H5xgbp02O384LsNWdj=Qj^JZ3!lILPy=GHye7`46Q3U z2EDQ1O@V%xrw*zfi>N!|<9K9u6eDSB1_3?GL}_A_B7G#(dnZNsuFkpYW%YJrdUaXG zm7}V4eT-!!ikUFNwk~6uRP9V}SnX-PY@l5XXwqSa!Hk1_a;k2lyP#pi!rM{?XbJV7 z+VTw7He7(84a=q$nH^O|1@G4(>TlAYzy}n*t9D7n^Bw$l+C>=Q8@RP*oPglJ`==41CP<|1)D6 zGvwYh?fcDw8nw!EnEWz?tII~;otsqHVZftS$oQp!eee1J-m9HpRw|;z3^Xi3)-SUa zJ(eGLk4{0U&c}fhA}W#fuo>h&Q&G($g&vVpK@Fp|-&a|1!aC3&}iPXH8B z$MA9n#*2c5F%iZI{s|Cx-Ar=g)$xE*)qwZLMNL$rB3Y9qBgaBt)F-0^l8LZ;7vHJV zm7+|F%#e{@*LiPpo#@!(OC9qc^d!0|>2#F1cWpUsQlFQlQRh z2KyX6Q1jabK~Bc%>|N&In2w&o+OL}!^wBSxA1h7VGlo87c<{L#?PgKQtj;Vk<*7*tvmA-}=*~!{pMt1XI%3DM|g3{?fsYjxtIV z{Li4^lOS++&*m>Q_jk4MZ}d9<96rQ_;lt!H4_;m^qfLh*Xh&p4qN4N&)2L4hjIv0{&6w#^ zuT@j}K-l|2eagpt0ZeM9YD^o5<4{zdC06!lHeeE3*woHADC#4o1V*#(1b)~Yd`>dGc+kGCQP*d$Kb4WY64S1)Ji~Fcw;qKG#l*my`c>-b6?GIr|mzqH+Q(>!?swj%x2 z8%4rRJT~P)c;m^>imlnE7}R!9V{)=n%0~~~KXdDm1?a67xEcP3;y)CrLuHQc;n37R zG~F4R3iZ^BjDBt>``nzPC4GhSXtYiL+tj00S=X)~^;LBOxe3EUXisDwR4P7MpR8g&yJrJ;BXtk%F| zQtqddK(ZD$Bn5P81AS?sN?p{=xJYHUXLsSs|9g|65370#I+zH>po~@!V!8zNdjaAr zFblhkpomJ^W{If@eiQm7h|2=H4fJ3;Qi2?~F?I*mZ(>LGouY8iMM3-tj)`X$T#a;H z$rOY0miaA5{xG(SwU+4Mq zy2|Lo*PjtjJ@O^@8duNEurWbMj{u8b9(_Ebn);${$UvoD*UZZEMiGNxlM>=uzCSwA zc@ug59$-C00dVH|xx2F=jXZ4N$I0&3{Rf`{tO)cAkH$?^e;RA;HoDb2lY{;wkiqfS zv}v|((6ZRZn2a@hruJR-Y}DkCkfd*Js|}mjuEK|KX`>5>Rp@pOX0v_|&-AV-&>Nsz zQ2%sKiyZNdkb_{-AB>0!_j~HNg8$4IfeLmfO6XAE{s;&7dD=1JMXJseP_*YQ1CUdos@ir2biHN*Fh(+?35boutveWRvs^0yvA3v* z*MK>!bHD`nocCg*_Q+(jV_-r6CE-fRb=N|uW%(5x0*0K5#^fs@d&{IkFk%i21zG{f zY3hneQI!UOItdQ-F@OT_Sq8=be5G!R)j}c`vrXaX)zO$^4OZlfXUf1GLEXqiD#S{E zn%^4L08@mtTw|n?F2RWe)vYtBRE^-zwQ6#PLebz#1W5_84T>f$QXrINu#nT4JQUF- zmRn>xlRLz&A8nN{cV~uOinp=#esLOdcmKvtLIT)bwc*qjwuE}am+)DY<(Fyg908Be z2Nkr2mu}m(`-PNj;-D@ULrvXCR~(K_j?qks7W+lk%_VCdv?^zEzR= zpo0kg@^g{NT=#gZI)pyUuNt+mKpgsAaD;G_M$m|<_LLuu8g)F?+V1uSr%!_9$O9Q^ z=Jc2OG$l}FicjpDdIugb>ejp4&K_W?^l#_0vH`7nvj9&RAm<0_c@tYFn1$F1*#*XF z@>=K>fX#>RcF|t$El8xH$*frTEy4Guq4+Mq&$U0Wfj<2X3K8;n6U{5!yzsXy&g|%IJBTRPcJJ<3XBpMMBtQJW3NTbU0fjQimqYECQMPD@&eT9 zO=+2P01$zC3q>YwO3sQP!m-zoZw?r8?14mU#e`lOdJof^bJLNTplnY#9S!pc&=;6? z$V0^$s4{WYr@A%OnUvwW&*0_SDL zIFNJd0IyLLC1~c<#aAVW6txgnGbf5H(9W&#L{T52b*g1m&%q;ztnvuBR?TMVq9K2w z%A{>E3#UFtEHEX4lXGR#C)g*pLaGPPUJZ0{IiANnThJv$LtG+XLYKMaYwX&~>^Yj7 zac}Db{A%+aXuYtp*$o%=&oBn(M#W~o)rI|a)wf>Q?pe92zUG)*RU104_Ri}56`QZx zEB;4S&F^2a`KrBQJS(x9tvnvD+kV)gVq~>IhnmqU4q_60pjX*O{igxz+#3@osdLNZ`}UP_0(x#8g6wN)^93cg__@C$D>E2;Zp3*r;6+|Rl_U5wft$cE9C^%Vxd{`m7D zlmeHz@TvOdZusTlX!ICxYO4|qUXL;N@Wb&Ro(#PgFtO^Ac{u)$r~C??%hemy7@e0! zW%O|T#FPAAkI_{_z;?ZBXEk&&)Fn{8NwqWFM&u^c?I~=PM=7Lh(OSeR#V) zHj>X{uqizpKYj8n`quOiiu^fR1PuV^==7^Llq&c1X|x<^&eZ#7vLpEa>f!JIARmG> z^jPsD>0}M>{)F#pjOnZ4$sgd!<6JGkIT+t@cz=XP48RD#{{wy#fF-4d#-}g_Y~`E~ zjZ|fOPt3Dsa;42 zQgJAIcQ#ZNk57$a^E^{r(SM&06%Ag^@*(fE!51`wKT2L!i!~C=M1A&jSN-3ghitnX zCWqx@q{7{r%|0h+Q`EJwc^4 z45|o>Ent7Dc?X6T_004HnugdBr~_isl8evj>_qL}3rf(4o+8bKWm`eH1_Q{d33edW zUI*%Org%p7=bKl$e`+-><{GG+3&n2ZtwLIL@RTSfAJ7X)s_d~J060=r;4&cKHL|Oj zDtndA8i6fBTJj;?=rq5_D8>Bz@ol_fjilt&>xanoTH1h7V)d;n{Q+Hxf zHt|X|ewSD0o_#PcO80kqtA3CE&_ev1NnOom|4Yf<@dmGE=@h(#MU(JIb??+__(2Yx z&Yq#Yahg}~&pcgRR=2$~`x$1OfbT#k9?~?N1$P!y^PQ^WJFXx;QCYQ8o8zV8w?4(Y zog?#z7#BnNdId3Rr?dp%d!yI@l)`Dpqb+6@ow%4(s?($t% zHwWrscvYvTArD*dNE6(YaR2gZi`n08}iN8?-<32mMKFDSTEK}0=Q87px|1qUe2mj_RW?W>V<_nEMLOP9WPf|QGK)8=QL$6R?DTj zgHEcC=x(+Bs+^u}SMNcVed|hp_0~bop*jRbI#qc@BHi{Cf4OTRv9up&)wKFnXXXa- z3eMB|)3=r2ew=+MZu8kk5ESlp5j^#-fhXR*{`)pkm@i)#Pm%3sx`(QS1>i3%+oZ*1 zwWxk+s&ad2w1xJ0Lh2+dM;gnet!?574knOq=cM|Sm-nK~5aHZXvD!mZjDfw+sy}I| z4E2R?F={4WAxB&F+VA*)t8*Hd&Z$=cYi+abyoPR zPWcXm&0}T-Rlq2^!R68V+E42AX4Y@&2}GXP)7->e=^VG85EQDO1YVSv5aOy{oPIiU z!o}5#5%<)0m!uZd2kJ{G2L0L)D~gHK^k>mzdZNxRcby>dI8@c{(0&w&|GL}Rp_0Cy zYAEIr)bXS%9kH&|XtDgy#=#S&zNWIV_#4;kZukW?v`_yXHMFp~KC&_$%o;TAeoyGw z!5Rm}-WHc$iCJ-p#yrF#N}Jr#GRlIa%1_N}T_t=(*2)c^l9A^;tl^3hY;=B%Wg_1i z;H^#l*5x8~MAWOc0%=?T`>t1ZAA^R-qs-Q9l*f=uLkFOlC0gf8Y!|;esPhz^iKE?? zmXpWevJ~k)RinhFZL3j5y=t%?(4hhi^2TqiGPa~aU1rZ~`(R%bc$Mc1I6<-uKciv} zpcEH#p6Ox;E_PHy?=qc|feHt2=#vt&+kBwFH(lC&NCg$$rKaBH7@G&>oM$}b!{hVg zpI>}(uS^Fw(tYq!&Zc)|oJ(22_j^DU6k$?bdF@1AEIoFD z>O6(VXC`s_M9^LOA_7NahbntSM`Q#hu)gV?Evg@F*E9ae>m5Da&_lh3mbp{Q7tt3} zf7Ki3c)Qx+xq9*!HK1{@z4UaS_OhHXA9XOD;?&_^O+5+Zdoe5W45a&}=VK=6g5!9e zW7M4(Qp`C8LwJ1Ji?XENosygL(3(WIVy(A{0{ov(%n((=JNvmf&0CYAnf0w zz!igfsfx(Q$F0a3!AvGUs3Dc@MOXsag$Z+z!FT@EP99KdyWd$W_)Fk($v&;iAzua| z^OIE#%6`tx3C7~oX0_Fu-wSKk@VihOkqR(|m%vdn*Wk93^z+elWSGT)#MI>;C=sPn zJZn(`x}0q54R_(r^a>k12s>$WfdSZ2x&+iu6J!tn{SWhrQaj#iA2Vn$ zlzOj`=oq@Gqx#b`o<0)^YI&RJXYWz*ahJ?;wO9=Gre_YH4U;`>iib(=>@3_dux02s zT{(8ia0LA#2jfKjDX&LrTcF?Fqo6mZ08U(7sq^_3`fz4819g)n1lnsw$-%V%h03lE z;UZ@F64)xBSBMZ)V<-=t+)wB_0icA%1abXVB?7;x##L{LN+f#&PSXo8#*lIx_d`Cr z+BI7p8y(qgi&=>a#c!oG=0dp4ZE}0uy{d6YDUi-Ik_txlSD4B6a&5=JG@Z1#wgJdP z*l=Q7_!{bte?vH^V)_O$x!l z;QOUW%cT-x)%}M8A*Vwx&4Ja8Odv&sWdVR)%d@<37t2hbi*up2LuxkFndziz8^PAG z3eM4MHX8z(%0s5}*TnZm?SVj&g=*eIaw8`L1peYjK;zfh_kFpVgm#r znbr)piIW}Uy6i=8#N5?HiRL(+`UNKMNeuu&BL*pu!!2utD<_#N?B3AK`!I1&+c2@y zE%`7yd(XYnfZ4&YefBf(JGDPliC!OvkkYrv|QydtJj zolQh1RdF-!xg_j)(ia2yL5>PgxO>T14Q-(Q*40~uke*(wU}y@^Uuyc9y1ET|Y3%e8 z7R_HZpN7>9BE82{SQ3*M3t~bMeTQJq2!|i@RbtMb;hx)lLObBQ-g&bx;C@b;96fss;j{A%?D94dk zpxAqT8Z5wD6$@aThF{NvK2FFfQJvl7$r)9q zJ=PFf+t77-w=5J>2ic``Gwv(ZDMj-JGAV&XOMjP^I%^r`A+c?Z%-878>OQ9iVf&WV zjjYecW-De5f-Ey&OD?P-M$F3vavKd;Z4hG(!XzjuS9K-pB!smc(kz4EG*km6R{#`^GF5V+_A&%{&p@Y!8-uKe7Xzm&$ov3D zvnrZvd6W$R=O>^)4z{ejp(6JfUOk89vO=+GR?jh`bpiOD9?!~rw18k44~(+jp478XcTsE!8>?A`k}Y> z^kF1-vAZCfS7O7q(|JH$`lUHx!wAFWVKmctvw}{+a^}Demy}zS^cln}{uXR*&;VF9 ziL;f-xA5tZVqs3j()5OKZhI(|^v*op1L`kb!(4wf8VyFejflqJpD4@R8MTNzZD=wV zS`lWkAyA%e@Z@cTpM$4HHK#0jq?##8wL|8WDr~F8uqRMB;q(u&nfx||{OMTBh3jV` zMG;QDXV{Pi(cYz5*pwwbmr-?Kp5kU%nVbb#t~eyOUwQHQZ?nxm5k+=x&2l5&qqCO0PHGxLI;~e$_;{8o`E#4S4^xQeXJule; z_CoUyP&VKocvHA!IiP&*$=s4$#xrI~3aWVR2LYipX&!Kcx}4PV{c3I|_le6fJt!K? zdC`YLST~mb{&^Byz^aC!(6%9U^NF9Q zH0Gz0w}c(_Y~SKa+Kq&vau!y~&cKe#O7OxNd5g@TuJ4!Woac6YzqF=YdEk zRrN_LNci@$`KpM*+D~jyFhNf06oB)*p5__jV-X>CRUAMaoPBaGZ%WGwxclW?Uy-)7 zU0Qn7oQfGzYv>;n^%%X$%mQ~eJgqH-9nMlB;0^0BH^k@(66lWe98=W;1U8e-`66V=>6oALTJDhDn6c zkb98!!cS`J!A=anBb4N?*o{wkHyT|@M@z8tWe1BPgmw=#br>d+qFH!Cb^K889%z9y zg+jMEGN4waK>>F(td{8bcu?M3BglEr12^N_i2}WXKxnoqd>fa96^HcquUyg z$l&2f&Cn8=zo^s??B$i(x!f?B3>pn60D>1mQ!N2Khs~+wB#+iZnFSClic?`%U}~st z6rp^Y2~aER^oV2vF~4<;VNCm@2Q@QZE=Rh5=926v6A?F6cPo?IXQ`2ahzF5}JjXK_ z>(c`ureeZpo(Ik#A-t8g_xoLWC**kdeR1gR30cG85}v;Sa4KPMJ=24O{_ejM&BoQ7(m~W;8skA4}4H4L| zjS8|q^M_(~yLlz8E!mnel5_iroykhoQDTWQZmA< zss#5sZ1sR2MHfWG!!M>~MSvzvZd%7wUQN=kX5hm^kVce>)56&ob->F#(qfS`AUhe( z`Ji2Z+tG#oD4!-5LZ72fC$LfjU|KE+FhD(HJ2Zdu*u=A_TM(wu$QE`kjen4T6KgEIn;A!v<5-rPeM6!gJN%Q_*>T+B(s#6aI9t zX`sHI3Mc6lKR%;>o<ww@NYd$`P)bQNRcsgPnV z;1%XJbZ)P65U zPH*kXD>j3ta1CcM_!t0Y`^HsN7}tTj#o^n;?r}Xgc}q)&(?P5svC(es$(>jY68-GV z_ZtvhP#0(n?x_14@Px~?c3cU9Y4O*57nnl7J@`#3MQqBbXO{W2s&$@6H6ha6H^~o) zlnj$zSsg9XNwHMd`@o5uqnv2=NK{lz^5t;3wAG7vNGMZosu)U=^ts~AVGOBEmMkKf3Nh=Kc|QjBl2VZ2x}&lXtLXT^B|ydj(7iZV8l;|z@? z1Y3HSV@@WansBF87agu8r#$e{8O)s-uD4%$*sfO=DVJBvQKXq**ezAqeUsH-jZ#xQ z#;^u;bCp%OYv)`mAcLbQmZjCX4sAWo%@*-WZ4E#Ho~;MX^t2i?CIE<|k9}(8Wd|`! zE2@h)~4MM)l}KK!M_@>E2Kc7>R+@JQ!wg$9b2jw z{o<@#jQKD`jtQtyi*l8_AGk859bXO-8M!-HlNKBX=g@ z;p7mOAY68~-=5!I|BX&5r0lW_!)h{TTMC^t{b@Eo++O^M6DjzcOrHx?;aNaEe&Ni=l+EATL#RL3GAqo}n{ zQ9j0J1+=rgMmTEWyfW~=j0-{%RgAY`KeZ)UFBEMrlkGOe6H3y3A!>ZA&L) zWEd_WCi;q#>GlB(UBlacr!w%12S{?z#exT;H+Ua`IxY@G6RPAS<&SyTf);E)uEn~i zKB)aNvq2f&eHI1miCk!5bowV>qS*4H^Ek24y6Qd=Q`Y&C=F#7*a(J6Q4TtzSYBdl$ zHjV7amg-MGpAD1GK6e%k7>{q>Z6(K+4I2XI$Pi)n)%_Ac?u0wg1Lhkzf@-v5jW;HI zHoD?$U9z7opL4&Eiu{#KQU_)BQHkKDe$hB^=q9YEaY~TpzPMVq_si{2dy^X9)09j< z<;V2|y$EB#4Z(VD05=V=ms;?mdP)?|dv+JKgoJj+VtY{*9cT+*V(vVxG0rUxeV`6? zLZNB}#B^<_KBc71kb?m;LH3$@LIt9&S$GWyB7?GkLl#}? z{g&v)mKg8$iQ%&Ud%C@|DLgZ}54h{pVIRk$!9+~7uJok0)4Hl$pyfgjQ90bey9g&dYJ_YJQ2j00w zcNiWZKunI20MmpEsqCIdR}cw+(Q_XyV}e`!`p+C*N5tl_XqD@ zBmP1@Ux1r2uoa-5(ar1U2ad2=wk9qMM53KG=_i)4!D;Q(@8_9mzooq$uN}q6O!O-O zSEgIh;6Pcf7Pex^hBg3!(@`h*hQmAZfA)gO$iT^0_9Y z51odg&?q_1mj>zd!uK4#Ai&6i;t{M5Zt|(DZZIxN?Icu(gSxvh4#}fdm|E%;dgcm4 zeR5m?iUbG;zzKrCI4I8{7c2NZ=>Czu5YrIXs|i6_pc@`d8;80m>1RpUO{*nLF*^sq z4Cso~^daA00+fMThig1!2$2C)l;C$2&2$!|Fj-&!heprSV;ge-BrR%2F%!*&QTmI> zM|UU4sK+uCB}fC}uL@iZ20D?7kQ&%MIwP-8baQXm{`)b#huGHOY;eL=)MLm?Q%q2U zZ$nEc3< zXU8Qw;pacfkT_Vj@cOPp= zvWiBFLCW)@F>pK1to-2D$B{;4U`GG~vKvD^KO{#rh2UJK#f+nVR6B6T1{EEk)4QtV z5N5pml&oQ6+5WK!-&Ef0CpY*)5!|xy;7*nxp*)e}?Z?|&>=T=iWqd8Lc!aTXa zjRb6WIwhtZAdZQhn4n{!yg(J72%Z=)Lpi#3n74qJ0d5bbCgi*?jI;O!aM^VZd0ao# z#b*Ra*KQJO>QaJntJEIQH9lzJq9%5re1Ye6A^<-1@na_)G#(f-WEd$PMDnO;9>F%& zgERz`{5!hj5Ac;>siQ=v9zDrwqQKA)`x+D-@396rJ!X=DS#o{Zk*j3T1H-JDPO7~- z1ZL2<6}E@FW-a1s#Q(VDSI-Gls&SBib)5&7lkq66$E6 z+eX@=&HzHpXqkX#Ea{7H^epx?Pz{PM&fez&z3O^!8~5@OC`s1q7S0Y4GnPjUKN^V9g8igV zXCtLGD1qUlD638QVkx;R2_ev4l0rig3NqGCT*1nSQfh`_Vf)PhlXDo-1s}Vzl>T0J zxDQez@fIm&NJ+0?d#32lhdss@&Cm$acsI6}iT7^17kOewQw4)CDi`SeLfBq`>D9x9G=GD{n^AP-3!irQEoiwo9? z=sYdTh{kq~3LyRP&J)nZK42dM0KJGQ|LFUe*3X&bD$!FgPIN{leNEW=%tZ_=GTnaP z?@(Zat~?poKZg4;v=j%a%!N3#*lH!st-!hA3j_w^j&aFAJ0RBqX>)|a*oZl-TdT82 zov7H6v)c}gEeZ?baY#NGAv}UCVEuuES&v)X>DZwU`q5wDT)+aSF4a%w1VaQ@S_@_x zNXm513UG;6QW?M*LTTbM5GPR2<}_EQADOhM4}h6zwi4hJ=NiI8{%7dfsXy8?1RV}O z@OPU0Y`+ao%GqgM(3-DE{6sb?6`M}ZB^pv1w04-ym=HD;`kQ$9BD9CgyvpDRiTx*m%z^3Xg=rVZI*TtT`hQLCe^)U6hotNcHT*r$PQQdo8q} ztBKrql8*`z62;~Eo1b@d4seY<=G+GwQHml#O0L&nPlHafn--*1~ZtcHktfkSGT|j25PQ3NOldWqf&B(bL{; z@c)s76j4G_blTIPe{JS8pCVy45~wlaeB@1ENY?J!RdCf7+BfLuSpPAUX>iRE7(2hD zV$GbG*y1}#CZ$#?tQX*(WusQunC|&Z#rSI{ZnasyyiRL2gbHLN!%yS$i7Imd-b+-R zQrY!fKrv8c+@y8c?_H)~WS_3)IVbyZdofNRON59cx$_4Iu}5k=kVDGr7G%8Z87UeD z>xehD5pYR!pLp62NJCdCA^AF=E%KUzr7}|13U->rG)TjUqS^#B_$FhelroKer0>#y zMb_6ItgvQw$@sX=mu-3xf-gQS$D+7p`uq7bnP%EyET#@HWsgH`0B5DN0<^2MHI59r z4;N8S1el{EpaD<^sq*L|=FXi9SWKbQ05oAUgD}B2d5<(Gyac1CIPIuWJ#Ca!Bks|h&owLjYTe?k)TGZ=t7=lvsq&M z#8dbXnUmTceWHgBeHiMq>?U3Xx_ekcVgn%Wo*!krrh#>sJUtsYW?czV$IXq-@xi{1 zsC#*xPd;g)fWAxc9$h1pcnIE(C+3!IW|lFfA8M zmy23g2083FHW{LI0Vy!Sz*lO&oHCSci?YCC-RXHmMy@YL4t49q4}O1a%BVm9)pMxp z6|<|#93#4bToRL9j6Q_)Og&*!pl!bI^6q%(_?*aVp3j7J6>06I%YxE(r`*9auYyR4 zja;Fd_4dwTB}CtS?OY2D4~B?Q7-xijxXR-KP76JD<5xD#4CHIfLBBRnKlu)GW4sYL zyLA}&9yyfWERr7_VQEq4HakI3Zwxmi@?GgpX(i?(xKbX`xm3kFmXos({W!p`(q{*ba!hl{=IK9A1k zzyG&zHjnt4XV0VydG<`Znd>-k&%s=K^XpKz%k#_V&?WY{l0PC(F+_w1dn;aI8!9_^duSEQ539|9a3gfJnVbA#aV^h?8t0*yk zR;NX|+|=sCmedvGr6Mwu{2-{ut9ir)?ZTFuzrkYkMOGcGmeohVhS@YVsvKg-^admG z70>PQUw=G5dHuJO^Mm&(bjetdH9@gzZghpM5sA1(Cm@YSog7#cAzvTT;O6np*t z6_f#-MAe#Rn@RRK264nm^F7TxRSZzzW?fE>>I3+ve~M>_$M7z#r-h=#v*r-cwADR4 z&HPk21Z=oisGa(%nyV@7-_pOsWUc?m@LwAo8LCLRL>C0NzQ6wlHX$m0TAZ2Wf_|~T zyTzV(A+)A8RRM6f+-X$~cv<^a{r*q;b}!IaTHLQ_{tnX{Xy!llj6ki5iMB7*@9)ja zuo2@#w`%1TB_Oj@p7GL&g@;M98V$I}9TkUV!>4{qkf!*D<3t1*9+mj85CL_!Z$UTo z9I43&TeVn9?l5eK7gu&(ef}bye3}wiHS7fO6)xxb&XO3WYMK7Ez! zqNNi4)RllyiM%oaegIvuF7rvcA|u!wV=l-8ngn^Q9F;_?+N3c+CzfkWSbvqL@TQ?8 zb2RUuj2jtG54pqr5k!zch&(T9QVS@qd0r?6#LiGe_;{-X=nral5mMzXC^}YLlRQqIy0jCX7n#_WnDp=)0hzI~SiJI_epX`UXZF47hc?;aHKC?;7oWI)1Myvj~fL2GlMD zRk~KaB6yk^6%h2DNR}wNBHAwc^cMFW6hULBvoTF4d2goW&6Q|>nuI_y7o@)tVWUEISs@|J6C^w;-(bM?EV;(1T6E{@Gi&pYgps0q$J&%C zw@^h$f--1xKUz%R0}*&`doU{1gBTo;(@#97?u>$dh47}1RkxHZV+t#=7*wQ6H11ta zdy%TC7b8N8d6BC#B))zz~=kXiCF;?p$PGP#nS4H9JfIDirK zzGbf=e5#mnYWCD$mRNUzjTQv34ZJD5UAl&L@446-@rje*>2MFqyh**=#mmvTQ&aYa zEs>>4mpjI2MUEo)Edvo4q++z65&h`6UxU=o)3?yPBlBNsX&)tih^X#|^QdXJU%%SU ziVx`zM(U*HiY298Eb3!V_C9QDqxWg4>b6++<>qNA=HRR9m6CvJtrrEI!t9yevOuLH zS$GJQSm&+ec6Xv_ujS5ZkzzWpFsBALbKq?nP*eMIod%S%r=%zZufpn6zKa5r+L3#M z_8h9dI_5};CU1cSJO&Oh$bP&nPje5x#+|p0{jhmYXm6BRu^_-62#N-P5)`-~ZBmn{ z%T?rr1_s#!x+lv?u{g|RQ|3tz{sNP@9Mh@*RfC=!P(VULh)8Zmc#ho*FUl9|!%VWM z=g<(qP{6+*Qd~*ug~-Da#a(YKMS}LQ!%UZ=yVWCXA@*|oXhFRnDf6HnmUB<4OA>6o z{Rn^~>96f62~UyMCfhU9-79gN5px_|H~J0_H58{=p5sBsG96|ilZKa@e%Z>b!dZes zM`(b119plw?)9bKT2@kZE_~Y_I0!|0)ycnfei_yu_TjE|qT!>T>zO~zwpBKt1(F2n zkcdg(b45W8eP|t=2~k`HKR|pt2=FaWyI*Cd zgpdZ%yyo%E?#`042co1E1qRS*G=F=WU%}?1A0J^`>tBC=Q7xAcrq4f5s)aoXfb8&h zc>Z6c_d+eGePC4vYqzeot&9KW9;{^#c7#2zREO1ku79iVZ4>#enmbNt;d^Fb-+?~$ z*Aex7?;X%eF%v)?hwy9-fBhuhV#nf2ebdI9I?ub>Nrg^WG;^(AO20W@ zUNg<`=&#}t&E$qQa3EW?;|%Gue$+J(kT7r@phjCjW2OEVnhk)OZz-c7XhtC$4&P!B zETHvz$C?Axo$T%VRS4y@DQ4U3$25(ezZDs0>6=aE$tXjb#Q}c#UGS-^j2vtZfco}2 zn{UA9%Jf5z)V_daBCPOPRZbz94I1q*Vqu=E$7?Dw)KR_w@c{aC2)8#*w_M$cVuNIr z~VyI*-65Y6di!L;E2t0WgdJO%1kyX^4>kFjihM z(b)5Jc9X6fSzEI}7shn5$@T*W z+o*H>N-#>%n2JCE!a!E~5cT#CwWaAHy@F#_F6;H!uS)%QittjZ`Ag;w$nz4ykE+F) zpJd6f2%x-w0E=Sp1pj7%xAJD$4^v5s`oIBSeZiI_%FfNB=t0flyIxe+i5o>)(gznU07(tu?$`2W%EE)AI)_fA9u@;X*%NL#xANW(lTq#UG}Fpci<%F z+jEVjhU8asMfWK_k=4;P`YZ2Fby4ce(i!#Be}<*~ls<+1iIk9GX=Sl{RJSnJEf3dmxx11zqy45f~fD=iMV2rhl%}D3om5-fpmB< zg75QC>gVrb`zUX34{ zZ0#qiG=T6UDk_=5*N%G|MZ*CUDxqKPaB|&(Z~?$0_A*EB1LH78akwoL4Ut2~s(Uoi z>W`uHX8K}Muvd8_dI<~x4;T{m&SpRC1Rj%)^5ubaWV{)8e(uI~r_S|!!ss5Q5W!D8 z3CIej4%3>gHMWYaZ+S6YcYN#n&h#0>R!y%1A4PDjYdA#$F)lFKKj=Sm%5#PM5+`xf zYY5Z4%jyP2fux%obyPzrp$dxIZJRT-$W@(@Zy!7HO)%Wa=uar*SWV!xA^;4mk$ZlN z-b{#BI9uyF`_@(3;K=CDt{JotRoUe5p$$)UKFdF+*vUk~*9h zri&LzAH(!92T5A7&?3%Zw( zC4}uE+BVN2z4fL6shS2YAxGGV9!N{n(H?HUXyisb^lW1Uk^m&QT^a}6-JZxy32Aqc1HC#F{q)Kt$}MnUb|oY-T5k8d!>6&&n}Up-dNP? z7Rw-U(K?cd*a;8Zs<#rH)odFxP55Z*u(azBsoLuWifnuPI~IS!?j&_oq7j+$49IF| zQI(81(w@^MDq};nuiw9Ft8~}aT07ivqi*-fb)R0}*7=pJ0g+;Q=!DxF#}2F;XTp`J z2d3$LuPy_zA?oA1JK8`H_}0sUPqpr=_}=2+#ofrW4c)lgu=V0s$5#6j_FE^=y-M#lad|(vDxi_?(Z7UqNmfwc{!8@S zapPeM5l|sGNnJ}9FF8T4CobqPlk|2EUS|-ej$lraJWp6)kJ59+3USg@oh@mke{)e8 zes!GZ96@G+coxQ4wl9XT_+$9LyMcdgd0&Tc z^xl4rmT!f`Mf9lW-bVozF1#LHmB`5B->RokU8M)`Vq>Re0D7H+RPZsM&hSi!5!R%j zB(1D@r*UckJ?^E(T6vc*udA%zo8?nL=}=+@RNWigQ|Y-c2@R@rU!-*lxJPF{P_GNn zFJ4dX(Gd#9oRHzHKOhr)Mv+vUhYCCl>ztI2m=*9^<5okAESB-mNMGa)X{ z1XhLl83r)%aLja|I-IJt1A*C_r5FY3P*`3=lTUlcb}>r9#2 zv9IWY|LUz4JwrP-jm5$u24Lz{T7*|S+o*e!?YFJQmfJb9XYqFai?&%dkdZ)G-W`UTZPqGUtAk3~2%Fc?WN#zTe?&D6}Os z8;vLK3dc6(s>#Or{^*3Z96{>tV7dkJUBXI-`uGGBonv%rxob@?!z3%3U#db842wJ9 zH3Z2k86s63ze!cq87`adRdT&tE}HS<$5X}Mtssf;9C7hxYyM~SOY^vBnpNIBezy0= zKku|1`d>pc)%_PBD8b^mz8ku(S4QqOlA9+6Yd8iMioyg!RkO5?zXCwBg&UB$J=f(Q zL*%u9$s*2f(3aci{pf`Q+OnIxHP?h0Zy%@`Re21zScClff}ube+k&Elmenq8YP1c@ zlss7Z8}P})xa7jbB_~CO`2w1ylA7UQ#;*E7i<#LT&>hFq7Mvas6QN9Wz>+F`q4U## z(-TsR#>f}%e$CEGE?bYY&jPC&C{`o+j(=fr z#@BQL&(Zch(Z^2r;%j2{+qQ}vW!H9T7SZ4q`MJw%YIE{}*~>v<{_Hx{Uib9S@6okW z{SEZToWZZ{ScKKJ_@)P7X_sKCzeZfx9y}70Y5fRD6h1o(8}glai}?Mr3HkTOa@sC_ z+z+Qriorg{EQ5B5Srs$rzSP`t6yH|xc8J0Oq<-vl{}<~1FRJ@uip&^(2?Zz(uOf<{ z{^H$iH8O*4BrHyr4EV)ui-1sArN2dSzHnU?)AmNOjfaF~R%D?QXL>s_aJqyrMB&03 zYSoV3qt&>g97^64z~R127f>Bmg+N{P(^=S{8ej|AR&NNn75;c~)<&+l*mK2jFRw@Q zqT~;U)TKBci~jJ5{V=`l(-ZySle2c}x>Z-W#`|}L$FQj@+XY_J4&np19fAG^TS+f` zup?^Ve*s1BX&rt-2`$W*x{^@?2x-o^MjF6Z#B@9?Q-4rb zJiI1=G$kKdDNV}pC8sM!1IjMW$xUwaO6o{WsuSgQ6p$eGxYitG2AYjk(-{b#O6r>x zdHus?Ia`b-CZl_!Mya*ps23QM&a#J0FC^CjAr56WhOc`k=fgNbYJW+icd=%;lbS8TS zoyLqFfB3h^=n+fl(V1(bXgD`!(R zLMAbp)QBNm(GZJ4m5(&0nH?qgU!!|A6>fC8LCX4Uu1f214GHaVwYk;C+(?y4QKl$9 zT)>l}o~)GE3H6z1Y(?c)*eAPyqiCAQf4(|;ro^f}tPShtA^p_OBW$^G9CO?*;5lNG znyuCs>8)*Cwj$k=ka=FLNh_KrSNV<2d<=4|tF%+Dwstn@cGzH0u zD!b)Y#X~9Df(u4A9{>cE$-CJo0C%$9IwK_-ymoLj$$3odFeAnM7xY*yiJEY_L=fAj zXat=zkwB!8P5uicl(nA}{szVm*VKHmTxJ5E#jSj@92q6f}bgRLv^Y+%l3Yl~0YuK@ zpZ-L$*VZ5)dMM{Gj{t|H^qj77;b37WqzH0E+8#;;iF4DqNp{{2N7K&pg*n4sb+$Kp!hafOmA4|tgg?n zcS8%}iRv4zH*o5tdJGSTT07+r_@(1r=qXY|2!z5YXBvlzjqCDXL%L17J{Tii>U+YGH&9Vz@)9-(cuuait@%w(l=C_Lp;Rd^5mK) zZ}W2K9%}ljp1W4=pu29Dg2=*oUQcbM;RgW`h&u7|@PV;t8GPVV9duNF9CQ=3(3UL2 zo|hHJECK#hQh#J4Q4%bAcmRTmV%+iHzF*&<&HQkGvhRPb78Sm8NVtSlY#Azj=J&Jn zOV7NicT~)**}Z9;CQpmxccN2g-p)NO+N>UTot?D3Q(d*yZ|rU4F7(ByyYT2iFSqXI zT}MubVC(vOJ#OrJ(6!%N_g*`9;0^ioY}j!gz7}`i#4Twe>O`MLwvC}OiZ}2lgb?^C zQO5)r$Mh0p$z}mMcalYr=|&t$fT}it{feQlmSE#Bb_?V&CPt}XbSm0+&Sml4o1ta1 zSWe2O;i{LAC%NHVQw(^8I2*>RFf(BHZWNg#w-5a<=T@yHmh+C5WIrG>!ry@AsLj>D;XhOx;7@=b zIL-#%-F`>~O~*&-6ULS?{`J&%F zGqR{VKRjo6Kgd#QQtap=$e8*S450+U1vdm~Hy93ck{6%R2nt%Z`>q@Fvu1@xeuHfp z@7=qd$wZg`KIDjht_ub+iFCYNwhq(3J8T{2nU9cj=lpww#;MQW-%JSnV$>5=gB#0O zT-n@Dq@IQ@8G`Q!VEufWpby<(NKh+RoGDA=l_J$wBm;@MDgAVyr7lOJ1xRBU8{oTC za!#g@c(no1x{t&Zyl6bCD*cAktEU_0P(~B{1&SlQhG^tzJckcf4W5zaLq$bxCDboW zYuneoXp?yEv!qYYVl-rn`;8>wJnEvC_z1=^befNJo26r_Nq0Ciu zEK<-BSQ+Ptl^jO5bBKwM7l?L;O6Al>Qc~LGZVq?75K83@iWs6uwmSzw^us-G4NrfYA9eU)zfRXQ2g%NWc z4&;{rh9J|3YkT2)Fnt0#QsZgSTl{XZD_!2s9v_M2gJAHe+<+`!+l``!qN;>)BbU$d z>V=nG3INy*X56Px`mC$CwwS<7vb7p5l-dpgM@`uA^V>A1#-XL zJFK%h^gkA)j`ytH26lrEzDZ4HgSRM03^_L6efM2=NpES?l0RwxAg&^^N9=(wq1Ft8-(zLOIW_=9#-e8|B- z3vFk>qY6AKv#_^I-sSA_L6=Zhm1vbHmd1S(@&&?gaG#{EjXI|drsJ?A>i2x>?c2T- z_dipz3*E=!8yyKvBR|yD!VVo&OJ%OsC;Nb|y~}mq>9^?RHaoGd7F$%RH*H6ax;F5q z?t9$^FO`1RQ2p{y2UrQa4uH7W2*~x<>0MjXqCH)GKQ{H73@D z5GX`cFS}=%ZH+vdd7ej42ACiFdMf$JZCG&iKnb*xwd+(w5NwaiY zOu;VPEM<#FXsb?VeQJ8(dkxwd##VLiZ?|>7)lqVIr4`HQw}rR1)+I^LBE4RUb4=BN z%GmKn*M}%LO~e(^aGGWYjrSwo+IM_;S64}n;Kek&hLY{D>y%oNF}Adue9;b_;E=3a z=TFf`x!;?s4WWvFC;F-c?yQGUHu|r!BAP^hg9Qn&?%m4g&D7MLqJtSkon$Gv8f1Ah zsS65{=Zqh?16?0W|Kx;zmWQRi<(=@xHfPB@i6k5IoE&=YeYzd;pbR|y%xeZv(jGP2 zJ3|5ybETrC)wL~KofBjgwjnW7OW)@}dT_?dd7yW&8#w&)RL5YugV25258sWJVFTNE zna1N&Lm$okod}44EJi>A-t1sz10nR9`)5+8H(c|Zsy5)l4bTa?gn~rFLUpGK`H}|d zSx1xWv_4q&_Xeo6>Yeu@7P8Ai4wix<@9A-6vnMqg!;=?tY0IFJep3s>s=9PP}QqZ*Z@n zl8ny?x93<~nJ|*Esr~M2eJa8`V%F}`)zT;WqGzBY{b99kPKgVBs;iOiAsgp;=DCyU zN&?hMZ;)A=J6OvQp?v!QwLdOqBJfn7j?3@u~szdI`Nh zv$$VY)R|B7b^6=ZjS=h#x7F`$EwSBjpViScaO+tfcDl1+pT_Rb_!njbWBcdk6|`vb zW*WbDTl=*WqNS(*Lnb7C6RGnnT#^W>plB$Vrm5yRdhpjA(}U=;Ym4X%T0s%6&UtYF z;<>E6r~rgN0VP7IUF%&|M_n-Q==cjyqF=@0SYG`(@#=$!ebFQ|ATJo^A0j5WuV8Tk z>)7XrCtKVv83v{JF!h0@<`$?$zw(`i23s;Uts2{{=rC$F95$%EoZrK7wBK>X@L+2W zJhpc}lgmn!3vBa3{LC(h*XdTv_H}JX^Zc|{rGq}1;Sdp@hKajlj~Ig#ZdO}^gtIQw zzL=+rJ_LQGOFx;6H*|H(P2_6?z^Sg=xTaw5tFkE5xvAYkjXHI!UoDo6)c?#I#J`{rhL0=Vy85Qca%^ptXnYJ9 zOUqnsw{~AWfUlWJ%OcTvC>;^&E4DCmpiz4C4c4F69(U@Tu&Xd}CPX90W-*M{ip*+k z3v_-jcEg617js0Dhu_nD-~}Qrd+-jedJM$l0O0ueBmiC0ib^9PN7S>uC*~Pp9l*1v z=9!)jc=`;HYyS9efm{;-t|^-Lt9h=TNCcOwv{~*JO-XN%u`zHmiz0&ck}e933Db&% zgKq(ef&i;ziKa{!&2_bW%&&D_%j)>AKem-!R?X+BE~)-JsSfAUw!-TDvMT0N(-GKL zjX|Cuj%VyBwq_(b_HvvCfO`bi-`+~4N+6%fp zyhAdZ^qU+b{XXXdVeL-7EC}8H&dn~%Lq^C+DFkT@Jj2-eA}u0Jz)f>&=_gO zrV!9)>iX;2jAA}Wjn$>YA{eW9v^ zZfmy!WR6dv2wf<2V~k!_&Vn_#{~KR4;=9{ZeVpZK0rDVhqL z%z@W+b%TLMuR$j7_kNF}{1yD@fhq zaE}a=1z@+TD+EqF+$esd`T!l#aWu!!>hlO%1*V=H6C*1rAXs3whNebdIQoY_51XfM zKi5r}A=@KP(`ZZZ@`^0BH08F0UO|~%C&d(Rx&;1q3drk37L?EB2hMFFK5(A*MEEf| z_$q#FRssB|qOz!!C2W5{sB0#5jsR#Bc#MunC`neytD|?80^;{SLMO9ovI5_%5kcO) zCQq$?Pt~ovs!LPamyt;a4;fQ0n`)+5;$F1mEXVY#z1=_T?d>gYxsYk`vdY$@bOA1A zFTuOAuRj+fCK{ya61i>RUS(VokI2(LmgMV=;;xZ=IkZb172E=HFNzvQdcWYmews@` z5T5gSCNnwnuYEsd?_g6i{s&TOQ4P{wY^%g#i>Tditxh%GV}#B4yY~X@3D|zN+bisoFKtKzC0=Bxtd;GVko=u3KG{A| zddYZMBLzV&Kj-zm`#4mE(5o_H+l2e!Imt+-TSKPHLn54m(28P+a%3R#9LR4hosNB^ z_oDL|%Zj7Ls#a{ISU1Or<`AVihj@oLQ4+dAeeqzT4Uhoi)*OeNw?f*}3h-q2Rdx49 zqIYZ1`z0?yx`&m4I=6693Fe$2e>?69D$pDFsj1=VsL%{*4H$9Dy6;b<7MLTJC@l>0 zP72<$sEYrDZ?ynQ|Ay~buEc-Bx77Wy&LO8keup>$r{X5d;qz z(&fIIfMK`Klwvb2TGg`?lJDSr^wm$_Y}9pjcvjU{CNNy@`onhzKfXTy`QzL34)`{uq>(hwj>%xB4?^g72W7|Q!FKIQuQiM@r3C-$7_ee9L#Jx#o! z9g$@j><8O`*4osOqjiwSkH7Zqx>H1mPL51^OuRC%mHoKh)mO$n96$ZzQ}>P5ropS< zyRT3y2haa#w5{o)07dKG@S&?$R89T~m9PKt@2Y(5L=E2(sQeGm9@w%|kB z5^2$QULMt;kIQ73UgV913ID4`i%mrwu)ySH?U^40!vP$WT|Lx?>9B%Fjw0+|74<w(bULuEr4|1r@Hpr$SJgpH%8gu~zLP zs;ebU?j2c!OqFC5CR;FDuegp)jp=%MF2M+EYNZG^iuK|cS+LD>%F-32%QMya!&Y>| zj@X!lwB}W9umW(RR9|~geb3yvGxmI9MJ^z{BU*f!ncB=Nb`t1{xS^rWA#_X0PJKo2 zZc1%Gu81Pcs_8V>GM3s*Q{>$cRk(yGodN1u1Cng+W}`=w7nStFX!KckLkJ}dnIC4N zpQF}NK$}NIKYREGzG@f3g81Gnt;$yW?F8_7T1OkzfqDv1s-E35TUD;QKB@9GU&@D` zuNAwq*yD$Lc@U`aV$R_Hkh)a0fu2^+mKW3$8`{iQIeXvB#{hW2`$~scB;O}bd_JvZ z7XDxM-gUc;^`Zr!>l`?Q;fRwaQXnG%SEkqHuX znQP6gHSaJlGymrQ%sb4J%)Uj$j>t#=lv9RnIHQ0jNA|?2BwL-aGO`wGyhEs*UUG-FNM=nebM+7Wl<>o~KAAtI>V!dvA&duJ^W>6X%r)cs3D$cuImGrfQ-HTwND>)FS2&xoFTQ z!eGNAL>oE!euTDgVb1^AP}HooBYU=FH*~iI{_@*7)p=U2KdMkeUnh{2tb$vShZ8}X z6ryN>93>6urFNZDJsGKML+WZ`!3*d;Z3okJ_jtdhI1`{FF7-h$Q?~wybMixTZh7Ul z@J0_06C4|c@VI(9ji|XmyMv!_W%{d7K@-V#sr$MO2IZWcrM=FjNgEcWE#&sEZH?d& zX&Xl&bsJ1cf7sj7B|6Ha#HqU_#xd?o9+~pS7$eL_`h4Qe!)fw7+VZB)6z@t#(Hefa zi%cdeu_hDYS|lZ#S`Ay$c|(L}Nr5^RR1*w1&ZF+Zt3m)tK(@c0rJ->fbJA-rOJvNJ zooJ#-{T=T=X<;$Z0DzW|3unA~w>DcZm$B3W_kJnDk7UfLIS|bz<}n#*DRoVyUX9)m zQ~;k5eT;4l!Hp^PA!jQQ0>JA6eP6MU;JG!c$af7lbUH2um>%i6EG8J1F>*Z!ywG_F zuoT_|d<@I1shhmUGRfZ<1{DkIss=JgbD5-Kz z_KCvm@Ss)MmY{iY46=_nstm4~Cw0zD>~na|!CKJ)M?*dN9LP)Xmg$ctnBE7Nl6Yyh z^ha?o?ujM8&jFTXQ6c_}QL}WtTF3{a=feqN<{N<%3@dtx84-;YesfJ@TRf~u(1oK_ z(94FTdo*hdGdxSCjpM`WR)jixQN+MTP!LK9ji_s0o<^g*HRz*MmN4~JD%c<`J#b4z z4cqvah#&&x>4m%QakDnOq{+~mGh=m4(k=9W8fRvdrmEgXxM;o!d0#b6hD1yTQQ8mj zp~a3Q#I|4bqzRj&d5T#&3I#14v1?wz5^$lEH{E-}{p4=q?~MlaiuET2+@~u-i$^-% zi%ltVs2OvonYrZ6+Mx}Uc7!@~GHaKG^ddvKaGl+wO#qAAFT15(RrxyjvAeZ6X;8tQ zr3zPLdv!ROS3H?lFqvxk;ds(3zb8wt=U_zb_2AR>DJl$X8m73`^_vP$u1=vY;Xdms zu@uE)q(y>{H-03vr)Os4XUP0KmpVnxYr#F&(*bLgV`OmEQ(I;e{-onzYIu0lbW_Bk zk^0?ZSN#|DeR4a?u8Q&I-s=s&aU@>`hS}c#>4saP>$T}Jv4k%KffH~w;}m|MN+>HiQkvL?+xb|o`o!`AzJ;tgcLjmJ_Z6SQ<9eX z`Sd>-oJJzN$>bE`W9b5)(&Xd3nJQvhmUB*gDgrf+;N}v|w4d{QPCDU5HvTO3tYQ&X zF)kO2yqcGU1I`&hm9>j)VO>W4JG)Ij(u3Z*loP&uv`$UKmARdPc`^7ANXMTdH|CFY z4Qbv%+1AAL6On*V_=D3{NqC($$F5z_cALOJEq=1BLz-S&9(wRePt$(8*wq-D z4d#bmn+j65k3Th4tM}W-p%N7-b_0V^_lZwtgCnK4UnxG$@9R<)?h?#iSSFLa&KDqO zf{E^l{brWCJv-%l%egkf!H^zB)28oPra?wj7&FcC~~?OKBhg!X`xu=0N zwZ5a>&jL-d-7JK+r1h0I1r%QautkG`V6wJ7?{xOmElN=f6YA%I@IT`)hqvVLVOgP- zSZIadxN%SAVQZd6}$vyd9YYV5n$wzI}kcB}{U zB4$=K#zpsSL}ZI8jhj2CA7erhBwx^o-S2Q zhCV|TJUM$+4>-JHlJ8wr7ZS)Iag)SkM-FP2w+Q+*h13C5d|s^lrD3-K-0)%~z#WVq z^TSS0zB1Sf#?wOFM3gGluoSEjgm9Nlrdg#w#i{{|8A6o|PYD!21Jy;^m1VQF6f=Zf zrEVauUI$&luuANK8oMlke1`2@VQR`V7$W6*kw{pH#kB&vG%WGKbZ-0#bI*Q5!Bn>m z_+N9l?4#H+T4oj-hbv93>lm-AB(%o+tvCaUirs6K&(CP9=4cDKAWT}0JTNNHJV#T4 z8DBD@j&+(d6?9+f#D05&m=qHl1-|5iaN>7g&aQR+x5rp>5>I?a*__hAib}P5PdZ)n zPl46_mkxHEk74-sUpl?u+YV->Vt1Ch4By$W@pPfj!xG{kWgut!9sByU|EVlyU6{F^ zH#PP`vn@$=8)Jpl?1r;q0g;eBVk}Gr=%ko)nsj{N6)bMb*a8s< zFg(k&owsEx1ZLk(N11)?E|x>sS+|vXr7|c94PWI4&Z0XehkfU$#X6GWJTcC46l7Uq zJvSKb(=ubf=s0@m0B0G5^Kjqy>@qKgr)>bgK^4vKu~UuUX+CF7P#{9oeJHJ1c1)hf zUFFOm=xg+a6E05hkJB}b%1!mOq<|>vr+gmxDRW$}^HOFv0+gtrTo>Bw`ShbF0O1OW zPMVW4QmgMzj39Elr;KJe_mB*6Orv18L0?Aflg;iVnrLGdW0%vAJ)+p%x|&amAQhFk zD7-1)AUl%ukiZ|^BNajJUKbPR3f)zogEYilpnEU>dg9)rhOatiu@}xgx;jrH{7578 zd7d4DyeVx&62HWp1l(M>gtDfWQnBl)cHL;1Hr*rnFr2hg>KaelPx%F@IT?3gIji#p zJ?eJ4C)v*5P9OKk_r;FGYfUX;F?oYAQ0)8BCs5O_)ZR*_u16#LJxx0O3j5npSP?I> zcN!zP%{$o5_(PX2hoaM^lm2!OS%l@Kk*g+oqO7@R8gG@8(2E!^jv&j;S*#T}pHSeJ z4&V>GpRe*9m@FFKR52LATn{O9tcOHq2KRV$k0WEJUw`5xYNF z7)7Ri_wO+>tr48UOL#?`ertp*LA)Yo|7Wq`pA=97<-UPiJcUb{Pfz2p zPdqYMntSvr(l%@?jNRp2Jg}LoJObQh3q^Z^S+K{;1t!7z$Kj`%l@n;M0zq0XvT)cN z0J^-&V~Aa@OWzCzE#KXx>UhGK*!B)I$J^)ux8~zT#F~>I1M zz#O906pQgRkA5%(E*)_p%QxD%Q8OD>cGDbQ_YG)lnk0~v! zYCqOHswvhxda+gS5QvPb7U(u(iQ<^{9mbhA!R{_rKXbs<1fUOHG}!3TSvJ*C)>L$( zChOzcdIhrG#PSnJzWI7LU&(L%MB;UE9Xm$R+qX3y7c)`?N^#Nj>QKZm9 z0~P1%MwEa_QSsBh={SdopaY~0N&dyK2-kn3Hcv3%v@|N{F0O-X*^KI$?KwAR@gE29?*R_!Ddba#XE%_wVC=WVe=WNHEMhIGKSLV0Gc`o5RANdU#sglb5R+YWAVnK` zv4>3nV*aEVkR!XxWnx|QXZa0Ma`%@oZH^Q^@dk#oMc4lg8nbmRN$29aH2$t{y-N#! ztSy~tjTvV|%f^0{KG>y+Zj5*sIa3n*5Xsj~m|5YtCX&d_fqr4U2?K)AMOu^O*y+`! zJ%60WOq!mDAuerGgy0}+rYB82irG9b42nVNCrI$)L*^n!?h606htwiJTNZe7&e&@z zpIHphR?zn~Ag)C#MdhFldBB&~N-laKe!g(9kPZpzCx*Tit}@AdL= zzIq{97xeYQozJ`HE0ynqE*LGk=$fv5|A5vP7T%af3xcH*2N00tZbRK~HO>&}QPrnz zp76!^G2HmG#fw!@;loN5{5UJ+tfPB0N~YN6jR{GX9aV#@e+f&7vRr$k7xnFySutAEV_an`FFQsfUB;H(nFWS{ZN|Q3)yrb?57dl|PZh~##Z|b< zo`=z-iZ_Omy@aK2LzJ7^8N)eNUU4?RU!gf&HQWcYZE~4V?e9KEd%1oRO=})B7cgXu zAIWCu1$CBtLqJF^cn|lZlANS25xUlbbcuP@VRez;j6zwgNIxU}Rj^zN_DeoBHN44g zG0DjUzM64uW)@6|%etOdqut$$qP|?B6TlUnk<(lG(f?HK7F7k@Yj^nF_g_o8m?<1J z`D9)ea71h~$M;w|PML!-F^GT=ul#(PUEm=RYXRSpnsbG^O#+wu8)-Skgo!&;b6a@4 z$mjzyi5kTvY^%@3To-Ji6Ju2M?5XW~P%kkMj&hov<8gRHHVI@4OJL0S$Lx797~Io7`ei*2zi1de z36ew)>tj*9CY9j_j-(kW?2>yku4RGpZ`Eo=-@LV=G^d5*HUaOe(RV`0k1>!c* zc9nq~*>d)>!&os{Bh;+EL`>BLar=+8fBWU_YYWYA?>`dqZBEG7wC4B*ogd$SJbv-x zt7aKWc$(pklq$1nr`MO_{OwQq1sr;d+vdj$d9&_)2n#tEW^Ssz1aTrkG7$!6BMo;SERsB_bZh%)1i)-zKpe>XUV!L`D6woK6Ys z8O~QsdBPvbeJY3$M^EpB~`>l;Cemf^Excula<)k43#8 zt)WYiH@<$0f#+f(wgOH9huMjY?FnHkF#;|}W4P0eV?Q0%8R}cf!Lt}Ir*1n7Ls(CMl8zlG>N^3~m%8|8P333gp#Wm`vUoL18LKU*P32WtdMY zNT+;qgK*E^{KnJ=GAFDFe|;C5BskIeEg3&>!DNzx(KiP!z?2i%`&EbQ!1?QZ1ex1B zPIlq%>N1<#g6LtL&F8=|(99Hp88w%AK2bBN=!vwd6m36a~#^W}e=&I{et z;6eVONy&T}U;#$c!c{O~)BH^~hk^8ogJD1Wi@j-6Lk|tY|8Vru^2y*7gOt5K!~bZ0 zR(=UjYd>UFZZLpYT6<{{Z53R1FU-wsJ7qP@{3La~ET|7Xo%Jj1i9cgs*b_5&?n$=l z@{`ofM$i-I0>1i*9a5dC$gHwimQby}m|cj)Z{rfuHB+?!pJKSsQ~Je3!rWQ)z%1XX zi>n;NoN74$(YbMYn~2ve+q>y3PjrT+wwG zT{r~4(G@gfC1^hIXtEsVE|L}=J%zjIZ;aiBYteh} zc%E?+*wikRRg4KPyo9AQ@EFXzCfcJP#SDwk-~7vD4*$Q78(25(OsZQm0&PVL5JMV! zD*_rqoz!e2gnXG?^V@li8z|Y~9ysQsHYyZ-=CeCn$=pugj~#KcM~p)6TJ>oMizx9a$dKCn8~7hb(!M=?lv>%uSsx&+#Q-vu}kMAF?+oIHM^rlOKN*d&E;U~n=NMJk zk4_WO#w*GZEc3LRbcaChRz!qmQZt*fmtzo3YOTBp$AvSEklU2`0|{ts_OA zX3$jm)3(Y3Cce4yt>!-%+Kw>&M@n4aGx6#F0An6*9*!TNx zADKhW>(EZ3CZ(6``sh&iUo{x+3Tcx(M*-VZ8Bb(3ZjB-~YV6l4x2T?F7amIH@Grh;GJJb%bYrB#=F# zm{O{+Y;|PTuTWSBR;%2WH_K_7*QD7{YA@|Utdw2)F>2-DmKx>&|0saDsG&x^E~#Sm zY6aI>DbaSD)lS3W^m!tmz{NI#UaQo65k`ZtW z3#HC$0?JZ}X-p6LaiNr;S_o3(U}FU#h1s|sWiHcS;iXWSQWiNHLCN&D$SZQeM#~7k zl6V{MUJcy9CyiGAQcGX~>6PFuJ0k$QSaHgM?cE2y-Sy6lZOs}9AqLb%yYty`asFU!S?l`Meyv#1oQnj$Cz$nV@Zv^~ii;DI2n5COfL(+b2s zp;?tc9d!ceFC%vk{o1LpSx~_dc4C~d`BCfETF1lKuTnM2CmlM_Ill4YsQO>Lp zUK_Z@K=~s)Lguqef~`3~L2}i`(s5ZXKoZT+(hl2GTjHChcHIc-gk)O_65h`!aQ~dL zwc;ih4Z|&L|Iag0*W}i~Z_XmLG*Sd3&PIj)&!RR$QFW3a#MboIL#UOs^XL{ShU)AL zB_dOM;*t;heg7FHwx3DZ|M{AKW#gAtU=aN3}@FcGcW46S^}{ z_EjUv%Z)%H@_xo{{>;;ya#Gh)g^TFvjab+l#&tGLWB3IvlL@s1lHpu+_Ro)3Ntc35 zkatzZ#jK}ZPWGlPxxLw@Z;65({3;2e&8CYyo7^UgBWKlSt6zhEO{wzfl)@IH|@~-F15EVU1KgYgkH#}6H5|1 zNTfCqynToxYdmyUNbgk;2Rwo)2L5AMhp?8fB>wy>#~XGvPPb#xU9^oeztmB+7@o<0 zmvc|T!573sdQq8k`|D*S)w37k=TsS_$)IRIu}Xm$tztRZto%1ND^oG~3j~R-a6dtF zW}qJY-g~~&(Q&WU>+*u-6!m3!p$yHit{j5LIF1KlZ+wzAy$YT-2LTuDYgh~TF(KcH z3HdD`1r(0$6o&6mk!$CS5-OBljN!&n;T;^)`1mU2W`?*%$ZcbczBfQbKhiCfUc~Ci zS>;A;z&-x&e;@QxqYzoEGN*aeejKVITg%$+sNP)r?S;~*VeHPQ$WC6D&r32n_>rgf zqmlcYnGr6neT07u6nWWV_il{88u_iSZ|u5#V3s85y{UwbPXGJ1&f4-^AgICe3ZITN zm7&=S?NhVTgus-}yd<)bl@Mn+Ll}^f5hP(<3u=RJSwot#eUt2D%;4CB&FWKke_(H= zqZx3*qP|rISau%Tac{gJZe$JNM%gknmTDJ1=Xq-AYcY0J5Wv+5hSG6W;#^cUUY3nZ z-+3`b;SwcV;|+rcT@~}2xW=?GIBLvA!M+(X3D^7a^3QlOsQgpo+uSoVbiKjC^RF{U zk%8=}p8czg$5Ft96|X0b^{OuNDYn@StE#sjgx*h*G3zu@ji-D^Cy(xACMIWy& zzjU_pOFy=}%PSl|D!46}k#%jzLPM#Rx@8lRbtRj!mCpJBlWhdVR}w;z6^etjh;ZMT0R8tpaIT0y?Dr+PnUZb2RmZ0S` zz3;bb5c<{HU_^r+%7$y(b9UAr@$z|oUW^ez455-J81n*%6%L(pR*dPkg?NAN3Ed7X zjQnLj{)}K*`HeA3X0^M7GL9Tw0c%w8R5y7(x2W=$3(^eW@q7h0(Wx_97H`y2!GeC9 zB1eq_jbK?hKOAfHOHOn4tb+2-4$KETJN^f8>}p>f??;i_`S+x=k-&b%@TvofF}j}o z=1*t6FQOw6>z@MH_(r*LHkw$$M^8Jiee?Sxt|QA%;58M+h;957{tKqia1$mqstb(;En zxlYUAxCZ!L6i6gxQlHo}qpDlJg|6|pRuh%L?YkT!vhsvwTQipRO0XSsfBi*tALgcS z@4s3DVCu1WX|MrbqYLMYN-p&>Cp3tnD&3ukaN4A9MLY~3hygfmU8I!1(tq7q_BA9M&+OJT@UvOnpV5>h$<)x64b5DXM!T0QPZELY!HUEd#;q8On7J4% z@<~aaEsP~(LVUy#MBI)@XI9^o65~agk1Cjr>KyM+iyRYW!t^c*^hUf|PU~!zm&*#| z3nT&*TNoOSVhD2QWoSs+Iu>V@y^CeZ&oHGwM@pO(==S(C&W3qAb8g7oMFC!LZDxsn z&jD08!F-Pd`aeUX1@Xho004e~Q;E8`lN>2zL|Z9)uFTBomJ&-5?I#Inq_puWpu%-n z1y&2^bUw{%(u8Wp=d0uH!Fa=+u3biAI0X%%pB1;}NaE$X*kXrBB`P%&b@5w{+M-w>C8&d!PhyMlP zL(V_3(J`|i*X)01JNDG;nAa`W%SOj|0x{>tIPZQ+lVKpbe%je-3UNj-cY_M&87~$u zY6-FFIz1?NUYx`CecTI%K5O7Oj0_3-dFk+`s#NdrmIibX`e>`5BFn(ea?1S(b#0nOnzjkjiT^Ykq(6lxPMEa z04$1QubP4UDm99Q6xc58e;e?D>iXgX?5RK2#k7hR+=UV!5h>^663bW=>Y|?JRSdJh z!idyV#C$+0gx8iIV+m9IaFN{<6eijJ!Gz1$S;->Eb-OI_ee@CQnCF4S{Gk|s4nCmg zA8UMJYqpzZOOOU`;oaL;FaGrVKfikM6MH6dxxRa>i@kaG_QM|{<>z$A38b@dHW*jjHX4uNKqY4sz}jdLiA3J7n41~MwasCiM+>O_lk*nFHht>?V%(6 zUY^K%MCwv6Ij6LJ!~N4Q;itU6$m<_LBKGRG&-^kUB)4Bs?1SVtG&5(suJn9ZN4lP? z(zzZUsN14`A!R*JCS6v)*f)^`_)vt6ij)w>(}8J(u4U)YGrEnUZU8PgE417LH@(H5 zma{o@6U49=Ju%&Jv1+-;VCfNiG+9u0zLzpwT+M-Gu1%2Q`^hmoPvv}oc2=DG@wQ|9 zC-4S^VBlvanQjhOk7@Bahnou&ricjZBikW#S`f(Snl9jy==LuV@}x+iA=G7x4V@uS znE|69V0~5=qAy7o1jDI8x3KO@B1Vc4x}f{kB4-;JeMh%gRR$9nB0L4%wsI)I^RSX| z4VDAoN>*bG7G361n@&Ao4Ng&Un0P*UZo2SpMq&4VMer{2VWBJfq&TOkm7CqqIR2mM zV#hm&M*b75tHV3*S~M4gaJGNEWB<&QRx+iDO!b)jxPaHxd89`u-G|Hk;zix1-&GDb z9TYEnG;{zJcqvr3NR^Lqk&CLMd^Ugv1UTI|r@|8*at}`rX^g+zDWfkAp+^IuZHqm? z?ALH7Qp0loB;Hu{f*(*FDrzOGvmM$i%268?b)pwQ?wb>7UrhJNaLS)bH&0|ZrxrNr z9{8?2jK27M>s~%jPSl?a;ob{T1o2;^A~wUp?Vr--55t(4de+g43@J5B)Y})hC04@H zwqqj^S`ZzcWS3aj4xS}FW-7AZB@G6a#@)gOrE)6#s5{T*?21L&tDa*l69Ujd1HzBHM+vqX9F!K!zGJzizE z@QS!1L+~(AVlNf5IgGGVTc!h&HGvPwx*r&*La1hZiSujJ));zro^++Mk?Wm6Bgy0z zm6{A;b=C!trfq~-<@l0kJV0URA>n4W9M0;Q17L6hjt%B`9sAncw! z3yPUXFgOi_aDp98;#BRpnMrxt zOd1vueO!`toLJ|^DNFDIlTwh#Q8`+GtA9mGU4O*Tcl6nyC8?`?dQSd#ID=$}2v8PX zJ`9E0#Me>c{}s>+b!|M(=e1}I@%;1PphoUb6r8Hmq}d^Pk1QIz2WHq0RHjaiV+OS_5 zxGQnaMwPL+QZg15^`hcoo9h}-40IWm00Udnq`1m1FlNe743CSa%1-Pk`OnA6A^VS` zr2$U*Du-E`gcDZk{ad-3#4`wi3`bU|5POl#XL$6!H~TYo{@Np#%ci{7^$DyGT_QlJ zFU3*n;2JvK(La#3xi}WOhqzO`XaQ4^@!p@IdoAmnl>bG^&{cE-s1CK{#O6bu&RH#1 zOS}PeM(O+PiQG3kdT-HHm2Yl77zJAJ> zy1)yrjPnh>ZE2(D1D#NVT5{C324(K2+1L~|N;|;K!HK^8cBuwTH4zCTl^9c+{S!L} zr-4UH!~_z?v$ljxN*kCFMxt3ig_RpIm|Dl6*VDGxOP)(2ASl+0$6dN=2wSZU_@|^y zUYLTOd@?8@81eF?dvjTgF9`)w%S}HQ7^{Mqh_^S{ZLbY_$tRxB`|Mt!oCq_c1$~%C z39#%SMRyD_qG}h8Z0n=qu9v`pb}l5oy(7E8ea0!uIF{4jBpuZ*IwF`)Qy+@b1cC-1 zb0@>^JjlwHFL?#bm-Jb2bzweA$tovoMxYQe#Fh0+6-|;m2bym@4n)Ru_<_!1wfV`o zHQs>~|=zqf!=)z)EhZopbV;M`2R}g2*P#*=(}fuIh@VM5{Al^fmajpS-g`l%`4v z^1;?b3)XiDi}uDJxKC*BlHZW)ujm-iPi`Ae{^p$u@@5n~pea$4Aka4gxn;xT%sf)y zOO{-fj# zs9*2m>r%TI#bZ}ffp5ZBRx2lZSGF(k`yF#(D`n9`G8~%^A76Xs zT{8l&jD18#UCp^alrJ&r_DA^q{+wHCZyo}hzQ?5&Mba(@<(Esu_!9IQQWLt=WTdy{ zB%`p&COxvf6SAAhCwWF(d}07$iV5EfMav+SGkwa{jhLu8LW4wY;+W-!TXJvML*`jda<^Z(*q8 ze1gBt>?QvrJw$8ez>iZTT@^;T81)Hug>;!*StdFF)`sCMT4-3TjHeI!c1T@NGlzb{ z(CuHI+ckx7aN6DK1bS?_3$w?6lEOP zg<99iAgIqDz^-Wo5hU}SZ+ppak|7*`d+>Ym+uw$P0oGJvE5HptL-NZJOO$3FksC4c zn=1~h=%RrI5xi7bK`$&sTg44jbkEggaW1MrGw|jWsoU+EmlDVBD(b26!Z>@$`hG*7>rP^Y-{3Yv}j_vw&>veg?>+<)%hx5fIFHw+Hxk`~u&ibsR3}PH<1n26wCjIp+OW)SIrYm-X2; z%R1PPW~@hv1}`ib%=rA)=FUd7vcYg;I1ZEAnbi#s+@+I}J`-TbnMn$8HXTo~)%+SY zLE;qvt&pwxNa_yGcyxBVLI2y}*faKaQ4CSpj=LDD2xYiaBEZT^0U7i$&7*1-tbAQQ zUlCBTm=|;=?on_y3M$*e8<@vQ{jZ>#u76wwvhHrUa#SH&5V5kb1$-VbH2cZ0<&&Yi zzyx7!UG4@0(fNq!lBgGY*tN@p1>GomF{<-tV-2K^gk3il&X2r~B}r|3wZ8>5S97%M z6A~IFkZ^=~d|BQcoZl2~DyEni&7)(KmkZrZ5@avx^J3 z?^RtBum;sEF^3D5Qlw5F5#=>MqPF!%57sI-8mJY%FdP9b|0PHU2-7;U!`MNX=fGnd zQ_7kiKW@Iq85w!#$MieNdtII`=;JU{9AS+J*YE1yF}+AbHIlsXV*mRP4bsh z1k8(zi(7UZ0)dDQcyc8MsfKPdU(JBgewCadRw?w770C#rUAc8yUR~Y3SnWrb052k5 z=rtE^(WM!b5q}mIoMG$44BX5`Ly^`{3$1TWIvQ=v$Kz`@ow7OyXno)aKUh8Ucw~QT zE?G?XC%raQjq?s8pdY|JoXc*1g6l=plbWIOUC!+Xy%W`p@TXGie&cN|qPc2|9(64% z_Zw7Gu*TAgZ4JB#7GfVget}0ww?XA}IiXSt^{*MmPJs(G=|PcSjRdd=X}YRGjLfeK z_Nu8^SxhOc(J7`tACyKIdwG?!1`_et6lgoc5gR9(^Mtt{+WXm&V4ByBZebc*TsqVU zRaZfU9o$|WzV;!|TX{3HB7X>Q;dCY@+AaW9v>TGHqvKk@ZjqGDqd$SD&{cfavrbm| zYHs3)=jBazI1ny%J}p6NXj~uY9c=KfZjkgS&{bSGD)9R4aL|i2SImx?vbo_$)KQv= zKhB$t#oB--=-V_o8T7wT;VuLJe2xEn-~ay9lbtjl9(XB^xEpD*Cp`dq$**~i0`y}$ zW}F;_xy0umhV%6IzCq05{?{HOgLGHMRVI1QzYouU&do&xPqwuT_Z#D9suD_OFrmmUPrfBLkc#||n*$&Q7 zN4cCSH%X4sCp~Rha?Ghh!Qnt0<8DXipy30ig6^|#2ER!}pELZ8yymh!at)kLy(&pf z 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⪙79(8!uJN~zCi1O6x7+RWn$I*j66 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

-{B7 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 (!HfPDE}r#RJgIgAneu?D576Fr_twMK3) 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%0A!r&?L7<_~ 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
0U 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
    ~0{fY1 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 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( "