diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..42ac651c Binary files /dev/null and b/.DS_Store differ diff --git a/vue-solo/.gitignore b/vue-solo/.gitignore new file mode 100644 index 00000000..185e6631 --- /dev/null +++ b/vue-solo/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw* diff --git a/vue-solo/README.md b/vue-solo/README.md new file mode 100644 index 00000000..eb6d89a4 --- /dev/null +++ b/vue-solo/README.md @@ -0,0 +1,10 @@ +# Fantasy Map Generator (ES6/7 port) + +## Refactoring Goal + +Take the original scripts.js and index.html and convert them into modules so that developers can work on more manageable files with fewer lines of code and use the latest es6/7 syntax support leveraging VueJS without webpack. In the end, the files could be built and deployed as a single, minified file. + +## Credit + +We're following this approach: +[Vue without Webpack](https://github.com/maoberlehner/goodbye-webpack-building-vue-applications-without-webpack) diff --git a/vue-solo/package-lock.json b/vue-solo/package-lock.json new file mode 100644 index 00000000..24a1f4f9 --- /dev/null +++ b/vue-solo/package-lock.json @@ -0,0 +1,2457 @@ +{ + "name": "goodbye-webpack-building-vue-applications-without-webpack", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "axios": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz", + "integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=", + "dev": true, + "requires": { + "follow-redirects": "^1.2.5", + "is-buffer": "^1.1.5" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "batch": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz", + "integrity": "sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ=", + "dev": true + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "binary-extensions": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "dev": true + }, + "blob": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "browser-sync": { + "version": "2.24.4", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.24.4.tgz", + "integrity": "sha512-qfXv8vQA/Dctub2v44v/vPuvfC4XNd6bn+W5vWZVuhuy6w91lPsdY6qhalT2s2PjnJ3FR6kWq5wkTQgN26eKzA==", + "dev": true, + "requires": { + "browser-sync-ui": "v1.0.1", + "bs-recipes": "1.3.4", + "chokidar": "1.7.0", + "connect": "3.5.0", + "connect-history-api-fallback": "^1.5.0", + "dev-ip": "^1.0.1", + "easy-extender": "2.3.2", + "eazy-logger": "3.0.2", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "1.15.2", + "immutable": "3.8.2", + "localtunnel": "1.9.0", + "micromatch": "2.3.11", + "opn": "4.0.2", + "portscanner": "2.1.1", + "qs": "6.2.3", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "serve-index": "1.8.0", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "2.0.4", + "ua-parser-js": "0.7.17", + "yargs": "6.4.0" + } + }, + "browser-sync-ui": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-1.0.1.tgz", + "integrity": "sha512-RIxmwVVcUFhRd1zxp7m2FfLnXHf59x4Gtj8HFwTA//3VgYI3AKkaQAuDL8KDJnE59XqCshxZa13JYuIWtZlKQg==", + "dev": true, + "requires": { + "async-each-series": "0.1.1", + "connect-history-api-fallback": "^1.1.0", + "immutable": "^3.7.6", + "server-destroy": "1.0.1", + "socket.io-client": "2.0.4", + "stream-throttle": "^0.1.3" + } + }, + "bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "connect": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.5.0.tgz", + "integrity": "sha1-s1dSWgtMH1BZnNmD4dnv7qlncZg=", + "dev": true, + "requires": { + "debug": "~2.2.0", + "finalhandler": "0.5.0", + "parseurl": "~1.3.1", + "utils-merge": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "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 + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha1-p2o+0YVb56ASu4rBbLgPPADcKPA=", + "dev": true + }, + "easy-extender": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.2.tgz", + "integrity": "sha1-PTJI/r4rFZYHMW2PnPSRwWZIIh0=", + "dev": true, + "requires": { + "lodash": "^3.10.1" + } + }, + "eazy-logger": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-3.0.2.tgz", + "integrity": "sha1-oyWqXlPROiIliJsqxBE7K5Y29Pw=", + "dev": true, + "requires": { + "tfunk": "^3.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "engine.io": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz", + "integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "uws": "~9.14.0", + "ws": "~3.3.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz", + "integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz", + "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.4", + "has-binary2": "~1.0.2" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "finalhandler": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.5.0.tgz", + "integrity": "sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc=", + "dev": true, + "requires": { + "debug": "~2.2.0", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "statuses": "~1.3.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "follow-redirects": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", + "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "fsevents": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.3.tgz", + "integrity": "sha512-X+57O5YkDTiEQGiw8i7wYc2nQgweIekqkepI8Q3y4wVlurgBt2SuwxTeYUYMZIGpLZH3r/TsMjczCMXE5ZOt7Q==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.9.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.9.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", + "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", + "dev": true, + "requires": { + "isarray": "2.0.1" + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "hosted-git-info": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.15.2.tgz", + "integrity": "sha1-ZC/cr/5S00SNK9o7AHnpQJBk2jE=", + "dev": true, + "requires": { + "eventemitter3": "1.x.x", + "requires-port": "1.x.x" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "dev": true, + "requires": { + "lodash.isfinite": "^3.3.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "limiter": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.3.tgz", + "integrity": "sha512-zrycnIMsLw/3ZxTbW7HCez56rcFGecWTx5OZNplzcXUUmJLmoYArC6qdJzmAN5BWiNXGcpjhF9RQ1HSv5zebEw==", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "localtunnel": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-1.9.0.tgz", + "integrity": "sha512-wCIiIHJ8kKIcWkTQE3m1VRABvsH2ZuOkiOpZUofUCf6Q42v3VIZ+Q0YfX1Z4sYDRj0muiKL1bLvz1FeoxsPO0w==", + "dev": true, + "requires": { + "axios": "0.17.1", + "debug": "2.6.8", + "openurl": "1.1.1", + "yargs": "6.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + } + } + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=", + "dev": true + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "~1.33.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true, + "optional": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-path": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", + "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "openurl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", + "integrity": "sha1-OHW0sO96UsFW8NtB1GCduw+Us4c=", + "dev": true + }, + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "portscanner": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz", + "integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=", + "dev": true, + "requires": { + "async": "1.5.2", + "is-number-like": "^1.0.3" + } + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "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 + }, + "qs": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", + "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=", + "dev": true + }, + "randomatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", + "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "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", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08=", + "dev": true, + "requires": { + "debug": "^2.2.0", + "minimatch": "^3.0.2" + } + }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=", + "dev": true + }, + "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 + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, + "serve-index": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.8.0.tgz", + "integrity": "sha1-fF2WwT+xMRAfk8HFd0+FFqHnjTs=", + "dev": true, + "requires": { + "accepts": "~1.3.3", + "batch": "0.5.3", + "debug": "~2.2.0", + "escape-html": "~1.0.3", + "http-errors": "~1.5.0", + "mime-types": "~2.1.11", + "parseurl": "~1.3.1" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "http-errors": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", + "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "setprototypeof": "1.0.2", + "statuses": ">= 1.3.1 < 2" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "setprototypeof": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", + "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=", + "dev": true + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "socket.io": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", + "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", + "dev": true, + "requires": { + "debug": "~2.6.6", + "engine.io": "~3.1.0", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.0.4", + "socket.io-parser": "~3.1.1" + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true + }, + "socket.io-client": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", + "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~2.6.4", + "engine.io-client": "~3.1.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.1.1", + "to-array": "0.1.4" + } + }, + "socket.io-parser": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz", + "integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "has-binary2": "~1.0.2", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM=", + "dev": true, + "requires": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "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" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tfunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-3.1.0.tgz", + "integrity": "sha1-OORBT8ZJd9h6/apy+sttKfgve1s=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "object-path": "^0.9.0" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.17", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", + "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==", + "dev": true + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", + "dev": true + }, + "uws": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz", + "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==", + "dev": true, + "optional": true + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.4.0.tgz", + "integrity": "sha1-gW4ahm1VmMzzTlWW3c4i2S2kkNQ=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.1.0" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + } + } +} diff --git a/vue-solo/package.json b/vue-solo/package.json new file mode 100644 index 00000000..8a7d3a95 --- /dev/null +++ b/vue-solo/package.json @@ -0,0 +1,14 @@ +{ + "name": "goodbye-webpack-building-vue-applications-without-webpack", + "version": "0.1.0", + "author": "Markus Oberlehner", + "homepage": "https://github.com/maoberlehner/goodbye-webpack-building-vue-applications-without-webpack", + "license": "MIT", + "private": true, + "scripts": { + "start": "browser-sync start --server 'src' --files 'src' --single" + }, + "devDependencies": { + "browser-sync": "^2.24.1" + } +} diff --git a/vue-solo/src/_redirects b/vue-solo/src/_redirects new file mode 100644 index 00000000..ad37e2c2 --- /dev/null +++ b/vue-solo/src/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/vue-solo/src/components/App.js b/vue-solo/src/components/App.js new file mode 100644 index 00000000..61224622 --- /dev/null +++ b/vue-solo/src/components/App.js @@ -0,0 +1,20 @@ +import UserList from './UserList.js'; +import FantasyMapGenerator from './FantasyMapGenerator.js'; + +export default { + name: `App`, + components: { + FantasyMapGenerator, + UserList, + }, + template: ` +
+
+ +
+
+ +
+
+ `, +}; diff --git a/vue-solo/src/components/FantasyMapGenerator.js b/vue-solo/src/components/FantasyMapGenerator.js new file mode 100644 index 00000000..d9dcc5b6 --- /dev/null +++ b/vue-solo/src/components/FantasyMapGenerator.js @@ -0,0 +1,10405 @@ +/* eslint-disable */ + +export default { + name: 'FantasyMapGenerator', + mounted (){ + console.log("mounted") + this.fantasyMap(); + }, + props: { + msg: String + }, + data() { + return { + square: 10, + } + }, + methods: { + fantasyMap: function(){ + // Fantasy Map Generator main script + // Azgaar (maxganiev@yandex.com). Minsk, 2017-2018 + // https://github.com/Azgaar/Fantasy-Map-Generator + // GNU General Public License v3.0 + + // To programmers: + // I don't mind of any help with programming + // I know the code is badly structurized and it's hard to read it as a single page + // Meanwhile a core part takes only 300-500 lines + + // What should be done generally: + // Refactor the code + // Modernize the code (ES6) + // Optimize the code + // Modulize the code + + // And particularry: + // Migrate from d3-voronoi to mapbox-delunator or d3-delaunay + // Use typed arrays instead of array of objects + // Get rid of jQuery as d3.js can almost all the same and more + // Re-build UI on reactive approach, vue.js + + "use strict"; + fantasyMap(); + function fantasyMap() { + // Version control + const version = "0.60b"; + document.title += " v. " + version; + + // Declare variables + let svg = d3.select("svg"); + let defs = svg.select("#deftemp"); + let viewbox = svg.append("g").attr("id", "viewbox"); + let ocean = viewbox.append("g").attr("id", "ocean"); + let oceanLayers = ocean.append("g").attr("id", "oceanLayers"); + let oceanPattern = ocean.append("g").attr("id", "oceanPattern"); + let landmass = viewbox.append("g").attr("id", "landmass"); + let terrs = viewbox.append("g").attr("id", "terrs"); + let grid = viewbox.append("g").attr("id", "grid"); + let overlay = viewbox.append("g").attr("id", "overlay"); + let rivers = viewbox.append("g").attr("id", "rivers"); + let terrain = viewbox.append("g").attr("id", "terrain"); + let cults = viewbox.append("g").attr("id", "cults"); + let regions = viewbox.append("g").attr("id", "regions"); + let borders = viewbox.append("g").attr("id", "borders"); + let stateBorders = borders.append("g").attr("id", "stateBorders"); + let neutralBorders = borders.append("g").attr("id", "neutralBorders"); + let lakes = viewbox.append("g").attr("id", "lakes"); + let routes = viewbox.append("g").attr("id", "routes"); + let roads = routes.append("g").attr("id", "roads").attr("data-type", "land"); + let trails = routes.append("g").attr("id", "trails").attr("data-type", "land"); + let searoutes = routes.append("g").attr("id", "searoutes").attr("data-type", "sea"); + let coastline = viewbox.append("g").attr("id", "coastline"); + let labels = viewbox.append("g").attr("id", "labels"); + let burgLabels = labels.append("g").attr("id", "burgLabels"); + let icons = viewbox.append("g").attr("id", "icons"); + let burgIcons = icons.append("g").attr("id", "burgIcons"); + let markers = viewbox.append("g").attr("id", "markers"); + let ruler = viewbox.append("g").attr("id", "ruler"); + let debug = viewbox.append("g").attr("id", "debug"); + + labels.append("g").attr("id", "countries"); + burgIcons.append("g").attr("id", "capitals"); + burgLabels.append("g").attr("id", "capitals"); + burgIcons.append("g").attr("id", "towns"); + burgLabels.append("g").attr("id", "towns"); + icons.append("g").attr("id", "capital-anchors"); + icons.append("g").attr("id", "town-anchors"); + terrain.append("g").attr("id", "hills"); + terrain.append("g").attr("id", "mounts"); + terrain.append("g").attr("id", "swamps"); + terrain.append("g").attr("id", "forests"); + + // append ocean pattern + oceanPattern.append("rect").attr("fill", "url(#oceanic)").attr("stroke", "none"); + oceanLayers.append("rect").attr("id", "oceanBase"); + + // main data variables + let seed; + let params; + let voronoi; + let diagram; + let polygons; + let spacing; + let points = []; + let heights; + // Common variables + const modules = {}; + let customization = 0; + let history = []; + let historyStage = 0; + let elSelected; + let autoResize = true; + let graphSize; + let cells = []; + let land = []; + let riversData = []; + let manors = []; + let states = []; + let features = []; + let notes = []; + let queue = []; + const fonts = ["Almendra+SC", "Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New"]; + + // Cultures-related data + let defaultCultures = []; + let cultures = []; + const chain = {}; + let nameBases = []; + let nameBase = []; + let cultureTree; + const vowels = "aeiouy"; + + // canvas element for raster images + const canvas = document.getElementById("canvas"); + const ctx = canvas.getContext("2d"); + + // Color schemes + let color = d3.scaleSequential(d3.interpolateSpectral); + const colors8 = d3.scaleOrdinal(d3.schemeSet2); + const colors20 = d3.scaleOrdinal(d3.schemeCategory20); + + // D3 drag and zoom behavior + let scale = 1, viewX = 0, viewY = 0; + const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomed); + svg.call(zoom); + + // D3 Line generator variables + const lineGen = d3.line().x(function (d) { + return d.scX; + }).y(function (d) { + return d.scY; + }).curve(d3.curveCatmullRom); + + applyStoredOptions(); + let graphWidth = +mapWidthInput.value; // voronoi graph extention, should be stable for each map + let graphHeight = +mapHeightInput.value; + let svgWidth = graphWidth, svgHeight = graphHeight; // svg canvas resolution, can vary for each map + + // toggle off loading screen and on menus + $("#loading, #initial").remove(); + svg.style("background-color", "#000000"); + $("#optionsContainer, #tooltip").show(); + if (localStorage.getItem("disable_click_arrow_tooltip")) { + tooltip.innerHTML = ""; + tooltip.setAttribute("data-main", ""); + $("#optionsTrigger").removeClass("glow"); + } + + $("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"}); + $("#mapLayers").sortable({items: "li:not(.solid)", cancel: ".solid", update: moveLayer}); + $("#templateBody").sortable({items: "div:not(div[data-type='Mountain'])"}); + $("#mapLayers, #templateBody").disableSelection(); + + const drag = d3.drag() + .container(function () { + return this; + }) + .subject(function () { + const p = [d3.event.x, d3.event.y]; + return [p, p]; + }) + .on("start", dragstarted); + + function zoomed() { + const scaleDiff = Math.abs(scale - d3.event.transform.k); + scale = d3.event.transform.k; + viewX = d3.event.transform.x; + viewY = d3.event.transform.y; + viewbox.attr("transform", d3.event.transform); + // rescale only if zoom is significally changed + if (scaleDiff > 0.001) { + invokeActiveZooming(); + drawScaleBar(); + } + } + + // Manually update viewbox + function zoomUpdate(duration) { + const dur = duration || 0; + const transform = d3.zoomIdentity.translate(viewX, viewY).scale(scale); + svg.transition().duration(dur).call(zoom.transform, transform); + } + + // Zoom to specific point (x,y - coods, z - scale, d - duration) + function zoomTo(x, y, z, d) { + const transform = d3.zoomIdentity.translate(x * -z + graphWidth / 2, y * -z + graphHeight / 2).scale(z); + svg.transition().duration(d).call(zoom.transform, transform); + } + + // Reset zoom to initial + function resetZoom(duration) { + zoom.transform(svg, d3.zoomIdentity); + } + + // Active zooming + function invokeActiveZooming() { + // toggle shade/blur filter on zoom + let filter = scale > 2.6 ? "url(#blurFilter)" : "url(#dropShadow)"; + if (scale > 1.5 && scale <= 2.6) filter = null; + coastline.attr("filter", filter); + // rescale lables on zoom (active zooming) + labels.selectAll("g").each(function(d) { + const el = d3.select(this); + if (el.attr("id") === "burgLabels") return; + const desired = +el.attr("data-size"); + let relative = rn((desired + desired / scale) / 2, 2); + if (relative < 2) relative = 2; + el.attr("font-size", relative); + if (hideLabels.checked) { + el.classed("hidden", relative * scale < 6); + updateLabelGroups(); + } + }); + + // rescale map markers + markers.selectAll("use").each(function(d) { + const el = d3.select(this); + let x = +el.attr("data-x"), y = +el.attr("data-y"); + const desired = +el.attr("data-size"); + let size = desired * 5 + 25 / scale; + if (size < 1) size = 1; + el.attr("x", x - size / 2).attr("y", y - size).attr("width", size).attr("height", size); + }); + + if (ruler.size()) { + if (ruler.style("display") !== "none") { + if (ruler.selectAll("g").size() < 1) {return;} + const factor = rn(1 / Math.pow(scale, 0.3), 1); + ruler.selectAll("circle:not(.center)").attr("r", 2 * factor).attr("stroke-width", 0.5 * factor); + ruler.selectAll("circle.center").attr("r", 1.2 * factor).attr("stroke-width", 0.3 * factor); + ruler.selectAll("text").attr("font-size", 10 * factor); + ruler.selectAll("line, path").attr("stroke-width", factor); + } + } + } + + addDragToUpload(); + + // Changelog dialog window + const storedVersion = localStorage.getItem("version"); // show message on load + if (storedVersion != version) { + alertMessage.innerHTML = `2018-29-23: + The Fantasy Map Generator is updated up to version ${version}. + Main changes:

+
  • Map Markers
  • +
  • Legend Editor (text notes)
  • +
  • Bug fixes
  • +
    See a dedicated post for the details. +

    + Join our Reddit community + to share created maps, discuss the Generator, report bugs, ask questions and propose new features. + You may also report bugs here.`; + + $("#alert").dialog( + {resizable: false, title: "Fantasy Map Generator update", width: 320, + buttons: { + "Don't show again": function() { + localStorage.setItem("version", version); + $(this).dialog("close"); + }, + Close: function() {$(this).dialog("close");} + }, + position: {my: "center", at: "center", of: "svg"} + }); + } + + getSeed(); // get and set random generator seed + applyNamesData(); // apply default namesbase on load + generate(); // generate map on load + applyDefaultStyle(); // apply style on load + focusOn(); // based on searchParams focus on point, cell or burg from MFCG + invokeActiveZooming(); // to hide what need to be hidden + + function generate() { + console.group("Random map"); + console.time("TOTAL"); + applyMapSize(); + randomizeOptions(); + placePoints(); + calculateVoronoi(points); + detectNeighbors(); + drawScaleBar(); + defineHeightmap(); + markFeatures(); + drawOcean(); + elevateLakes(); + resolveDepressionsPrimary(); + reGraph(); + resolveDepressionsSecondary(); + flux(); + addLakes(); + drawCoastline(); + drawRelief(); + generateCultures(); + manorsAndRegions(); + cleanData(); + console.timeEnd("TOTAL"); + console.groupEnd("Random map"); + } + + // get or generate map seed + function getSeed() { + const url = new URL(window.location.href); + params = url.searchParams; + seed = params.get("seed") || Math.floor(Math.random() * 1e9); + console.log(" seed: " + seed); + optionsSeed.value = seed; + Math.seedrandom(seed); + } + + // generate new map seed + function changeSeed() { + seed = Math.floor(Math.random() * 1e9); + console.log(" seed: " + seed); + optionsSeed.value = seed; + Math.seedrandom(seed); + } + + function updateURL() { + const url = new URL(window.location.href); + url.searchParams.set("seed", seed); + if (url.protocol !== "file:") window.history.pushState({seed}, "", "url.search"); + } + + // load options from LocalStorage is any + function applyStoredOptions() { + if (localStorage.getItem("mapWidth") && localStorage.getItem("mapHeight")) { + mapWidthInput.value = localStorage.getItem("mapWidth"); + mapHeightInput.value = localStorage.getItem("mapHeight"); + } else { + mapWidthInput.value = window.innerWidth; + mapHeightInput.value = window.innerHeight; + } + if (localStorage.getItem("graphSize")) { + graphSize = localStorage.getItem("graphSize"); + sizeInput.value = sizeOutput.value = graphSize; + } else { + graphSize = +sizeInput.value; + } + if (localStorage.getItem("template")) { + templateInput.value = localStorage.getItem("template"); + lockTemplateInput.setAttribute("data-locked", 1); + lockTemplateInput.className = "icon-lock"; + } + if (localStorage.getItem("manors")) { + manorsInput.value = manorsOutput.value = localStorage.getItem("manors"); + lockManorsInput.setAttribute("data-locked", 1); + lockManorsInput.className = "icon-lock"; + } + if (localStorage.getItem("regions")) { + regionsInput.value = regionsOutput.value = localStorage.getItem("regions"); + lockRegionsInput.setAttribute("data-locked", 1); + lockRegionsInput.className = "icon-lock"; + } + if (localStorage.getItem("power")) { + powerInput.value = powerOutput.value = localStorage.getItem("power"); + lockPowerInput.setAttribute("data-locked", 1); + lockPowerInput.className = "icon-lock"; + } + if (localStorage.getItem("neutral")) neutralInput.value = neutralOutput.value = localStorage.getItem("neutral"); + if (localStorage.getItem("names")) { + namesInput.value = localStorage.getItem("names"); + lockNamesInput.setAttribute("data-locked", 1); + lockNamesInput.className = "icon-lock"; + } + if (localStorage.getItem("cultures")) { + culturesInput.value = culturesOutput.value = localStorage.getItem("cultures"); + lockCulturesInput.setAttribute("data-locked", 1); + lockCulturesInput.className = "icon-lock"; + } + if (localStorage.getItem("prec")) { + precInput.value = precOutput.value = localStorage.getItem("prec"); + lockPrecInput.setAttribute("data-locked", 1); + lockPrecInput.className = "icon-lock"; + } + if (localStorage.getItem("swampiness")) swampinessInput.value = swampinessOutput.value = localStorage.getItem("swampiness"); + if (localStorage.getItem("outlineLayers")) outlineLayersInput.value = localStorage.getItem("outlineLayers"); + if (localStorage.getItem("pngResolution")) { + pngResolutionInput.value = localStorage.getItem("pngResolution"); + pngResolutionOutput.value = pngResolutionInput.value + "x"; + } + if (localStorage.getItem("transparency")) { + transparencyInput.value = transparencyOutput.value = localStorage.getItem("transparency"); + changeDialogsTransparency(transparencyInput.value); + } else {changeDialogsTransparency(0);} + } + + function restoreDefaultOptions() { + // remove ALL saved data from LocalStorage + localStorage.clear(); + // set defaut values + mapWidthInput.value = window.innerWidth; + mapHeightInput.value = window.innerHeight; + changeMapSize(); + graphSize = sizeInput.value = sizeOutput.value = 1; + $("#options i[class^='icon-lock']").each(function() { + this.setAttribute("data-locked", 0); + this.className = "icon-lock-open"; + if (this.id === "lockNeutralInput" || this.id === "lockSwampinessInput") { + this.setAttribute("data-locked", 1); + this.className = "icon-lock"; + } + }); + neutralInput.value = neutralOutput.value = 200; + swampinessInput.value = swampinessOutput.value = 10; + outlineLayersInput.value = "-6,-3,-1"; + transparencyInput.value = transparencyOutput.value = 0; + changeDialogsTransparency(0); + pngResolutionInput.value = 5; + pngResolutionOutput.value = "5x"; + randomizeOptions(); + } + + // apply names data from localStorage if available + function applyNamesData() { + applyDefaultNamesData(); + defaultCultures = [ + {name:"Shwazen", color:"#b3b3b3", base:0}, + {name:"Angshire", color:"#fca463", base:1}, + {name:"Luari", color:"#99acfb", base:2}, + {name:"Tallian", color:"#a6d854", base:3}, + {name:"Toledi", color:"#ffd92f", base:4}, + {name:"Slovian", color:"#e5c494", base:5}, + {name:"Norse", color:"#dca3e4", base:6}, + {name:"Elladian", color:"#66c4a0", base:7}, + {name:"Latian", color:"#ff7174", base:8}, + {name:"Soomi", color:"#85c8fa", base:9}, + {name:"Koryo", color:"#578880", base:10}, + {name:"Hantzu", color:"#becb8d", base:11}, + {name:"Yamoto", color:"#ffd9da", base:12} + ]; + } + + // apply default names data + function applyDefaultNamesData() { + nameBases = [ // min; max; mean; common + {name: "German", method: "let-to-syl", min: 4, max: 11, d: "lt", m: 0.1}, // real: 3; 17; 8.6; 8 + {name: "English", method: "let-to-syl", min: 5, max: 10, d: "", m: 0.3}, // real: 4; 13; 7.9; 8 + {name: "French", method: "let-to-syl", min: 4, max: 10, d: "lns", m: 0.3}, // real: 3; 15; 7.6; 6 + {name: "Italian", method: "let-to-syl", min: 4, max: 11, d: "clrt", m: 0.2}, // real: 4; 14; 7.7; 7 + {name: "Castillian", method: "let-to-syl", min: 4, max: 10, d: "lr", m: 0}, // real: 2; 13; 7.5; 8 + {name: "Ruthenian", method: "let-to-syl", min: 4, max: 9, d: "", m: 0}, // real: 3; 12; 7.1; 7 + {name: "Nordic", method: "let-to-syl", min: 5, max: 9, d: "kln", m: 0.1}, // real: 3; 12; 7.5; 6 + {name: "Greek", method: "let-to-syl", min: 4, max: 10, d: "ls", m: 0.2}, // real: 3; 14; 7.1; 6 + {name: "Roman", method: "let-to-syl", min: 5, max: 10, d: "", m: 1}, // real: 3; 15; 8.0; 7 + {name: "Finnic", method: "let-to-syl", min: 3, max: 10, d: "aktu", m: 0}, // real: 3; 13; 7.5; 6 + {name: "Korean", method: "let-to-syl", min: 5, max: 10, d: "", m: 0}, // real: 3; 13; 6.8; 7 + {name: "Chinese", method: "let-to-syl", min: 5, max: 9, d: "", m: 0}, // real: 4; 11; 6.9; 6 + {name: "Japanese", method: "let-to-syl", min: 3, max: 9, d: "", m: 0} // real: 2; 15; 6.8; 6 + ]; + nameBase = [ + ["Achern","Aichhalden","Aitern","Albbruck","Alpirsbach","Altensteig","Althengstett","Appenweier","Auggen","Wildbad","Badenen","Badenweiler","Baiersbronn","Ballrechten","Bellingen","Berghaupten","Bernau","Biberach","Biederbach","Binzen","Birkendorf","Birkenfeld","Bischweier","Blumberg","Bollen","Bollschweil","Bonndorf","Bosingen","Braunlingen","Breisach","Breisgau","Breitnau","Brigachtal","Buchenbach","Buggingen","Buhl","Buhlertal","Calw","Dachsberg","Dobel","Donaueschingen","Dornhan","Dornstetten","Dottingen","Dunningen","Durbach","Durrheim","Ebhausen","Ebringen","Efringen","Egenhausen","Ehrenkirchen","Ehrsberg","Eimeldingen","Eisenbach","Elzach","Elztal","Emmendingen","Endingen","Engelsbrand","Enz","Enzklosterle","Eschbronn","Ettenheim","Ettlingen","Feldberg","Fischerbach","Fischingen","Fluorn","Forbach","Freiamt","Freiburg","Freudenstadt","Friedenweiler","Friesenheim","Frohnd","Furtwangen","Gaggenau","Geisingen","Gengenbach","Gernsbach","Glatt","Glatten","Glottertal","Gorwihl","Gottenheim","Grafenhausen","Grenzach","Griesbach","Gutach","Gutenbach","Hag","Haiterbach","Hardt","Harmersbach","Hasel","Haslach","Hausach","Hausen","Hausern","Heitersheim","Herbolzheim","Herrenalb","Herrischried","Hinterzarten","Hochenschwand","Hofen","Hofstetten","Hohberg","Horb","Horben","Hornberg","Hufingen","Ibach","Ihringen","Inzlingen","Kandern","Kappel","Kappelrodeck","Karlsbad","Karlsruhe","Kehl","Keltern","Kippenheim","Kirchzarten","Konigsfeld","Krozingen","Kuppenheim","Kussaberg","Lahr","Lauchringen","Lauf","Laufenburg","Lautenbach","Lauterbach","Lenzkirch","Liebenzell","Loffenau","Loffingen","Lorrach","Lossburg","Mahlberg","Malsburg","Malsch","March","Marxzell","Marzell","Maulburg","Monchweiler","Muhlenbach","Mullheim","Munstertal","Murg","Nagold","Neubulach","Neuenburg","Neuhausen","Neuried","Neuweiler","Niedereschach","Nordrach","Oberharmersbach","Oberkirch","Oberndorf","Oberbach","Oberried","Oberwolfach","Offenburg","Ohlsbach","Oppenau","Ortenberg","otigheim","Ottenhofen","Ottersweier","Peterstal","Pfaffenweiler","Pfalzgrafenweiler","Pforzheim","Rastatt","Renchen","Rheinau","Rheinfelden","Rheinmunster","Rickenbach","Rippoldsau","Rohrdorf","Rottweil","Rummingen","Rust","Sackingen","Sasbach","Sasbachwalden","Schallbach","Schallstadt","Schapbach","Schenkenzell","Schiltach","Schliengen","Schluchsee","Schomberg","Schonach","Schonau","Schonenberg","Schonwald","Schopfheim","Schopfloch","Schramberg","Schuttertal","Schwenningen","Schworstadt","Seebach","Seelbach","Seewald","Sexau","Simmersfeld","Simonswald","Sinzheim","Solden","Staufen","Stegen","Steinach","Steinen","Steinmauern","Straubenhardt","Stuhlingen","Sulz","Sulzburg","Teinach","Tiefenbronn","Tiengen","Titisee","Todtmoos","Todtnau","Todtnauberg","Triberg","Tunau","Tuningen","uhlingen","Unterkirnach","Reichenbach","Utzenfeld","Villingen","Villingendorf","Vogtsburg","Vohrenbach","Waldachtal","Waldbronn","Waldkirch","Waldshut","Wehr","Weil","Weilheim","Weisenbach","Wembach","Wieden","Wiesental","Wildberg","Winzeln","Wittlingen","Wittnau","Wolfach","Wutach","Wutoschingen","Wyhlen","Zavelstein"], + ["Abingdon","Albrighton","Alcester","Almondbury","Altrincham","Amersham","Andover","Appleby","Ashboume","Atherstone","Aveton","Axbridge","Aylesbury","Baldock","Bamburgh","Barton","Basingstoke","Berden","Bere","Berkeley","Berwick","Betley","Bideford","Bingley","Birmingham","Blandford","Blechingley","Bodmin","Bolton","Bootham","Boroughbridge","Boscastle","Bossinney","Bramber","Brampton","Brasted","Bretford","Bridgetown","Bridlington","Bromyard","Bruton","Buckingham","Bungay","Burton","Calne","Cambridge","Canterbury","Carlisle","Castleton","Caus","Charmouth","Chawleigh","Chichester","Chillington","Chinnor","Chipping","Chisbury","Cleobury","Clifford","Clifton","Clitheroe","Cockermouth","Coleshill","Combe","Congleton","Crafthole","Crediton","Cuddenbeck","Dalton","Darlington","Dodbrooke","Drax","Dudley","Dunstable","Dunster","Dunwich","Durham","Dymock","Exeter","Exning","Faringdon","Felton","Fenny","Finedon","Flookburgh","Fowey","Frampton","Gateshead","Gatton","Godmanchester","Grampound","Grantham","Guildford","Halesowen","Halton","Harbottle","Harlow","Hatfield","Hatherleigh","Haydon","Helston","Henley","Hertford","Heytesbury","Hinckley","Hitchin","Holme","Hornby","Horsham","Kendal","Kenilworth","Kilkhampton","Kineton","Kington","Kinver","Kirby","Knaresborough","Knutsford","Launceston","Leighton","Lewes","Linton","Louth","Luton","Lyme","Lympstone","Macclesfield","Madeley","Malborough","Maldon","Manchester","Manningtree","Marazion","Marlborough","Marshfield","Mere","Merryfield","Middlewich","Midhurst","Milborne","Mitford","Modbury","Montacute","Mousehole","Newbiggin","Newborough","Newbury","Newenden","Newent","Norham","Northleach","Noss","Oakham","Olney","Orford","Ormskirk","Oswestry","Padstow","Paignton","Penkneth","Penrith","Penzance","Pershore","Petersfield","Pevensey","Pickering","Pilton","Pontefract","Portsmouth","Preston","Quatford","Reading","Redcliff","Retford","Rockingham","Romney","Rothbury","Rothwell","Salisbury","Saltash","Seaford","Seasalter","Sherston","Shifnal","Shoreham","Sidmouth","Skipsea","Skipton","Solihull","Somerton","Southam","Southwark","Standon","Stansted","Stapleton","Stottesdon","Sudbury","Swavesey","Tamerton","Tarporley","Tetbury","Thatcham","Thaxted","Thetford","Thornbury","Tintagel","Tiverton","Torksey","Totnes","Towcester","Tregoney","Trematon","Tutbury","Uxbridge","Wallingford","Wareham","Warenmouth","Wargrave","Warton","Watchet","Watford","Wendover","Westbury","Westcheap","Weymouth","Whitford","Wickwar","Wigan","Wigmore","Winchelsea","Winkleigh","Wiscombe","Witham","Witheridge","Wiveliscombe","Woodbury","Yeovil"], + ["Adon","Aillant","Amilly","Andonville","Ardon","Artenay","Ascheres","Ascoux","Attray","Aubin","Audeville","Aulnay","Autruy","Auvilliers","Auxy","Aveyron","Baccon","Bardon","Barville","Batilly","Baule","Bazoches","Beauchamps","Beaugency","Beaulieu","Beaune","Bellegarde","Boesses","Boigny","Boiscommun","Boismorand","Boisseaux","Bondaroy","Bonnee","Bonny","Bordes","Bou","Bougy","Bouilly","Boulay","Bouzonville","Bouzy","Boynes","Bray","Breteau","Briare","Briarres","Bricy","Bromeilles","Bucy","Cepoy","Cercottes","Cerdon","Cernoy","Cesarville","Chailly","Chaingy","Chalette","Chambon","Champoulet","Chanteau","Chantecoq","Chapell","Charme","Charmont","Charsonville","Chateau","Chateauneuf","Chatel","Chatenoy","Chatillon","Chaussy","Checy","Chevannes","Chevillon","Chevilly","Chevry","Chilleurs","Choux","Chuelles","Clery","Coinces","Coligny","Combleux","Combreux","Conflans","Corbeilles","Corquilleroy","Cortrat","Coudroy","Coullons","Coulmiers","Courcelles","Courcy","Courtemaux","Courtempierre","Courtenay","Cravant","Crottes","Dadonville","Dammarie","Dampierre","Darvoy","Desmonts","Dimancheville","Donnery","Dordives","Dossainville","Douchy","Dry","Echilleuses","Egry","Engenville","Epieds","Erceville","Ervauville","Escrennes","Escrignelles","Estouy","Faverelles","Fay","Feins","Ferolles","Ferrieres","Fleury","Fontenay","Foret","Foucherolles","Freville","Gatinais","Gaubertin","Gemigny","Germigny","Gidy","Gien","Girolles","Givraines","Gondreville","Grangermont","Greneville","Griselles","Guigneville","Guilly","Gyleslonains","Huetre","Huisseau","Ingrannes","Ingre","Intville","Isdes","Jargeau","Jouy","Juranville","Bussiere","Laas","Ladon","Lailly","Langesse","Leouville","Ligny","Lombreuil","Lorcy","Lorris","Loury","Louzouer","Malesherbois","Marcilly","Mardie","Mareau","Marigny","Marsainvilliers","Melleroy","Menestreau","Merinville","Messas","Meung","Mezieres","Migneres","Mignerette","Mirabeau","Montargis","Montbarrois","Montbouy","Montcresson","Montereau","Montigny","Montliard","Mormant","Morville","Moulinet","Moulon","Nancray","Nargis","Nesploy","Neuville","Neuvy","Nevoy","Nibelle","Nogent","Noyers","Ocre","Oison","Olivet","Ondreville","Onzerain","Orleans","Ormes","Orville","Oussoy","Outarville","Ouzouer","Pannecieres","Pannes","Patay","Paucourt","Pers","Pierrefitte","Pithiverais","Pithiviers","Poilly","Potier","Prefontaines","Presnoy","Pressigny","Puiseaux","Quiers","Ramoulu","Rebrechien","Rouvray","Rozieres","Rozoy","Ruan","Sandillon","Santeau","Saran","Sceaux","Seichebrieres","Semoy","Sennely","Sermaises","Sigloy","Solterre","Sougy","Sully","Sury","Tavers","Thignonville","Thimory","Thorailles","Thou","Tigy","Tivernon","Tournoisis","Trainou","Treilles","Trigueres","Trinay","Vannes","Varennes","Vennecy","Vieilles","Vienne","Viglain","Vignes","Villamblain","Villemandeur","Villemoutiers","Villemurlin","Villeneuve","Villereau","Villevoques","Villorceau","Vimory","Vitry","Vrigny","Ivre"], + ["Accumoli","Acquafondata","Acquapendente","Acuto","Affile","Agosta","Alatri","Albano","Allumiere","Alvito","Amaseno","Amatrice","Anagni","Anguillara","Anticoli","Antrodoco","Anzio","Aprilia","Aquino","Arce","Arcinazzo","Ardea","Ariccia","Arlena","Arnara","Arpino","Arsoli","Artena","Ascrea","Atina","Ausonia","Bagnoregio","Barbarano","Bassano","Bassiano","Bellegra","Belmonte","Blera","Bolsena","Bomarzo","Borbona","Borgo","Borgorose","Boville","Bracciano","Broccostella","Calcata","Camerata","Campagnano","Campodimele","Campoli","Canale","Canepina","Canino","Cantalice","Cantalupo","Canterano","Capena","Capodimonte","Capranica","Caprarola","Carbognano","Casalattico","Casalvieri","Casape","Casaprota","Casperia","Cassino","Castelforte","Castelliri","Castello","Castelnuovo","Castiglione","Castro","Castrocielo","Cave","Ceccano","Celleno","Cellere","Ceprano","Cerreto","Cervara","Cervaro","Cerveteri","Ciampino","Ciciliano","Cineto","Cisterna","Cittaducale","Cittareale","Civita","Civitavecchia","Civitella","Colfelice","Collalto","Colle","Colleferro","Collegiove","Collepardo","Collevecchio","Colli","Colonna","Concerviano","Configni","Contigliano","Corchiano","Coreno","Cori","Cottanello","Esperia","Fabrica","Faleria","Falvaterra","Fara","Farnese","Ferentino","Fiamignano","Fiano","Filacciano","Filettino","Fiuggi","Fiumicino","Fondi","Fontana","Fonte","Fontechiari","Forano","Formello","Formia","Frascati","Frasso","Frosinone","Fumone","Gaeta","Gallese","Gallicano","Gallinaro","Gavignano","Genazzano","Genzano","Gerano","Giuliano","Gorga","Gradoli","Graffignano","Greccio","Grottaferrata","Grotte","Guarcino","Guidonia","Ischia","Isola","Itri","Jenne","Labico","Labro","Ladispoli","Lanuvio","Lariano","Latera","Lenola","Leonessa","Licenza","Longone","Lubriano","Maenza","Magliano","Mandela","Manziana","Marano","Marcellina","Marcetelli","Marino","Marta","Mazzano","Mentana","Micigliano","Minturno","Mompeo","Montalto","Montasola","Monte","Montebuono","Montefiascone","Monteflavio","Montelanico","Monteleone","Montelibretti","Montenero","Monterosi","Monterotondo","Montopoli","Montorio","Moricone","Morlupo","Morolo","Morro","Nazzano","Nemi","Nepi","Nerola","Nespolo","Nettuno","Norma","Olevano","Onano","Oriolo","Orte","Orvinio","Paganico","Palestrina","Paliano","Palombara","Pastena","Patrica","Percile","Pescorocchiano","Pescosolido","Petrella","Piansano","Picinisco","Pico","Piedimonte","Piglio","Pignataro","Pisoniano","Pofi","Poggio","Poli","Pomezia","Pontecorvo","Pontinia","Ponza","Ponzano","Posta","Pozzaglia","Priverno","Proceno","Prossedi","Riano","Rieti","Rignano","Riofreddo","Ripi","Rivodutri","Rocca","Roccagiovine","Roccagorga","Roccantica","Roccasecca","Roiate","Ronciglione","Roviano","Sabaudia","Sacrofano","Salisano","Sambuci","Santa","Santi","Santopadre","Saracinesco","Scandriglia","Segni","Selci","Sermoneta","Serrone","Settefrati","Sezze","Sgurgola","Sonnino","Sora","Soriano","Sperlonga","Spigno","Stimigliano","Strangolagalli","Subiaco","Supino","Sutri","Tarano","Tarquinia","Terelle","Terracina","Tessennano","Tivoli","Toffia","Tolfa","Torre","Torri","Torrice","Torricella","Torrita","Trevi","Trevignano","Trivigliano","Turania","Tuscania","Vacone","Valentano","Vallecorsa","Vallemaio","Vallepietra","Vallerano","Vallerotonda","Vallinfreda","Valmontone","Varco","Vasanello","Vejano","Velletri","Ventotene","Veroli","Vetralla","Vicalvi","Vico","Vicovaro","Vignanello","Viterbo","Viticuso","Vitorchiano","Vivaro","Zagarolo"], + ["Abanades","Ablanque","Adobes","Ajofrin","Alameda","Alaminos","Alarilla","Albalate","Albares","Albarreal","Albendiego","Alcabon","Alcanizo","Alcaudete","Alcocer","Alcolea","Alcoroches","Aldea","Aldeanueva","Algar","Algora","Alhondiga","Alique","Almadrones","Almendral","Almoguera","Almonacid","Almorox","Alocen","Alovera","Alustante","Angon","Anguita","Anover","Anquela","Arbancon","Arbeteta","Arcicollar","Argecilla","Arges","Armallones","Armuna","Arroyo","Atanzon","Atienza","Aunon","Azuqueca","Azutan","Baides","Banos","Banuelos","Barcience","Bargas","Barriopedro","Belvis","Berninches","Borox","Brihuega","Budia","Buenaventura","Bujalaro","Burguillos","Burujon","Bustares","Cabanas","Cabanillas","Calera","Caleruela","Calzada","Camarena","Campillo","Camunas","Canizar","Canredondo","Cantalojas","Cardiel","Carmena","Carranque","Carriches","Casa","Casarrubios","Casas","Casasbuenas","Caspuenas","Castejon","Castellar","Castilforte","Castillo","Castilnuevo","Cazalegas","Cebolla","Cedillo","Cendejas","Centenera","Cervera","Checa","Chequilla","Chillaron","Chiloeches","Chozas","Chueca","Cifuentes","Cincovillas","Ciruelas","Ciruelos","Cobeja","Cobeta","Cobisa","Cogollor","Cogolludo","Condemios","Congostrina","Consuegra","Copernal","Corduente","Corral","Cuerva","Domingo","Dosbarrios","Driebes","Duron","El","Embid","Erustes","Escalona","Escalonilla","Escamilla","Escariche","Escopete","Espinosa","Espinoso","Esplegares","Esquivias","Estables","Estriegana","Fontanar","Fuembellida","Fuensalida","Fuentelsaz","Gajanejos","Galve","Galvez","Garciotum","Gascuena","Gerindote","Guadamur","Henche","Heras","Herreria","Herreruela","Hijes","Hinojosa","Hita","Hombrados","Hontanar","Hontoba","Horche","Hormigos","Huecas","Huermeces","Huerta","Hueva","Humanes","Illan","Illana","Illescas","Iniestola","Irueste","Jadraque","Jirueque","Lagartera","Las","Layos","Ledanca","Lillo","Lominchar","Loranca","Los","Lucillos","Lupiana","Luzaga","Luzon","Madridejos","Magan","Majaelrayo","Malaga","Malaguilla","Malpica","Mandayona","Mantiel","Manzaneque","Maqueda","Maranchon","Marchamalo","Marjaliza","Marrupe","Mascaraque","Masegoso","Matarrubia","Matillas","Mazarete","Mazuecos","Medranda","Megina","Mejorada","Mentrida","Mesegar","Miedes","Miguel","Millana","Milmarcos","Mirabueno","Miralrio","Mocejon","Mochales","Mohedas","Molina","Monasterio","Mondejar","Montarron","Mora","Moratilla","Morenilla","Muduex","Nambroca","Navalcan","Negredo","Noblejas","Noez","Nombela","Noves","Numancia","Nuno","Ocana","Ocentejo","Olias","Olmeda","Ontigola","Orea","Orgaz","Oropesa","Otero","Palmaces","Palomeque","Pantoja","Pardos","Paredes","Pareja","Parrillas","Pastrana","Pelahustan","Penalen","Penalver","Pepino","Peralejos","Peralveche","Pinilla","Pioz","Piqueras","Polan","Portillo","Poveda","Pozo","Pradena","Prados","Puebla","Puerto","Pulgar","Quer","Quero","Quintanar","Quismondo","Rebollosa","Recas","Renera","Retamoso","Retiendas","Riba","Rielves","Rillo","Riofrio","Robledillo","Robledo","Romanillos","Romanones","Rueda","Sacecorbo","Sacedon","Saelices","Salmeron","San","Santa","Santiuste","Santo","Sartajada","Sauca","Sayaton","Segurilla","Selas","Semillas","Sesena","Setiles","Sevilleja","Sienes","Siguenza","Solanillos","Somolinos","Sonseca","Sotillo","Sotodosos","Talavera","Tamajon","Taragudo","Taravilla","Tartanedo","Tembleque","Tendilla","Terzaga","Tierzo","Tordellego","Tordelrabano","Tordesilos","Torija","Torralba","Torre","Torrecilla","Torrecuadrada","Torrejon","Torremocha","Torrico","Torrijos","Torrubia","Tortola","Tortuera","Tortuero","Totanes","Traid","Trijueque","Trillo","Turleque","Uceda","Ugena","Ujados","Urda","Utande","Valdarachas","Valdesotos","Valhermoso","Valtablado","Valverde","Velada","Viana","Vinuelas","Yebes","Yebra","Yelamos","Yeles","Yepes","Yuncler","Yunclillos","Yuncos","Yunquera","Zaorejas","Zarzuela","Zorita"], + ["Belgorod","Beloberezhye","Belyi","Belz","Berestiy","Berezhets","Berezovets","Berezutsk","Bobruisk","Bolonets","Borisov","Borovsk","Bozhesk","Bratslav","Bryansk","Brynsk","Buryn","Byhov","Chechersk","Chemesov","Cheremosh","Cherlen","Chern","Chernigov","Chernitsa","Chernobyl","Chernogorod","Chertoryesk","Chetvertnia","Demyansk","Derevesk","Devyagoresk","Dichin","Dmitrov","Dorogobuch","Dorogobuzh","Drestvin","Drokov","Drutsk","Dubechin","Dubichi","Dubki","Dubkov","Dveren","Galich","Glebovo","Glinsk","Goloty","Gomiy","Gorodets","Gorodische","Gorodno","Gorohovets","Goroshin","Gorval","Goryshon","Holm","Horobor","Hoten","Hotin","Hotmyzhsk","Ilovech","Ivan","Izborsk","Izheslavl","Kamenets","Kanev","Karachev","Karna","Kavarna","Klechesk","Klyapech","Kolomyya","Kolyvan","Kopyl","Korec","Kornik","Korochunov","Korshev","Korsun","Koshkin","Kotelno","Kovyla","Kozelsk","Kozelsk","Kremenets","Krichev","Krylatsk","Ksniatin","Kulatsk","Kursk","Kursk","Lebedev","Lida","Logosko","Lomihvost","Loshesk","Loshichi","Lubech","Lubno","Lubutsk","Lutsk","Luchin","Luki","Lukoml","Luzha","Lvov","Mtsensk","Mdin","Medniki","Melecha","Merech","Meretsk","Mescherskoe","Meshkovsk","Metlitsk","Mezetsk","Mglin","Mihailov","Mikitin","Mikulino","Miloslavichi","Mogilev","Mologa","Moreva","Mosalsk","Moschiny","Mozyr","Mstislav","Mstislavets","Muravin","Nemech","Nemiza","Nerinsk","Nichan","Novgorod","Novogorodok","Obolichi","Obolensk","Obolensk","Oleshsk","Olgov","Omelnik","Opoka","Opoki","Oreshek","Orlets","Osechen","Oster","Ostrog","Ostrov","Perelai","Peremil","Peremyshl","Pererov","Peresechen","Perevitsk","Pereyaslav","Pinsk","Ples","Polotsk","Pronsk","Proposhesk","Punia","Putivl","Rechitsa","Rodno","Rogachev","Romanov","Romny","Roslavl","Rostislavl","Rostovets","Rsha","Ruza","Rybchesk","Rylsk","Rzhavesk","Rzhev","Rzhischev","Sambor","Serensk","Serensk","Serpeysk","Shilov","Shuya","Sinech","Sizhka","Skala","Slovensk","Slutsk","Smedin","Sneporod","Snitin","Snovsk","Sochevo","Sokolec","Starica","Starodub","Stepan","Sterzh","Streshin","Sutesk","Svinetsk","Svisloch","Terebovl","Ternov","Teshilov","Teterin","Tiversk","Torchevsk","Toropets","Torzhok","Tripolye","Trubchevsk","Tur","Turov","Usvyaty","Uteshkov","Vasilkov","Velil","Velye","Venev","Venicha","Verderev","Vereya","Veveresk","Viazma","Vidbesk","Vidychev","Voino","Volodimer","Volok","Volyn","Vorobesk","Voronich","Voronok","Vorotynsk","Vrev","Vruchiy","Vselug","Vyatichsk","Vyatka","Vyshegorod","Vyshgorod","Vysokoe","Yagniatin","Yaropolch","Yasenets","Yuryev","Yuryevets","Zaraysk","Zhitomel","Zholvazh","Zizhech","Zubkov","Zudechev","Zvenigorod"], + ["Akureyri","Aldra","Alftanes","Andenes","Austbo","Auvog","Bakkafjordur","Ballangen","Bardal","Beisfjord","Bifrost","Bildudalur","Bjerka","Bjerkvik","Bjorkosen","Bliksvaer","Blokken","Blonduos","Bolga","Bolungarvik","Borg","Borgarnes","Bosmoen","Bostad","Bostrand","Botsvika","Brautarholt","Breiddalsvik","Bringsli","Brunahlid","Budardalur","Byggdakjarni","Dalvik","Djupivogur","Donnes","Drageid","Drangsnes","Egilsstadir","Eiteroga","Elvenes","Engavogen","Ertenvog","Eskifjordur","Evenes","Eyrarbakki","Fagernes","Fallmoen","Fellabaer","Fenes","Finnoya","Fjaer","Fjelldal","Flakstad","Flateyri","Flostrand","Fludir","Gardabær","Gardur","Gimstad","Givaer","Gjeroy","Gladstad","Godoya","Godoynes","Granmoen","Gravdal","Grenivik","Grimsey","Grindavik","Grytting","Hafnir","Halsa","Hauganes","Haugland","Hauknes","Hella","Helland","Hellissandur","Hestad","Higrav","Hnifsdalur","Hofn","Hofsos","Holand","Holar","Holen","Holkestad","Holmavik","Hopen","Hovden","Hrafnagil","Hrisey","Husavik","Husvik","Hvammstangi","Hvanneyri","Hveragerdi","Hvolsvollur","Igeroy","Indre","Inndyr","Innhavet","Innes","Isafjordur","Jarklaustur","Jarnsreykir","Junkerdal","Kaldvog","Kanstad","Karlsoy","Kavosen","Keflavik","Kjelde","Kjerstad","Klakk","Kopasker","Kopavogur","Korgen","Kristnes","Krutoga","Krystad","Kvina","Lande","Laugar","Laugaras","Laugarbakki","Laugarvatn","Laupstad","Leines","Leira","Leiren","Leland","Lenvika","Loding","Lodingen","Lonsbakki","Lopsmarka","Lovund","Luroy","Maela","Melahverfi","Meloy","Mevik","Misvaer","Mornes","Mosfellsbær","Moskenes","Myken","Naurstad","Nesberg","Nesjahverfi","Nesset","Nevernes","Obygda","Ofoten","Ogskardet","Okervika","Oknes","Olafsfjordur","Oldervika","Olstad","Onstad","Oppeid","Oresvika","Orsnes","Orsvog","Osmyra","Overdal","Prestoya","Raudalaekur","Raufarhofn","Reipo","Reykholar","Reykholt","Reykjahlid","Rif","Rinoya","Rodoy","Rognan","Rosvika","Rovika","Salhus","Sanden","Sandgerdi","Sandoker","Sandset","Sandvika","Saudarkrokur","Selfoss","Selsoya","Sennesvik","Setso","Siglufjordur","Silvalen","Skagastrond","Skjerstad","Skonland","Skorvogen","Skrova","Sleneset","Snubba","Softing","Solheim","Solheimar","Sorarnoy","Sorfugloy","Sorland","Sormela","Sorvaer","Sovika","Stamsund","Stamsvika","Stave","Stokka","Stokkseyri","Storjord","Storo","Storvika","Strand","Straumen","Strendene","Sudavik","Sudureyri","Sundoya","Sydalen","Thingeyri","Thorlakshofn","Thorshofn","Tjarnabyggd","Tjotta","Tosbotn","Traelnes","Trofors","Trones","Tverro","Ulvsvog","Unnstad","Utskor","Valla","Vandved","Varmahlid","Vassos","Vevelstad","Vidrek","Vik","Vikholmen","Vogar","Vogehamn","Vopnafjordur"], + ["Abdera","Abila","Abydos","Acanthus","Acharnae","Actium","Adramyttium","Aegae","Aegina","Aegium","Aenus","Agrinion","Aigosthena","Akragas","Akrai","Akrillai","Akroinon","Akrotiri","Alalia","Alexandreia","Alexandretta","Alexandria","Alinda","Amarynthos","Amaseia","Ambracia","Amida","Amisos","Amnisos","Amphicaea","Amphigeneia","Amphipolis","Amphissa","Ankon","Antigona","Antipatrea","Antioch","Antioch","Antiochia","Andros","Apamea","Aphidnae","Apollonia","Argos","Arsuf","Artanes","Artemita","Argyroupoli","Asine","Asklepios","Aspendos","Assus","Astacus","Athenai","Athmonia","Aytos","Ancient","Baris","Bhrytos","Borysthenes","Berge","Boura","Bouthroton","Brauron","Byblos","Byllis","Byzantium","Bythinion","Callipolis","Cebrene","Chalcedon","Calydon","Carystus","Chamaizi","Chalcis","Chersonesos","Chios","Chytri","Clazomenae","Cleonae","Cnidus","Colosse","Corcyra","Croton","Cyme","Cyrene","Cythera","Decelea","Delos","Delphi","Demetrias","Dicaearchia","Dimale","Didyma","Dion","Dioscurias","Dodona","Dorylaion","Dyme","Edessa","Elateia","Eleusis","Eleutherna","Emporion","Ephesus","Ephyra","Epidamnos","Epidauros","Eresos","Eretria","Erythrae","Eubea","Gangra","Gaza","Gela","Golgi","Gonnos","Gorgippia","Gournia","Gortyn","Gythium","Hagios","Hagia","Halicarnassus","Halieis","Helike","Heliopolis","Hellespontos","Helorus","Hemeroskopeion","Heraclea","Hermione","Hermonassa","Hierapetra","Hierapolis","Himera","Histria","Hubla","Hyele","Ialysos","Iasus","Idalium","Imbros","Iolcus","Itanos","Ithaca","Juktas","Kallipolis","Kamares","Kameiros","Kannia","Kamarina","Kasmenai","Katane","Kerkinitida","Kepoi","Kimmerikon","Kios","Klazomenai","Knidos","Knossos","Korinthos","Kos","Kourion","Kume","Kydonia","Kynos","Kyrenia","Lamia","Lampsacus","Laodicea","Lapithos","Larissa","Lato","Laus","Lebena","Lefkada","Lekhaion","Leibethra","Leontinoi","Lepreum","Lessa","Lilaea","Lindus","Lissus","Epizephyrian","Madytos","Magnesia","Mallia","Mantineia","Marathon","Marmara","Maroneia","Masis","Massalia","Megalopolis","Megara","Mesembria","Messene","Metapontum","Methana","Methone","Methumna","Miletos","Misenum","Mochlos","Monastiraki","Morgantina","Mulai","Mukenai","Mylasa","Myndus","Myonia","Myra","Myrmekion","Mutilene","Myos","Nauplios","Naucratis","Naupactus","Naxos","Neapoli","Neapolis","Nemea","Nicaea","Nicopolis","Nirou","Nymphaion","Nysa","Oenoe","Oenus","Odessos","Olbia","Olous","Olympia","Olynthus","Opus","Orchomenus","Oricos","Orestias","Oreus","Oropus","Onchesmos","Pactye","Pagasae","Palaikastro","Pandosia","Panticapaeum","Paphos","Parium","Paros","Parthenope","Patrae","Pavlopetri","Pegai","Pelion","Peiraieús","Pella","Percote","Pergamum","Petsofa","Phaistos","Phaleron","Phanagoria","Pharae","Pharnacia","Pharos","Phaselis","Philippi","Pithekussa","Philippopolis","Platanos","Phlius","Pherae","Phocaea","Pinara","Pisa","Pitane","Pitiunt","Pixous","Plataea","Poseidonia","Potidaea","Priapus","Priene","Prousa","Pseira","Psychro","Pteleum","Pydna","Pylos","Pyrgos","Rhamnus","Rhegion","Rhithymna","Rhodes","Rhypes","Rizinia","Salamis","Same","Samos","Scyllaeum","Selinus","Seleucia","Semasus","Sestos","Scidrus","Sicyon","Side","Sidon","Siteia","Sinope","Siris","Sklavokampos","Smyrna","Soli","Sozopolis","Sparta","Stagirus","Stratos","Stymphalos","Sybaris","Surakousai","Taras","Tanagra","Tanais","Tauromenion","Tegea","Temnos","Tenedos","Tenea","Teos","Thapsos","Thassos","Thebai","Theodosia","Therma","Thespiae","Thronion","Thoricus","Thurii","Thyreum","Thyria","Tiruns","Tithoraea","Tomis","Tragurion","Trapeze","Trapezus","Tripolis","Troizen","Troliton","Troy","Tylissos","Tyras","Tyros","Tyritake","Vasiliki","Vathypetros","Zakynthos","Zakros","Zankle"], + ["Abila","Adflexum","Adnicrem","Aelia","Aelius","Aeminium","Aequum","Agrippina","Agrippinae","Ala","Albanianis","Ambianum","Andautonia","Apulum","Aquae","Aquaegranni","Aquensis","Aquileia","Aquincum","Arae","Argentoratum","Ariminum","Ascrivium","Atrebatum","Atuatuca","Augusta","Aurelia","Aurelianorum","Batavar","Batavorum","Belum","Biriciana","Blestium","Bonames","Bonna","Bononia","Borbetomagus","Bovium","Bracara","Brigantium","Burgodunum","Caesaraugusta","Caesarea","Caesaromagus","Calleva","Camulodunum","Cannstatt","Cantiacorum","Capitolina","Castellum","Castra","Castrum","Cibalae","Clausentum","Colonia","Concangis","Condate","Confluentes","Conimbriga","Corduba","Coria","Corieltauvorum","Corinium","Coriovallum","Cornoviorum","Danum","Deva","Divodurum","Dobunnorum","Drusi","Dubris","Dumnoniorum","Durnovaria","Durocobrivis","Durocornovium","Duroliponte","Durovernum","Durovigutum","Eboracum","Edetanorum","Emerita","Emona","Euracini","Faventia","Flaviae","Florentia","Forum","Gerulata","Gerunda","Glevensium","Hadriani","Herculanea","Isca","Italica","Iulia","Iuliobrigensium","Iuvavum","Lactodurum","Lagentium","Lauri","Legionis","Lemanis","Lentia","Lepidi","Letocetum","Lindinis","Lindum","Londinium","Lopodunum","Lousonna","Lucus","Lugdunum","Luguvalium","Lutetia","Mancunium","Marsonia","Martius","Massa","Matilo","Mattiacorum","Mediolanum","Mod","Mogontiacum","Moridunum","Mursa","Naissus","Nervia","Nida","Nigrum","Novaesium","Noviomagus","Olicana","Ovilava","Parisiorum","Partiscum","Paterna","Pistoria","Placentia","Pollentia","Pomaria","Pons","Portus","Praetoria","Praetorium","Pullum","Ragusium","Ratae","Raurica","Regina","Regium","Regulbium","Rigomagus","Roma","Romula","Rutupiae","Salassorum","Salernum","Salona","Scalabis","Segovia","Silurum","Sirmium","Siscia","Sorviodurum","Sumelocenna","Tarraco","Taurinorum","Theranda","Traiectum","Treverorum","Tungrorum","Turicum","Ulpia","Valentia","Venetiae","Venta","Verulamium","Vesontio","Vetera","Victoriae","Victrix","Villa","Viminacium","Vindelicorum","Vindobona","Vinovia","Viroconium"], + ["Aanekoski","Abjapaluoja","Ahlainen","Aholanvaara","Ahtari","Aijala","Aimala","Akaa","Alajarvi","Alatornio","Alavus","Antsla","Aspo","Bennas","Bjorkoby","Elva","Emasalo","Espoo","Esse","Evitskog","Forssa","Haapajarvi","Haapamaki","Haapavesi","Haapsalu","Haavisto","Hameenlinna","Hameenmaki","Hamina","Hanko","Harjavalta","Hattuvaara","Haukipudas","Hautajarvi","Havumaki","Heinola","Hetta","Hinkabole","Hirmula","Hossa","Huittinen","Husula","Hyryla","Hyvinkaa","Iisalmi","Ikaalinen","Ilmola","Imatra","Inari","Iskmo","Itakoski","Jamsa","Jarvenpaa","Jeppo","Jioesuu","Jiogeva","Joensuu","Jokela","Jokikyla","Jokisuu","Jormua","Juankoski","Jungsund","Jyvaskyla","Kaamasmukka","Kaarina","Kajaani","Kalajoki","Kallaste","Kankaanpaa","Kannus","Kardla","Karesuvanto","Karigasniemi","Karkkila","Karkku","Karksinuia","Karpankyla","Kaskinen","Kasnas","Kauhajoki","Kauhava","Kauniainen","Kauvatsa","Kehra","Keila","Kellokoski","Kelottijarvi","Kemi","Kemijarvi","Kerava","Keuruu","Kiikka","Kiipu","Kilinginiomme","Kiljava","Kilpisjarvi","Kitee","Kiuruvesi","Kivesjarvi","Kiviioli","Kivisuo","Klaukkala","Klovskog","Kohtlajarve","Kokemaki","Kokkola","Kolho","Koria","Koskue","Kotka","Kouva","Kouvola","Kristiina","Kaupunki","Kuhmo","Kunda","Kuopio","Kuressaare","Kurikka","Kusans","Kuusamo","Kylmalankyla","Lahti","Laitila","Lankipohja","Lansikyla","Lappeenranta","Lapua","Laurila","Lautiosaari","Lepsama","Liedakkala","Lieksa","Lihula","Littoinen","Lohja","Loimaa","Loksa","Loviisa","Luohuanylipaa","Lusi","Maardu","Maarianhamina","Malmi","Mantta","Masaby","Masala","Matasvaara","Maula","Miiluranta","Mikkeli","Mioisakula","Munapirtti","Mustvee","Muurahainen","Naantali","Nappa","Narpio","Nickby","Niinimaa","Niinisalo","Nikkila","Nilsia","Nivala","Nokia","Nummela","Nuorgam","Nurmes","Nuvvus","Obbnas","Oitti","Ojakkala","Ollola","onningeby","Orimattila","Orivesi","Otanmaki","Otava","Otepaa","Oulainen","Oulu","Outokumpu","Paavola","Paide","Paimio","Pakankyla","Paldiski","Parainen","Parkano","Parkumaki","Parola","Perttula","Pieksamaki","Pietarsaari","Pioltsamaa","Piolva","Pohjavaara","Porhola","Pori","Porrasa","Porvoo","Pudasjarvi","Purmo","Pussi","Pyhajarvi","Raahe","Raasepori","Raisio","Rajamaki","Rakvere","Rapina","Rapla","Rauma","Rautio","Reposaari","Riihimaki","Rovaniemi","Roykka","Ruonala","Ruottala","Rutalahti","Saarijarvi","Salo","Sastamala","Saue","Savonlinna","Seinajoki","Sillamae","Sindi","Siuntio","Somero","Sompujarvi","Suonenjoki","Suurejaani","Syrjantaka","Tampere","Tamsalu","Tapa","Temmes","Tiorva","Tormasenvaara","Tornio","Tottijarvi","Tulppio","Turenki","Turi","Tuukkala","Tuurala","Tuuri","Tuuski","Ulvila","Unari","Upinniemi","Utti","Uusikaarlepyy","Uusikaupunki","Vaaksy","Vaalimaa","Vaarinmaja","Vaasa","Vainikkala","Valga","Valkeakoski","Vantaa","Varkaus","Vehkapera","Vehmasmaki","Vieki","Vierumaki","Viitasaari","Viljandi","Vilppula","Viohma","Vioru","Virrat","Ylike","Ylivieska","Ylojarvi"], + ["Sabi","Wiryeseong","Hwando","Gungnae","Ungjin","Wanggeomseong","Ganggyeong","Jochiwon","Cheorwon","Beolgyo","Gangjin","Gampo","Yecheon","Geochang","Janghang","Hadong","Goseong","Yeongdong","Yesan","Sintaein","Geumsan","Boseong","Jangheung","Uiseong","Jumunjin","Janghowon","Hongseong","Gimhwa","Gwangcheon","Guryongpo","Jinyeong","Buan","Damyang","Jangseong","Wando","Angang","Okcheon","Jeungpyeong","Waegwan","Cheongdo","Gwangyang","Gochang","Haenam","Yeonggwang","Hanam","Eumseong","Daejeong","Hanrim","Samrye","Yongjin","Hamyang","Buyeo","Changnyeong","Yeongwol","Yeonmu","Gurye","Hwasun","Hampyeong","Namji","Samnangjin","Dogye","Hongcheon","Munsan","Gapyeong","Ganghwa","Geojin","Sangdong","Jeongseon","Sabuk","Seonghwan","Heunghae","Hapdeok","Sapgyo","Taean","Boeun","Geumwang","Jincheon","Bongdong","Doyang","Geoncheon","Pungsan","Punggi","Geumho","Wonju","Gaun","Hayang","Yeoju","Paengseong","Yeoncheon","Yangpyeong","Ganseong","Yanggu","Yangyang","Inje","Galmal","Pyeongchang","Hwacheon","Hoengseong","Seocheon","Cheongyang","Goesan","Danyang","Hamyeol","Muju","Sunchang","Imsil","Jangsu","Jinan","Goheung","Gokseong","Muan","Yeongam","Jindo","Seonsan","Daegaya","Gunwi","Bonghwa","Seongju","Yeongdeok","Yeongyang","Ulleung","Uljin","Cheongsong","wayang","Namhae","Sancheong","Uiryeong","Gaya","Hapcheon","Wabu","Dongsong","Sindong","Wondeok","Maepo","Anmyeon","Okgu","Sariwon","Dolsan","Daedeok","Gwansan","Geumil","Nohwa","Baeksu","Illo","Jido","Oedong","Ocheon","Yeonil","Hamchang","Pyeonghae","Gijang","Jeonggwan","Aewor","Gujwa","Seongsan","Jeongok","Seonggeo","Seungju","Hongnong","Jangan","Jocheon","Gohan","Jinjeop","Bubal","Beobwon","Yeomchi","Hwado","Daesan","Hwawon","Apo","Nampyeong","Munsan","Sinbuk","Munmak","Judeok","Bongyang","Ungcheon","Yugu","Unbong","Mangyeong","Dong","Naeseo","Sanyang","Soheul","Onsan","Eonyang","Nongong","Dasa","Goa","Jillyang","Bongdam","Naesu","Beomseo","Opo","Gongdo","Jingeon","Onam","Baekseok","Jiksan","Mokcheon","Jori","Anjung","Samho","Ujeong","Buksam","Tongjin","Chowol","Gonjiam","Pogok","Seokjeok","Poseung","Ochang","Hyangnam","Baebang","Gochon","Songak","Samhyang","Yangchon","Osong","Aphae","Ganam","Namyang","Chirwon","Andong","Ansan","Anseong","Anyang","Asan","Boryeong","Bucheon","Busan","Changwon","Cheonan","Cheongju","Chuncheon","Chungju","Daegu","Daejeon","Dangjin","Dongducheon","Donghae","Gangneung","Geoje","Gimcheon","Gimhae","Gimje","Gimpo","Gongju","Goyang","Gumi","Gunpo","Gunsan","Guri","Gwacheon","Gwangju","Gwangju","Gwangmyeong","Gyeongju","Gyeongsan","Gyeryong","Hwaseong","Icheon","Iksan","Incheon","Jecheon","Jeongeup","Jeonju","Jeju","Jinju","Naju","Namyangju","Namwon","Nonsan","Miryang","Mokpo","Mungyeong","Osan","Paju","Pocheon","Pohang","Pyeongtaek","Sacheon","Sangju","Samcheok","Sejong","Seogwipo","Seongnam","Seosan","Seoul","Siheung","Sokcho","Suncheon","Suwon","Taebaek","Tongyeong","Uijeongbu","Uiwang","Ulsan","Yangju","Yangsan","Yeongcheon","Yeongju","Yeosu","Yongin","Chungmu","Daecheon","Donggwangyang","Geumseong","Gyeongseong","Iri","Jangseungpo","Jeomchon","Jeongju","Migeum","Onyang","Samcheonpo","Busan","Busan","Cheongju","Chuncheon","Daegu","Daegu","Daejeon","Daejeon","Gunsan","Gwangju","Gwangju","Gyeongseong","Incheon","Incheon","Iri","Jeonju","Jinhae","Jinju","Masan","Masan","Mokpo","Songjeong","Songtan","Ulsan","Yeocheon","Cheongjin","Gaeseong","Haeju","Hamheung","Heungnam","Jinnampo","Najin","Pyeongyang","Seongjin","Sineuiju","Songnim","Wonsan"], + ["Anding","Anlu","Anqing","Anshun","Baan","Baixing","Banyang","Baoding","Baoqing","Binzhou","Caozhou","Changbai","Changchun","Changde","Changling","Changsha","Changtu","Changzhou","Chaozhou","Cheli","Chengde","Chengdu","Chenzhou","Chizhou","Chongqing","Chuxiong","Chuzhou","Dading","Dali","Daming","Datong","Daxing","Dean","Dengke","Dengzhou","Deqing","Dexing","Dihua","Dingli","Dongan","Dongchang","Dongchuan","Dongping","Duyun","Fengtian","Fengxiang","Fengyang","Fenzhou","Funing","Fuzhou","Ganzhou","Gaoyao","Gaozhou","Gongchang","Guangnan","Guangning","Guangping","Guangxin","Guangzhou","Guide","Guilin","Guiyang","Hailong","Hailun","Hangzhou","Hanyang","Hanzhong","Heihe","Hejian","Henan","Hengzhou","Hezhong","Huaian","Huaide","Huaiqing","Huanglong","Huangzhou","Huining","Huizhou","Hulan","Huzhou","Jiading","Jian","Jianchang","Jiande","Jiangning","Jiankang","Jianning","Jiaxing","Jiayang","Jilin","Jinan","Jingjiang","Jingzhao","Jingzhou","Jinhua","Jinzhou","Jiujiang","Kaifeng","Kaihua","Kangding","Kuizhou","Laizhou","Lanzhou","Leizhou","Liangzhou","Lianzhou","Liaoyang","Lijiang","Linan","Linhuang","Linjiang","Lintao","Liping","Liuzhou","Longan","Longjiang","Longqing","Longxing","Luan","Lubin","Lubin","Luzhou","Mishan","Nanan","Nanchang","Nandian","Nankang","Nanning","Nanyang","Nenjiang","Ningan","Ningbo","Ningguo","Ninguo","Ningwu","Ningxia","Ningyuan","Pingjiang","Pingle","Pingliang","Pingyang","Puer","Puzhou","Qianzhou","Qingyang","Qingyuan","Qingzhou","Qiongzhou","Qujing","Quzhou","Raozhou","Rende","Ruian","Ruizhou","Runing","Shafeng","Shajing","Shaoqing","Shaowu","Shaoxing","Shaozhou","Shinan","Shiqian","Shouchun","Shuangcheng","Shulei","Shunde","Shunqing","Shuntian","Shuoping","Sicheng","Sien","Sinan","Sizhou","Songjiang","Suiding","Suihua","Suining","Suzhou","Taian","Taibei","Tainan","Taiping","Taiwan","Taiyuan","Taizhou","Taonan","Tengchong","Tieli","Tingzhou","Tongchuan","Tongqing","Tongren","Tongzhou","Weihui","Wensu","Wenzhou","Wuchang","Wuding","Wuzhou","Xian","Xianchun","Xianping","Xijin","Xiliang","Xincheng","Xingan","Xingde","Xinghua","Xingjing","Xingqing","Xingyi","Xingyuan","Xingzhong","Xining","Xinmen","Xiping","Xuanhua","Xunzhou","Xuzhou","Yanan","Yangzhou","Yanji","Yanping","Yanqi","Yanzhou","Yazhou","Yichang","Yidu","Yilan","Yili","Yingchang","Yingde","Yingtian","Yingzhou","Yizhou","Yongchang","Yongping","Yongshun","Yongzhou","Yuanzhou","Yuezhou","Yulin","Yunnan","Yunyang","Zezhou","Zhangde","Zhangzhou","Zhaoqing","Zhaotong","Zhenan","Zhending","Zhengding","Zhenhai","Zhenjiang","Zhenxi","Zhenyun","Zhongshan","Zunyi"], + ["Nanporo","Naie","Kamisunagawa","Yuni","Naganuma","Kuriyama","Tsukigata","Urausu","Shintotsukawa","Moseushi","Chippubetsu","Uryu","Hokuryu","Numata","Tobetsu","Suttsu","Kuromatsunai","Rankoshi","Niseko","Kimobetsu","Kyogoku","Kutchan","Kyowa","Iwanai","Shakotan","Furubira","Niki","Yoichi","Toyoura","Toyako","Sobetsu","Shiraoi","Atsuma","Abira","Mukawa","Hidaka","Biratori","Niikappu","Urakawa","Samani","Erimo","Shinhidaka","Matsumae","Fukushima","Shiriuchi","Kikonai","Nanae","Shikabe","Mori","Yakumo","Oshamambe","Esashi","Kaminokuni","Assabu","Otobe","Okushiri","Imakane","Setana","Takasu","Higashikagura","Toma","Pippu","Aibetsu","Kamikawa","Higashikawa","Biei","Kamifurano","Nakafurano","Minamifurano","Horokanai","Wassamu","Kenbuchi","Shimokawa","Bifuka","Nakagawa","Mashike","Obira","Tomamae","Haboro","Enbetsu","Teshio","Hamatonbetsu","Nakatonbetsu","Esashi","Toyotomi","Horonobe","Rebun","Rishiri","Rishirifuji","Bihoro","Tsubetsu","Ozora","Shari","Kiyosato","Koshimizu","Kunneppu","Oketo","Saroma","Engaru","Yubetsu","Takinoue","Okoppe","Omu","Otofuke","Shihoro","Kamishihoro","Shikaoi","Shintoku","Shimizu","Memuro","Taiki","Hiroo","Makubetsu","Ikeda","Toyokoro","Honbetsu","Ashoro","Rikubetsu","Urahoro","Kushiro","Akkeshi","Hamanaka","Shibecha","Teshikaga","Shiranuka","Betsukai","Nakashibetsu","Shibetsu","Rausu","Hiranai","Imabetsu","Sotogahama","Ajigasawa","Fukaura","Fujisaki","Owani","Itayanagi","Tsuruta","Nakadomari","Noheji","Shichinohe","Rokunohe","Yokohama","Tohoku","Oirase","Oma","Sannohe","Gonohe","Takko","Nanbu","Hashikami","Shizukuishi","Kuzumaki","Iwate","Shiwa","Yahaba","Nishiwaga","Kanegasaki","Hiraizumi","Sumita","Otsuchi","Yamada","Iwaizumi","Karumai","Hirono","Ichinohe","Zao","Shichikashuku","Ogawara","Murata","Shibata","Kawasaki","Marumori","Watari","Yamamoto","Matsushima","Shichigahama","Rifu","Taiwa","Osato","Shikama","Kami","Wakuya","Misato","Onagawa","Minamisanriku","Kosaka","Fujisato","Mitane","Happo","Gojome","Hachirogata","Ikawa","Misato","Ugo","Yamanobe","Nakayama","Kahoku","Nishikawa","Asahi","Oe","Oishida","Kaneyama","Mogami","Funagata","Mamurogawa","Takahata","Kawanishi","Oguni","Shirataka","Iide","Mikawa","Shonai","Yuza","Koori","Kunimi","Kawamata","Kagamiishi","Shimogo","Tadami","Minamiaizu","Nishiaizu","Bandai","Inawashiro","Aizubange","Yanaizu","Mishima","Kaneyama","Aizumisato","Yabuki","Tanagura","Yamatsuri","Hanawa","Ishikawa","Asakawa","Furudono","Miharu","Ono","Hirono","Naraha","Tomioka","Okuma","Futaba","Namie","Shinchi","Ibaraki","Oarai","Shirosato","Daigo","Ami","Kawachi","Yachiyo","Goka","Sakai","Tone","Kaminokawa","Mashiko","Motegi","Ichikai","Haga","Mibu","Nogi","Shioya","Takanezawa","Nasu","Nakagawa","Yoshioka","Kanna","Shimonita","Kanra","Nakanojo","Naganohara","Kusatsu","Higashiagatsuma","Minakami","Tamamura","Itakura","Meiwa","Chiyoda","Oizumi","Ora","Ina","Miyoshi","Moroyama","Ogose","Namegawa","Ranzan","Ogawa","Kawajima","Yoshimi","Hatoyama","Tokigawa","Yokoze","Minano","Nagatoro","Ogano","Misato","Kamikawa","Kamisato","Yorii","Miyashiro","Sugito","Matsubushi","Shisui","Sakae","Kozaki","Tako","Tonosho","Kujukuri","Shibayama","Yokoshibahikari","Ichinomiya","Mutsuzawa","Shirako","Nagara","Chonan","Otaki","Onjuku","Kyonan","Mizuho","Hinode","Okutama","Oshima","Hachijo","Aikawa","Hayama","Samukawa","Oiso","Ninomiya","Nakai","Oi","Matsuda","Yamakita","Kaisei","Hakone","Manazuru","Yugawara","Seiro","Tagami","Aga","Izumozaki","Yuzawa","Tsunan","Kamiichi","Tateyama","Nyuzen","Asahi","Kawakita","Tsubata","Uchinada","Shika","Hodatsushimizu","Nakanoto","Anamizu","Noto","Eiheiji","Ikeda","Minamiechizen","Echizen","Mihama","Takahama","Oi","Wakasa","Ichikawamisato","Hayakawa","Minobu","Nanbu","Fujikawa","Showa","Nishikatsura","Fujikawaguchiko","Koumi","Sakuho","Karuizawa","Miyota","Tateshina","Nagawa","Shimosuwa","Fujimi","Tatsuno","Minowa","Iijima","Matsukawa","Takamori","Anan","Agematsu","Nagiso","Kiso","Ikeda","Sakaki","Obuse","Yamanouchi","Shinano","Iizuna","Ginan","Kasamatsu","Yoro","Tarui","Sekigahara","Godo","Wanouchi","Anpachi","Ibigawa","Ono","Ikeda","Kitagata","Sakahogi","Tomika","Kawabe","Hichiso","Yaotsu","Shirakawa","Mitake","Higashiizu","Kawazu","Minamiizu","Matsuzaki","Nishiizu","Kannami","Shimizu","Nagaizumi","Oyama","Yoshida","Kawanehon","Mori","Togo","Toyoyama","Oguchi","Fuso","Oharu","Kanie","Agui","Higashiura","Minamichita","Mihama","Taketoyo","Mihama","Kota","Shitara","Toei","Kisosaki","Toin","Komono","Asahi","Kawagoe","Taki","Meiwa","Odai","Tamaki","Watarai","Taiki","Minamiise","Kihoku","Mihama","Kiho","Hino","Ryuo","Aisho","Toyosato","Kora","Taga","Oyamazaki","Kumiyama","Ide","Ujitawara","Kasagi","Wazuka","Seika","Kyotamba","Ine","Yosano","Shimamoto","Toyono","Nose","Tadaoka","Kumatori","Tajiri","Misaki","Taishi","Kanan","Inagawa","Taka","Inami","Harima","Ichikawa","Fukusaki","Kamikawa","Taishi","Kamigori","Sayo","Kami","Shinonsen","Heguri","Sango","Ikaruga","Ando","Kawanishi","Miyake","Tawaramoto","Takatori","Kanmaki","Oji","Koryo","Kawai","Yoshino","Oyodo","Shimoichi","Kushimoto","Kimino","Katsuragi","Kudoyama","Koya","Yuasa","Hirogawa","Aridagawa","Mihama","Hidaka","Yura","Inami","Minabe","Hidakagawa","Shirahama","Kamitonda","Susami","Nachikatsuura","Taiji","Kozagawa","Iwami","Wakasa","Chizu","Yazu","Misasa","Yurihama","Kotoura","Hokuei","Daisen","Nanbu","Hoki","Nichinan","Hino","Kofu","Okuizumo","Iinan","Kawamoto","Misato","Onan","Tsuwano","Yoshika","Ama","Nishinoshima","Okinoshima","Wake","Hayashima","Satosho","Yakage","Kagamino","Shoo","Nagi","Kumenan","Misaki","Kibichuo","Fuchu","Kaita","Kumano","Saka","Kitahiroshima","Akiota","Osakikamijima","Sera","Jinsekikogen","Suooshima","Waki","Kaminoseki","Tabuse","Hirao","Abu","Katsuura","Kamikatsu","Ishii","Kamiyama","Naka","Mugi","Minami","Kaiyo","Matsushige","Kitajima","Aizumi","Itano","Kamiita","Tsurugi","Higashimiyoshi","Tonosho","Shodoshima","Miki","Naoshima","Utazu","Ayagawa","Kotohira","Tadotsu","Manno","Kamijima","Kumakogen","Masaki","Tobe","Uchiko","Ikata","Kihoku","Matsuno","Ainan","Toyo","Nahari","Tano","Yasuda","Motoyama","Otoyo","Tosa","Ino","Niyodogawa","Nakatosa","Sakawa","Ochi","Yusuhara","Tsuno","Shimanto","Otsuki","Kuroshio","Nakagawa","Umi","Sasaguri","Shime","Sue","Shingu","Hisayama","Kasuya","Ashiya","Mizumaki","Okagaki","Onga","Kotake","Kurate","Keisen","Chikuzen","Tachiarai","Oki","Hirokawa","Kawara","Soeda","Itoda","Kawasaki","Oto","Fukuchi","Kanda","Miyako","Yoshitomi","Koge","Chikujo","Yoshinogari","Kiyama","Kamimine","Miyaki","Genkai","Arita","Omachi","Kohoku","Shiroishi","Tara","Nagayo","Togitsu","Higashisonogi","Kawatana","Hasami","Ojika","Saza","Shinkamigoto","Misato","Gyokuto","Nankan","Nagasu","Nagomi","Ozu","Kikuyo","Minamioguni","Oguni","Takamori","Mifune","Kashima","Mashiki","Kosa","Yamato","Hikawa","Ashikita","Tsunagi","Nishiki","Taragi","Yunomae","Asagiri","Reihoku","Hiji","Kusu","Kokonoe","Mimata","Takaharu","Kunitomi","Aya","Takanabe","Shintomi","Kijo","Kawaminami","Tsuno","Kadogawa","Misato","Takachiho","Hinokage","Gokase","Satsuma","Nagashima","Yusui","Osaki","Higashikushira","Kinko","Minamiosumi","Kimotsuki","Nakatane","Minamitane","Yakushima","Setouchi","Tatsugo","Kikai","Tokunoshima","Amagi","Isen","Wadomari","China","Yoron","Motobu","Kin","Kadena","Chatan","Nishihara","Yonabaru","Haebaru","Kumejima","Yaese","Taketomi","Yonaguni"] + ]; + } + + // randomize options if randomization is allowed in option + function randomizeOptions() { + const mod = rn((graphWidth + graphHeight) / 1500, 2); // add mod for big screens + if (lockRegionsInput.getAttribute("data-locked") == 0) regionsInput.value = regionsOutput.value = rand(7, 17); + if (lockManorsInput.getAttribute("data-locked") == 0) { + const manors = regionsInput.value * 20 + rand(180 * mod); + manorsInput.value = manorsOutput.innerHTML = manors; + } + if (lockPowerInput.getAttribute("data-locked") == 0) powerInput.value = powerOutput.value = rand(2, 8); + if (lockNeutralInput.getAttribute("data-locked") == 0) neutralInput.value = neutralOutput.value = rand(100, 300); + if (lockNamesInput.getAttribute("data-locked") == 0) namesInput.value = rand(0, 1); + if (lockCulturesInput.getAttribute("data-locked") == 0) culturesInput.value = culturesOutput.value = rand(5, 10); + if (lockPrecInput.getAttribute("data-locked") == 0) precInput.value = precOutput.value = rand(3, 12); + if (lockSwampinessInput.getAttribute("data-locked") == 0) swampinessInput.value = swampinessOutput.value = rand(100); + } + + // Locate points to calculate Voronoi diagram + function placePoints() { + console.time("placePoints"); + points = []; + points = getJitteredGrid(); + heights = new Uint8Array(points.length); + console.timeEnd("placePoints"); + } + + // Calculate Voronoi Diagram + function calculateVoronoi(points) { + console.time("calculateVoronoi"); + diagram = voronoi(points); + // round edges to simplify future calculations + diagram.edges.forEach(function(e) { + e[0][0] = rn(e[0][0],2); + e[0][1] = rn(e[0][1],2); + e[1][0] = rn(e[1][0],2); + e[1][1] = rn(e[1][1],2); + }); + polygons = diagram.polygons(); + console.log(" cells: " + points.length); + console.timeEnd("calculateVoronoi"); + } + + // Get cell info on mouse move (useful for debugging) + function moved() { + const point = d3.mouse(this); + const i = diagram.find(point[0],point[1]).index; + + // update cellInfo + if (i) { + const p = cells[i]; // get cell + infoX.innerHTML = rn(point[0]); + infoY.innerHTML = rn(point[1]); + infoCell.innerHTML = i; + infoArea.innerHTML = ifDefined(p.area, "n/a", 2); + if (customization === 1) {infoHeight.innerHTML = getFriendlyHeight(heights[i]);} + else {infoHeight.innerHTML = getFriendlyHeight(p.height);} + infoFlux.innerHTML = ifDefined(p.flux, "n/a", 2); + let country = p.region === undefined ? "n/a" : p.region === "neutral" ? "neutral" : states[p.region].name + " (" + p.region + ")"; + infoCountry.innerHTML = country; + let culture = ifDefined(p.culture) !== "no" ? cultures[p.culture].name + " (" + p.culture + ")" : "n/a"; + infoCulture.innerHTML = culture; + infoPopulation.innerHTML = ifDefined(p.pop, "n/a", 2); + infoBurg.innerHTML = ifDefined(p.manor) !== "no" ? manors[p.manor].name + " (" + p.manor + ")" : "no"; + const feature = features[p.fn]; + if (feature !== undefined) { + const fType = feature.land ? "Island" : feature.border ? "Ocean" : "Lake"; + infoFeature.innerHTML = fType + " (" + p.fn + ")"; + } else { + infoFeature.innerHTML = "n/a"; + } + } + + // update tooltip + if (toggleTooltips.checked) { + tooltip.innerHTML = tooltip.getAttribute("data-main"); + const tag = event.target.tagName; + const path = event.composedPath(); + const group = path[path.length - 7].id; + const subgroup = path[path.length - 8].id; + if (group === "rivers") tip("Click to open River Editor"); + if (group === "routes") tip("Click to open Route Editor"); + if (group === "terrain") tip("Click to open Relief Icon Editor"); + if (group === "labels") tip("Click to open Label Editor"); + if (group === "icons") tip("Click to open Icon Editor"); + if (group === "markers") tip("Click to open Marker Editor"); + if (group === "ruler") { + if (tag === "path" || tag === "line") tip("Drag to move the measurer"); + if (tag === "text") tip("Click to remove the measurer"); + if (tag === "circle") tip("Drag to adjust the measurer"); + } + if (subgroup === "burgIcons") tip("Click to open Burg Editor"); + if (subgroup === "burgLabels") tip("Click to open Burg Editor"); + + // show legend on hover (if any) + let id = event.target.id; + if (id === "") id = event.target.parentNode.id; + if (subgroup === "burgLabels") id = "burg" + event.target.getAttribute("data-id"); + + let note = notes.find(note => note.id === id); + let legend = document.getElementById("legend"); + let legendHeader = document.getElementById("legendHeader"); + let legendBody = document.getElementById("legendBody"); + if (note !== undefined && note.legend !== "") { + legend.style.display = "block"; + legendHeader.innerHTML = note.name; + legendBody.innerHTML = note.legend; + } else { + legend.style.display = "none"; + legendHeader.innerHTML = ""; + legendBody.innerHTML = ""; + } + } + + // draw line for ranges placing for heightmap Customization + if (customization === 1) { + const line = debug.selectAll(".line"); + if (debug.selectAll(".tag").size() === 1) { + const x = +debug.select(".tag").attr("cx"); + const y = +debug.select(".tag").attr("cy"); + if (line.size()) {line.attr("x1", x).attr("y1", y).attr("x2", point[0]).attr("y2", point[1]);} + else {debug.insert("line", ":first-child").attr("class", "line") + .attr("x1", x).attr("y1", y).attr("x2", point[0]).attr("y2", point[1]);} + } else { + line.remove(); + } + } + + // change radius circle for Customization + if (customization > 0) { + const brush = $("#brushesButtons > .pressed"); + const brushId = brush.attr("id"); + if (brushId === "brushRange" || brushId === "brushTrough") return; + if (customization !== 5 && !brush.length && !$("div.selected").length) return; + let radius = 0; + if (customization === 1) { + radius = brushRadius.value; + if (brushId === "brushHill" || brushId === "brushPit") { + radius = Math.pow(brushPower.value * 4, .5); + } + } + else if (customization === 2) radius = countriesManuallyBrush.value; + else if (customization === 4) radius = culturesManuallyBrush.value; + else if (customization === 5) radius = reliefBulkRemoveRadius.value; + + const r = rn(6 / graphSize * radius, 1); + let clr = "#373737"; + if (customization === 2) { + const state = +$("div.selected").attr("id").slice(5); + clr = states[state].color === "neutral" ? "white" : states[state].color; + } + if (customization === 4) { + const culture = +$("div.selected").attr("id").slice(7); + clr = cultures[culture].color; + } + moveCircle(point[0], point[1], r, clr); + } + } + + // return value (v) if defined with specified number of decimals (d) + // else return "no" or attribute (r) + function ifDefined(v, r, d) { + if (v === null || v === undefined) return r || "no"; + if (d) return v.toFixed(d); + return v; + } + + // get user-friendly (real-world) height value from map data + function getFriendlyHeight(h) { + let exponent = +heightExponent.value; + let unit = heightUnit.value; + let unitRatio = 1; // default calculations are in meters + if (unit === "ft") unitRatio = 3.28; // if foot + if (unit === "f") unitRatio = 0.5468; // if fathom + let height = -990; + if (h >= 20) height = Math.pow(h - 18, exponent); + if (h < 20 && h > 0) height = (h - 20) / h * 50; + return h + " (" + rn(height * unitRatio) + " " + unit + ")"; + } + + // move brush radius circle + function moveCircle(x, y, r, c) { + let circle = debug.selectAll(".circle"); + if (!circle.size()) circle = debug.insert("circle", ":first-child").attr("class", "circle"); + circle.attr("cx", x).attr("cy", y); + if (r) circle.attr("r", r); + if (c) circle.attr("stroke", c); + } + + // Drag actions + function dragstarted() { + const x0 = d3.event.x, y0 = d3.event.y, + c0 = diagram.find(x0, y0).index; + let c1 = c0; + let x1, y1; + const opisometer = $("#addOpisometer").hasClass("pressed"); + const planimeter = $("#addPlanimeter").hasClass("pressed"); + const factor = rn(1 / Math.pow(scale, 0.3), 1); + + if (opisometer || planimeter) { + $("#ruler").show(); + const type = opisometer ? "opisometer" : "planimeter"; + var rulerNew = ruler.append("g").attr("class", type).call(d3.drag().on("start", elementDrag)); + var points = [{scX: rn(x0, 2), scY: rn(y0, 2)}]; + if (opisometer) { + var curve = rulerNew.append("path").attr("class", "opisometer white").attr("stroke-width", factor); + const dash = rn(30 / distanceScale.value, 2); + var curveGray = rulerNew.append("path").attr("class", "opisometer gray").attr("stroke-dasharray", dash).attr("stroke-width", factor); + } else { + var curve = rulerNew.append("path").attr("class", "planimeter").attr("stroke-width", factor); + } + var text = rulerNew.append("text").attr("dy", -1).attr("font-size", 10 * factor); + } + + d3.event.on("drag", function() { + x1 = d3.event.x, y1 = d3.event.y; + const c2 = diagram.find(x1, y1).index; + + // Heightmap customization + if (customization === 1) { + if (c2 === c1 && x1 !== x0 && y1 !== y0) return; + c1 = c2; + const brush = $("#brushesButtons > .pressed"); + const id = brush.attr("id"); + const power = +brushPower.value; + if (id === "brushHill") {add(c2, "hill", power); updateHeightmap();} + if (id === "brushPit") {addPit(1, power, c2); updateHeightmap();} + if (id !== "brushRange" || id !== "brushTrough") { + // move a circle to show approximate change radius + moveCircle(x1, y1); + updateCellsInRadius(c2, c0); + } + } + + // Countries / cultures manuall assignment + if (customization === 2 || customization === 4) { + if ($("div.selected").length === 0) return; + if (c2 === c1) return; + c1 = c2; + let radius = customization === 2 ? +countriesManuallyBrush.value : +culturesManuallyBrush.value; + const r = rn(6 / graphSize * radius, 1); + moveCircle(x1, y1, r); + let selection = defineBrushSelection(c2, radius); + if (selection) { + if (customization === 2) changeStateForSelection(selection); + if (customization === 4) changeCultureForSelection(selection); + } + } + + if (opisometer || planimeter) { + const l = points[points.length - 1]; + const diff = Math.hypot(l.scX - x1, l.scY - y1); + if (diff > 5) {points.push({scX: x1, scY: y1});} + if (opisometer) { + lineGen.curve(d3.curveBasis); + var d = round(lineGen(points)); + curve.attr("d", d); + curveGray.attr("d", d); + const dist = rn(curve.node().getTotalLength()); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + text.attr("x", x1).attr("y", y1 - 10).text(label); + } else { + lineGen.curve(d3.curveBasisClosed); + var d = round(lineGen(points)); + curve.attr("d", d); + } + } + }); + + d3.event.on("end", function() { + if (customization === 1) updateHistory(); + if (opisometer || planimeter) { + $("#addOpisometer, #addPlanimeter").removeClass("pressed"); + restoreDefaultEvents(); + if (opisometer) { + const dist = rn(curve.node().getTotalLength()); + var c = curve.node().getPointAtLength(dist / 2); + const p = curve.node().getPointAtLength((dist / 2) - 1); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + const atan = p.x > c.x ? Math.atan2(p.y - c.y, p.x - c.x) : Math.atan2(c.y - p.y, c.x - p.x); + const angle = rn(atan * 180 / Math.PI, 3); + const tr = "rotate(" + angle + " " + c.x + " " + c.y + ")"; + text.attr("data-points", JSON.stringify(points)).attr("data-dist", dist).attr("x", c.x).attr("y", c.y).attr("transform", tr).text(label).on("click", removeParent); + rulerNew.append("circle").attr("cx", points[0].scX).attr("cy", points[0].scY).attr("r", 2 * factor).attr("stroke-width", 0.5 * factor) + .attr("data-edge", "start").call(d3.drag().on("start", opisometerEdgeDrag)); + rulerNew.append("circle").attr("cx", points[points.length - 1].scX).attr("cy", points[points.length - 1].scY).attr("r", 2 * factor).attr("stroke-width", 0.5 * factor) + .attr("data-edge", "end").call(d3.drag().on("start", opisometerEdgeDrag)); + } else { + const vertices = points.map(function (p) { + return [p.scX, p.scY] + }); + const area = rn(Math.abs(d3.polygonArea(vertices))); // initial area as positive integer + let areaConv = area * Math.pow(distanceScale.value, 2); // convert area to distanceScale + areaConv = si(areaConv); + if (areaUnit.value === "square") {areaConv += " " + distanceUnit.value + "²"} else {areaConv += " " + areaUnit.value;} + var c = polylabel([vertices],1.0); // pole of inaccessibility + text.attr("x", rn(c[0],2)).attr("y", rn(c[1],2)).attr("data-area", area).text(areaConv).on("click", removeParent); + } + } + }); + } + + // restore default drag (map panning) and cursor + function restoreDefaultEvents() { + viewbox.style("cursor", "default").on(".drag", null).on("click", null); + } + + // remove parent element (usually if child is clicked) + function removeParent() { + $(this.parentNode).remove(); + } + + // define selection based on radius + function defineBrushSelection(center, r) { + let radius = r; + let selection = [center]; + if (radius > 1) selection = selection.concat(cells[center].neighbors); + selection = $.grep(selection, function(e) {return cells[e].height >= 20;}); + if (radius === 2) return selection; + let frontier = cells[center].neighbors; + while (radius > 2) { + let cycle = frontier.slice(); + frontier = []; + cycle.map(function(s) { + cells[s].neighbors.forEach(function(e) { + if (selection.indexOf(e) !== -1) return; + // if (cells[e].height < 20) return; + selection.push(e); + frontier.push(e); + }); + }); + radius--; + } + selection = $.grep(selection, function(e) {return cells[e].height >= 20;}); + return selection; + } + + // change region within selection + function changeStateForSelection(selection) { + if (selection.length === 0) return; + const temp = regions.select("#temp"); + const stateNew = +$("div.selected").attr("id").slice(5); + const color = states[stateNew].color === "neutral" ? "white" : states[stateNew].color; + selection.map(function(index) { + // keep stateOld and stateNew as integers! + const exists = temp.select("path[data-cell='"+index+"']"); + const region = cells[index].region === "neutral" ? states.length - 1 : cells[index].region; + const stateOld = exists.size() ? +exists.attr("data-state") : region; + if (stateNew === stateOld) return; + if (states[stateOld].capital === cells[index].manor) return; // not allowed to re-draw calitals + // change of append new element + if (exists.size()) { + exists.attr("data-state", stateNew).attr("fill", color).attr("stroke", color); + } else { + temp.append("path").attr("data-cell", index).attr("data-state", stateNew) + .attr("d", "M" + polygons[index].join("L") + "Z") + .attr("fill", color).attr("stroke", color); + } + }); + } + + // change culture within selection + function changeCultureForSelection(selection) { + if (selection.length === 0) return; + const cultureNew = +$("div.selected").attr("id").slice(7); + const clr = cultures[cultureNew].color; + selection.map(function(index) { + const cult = cults.select("#cult"+index); + const cultureOld = cult.attr("data-culture") !== null + ? +cult.attr("data-culture") + : cells[index].culture; + if (cultureOld === cultureNew) return; + cult.attr("data-culture", cultureNew).attr("fill", clr).attr("stroke", clr); + }); + } + + // update cells in radius if non-feature brush selected + function updateCellsInRadius(cell, source) { + const power = +brushPower.value; + let radius = +brushRadius.value; + const brush = $("#brushesButtons > .pressed").attr("id"); + if ($("#brushesButtons > .pressed").hasClass("feature")) {return;} + // define selection besed on radius + let selection = [cell]; + if (radius > 1) selection = selection.concat(cells[cell].neighbors); + if (radius > 2) { + let frontier = cells[cell].neighbors; + while (radius > 2) { + let cycle = frontier.slice(); + frontier = []; + cycle.map(function(s) { + cells[s].neighbors.forEach(function(e) { + if (selection.indexOf(e) !== -1) {return;} + selection.push(e); + frontier.push(e); + }); + }); + radius--; + } + } + // change each cell in the selection + const sourceHeight = heights[source]; + selection.map(function(s) { + // calculate changes + if (brush === "brushElevate") { + if (heights[s] < 20) {heights[s] = 20;} + else {heights[s] += power;} + if (heights[s] > 100) heights[s] = 100; + } + if (brush === "brushDepress") { + heights[s] -= power; + if (heights[s] > 100) heights[s] = 0; + } + if (brush === "brushAlign") {heights[s] = sourceHeight;} + if (brush === "brushSmooth") { + let hs = [heights[s]]; + cells[s].neighbors.forEach(function(e) {hs.push(heights[e]);}); + heights[s] = (heights[s] + d3.mean(hs)) / 2; + } + }); + updateHeightmapSelection(selection); + } + + // Mouseclick events + function placeLinearFeature() { + const point = d3.mouse(this); + const index = getIndex(point); + let tag = debug.selectAll(".tag"); + if (!tag.size()) { + tag = debug.append("circle").attr("data-cell", index).attr("class", "tag") + .attr("r", 3).attr("cx", point[0]).attr("cy", point[1]); + } else { + const from = +tag.attr("data-cell"); + debug.selectAll(".tag, .line").remove(); + const power = +brushPower.value; + const mod = $("#brushesButtons > .pressed").attr("id") === "brushRange" ? 1 : -1; + const selection = addRange(mod, power, from, index); + updateHeightmapSelection(selection); + } + } + + // turn D3 polygons array into cell array, define neighbors for each cell + function detectNeighbors(withGrid) { + console.time("detectNeighbors"); + let gridPath = ""; // store grid as huge single path string + cells = []; + polygons.map(function(i, d) { + const neighbors = []; + let type; // define cell type + if (withGrid) {gridPath += "M" + i.join("L") + "Z";} // grid path + diagram.cells[d].halfedges.forEach(function(e) { + const edge = diagram.edges[e]; + if (edge.left && edge.right) { + const ea = edge.left.index === d ? edge.right.index : edge.left.index; + neighbors.push(ea); + } else { + type = "border"; // polygon is on border if it has edge without opposite side polygon + } + }); + cells.push({index: d, data: i.data, height: 0, type, neighbors}); + }); + if (withGrid) {grid.append("path").attr("d", round(gridPath, 1));} + console.timeEnd("detectNeighbors"); + } + + // Generate Heigtmap routine + function defineHeightmap() { + console.time('defineHeightmap'); + if (lockTemplateInput.getAttribute("data-locked") == 0) { + const rnd = Math.random(); + if (rnd > 0.95) {templateInput.value = "Volcano";} + else if (rnd > 0.75) {templateInput.value = "High Island";} + else if (rnd > 0.55) {templateInput.value = "Low Island";} + else if (rnd > 0.35) {templateInput.value = "Continents";} + else if (rnd > 0.15) {templateInput.value = "Archipelago";} + else if (rnd > 0.10) {templateInput.value = "Mainland";} + else if (rnd > 0.01) {templateInput.value = "Peninsulas";} + else {templateInput.value = "Atoll";} + } + const mapTemplate = templateInput.value; + if (mapTemplate === "Volcano") templateVolcano(); + if (mapTemplate === "High Island") templateHighIsland(); + if (mapTemplate === "Low Island") templateLowIsland(); + if (mapTemplate === "Continents") templateContinents(); + if (mapTemplate === "Archipelago") templateArchipelago(); + if (mapTemplate === "Atoll") templateAtoll(); + if (mapTemplate === "Mainland") templateMainland(); + if (mapTemplate === "Peninsulas") templatePeninsulas(); + console.log(" template: " + mapTemplate); + console.timeEnd('defineHeightmap'); + } + + // Heighmap Template: Volcano + function templateVolcano(mod) { + addMountain(); + modifyHeights("all", 10, 1); + addHill(5, 0.35); + addRange(3); + addRange(-4); + } + + // Heighmap Template: High Island + function templateHighIsland(mod) { + addMountain(); + modifyHeights("all", 10, 1); + addRange(6); + addHill(12, 0.25); + addRange(-3); + modifyHeights("land", 0, 0.75); + addPit(1); + addHill(3, 0.15); + } + + // Heighmap Template: Low Island + function templateLowIsland(mod) { + addMountain(); + modifyHeights("all", 10, 1); + smoothHeights(2); + addRange(2); + addHill(4, 0.4); + addHill(12, 0.2); + addRange(-8); + modifyHeights("land", 0, 0.35); + } + + // Heighmap Template: Continents + function templateContinents(mod) { + addMountain(); + modifyHeights("all", 10, 1); + addHill(30, 0.25); + const count = Math.ceil(Math.random() * 4 + 4); + addStrait(count); + addPit(10); + addRange(-10); + modifyHeights("land", 0, 0.6); + smoothHeights(2); + addRange(3); + } + + // Heighmap Template: Archipelago + function templateArchipelago(mod) { + addMountain(); + modifyHeights("all", 10, 1); + addHill(12, 0.15); + addRange(8); + const count = Math.ceil(Math.random() * 2 + 2); + addStrait(count); + addRange(-15); + addPit(10); + modifyHeights("land", -5, 0.7); + smoothHeights(3); + } + + // Heighmap Template: Atoll + function templateAtoll(mod) { + addMountain(); + modifyHeights("all", 10, 1); + addHill(2, 0.35); + addRange(2); + smoothHeights(1); + modifyHeights("27-100", 0, 0.1); + } + + // Heighmap Template: Mainland + function templateMainland(mod) { + addMountain(); + modifyHeights("all", 10, 1); + addHill(30, 0.2); + addRange(10); + addPit(20); + addHill(10, 0.15); + addRange(-10); + modifyHeights("land", 0, 0.4); + addRange(10); + smoothHeights(3); + } + + // Heighmap Template: Peninsulas + function templatePeninsulas(mod) { + addMountain(); + modifyHeights("all", 15, 1); + addHill(30, 0); + addRange(5); + addPit(15); + const count = Math.ceil(Math.random() * 5 + 15); + addStrait(count); + } + + function addMountain() { + const x = Math.floor(Math.random() * graphWidth / 3 + graphWidth / 3); + const y = Math.floor(Math.random() * graphHeight * 0.2 + graphHeight * 0.4); + const cell = diagram.find(x, y).index; + const height = Math.random() * 10 + 90; // 90-99 + add(cell, "mountain", height); + } + + // place with shift 0-0.5 + function addHill(count, shift) { + for (let c = 0; c < count; c++) { + let limit = 0, cell, height; + do { + height = Math.random() * 40 + 10; // 10-50 + const x = Math.floor(Math.random() * graphWidth * (1 - shift * 2) + graphWidth * shift); + const y = Math.floor(Math.random() * graphHeight * (1 - shift * 2) + graphHeight * shift); + cell = diagram.find(x, y).index; + limit++; + } while (heights[cell] + height > 90 && limit < 100); + add(cell, "hill", height); + } + } + + function add(start, type, height) { + const session = Math.ceil(Math.random() * 1e5); + let radius; + let hRadius; + let mRadius; + switch (+graphSize) { + case 1: hRadius = 0.991; mRadius = 0.91; break; + case 2: hRadius = 0.9967; mRadius = 0.951; break; + case 3: hRadius = 0.999; mRadius = 0.975; break; + case 4: hRadius = 0.9994; mRadius = 0.98; break; + } + radius = type === "mountain" ? mRadius : hRadius; + const queue = [start]; + if (type === "mountain") heights[start] = height; + for (let i=0; i < queue.length && height >= 1; i++) { + if (type === "mountain") {height = heights[queue[i]] * radius - height / 100;} + else {height *= radius;} + cells[queue[i]].neighbors.forEach(function(e) { + if (cells[e].used === session) return; + const mod = Math.random() * 0.2 + 0.9; // 0.9-1.1 random factor + heights[e] += height * mod; + if (heights[e] > 100) heights[e] = 100; + cells[e].used = session; + queue.push(e); + }); + } + } + + function addRange(mod, height, from, to) { + const session = Math.ceil(Math.random() * 100000); + const count = Math.abs(mod); + let range = []; + for (let c = 0; c < count; c++) { + range = []; + let diff = 0, start = from, end = to; + if (!start || !end) { + do { + const xf = Math.floor(Math.random() * (graphWidth * 0.7)) + graphWidth * 0.15; + const yf = Math.floor(Math.random() * (graphHeight * 0.6)) + graphHeight * 0.2; + start = diagram.find(xf, yf).index; + const xt = Math.floor(Math.random() * (graphWidth * 0.7)) + graphWidth * 0.15; + const yt = Math.floor(Math.random() * (graphHeight * 0.6)) + graphHeight * 0.2; + end = diagram.find(xt, yt).index; + diff = Math.hypot(xt - xf, yt - yf); + } while (diff < 150 / graphSize || diff > 300 / graphSize) + } + if (start && end) { + for (let l = 0; start != end && l < 10000; l++) { + let min = 10000; + cells[start].neighbors.forEach(function(e) { + diff = Math.hypot(cells[end].data[0] - cells[e].data[0],cells[end].data[1] - cells[e].data[1]); + if (Math.random() > 0.8) diff = diff / 2; + if (diff < min) {min = diff, start = e;} + }); + range.push(start); + } + } + const change = height ? height : Math.random() * 10 + 10; + range.map(function(r) { + let rnd = Math.random() * 0.4 + 0.8; + if (mod > 0) heights[r] += change * rnd; + else if (heights[r] >= 10) {heights[r] -= change * rnd;} + cells[r].neighbors.forEach(function(e) { + if (cells[e].used === session) return; + cells[e].used = session; + rnd = Math.random() * 0.4 + 0.8; + const ch = change / 2 * rnd; + if (mod > 0) {heights[e] += ch;} else if (heights[e] >= 10) {heights[e] -= ch;} + if (heights[e] > 100) heights[e] = mod > 0 ? 100 : 5; + }); + if (heights[r] > 100) heights[r] = mod > 0 ? 100 : 5; + }); + } + return range; + } + + function addStrait(width) { + const session = Math.ceil(Math.random() * 100000); + const top = Math.floor(Math.random() * graphWidth * 0.35 + graphWidth * 0.3); + const bottom = Math.floor((graphWidth - top) - (graphWidth * 0.1) + (Math.random() * graphWidth * 0.2)); + let start = diagram.find(top, graphHeight * 0.1).index; + const end = diagram.find(bottom, graphHeight * 0.9).index; + let range = []; + for (let l = 0; start !== end && l < 1000; l++) { + let min = 10000; // dummy value + cells[start].neighbors.forEach(function(e) { + let diff = Math.hypot(cells[end].data[0] - cells[e].data[0], cells[end].data[1] - cells[e].data[1]); + if (Math.random() > 0.8) {diff = diff / 2} + if (diff < min) {min = diff; start = e;} + }); + range.push(start); + } + const query = []; + for (; width > 0; width--) { + range.map(function(r) { + cells[r].neighbors.forEach(function(e) { + if (cells[e].used === session) {return;} + cells[e].used = session; + query.push(e); + heights[e] *= 0.23; + if (heights[e] > 100 || heights[e] < 5) heights[e] = 5; + }); + range = query.slice(); + }); + } + } + + function addPit(count, height, cell) { + const session = Math.ceil(Math.random() * 1e5); + for (let c = 0; c < count; c++) { + let change = height ? height + 10 : Math.random() * 10 + 20; + let start = cell; + if (!start) { + const lowlands = $.grep(cells, function(e) {return (heights[e.index] >= 20);}); + if (!lowlands.length) return; + const rnd = Math.floor(Math.random() * lowlands.length); + start = lowlands[rnd].index; + } + let query = [start],newQuery= []; + // depress pit center + heights[start] -= change; + if (heights[start] < 5 || heights[start] > 100) heights[start] = 5; + cells[start].used = session; + for (let i = 1; i < 10000; i++) { + const rnd = Math.random() * 0.4 + 0.8; + change -= i / 0.6 * rnd; + if (change < 1) break; + query.map(function(p) { + cells[p].neighbors.forEach(function(e) { + if (cells[e].used === session) return; + cells[e].used = session; + if (Math.random() > 0.8) return; + newQuery.push(e); + heights[e] -= change; + if (heights[e] < 5 || heights[e] > 100) heights[e] = 5; + }); + }); + query = newQuery.slice(); + newQuery = []; + } + } + } + + // Modify heights adding or multiplying by value + function modifyHeights(range, add, mult) { + function modify(v) { + if (add) v += add; + if (mult !== 1) { + if (mult === "^2") mult = (v - 20) / 100; + if (mult === "^3") mult = ((v - 20) * (v - 20)) / 100; + if (range === "land") {v = 20 + (v - 20) * mult;} + else {v *= mult;} + } + if (v < 0) v = 0; + if (v > 100) v = 100; + return v; + } + const limMin = range === "land" ? 20 : range === "all" ? 0 : +range.split("-")[0]; + const limMax = range === "land" || range === "all" ? 100 : +range.split("-")[1]; + + for (let i=0; i < heights.length; i++) { + if (heights[i] < limMin || heights[i] > limMax) continue; + heights[i] = modify(heights[i]); + } + } + + // Smooth heights using mean of neighbors + function smoothHeights(fraction) { + const fr = fraction || 2; + for (let i=0; i < heights.length; i++) { + const nHeights = [heights[i]]; + cells[i].neighbors.forEach(function(e) {nHeights.push(heights[e]);}); + heights[i] = (heights[i] * (fr - 1) + d3.mean(nHeights)) / fr; + } + } + + // Randomize heights a bit + function disruptHeights() { + for (let i=0; i < heights.length; i++) { + if (heights[i] < 18) continue; + if (Math.random() < 0.5) continue; + heights[i] += 2 - Math.random() * 4; + } + } + + // Mark features (ocean, lakes, islands) + function markFeatures() { + console.time("markFeatures"); + Math.seedrandom(seed); // reset seed to get the same result on heightmap edit + for (let i=0, queue=[0]; queue.length > 0; i++) { + const cell = cells[queue[0]]; + cell.fn = i; // feature number + const land = heights[queue[0]] >= 20; + let border = cell.type === "border"; + if (border && land) cell.ctype = 2; + + while (queue.length) { + const q = queue.pop(); + if (cells[q].type === "border") { + border = true; + if (land) cells[q].ctype = 2; + } + + cells[q].neighbors.forEach(function(e) { + const eLand = heights[e] >= 20; + if (land === eLand && cells[e].fn === undefined) { + cells[e].fn = i; + queue.push(e); + } + if (land && !eLand) { + cells[q].ctype = 2; + cells[e].ctype = -1; + cells[q].harbor = cells[q].harbor ? cells[q].harbor + 1 : 1; + } + }); + } + features.push({i, land, border}); + + // find unmarked cell + for (let c=0; c < cells.length; c++) { + if (cells[c].fn === undefined) { + queue[0] = c; + break; + } + } + } + console.timeEnd("markFeatures"); + } + + // remove closed lakes near ocean + function reduceClosedLakes() { + console.time("reduceClosedLakes"); + const fs = JSON.parse(JSON.stringify(features)); + let lakesInit = lakesNow = features.reduce(function(s, f) { + return !f.land && !f.border ? s + 1 : s; + }, 0); + + for (let c=0; c < cells.length && lakesNow > 0; c++) { + if (heights[c] < 20) continue; // not land + if (cells[c].ctype !== 2) continue; // not near water + let ocean = null, lake = null; + // find land cells with lake and ocean nearby + cells[c].neighbors.forEach(function(n) { + if (heights[n] >= 20) return; + const fn = cells[n].fn; + if (features[fn].border !== false) ocean = fn; + if (fs[fn].border === false) lake = fn; + }); + // if found, make it water and turn lake to ocean + if (ocean !== null && lake !== null) { + //debug.append("circle").attr("cx", cells[c].data[0]).attr("cy", cells[c].data[1]).attr("r", 2).attr("fill", "red"); + lakesNow --; + fs[lake].border = ocean; + heights[c] = 19; + cells[c].fn = ocean; + cells[c].ctype = -1; + cells[c].neighbors.forEach(function(e) {if (heights[e] >= 20) cells[e].ctype = 2;}); + } + } + + if (lakesInit === lakesNow) return; // nothing was changed + for (let i=0; i < cells.length; i++) { + if (heights[i] >= 20) continue; // not water + const fn = cells[i].fn; + if (fs[fn].border !== features[fn].border) { + cells[i].fn = fs[fn].border; + //debug.append("circle").attr("cx", cells[i].data[0]).attr("cy", cells[i].data[1]).attr("r", 1).attr("fill", "blue"); + } + } + console.timeEnd("reduceClosedLakes"); + } + + function drawOcean() { + console.time("drawOcean"); + let limits = []; + let odd = 0.8; // initial odd for ocean layer is 80% + // Define type of ocean cells based on cell distance form land + let frontier = $.grep(cells, function(e) {return e.ctype === -1;}); + if (Math.random() < odd) {limits.push(-1); odd = 0.2;} + for (let c = -2; frontier.length > 0 && c > -10; c--) { + if (Math.random() < odd) {limits.unshift(c); odd = 0.2;} else {odd += 0.2;} + frontier.map(function(i) { + i.neighbors.forEach(function(e) { + if (!cells[e].ctype) cells[e].ctype = c; + }); + }); + frontier = $.grep(cells, function(e) {return e.ctype === c;}); + } + if (outlineLayersInput.value === "none") return; + if (outlineLayersInput.value !== "random") limits = outlineLayersInput.value.split(","); + // Define area edges + const opacity = rn(0.4 / limits.length, 2); + for (let l=0; l < limits.length; l++) { + const edges = []; + const lim = +limits[l]; + for (let i = 0; i < cells.length; i++) { + if (cells[i].ctype < lim || cells[i].ctype === undefined) continue; + if (cells[i].ctype > lim && cells[i].type !== "border") continue; + const cell = diagram.cells[i]; + cell.halfedges.forEach(function(e) { + const edge = diagram.edges[e]; + const start = edge[0].join(" "); + const end = edge[1].join(" "); + if (edge.left && edge.right) { + const ea = edge.left.index === i ? edge.right.index : edge.left.index; + if (cells[ea].ctype < lim) edges.push({start, end}); + } else { + edges.push({start, end}); + } + }); + } + lineGen.curve(d3.curveBasis); + let relax = 0.8 - l / 10; + if (relax < 0.2) relax = 0.2; + const line = getContinuousLine(edges, 0, relax); + oceanLayers.append("path").attr("d", line).attr("fill", "#ecf2f9").style("opacity", opacity); + } + console.timeEnd("drawOcean"); + } + + // recalculate Voronoi Graph to pack cells + function reGraph() { + console.time("reGraph"); + const tempCells = [], newPoints = []; // to store new data + // get average precipitation based on graph size + const avPrec = precInput.value / 5000; + const smallLakesMax = 500; + let smallLakes = 0; + const evaporation = 2; + cells.map(function(i, d) { + let height = i.height || heights[d]; + if (height > 100) height = 100; + const pit = i.pit; + const ctype = i.ctype; + if (ctype !== -1 && ctype !== -2 && height < 20) return; // exclude all deep ocean points + const x = rn(i.data[0],1), y = rn(i.data[1],1); + const fn = i.fn; + const harbor = i.harbor; + let lake = i.lake; + // mark potential cells for small lakes to add additional point there + if (smallLakes < smallLakesMax && !lake && pit > evaporation && ctype !== 2) { + lake = 2; + smallLakes++; + } + const region = i.region; // handle value for edit heightmap mode only + const culture = i.culture; // handle value for edit heightmap mode only + let copy = $.grep(newPoints, function(e) {return (e[0] == x && e[1] == y);}); + if (!copy.length) { + newPoints.push([x, y]); + tempCells.push({index:tempCells.length, data:[x, y],height, pit, ctype, fn, harbor, lake, region, culture}); + } + // add additional points for cells along coast + if (ctype === 2 || ctype === -1) { + if (i.type === "border") return; + if (!features[fn].land && !features[fn].border) return; + i.neighbors.forEach(function(e) { + if (cells[e].ctype === ctype) { + let x1 = (x * 2 + cells[e].data[0]) / 3; + let y1 = (y * 2 + cells[e].data[1]) / 3; + x1 = rn(x1, 1), y1 = rn(y1, 1); + copy = $.grep(newPoints, function(e) {return e[0] === x1 && e[1] === y1;}); + if (copy.length) return; + newPoints.push([x1, y1]); + tempCells.push({index:tempCells.length, data:[x1, y1],height, pit, ctype, fn, harbor, lake, region, culture}); + } + }); + } + if (lake === 2) { // add potential small lakes + polygons[i.index].forEach(function(e) { + if (Math.random() > 0.8) return; + let rnd = Math.random() * 0.6 + 0.8; + const x1 = rn((e[0] * rnd + i.data[0]) / (1 + rnd), 2); + rnd = Math.random() * 0.6 + 0.8; + const y1 = rn((e[1] * rnd + i.data[1]) / (1 + rnd), 2); + copy = $.grep(newPoints, function(c) {return x1 === c[0] && y1 === c[1];}); + if (copy.length) return; + newPoints.push([x1, y1]); + tempCells.push({index:tempCells.length, data:[x1, y1],height, pit, ctype, fn, region, culture}); + }); + } + }); + console.log( "small lakes candidates: " + smallLakes); + cells = tempCells; // use tempCells as the only cells array + calculateVoronoi(newPoints); // recalculate Voronoi diagram using new points + let gridPath = ""; // store grid as huge single path string + cells.map(function(i, d) { + if (i.height >= 20) { + // calc cell area + i.area = rn(Math.abs(d3.polygonArea(polygons[d])), 2); + const prec = rn(avPrec * i.area, 2); + i.flux = i.lake ? prec * 10 : prec; + } + const neighbors = []; // re-detect neighbors + diagram.cells[d].halfedges.forEach(function(e) { + const edge = diagram.edges[e]; + if (edge.left === undefined || edge.right === undefined) { + if (i.height >= 20) i.ctype = 99; // border cell + return; + } + const ea = edge.left.index === d ? edge.right.index : edge.left.index; + neighbors.push(ea); + if (d < ea && i.height >= 20 && i.lake !== 1 && cells[ea].height >= 20 && cells[ea].lake !== 1) { + gridPath += "M" + edge[0][0] + "," + edge[0][1] + "L" + edge[1][0] + "," + edge[1][1]; + } + }); + i.neighbors = neighbors; + if (i.region === undefined) delete i.region; + if (i.culture === undefined) delete i.culture; + }); + grid.append("path").attr("d", gridPath); + console.timeEnd("reGraph"); + } + + // redraw all cells for Customization 1 mode + function mockHeightmap() { + let landCells = 0; + $("#landmass").empty(); + const limit = renderOcean.checked ? 1 : 20; + for (let i=0; i < heights.length; i++) { + if (heights[i] < limit) continue; + if (heights[i] > 100) heights[i] = 100; + const clr = color(1 - heights[i] / 100); + landmass.append("path").attr("id", "cell"+i) + .attr("d", "M" + polygons[i].join("L") + "Z") + .attr("fill", clr).attr("stroke", clr); + } + } + + $("#renderOcean").click(mockHeightmap); + + // draw or update all cells + function updateHeightmap() { + const limit = renderOcean.checked ? 1 : 20; + for (let i=0; i < heights.length; i++) { + if (heights[i] > 100) heights[i] = 100; + let cell = landmass.select("#cell"+i); + const clr = color(1 - heights[i] / 100); + if (cell.size()) { + if (heights[i] < limit) {cell.remove();} + else {cell.attr("fill", clr).attr("stroke", clr);} + } else if (heights[i] >= limit) { + cell = landmass.append("path").attr("id", "cell"+i) + .attr("d", "M" + polygons[i].join("L") + "Z") + .attr("fill", clr).attr("stroke", clr); + } + } + } + + // draw or update cells from the selection + function updateHeightmapSelection(selection) { + if (selection === undefined) return; + const limit = renderOcean.checked ? 1 : 20; + selection.map(function(s) { + if (heights[s] > 100) heights[s] = 100; + let cell = landmass.select("#cell"+s); + const clr = color(1 - heights[s] / 100); + if (cell.size()) { + if (heights[s] < limit) {cell.remove();} + else {cell.attr("fill", clr).attr("stroke", clr);} + } else if (heights[s] >= limit) { + cell = landmass.append("path").attr("id", "cell"+s) + .attr("d", "M" + polygons[s].join("L") + "Z") + .attr("fill", clr).attr("stroke", clr); + } + }); + } + + function updateHistory() { + let landCells = 0; // count number of land cells + if (renderOcean.checked) { + landCells = heights.reduce(function(s, v) {if (v >= 20) {return s + 1;} else {return s;}}, 0); + } else { + landCells = landmass.selectAll("*").size(); + } + history = history.slice(0, historyStage); + history[historyStage] = heights.slice(); + historyStage++; + undo.disabled = templateUndo.disabled = historyStage <= 1; + redo.disabled = templateRedo.disabled = true; + const landMean = Math.trunc(d3.mean(heights)); + const landRatio = rn(landCells / heights.length * 100); + landmassCounter.innerHTML = landCells; + landmassRatio.innerHTML = landRatio; + landmassAverage.innerHTML = landMean; + // if perspective view dialog is opened, update it + if ($("#perspectivePanel").is(":visible")) drawPerspective(); + } + + // restoreHistory + function restoreHistory(step) { + historyStage = step; + redo.disabled = templateRedo.disabled = historyStage >= history.length; + undo.disabled = templateUndo.disabled = historyStage <= 1; + if (history[historyStage - 1] === undefined) return; + heights = history[historyStage - 1].slice(); + updateHeightmap(); + } + + // restart history from 1st step + function restartHistory() { + history = []; + historyStage = 0; + redo.disabled = templateRedo.disabled = true; + undo.disabled = templateUndo.disabled = true; + updateHistory(); + } + + // Detect and draw the coasline + function drawCoastline() { + console.time('drawCoastline'); + Math.seedrandom(seed); // reset seed to get the same result on heightmap edit + const shape = defs.append("mask").attr("id", "shape").attr("fill", "black").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%"); + $("#landmass").empty(); + let minX = graphWidth, maxX = 0; // extreme points + let minXedge, maxXedge; // extreme edges + const oceanEdges = [],lakeEdges = []; + for (let i=0; i < land.length; i++) { + const id = land[i].index, cell = diagram.cells[id]; + const f = land[i].fn; + land[i].height = Math.trunc(land[i].height); + if (!oceanEdges[f]) {oceanEdges[f] = []; lakeEdges[f] = [];} + cell.halfedges.forEach(function(e) { + const edge = diagram.edges[e]; + const start = edge[0].join(" "); + const end = edge[1].join(" "); + if (edge.left && edge.right) { + const ea = edge.left.index === id ? edge.right.index : edge.left.index; + cells[ea].height = Math.trunc(cells[ea].height); + if (cells[ea].height < 20) { + cells[ea].ctype = -1; + if (land[i].ctype !== 1) { + land[i].ctype = 1; // mark coastal land cells + // move cell point closer to coast + const x = (land[i].data[0] + cells[ea].data[0]) / 2; + const y = (land[i].data[1] + cells[ea].data[1]) / 2; + land[i].haven = ea; // harbor haven (oposite water cell) + land[i].coastX = rn(x + (land[i].data[0] - x) * 0.1, 1); + land[i].coastY = rn(y + (land[i].data[1] - y) * 0.1, 1); + land[i].data[0] = rn(x + (land[i].data[0] - x) * 0.5, 1); + land[i].data[1] = rn(y + (land[i].data[1] - y) * 0.5, 1); + } + if (features[cells[ea].fn].border) { + oceanEdges[f].push({start, end}); + // island extreme points + if (edge[0][0] < minX) {minX = edge[0][0]; minXedge = edge[0]} + if (edge[1][0] < minX) {minX = edge[1][0]; minXedge = edge[1]} + if (edge[0][0] > maxX) {maxX = edge[0][0]; maxXedge = edge[0]} + if (edge[1][0] > maxX) {maxX = edge[1][0]; maxXedge = edge[1]} + } else { + const l = cells[ea].fn; + if (!lakeEdges[f][l]) lakeEdges[f][l] = []; + lakeEdges[f][l].push({start, end}); + } + } + } else { + oceanEdges[f].push({start, end}); + } + }); + } + + for (let f = 0; f < features.length; f++) { + if (!oceanEdges[f]) continue; + if (!oceanEdges[f].length && lakeEdges[f].length) { + const m = lakeEdges[f].indexOf(d3.max(lakeEdges[f])); + oceanEdges[f] = lakeEdges[f][m]; + lakeEdges[f][m] = []; + } + lineGen.curve(d3.curveCatmullRomClosed.alpha(0.1)); + const oceanCoastline = getContinuousLine(oceanEdges[f],3, 0); + if (oceanCoastline) { + shape.append("path").attr("d", oceanCoastline).attr("fill", "white"); // draw the mask + coastline.append("path").attr("d", oceanCoastline); // draw the coastline + } + lineGen.curve(d3.curveBasisClosed); + lakeEdges[f].forEach(function(l) { + const lakeCoastline = getContinuousLine(l, 3, 0); + if (lakeCoastline) { + shape.append("path").attr("d", lakeCoastline).attr("fill", "black"); // draw the mask + lakes.append("path").attr("d", lakeCoastline); // draw the lakes + } + }); + } + landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); // draw the landmass + drawDefaultRuler(minXedge, maxXedge); + console.timeEnd('drawCoastline'); + } + + // draw default scale bar + function drawScaleBar() { + if ($("#scaleBar").hasClass("hidden")) return; // no need to re-draw hidden element + svg.select("#scaleBar").remove(); // fully redraw every time + // get size + const size = +barSize.value; + const dScale = distanceScale.value; + const unit = distanceUnit.value; + const scaleBar = svg.append("g").attr("id", "scaleBar") + .on("click", editScale) + .on("mousemove", function () { + tip("Click to open Scale Editor, drag to move"); + }) + .call(d3.drag().on("start", elementDrag)); + const init = 100; // actual length in pixels if scale, dScale and size = 1; + let val = init * size * dScale / scale; // bar length in distance unit + if (val > 900) {val = rn(val, -3);} // round to 1000 + else if (val > 90) {val = rn(val, -2);} // round to 100 + else if (val > 9) {val = rn(val, -1);} // round to 10 + else {val = rn(val)} // round to 1 + const l = val * scale / dScale; // actual length in pixels on this scale + const x = 0, y = 0; // initial position + scaleBar.append("line").attr("x1", x+0.5).attr("y1", y).attr("x2", x+l+size-0.5).attr("y2", y).attr("stroke-width", size).attr("stroke", "white"); + scaleBar.append("line").attr("x1", x).attr("y1", y + size).attr("x2", x+l+size).attr("y2", y + size).attr("stroke-width", size).attr("stroke", "#3d3d3d"); + const dash = size + " " + rn(l / 5 - size, 2); + scaleBar.append("line").attr("x1", x).attr("y1", y).attr("x2", x+l+size).attr("y2", y) + .attr("stroke-width", rn(size * 3, 2)).attr("stroke-dasharray", dash).attr("stroke", "#3d3d3d"); + // big scale + for (let b = 0; b < 6; b++) { + const value = rn(b * l / 5, 2); + const label = rn(value * dScale / scale); + if (b === 5) { + scaleBar.append("text").attr("x", x + value).attr("y", y - 2 * size).attr("font-size", rn(5 * size, 1)).text(label + " " + unit); + } else { + scaleBar.append("text").attr("x", x + value).attr("y", y - 2 * size).attr("font-size", rn(5 * size, 1)).text(label); + } + } + if (barLabel.value !== "") { + scaleBar.append("text").attr("x", x + (l+1) / 2).attr("y", y + 2 * size) + .attr("dominant-baseline", "text-before-edge") + .attr("font-size", rn(5 * size, 1)).text(barLabel.value); + } + const bbox = scaleBar.node().getBBox(); + // append backbround rectangle + scaleBar.insert("rect", ":first-child").attr("x", -10).attr("y", -20).attr("width", bbox.width + 10).attr("height", bbox.height + 15) + .attr("stroke-width", size).attr("stroke", "none").attr("filter", "url(#blur5)") + .attr("fill", barBackColor.value).attr("opacity", +barBackOpacity.value); + fitScaleBar(); + } + + // draw default ruler measiring land x-axis edges + function drawDefaultRuler(minXedge, maxXedge) { + const rulerNew = ruler.append("g").attr("class", "linear").call(d3.drag().on("start", elementDrag)); + if (!minXedge) minXedge = [0, 0]; + if (!maxXedge) maxXedge = [svgWidth, svgHeight]; + const x1 = rn(minXedge[0],2), y1 = rn(minXedge[1],2), x2 = rn(maxXedge[0],2), y2 = rn(maxXedge[1],2); + rulerNew.append("line").attr("x1", x1).attr("y1", y1).attr("x2", x2).attr("y2", y2).attr("class", "white"); + rulerNew.append("line").attr("x1", x1).attr("y1", y1).attr("x2", x2).attr("y2", y2).attr("class", "gray").attr("stroke-dasharray", 10); + rulerNew.append("circle").attr("r", 2).attr("cx", x1).attr("cy", y1).attr("stroke-width", 0.5).attr("data-edge", "left").call(d3.drag().on("drag", rulerEdgeDrag)); + rulerNew.append("circle").attr("r", 2).attr("cx", x2).attr("cy", y2).attr("stroke-width", 0.5).attr("data-edge", "rigth").call(d3.drag().on("drag", rulerEdgeDrag)); + const x0 = rn((x1 + x2) / 2, 2), y0 = rn((y1 + y2) / 2, 2); + rulerNew.append("circle").attr("r", 1.2).attr("cx", x0).attr("cy", y0).attr("stroke-width", 0.3).attr("class", "center").call(d3.drag().on("start", rulerCenterDrag)); + const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI; + const tr = "rotate(" + angle + " " + x0 + " " + y0 +")"; + const dist = rn(Math.hypot(x1 - x2, y1 - y2)); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + rulerNew.append("text").attr("x", x0).attr("y", y0).attr("dy", -1).attr("transform", tr).attr("data-dist", dist).text(label).on("click", removeParent).attr("font-size", 10); + } + + // drag any element changing transform + function elementDrag() { + const el = d3.select(this); + const tr = parseTransform(el.attr("transform")); + const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y; + + d3.event.on("drag", function() { + const x = d3.event.x, y = d3.event.y; + const transform = `translate(${(dx+x)},${(dy+y)}) rotate(${tr[2]} ${tr[3]} ${tr[4]})`; + el.attr("transform", transform); + const pp = this.parentNode.parentNode.id; + if (pp === "burgIcons" || pp === "burgLabels") { + tip('Use dragging for fine-tuning only, to move burg to a different cell use "Relocate" button'); + } + if (pp === "labels") { + // also transform curve control circle + debug.select("circle").attr("transform", transform); + } + }); + + d3.event.on("end", function() { + // remember scaleBar bottom-right position + if (el.attr("id") === "scaleBar") { + const xEnd = d3.event.x, yEnd = d3.event.y; + const diff = Math.abs(dx - xEnd) + Math.abs(dy - yEnd); + if (diff > 5) { + const bbox = el.node().getBoundingClientRect(); + sessionStorage.setItem("scaleBar", [bbox.right, bbox.bottom]); + } + } + }); + } + + // draw ruler circles and update label + function rulerEdgeDrag() { + const group = d3.select(this.parentNode); + const edge = d3.select(this).attr("data-edge"); + const x = d3.event.x, y = d3.event.y; + let x0, y0; + d3.select(this).attr("cx", x).attr("cy", y); + const line = group.selectAll("line"); + if (edge === "left") { + line.attr("x1", x).attr("y1", y); + x0 = +line.attr("x2"), y0 = +line.attr("y2"); + } else { + line.attr("x2", x).attr("y2", y); + x0 = +line.attr("x1"), y0 = +line.attr("y1"); + } + const xc = rn((x + x0) / 2, 2), yc = rn((y + y0) / 2, 2); + group.select(".center").attr("cx", xc).attr("cy", yc); + const dist = rn(Math.hypot(x0 - x, y0 - y)); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + const atan = x0 > x ? Math.atan2(y0 - y, x0 - x) : Math.atan2(y - y0, x - x0); + const angle = rn(atan * 180 / Math.PI, 3); + const tr = "rotate(" + angle + " " + xc + " " + yc + ")"; + group.select("text").attr("x", xc).attr("y", yc).attr("transform", tr).attr("data-dist", dist).text(label); + } + + // draw ruler center point to split ruler into 2 parts + function rulerCenterDrag() { + let xc1, yc1, xc2, yc2; + const group = d3.select(this.parentNode); // current ruler group + let x = d3.event.x, y = d3.event.y; // current coords + const line = group.selectAll("line"); // current lines + const x1 = +line.attr("x1"), y1 = +line.attr("y1"), x2 = +line.attr("x2"), y2 = +line.attr("y2"); // initial line edge points + const rulerNew = ruler.insert("g", ":first-child"); + rulerNew.attr("transform", group.attr("transform")).call(d3.drag().on("start", elementDrag)); + const factor = rn(1 / Math.pow(scale, 0.3), 1); + rulerNew.append("line").attr("class", "white").attr("stroke-width", factor); + const dash = +group.select(".gray").attr("stroke-dasharray"); + rulerNew.append("line").attr("class", "gray").attr("stroke-dasharray", dash).attr("stroke-width", factor); + rulerNew.append("text").attr("dy", -1).on("click", removeParent).attr("font-size", 10 * factor).attr("stroke-width", factor); + + d3.event.on("drag", function() { + x = d3.event.x, y = d3.event.y; + d3.select(this).attr("cx", x).attr("cy", y); + // change first part + line.attr("x1", x1).attr("y1", y1).attr("x2", x).attr("y2", y); + let dist = rn(Math.hypot(x1 - x, y1 - y)); + let label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + let atan = x1 > x ? Math.atan2(y1 - y, x1 - x) : Math.atan2(y - y1, x - x1); + xc1 = rn((x + x1) / 2, 2), yc1 = rn((y + y1) / 2, 2); + let tr = "rotate(" + rn(atan * 180 / Math.PI, 3) + " " + xc1 + " " + yc1 + ")"; + group.select("text").attr("x", xc1).attr("y", yc1).attr("transform", tr).attr("data-dist", dist).text(label); + // change second (new) part + dist = rn(Math.hypot(x2 - x, y2 - y)); + label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + atan = x2 > x ? Math.atan2(y2 - y, x2 - x) : Math.atan2(y - y2, x - x2); + xc2 = rn((x + x2) / 2, 2), yc2 = rn((y + y2) / 2, 2); + tr = "rotate(" + rn(atan * 180 / Math.PI, 3) + " " + xc2 + " " + yc2 +")"; + rulerNew.selectAll("line").attr("x1", x).attr("y1", y).attr("x2", x2).attr("y2", y2); + rulerNew.select("text").attr("x", xc2).attr("y", yc2).attr("transform", tr).attr("data-dist", dist).text(label); + }); + + d3.event.on("end", function() { + // circles for 1st part + group.selectAll("circle").remove(); + group.append("circle").attr("cx", x1).attr("cy", y1).attr("r", 2 * factor).attr("stroke-width", 0.5 * factor).attr("data-edge", "left").call(d3.drag().on("drag", rulerEdgeDrag)); + group.append("circle").attr("cx", x).attr("cy", y).attr("r", 2 * factor).attr("stroke-width", 0.5 * factor).attr("data-edge", "rigth").call(d3.drag().on("drag", rulerEdgeDrag)); + group.append("circle").attr("cx", xc1).attr("cy", yc1).attr("r", 1.2 * factor).attr("stroke-width", 0.3 * factor).attr("class", "center").call(d3.drag().on("start", rulerCenterDrag)); + // circles for 2nd part + rulerNew.append("circle").attr("cx", x).attr("cy", y).attr("r", 2 * factor).attr("stroke-width", 0.5 * factor).attr("data-edge", "left").call(d3.drag().on("drag", rulerEdgeDrag)); + rulerNew.append("circle").attr("cx", x2).attr("cy", y2).attr("r", 2 * factor).attr("stroke-width", 0.5 * factor).attr("data-edge", "rigth").call(d3.drag().on("drag", rulerEdgeDrag)); + rulerNew.append("circle").attr("cx", xc2).attr("cy", yc2).attr("r", 1.2 * factor).attr("stroke-width", 0.3 * factor).attr("class", "center").call(d3.drag().on("start", rulerCenterDrag)); + }); + } + + function opisometerEdgeDrag() { + const el = d3.select(this); + const x0 = +el.attr("cx"), y0 = +el.attr("cy"); + const group = d3.select(this.parentNode); + const curve = group.select(".white"); + const curveGray = group.select(".gray"); + const text = group.select("text"); + const points = JSON.parse(text.attr("data-points")); + if (x0 === points[0].scX && y0 === points[0].scY) {points.reverse();} + + d3.event.on("drag", function() { + const x = d3.event.x, y = d3.event.y; + el.attr("cx", x).attr("cy", y); + const l = points[points.length - 1]; + const diff = Math.hypot(l.scX - x, l.scY - y); + if (diff > 5) {points.push({scX: x, scY: y});} else {return;} + lineGen.curve(d3.curveBasis); + const d = round(lineGen(points)); + curve.attr("d", d); + curveGray.attr("d", d); + const dist = rn(curve.node().getTotalLength()); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + text.attr("x", x).attr("y", y).text(label); + }); + + d3.event.on("end", function() { + const dist = rn(curve.node().getTotalLength()); + const c = curve.node().getPointAtLength(dist / 2); + const p = curve.node().getPointAtLength((dist / 2) - 1); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + const atan = p.x > c.x ? Math.atan2(p.y - c.y, p.x - c.x) : Math.atan2(c.y - p.y, c.x - p.x); + const angle = rn(atan * 180 / Math.PI, 3); + const tr = "rotate(" + angle + " " + c.x + " " + c.y + ")"; + text.attr("data-points", JSON.stringify(points)).attr("data-dist", dist).attr("x", c.x).attr("y", c.y).attr("transform", tr).text(label); + }); + } + + function getContinuousLine(edges, indention, relax) { + let line = ""; + if (edges.length < 3) return ""; + while (edges.length > 2) { + let edgesOrdered = []; // to store points in a correct order + let start = edges[0].start; + let end = edges[0].end; + edges.shift(); + let spl = start.split(" "); + edgesOrdered.push({scX: +spl[0],scY: +spl[1]}); + spl = end.split(" "); + edgesOrdered.push({scX: +spl[0],scY: +spl[1]}); + let x0 = +spl[0],y0 = +spl[1]; + for (let i = 0; end !== start && i < 100000; i++) { + let next = null, index = null; + for (let e = 0; e < edges.length; e++) { + const edge = edges[e]; + if (edge.start == end || edge.end == end) { + next = edge; + end = next.start == end ? next.end : next.start; + index = e; + break; + } + } + if (!next) { + console.error("Next edge is not found"); + return ""; + } + spl = end.split(" "); + if (indention || relax) { + const dist = Math.hypot(+spl[0] - x0, +spl[1] - y0); + if (dist >= indention && Math.random() > relax) { + edgesOrdered.push({scX: +spl[0],scY: +spl[1]}); + x0 = +spl[0],y0 = +spl[1]; + } + } else { + edgesOrdered.push({scX: +spl[0],scY: +spl[1]}); + } + edges.splice(index, 1); + if (i === 100000-1) { + console.error("Line not ended, limit reached"); + break; + } + } + line += lineGen(edgesOrdered); + } + return round(line, 1); + } + + // temporary elevate lakes to min neighbors heights to correctly flux the water + function elevateLakes() { + console.time('elevateLakes'); + const lakes = $.grep(cells, function(e, d) {return heights[d] < 20 && !features[e.fn].border;}); + lakes.sort(function(a, b) {return heights[b.index] - heights[a.index];}); + for (let i=0; i < lakes.length; i++) { + const hs = [],id = lakes[i].index; + cells[id].height = heights[id]; // use height on object level + lakes[i].neighbors.forEach(function(n) { + const nHeight = cells[n].height || heights[n]; + if (nHeight >= 20) hs.push(nHeight); + }); + if (hs.length) cells[id].height = d3.min(hs) - 1; + if (cells[id].height < 20) cells[id].height = 20; + lakes[i].lake = 1; + } + console.timeEnd('elevateLakes'); + } + + // Depression filling algorithm (for a correct water flux modeling; phase1) + function resolveDepressionsPrimary() { + console.time('resolveDepressionsPrimary'); + land = $.grep(cells, function(e, d) { + if (!e.height) e.height = heights[d]; // use height on object level + return e.height >= 20; + }); + land.sort(function(a, b) {return b.height - a.height;}); + const limit = 10; + for (let l = 0, depression = 1; depression > 0 && l < limit; l++) { + depression = 0; + for (let i = 0; i < land.length; i++) { + const id = land[i].index; + if (land[i].type === "border") continue; + const hs = land[i].neighbors.map(function(n) {return cells[n].height;}); + const minHigh = d3.min(hs); + if (cells[id].height <= minHigh) { + depression++; + land[i].pit = land[i].pit ? land[i].pit + 1 : 1; + cells[id].height = minHigh + 2; + } + } + if (l === 0) console.log(" depressions init: " + depression); + } + console.timeEnd('resolveDepressionsPrimary'); + } + + // Depression filling algorithm (for a correct water flux modeling; phase2) + function resolveDepressionsSecondary() { + console.time('resolveDepressionsSecondary'); + land = $.grep(cells, function(e) {return e.height >= 20;}); + land.sort(function(a, b) {return b.height - a.height;}); + const limit = 100; + for (let l = 0, depression = 1; depression > 0 && l < limit; l++) { + depression = 0; + for (let i = 0; i < land.length; i++) { + if (land[i].ctype === 99) continue; + const nHeights = land[i].neighbors.map(function(n) {return cells[n].height}); + const minHigh = d3.min(nHeights); + if (land[i].height <= minHigh) { + depression++; + land[i].pit = land[i].pit ? land[i].pit + 1 : 1; + land[i].height = Math.trunc(minHigh + 2); + } + } + if (l === 0) console.log(" depressions reGraphed: " + depression); + if (l === limit - 1) console.error("Error: resolveDepressions iteration limit"); + } + console.timeEnd('resolveDepressionsSecondary'); + } + + // restore initial heights if user don't want system to change heightmap + function restoreCustomHeights() { + land.forEach(function(l) { + if (!l.pit) return; + l.height = Math.trunc(l.height - l.pit * 2); + if (l.height < 20) l.height = 20; + }); + } + + function flux() { + console.time('flux'); + riversData = []; + let riverNext = 0; + land.sort(function(a, b) {return b.height - a.height;}); + for (let i = 0; i < land.length; i++) { + const id = land[i].index; + const sx = land[i].data[0]; + const sy = land[i].data[1]; + let fn = land[i].fn; + if (land[i].ctype === 99) { + if (land[i].river !== undefined) { + let x, y; + const min = Math.min(sy, graphHeight - sy, sx, graphWidth - sx); + if (min === sy) {x = sx; y = 0;} + if (min === graphHeight - sy) {x = sx; y = graphHeight;} + if (min === sx) {x = 0; y = sy;} + if (min === graphWidth - sx) {x = graphWidth; y = sy;} + riversData.push({river: land[i].river, cell: id, x, y}); + } + continue; + } + if (features[fn].river !== undefined) { + if (land[i].river !== features[fn].river) { + land[i].river = undefined; + land[i].flux = 0; + } + } + let minHeight = 1000, min; + land[i].neighbors.forEach(function(e) { + if (cells[e].height < minHeight) { + minHeight = cells[e].height; + min = e; + } + }); + // Define river number + if (min !== undefined && land[i].flux > 1) { + if (land[i].river === undefined) { + // State new River + land[i].river = riverNext; + riversData.push({river: riverNext, cell: id, x: sx, y: sy}); + riverNext += 1; + } + // Assing existing River to the downhill cell + if (cells[min].river == undefined) { + cells[min].river = land[i].river; + } else { + const riverTo = cells[min].river; + const iRiver = $.grep(riversData, function (e) { + return (e.river == land[i].river); + }); + const minRiver = $.grep(riversData, function (e) { + return (e.river == riverTo); + }); + let iRiverL = iRiver.length; + let minRiverL = minRiver.length; + // re-assing river nunber if new part is greater + if (iRiverL >= minRiverL) { + cells[min].river = land[i].river; + iRiverL += 1; + minRiverL -= 1; + } + // mark confluences + if (cells[min].height >= 20 && iRiverL > 1 && minRiverL > 1) { + if (!cells[min].confluence) { + cells[min].confluence = minRiverL-1; + } else { + cells[min].confluence += minRiverL-1; + } + } + } + } + if (cells[min].flux) cells[min].flux += land[i].flux; + if (land[i].river !== undefined) { + const px = cells[min].data[0]; + const py = cells[min].data[1]; + if (cells[min].height < 20) { + // pour water to the sea + const x = (px + sx) / 2 + (px - sx) / 10; + const y = (py + sy) / 2 + (py - sy) / 10; + riversData.push({river: land[i].river, cell: id, x, y}); + } else { + if (cells[min].lake === 1) { + fn = cells[min].fn; + if (features[fn].river === undefined) features[fn].river = land[i].river; + } + // add next River segment + riversData.push({river: land[i].river, cell: min, x: px, y: py}); + } + } + } + console.timeEnd('flux'); + drawRiverLines(riverNext); + } + + function drawRiverLines(riverNext) { + console.time('drawRiverLines'); + for (let i = 0; i < riverNext; i++) { + const dataRiver = $.grep(riversData, function (e) { + return e.river === i; + }); + if (dataRiver.length > 1) { + const riverAmended = amendRiver(dataRiver, 1); + const width = rn(0.8 + Math.random() * 0.4, 1); + const increment = rn(0.8 + Math.random() * 0.4, 1); + const d = drawRiver(riverAmended, width, increment); + rivers.append("path").attr("d", d).attr("id", "river"+i).attr("data-width", width).attr("data-increment", increment); + } + } + rivers.selectAll("path").on("click", editRiver); + console.timeEnd('drawRiverLines'); + } + + // add more river points on 1/3 and 2/3 of length + function amendRiver(dataRiver, rndFactor) { + const riverAmended = []; + let side = 1; + for (let r = 0; r < dataRiver.length; r++) { + const dX = dataRiver[r].x; + const dY = dataRiver[r].y; + const cell = dataRiver[r].cell; + const c = cells[cell].confluence || 0; + riverAmended.push([dX, dY, c]); + if (r+1 < dataRiver.length) { + const eX = dataRiver[r + 1].x; + const eY = dataRiver[r + 1].y; + const angle = Math.atan2(eY - dY, eX - dX); + const serpentine = 1 / (r + 1); + const meandr = serpentine + 0.3 + Math.random() * 0.3 * rndFactor; + if (Math.random() > 0.5) { + side *= -1 + } + const dist = Math.hypot(eX - dX, eY - dY); + // if dist is big or river is small add 2 extra points + if (dist > 8 || (dist > 4 && dataRiver.length < 6)) { + let stX = (dX * 2 + eX) / 3; + let stY = (dY * 2 + eY) / 3; + let enX = (dX + eX * 2) / 3; + let enY = (dY + eY * 2) / 3; + stX += -Math.sin(angle) * meandr * side; + stY += Math.cos(angle) * meandr * side; + if (Math.random() > 0.8) { + side *= -1 + } + enX += Math.sin(angle) * meandr * side; + enY += -Math.cos(angle) * meandr * side; + riverAmended.push([stX, stY],[enX, enY]); + // if dist is medium or river is small add 1 extra point + } else if (dist > 4 || dataRiver.length < 6) { + let scX = (dX + eX) / 2; + let scY = (dY + eY) / 2; + scX += -Math.sin(angle) * meandr * side; + scY += Math.cos(angle) * meandr * side; + riverAmended.push([scX, scY]); + } + } + } + return riverAmended; + } + + // draw river polygon using arrpoximation + function drawRiver(points, width, increment) { + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); + let extraOffset = 0.03; // start offset to make river source visible + width = width || 1; // river width modifier + increment = increment || 1; // river bed widening modifier + let riverLength = 0; + points.map(function(p, i) { + if (i === 0) {return 0;} + riverLength += Math.hypot(p[0] - points[i-1][0],p[1] - points[i-1][1]); + }); + const widening = rn((1000 + (riverLength * 30)) * increment); + const riverPointsLeft = [], riverPointsRight = []; + const last = points.length - 1; + const factor = riverLength / points.length; + + // first point + let x = points[0][0], y = points[0][1], c; + let angle = Math.atan2(y - points[1][1], x - points[1][0]); + let xLeft = x + -Math.sin(angle) * extraOffset, yLeft = y + Math.cos(angle) * extraOffset; + riverPointsLeft.push({scX:xLeft, scY:yLeft}); + let xRight = x + Math.sin(angle) * extraOffset, yRight = y + -Math.cos(angle) * extraOffset; + riverPointsRight.unshift({scX:xRight, scY:yRight}); + + // middle points + for (let p = 1; p < last; p ++) { + x = points[p][0],y = points[p][1],c = points[p][2]; + if (c) {extraOffset += Math.atan(c * 10 / widening);} // confluence + const xPrev = points[p - 1][0], yPrev = points[p - 1][1]; + const xNext = points[p + 1][0], yNext = points[p + 1][1]; + angle = Math.atan2(yPrev - yNext, xPrev - xNext); + var offset = (Math.atan(Math.pow(p * factor, 2) / widening) / 2 * width) + extraOffset; + xLeft = x + -Math.sin(angle) * offset, yLeft = y + Math.cos(angle) * offset; + riverPointsLeft.push({scX:xLeft, scY:yLeft}); + xRight = x + Math.sin(angle) * offset, yRight = y + -Math.cos(angle) * offset; + riverPointsRight.unshift({scX:xRight, scY:yRight}); + } + + // end point + x = points[last][0],y = points[last][1],c = points[last][2]; + if (c) {extraOffset += Math.atan(c * 10 / widening);} // confluence + angle = Math.atan2(points[last-1][1] - y, points[last-1][0] - x); + xLeft = x + -Math.sin(angle) * offset, yLeft = y + Math.cos(angle) * offset; + riverPointsLeft.push({scX:xLeft, scY:yLeft}); + xRight = x + Math.sin(angle) * offset, yRight = y + -Math.cos(angle) * offset; + riverPointsRight.unshift({scX:xRight, scY:yRight}); + + // generate path and return + const right = lineGen(riverPointsRight); + let left = lineGen(riverPointsLeft); + left = left.substring(left.indexOf("C")); + return round(right + left, 2); + } + + // draw river polygon with best quality + function drawRiverSlow(points, width, increment) { + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); + width = width || 1; + const extraOffset = 0.02 * width; + increment = increment || 1; + const riverPoints = points.map(function (p) { + return {scX: p[0], scY: p[1]}; + }); + const river = defs.append("path").attr("d", lineGen(riverPoints)); + const riverLength = river.node().getTotalLength(); + const widening = rn((1000 + (riverLength * 30)) * increment); + const riverPointsLeft = [], riverPointsRight = []; + + for (let l = 0; l < riverLength; l++) { + var point = river.node().getPointAtLength(l); + var from = river.node().getPointAtLength(l - 0.1); + const to = river.node().getPointAtLength(l + 0.1); + var angle = Math.atan2(from.y - to.y, from.x - to.x); + var offset = (Math.atan(Math.pow(l, 2) / widening) / 2 * width) + extraOffset; + var xLeft = point.x + -Math.sin(angle) * offset; + var yLeft = point.y + Math.cos(angle) * offset; + riverPointsLeft.push({scX:xLeft, scY:yLeft}); + var xRight = point.x + Math.sin(angle) * offset; + var yRight = point.y + -Math.cos(angle) * offset; + riverPointsRight.unshift({scX:xRight, scY:yRight}); + } + + var point = river.node().getPointAtLength(riverLength); + var from = river.node().getPointAtLength(riverLength - 0.1); + var angle = Math.atan2(from.y - point.y, from.x - point.x); + var offset = (Math.atan(Math.pow(riverLength, 2) / widening) / 2 * width) + extraOffset; + var xLeft = point.x + -Math.sin(angle) * offset; + var yLeft = point.y + Math.cos(angle) * offset; + riverPointsLeft.push({scX:xLeft, scY:yLeft}); + var xRight = point.x + Math.sin(angle) * offset; + var yRight = point.y + -Math.cos(angle) * offset; + riverPointsRight.unshift({scX:xRight, scY:yRight}); + + river.remove(); + // generate path and return + const right = lineGen(riverPointsRight); + let left = lineGen(riverPointsLeft); + left = left.substring(left.indexOf("C")); + return round(right + left, 2); + } + + // add lakes on depressed points on river course + function addLakes() { + console.time('addLakes'); + let smallLakes = 0; + for (let i=0; i < land.length; i++) { + // elavate all big lakes + if (land[i].lake === 1) { + land[i].height = 19; + land[i].ctype = -1; + } + // define eligible small lakes + if (land[i].lake === 2 && smallLakes < 100) { + if (land[i].river !== undefined) { + land[i].height = 19; + land[i].ctype = -1; + land[i].fn = -1; + smallLakes++; + } else { + land[i].lake = undefined; + land[i].neighbors.forEach(function(n) { + if (cells[n].lake !== 1 && cells[n].river !== undefined) { + cells[n].lake = 2; + cells[n].height = 19; + cells[n].ctype = -1; + cells[n].fn = -1; + smallLakes++; + } else if (cells[n].lake === 2) { + cells[n].lake = undefined; + } + }); + } + } + } + console.log( "small lakes: " + smallLakes); + + // mark small lakes + let unmarked = $.grep(land, function(e) {return e.fn === -1}); + while (unmarked.length) { + let fn = -1, queue = [unmarked[0].index],lakeCells = []; + unmarked[0].session = "addLakes"; + while (queue.length) { + const q = queue.pop(); + lakeCells.push(q); + if (cells[q].fn !== -1) fn = cells[q].fn; + cells[q].neighbors.forEach(function(e) { + if (cells[e].lake && cells[e].session !== "addLakes") { + cells[e].session = "addLakes"; + queue.push(e); + } + }); + } + if (fn === -1) { + fn = features.length; + features.push({i: fn, land: false, border: false}); + } + lakeCells.forEach(function(c) {cells[c].fn = fn;}); + unmarked = $.grep(land, function(e) {return e.fn === -1}); + } + + land = $.grep(cells, function(e) {return e.height >= 20;}); + console.timeEnd('addLakes'); + } + + function editLabel() { + if (customization) return; + + unselect(); + closeDialogs("#labelEditor, .stable"); + elSelected = d3.select(this).call(d3.drag().on("start", elementDrag)).classed("draggable", true); + + // update group parameters + let group = d3.select(this.parentNode); + updateGroupOptions(); + labelGroupSelect.value = group.attr("id"); + labelFontSelect.value = fonts.indexOf(group.attr("data-font")); + labelSize.value = group.attr("data-size"); + labelColor.value = toHEX(group.attr("fill")); + labelOpacity.value = group.attr("opacity"); + labelText.value = elSelected.text(); + const tr = parseTransform(elSelected.attr("transform")); + labelAngle.value = tr[2]; + labelAngleValue.innerHTML = Math.abs(+tr[2]) + "°"; + + $("#labelEditor").dialog({ + title: "Edit Label: " + labelText.value, + minHeight: 30, width: "auto", maxWidth: 275, resizable: false, + position: {my: "center top+10", at: "bottom", of: this}, + close: unselect + }); + + if (modules.editLabel) return; + modules.editLabel = true; + + loadDefaultFonts(); + + function updateGroupOptions() { + labelGroupSelect.innerHTML = ""; + labels.selectAll("g:not(#burgLabels)").each(function(d) { + if (this.parentNode.id === "burgLabels") return; + let id = d3.select(this).attr("id"); + let opt = document.createElement("option"); + opt.value = opt.innerHTML = id; + labelGroupSelect.add(opt); + }); + } + + $("#labelGroupButton").click(function() { + $("#labelEditor > button").not(this).toggle(); + $("#labelGroupButtons").toggle(); + }); + + // on group change + document.getElementById("labelGroupSelect").addEventListener("change", function() { + document.getElementById(this.value).appendChild(elSelected.remove().node()); + }); + + // toggle inputs to declare a new group + document.getElementById("labelGroupNew").addEventListener("click", function() { + if ($("#labelGroupInput").css("display") === "none") { + $("#labelGroupInput").css("display", "inline-block"); + $("#labelGroupSelect").css("display", "none"); + labelGroupInput.focus(); + } else { + $("#labelGroupSelect").css("display", "inline-block"); + $("#labelGroupInput").css("display", "none"); + } + }); + + // toggle inputs to select a group + document.getElementById("labelExternalFont").addEventListener("click", function() { + if ($("#labelFontInput").css("display") === "none") { + $("#labelFontInput").css("display", "inline-block"); + $("#labelFontSelect").css("display", "none"); + labelFontInput.focus(); + } else { + $("#labelFontSelect").css("display", "inline-block"); + $("#labelFontInput").css("display", "none"); + } + }); + + // on new group creation + document.getElementById("labelGroupInput").addEventListener("change", function() { + if (!this.value) { + tip("Please provide a valid group name"); + return; + } + let group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (Number.isFinite(+group.charAt(0))) group = "g" + group; + // if el with this id exists, add size to id + while (labels.selectAll("#"+group).size()) {group += "_new";} + createNewLabelGroup(group); + }); + + function createNewLabelGroup(g) { + let group = elSelected.node().parentNode.cloneNode(false); + let groupNew = labels.append(f => group).attr("id", g); + groupNew.append(f => elSelected.remove().node()); + updateGroupOptions(); + $("#labelGroupSelect, #labelGroupInput").toggle(); + labelGroupInput.value = ""; + labelGroupSelect.value = g; + updateLabelGroups(); + } + + // remove label group on click + document.getElementById("labelGroupRemove").addEventListener("click", function() { + let group = d3.select(elSelected.node().parentNode); + let id = group.attr("id"); + let count = group.selectAll("text").size(); + // remove group with < 2 label without ask + if (count < 2) { + removeAllLabelsInGroup(id); + $("#labelEditor").dialog("close"); + return; + } + alertMessage.innerHTML = "Are you sure you want to remove all labels (" + count + ") of that group?"; + $("#alert").dialog({resizable: false, title: "Remove label group", + buttons: { + Remove: function() { + $(this).dialog("close"); + removeAllLabelsInGroup(id); + $("#labelEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + + $("#labelTextButton").click(function() { + $("#labelEditor > button").not(this).toggle(); + $("#labelTextButtons").toggle(); + }); + + // on label text change + document.getElementById("labelText").addEventListener("input", function() { + if (!this.value) { + tip("Name should not be blank, set opacity to 0 to hide label or click remove button to delete"); + return; + } + // change Label text + if (elSelected.select("textPath").size()) elSelected.select("textPath").text(this.value); + else elSelected.text(this.value); + $("div[aria-describedby='labelEditor'] .ui-dialog-title").text("Edit Label: " + this.value); + // check if label is a country name + let id = elSelected.attr("id") || ""; + if (id.includes("regionLabel")) { + let state = +elSelected.attr("id").slice(11); + states[state].name = this.value; + } + }); + + // generate a random country name + document.getElementById("labelTextRandom").addEventListener("click", function() { + let name = elSelected.text(); + let id = elSelected.attr("id") || ""; + if (id.includes("regionLabel")) { + // label is a country name + let state = +elSelected.attr("id").slice(11); + name = generateStateName(state.i); + states[state].name = name; + } else { + // label is not a country name, use random culture + let c = elSelected.node().getBBox(); + let closest = cultureTree.find((c.x + c.width / 2), (c.y + c.height / 2)); + let culture = Math.floor(Math.random() * cultures.length); + name = generateName(culture); + } + labelText.value = name; + $("div[aria-describedby='labelEditor'] .ui-dialog-title").text("Edit Label: " + name); + // change Label text + if (elSelected.select("textPath").size()) elSelected.select("textPath").text(name); + else elSelected.text(name); + }); + + $("#labelFontButton").click(function() { + $("#labelEditor > button").not(this).toggle(); + $("#labelFontButtons").toggle(); + }); + + // on label font change + document.getElementById("labelFontSelect").addEventListener("change", function() { + let group = elSelected.node().parentNode; + let font = fonts[this.value].split(':')[0].replace(/\+/g, " "); + group.setAttribute("font-family", font); + group.setAttribute("data-font", fonts[this.value]); + }); + + // on adding custom font + document.getElementById("labelFontInput").addEventListener("change", function() { + fetchFonts(this.value).then(fetched => { + if (!fetched) return; + labelExternalFont.click(); + labelFontInput.value = ""; + if (fetched === 1) $("#labelFontSelect").val(fonts.length - 1).change(); + }); + }); + + // on label size input + document.getElementById("labelSize").addEventListener("input", function() { + let group = elSelected.node().parentNode; + let size = +this.value; + group.setAttribute("data-size", size); + group.setAttribute("font-size", rn((size + (size / scale)) / 2, 2)) + }); + + $("#labelStyleButton").click(function() { + $("#labelEditor > button").not(this).toggle(); + $("#labelStyleButtons").toggle(); + }); + + // on label fill color input + document.getElementById("labelColor").addEventListener("input", function() { + let group = elSelected.node().parentNode; + group.setAttribute("fill", this.value); + }); + + // on label opacity input + document.getElementById("labelOpacity").addEventListener("input", function() { + let group = elSelected.node().parentNode; + group.setAttribute("opacity", this.value); + }); + + $("#labelAngleButton").click(function() { + $("#labelEditor > button").not(this).toggle(); + $("#labelAngleButtons").toggle(); + }); + + // on label angle input + document.getElementById("labelAngle").addEventListener("input", function() { + const tr = parseTransform(elSelected.attr("transform")); + labelAngleValue.innerHTML = Math.abs(+this.value) + "°"; + const c = elSelected.node().getBBox(); + const angle = +this.value; + const transform = `translate(${tr[0]},${tr[1]}) rotate(${angle} ${(c.x+c.width/2)} ${(c.y+c.height/2)})`; + elSelected.attr("transform", transform); + }); + + // display control points to curve label (place on path) + document.getElementById("labelCurve").addEventListener("click", function() { + let c = elSelected.node().getBBox(); + let cx = c.x + c.width / 2, cy = c.y + c.height / 2; + + if (!elSelected.select("textPath").size()) { + let id = elSelected.attr("id"); + let pathId = "#textPath_" + id; + let path = `M${cx-c.width},${cy} q${c.width},0 ${c.width * 2},0`; + let text = elSelected.text(), x = elSelected.attr("x"), y = elSelected.attr("y"); + elSelected.text(null).attr("data-x", x).attr("data-y", y).attr("x", null).attr("y", null); + defs.append("path").attr("id", "textPath_" + id).attr("d", path); + elSelected.append("textPath").attr("href", pathId).attr("startOffset", "50%").text(text); + } + + if (!debug.select("circle").size()) { + debug.append("circle").attr("id", "textPathControl").attr("r", 1.6) + .attr("cx", cx).attr("cy", cy).attr("transform", elSelected.attr("transform") || null) + .call(d3.drag().on("start", textPathControlDrag)); + } + }); + + // drag textPath controle point to curve the label + function textPathControlDrag() { + let textPath = defs.select("#textPath_" + elSelected.attr("id")); + let path = textPath.attr("d").split(" "); + let M = path[0].split(","); + let q = path[1].split(","); // +q[1] to get qy - the only changeble value + let y = d3.event.y; + + d3.event.on("drag", function() { + let dy = d3.event.y - y; + let total = +q[1] + dy * 8; + d3.select(this).attr("cy", d3.event.y); + textPath.attr("d", `${M[0]},${+M[1] - dy} ${q[0]},${total} ${path[2]}`); + }); + } + + // cancel label curvature + document.getElementById("labelCurveCancel").addEventListener("click", function() { + if (!elSelected.select("textPath").size()) return; + let text = elSelected.text(), x = elSelected.attr("data-x"), y = elSelected.attr("data-y"); + elSelected.text(); + elSelected.attr("x", x).attr("y", y).attr("data-x", null).attr("data-y", null).text(text); + defs.select("#textPath_" + elSelected.attr("id")).remove(); + debug.select("circle").remove(); + }); + + // open legendsEditor + document.getElementById("labelLegend").addEventListener("click", function() { + let id = elSelected.attr("id"); + let name = elSelected.text(); + editLegends(id, name); + }); + + // copy label on click + document.getElementById("labelCopy").addEventListener("click", function() { + let group = d3.select(elSelected.node().parentNode); + copy = group.append(f => elSelected.node().cloneNode(true)); + let id = "label" + Date.now().toString().slice(7); + copy.attr("id", id).attr("class", null).on("click", editLabel); + let shift = +group.attr("font-size") + 1; + if (copy.select("textPath").size()) { + let path = defs.select("#textPath_" + elSelected.attr("id")).attr("d"); + let textPath = defs.append("path").attr("id", "textPath_" + id); + copy.select("textPath").attr("href", "#textPath_" + id); + let pathArray = path.split(" "); + let x = +pathArray[0].split(",")[0].slice(1); + let y = +pathArray[0].split(",")[1]; + textPath.attr("d", `M${x-shift},${y-shift} ${pathArray[1]} ${pathArray[2]}`);shift + } else { + let x = +elSelected.attr("x") - shift; + let y = +elSelected.attr("y") - shift; + while (group.selectAll("text[x='" + x + "']").size()) {x -= shift; y -= shift;} + copy.attr("x", x).attr("y", y); + } + }); + + // remove label on click + document.getElementById("labelRemoveSingle").addEventListener("click", function() { + alertMessage.innerHTML = "Are you sure you want to remove the label?"; + $("#alert").dialog({resizable: false, title: "Remove label", + buttons: { + Remove: function() { + $(this).dialog("close"); + elSelected.remove(); + defs.select("#textPath_" + elSelected.attr("id")).remove(); + $("#labelEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + } + + function editRiver() { + if (customization) return; + if (elSelected) { + const self = d3.select(this).attr("id") === elSelected.attr("id"); + const point = d3.mouse(this); + if (elSelected.attr("data-river") === "new") { + addRiverPoint([point[0],point[1]]); + completeNewRiver(); + return; + } else if (self) { + riverAddControlPoint(point); + return; + } + } + + unselect(); + closeDialogs("#riverEditor, .stable"); + elSelected = d3.select(this); + elSelected.call(d3.drag().on("start", riverDrag)); + + const tr = parseTransform(elSelected.attr("transform")); + riverAngle.value = tr[2]; + riverAngleValue.innerHTML = Math.abs(+tr[2]) + "°"; + riverScale.value = tr[5]; + riverWidthInput.value = +elSelected.attr("data-width"); + riverIncrement.value = +elSelected.attr("data-increment"); + + $("#riverEditor").dialog({ + title: "Edit River", + minHeight: 30, width: "auto", resizable: false, + position: {my: "center top+20", at: "top", of: d3.event}, + close: function() { + if ($("#riverNew").hasClass('pressed')) completeNewRiver(); + unselect(); + } + }); + + if (!debug.select(".controlPoints").size()) debug.append("g").attr("class", "controlPoints"); + riverDrawPoints(); + + if (modules.editRiver) {return;} + modules.editRiver = true; + + function riverAddControlPoint(point) { + let dists = []; + debug.select(".controlPoints").selectAll("circle").each(function() { + const x = +d3.select(this).attr("cx"); + const y = +d3.select(this).attr("cy"); + dists.push(Math.hypot(point[0] - x, point[1] - y)); + }); + let index = dists.length; + if (dists.length > 1) { + const sorted = dists.slice(0).sort(function(a, b) {return a-b;}); + const closest = dists.indexOf(sorted[0]); + const next = dists.indexOf(sorted[1]); + if (closest <= next) {index = closest+1;} else {index = next+1;} + } + const before = ":nth-child(" + (index + 1) + ")"; + debug.select(".controlPoints").insert("circle", before) + .attr("cx", point[0]).attr("cy", point[1]).attr("r", 0.35) + .call(d3.drag().on("drag", riverPointDrag)) + .on("click", function(d) { + $(this).remove(); + redrawRiver(); + }); + redrawRiver(); + } + + function riverDrawPoints() { + const node = elSelected.node(); + // river is a polygon, so divide length by 2 to get course length + const l = node.getTotalLength() / 2; + const parts = (l / 5) >> 0; // number of points + let inc = l / parts; // increment + if (inc === Infinity) {inc = l;} // 2 control points for short rivers + // draw control points + for (let i = l, c = l; i > 0; i -= inc, c += inc) { + const p1 = node.getPointAtLength(i); + const p2 = node.getPointAtLength(c); + const p = [(p1.x + p2.x) / 2, (p1.y + p2.y) / 2]; + addRiverPoint(p); + } + // last point should be accurate + const lp1 = node.getPointAtLength(0); + const lp2 = node.getPointAtLength(l * 2); + const p = [(lp1.x + lp2.x) / 2, (lp1.y + lp2.y) / 2]; + addRiverPoint(p); + } + + function addRiverPoint(point) { + debug.select(".controlPoints").append("circle") + .attr("cx", point[0]).attr("cy", point[1]).attr("r", 0.35) + .call(d3.drag().on("drag", riverPointDrag)) + .on("click", function(d) { + $(this).remove(); + redrawRiver(); + }); + } + + function riverPointDrag() { + d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y); + redrawRiver(); + } + + function riverDrag() { + const x = d3.event.x, y = d3.event.y; + const tr = parseTransform(elSelected.attr("transform")); + d3.event.on("drag", function() { + let xc = d3.event.x, yc = d3.event.y; + let transform = `translate(${(+tr[0]+xc-x)},${(+tr[1]+yc-y)}) rotate(${tr[2]} ${tr[3]} ${tr[4]}) scale(${tr[5]})`; + elSelected.attr("transform", transform); + debug.select(".controlPoints").attr("transform", transform); + }); + } + + function redrawRiver() { + let points = []; + debug.select(".controlPoints").selectAll("circle").each(function() { + const el = d3.select(this); + points.push([+el.attr("cx"), +el.attr("cy")]); + }); + const width = +riverWidthInput.value; + const increment = +riverIncrement.value; + const d = drawRiverSlow(points, width, increment); + elSelected.attr("d", d); + } + + $("#riverWidthInput, #riverIncrement").change(function() { + const width = +riverWidthInput.value; + const increment = +riverIncrement.value; + elSelected.attr("data-width", width).attr("data-increment", increment); + redrawRiver(); + }); + + $("#riverRegenerate").click(function() { + let points = [],amended = [],x, y, p1, p2; + const node = elSelected.node(); + const l = node.getTotalLength() / 2; + const parts = (l / 8) >> 0; // number of points + let inc = l / parts; // increment + if (inc === Infinity) {inc = l;} // 2 control points for short rivers + for (let i = l, e = l; i > 0; i -= inc, e += inc) { + p1 = node.getPointAtLength(i); + p2 = node.getPointAtLength(e); + x = (p1.x + p2.x) / 2, y = (p1.y + p2.y) / 2; + points.push([x, y]); + } + // last point should be accurate + p1 = node.getPointAtLength(0); + p2 = node.getPointAtLength(l * 2); + x = (p1.x + p2.x) / 2, y = (p1.y + p2.y) / 2; + points.push([x, y]); + // amend points + const rndFactor = 0.3 + Math.random() * 1.4; // random factor in range 0.2-1.8 + for (let i = 0; i < points.length; i++) { + x = points[i][0],y = points[i][1]; + amended.push([x, y]); + // add additional semi-random point + if (i + 1 < points.length) { + const x2 = points[i+1][0],y2 = points[i+1][1]; + let side = Math.random() > 0.5 ? 1 : -1; + const angle = Math.atan2(y2 - y, x2 - x); + const serpentine = 2 / (i+1); + const meandr = serpentine + 0.3 + Math.random() * rndFactor; + x = (x + x2) / 2, y = (y + y2) / 2; + x += -Math.sin(angle) * meandr * side; + y += Math.cos(angle) * meandr * side; + amended.push([x, y]); + } + } + const width = +riverWidthInput.value * 0.6 + Math.random(); + const increment = +riverIncrement.value * 0.9 + Math.random() * 0.2; + riverWidthInput.value = width; + riverIncrement.value = increment; + elSelected.attr("data-width", width).attr("data-increment", increment); + const d = drawRiverSlow(amended, width, increment); + elSelected.attr("d", d).attr("data-width", width).attr("data-increment", increment); + debug.select(".controlPoints").selectAll("*").remove(); + amended.map(function(p) {addRiverPoint(p);}); + }); + + $("#riverAngle").on("input", function() { + const tr = parseTransform(elSelected.attr("transform")); + riverAngleValue.innerHTML = Math.abs(+this.value) + "°"; + const c = elSelected.node().getBBox(); + const angle = +this.value, scale = +tr[5]; + const transform = `translate(${tr[0]},${tr[1]}) rotate(${angle} ${(c.x+c.width/2)*scale} ${(c.y+c.height/2)*scale}) scale(${scale})`; + elSelected.attr("transform", transform); + debug.select(".controlPoints").attr("transform", transform); + }); + + $("#riverReset").click(function() { + elSelected.attr("transform", ""); + debug.select(".controlPoints").attr("transform", ""); + riverAngle.value = 0; + riverAngleValue.innerHTML = "0°"; + riverScale.value = 1; + }); + + $("#riverScale").change(function() { + const tr = parseTransform(elSelected.attr("transform")); + const scaleOld = +tr[5],scale = +this.value; + const c = elSelected.node().getBBox(); + const cx = c.x + c.width / 2, cy = c.y + c.height / 2; + const trX = +tr[0] + cx * (scaleOld - scale); + const trY = +tr[1] + cy * (scaleOld - scale); + const scX = +tr[3] * scale/scaleOld; + const scY = +tr[4] * scale/scaleOld; + const transform = `translate(${trX},${trY}) rotate(${tr[2]} ${scX} ${scY}) scale(${scale})`; + elSelected.attr("transform", transform); + debug.select(".controlPoints").attr("transform", transform); + }); + + $("#riverNew").click(function() { + if ($(this).hasClass('pressed')) { + completeNewRiver(); + } else { + // enter creation mode + $(".pressed").removeClass('pressed'); + $(this).addClass('pressed'); + if (elSelected) elSelected.call(d3.drag().on("drag", null)); + debug.select(".controlPoints").selectAll("*").remove(); + viewbox.style("cursor", "crosshair").on("click", newRiverAddPoint); + } + }); + + function newRiverAddPoint() { + const point = d3.mouse(this); + addRiverPoint([point[0],point[1]]); + if (!elSelected || elSelected.attr("data-river") !== "new") { + const id = +$("#rivers > path").last().attr("id").slice(5) + 1; + elSelected = rivers.append("path").attr("data-river", "new").attr("id", "river"+id) + .attr("data-width", 2).attr("data-increment", 1).on("click", completeNewRiver); + } else { + redrawRiver(); + let cell = diagram.find(point[0],point[1]).index; + let f = cells[cell].fn; + let ocean = !features[f].land && features[f].border; + if (ocean && debug.select(".controlPoints").selectAll("circle").size() > 5) completeNewRiver(); + } + } + + function completeNewRiver() { + $("#riverNew").removeClass('pressed'); + restoreDefaultEvents(); + if (!elSelected || elSelected.attr("data-river") !== "new") return; + redrawRiver(); + elSelected.attr("data-river", ""); + elSelected.call(d3.drag().on("start", riverDrag)).on("click", editRiver); + const r = +elSelected.attr("id").slice(5); + debug.select(".controlPoints").selectAll("circle").each(function() { + const x = +d3.select(this).attr("cx"); + const y = +d3.select(this).attr("cy"); + const cell = diagram.find(x, y, 3); + if (!cell) return; + if (cells[cell.index].river === undefined) cells[cell.index].river = r; + }); + unselect(); + debug.append("g").attr("class", "controlPoints"); + } + + $("#riverCopy").click(function() { + const tr = parseTransform(elSelected.attr("transform")); + const d = elSelected.attr("d"); + let x = 2, y = 2; + let transform = `translate(${tr[0]-x},${tr[1]-y}) rotate(${tr[2]} ${tr[3]} ${tr[4]}) scale(${tr[5]})`; + while (rivers.selectAll("[transform='" + transform + "'][d='" + d + "']").size() > 0) { + x += 2; y += 2; + transform = `translate(${tr[0]-x},${tr[1]-y}) rotate(${tr[2]} ${tr[3]} ${tr[4]}) scale(${tr[5]})`; + } + const river = +$("#rivers > path").last().attr("id").slice(5) + 1; + rivers.append("path").attr("d", d) + .attr("transform", transform) + .attr("id", "river"+river).on("click", editRiver) + .attr("data-width", elSelected.attr("data-width")) + .attr("data-increment", elSelected.attr("data-increment")); + unselect(); + }); + + // open legendsEditor + document.getElementById("riverLegend").addEventListener("click", function() { + let id = elSelected.attr("id"); + editLegends(id, id); + }); + + $("#riverRemove").click(function() { + alertMessage.innerHTML = `Are you sure you want to remove the river?`; + $("#alert").dialog({resizable: false, title: "Remove river", + buttons: { + Remove: function() { + $(this).dialog("close"); + const river = +elSelected.attr("id").slice(5); + const avPrec = rn(precInput.value / Math.sqrt(cells.length), 2); + land.map(function(l) { + if (l.river === river) { + l.river = undefined; + l.flux = avPrec; + } + }); + elSelected.remove(); + unselect(); + $("#riverEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }) + }); + + } + + function editRoute() { + if (customization) {return;} + if (elSelected) { + const self = d3.select(this).attr("id") === elSelected.attr("id"); + const point = d3.mouse(this); + if (elSelected.attr("data-route") === "new") { + addRoutePoint({x:point[0],y:point[1]}); + completeNewRoute(); + return; + } else if (self) { + routeAddControlPoint(point); + return; + } + } + + unselect(); + closeDialogs("#routeEditor, .stable"); + + if (this && this !== window) { + elSelected = d3.select(this); + if (!debug.select(".controlPoints").size()) debug.append("g").attr("class", "controlPoints"); + routeDrawPoints(); + routeUpdateGroups(); + let routeType = d3.select(this.parentNode).attr("id"); + routeGroup.value = routeType; + + $("#routeEditor").dialog({ + title: "Edit Route", + minHeight: 30, width: "auto", resizable: false, + position: {my: "center top+20", at: "top", of: d3.event}, + close: function() { + if ($("#addRoute").hasClass('pressed')) completeNewRoute(); + if ($("#routeSplit").hasClass('pressed')) $("#routeSplit").removeClass('pressed'); + unselect(); + } + }); + } else {elSelected = null;} + + if (modules.editRoute) {return;} + modules.editRoute = true; + + function routeAddControlPoint(point) { + let dists = []; + debug.select(".controlPoints").selectAll("circle").each(function() { + const x = +d3.select(this).attr("cx"); + const y = +d3.select(this).attr("cy"); + dists.push(Math.hypot(point[0] - x, point[1] - y)); + }); + let index = dists.length; + if (dists.length > 1) { + const sorted = dists.slice(0).sort(function(a, b) {return a-b;}); + const closest = dists.indexOf(sorted[0]); + const next = dists.indexOf(sorted[1]); + if (closest <= next) {index = closest+1;} else {index = next+1;} + } + const before = ":nth-child(" + (index + 1) + ")"; + debug.select(".controlPoints").insert("circle", before) + .attr("cx", point[0]).attr("cy", point[1]).attr("r", 0.35) + .call(d3.drag().on("drag", routePointDrag)) + .on("click", function(d) { + $(this).remove(); + routeRedraw(); + }); + routeRedraw(); + } + + function routeDrawPoints() { + if (!elSelected.size()) return; + const node = elSelected.node(); + const l = node.getTotalLength(); + const parts = (l / 5) >> 0; // number of points + let inc = l / parts; // increment + if (inc === Infinity) inc = l; // 2 control points for short routes + // draw control points + for (let i = 0; i <= l; i += inc) { + const p = node.getPointAtLength(i); + addRoutePoint(p); + } + // convert length to distance + routeLength.innerHTML = rn(l * distanceScale.value) + " " + distanceUnit.value; + } + + function addRoutePoint(point) { + const controlPoints = debug.select(".controlPoints").size() + ? debug.select(".controlPoints") + : debug.append("g").attr("class", "controlPoints"); + controlPoints.append("circle") + .attr("cx", point.x).attr("cy", point.y).attr("r", 0.35) + .call(d3.drag().on("drag", routePointDrag)) + .on("click", function(d) { + if ($("#routeSplit").hasClass('pressed')) { + routeSplitInPoint(this); + } else { + $(this).remove(); + routeRedraw(); + } + }); + } + + function routePointDrag() { + d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y); + routeRedraw(); + } + + function routeRedraw() { + let points = []; + debug.select(".controlPoints").selectAll("circle").each(function() { + const el = d3.select(this); + points.push({scX: +el.attr("cx"), scY: +el.attr("cy")}); + }); + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); + elSelected.attr("d", lineGen(points)); + // get route distance + const l = elSelected.node().getTotalLength(); + routeLength.innerHTML = rn(l * distanceScale.value) + " " + distanceUnit.value; + } + + function addNewRoute() { + let routeType = elSelected && elSelected.node() ? elSelected.node().parentNode.id : "searoutes"; + const group = routes.select("#"+routeType); + const id = routeType + "" + group.selectAll("*").size(); + elSelected = group.append("path").attr("data-route", "new").attr("id", id).on("click", editRoute); + routeUpdateGroups(); + $("#routeEditor").dialog({ + title: "Edit Route", minHeight: 30, width: "auto", resizable: false, + close: function() { + if ($("#addRoute").hasClass('pressed')) completeNewRoute(); + if ($("#routeSplit").hasClass('pressed')) $("#routeSplit").removeClass('pressed'); + unselect(); + } + }); + } + + function newRouteAddPoint() { + const point = d3.mouse(this); + const x = rn(point[0],2), y = rn(point[1],2); + addRoutePoint({x, y}); + routeRedraw(); + } + + function completeNewRoute() { + $("#routeNew, #addRoute").removeClass('pressed'); + restoreDefaultEvents(); + if (!elSelected.size()) return; + if (elSelected.attr("data-route") === "new") { + routeRedraw(); + elSelected.attr("data-route", ""); + const node = elSelected.node(); + const l = node.getTotalLength(); + let pathCells = []; + for (let i = 0; i <= l; i ++) { + const p = node.getPointAtLength(i); + const cell = diagram.find(p.x, p.y); + if (!cell) {return;} + pathCells.push(cell.index); + } + const uniqueCells = [...new Set(pathCells)]; + uniqueCells.map(function(c) { + if (cells[c].path !== undefined) {cells[c].path += 1;} + else {cells[c].path = 1;} + }); + } + tip("", true); + } + + function routeUpdateGroups() { + routeGroup.innerHTML = ""; + routes.selectAll("g").each(function() { + const opt = document.createElement("option"); + opt.value = opt.innerHTML = this.id; + routeGroup.add(opt); + }); + } + + function routeSplitInPoint(clicked) { + const group = d3.select(elSelected.node().parentNode); + $("#routeSplit").removeClass('pressed'); + const points1 = [],points2 = []; + let points = points1; + debug.select(".controlPoints").selectAll("circle").each(function() { + const el = d3.select(this); + points.push({scX: +el.attr("cx"), scY: +el.attr("cy")}); + if (this === clicked) { + points = points2; + points.push({scX: +el.attr("cx"), scY: +el.attr("cy")}); + } + el.remove(); + }); + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); + elSelected.attr("d", lineGen(points1)); + const id = routeGroup.value + "" + group.selectAll("*").size(); + group.append("path").attr("id", id).attr("d", lineGen(points2)).on("click", editRoute); + routeDrawPoints(); + } + + $("#routeGroup").change(function() { + $(elSelected.node()).detach().appendTo($("#"+this.value)); + }); + + // open legendsEditor + document.getElementById("routeLegend").addEventListener("click", function() { + let id = elSelected.attr("id"); + editLegends(id, id); + }); + + $("#routeNew").click(function() { + if ($(this).hasClass('pressed')) { + completeNewRoute(); + } else { + // enter creation mode + $(".pressed").removeClass('pressed'); + $("#routeNew, #addRoute").addClass('pressed'); + debug.select(".controlPoints").selectAll("*").remove(); + addNewRoute(); + viewbox.style("cursor", "crosshair").on("click", newRouteAddPoint); + tip("Click on map to add route point", true); + } + }); + + $("#routeRemove").click(function() { + alertMessage.innerHTML = `Are you sure you want to remove the route?`; + $("#alert").dialog({resizable: false, title: "Remove route", + buttons: { + Remove: function() { + $(this).dialog("close"); + elSelected.remove(); + $("#routeEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }) + }); + } + + function editIcon() { + if (customization) return; + if (elSelected) if (this.isSameNode(elSelected.node())) return; + + unselect(); + closeDialogs("#iconEditor, .stable"); + elSelected = d3.select(this).call(d3.drag().on("start", elementDrag)).classed("draggable", true); + + // update group parameters + const group = d3.select(this.parentNode); + iconUpdateGroups(); + iconGroup.value = group.attr("id"); + iconFillColor.value = group.attr("fill"); + iconStrokeColor.value = group.attr("stroke"); + iconSize.value = group.attr("size"); + iconStrokeWidth.value = group.attr("stroke-width"); + + $("#iconEditor").dialog({ + title: "Edit icon: " + group.attr("id"), + minHeight: 30, width: "auto", resizable: false, + position: {my: "center top+20", at: "top", of: d3.event}, + close: unselect + }); + + if (modules.editIcon) {return;} + modules.editIcon = true; + + $("#iconGroups").click(function() { + $("#iconEditor > button").not(this).toggle(); + $("#iconGroupsSelection").toggle(); + }); + + function iconUpdateGroups() { + iconGroup.innerHTML = ""; + const anchor = group.attr("id").includes("anchor"); + icons.selectAll("g").each(function(d) { + const id = d3.select(this).attr("id"); + if (id === "burgs") return; + if (!anchor && id.includes("anchor")) return; + if (anchor && !id.includes("anchor")) return; + const opt = document.createElement("option"); + opt.value = opt.innerHTML = id; + iconGroup.add(opt); + }); + } + + $("#iconGroup").change(function() { + const newGroup = this.value; + const to = $("#icons > #"+newGroup); + $(elSelected.node()).detach().appendTo(to); + }); + + $("#iconCopy").click(function() { + const group = d3.select(elSelected.node().parentNode); + const copy = elSelected.node().cloneNode(); + copy.removeAttribute("data-id"); // remove assignment to burg if any + const tr = parseTransform(copy.getAttribute("transform")); + const shift = 10 / Math.sqrt(scale); + let transform = "translate(" + rn(tr[0] - shift, 1) + "," + rn(tr[1] - shift, 1) + ")"; + for (let i=2; group.selectAll("[transform='" + transform + "']").size() > 0; i++) { + transform = "translate(" + rn(tr[0] - shift * i, 1) + "," + rn(tr[1] - shift * i, 1) + ")"; + } + copy.setAttribute("transform", transform); + group.node().insertBefore(copy, null); + copy.addEventListener("click", editIcon); + }); + + $("#iconRemoveGroup").click(function() { + const group = d3.select(elSelected.node().parentNode); + const count = group.selectAll("*").size(); + if (count < 2) { + group.remove(); + $("#labelEditor").dialog("close"); + return; + } + const message = "Are you sure you want to remove all '" + iconGroup.value + "' icons (" + count + ")?"; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Remove icon group", + buttons: { + Remove: function() { + $(this).dialog("close"); + group.remove(); + $("#iconEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + + $("#iconColors").click(function() { + $("#iconEditor > button").not(this).toggle(); + $("#iconColorsSection").toggle(); + }); + + $("#iconFillColor").change(function() { + const group = d3.select(elSelected.node().parentNode); + group.attr("fill", this.value); + }); + + $("#iconStrokeColor").change(function() { + const group = d3.select(elSelected.node().parentNode); + group.attr("stroke", this.value); + }); + + $("#iconSetSize").click(function() { + $("#iconEditor > button").not(this).toggle(); + $("#iconSizeSection").toggle(); + }); + + $("#iconSize").change(function() { + const group = d3.select(elSelected.node().parentNode); + const size = +this.value; + group.attr("size", size); + group.selectAll("*").each(function() {d3.select(this).attr("width", size).attr("height", size)}); + }); + + $("#iconStrokeWidth").change(function() { + const group = d3.select(elSelected.node().parentNode); + group.attr("stroke-width", this.value); + }); + + $("#iconRemove").click(function() { + alertMessage.innerHTML = `Are you sure you want to remove the icon?`; + $("#alert").dialog({resizable: false, title: "Remove icon", + buttons: { + Remove: function() { + $(this).dialog("close"); + elSelected.remove(); + $("#iconEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }) + }); + } + + function editReliefIcon() { + if (customization) return; + if (elSelected) if (this.isSameNode(elSelected.node())) return; + + unselect(); + closeDialogs("#reliefEditor, .stable"); + elSelected = d3.select(this).raise().call(d3.drag().on("start", elementDrag)).classed("draggable", true); + const group = elSelected.node().parentNode.id; + reliefGroup.value = group; + + let bulkRemoveSection = document.getElementById("reliefBulkRemoveSection"); + if (bulkRemoveSection.style.display != "none") reliefBulkRemove.click(); + + $("#reliefEditor").dialog({ + title: "Edit relief icon", + minHeight: 30, width: "auto", resizable: false, + position: {my: "center top+40", at: "top", of: d3.event}, + close: unselect + }); + + if (modules.editReliefIcon) {return;} + modules.editReliefIcon = true; + + $("#reliefGroups").click(function() { + $("#reliefEditor > button").not(this).toggle(); + $("#reliefGroupsSelection").toggle(); + }); + + $("#reliefGroup").change(function() { + const type = this.value; + const bbox = elSelected.node().getBBox(); + const cx = bbox.x; + const cy = bbox.y + bbox.height / 2; + const cell = diagram.find(cx, cy).index; + const height = cell !== undefined ? cells[cell].height : 50; + elSelected.remove(); + elSelected = addReliefIcon(height / 100, type, cx, cy, cell); + elSelected.call(d3.drag().on("start", elementDrag)); + }); + + $("#reliefCopy").click(function() { + const group = d3.select(elSelected.node().parentNode); + const copy = elSelected.node().cloneNode(true); + const tr = parseTransform(copy.getAttribute("transform")); + const shift = 10 / Math.sqrt(scale); + let transform = "translate(" + rn(tr[0] - shift, 1) + "," + rn(tr[1] - shift, 1) + ")"; + for (let i=2; group.selectAll("[transform='" + transform + "']").size() > 0; i++) { + transform = "translate(" + rn(tr[0] - shift * i, 1) + "," + rn(tr[1] - shift * i, 1) + ")"; + } + copy.setAttribute("transform", transform); + group.node().insertBefore(copy, null); + copy.addEventListener("click", editReliefIcon); + }); + + $("#reliefAddfromEditor").click(function() { + clickToAdd(); // to load on click event function + $("#addRelief").click(); + }); + + $("#reliefRemoveGroup").click(function() { + const group = d3.select(elSelected.node().parentNode); + const count = group.selectAll("*").size(); + if (count < 2) { + group.selectAll("*").remove(); + $("#labelEditor").dialog("close"); + return; + } + const message = "Are you sure you want to remove all '" + reliefGroup.value + "' icons (" + count + ")?"; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Remove all icons within group", + buttons: { + Remove: function() { + $(this).dialog("close"); + group.selectAll("*").remove(); + $("#reliefEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + + $("#reliefBulkRemove").click(function() { + $("#reliefEditor > button").not(this).toggle(); + let section = document.getElementById("reliefBulkRemoveSection"); + if (section.style.display === "none") { + section.style.display = "inline-block"; + tip("Drag to remove relief icons in radius", true); + viewbox.style("cursor", "crosshair").call(d3.drag().on("drag", dragToRemoveReliefIcons)); + customization = 5; + } else { + section.style.display = "none"; + restoreDefaultEvents(); + customization = 0; + } + }); + + function dragToRemoveReliefIcons() { + let point = d3.mouse(this); + let cell = diagram.find(point[0], point[1]).index; + let radius = +reliefBulkRemoveRadius.value; + let r = rn(6 / graphSize * radius, 1); + moveCircle(point[0], point[1], r); + let selection = defineBrushSelection(cell, radius); + if (selection) removeReliefIcons(selection); + } + + function removeReliefIcons(selection) { + if (selection.length === 0) return; + selection.map(function(index) { + const selected = terrain.selectAll("g").selectAll("g[data-cell='"+index+"']"); + selected.remove(); + }); + } + + $("#reliefRemove").click(function() { + alertMessage.innerHTML = `Are you sure you want to remove the icon?`; + $("#alert").dialog({resizable: false, title: "Remove relief icon", + buttons: { + Remove: function() { + $(this).dialog("close"); + elSelected.remove(); + $("#reliefEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }) + }); + } + + function editBurg() { + if (customization) return; + unselect(); + closeDialogs("#burgEditor, .stable"); + elSelected = d3.select(this); + const id = +elSelected.attr("data-id"); + if (id === undefined) return; + d3.selectAll("[data-id='" + id + "']").call(d3.drag().on("start", elementDrag)).classed("draggable", true); + + // update Burg details + const type = elSelected.node().parentNode.id; + const labelGroup = burgLabels.select("#"+type); + const iconGroup = burgIcons.select("#"+type); + burgNameInput.value = manors[id].name; + updateBurgsGroupOptions(); + burgSelectGroup.value = labelGroup.attr("id"); + burgSelectDefaultFont.value = fonts.indexOf(labelGroup.attr("data-font")); + burgSetLabelSize.value = labelGroup.attr("data-size"); + burgLabelColorInput.value = toHEX(labelGroup.attr("fill")); + burgLabelOpacity.value = labelGroup.attr("opacity") === undefined ? 1 : +labelGroup.attr("opacity"); + const tr = parseTransform(elSelected.attr("transform")); + burgLabelAngle.value = tr[2]; + burgLabelAngleOutput.innerHTML = Math.abs(+tr[2]) + "°"; + burgIconSize.value = iconGroup.attr("size"); + burgIconFillOpacity.value = iconGroup.attr("fill-opacity") === undefined ? 1 : +iconGroup.attr("fill-opacity"); + burgIconFillColor.value = iconGroup.attr("fill"); + burgIconStrokeWidth.value = iconGroup.attr("stroke-width"); + burgIconStrokeOpacity.value = iconGroup.attr("stroke-opacity") === undefined ? 1 : +iconGroup.attr("stroke-opacity"); + burgIconStrokeColor.value = iconGroup.attr("stroke"); + const cell = cells[manors[id].cell]; + if (cell.region !== "neutral" && cell.region !== undefined) { + burgToggleCapital.disabled = false; + const capital = states[manors[id].region] ? id === states[manors[id].region].capital ? 1 : 0 : 0; + d3.select("#burgToggleCapital").classed("pressed", capital); + } else { + burgToggleCapital.disabled = true; + d3.select("#burgToggleCapital").classed("pressed", false); + } + d3.select("#burgTogglePort").classed("pressed", cell.port !== undefined); + burgPopulation.value = manors[id].population; + burgPopulationFriendly.value = rn(manors[id].population * urbanization.value * populationRate.value * 1000); + + $("#burgEditor").dialog({ + title: "Edit Burg: " + manors[id].name, + minHeight: 30, width: "auto", resizable: false, + position: {my: "center top+40", at: "top", of: d3.event}, + close: function() { + d3.selectAll("[data-id='" + id + "']").call(d3.drag().on("drag", null)).classed("draggable", false); + elSelected = null; + } + }); + + if (modules.editBurg) return; + modules.editBurg = true; + + loadDefaultFonts(); + + function updateBurgsGroupOptions() { + burgSelectGroup.innerHTML = ""; + burgIcons.selectAll("g").each(function(d) { + const opt = document.createElement("option"); + opt.value = opt.innerHTML = d3.select(this).attr("id"); + burgSelectGroup.add(opt); + }); + } + + $("#burgEditor > button").not("#burgAddfromEditor").not("#burgRelocate").not("#burgRemove").click(function() { + if ($(this).next().is(":visible")) { + $("#burgEditor > button").show(); + $(this).next("div").hide(); + } else { + $("#burgEditor > *").not(this).hide(); + $(this).next("div").show(); + } + }); + + $("#burgEditor > div > button").click(function() { + if ($(this).next().is(":visible")) { + $("#burgEditor > div > button").show(); + $(this).parent().prev().show(); + $(this).next("div").hide(); + } else { + $("#burgEditor > div > button").not(this).hide(); + $(this).parent().prev().hide(); + $(this).next("div").show(); + } + }); + + $("#burgSelectGroup").change(function() { + const id = +elSelected.attr("data-id"); + const g = this.value; + moveBurgToGroup(id, g); + }); + + $("#burgInputGroup").change(function() { + let newGroup = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (Number.isFinite(+newGroup.charAt(0))) newGroup = "g" + newGroup; + if (burgLabels.select("#"+newGroup).size()) { + tip('The group "'+ newGroup + '" is already exists'); + return; + } + burgInputGroup.value = ""; + // clone old group assigning new id + const id = elSelected.node().parentNode.id; + const l = burgLabels.select("#"+id).node().cloneNode(false); + l.id = newGroup; + const i = burgIcons.select("#"+id).node().cloneNode(false); + i.id = newGroup; + burgLabels.node().insertBefore(l, null); + burgIcons.node().insertBefore(i, null); + // select new group + const opt = document.createElement("option"); + opt.value = opt.innerHTML = newGroup; + burgSelectGroup.add(opt); + $("#burgSelectGroup").val(newGroup).change(); + $("#burgSelectGroup, #burgInputGroup").toggle(); + updateLabelGroups(); + }); + + $("#burgAddGroup").click(function() { + if ($("#burgInputGroup").css("display") === "none") { + $("#burgInputGroup").css("display", "inline-block"); + $("#burgSelectGroup").css("display", "none"); + burgInputGroup.focus(); + } else { + $("#burgSelectGroup").css("display", "inline-block"); + $("#burgInputGroup").css("display", "none"); + } + }); + + $("#burgRemoveGroup").click(function() { + const group = d3.select(elSelected.node().parentNode); + const type = group.attr("id"); + const id = +elSelected.attr("data-id"); + const count = group.selectAll("*").size(); + const message = "Are you sure you want to remove all Burgs (" + count + ") of that group?"; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Remove Burgs", + buttons: { + Remove: function() { + $(this).dialog("close"); + group.selectAll("*").each(function(d) { + const id = +d3.select(this).attr("data-id"); + if (id === undefined) return; + const cell = manors[id].cell; + const state = manors[id].region; + if (states[state]) { + if (states[state].capital === id) states[state].capital = "select"; + states[state].burgs --; + } + manors[id].region = "removed"; + cells[cell].manor = undefined; + }); + burgLabels.select("#"+type).selectAll("*").remove(); + burgIcons.select("#"+type).selectAll("*").remove(); + $("#icons g[id*='anchors'] [data-id=" + id + "]").parent().children().remove(); + closeDialogs(".stable"); + updateCountryEditors(); + $("#burgEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + + }); + + $("#burgNameInput").on("input", function() { + if (this.value === "") { + tip("Name should not be blank, set opacity to 0 to hide label or remove button to delete"); + return; + } + const id = +elSelected.attr("data-id"); + burgLabels.selectAll("[data-id='" + id + "']").text(this.value); + manors[id].name = this.value; + $("div[aria-describedby='burgEditor'] .ui-dialog-title").text("Edit Burg: " + this.value); + }); + + $("#burgNameReCulture, #burgNameReRandom").click(function() { + const id = +elSelected.attr("data-id"); + const culture = this.id === "burgNameReCulture" ? manors[id].culture : Math.floor(Math.random() * cultures.length); + const name = generateName(culture); + burgLabels.selectAll("[data-id='" + id + "']").text(name); + manors[id].name = name; + burgNameInput.value = name; + $("div[aria-describedby='burgEditor'] .ui-dialog-title").text("Edit Burg: " + name); + }); + + $("#burgToggleExternalFont").click(function() { + if ($("#burgInputExternalFont").css("display") === "none") { + $("#burgInputExternalFont").css("display", "inline-block"); + $("#burgSelectDefaultFont").css("display", "none"); + burgInputExternalFont.focus(); + } else { + $("#burgSelectDefaultFont").css("display", "inline-block"); + $("#burgInputExternalFont").css("display", "none"); + } + }); + + $("#burgSelectDefaultFont").change(function() { + const type = elSelected.node().parentNode.id; + const group = burgLabels.select("#"+type); + if (burgSelectDefaultFont.value === "") return; + const font = fonts[burgSelectDefaultFont.value].split(':')[0].replace(/\+/g, " "); + group.attr("font-family", font).attr("data-font", fonts[burgSelectDefaultFont.value]); + }); + + $("#burgInputExternalFont").change(function() { + fetchFonts(this.value).then(fetched => { + if (!fetched) return; + burgToggleExternalFont.click(); + burgInputExternalFont.value = ""; + if (fetched === 1) $("#burgSelectDefaultFont").val(fonts.length - 1).change(); + }); + }); + + $("#burgSetLabelSize").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgLabels.select("#"+type); + group.attr("data-size", +this.value); + invokeActiveZooming(); + }); + + $("#burgLabelColorInput").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgLabels.select("#"+type); + group.attr("fill", this.value); + }); + + $("#burgLabelOpacity").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgLabels.select("#"+type); + group.attr("opacity", +this.value); + }); + + $("#burgLabelAngle").on("input", function() { + const id = +elSelected.attr("data-id"); + const el = burgLabels.select("[data-id='"+ id +"']"); + const tr = parseTransform(el.attr("transform")); + const c = el.node().getBBox(); + burgLabelAngleOutput.innerHTML = Math.abs(+this.value) + "°"; + const angle = +this.value; + const transform = `translate(${tr[0]},${tr[1]}) rotate(${angle} ${(c.x+c.width/2)} ${(c.y+c.height/2)})`; + el.attr("transform", transform); + }); + + $("#burgIconSize").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgIcons.select("#"+type); + const size = +this.value; + group.attr("size", size); + group.selectAll("*").each(function() {d3.select(this).attr("r", size)}); + }); + + $("#burgIconFillOpacity").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgIcons.select("#"+type); + group.attr("fill-opacity", +this.value); + }); + + $("#burgIconFillColor").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgIcons.select("#"+type); + group.attr("fill", this.value); + }); + + $("#burgIconStrokeWidth").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgIcons.select("#"+type); + group.attr("stroke-width", +this.value); + }); + + $("#burgIconStrokeOpacity").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgIcons.select("#"+type); + group.attr("stroke-opacity", +this.value); + }); + + $("#burgIconStrokeColor").on("input", function() { + const type = elSelected.node().parentNode.id; + const group = burgIcons.select("#"+type); + group.attr("stroke", this.value); + }); + + $("#burgToggleCapital").click(function() { + const id = +elSelected.attr("data-id"); + const state = manors[id].region; + if (states[state] === undefined) return; + const capital = states[manors[id].region] ? id === states[manors[id].region].capital ? 0 : 1 : 1; + if (capital && states[state].capital !== "select") { + // move oldCapital to a town group + const oldCapital = states[state].capital; + moveBurgToGroup(oldCapital, "towns"); + } + states[state].capital = capital ? id : "select"; + d3.select("#burgToggleCapital").classed("pressed", capital); + const g = capital ? "capitals" : "towns"; + moveBurgToGroup(id, g); + }); + + $("#burgTogglePort").click(function() { + const id = +elSelected.attr("data-id"); + const cell = cells[manors[id].cell]; + const markAsPort = cell.port === undefined ? true : undefined; + cell.port = markAsPort; + d3.select("#burgTogglePort").classed("pressed", markAsPort); + if (markAsPort) { + const type = elSelected.node().parentNode.id; + const ag = type === "capitals" ? "#capital-anchors" : "#town-anchors"; + const group = icons.select(ag); + const size = +group.attr("size"); + const x = rn(manors[id].x - size * 0.47, 2); + const y = rn(manors[id].y - size * 0.47, 2); + group.append("use").attr("xlink:href", "#icon-anchor").attr("data-id", id) + .attr("x", x).attr("y", y).attr("width", size).attr("height", size) + .on("click", editIcon); + } else { + $("#icons g[id*='anchors'] [data-id=" + id + "]").remove(); + } + }); + + $("#burgPopulation").on("input", function() { + const id = +elSelected.attr("data-id"); + burgPopulationFriendly.value = rn(this.value * urbanization.value * populationRate.value * 1000); + manors[id].population = +this.value; + }); + + $("#burgRelocate").click(function() { + if ($(this).hasClass('pressed')) { + $(".pressed").removeClass('pressed'); + restoreDefaultEvents(); + tip("", true); + } else { + $(".pressed").removeClass('pressed'); + const id = elSelected.attr("data-id"); + $(this).addClass('pressed').attr("data-id", id); + viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick); + tip("Click on map to relocate burg. Hold Shift for continuous move", true); + } + }); + + // open legendsEditor + document.getElementById("burglLegend").addEventListener("click", function() { + let burg = +elSelected.attr("data-id"); + let id = "burg" + burg; + let name = manors[burg].name; + editLegends(id, name); + }); + + // move burg to a different cell + function relocateBurgOnClick() { + const point = d3.mouse(this); + const index = getIndex(point); + const i = +$("#burgRelocate").attr("data-id"); + if (isNaN(i) || !manors[i]) return; + + if (cells[index].height < 20) { + tip("Cannot place burg in the water! Select a land cell", null, "error"); + return; + } + + if (cells[index].manor !== undefined && cells[index].manor !== i) { + tip("There is already a burg in this cell. Please select a free cell", null, "error"); + $('#grid').fadeIn(); + d3.select("#toggleGrid").classed("buttonoff", false); + return; + } + + let region = cells[index].region; + const oldRegion = manors[i].region; + // relocating capital to other country you "conquer" target cell + if (states[oldRegion] && states[oldRegion].capital === i) { + if (region !== oldRegion) { + tip("Capital cannot be moved to another country!", null, "error"); + return; + } + } + + if (d3.event.shiftKey === false) { + $("#burgRelocate").removeClass("pressed"); + restoreDefaultEvents(); + tip("", true); + if (region !== oldRegion) { + recalculateStateData(oldRegion); + recalculateStateData(region); + updateCountryEditors(); + } + } + + const x = rn(point[0],2), y = rn(point[1],2); + burgIcons.select("circle[data-id='"+i+"']").attr("transform", null).attr("cx", x).attr("cy", y); + burgLabels.select("text[data-id='"+i+"']").attr("transform", null).attr("x", x).attr("y", y); + const anchor = icons.select("use[data-id='"+i+"']"); + if (anchor.size()) { + const size = anchor.attr("width"); + const xa = rn(x - size * 0.47, 2); + const ya = rn(y - size * 0.47, 2); + anchor.attr("transform", null).attr("x", xa).attr("y", ya); + } + cells[index].manor = i; + cells[manors[i].cell].manor = undefined; + manors[i].x = x, manors[i].y = y, manors[i].region = region, manors[i].cell = index; + } + + // open in MFCG + $("#burgSeeInMFCG").click(function() { + const id = +elSelected.attr("data-id"); + const name = manors[id].name; + const cell = manors[id].cell; + const pop = rn(manors[id].population); + const size = pop > 65 ? 65 : pop < 6 ? 6 : pop; + const s = seed + "" + id; + const hub = cells[cell].crossroad > 2 ? 1 : 0; + const river = cells[cell].river ? 1 : 0; + const coast = cells[cell].port !== undefined ? 1 : 0; + const sec = pop > 40 ? 1 : Math.random() < pop / 100 ? 1 : 0; + const thr = sec && Math.random() < 0.8 ? 1 : 0; + const url = "http://fantasycities.watabou.ru/"; + let params = `?name=${name}&size=${size}&seed=${s}&hub=${hub}&random=0&continuous=0`; + params += `&river=${river}&coast=${coast}&citadel=${id&1}&plaza=${sec}&temple=${thr}&walls=${sec}&shantytown=${sec}`; + const win = window.open(url+params, '_blank'); + win.focus(); + }); + + $("#burgAddfromEditor").click(function() { + clickToAdd(); // to load on click event function + $("#addBurg").click(); + }); + + $("#burgRemove").click(function() { + alertMessage.innerHTML = `Are you sure you want to remove the Burg?`; + $("#alert").dialog({resizable: false, title: "Remove Burg", + buttons: { + Remove: function() { + $(this).dialog("close"); + const id = +elSelected.attr("data-id"); + d3.selectAll("[data-id='" + id + "']").remove(); + const cell = manors[id].cell; + const state = manors[id].region; + if (states[state]) { + if (states[state].capital === id) states[state].capital = "select"; + states[state].burgs --; + } + manors[id].region = "removed"; + cells[cell].manor = undefined; + closeDialogs(".stable"); + updateCountryEditors(); + }, + Cancel: function() {$(this).dialog("close");} + } + }) + }); + } + + function editMarker() { + if (customization) return; + + unselect(); + closeDialogs("#markerEditor, .stable"); + elSelected = d3.select(this).call(d3.drag().on("start", elementDrag)).classed("draggable", true); + + $("#markerEditor").dialog({ + title: "Edit Marker", + minHeight: 30, width: "auto", maxWidth: 275, resizable: false, + position: {my: "center top+30", at: "bottom", of: d3.event}, + close: unselect + }); + + // update inputs + let id = elSelected.attr("href"); + let symbol = d3.select("#defs-markers").select(id); + let icon = symbol.select("text"); + markerSelectGroup.value = id.slice(1); + markerIconSize.value = parseFloat(icon.attr("font-size")); + markerIconShiftX.value = parseFloat(icon.attr("x")); + markerIconShiftY.value = parseFloat(icon.attr("y")); + markerIconFill.value = icon.attr("fill"); + markerIconStrokeWidth.value = icon.attr("stroke-width"); + markerIconStroke.value = icon.attr("stroke"); + markerSize.value = elSelected.attr("data-size"); + markerBase.value = symbol.select("path").attr("fill"); + markerFill.value = symbol.select("circle").attr("fill"); + let opacity = symbol.select("circle").attr("opacity"); + markerToggleBubble.className = opacity === "0" ? "icon-info" : "icon-info-circled"; + + let table = document.getElementById("markerIconTable"); + let selected = table.getElementsByClassName("selected"); + if (selected.length) selected[0].removeAttribute("class"); + selected = document.querySelectorAll("#markerIcon" + icon.text().codePointAt()); + if (selected.length) selected[0].className = "selected"; + markerIconCustom.value = selected.length ? "" : icon.text(); + + if (modules.editMarker) return; + modules.editMarker = true; + + $("#markerGroup").click(function() { + $("#markerEditor > button").not(this).toggle(); + $("#markerGroupSection").toggle(); + updateMarkerGroupOptions(); + }); + + function updateMarkerGroupOptions() { + markerSelectGroup.innerHTML = ""; + d3.select("#defs-markers").selectAll("symbol").each(function() { + let opt = document.createElement("option"); + opt.value = opt.innerHTML = this.id; + markerSelectGroup.add(opt); + }); + let id = elSelected.attr("href").slice(1); + markerSelectGroup.value = id; + } + + // on add marker type click + document.getElementById("markerAddGroup").addEventListener("click", function() { + if ($("#markerInputGroup").css("display") === "none") { + $("#markerInputGroup").css("display", "inline-block"); + $("#markerSelectGroup").css("display", "none"); + markerInputGroup.focus(); + } else { + $("#markerSelectGroup").css("display", "inline-block"); + $("#markerInputGroup").css("display", "none"); + } + }); + + // on marker type change + document.getElementById("markerSelectGroup").addEventListener("change", function() { + elSelected.attr("href", "#"+this.value); + elSelected.attr("data-id", "#"+this.value); + }); + + // on new type input + document.getElementById("markerInputGroup").addEventListener("change", function() { + let newGroup = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (Number.isFinite(+newGroup.charAt(0))) newGroup = "m" + newGroup; + if (d3.select("#defs-markers").select("#"+newGroup).size()) { + tip('The type "'+ newGroup + '" is already exists'); + return; + } + markerInputGroup.value = ""; + // clone old group assigning new id + let id = elSelected.attr("href"); + let l = d3.select("#defs-markers").select(id).node().cloneNode(true); + l.id = newGroup; + elSelected.attr("href", "#"+newGroup); + elSelected.attr("data-id", "#"+newGroup); + document.getElementById("defs-markers").insertBefore(l, null); + + // select new group + let opt = document.createElement("option"); + opt.value = opt.innerHTML = newGroup; + markerSelectGroup.add(opt); + $("#markerSelectGroup").val(newGroup).change(); + $("#markerSelectGroup, #markerInputGroup").toggle(); + updateMarkerGroupOptions(); + }); + + $("#markerIconButton").click(function() { + $("#markerEditor > button").not(this).toggle(); + $("#markerIconButtons").toggle(); + if (!$("#markerIconTable").text()) drawIconsList(icons); + }); + + $("#markerRemoveGroup").click(function() { + let id = elSelected.attr("href"); + let used = document.querySelectorAll("use[data-id='"+id+"']"); + let count = used.length === 1 ? "1 element" : used.length + " elements"; + const message = "Are you sure you want to remove the marker (" + count + ")?"; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Remove marker", + buttons: { + Remove: function() { + $(this).dialog("close"); + if (id !== "#marker0") d3.select("#defs-markers").select(id).remove(); + used.forEach(function(e) {e.remove();}); + updateMarkerGroupOptions(); + $("#markerEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + + function drawIconsList() { + let icons = [ + // emoticons in FF: + ["2693", "⚓", "Anchor"], + ["26EA", "⛪", "Church"], + ["1F3EF", "🏯", "Japanese Castle"], + ["1F3F0", "🏰", "Castle"], + ["1F5FC", "🗼", "Tower"], + ["1F3E0", "🏠", "House"], + ["1F3AA", "🎪", "Tent"], + ["1F3E8", "🏨", "Hotel"], + ["1F4B0", "💰", "Money bag"], + ["1F4A8", "💨", "Dashing away"], + ["1F334", "🌴", "Palm"], + ["1F335", "🌵", "Cactus"], + ["1F33E", "🌾", "Sheaf"], + ["1F5FB", "🗻", "Mountain"], + ["1F30B", "🌋", "Volcano"], + ["1F40E", "🐎", "Horse"], + ["1F434", "🐴", "Horse Face"], + ["1F42E", "🐮", "Cow"], + ["1F43A", "🐺", "Wolf Face"], + ["1F435", "🐵", "Monkey face"], + ["1F437", "🐷", "Pig face"], + ["1F414", "🐔", "Chiken"], + ["1F411", "🐑", "Eve"], + ["1F42B", "🐫", "Camel"], + ["1F418", "🐘", "Elephant"], + ["1F422", "🐢", "Turtle"], + ["1F40C", "🐌", "Snail"], + ["1F40D", "🐍", "Snake"], + ["1F433", "🐳", "Whale"], + ["1F42C", "🐬", "Dolphin"], + ["1F420", "🐟", "Fish"], + ["1F432", "🐲", "Dragon Head"], + ["1F479", "👹", "Ogre"], + ["1F47B", "👻", "Ghost"], + ["1F47E", "👾", "Alien"], + ["1F480", "💀", "Skull"], + ["1F374", "🍴", "Fork and knife"], + ["1F372", "🍲", "Food"], + ["1F35E", "🍞", "Bread"], + ["1F357", "🍗", "Poultry leg"], + ["1F347", "🍇", "Grapes"], + ["1F34F", "🍏", "Apple"], + ["1F352", "🍒", "Cherries"], + ["1F36F", "🍯", "Honey pot"], + ["1F37A", "🍺", "Beer"], + ["1F377", "🍷", "Wine glass"], + ["1F3BB", "🎻", "Violin"], + ["1F3B8", "🎸", "Guitar"], + ["26A1", "⚡", "Electricity"], + ["1F320", "🌠", "Shooting star"], + ["1F319", "🌙", "Crescent moon"], + ["1F525", "🔥", "Fire"], + ["1F4A7", "💧", "Droplet"], + ["1F30A", "🌊", "Wave"], + ["231B", "⌛", "Hourglass"], + ["1F3C6", "🏆", "Goblet"], + ["26F2", "⛲", "Fountain"], + ["26F5", "⛵", "Sailboat"], + ["26FA", "⛺", "Tend"], + ["1F489", "💉", "Syringe"], + ["1F4D6", "📚", "Books"], + ["1F3AF", "🎯", "Archery"], + ["1F52E", "🔮", "Magic ball"], + ["1F3AD", "🎭", "Performing arts"], + ["1F3A8", "🎨", "Artist palette"], + ["1F457", "👗", "Dress"], + ["1F451", "👑", "Crown"], + ["1F48D", "💍", "Ring"], + ["1F48E", "💎", "Gem"], + ["1F514", "🔔", "Bell"], + ["1F3B2", "🎲", "Die"], + // black and white icons in FF: + ["26A0", "⚠", "Alert"], + ["2317", "⌗", "Hash"], + ["2318", "⌘", "POI"], + ["2307", "⌇", "Wavy"], + ["21E6", "⇦", "Left arrow"], + ["21E7", "⇧", "Top arrow"], + ["21E8", "⇨", "Right arrow"], + ["21E9", "⇩", "Left arrow"], + ["21F6", "⇶", "Three arrows"], + ["2699", "⚙", "Gear"], + ["269B", "⚛", "Atom"], + ["0024", "$", "Dollar"], + ["2680", "⚀", "Die1"], + ["2681", "⚁", "Die2"], + ["2682", "⚂", "Die3"], + ["2683", "⚃", "Die4"], + ["2684", "⚄", "Die5"], + ["2685", "⚅", "Die6"], + ["26B4", "⚴", "Pallas"], + ["26B5", "⚵", "Juno"], + ["26B6", "⚶", "Vesta"], + ["26B7", "⚷", "Chiron"], + ["26B8", "⚸", "Lilith"], + ["263F", "☿", "Mercury"], + ["2640", "♀", "Venus"], + ["2641", "♁", "Earth"], + ["2642", "♂", "Mars"], + ["2643", "♃", "Jupiter"], + ["2644", "♄", "Saturn"], + ["2645", "♅", "Uranus"], + ["2646", "♆", "Neptune"], + ["2647", "♇", "Pluto"], + ["26B3", "⚳", "Ceres"], + ["2654", "♔", "Chess king"], + ["2655", "♕", "Chess queen"], + ["2656", "♖", "Chess rook"], + ["2657", "♗", "Chess bishop"], + ["2658", "♘", "Chess knight"], + ["2659", "♙", "Chess pawn"], + ["2660", "♠", "Spade"], + ["2663", "♣", "Club"], + ["2665", "♥", "Heart"], + ["2666", "♦", "Diamond"], + ["2698", "⚘", "Flower"], + ["2625", "☥", "Ankh"], + ["2626", "☦", "Orthodox"], + ["2627", "☧", "Chi Rho"], + ["2628", "☨", "Lorraine"], + ["2629", "☩", "Jerusalem"], + ["2670", "♰", "Syriac cross"], + ["2020", "†", "Dagger"], + ["262A", "☪", "Muslim"], + ["262D", "☭", "Soviet"], + ["262E", "☮", "Peace"], + ["262F", "☯", "Yin yang"], + ["26A4", "⚤", "Heterosexuality"], + ["26A2", "⚢", "Female homosexuality"], + ["26A3", "⚣", "Male homosexuality"], + ["26A5", "⚥", "Male and female"], + ["26AD", "⚭", "Rings"], + ["2690", "⚐", "White flag"], + ["2691", "⚑", "Black flag"], + ["263C", "☼", "Sun"], + ["263E", "☾", "Moon"], + ["2668", "♨", "Hot springs"], + ["2600", "☀", "Black sun"], + ["2601", "☁", "Cloud"], + ["2602", "☂", "Umbrella"], + ["2603", "☃", "Snowman"], + ["2604", "☄", "Comet"], + ["2605", "★", "Black star"], + ["2606", "☆", "White star"], + ["269D", "⚝", "Outlined star"], + ["2618", "☘", "Shamrock"], + ["21AF", "↯", "Lightning"], + ["269C", "⚜", "FleurDeLis"], + ["2622", "☢", "Radiation"], + ["2623", "☣", "Biohazard"], + ["2620", "☠", "Skull"], + ["2638", "☸", "Dharma"], + ["2624", "☤", "Caduceus"], + ["2695", "⚕", "Aeculapius staff"], + ["269A", "⚚", "Hermes staff"], + ["2697", "⚗", "Alembic"], + ["266B", "♫", "Music"], + ["2702", "✂", "Scissors"], + ["2696", "⚖", "Scales"], + ["2692", "⚒", "Hammer and pick"], + ["2694", "⚔", "Swords"] + ]; + + let table = document.getElementById("markerIconTable"), row = ""; + table.addEventListener("click", clickMarkerIconTable, false); + table.addEventListener("mouseover", hoverMarkerIconTable, false); + + for (let i=0; i < icons.length; i++) { + if (i%20 === 0) row = table.insertRow(0); + let cell = row.insertCell(0); + let icon = String.fromCodePoint(parseInt(icons[i][0],16)); + cell.innerHTML = icon; + cell.id = "markerIcon" + icon.codePointAt(); + cell.setAttribute("data-desc", icons[i][2]); + } + } + + function clickMarkerIconTable(e) { + if (e.target !== e.currentTarget) { + let table = document.getElementById("markerIconTable"); + let selected = table.getElementsByClassName("selected"); + if (selected.length) selected[0].removeAttribute("class"); + e.target.className = "selected"; + let id = elSelected.attr("href"); + let icon = e.target.innerHTML; + d3.select("#defs-markers").select(id).select("text").text(icon); + } + e.stopPropagation(); + } + + function hoverMarkerIconTable(e) { + if (e.target !== e.currentTarget) { + let desc = e.target.getAttribute("data-desc"); + tip(e.target.innerHTML + " " + desc); + } + e.stopPropagation(); + } + + // change marker icon size + document.getElementById("markerIconSize").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").attr("font-size", this.value + "px"); + }); + + // change marker icon x shift + document.getElementById("markerIconShiftX").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").attr("x", this.value + "%"); + }); + + // change marker icon y shift + document.getElementById("markerIconShiftY").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").attr("y", this.value + "%"); + }); + + // apply custom unicode icon on input + document.getElementById("markerIconCustom").addEventListener("input", function() { + if (!this.value) return; + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").text(this.value); + }); + + $("#markerStyleButton").click(function() { + $("#markerEditor > button").not(this).toggle(); + $("#markerStyleButtons").toggle(); + }); + + // change marker size + document.getElementById("markerSize").addEventListener("input", function() { + let id = elSelected.attr("data-id"); + let used = document.querySelectorAll("use[data-id='"+id+"']"); + let size = this.value; + used.forEach(function(e) {e.setAttribute("data-size", size);}); + invokeActiveZooming(); + }); + + // change marker base color + document.getElementById("markerBase").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select(id).select("path").attr("fill", this.value); + d3.select(id).select("circle").attr("stroke", this.value); + }); + + // change marker fill color + document.getElementById("markerFill").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select(id).select("circle").attr("fill", this.value); + }); + + // change marker icon y shift + document.getElementById("markerIconFill").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").attr("fill", this.value); + }); + + // change marker icon y shift + document.getElementById("markerIconStrokeWidth").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").attr("stroke-width", this.value); + }); + + // change marker icon y shift + document.getElementById("markerIconStroke").addEventListener("input", function() { + let id = elSelected.attr("href"); + d3.select("#defs-markers").select(id).select("text").attr("stroke", this.value); + }); + + // toggle marker bubble display + document.getElementById("markerToggleBubble").addEventListener("click", function() { + let id = elSelected.attr("href"); + let show = 1; + if (this.className === "icon-info-circled") { + this.className = "icon-info"; + show = 0; + } else { + this.className = "icon-info-circled";; + } + d3.select(id).select("circle").attr("opacity", show); + d3.select(id).select("path").attr("opacity", show); + }); + + // open legendsEditor + document.getElementById("markerLegendButton").addEventListener("click", function() { + let id = elSelected.attr("id"); + let symbol = elSelected.attr("href"); + let icon = d3.select("#defs-markers").select(symbol).select("text").text(); + let name = "Marker " + icon; + editLegends(id, name); + }); + + // click on master button to add new markers on click + document.getElementById("markerAdd").addEventListener("click", function() { + document.getElementById("addMarker").click(); + }); + + // remove marker on click + document.getElementById("markerRemove").addEventListener("click", function() { + alertMessage.innerHTML = "Are you sure you want to remove the marker?"; + $("#alert").dialog({resizable: false, title: "Remove marker", + buttons: { + Remove: function() { + $(this).dialog("close"); + elSelected.remove(); + $("#markerEditor").dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + } + + // clear elSelected variable + function unselect() { + tip("", true); + restoreDefaultEvents(); + if (customization === 5) customization = 0; + if (!elSelected) return; + elSelected.call(d3.drag().on("drag", null)).attr("class", null); + debug.selectAll("*").remove(); + viewbox.style("cursor", "default"); + elSelected = null; + } + + // transform string to array [translateX,translateY,rotateDeg,rotateX,rotateY,scale] + function parseTransform(string) { + if (!string) {return [0,0,0,0,0,1];} + const a = string.replace(/[a-z()]/g, "").replace(/[ ]/g, ",").split(","); + return [a[0] || 0, a[1] || 0, a[2] || 0, a[3] || 0, a[4] || 0, a[5] || 1]; + } + + // generic function to move any burg to any group + function moveBurgToGroup(id, g) { + $("#burgLabels [data-id=" + id + "]").detach().appendTo($("#burgLabels > #"+g)); + $("#burgIcons [data-id=" + id + "]").detach().appendTo($("#burgIcons > #"+g)); + const rSize = $("#burgIcons > #"+g).attr("size"); + $("#burgIcons [data-id=" + id + "]").attr("r", rSize); + const el = $("#icons g[id*='anchors'] [data-id=" + id + "]"); + if (el.length) { + const to = g === "towns" ? $("#town-anchors") : $("#capital-anchors"); + el.detach().appendTo(to); + const useSize = to.attr("size"); + const x = rn(manors[id].x - useSize * 0.47, 2); + const y = rn(manors[id].y - useSize * 0.47, 2); + el.attr("x", x).attr("y", y).attr("width", useSize).attr("height", useSize); + } + updateCountryEditors(); + } + + // generate cultures for a new map based on options and namesbase + function generateCultures() { + const count = +culturesInput.value; + cultures = d3.shuffle(defaultCultures).slice(0, count); + const centers = d3.range(cultures.length).map(function(d, i) { + const x = Math.floor(Math.random() * graphWidth * 0.8 + graphWidth * 0.1); + const y = Math.floor(Math.random() * graphHeight * 0.8 + graphHeight * 0.1); + const center = [x, y]; + cultures[i].center = center; + return center; + }); + cultureTree = d3.quadtree(centers); + } + + function manorsAndRegions() { + console.group('manorsAndRegions'); + calculateChains(); + rankPlacesGeography(); + locateCapitals(); + generateMainRoads(); + rankPlacesEconomy(); + locateTowns(); + getNames(); + shiftSettlements(); + checkAccessibility(); + defineRegions("withCultures"); + generatePortRoads(); + generateSmallRoads(); + generateOceanRoutes(); + calculatePopulation(); + drawManors(); + drawRegions(); + console.groupEnd('manorsAndRegions'); + } + + // Assess cells geographycal suitability for settlement + function rankPlacesGeography() { + console.time('rankPlacesGeography'); + land.map(function(c) { + let score = 0; + c.flux = rn(c.flux, 2); + // get base score from height (will be biom) + if (c.height <= 40) score = 2; + else if (c.height <= 50) score = 1.8; + else if (c.height <= 60) score = 1.6; + else if (c.height <= 80) score = 1.4; + score += (1 - c.height / 100) / 3; + if (c.ctype && Math.random() < 0.8 && !c.river) { + c.score = 0; // ignore 80% of extended cells + } else { + if (c.harbor) { + if (c.harbor === 1) {score += 1;} else {score -= 0.3;} // good sea harbor is valued + } + if (c.river) score += 1; // coastline is valued + if (c.river && c.ctype === 1) score += 1; // estuary is valued + if (c.flux > 1) score += Math.pow(c.flux, 0.3); // riverbank is valued + if (c.confluence) score += Math.pow(c.confluence, 0.7); // confluence is valued; + const neighbEv = c.neighbors.map(function(n) {if (cells[n].height >= 20) return cells[n].height;}); + const difEv = c.height - d3.mean(neighbEv); + // if (!isNaN(difEv)) score += difEv * 10 * (1 - c.height / 100); // local height maximums are valued + } + c.score = rn(Math.random() * score + score, 3); // add random factor + }); + land.sort(function(a, b) {return b.score - a.score;}); + console.timeEnd('rankPlacesGeography'); + } + + // Assess the cells economical suitability for settlement + function rankPlacesEconomy() { + console.time('rankPlacesEconomy'); + land.map(function(c) { + let score = c.score; + let path = c.path || 0; // roads are valued + if (path) { + path = Math.pow(path, 0.2); + const crossroad = c.crossroad || 0; // crossroads are valued + score = score + path + crossroad; + } + c.score = rn(Math.random() * score + score, 2); // add random factor + }); + land.sort(function(a, b) {return b.score - a.score;}); + console.timeEnd('rankPlacesEconomy'); + } + + // calculate population for manors, cells and states + function calculatePopulation() { + // neutral population factors < 1 as neutral lands are usually pretty wild + const ruralFactor = 0.5, urbanFactor = 0.9; + + // calculate population for each burg (based on trade/people attractors) + manors.map(function(m) { + const cell = cells[m.cell]; + let score = cell.score; + if (score <= 0) {score = rn(Math.random(), 2)} + if (cell.crossroad) {score += cell.crossroad;} // crossroads + if (cell.confluence) {score += Math.pow(cell.confluence, 0.3);} // confluences + if (m.i !== m.region && cell.port) {score *= 1.5;} // ports (not capital) + if (m.i === m.region && !cell.port) {score *= 2;} // land-capitals + if (m.i === m.region && cell.port) {score *= 3;} // port-capitals + if (m.region === "neutral") score *= urbanFactor; + const rnd = 0.6 + Math.random() * 0.8; // random factor + m.population = rn(score * rnd, 1); + }); + + // calculate rural population for each cell based on area + elevation (elevation to be changed to biome) + const graphSizeAdj = 90 / Math.sqrt(cells.length, 2); // adjust to different graphSize + land.map(function(l) { + let population = 0; + const elevationFactor = Math.pow(1 - l.height / 100, 3); + population = elevationFactor * l.area * graphSizeAdj; + if (l.region === "neutral") population *= ruralFactor; + l.pop = rn(population, 1); + }); + + // calculate population for each region + states.map(function(s, i) { + // define region burgs count + const burgs = $.grep(manors, function (e) { + return e.region === i; + }); + s.burgs = burgs.length; + // define region total and burgs population + let burgsPop = 0; // get summ of all burgs population + burgs.map(function(b) {burgsPop += b.population;}); + s.urbanPopulation = rn(burgsPop, 2); + const regionCells = $.grep(cells, function (e) { + return e.region === i; + }); + let cellsPop = 0; + regionCells.map(function(c) {cellsPop += c.pop}); + s.cells = regionCells.length; + s.ruralPopulation = rn(cellsPop, 1); + }); + + // collect data for neutrals + const neutralCells = $.grep(cells, function(e) {return e.region === "neutral";}); + if (neutralCells.length) { + let burgs = 0, urbanPopulation = 0, ruralPopulation = 0, area = 0; + manors.forEach(function(m) { + if (m.region !== "neutral") return; + urbanPopulation += m.population; + burgs++; + }); + neutralCells.forEach(function(c) { + ruralPopulation += c.pop; + area += cells[c.index].area; + }); + states.push({i: states.length, color: "neutral", name: "Neutrals", capital: "neutral", + cells: neutralCells.length, burgs, urbanPopulation: rn(urbanPopulation, 2), + ruralPopulation: rn(ruralPopulation, 2), area: rn(area)}); + } + } + + function locateCapitals() { + console.time('locateCapitals'); + // min distance detween capitals + const count = +regionsInput.value; + let spacing = (graphWidth + graphHeight) / 2 / count; + console.log(" states: " + count); + + for (let l = 0; manors.length < count; l++) { + const region = manors.length; + const x = land[l].data[0],y = land[l].data[1]; + let minDist = 10000; // dummy value + for (let c = 0; c < manors.length; c++) { + const dist = Math.hypot(x - manors[c].x, y - manors[c].y); + if (dist < minDist) minDist = dist; + if (minDist < spacing) break; + } + if (minDist >= spacing) { + const cell = land[l].index; + const closest = cultureTree.find(x, y); + const culture = getCultureId(closest); + manors.push({i: region, cell, x, y, region, culture}); + } + if (l === land.length - 1) { + console.error("Cannot place capitals with current spacing. Trying again with reduced spacing"); + l = -1, manors = [], spacing /= 1.2; + } + } + + // For each capital create a country + const scheme = count <= 8 ? colors8 : colors20; + const mod = +powerInput.value; + manors.forEach(function(m, i) { + const power = rn(Math.random() * mod / 2 + 1, 1); + const color = scheme(i / count); + states.push({i, color, power, capital: i}); + const p = cells[m.cell]; + p.manor = i; + p.region = i; + p.culture = m.culture; + }); + console.timeEnd('locateCapitals'); + } + + function locateTowns() { + console.time('locateTowns'); + const count = +manorsInput.value; + const neutral = +neutralInput.value; + const manorTree = d3.quadtree(); + manors.forEach(function(m) {manorTree.add([m.x, m.y]);}); + + for (let l = 0; manors.length < count && l < land.length; l++) { + const x = land[l].data[0],y = land[l].data[1]; + const c = manorTree.find(x, y); + const d = Math.hypot(x - c[0],y - c[1]); + if (d < 6) continue; + const cell = land[l].index; + let region = "neutral", culture = -1, closest = neutral; + for (let c = 0; c < states.length; c++) { + let dist = Math.hypot(manors[c].x - x, manors[c].y - y) / states[c].power; + const cap = manors[c].cell; + if (cells[cell].fn !== cells[cap].fn) dist *= 3; + if (dist < closest) {region = c; closest = dist;} + } + if (closest > neutral / 5 || region === "neutral") { + const closestCulture = cultureTree.find(x, y); + culture = getCultureId(closestCulture); + } else { + culture = manors[region].culture; + } + land[l].manor = manors.length; + land[l].culture = culture; + land[l].region = region; + manors.push({i: manors.length, cell, x, y, region, culture}); + manorTree.add([x, y]); + } + if (manors.length < count) { + const error = "Cannot place all burgs. Requested " + count + ", placed " + manors.length; + console.error(error); + } + console.timeEnd('locateTowns'); + } + + // shift settlements from cell point + function shiftSettlements() { + for (let i=0; i < manors.length; i++) { + const capital = i < regionsInput.value; + const cell = cells[manors[i].cell]; + let x = manors[i].x, y = manors[i].y; + if ((capital && cell.harbor) || cell.harbor === 1) { + // port: capital with any harbor and towns with good harbors + if (cell.haven === undefined) { + cell.harbor = undefined; + } else { + cell.port = cells[cell.haven].fn; + x = cell.coastX; + y = cell.coastY; + } + } + if (cell.river && cell.type !== 1) { + let shift = 0.2 * cell.flux; + if (shift < 0.2) shift = 0.2; + if (shift > 1) shift = 1; + shift = Math.random() > .5 ? shift : shift * -1; + x = rn(x + shift, 2); + shift = Math.random() > .5 ? shift : shift * -1; + y = rn(y + shift, 2); + } + cell.data[0] = manors[i].x = x; + cell.data[1] = manors[i].y = y; + } + } + + // Validate each island with manors has port + function checkAccessibility() { + console.time("checkAccessibility"); + for (let f = 0; f < features.length; f++) { + if (!features[f].land) continue; + const manorsOnIsland = $.grep(land, function (e) { + return e.manor !== undefined && e.fn === f; + }); + if (!manorsOnIsland.length) continue; + + // if lake port is the only port on lake, remove port + const lakePorts = $.grep(manorsOnIsland, function (p) { + return p.port && !features[p.port].border; + }); + if (lakePorts.length) { + const lakes = []; + lakePorts.forEach(function(p) {lakes[p.port] = lakes[p.port] ? lakes[p.port] + 1 : 1;}); + lakePorts.forEach(function(p) {if (lakes[p.port] === 1) p.port = undefined;}); + } + + // check how many ocean ports are there on island + const oceanPorts = $.grep(manorsOnIsland, function (p) { + return p.port && features[p.port].border; + }); + if (oceanPorts.length) continue; + const portCandidates = $.grep(manorsOnIsland, function (c) { + return c.harbor && features[cells[c.harbor].fn].border && c.ctype === 1; + }); + if (portCandidates.length) { + // No ports on island. Upgrading first burg to port + const candidate = portCandidates[0]; + candidate.harbor = 1; + candidate.port = cells[candidate.haven].fn; + const manor = manors[portCandidates[0].manor]; + candidate.data[0] = manor.x = candidate.coastX; + candidate.data[1] = manor.y = candidate.coastY; + // add score for each burg on island (as it's the only port) + candidate.score += Math.floor((portCandidates.length - 1) / 2); + } else { + // No ports on island. Reducing score for burgs + manorsOnIsland.forEach(function(e) {e.score -= 2;}); + } + } + console.timeEnd("checkAccessibility"); + } + + function generateMainRoads() { + console.time("generateMainRoads"); + lineGen.curve(d3.curveBasis); + if (states.length < 2 || manors.length < 2) return; + for (let f = 0; f < features.length; f++) { + if (!features[f].land) continue; + const manorsOnIsland = $.grep(land, function(e) {return e.manor !== undefined && e.fn === f;}); + if (manorsOnIsland.length > 1) { + for (let d = 1; d < manorsOnIsland.length; d++) { + for (let m = 0; m < d; m++) { + const path = findLandPath(manorsOnIsland[d].index, manorsOnIsland[m].index, "main"); + restorePath(manorsOnIsland[m].index, manorsOnIsland[d].index, "main", path); + } + } + } + } + console.timeEnd("generateMainRoads"); + } + + // add roads from port to capital if capital is not a port + function generatePortRoads() { + console.time("generatePortRoads"); + if (!states.length || manors.length < 2) return; + const portless = []; + for (let s=0; s < states.length; s++) { + const cell = manors[s].cell; + if (cells[cell].port === undefined) portless.push(s); + } + for (let l=0; l < portless.length; l++) { + const ports = $.grep(land, function(l) {return l.port !== undefined && l.region === portless[l];}); + if (!ports.length) continue; + let minDist = 1000, end = -1; + ports.map(function(p) { + const dist = Math.hypot(e.data[0] - p.data[0],e.data[1] - p.data[1]); + if (dist < minDist && dist > 1) {minDist = dist; end = p.index;} + }); + if (end !== -1) { + const start = manors[portless[l]].cell; + const path = findLandPath(start, end, "direct"); + restorePath(end, start, "main", path); + } + } + console.timeEnd("generatePortRoads"); + } + + function generateSmallRoads() { + console.time("generateSmallRoads"); + if (manors.length < 2) return; + for (let f = 0; f < features.length; f++) { + const manorsOnIsland = $.grep(land, function (e) { + return e.manor !== undefined && e.fn === f; + }); + const l = manorsOnIsland.length; + if (l > 1) { + const secondary = rn((l + 8) / 10); + for (let s = 0; s < secondary; s++) { + var start = manorsOnIsland[Math.floor(Math.random() * l)].index; + var end = manorsOnIsland[Math.floor(Math.random() * l)].index; + var dist = Math.hypot(cells[start].data[0] - cells[end].data[0],cells[start].data[1] - cells[end].data[1]); + if (dist > 10) { + var path = findLandPath(start, end, "direct"); + restorePath(end, start, "small", path); + } + } + manorsOnIsland.map(function(e, d) { + if (!e.path && d > 0) { + const start = e.index; + let end = -1; + const road = $.grep(land, function (e) { + return e.path && e.fn === f; + }); + if (road.length > 0) { + let minDist = 10000; + road.map(function(i) { + const dist = Math.hypot(e.data[0] - i.data[0], e.data[1] - i.data[1]); + if (dist < minDist) {minDist = dist; end = i.index;} + }); + } else { + end = manorsOnIsland[0].index; + } + const path = findLandPath(start, end, "main"); + restorePath(end, start, "small", path); + } + }); + } + } + console.timeEnd("generateSmallRoads"); + } + + function generateOceanRoutes() { + console.time("generateOceanRoutes"); + lineGen.curve(d3.curveBasis); + const cAnchors = icons.selectAll("#capital-anchors"); + const tAnchors = icons.selectAll("#town-anchors"); + const cSize = cAnchors.attr("size") || 2; + const tSize = tAnchors.attr("size") || 1; + + const ports = []; + // groups all ports on water feature + for (let m = 0; m < manors.length; m++) { + const cell = manors[m].cell; + const port = cells[cell].port; + if (port === undefined) continue; + if (ports[port] === undefined) ports[port] = []; + ports[port].push(cell); + + // draw anchor icon + const group = m < states.length ? cAnchors : tAnchors; + const size = m < states.length ? cSize : tSize; + const x = rn(cells[cell].data[0] - size * 0.47, 2); + const y = rn(cells[cell].data[1] - size * 0.47, 2); + group.append("use").attr("xlink:href", "#icon-anchor").attr("data-id", m) + .attr("x", x).attr("y", y).attr("width", size).attr("height", size); + icons.selectAll("use").on("click", editIcon); + } + + for (let w = 0; w < ports.length; w++) { + if (!ports[w]) continue; + if (ports[w].length < 2) continue; + const onIsland = []; + for (let i = 0; i < ports[w].length; i++) { + const cell = ports[w][i]; + const fn = cells[cell].fn; + if (onIsland[fn] === undefined) onIsland[fn] = []; + onIsland[fn].push(cell); + } + + for (let fn = 0; fn < onIsland.length; fn++) { + if (!onIsland[fn]) continue; + if (onIsland[fn].length < 2) continue; + const start = onIsland[fn][0]; + const paths = findOceanPaths(start, -1); + + for (let h=1; h < onIsland[fn].length; h++) { + // routes from all ports on island to 1st port on island + restorePath(onIsland[fn][h],start, "ocean", paths); + } + + // inter-island routes + for (let c=fn+1; c < onIsland.length; c++) { + if (!onIsland[c]) continue; + if (!onIsland[c].length) continue; + if (onIsland[fn].length > 3) { + const end = onIsland[c][0]; + restorePath(end, start, "ocean", paths); + } + } + + if (features[w].border && !features[fn].border && onIsland[fn].length > 5) { + // encircle the island + onIsland[fn].sort(function(a, b) {return cells[b].cost - cells[a].cost;}); + for (let a = 2; a < onIsland[fn].length && a < 10; a++) { + const from = onIsland[fn][1],to = onIsland[fn][a]; + const dist = Math.hypot(cells[from].data[0] - cells[to].data[0],cells[from].data[1] - cells[to].data[1]); + const distPath = getPathDist(from, to); + if (distPath > dist * 4 + 10) { + const totalCost = cells[from].cost + cells[to].cost; + const pathsAdd = findOceanPaths(from, to); + if (cells[to].cost < totalCost) { + restorePath(to, from, "ocean", pathsAdd); + break; + } + } + } + } + + } + + } + console.timeEnd("generateOceanRoutes"); + } + + function findLandPath(start, end, type) { + // A* algorithm + const queue = new PriorityQueue({ + comparator: function (a, b) { + return a.p - b.p + } + }); + const cameFrom = []; + const costTotal = []; + costTotal[start] = 0; + queue.queue({e: start, p: 0}); + while (queue.length > 0) { + const next = queue.dequeue().e; + if (next === end) {break;} + const pol = cells[next]; + pol.neighbors.forEach(function(e) { + if (cells[e].height >= 20) { + let cost = cells[e].height / 100 * 2; + if (cells[e].path && type === "main") { + cost = 0.15; + } else { + if (typeof e.manor === "undefined") {cost += 0.1;} + if (typeof e.river !== "undefined") {cost -= 0.1;} + if (cells[e].harbor) {cost *= 0.3;} + if (cells[e].path) {cost *= 0.5;} + cost += Math.hypot(cells[e].data[0] - pol.data[0],cells[e].data[1] - pol.data[1]) / 30; + } + const costNew = costTotal[next] + cost; + if (!cameFrom[e] || costNew < costTotal[e]) { // + costTotal[e] = costNew; + cameFrom[e] = next; + const dist = Math.hypot(cells[e].data[0] - cells[end].data[0], cells[e].data[1] - cells[end].data[1]) / 15; + const priority = costNew + dist; + queue.queue({e, p: priority}); + } + } + }); + } + return cameFrom; + } + + function findLandPaths(start, type) { + // Dijkstra algorithm (not used now) + const queue = new PriorityQueue({comparator: function(a, b) {return a.p - b.p}}); + const cameFrom = [],costTotal = []; + cameFrom[start] = "no", costTotal[start] = 0; + queue.queue({e: start, p: 0}); + while (queue.length > 0) { + const next = queue.dequeue().e; + const pol = cells[next]; + pol.neighbors.forEach(function(e) { + if (cells[e].height < 20) return; + let cost = cells[e].height / 100 * 2; + if (e.river !== undefined) cost -= 0.2; + if (pol.region !== cells[e].region) cost += 1; + if (cells[e].region === "neutral") cost += 1; + if (e.manor !== undefined) cost = 0.1; + const costNew = costTotal[next] + cost; + if (!cameFrom[e]) { + costTotal[e] = costNew; + cameFrom[e] = next; + queue.queue({e, p: costNew}); + } + }); + } + return cameFrom; + } + + function findOceanPaths(start, end) { + const queue = new PriorityQueue({comparator: function(a, b) {return a.p - b.p}}); + let next; + const cameFrom = [],costTotal = []; + cameFrom[start] = "no", costTotal[start] = 0; + queue.queue({e: start, p: 0}); + while (queue.length > 0 && next !== end) { + next = queue.dequeue().e; + const pol = cells[next]; + pol.neighbors.forEach(function(e) { + if (cells[e].ctype < 0 || cells[e].haven === next) { + let cost = 1; + if (cells[e].ctype > 0) cost += 100; + if (cells[e].ctype < -1) { + const dist = Math.hypot(cells[e].data[0] - pol.data[0],cells[e].data[1] - pol.data[1]); + cost += 50 + dist * 2; + } + if (cells[e].path && cells[e].ctype < 0) cost *= 0.8; + const costNew = costTotal[next] + cost; + if (!cameFrom[e]) { + costTotal[e] = costNew; + cells[e].cost = costNew; + cameFrom[e] = next; + queue.queue({e, p: costNew}); + } + } + }); + } + return cameFrom; + } + + function getPathDist(start, end) { + const queue = new PriorityQueue({ + comparator: function (a, b) { + return a.p - b.p + } + }); + let next, costNew; + const cameFrom = []; + const costTotal = []; + cameFrom[start] = "no"; + costTotal[start] = 0; + queue.queue({e: start, p: 0}); + while (queue.length > 0 && next !== end) { + next = queue.dequeue().e; + const pol = cells[next]; + pol.neighbors.forEach(function(e) { + if (cells[e].path && (cells[e].ctype === -1 || cells[e].haven === next)) { + const dist = Math.hypot(cells[e].data[0] - pol.data[0], cells[e].data[1] - pol.data[1]); + costNew = costTotal[next] + dist; + if (!cameFrom[e]) { + costTotal[e] = costNew; + cameFrom[e] = next; + queue.queue({e, p: costNew}); + } + } + }); + } + return costNew; + } + + function restorePath(end, start, type, from) { + let path = [], current = end; + const limit = 1000; + let prev = cells[end]; + if (type === "ocean" || !prev.path) {path.push({scX: prev.data[0],scY: prev.data[1],i: end});} + if (!prev.path) {prev.path = 1;} + for (let i = 0; i < limit; i++) { + current = from[current]; + let cur = cells[current]; + if (!cur) {break;} + if (cur.path) { + cur.path += 1; + path.push({scX: cur.data[0],scY: cur.data[1],i: current}); + prev = cur; + drawPath(); + } else { + cur.path = 1; + if (prev) {path.push({scX: prev.data[0],scY: prev.data[1],i: prev.index});} + prev = undefined; + path.push({scX: cur.data[0],scY: cur.data[1],i: current}); + } + if (current === start || !from[current]) {break;} + } + drawPath(); + function drawPath() { + if (path.length > 1) { + // mark crossroades + if (type === "main" || type === "small") { + const plus = type === "main" ? 4 : 2; + const f = cells[path[0].i]; + if (f.path > 1) { + if (!f.crossroad) {f.crossroad = 0;} + f.crossroad += plus; + } + const t = cells[(path[path.length - 1].i)]; + if (t.path > 1) { + if (!t.crossroad) {t.crossroad = 0;} + t.crossroad += plus; + } + } + // draw path segments + let line = lineGen(path); + line = round(line, 1); + let id = 0; // to create unique route id + if (type === "main") { + id = roads.selectAll("path").size(); + roads.append("path").attr("d", line).attr("id", "road"+id).on("click", editRoute); + } else if (type === "small") { + id = trails.selectAll("path").size(); + trails.append("path").attr("d", line).attr("id", "trail"+id).on("click", editRoute); + } else if (type === "ocean") { + id = searoutes.selectAll("path").size(); + searoutes.append("path").attr("d", line).attr("id", "searoute"+id).on("click", editRoute); + } + } + path = []; + } + } + + // Append burg elements + function drawManors() { + console.time('drawManors'); + const capitalIcons = burgIcons.select("#capitals"); + const capitalLabels = burgLabels.select("#capitals"); + const townIcons = burgIcons.select("#towns"); + const townLabels = burgLabels.select("#towns"); + const capitalSize = capitalIcons.attr("size") || 1; + const townSize = townIcons.attr("size") || 0.5; + capitalIcons.selectAll("*").remove(); + capitalLabels.selectAll("*").remove(); + townIcons.selectAll("*").remove(); + townLabels.selectAll("*").remove(); + + for (let i = 0; i < manors.length; i++) { + const x = manors[i].x, y = manors[i].y; + const cell = manors[i].cell; + const name = manors[i].name; + const ic = i < states.length ? capitalIcons : townIcons; + const lb = i < states.length ? capitalLabels : townLabels; + const size = i < states.length ? capitalSize : townSize; + ic.append("circle").attr("id", "burg"+i).attr("data-id", i).attr("cx", x).attr("cy", y).attr("r", size).on("click", editBurg); + lb.append("text").attr("data-id", i).attr("x", x).attr("y", y).attr("dy", "-0.35em").text(name).on("click", editBurg); + } + console.timeEnd('drawManors'); + } + + // get settlement and country names based on option selected + function getNames() { + console.time('getNames'); + // if names source is an external resource + if (namesInput.value === "1") { + const request = new XMLHttpRequest(); + const url = "https://archivist.xalops.com/archivist-core/api/name/settlement?count="; + request.open("GET", url+manors.length, true); + request.onload = function() { + const names = JSON.parse(request.responseText); + for (let i=0; i < manors.length; i++) { + manors[i].name = names[i]; + burgLabels.select("[data-id='" + i + "']").text(names[i]); + if (i < states.length) { + states[i].name = generateStateName(i); + labels.select("#countries").select("#regionLabel"+i).text(states[i].name); + } + } + console.log(names); + }; + request.send(null); + } + + if (namesInput.value !== "0") return; + for (let i=0; i < manors.length; i++) { + const culture = manors[i].culture; + manors[i].name = generateName(culture); + if (i < states.length) states[i].name = generateStateName(i); + } + console.timeEnd('getNames'); + } + + function calculateChains() { + for (let c=0; c < nameBase.length; c++) { + chain[c] = calculateChain(c); + } + } + + // calculate Markov's chain from namesbase data + function calculateChain(c) { + const chain = []; + const d = nameBase[c].join(" ").toLowerCase(); + const method = nameBases[c].method; + + for (let i = -1, prev = " ", str = ""; i < d.length - 2; prev = str, i += str.length, str = "") { + let vowel = 0, f = " "; + if (method === "let-to-let") {str = d[i+1];} else { + for (let c=i+1; str.length < 5; c++) { + if (d[c] === undefined) break; + str += d[c]; + if (str === " ") break; + if (d[c] !== "o" && d[c] !== "e" && vowels.includes(d[c]) && d[c+1] === d[c]) break; + if (d[c+2] === " ") {str += d[c+1]; break;} + if (vowels.includes(d[c])) vowel++; + if (vowel && vowels.includes(d[c+2])) break; + } + } + if (i >= 0) { + f = d[i]; + if (method === "syl-to-syl") f = prev; + } + if (chain[f] === undefined) chain[f] = []; + chain[f].push(str); + } + return chain; + } + + // generate random name using Markov's chain + function generateName(culture, base) { + if (base === undefined) { + if (!cultures[culture]) { + console.error("culture " + culture + " is not defined. Will load default cultures and set first culture"); + generateCultures(); + culture = 0; + } + base = cultures[culture].base; + } + if (!nameBases[base]) { + console.error("nameBase " + base + " is not defined. Will load default names data and first base"); + if (!nameBases[0]) applyDefaultNamesData(); + base = 0; + } + const method = nameBases[base].method; + const error = function(base) { + tip("Names data for base " + nameBases[base].name + " is incorrect. Please fix in Namesbase Editor"); + editNamesbase(); + }; + + if (method === "selection") { + if (nameBase[base].length < 1) {error(base); return;} + const rnd = rand(nameBase[base].length - 1); + const name = nameBase[base][rnd]; + return name; + } + + const data = chain[base]; + if (data === undefined || data[" "] === undefined) {error(base); return;} + const max = nameBases[base].max; + const min = nameBases[base].min; + const d = nameBases[base].d; + let word = "", variants = data[" "]; + if (variants === undefined) { + error(base); + return; + } + let cur = variants[rand(variants.length - 1)]; + for (let i=0; i < 21; i++) { + if (cur === " " && Math.random() < 0.8) { + // space means word end, but we don't want to end if word is too short + if (word.length < min) { + word = ""; + variants = data[" "]; + } else {break;} + } else { + const l = method === "let-to-syl" && cur.length > 1 ? cur[cur.length - 1] : cur; + variants = data[l]; + // word is getting too long, restart + word += cur; // add current el to word + if (word.length > max) word = ""; + } + if (variants === undefined) { + error(base); + return; + } + cur = variants[rand(variants.length - 1)]; + } + // very rare case, let's just select a random name + if (word.length < 2) word = nameBase[base][rand(nameBase[base].length - 1)]; + + // do not allow multi-word name if word is foo short or not allowed for culture + if (word.includes(" ")) { + let words = word.split(" "), parsed; + if (Math.random() > nameBases[base].m) {word = words.join("");} + else { + for (let i=0; i < words.length; i++) { + if (words[i].length < 2) { + if (!i) words[1] = words[0] + words[1]; + if (i) words[i-1] = words[i-1] + words[i]; + words.splice(i, 1); + i--; + } + } + word = words.join(" "); + } + } + + // parse word to get a final name + const name = [...word].reduce(function(r, c, i, data) { + if (c === " ") { + if (!r.length) return ""; + if (i+1 === data.length) return r; + } + if (!r.length) return c.toUpperCase(); + if (r.slice(-1) === " ") return r + c.toUpperCase(); + if (c === data[i-1]) { + if (!d.includes(c)) return r; + if (c === data[i-2]) return r; + } + return r + c; + }, ""); + return name; + } + + // Define areas based on the closest manor to a polygon + function defineRegions(withCultures) { + console.time('defineRegions'); + const manorTree = d3.quadtree(); + manors.forEach(function(m) {if (m.region !== "removed") manorTree.add([m.x, m.y]);}); + + const neutral = +neutralInput.value; + land.forEach(function(i) { + if (i.manor !== undefined && manors[i.manor].region !== "removed") { + i.region = manors[i.manor].region; + if (withCultures && manors[i.manor].culture !== undefined) i.culture = manors[i.manor].culture; + return; + } + const x = i.data[0],y = i.data[1]; + + let dist = 100000, manor = null; + if (manors.length) { + const c = manorTree.find(x, y); + dist = Math.hypot(c[0] - x, c[1] - y); + manor = getManorId(c); + } + if (dist > neutral / 2 || manor === null) { + i.region = "neutral"; + if (withCultures) { + const closestCulture = cultureTree.find(x, y); + i.culture = getCultureId(closestCulture); + } + } else { + const cell = manors[manor].cell; + if (cells[cell].fn !== i.fn) { + let minDist = dist * 3; + land.forEach(function(l) { + if (l.fn === i.fn && l.manor !== undefined) { + if (manors[l.manor].region === "removed") return; + const distN = Math.hypot(l.data[0] - x, l.data[1] - y); + if (distN < minDist) {minDist = distN; manor = l.manor;} + } + }); + } + i.region = manors[manor].region; + if (withCultures) i.culture = manors[manor].culture; + } + }); + console.timeEnd('defineRegions'); + } + + // Define areas cells + function drawRegions() { + console.time('drawRegions'); + labels.select("#countries").selectAll("*").remove(); + + // arrays to store edge data + const edges = [],coastalEdges = [],borderEdges = [],neutralEdges = []; + for (let a=0; a < states.length; a++) { + edges[a] = []; + coastalEdges[a] = []; + } + const e = diagram.edges; + for (let i=0; i < e.length; i++) { + if (e[i] === undefined) continue; + const start = e[i][0].join(" "); + const end = e[i][1].join(" "); + const p = {start, end}; + if (e[i].left === undefined) { + const r = e[i].right.index; + const rr = cells[r].region; + if (Number.isInteger(rr)) edges[rr].push(p); + continue; + } + if (e[i].right === undefined) { + const l = e[i].left.index; + const lr = cells[l].region; + if (Number.isInteger(lr)) edges[lr].push(p); + continue; + } + const l = e[i].left.index; + const r = e[i].right.index; + const lr = cells[l].region; + const rr = cells[r].region; + if (lr === rr) continue; + if (Number.isInteger(lr)) { + edges[lr].push(p); + if (rr === undefined) {coastalEdges[lr].push(p);} + else if (rr === "neutral") {neutralEdges.push(p);} + } + if (Number.isInteger(rr)) { + edges[rr].push(p); + if (lr === undefined) {coastalEdges[rr].push(p);} + else if (lr === "neutral") {neutralEdges.push(p);} + else if (Number.isInteger(lr)) {borderEdges.push(p);} + } + } + edges.map(function(e, i) { + if (e.length) { + drawRegion(e, i); + drawRegionCoast(coastalEdges[i],i); + } + }); + drawBorders(borderEdges, "state"); + drawBorders(neutralEdges, "neutral"); + console.timeEnd('drawRegions'); + } + + function drawRegion(edges, region) { + let path = ""; + const array = []; + lineGen.curve(d3.curveLinear); + while (edges.length > 2) { + const edgesOrdered = []; // to store points in a correct order + const start = edges[0].start; + let end = edges[0].end; + edges.shift(); + let spl = start.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + spl = end.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + for (let i = 0; end !== start && i < 2000; i++) { + const next = $.grep(edges, function (e) { + return (e.start == end || e.end == end); + }); + if (next.length > 0) { + if (next[0].start == end) { + end = next[0].end; + } else if (next[0].end == end) { + end = next[0].start; + } + spl = end.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + } + const rem = edges.indexOf(next[0]); + edges.splice(rem, 1); + } + path += lineGen(edgesOrdered) + "Z "; + array[array.length] = edgesOrdered.map(function(e) {return [+e.scX, +e.scY];}); + } + const color = states[region].color; + regions.append("path").attr("d", round(path, 1)).attr("fill", color).attr("class", "region"+region); + array.sort(function(a, b){return b.length - a.length;}); + let capital = states[region].capital; + // add capital cell as a hole + if (!isNaN(capital)) { + const capitalCell = manors[capital].cell; + array.push(polygons[capitalCell]); + } + const name = states[region].name; + const c = polylabel(array, 1.0); // pole of inaccessibility + labels.select("#countries").append("text").attr("id", "regionLabel"+region).attr("x", rn(c[0])).attr("y", rn(c[1])).text(name).on("click", editLabel); + states[region].area = rn(Math.abs(d3.polygonArea(array[0]))); // define region area + } + + function drawRegionCoast(edges, region) { + let path = ""; + while (edges.length > 0) { + const edgesOrdered = []; // to store points in a correct order + const start = edges[0].start; + let end = edges[0].end; + edges.shift(); + let spl = start.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + spl = end.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + let next = $.grep(edges, function (e) { + return (e.start == end || e.end == end); + }); + while (next.length > 0) { + if (next[0].start == end) { + end = next[0].end; + } else if (next[0].end == end) { + end = next[0].start; + } + spl = end.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + const rem = edges.indexOf(next[0]); + edges.splice(rem, 1); + next = $.grep(edges, function(e) {return (e.start == end || e.end == end);}); + } + path += lineGen(edgesOrdered); + } + const color = states[region].color; + regions.append("path").attr("d", round(path, 1)).attr("fill", "none").attr("stroke", color).attr("stroke-width", 5).attr("class", "region"+region); + } + + function drawBorders(edges, type) { + let path = ""; + if (edges.length < 1) {return;} + while (edges.length > 0) { + const edgesOrdered = []; // to store points in a correct order + const start = edges[0].start; + let end = edges[0].end; + edges.shift(); + let spl = start.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + spl = end.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + let next = $.grep(edges, function (e) { + return (e.start == end || e.end == end); + }); + while (next.length > 0) { + if (next[0].start == end) { + end = next[0].end; + } else if (next[0].end == end) { + end = next[0].start; + } + spl = end.split(" "); + edgesOrdered.push({scX: spl[0],scY: spl[1]}); + const rem = edges.indexOf(next[0]); + edges.splice(rem, 1); + next = $.grep(edges, function(e) {return (e.start == end || e.end == end);}); + } + path += lineGen(edgesOrdered); + } + if (type === "state") {stateBorders.append("path").attr("d", round(path, 1));} + if (type === "neutral") {neutralBorders.append("path").attr("d", round(path, 1));} + } + + // generate region name + function generateStateName(state) { + let culture = null; + if (states[state]) if(manors[states[state].capital]) culture = manors[states[state].capital].culture; + let name = "NameIdontWant"; + if (Math.random() < 0.85 || culture === null) { + // culture is random if capital is not yet defined + if (culture === null) culture = rand(cultures.length - 1); + // try to avoid too long words as a basename + for (let i=0; i < 20 && name.length > 7; i++) { + name = generateName(culture); + } + } else { + name = manors[state].name; + } + const base = cultures[culture].base; + + let addSuffix = false; + // handle special cases + const e = name.slice(-2); + if (base === 5 && (e === "sk" || e === "ev" || e === "ov")) { + // remove -sk and -ev/-ov for Ruthenian + name = name.slice(0,-2); + addSuffix = true; + } else if (name.length > 5 && base === 1 && name.slice(-3) === "ton") { + // remove -ton ending for English + name = name.slice(0,-3); + addSuffix = true; + } else if (name.length > 6 && name.slice(-4) === "berg") { + // remove -berg ending for any + name = name.slice(0,-4); + addSuffix = true; + } else if (base === 12) { + // Japanese ends on vowels + if (vowels.includes(name.slice(-1))) return name; + return name + "u"; + } else if (base === 10) { + // Korean has "guk" suffix + if (name.slice(-3) === "guk") return name; + if (name.slice(-1) === "g") name = name.slice(0,-1); + if (Math.random() < 0.2 && name.length < 7) name = name + "guk"; // 20% for "guk" + return name; + } else if (base === 11) { + // Chinese has "guo" suffix + if (name.slice(-3) === "guo") return name; + if (name.slice(-1) === "g") name = name.slice(0,-1); + if (Math.random() < 0.3 && name.length < 7) name = name + " Guo"; // 30% for "guo" + return name; + } + + // define if suffix should be used + let vowel = vowels.includes(name.slice(-1)); // last char is vowel + if (vowel && name.length > 3) { + if (Math.random() < 0.85) { + if (vowels.includes(name.slice(-2,-1))) { + name = name.slice(0,-2); + addSuffix = true; // 85% for vv + } else if (Math.random() < 0.7) { + name = name.slice(0,-1); + addSuffix = true; // ~60% for cv + } + } + } else if (Math.random() < 0.6) { + addSuffix = true; // 60% for cc and vc + } + + if (addSuffix === false) return name; + let suffix = "ia"; // common latin suffix + const rnd = Math.random(); + if (rnd < 0.05 && base === 3) suffix = "terra"; // 5% "terra" for Italian + else if (rnd < 0.05 && base === 4) suffix = "terra"; // 5% "terra" for Spanish + else if (rnd < 0.05 && base == 2) suffix = "terre"; // 5% "terre" for French + else if (rnd < 0.5 && base == 0) suffix = "land"; // 50% "land" for German + else if (rnd < 0.4 && base == 1) suffix = "land"; // 40% "land" for English + else if (rnd < 0.3 && base == 6) suffix = "land"; // 30% "land" for Nordic + else if (rnd < 0.1 && base == 7) suffix = "eia"; // 10% "eia" for Greek ("ia" is also Greek) + else if (rnd < 0.4 && base == 9) suffix = "maa"; // 40% "maa" for Finnic + if (name.slice(-1 * suffix.length) === suffix) return name; // no suffix if name already ends with it + if (name.slice(-1) === suffix.charAt(0)) name = name.slice(0, -1); // remove name last letter if it's a suffix first letter + return name + suffix; + } + + // re-calculate cultures + function recalculateCultures(fullRedraw) { + console.time("recalculateCultures"); + // For each capital find closest culture and assign it to capital + states.forEach(function(s) { + if (s.capital === "neutral" || s.capital === "select") return; + const capital = manors[s.capital]; + const c = cultureTree.find(capital.x, capital.y); + capital.culture = getCultureId(c); + }); + + // For each town if distance to its capital > neutral / 2, + // assign closest culture to the town; else assign capital's culture + const manorTree = d3.quadtree(); + const neutral = +neutralInput.value; + manors.forEach(function(m) { + if (m.region === "removed") return; + manorTree.add([m.x, m.y]); + if (m.region === "neutral") { + const culture = cultureTree.find(m.x, m.y); + m.culture = getCultureId(culture); + return; + } + const c = states[m.region].capital; + if (c !== "neutral" && c !== "select") { + const dist = Math.hypot(m.x - manors[c].x, m.y - manors[c].y); + if (dist <= neutral / 5) { + m.culture = manors[c].culture; + return; + } + } + const culture = cultureTree.find(m.x, m.y); + m.culture = getCultureId(culture); + }); + + // For each land cell if distance to closest manor > neutral / 2, + // assign closest culture to the cell; else assign manors's culture + const changed = []; + land.forEach(function(i) { + const x = i.data[0],y = i.data[1]; + const c = manorTree.find(x, y); + const culture = i.culture; + const dist = Math.hypot(c[0] - x, c[1] - y); + let manor = getManorId(c); + if (dist > neutral / 2 || manor === undefined) { + const closestCulture = cultureTree.find(i.data[0],i.data[1]); + i.culture = getCultureId(closestCulture); + } else { + const cell = manors[manor].cell; + if (cells[cell].fn !== i.fn) { + let minDist = dist * 3; + land.forEach(function(l) { + if (l.fn === i.fn && l.manor !== undefined) { + if (manors[l.manor].region === "removed") return; + const distN = Math.hypot(l.data[0] - x, l.data[1] - y); + if (distN < minDist) {minDist = distN; manor = l.manor;} + } + }); + } + i.culture = manors[manor].culture; + } + // re-color cells + if (i.culture !== culture || fullRedraw) { + const clr = cultures[i.culture].color; + cults.select("#cult"+i.index).attr("fill", clr).attr("stroke", clr); + } + }); + console.timeEnd("recalculateCultures"); + } + + // get culture Id from center coordinates + function getCultureId(c) { + for (let i=0; i < cultures.length; i++) { + if (cultures[i].center[0] === c[0]) if (cultures[i].center[1] === c[1]) return i; + } + } + + // get manor Id from center coordinates + function getManorId(c) { + for (let i=0; i < manors.length; i++) { + if (manors[i].x === c[0]) if (manors[i].y === c[1]) return i; + } + } + + // focus on coorditanes, cell or burg provided in searchParams + function focusOn() { + if (params.get("from") === "MFCG") { + // focus on burg from MFCG + findBurgForMFCG(); + return; + } + let s = params.get("scale") || 8; + let x = params.get("x"); + let y = params.get("y"); + let c = params.get("cell"); + if (c !== null) { + x = cells[+c].data[0]; + y = cells[+c].data[1]; + } + let b = params.get("burg"); + if (b !== null) { + x = manors[+b].x; + y = manors[+b].y; + } + if (x !== null && y !== null) zoomTo(x, y, s, 1600); + } + + // find burg from MFCG and focus on it + function findBurgForMFCG() { + if (!manors.length) {console.error("No burgs generated. Cannot select a burg for MFCG"); return;} + const size = +params.get("size"); + let coast = +params.get("coast"); + let port = +params.get("port"); + let river = +params.get("river"); + let selection = defineSelection(coast, port, river); + if (!selection.length) selection = defineSelection(coast, !port, !river); + if (!selection.length) selection = defineSelection(!coast, 0, !river); + if (!selection.length) selection = manors[0]; // select first if nothing is found + if (!selection.length) {console.error("Cannot find a burg for MFCG"); return;} + + function defineSelection(coast, port, river) { + let selection = []; + if (port && river) selection = $.grep(manors, function(e) {return cells[e.cell].port !== undefined && cells[e.cell].river !== undefined;}); + else if (!port && coast && river) selection = $.grep(manors, function(e) {return cells[e.cell].port === undefined && cells[e.cell].ctype === 1 && cells[e.cell].river !== undefined;}); + else if (!coast && !river) selection = $.grep(manors, function(e) {return cells[e.cell].ctype !== 1 && cells[e.cell].river === undefined;}); + else if (!coast && river) selection = $.grep(manors, function(e) {return cells[e.cell].ctype !== 1 && cells[e.cell].river !== undefined;}); + else if (coast && !river) selection = $.grep(manors, function(e) {return cells[e.cell].ctype === 1 && cells[e.cell].river === undefined;}); + return selection; + } + + // select a burg with closes population from selection + const selected = d3.scan(selection, function(a, b) {return Math.abs(a.population - size) - Math.abs(b.population - size);}); + const burg = selection[selected].i; + if (size && burg !== undefined) {manors[burg].population = size;} else {return;} + + // focus on found burg + const label = burgLabels.select("[data-id='" + burg + "']"); + if (!label.size()) { + console.error("Cannot find a label for MFCG burg "+burg); + return; + } + tip("Here stands the glorious city of "+manors[burg].name, true); + label.classed("drag", true).on("mouseover", function() { + d3.select(this).classed("drag", false); + tip("", true); + }); + const x = +label.attr("x"), y = +label.attr("y"); + zoomTo(x, y, 8, 1600); + } + + // draw the Heightmap + function toggleHeight() { + const scheme = styleSchemeInput.value; + let hColor = color; + if (scheme === "light") hColor = d3.scaleSequential(d3.interpolateRdYlGn); + if (scheme === "green") hColor = d3.scaleSequential(d3.interpolateGreens); + if (scheme === "monochrome") hColor = d3.scaleSequential(d3.interpolateGreys); + if (!terrs.selectAll("path").size()) { + cells.map(function(i, d) { + let height = i.height; + if (height < 20 && !i.lake) return; + if (i.lake) { + const nHeights = i.neighbors.map(function(e) {if (cells[e].height >= 20) return cells[e].height;}); + const mean = d3.mean(nHeights); + if (!mean) return; + height = Math.trunc(mean); + if (height < 20 || isNaN(height)) height = 20; + } + const clr = hColor((100 - height) / 100); + terrs.append("path") + .attr("d", "M" + polygons[d].join("L") + "Z") + .attr("fill", clr).attr("stroke", clr); + }); + } else { + terrs.selectAll("path").remove(); + } + } + + // draw Cultures + function toggleCultures() { + if (cults.selectAll("path").size() == 0) { + land.map(function(i) { + const color = cultures[i.culture].color; + cults.append("path") + .attr("d", "M" + polygons[i.index].join("L") + "Z") + .attr("id", "cult" + i.index) + .attr("fill", color) + .attr("stroke", color); + }); + } else { + cults.selectAll("path").remove(); + } + } + + // draw Overlay + function toggleOverlay() { + if (overlay.selectAll("*").size() === 0) { + const type = styleOverlayType.value; + const size = +styleOverlaySize.value; + if (type === "pointyHex" || type === "flatHex") { + let points = getHexGridPoints(size, type); + let hex = "m" + getHex(size, type).slice(0, 4).join("l"); + let d = points.map(function(p) {return "M" + p + hex;}).join(""); + overlay.append("path").attr("d", d); + } else if (type === "square") { + const x = d3.range(size, svgWidth, size); + const y = d3.range(size, svgHeight, size); + overlay.append("g").selectAll("line").data(x).enter().append("line") + .attr("x1", function(d) {return d;}) + .attr("x2", function(d) {return d;}) + .attr("y1", 0).attr("y2", svgHeight); + overlay.append("g").selectAll("line").data(y).enter().append("line") + .attr("y1", function(d) {return d;}) + .attr("y2", function(d) {return d;}) + .attr("x1", 0).attr("x2", svgWidth); + } else { + const tr = `translate(80 80) scale(${size / 20})`; + d3.select("#rose").attr("transform", tr); + overlay.append("use").attr("xlink:href","#rose"); + } + overlay.call(d3.drag().on("start", elementDrag)); + calculateFriendlyOverlaySize(); + } else { + overlay.selectAll("*").remove(); + } + } + + function getHex(radius, type) { + let x0 = 0, y0 = 0; + let s = type === "pointyHex" ? 0 : Math.PI / -6; + let thirdPi = Math.PI / 3; + let angles = [s, s + thirdPi, s + 2 * thirdPi, s + 3 * thirdPi, s + 4 * thirdPi, s + 5 * thirdPi]; + return angles.map(function(angle) { + const x1 = Math.sin(angle) * radius, + y1 = -Math.cos(angle) * radius, + dx = x1 - x0, + dy = y1 - y0; + x0 = x1, y0 = y1; + return [dx, dy]; + }); + } + + function getHexGridPoints(size, type) { + let points = []; + const rt3 = Math.sqrt(3); + const off = type === "pointyHex" ? rt3 * size / 2 : size * 3 / 2; + const ySpace = type === "pointyHex" ? size * 3 / 2 : rt3 * size / 2; + const xSpace = type === "pointyHex" ? rt3 * size : size * 3; + for (let y = 0, l = 0; y < graphHeight; y += ySpace, l++) { + for (let x = l % 2 ? 0 : off; x < graphWidth; x += xSpace) { + points.push([x, y]); + } + } + return points; + } + + // clean data to get rid of redundand info + function cleanData() { + console.time("cleanData"); + cells.map(function(c) { + delete c.cost; + delete c.used; + delete c.coastX; + delete c.coastY; + if (c.ctype === undefined) delete c.ctype; + if (c.lake === undefined) delete c.lake; + c.height = Math.trunc(c.height); + if (c.height >= 20) c.flux = rn(c.flux, 2); + }); + // restore layers if they was turned on + if (!$("#toggleHeight").hasClass("buttonoff") && !terrs.selectAll("path").size()) toggleHeight(); + if (!$("#toggleCultures").hasClass("buttonoff") && !cults.selectAll("path").size()) toggleCultures(); + closeDialogs(); + invokeActiveZooming(); + console.timeEnd("cleanData"); + } + + // close all dialogs except stated + function closeDialogs(except) { + except = except || "#except"; + $(".dialog:visible").not(except).each(function(e) { + $(this).dialog("close"); + }); + } + + // change transparency for modal windowa + function changeDialogsTransparency(v) { + localStorage.setItem("transparency", v); + const alpha = (100 - +v) / 100; + const optionsColor = "rgba(164, 139, 149, " + alpha + ")"; // purple-red + const dialogsColor = "rgba(255, 255, 255, " + alpha + ")"; // white + document.getElementById("options").style.backgroundColor = optionsColor; + document.getElementById("dialogs").style.backgroundColor = dialogsColor; + } + + // Draw the water flux system (for dubugging) + function toggleFlux() { + const colorFlux = d3.scaleSequential(d3.interpolateBlues); + if (terrs.selectAll("path").size() == 0) { + land.map(function(i) { + terrs.append("path") + .attr("d", "M" + polygons[i.index].join("L") + "Z") + .attr("fill", colorFlux(0.1 + i.flux)) + .attr("stroke", colorFlux(0.1 + i.flux)); + }); + } else { + terrs.selectAll("path").remove(); + } + } + + // Draw the Relief (need to create more beautiness) + function drawRelief() { + console.time('drawRelief'); + let h, count, rnd, cx, cy, swampCount = 0; + const hills = terrain.select("#hills"); + const mounts = terrain.select("#mounts"); + const swamps = terrain.select("#swamps"); + const forests = terrain.select("#forests"); + terrain.selectAll("g").selectAll("g").remove(); + // sort the land to Draw the top element first (reduce the elements overlapping) + land.sort(compareY); + for (let i = 0; i < land.length; i++) { + if (land[i].river) continue; // no icons on rivers + const cell = land[i].index; + const p = d3.polygonCentroid(polygons[cell]); // polygon centroid point + if (p === undefined) continue; // something is wrong with data + const height = land[i].height; + const area = land[i].area; + if (height >= 70) { + // mount icon + h = (height - 55) * 0.12; + for (let c = 0, a = area; Math.random() < a / 50; c++, a -= 50) { + if (polygons[cell][c] === undefined) break; + const g = mounts.append("g").attr("data-cell", cell); + if (c < 2) { + cx = p[0] - h / 100 * (1 - c / 10) - c * 2; + cy = p[1] + h / 400 + c; + } else { + const p2 = polygons[cell][c]; + cx = (p[0] * 1.2 + p2[0] * 0.8) / 2; + cy = (p[1] * 1.2 + p2[1] * 0.8) / 2; + } + rnd = Math.random() * 0.8 + 0.2; + let mount = "M" + cx + "," + cy + " L" + (cx + h / 3 + rnd) + "," + (cy - h / 4 - rnd * 1.2) + " L" + (cx + h / 1.1) + "," + (cy - h) + " L" + (cx + h + rnd) + "," + (cy - h / 1.2 + rnd) + " L" + (cx + h * 2) + "," + cy; + let shade = "M" + cx + "," + cy + " L" + (cx + h / 3 + rnd) + "," + (cy - h / 4 - rnd * 1.2) + " L" + (cx + h / 1.1) + "," + (cy - h) + " L" + (cx + h / 1.5) + "," + cy; + let dash = "M" + (cx - 0.1) + "," + (cy + 0.3) + " L" + (cx + 2 * h + 0.1) + "," + (cy + 0.3); + dash += "M" + (cx + 0.4) + "," + (cy + 0.6) + " L" + (cx + 2 * h - 0.3) + "," + (cy + 0.6); + g.append("path").attr("d", round(mount, 1)).attr("stroke", "#5c5c70"); + g.append("path").attr("d", round(shade, 1)).attr("fill", "#999999"); + g.append("path").attr("d", round(dash, 1)).attr("class", "strokes"); + } + } else if (height > 50) { + // hill icon + h = (height - 40) / 10; + if (h > 1.7) h = 1.7; + for (let c = 0, a = area; Math.random() < a / 30; c++, a -= 30) { + if (land[i].ctype === 1 && c > 0) break; + if (polygons[cell][c] === undefined) break; + const g = hills.append("g").attr("data-cell", cell); + if (c < 2) { + cx = p[0] - h - c * 1.2; + cy = p[1] + h / 4 + c / 1.6; + } else { + const p2 = polygons[cell][c]; + cx = (p[0] * 1.2 + p2[0] * 0.8) / 2; + cy = (p[1] * 1.2 + p2[1] * 0.8) / 2; + } + let hill = "M" + cx + "," + cy + " Q" + (cx + h) + "," + (cy - h) + " " + (cx + 2 * h) + "," + cy; + let shade = "M" + (cx + 0.6 * h) + "," + (cy + 0.1) + " Q" + (cx + h * 0.95) + "," + (cy - h * 0.91) + " " + (cx + 2 * h * 0.97) + "," + cy; + let dash = "M" + (cx - 0.1) + "," + (cy + 0.2) + " L" + (cx + 2 * h + 0.1) + "," + (cy + 0.2); + dash += "M" + (cx + 0.4) + "," + (cy + 0.4) + " L" + (cx + 2 * h - 0.3) + "," + (cy + 0.4); + g.append("path").attr("d", round(hill, 1)).attr("stroke", "#5c5c70"); + g.append("path").attr("d", round(shade, 1)).attr("fill", "white"); + g.append("path").attr("d", round(dash, 1)).attr("class", "strokes"); + } + } + + // swamp icons + if (height >= 21 && height < 22 && swampCount < +swampinessInput.value && land[i].used != 1) { + const g = swamps.append("g").attr("data-cell", cell); + swampCount++; + land[i].used = 1; + let swamp = drawSwamp(p[0],p[1]); + land[i].neighbors.forEach(function(e) { + if (cells[e].height >= 20 && cells[e].height < 30 && !cells[e].river && cells[e].used != 1) { + cells[e].used = 1; + swamp += drawSwamp(cells[e].data[0], cells[e].data[1]); + } + }); + g.append("path").attr("d", round(swamp, 1)); + } + + // forest icons + if (Math.random() < height / 100 && height >= 22 && height < 48) { + for (let c = 0, a = area; Math.random() < a / 15; c++, a -= 15) { + if (land[i].ctype === 1 && c > 0) break; + if (polygons[cell][c] === undefined) break; + const g = forests.append("g").attr("data-cell", cell); + if (c === 0) { + cx = rn(p[0] - 1 - Math.random(), 1); + cy = p[1] - 2; + } else { + const p2 = polygons[cell][c]; + if (c > 1) { + const dist = Math.hypot(p2[0] - polygons[cell][c-1][0],p2[1] - polygons[cell][c-1][1]); + if (dist < 2) continue; + } + cx = (p[0] * 0.5 + p2[0] * 1.5) / 2; + cy = (p[1] * 0.5 + p2[1] * 1.5) / 2 - 1; + } + const forest = "M" + cx + "," + cy + " q-1,0.8 -0.05,1.25 v0.75 h0.1 v-0.75 q0.95,-0.47 -0.05,-1.25 z "; + const light = "M" + cx + "," + cy + " q-1,0.8 -0.05,1.25 h0.1 q0.95,-0.47 -0.05,-1.25 z "; + const shade = "M" + cx + "," + cy + " q-1,0.8 -0.05,1.25 q-0.2,-0.55 0,-1.1 z "; + g.append("path").attr("d", forest); + g.append("path").attr("d", light).attr("fill", "white").attr("stroke", "none"); + g.append("path").attr("d", shade).attr("fill", "#999999").attr("stroke", "none"); + } + } + } + terrain.selectAll("g").selectAll("g").on("click", editReliefIcon); + console.timeEnd('drawRelief'); + } + + function addReliefIcon(height, type, cx, cy, cell) { + const g = terrain.select("#" + type).append("g").attr("data-cell", cell); + if (type === "mounts") { + const h = height >= 0.7 ? (height - 0.55) * 12 : 1.8; + const rnd = Math.random() * 0.8 + 0.2; + let mount = "M" + cx + "," + cy + " L" + (cx + h / 3 + rnd) + "," + (cy - h / 4 - rnd * 1.2) + " L" + (cx + h / 1.1) + "," + (cy - h) + " L" + (cx + h + rnd) + "," + (cy - h / 1.2 + rnd) + " L" + (cx + h * 2) + "," + cy; + let shade = "M" + cx + "," + cy + " L" + (cx + h / 3 + rnd) + "," + (cy - h / 4 - rnd * 1.2) + " L" + (cx + h / 1.1) + "," + (cy - h) + " L" + (cx + h / 1.5) + "," + cy; + let dash = "M" + (cx - 0.1) + "," + (cy + 0.3) + " L" + (cx + 2 * h + 0.1) + "," + (cy + 0.3); + dash += "M" + (cx + 0.4) + "," + (cy + 0.6) + " L" + (cx + 2 * h - 0.3) + "," + (cy + 0.6); + g.append("path").attr("d", round(mount, 1)).attr("stroke", "#5c5c70"); + g.append("path").attr("d", round(shade, 1)).attr("fill", "#999999"); + g.append("path").attr("d", round(dash, 1)).attr("class", "strokes"); + } + if (type === "hills") { + let h = height > 0.5 ? (height - 0.4) * 10 : 1.2; + if (h > 1.8) h = 1.8; + let hill = "M" + cx + "," + cy + " Q" + (cx + h) + "," + (cy - h) + " " + (cx + 2 * h) + "," + cy; + let shade = "M" + (cx + 0.6 * h) + "," + (cy + 0.1) + " Q" + (cx + h * 0.95) + "," + (cy - h * 0.91) + " " + (cx + 2 * h * 0.97) + "," + cy; + let dash = "M" + (cx - 0.1) + "," + (cy + 0.2) + " L" + (cx + 2 * h + 0.1) + "," + (cy + 0.2); + dash += "M" + (cx + 0.4) + "," + (cy + 0.4) + " L" + (cx + 2 * h - 0.3) + "," + (cy + 0.4); + g.append("path").attr("d", round(hill, 1)).attr("stroke", "#5c5c70"); + g.append("path").attr("d", round(shade, 1)).attr("fill", "white"); + g.append("path").attr("d", round(dash, 1)).attr("class", "strokes"); + } + if (type === "swamps") { + const swamp = drawSwamp(cx, cy); + g.append("path").attr("d", round(swamp, 1)); + } + if (type === "forests") { + const rnd = Math.random(); + const h = rnd * 0.4 + 0.6; + const forest = "M" + cx + "," + cy + " q-1,0.8 -0.05,1.25 v0.75 h0.1 v-0.75 q0.95,-0.47 -0.05,-1.25 z "; + const light = "M" + cx + "," + cy + " q-1,0.8 -0.05,1.25 h0.1 q0.95,-0.47 -0.05,-1.25 z "; + const shade = "M" + cx + "," + cy + " q-1,0.8 -0.05,1.25 q-0.2,-0.55 0,-1.1 z "; + g.append("path").attr("d", forest); + g.append("path").attr("d", light).attr("fill", "white").attr("stroke", "none"); + g.append("path").attr("d", shade).attr("fill", "#999999").attr("stroke", "none"); + } + g.on("click", editReliefIcon); + return g; + } + + function compareY(a, b) { + if (a.data[1] > b.data[1]) return 1; + if (a.data[1] < b.data[1]) return -1; + return 0; + } + + function drawSwamp(x, y) { + const h = 0.6; + let line = ""; + for (let c = 0; c < 3; c++) { + let cx; + let cy; + if (c == 0) { + cx = x; + cy = y - 0.5 - Math.random(); + } + if (c == 1) { + cx = x + h + Math.random(); + cy = y + h + Math.random(); + } + if (c == 2) { + cx = x - h - Math.random(); + cy = y + 2 * h + Math.random(); + } + line += "M" + cx + "," + cy + " H" + (cx - h / 6) + " M" + cx + "," + cy + " H" + (cx + h / 6) + " M" + cx + "," + cy + " L" + (cx - h / 3) + "," + (cy - h / 2) + " M" + cx + "," + cy + " V" + (cy - h / 1.5) + " M" + cx + "," + cy + " L" + (cx + h / 3) + "," + (cy - h / 2); + line += "M" + (cx - h) + "," + cy + " H" + (cx - h / 2) + " M" + (cx + h / 2) + "," + cy + " H" + (cx + h); + } + return line; + } + + function dragged(e) { + const el = d3.select(this); + const x = d3.event.x; + const y = d3.event.y; + el.raise().classed("drag", true); + if (el.attr("x")) { + el.attr("x", x).attr("y", y + 0.8); + const matrix = el.attr("transform"); + if (matrix) { + const angle = matrix.split('(')[1].split(')')[0].split(' ')[0]; + const bbox = el.node().getBBox(); + const rotate = "rotate(" + angle + " " + (bbox.x + bbox.width / 2) + " " + (bbox.y + bbox.height / 2) + ")"; + el.attr("transform", rotate); + } + } else { + el.attr("cx", x).attr("cy", y); + } + } + + function dragended(d) { + d3.select(this).classed("drag", false); + } + + // Complete the map for the "customize" mode + function getMap() { + if (customization !== 1) { + tip('Nothing to complete! Click on "Edit" or "Clear all" to enter a heightmap customization mode', null, "error"); + return; + } + if (+landmassCounter.innerHTML < 150) { + tip("Insufficient land area! Please add more land cells to complete the map", null, "error"); + return; + } + exitCustomization(); + console.time("TOTAL"); + markFeatures(); + drawOcean(); + elevateLakes(); + resolveDepressionsPrimary(); + reGraph(); + resolveDepressionsSecondary(); + flux(); + addLakes(); + if (!changeHeights.checked) restoreCustomHeights(); + drawCoastline(); + drawRelief(); + const keepData = states.length && manors.length; + if (keepData) { + restoreRegions(); + } else { + generateCultures(); + manorsAndRegions(); + } + cleanData(); + console.timeEnd("TOTAL"); + } + + // Add support "click to add" button events + $("#customizeTab").click(clickToAdd); + function clickToAdd() { + if (modules.clickToAdd) return; + modules.clickToAdd = true; + + // add label on click + $("#addLabel").click(function() { + if ($(this).hasClass('pressed')) { + $(".pressed").removeClass('pressed'); + restoreDefaultEvents(); + } else { + $(".pressed").removeClass('pressed'); + $(this).addClass('pressed'); + closeDialogs(".stable"); + viewbox.style("cursor", "crosshair").on("click", addLabelOnClick); + } + }); + + function addLabelOnClick() { + const point = d3.mouse(this); + const index = getIndex(point); + const x = rn(point[0],2), y = rn(point[1],2); + + // get culture in clicked point to generate a name + const closest = cultureTree.find(x, y); + const culture = cultureTree.data().indexOf(closest) || 0; + const name = generateName(culture); + + let group = labels.select("#addedLabels"); + if (!group.size()) { + group = labels.append("g").attr("id", "addedLabels") + .attr("fill", "#3e3e4b").attr("opacity", 1) + .attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC") + .attr("font-size", 18).attr("data-size", 18); + } + let id = "label" + Date.now().toString().slice(7); + group.append("text").attr("id", id).attr("x", x).attr("y", y).text(name).on("click", editLabel); + + if (d3.event.shiftKey === false) { + $("#addLabel").removeClass("pressed"); + restoreDefaultEvents(); + } + } + + // add burg on click + $("#addBurg").click(function() { + if ($(this).hasClass('pressed')) { + $(".pressed").removeClass('pressed'); + restoreDefaultEvents(); + tip("", true); + } else { + $(".pressed").removeClass('pressed'); + $(this).attr("data-state", -1).addClass('pressed'); + $("#burgAdd, #burgAddfromEditor").addClass('pressed'); + viewbox.style("cursor", "crosshair").on("click", addBurgOnClick); + tip("Click on map to place burg icon with a label. Hold Shift to place several", true); + } + }); + + function addBurgOnClick() { + const point = d3.mouse(this); + const index = getIndex(point); + const x = rn(point[0],2), y = rn(point[1],2); + + // get culture in clicked point to generate a name + let culture = cells[index].culture; + if (culture === undefined) culture = 0; + const name = generateName(culture); + + if (cells[index].height < 20) { + tip("Cannot place burg in the water! Select a land cell", null, "error"); + return; + } + if (cells[index].manor !== undefined) { + tip("There is already a burg in this cell. Please select a free cell", null, "error"); + $('#grid').fadeIn(); + d3.select("#toggleGrid").classed("buttonoff", false); + return; + } + const i = manors.length; + const size = burgIcons.select("#towns").attr("size"); + burgIcons.select("#towns").append("circle").attr("id", "burg"+i).attr("data-id", i).attr("cx", x).attr("cy", y).attr("r", size).on("click", editBurg); + burgLabels.select("#towns").append("text").attr("data-id", i).attr("x", x).attr("y", y).attr("dy", "-0.35em").text(name).on("click", editBurg); + invokeActiveZooming(); + + if (d3.event.shiftKey === false) { + $("#addBurg, #burgAdd, #burgAddfromEditor").removeClass("pressed"); + restoreDefaultEvents(); + } + + let region, state = +$("#addBurg").attr("data-state"); + if (state !== -1) { + region = states[state].capital === "neutral" ? "neutral" : state; + const oldRegion = cells[index].region; + if (region !== oldRegion) { + cells[index].region = region; + redrawRegions(); + } + } else { + region = cells[index].region; + state = region === "neutral" ? states.length - 1 : region; + } + cells[index].manor = i; + let score = cells[index].score; + if (score <= 0) {score = rn(Math.random(), 2);} + if (cells[index].crossroad) {score += cells[index].crossroad;} // crossroads + if (cells[index].confluence) {score += Math.pow(cells[index].confluence, 0.3);} // confluences + if (cells[index].port !== undefined) {score *= 3;} // port-capital + const population = rn(score, 1); + manors.push({i, cell:index, x, y, region, culture, name, population}); + recalculateStateData(state); + updateCountryEditors(); + tip("", true); + } + + // add river on click + $("#addRiver").click(function() { + if ($(this).hasClass('pressed')) { + $(".pressed").removeClass('pressed'); + unselect(); + } else { + $(".pressed").removeClass('pressed'); + unselect(); + $(this).addClass('pressed'); + closeDialogs(".stable"); + viewbox.style("cursor", "crosshair").on("click", addRiverOnClick); + tip("Click on map to place new river or extend an existing one", true); + } + }); + + function addRiverOnClick() { + const point = d3.mouse(this); + const index = diagram.find(point[0], point[1]).index; + let cell = cells[index]; + if (cell.river || cell.height < 20) return; + const dataRiver = []; // to store river points + const last = $("#rivers > path").last(); + const river = last.length ? +last.attr("id").slice(5) + 1 : 0; + cell.flux = 0.85; + while (cell) { + cell.river = river; + const x = cell.data[0], y = cell.data[1]; + dataRiver.push({x, y, cell:index}); + const nHeights = []; + cell.neighbors.forEach(function(e) {nHeights.push(cells[e].height);}); + const minId = nHeights.indexOf(d3.min(nHeights)); + const min = cell.neighbors[minId]; + const tx = cells[min].data[0], ty = cells[min].data[1]; + if (cells[min].height < 20) { + const px = (x + tx) / 2; + const py = (y + ty) / 2; + dataRiver.push({x: px, y: py, cell:index}); + cell = undefined; + } else { + if (cells[min].river === undefined) {cells[min].flux += cell.flux; cell = cells[min];} + else { + const r = cells[min].river; + const riverEl = $("#river"+r); + const riverCells = $.grep(land, function(e) {return e.river === r;}); + riverCells.sort(function(a, b) {return b.height - a.height}); + const riverCellsUpper = $.grep(riverCells, function(e) {return e.height > cells[min].height;}); + if (dataRiver.length > riverCellsUpper.length) { + // new river is more perspective + const avPrec = rn(precInput.value / Math.sqrt(cells.length), 2); + let dataRiverMin = []; + riverCells.map(function(c) { + if (c.height < cells[min].height) { + cells[c.index].river = undefined; + cells[c.index].flux = avPrec; + } else { + dataRiverMin.push({x:c.data[0],y:c.data[1],cell:c.index}); + } + }); + cells[min].flux += cell.flux; + if (cells[min].confluence) {cells[min].confluence += riverCellsUpper.length;} + else {cells[min].confluence = riverCellsUpper.length;} + cell = cells[min]; + // redraw old river's upper part or remove if small + if (dataRiverMin.length > 1) { + var riverAmended = amendRiver(dataRiverMin, 1); + var d = drawRiver(riverAmended, 1.3, 1); + riverEl.attr("d", d).attr("data-width", 1.3).attr("data-increment", 1); + } else { + riverEl.remove(); + dataRiverMin.map(function(c) {cells[c.cell].river = undefined;}); + } + } else { + if (cells[min].confluence) {cells[min].confluence += dataRiver.length;} + else {cells[min].confluence = dataRiver.length;} + cells[min].flux += cell.flux; + dataRiver.push({x: tx, y: ty, cell:min}); + cell = undefined; + } + } + } + } + const rndFactor = 0.2 + Math.random() * 1.6; // random factor in range 0.2-1.8 + var riverAmended = amendRiver(dataRiver, rndFactor); + var d = drawRiver(riverAmended, 1.3, 1); + rivers.append("path").attr("d", d).attr("id", "river"+river) + .attr("data-width", 1.3).attr("data-increment", 1).on("click", editRiver); + } + + // add relief icon on click + $("#addRelief").click(function() { + if ($(this).hasClass('pressed')) { + $(".pressed").removeClass('pressed'); + restoreDefaultEvents(); + } else { + $(".pressed").removeClass('pressed'); + $(this).addClass('pressed'); + closeDialogs(".stable"); + viewbox.style("cursor", "crosshair").on("click", addReliefOnClick); + tip("Click on map to place relief icon. Hold Shift to place several", true); + } + }); + + function addReliefOnClick() { + const point = d3.mouse(this); + const index = getIndex(point); + const height = cells[index].height; + if (height < 20) { + tip("Cannot place icon in the water! Select a land cell"); + return; + } + + const x = rn(point[0],2), y = rn(point[1],2); + const type = reliefGroup.value; + addReliefIcon(height / 100, type, x, y, index); + + if (d3.event.shiftKey === false) { + $("#addRelief").removeClass("pressed"); + restoreDefaultEvents(); + } + tip("", true); + } + + // add route on click + $("#addRoute").click(function() { + if (!modules.editRoute) editRoute(); + $("#routeNew").click(); + }); + + // add marker on click + $("#addMarker").click(function() { + if ($(this).hasClass('pressed')) { + $(".pressed").removeClass('pressed'); + restoreDefaultEvents(); + } else { + $(".pressed").removeClass('pressed'); + $(this).addClass('pressed'); + $("#markerAdd").addClass('pressed'); + viewbox.style("cursor", "crosshair").on("click", addMarkerOnClick); + } + }); + + function addMarkerOnClick() { + const point = d3.mouse(this); + let x = rn(point[0],2), y = rn(point[1],2); + let selected = markerSelectGroup.value; + let valid = selected && d3.select("#defs-markers").select("#"+selected).size() === 1; + let symbol = valid ? "#"+selected : "#marker0"; + let desired = valid ? markers.select("[data-id='" + symbol + "']").attr("data-size") : 1; + if (isNaN(desired)) desired = 1; + let id = "marker" + Date.now().toString().slice(7); // unique id + let size = desired * 5 + 25 / scale; + + markers.append("use").attr("id", id).attr("xlink:href", symbol).attr("data-id", symbol) + .attr("data-x", x).attr("data-y", y).attr("x", x - size / 2).attr("y", y - size) + .attr("data-size", desired).attr("width", size).attr("height", size).on("click", editMarker); + + if (d3.event.shiftKey === false) { + $("#addMarker, #markerAdd").removeClass("pressed"); + restoreDefaultEvents(); + } + } + + } + + // return cell / polly Index or error + function getIndex(point) { + let c = diagram.find(point[0], point[1]); + if (!c) { + console.error("Cannot find closest cell for points" + point[0] + ", " + point[1]); + return; + } + return c.index; + } + + // re-calculate data for a particular state + function recalculateStateData(state) { + const s = states[state] || states[states.length - 1]; + if (s.capital === "neutral") state = "neutral"; + const burgs = $.grep(manors, function(e) {return e.region === state;}); + s.burgs = burgs.length; + let burgsPop = 0; // get summ of all burgs population + burgs.map(function(b) {burgsPop += b.population;}); + s.urbanPopulation = rn(burgsPop, 1); + const regionCells = $.grep(cells, function(e) {return (e.region === state);}); + let cellsPop = 0, area = 0; + regionCells.map(function(c) { + cellsPop += c.pop; + area += c.area; + }); + s.cells = regionCells.length; + s.area = rn(area); + s.ruralPopulation = rn(cellsPop, 1); + } + + function changeSelectedOnClick() { + const point = d3.mouse(this); + const index = diagram.find(point[0],point[1]).index; + if (cells[index].height < 20) return; + $(".selected").removeClass("selected"); + let color; + + // select state + if (customization === 2) { + const assigned = regions.select("#temp").select("path[data-cell='"+index+"']"); + let s = assigned.size() ? assigned.attr("data-state") : cells[index].region; + if (s === "neutral") s = states.length - 1; + color = states[s].color; + if (color === "neutral") color = "white"; + $("#state"+s).addClass("selected"); + } + + // select culture + if (customization === 4) { + const assigned = cults.select("#cult"+index); + const c = assigned.attr("data-culture") !== null + ? +assigned.attr("data-culture") + : cells[index].culture; + color = cultures[c].color; + $("#culture"+c).addClass("selected"); + } + + debug.selectAll(".circle").attr("stroke", color); + } + + // fetch default fonts if not done before + function loadDefaultFonts() { + if (!$('link[href="fonts.css"]').length) { + $("head").append(''); + const fontsToAdd = ["Amatic+SC:700", "IM+Fell+English", "Great+Vibes", "MedievalSharp", "Metamorphous", + "Nova+Script", "Uncial+Antiqua", "Underdog", "Caesar+Dressing", "Bitter", "Yellowtail", "Montez", + "Shadows+Into+Light", "Fredericka+the+Great", "Orbitron", "Dancing+Script:700", + "Architects+Daughter", "Kaushan+Script", "Gloria+Hallelujah", "Satisfy", "Comfortaa:700", "Cinzel"]; + fontsToAdd.forEach(function(f) {if (fonts.indexOf(f) === -1) fonts.push(f);}); + updateFontOptions(); + } + } + + function fetchFonts(url) { + return new Promise((resolve, reject) => { + if (url === "") { + tip("Use a direct link to any @font-face declaration or just font name to fetch from Google Fonts"); + return; + } + if (url.indexOf("http") === -1) { + url = url.replace(url.charAt(0), url.charAt(0).toUpperCase()).split(" ").join("+"); + url = "https://fonts.googleapis.com/css?family=" + url; + } + const fetched = addFonts(url).then(fetched => { + if (fetched === undefined) { + tip("Cannot fetch font for this value!"); + return; + } + if (fetched === 0) { + tip("Already in the fonts list!"); + return; + } + updateFontOptions(); + if (fetched === 1) { + tip("Font " + fonts[fonts.length - 1] + " is fetched"); + } else if (fetched > 1) { + tip(fetched + " fonts are added to the list"); + } + resolve(fetched); + }); + }) + } + + function addFonts(url) { + $("head").append(''); + return fetch(url) + .then(resp => resp.text()) + .then(text => { + let s = document.createElement('style'); + s.innerHTML = text; + document.head.appendChild(s); + let styleSheet = Array.prototype.filter.call( + document.styleSheets, + sS => sS.ownerNode === s)[0]; + let FontRule = rule => { + let family = rule.style.getPropertyValue('font-family'); + let font = family.replace(/['"]+/g, '').replace(/ /g, "+"); + let weight = rule.style.getPropertyValue('font-weight'); + if (weight !== "400") font += ":" + weight; + if (fonts.indexOf(font) == -1) { + fonts.push(font); + fetched++ + } + }; + let fetched = 0; + for (let r of styleSheet.cssRules) {FontRule(r);} + document.head.removeChild(s); + return fetched; + }) + .catch(function() {}); + } + + // Update font list for Label and Burg Editors + function updateFontOptions() { + labelFontSelect.innerHTML = ""; + for (let i=0; i < fonts.length; i++) { + const opt = document.createElement('option'); + opt.value = i; + const font = fonts[i].split(':')[0].replace(/\+/g, " "); + opt.style.fontFamily = opt.innerHTML = font; + labelFontSelect.add(opt); + } + burgSelectDefaultFont.innerHTML = labelFontSelect.innerHTML; + } + + // convert RGB color string to HEX without # + function toHEX(rgb){ + if (rgb.charAt(0) === "#") {return rgb;} + rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + return (rgb && rgb.length === 4) ? "#" + + ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; + } + + // random number in a range + function rand(min, max) { + if (min === undefined && !max === undefined) return Math.random(); + if (max === undefined) {max = min; min = 0;} + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + // round value to d decimals + function rn(v, d) { + var d = d || 0; + const m = Math.pow(10, d); + return Math.round(v * m) / m; + } + + // round string to d decimals + function round(s, d) { + var d = d || 1; + return s.replace(/[\d\.-][\d\.e-]*/g, function(n) {return rn(n, d);}) + } + + // corvent number to short string with SI postfix + function si(n) { + if (n >= 1e9) {return rn(n / 1e9, 1) + "B";} + if (n >= 1e8) {return rn(n / 1e6) + "M";} + if (n >= 1e6) {return rn(n / 1e6, 1) + "M";} + if (n >= 1e4) {return rn(n / 1e3) + "K";} + if (n >= 1e3) {return rn(n / 1e3, 1) + "K";} + return rn(n); + } + + // getInteger number from user input data + function getInteger(value) { + const metric = value.slice(-1); + if (metric === "K") {return parseInt(value.slice(0, -1) * 1e3);} + if (metric === "M") {return parseInt(value.slice(0, -1) * 1e6);} + if (metric === "B") {return parseInt(value.slice(0, -1) * 1e9);} + return parseInt(value); + } + + // downalod map as SVG or PNG file + function saveAsImage(type) { + console.time("saveAsImage"); + const webSafe = ["Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"]; + // get non-standard fonts used for labels to fetch them from web + const fontsInUse = []; // to store fonts currently in use + labels.selectAll("g").each(function(d) { + const font = d3.select(this).attr("data-font"); + if (!font) return; + if (webSafe.indexOf(font) !== -1) return; // do not fetch web-safe fonts + if (fontsInUse.indexOf(font) === -1) fontsInUse.push(font); + }); + const fontsToLoad = "https://fonts.googleapis.com/css?family=" + fontsInUse.join("|"); + + // clone svg + const cloneEl = document.getElementsByTagName("svg")[0].cloneNode(true); + cloneEl.id = "fantasyMap"; + document.getElementsByTagName("body")[0].appendChild(cloneEl); + const clone = d3.select("#fantasyMap"); + + // rteset transform for svg + if (type === "svg") { + clone.attr("width", graphWidth).attr("height", graphHeight); + clone.select("#viewbox").attr("transform", null); + if (svgWidth !== graphWidth || svgHeight !== graphHeight) { + // move scale bar to right bottom corner + const el = clone.select("#scaleBar"); + if (!el.size()) return; + const bbox = el.select("rect").node().getBBox(); + const tr = [graphWidth - bbox.width, graphHeight - (bbox.height - 10)]; + el.attr("transform", "translate(" + rn(tr[0]) + "," + rn(tr[1]) + ")"); + } + + // to fix use elements sizing + clone.selectAll("use").each(function() { + const size = this.parentNode.getAttribute("size") || 1; + this.setAttribute("width", size + "px"); + this.setAttribute("height", size + "px"); + }); + + // clean attributes + //clone.selectAll("*").each(function() { + // const attributes = this.attributes; + // for (let i = 0; i < attributes.length; i++) { + // const attr = attributes[i]; + // if (attr.value === "" || attr.name.includes("data")) { + // this.removeAttribute(attr.name); + // } + // } + //}); + + } + + // for each g element get inline style + const emptyG = clone.append("g").node(); + const defaultStyles = window.getComputedStyle(emptyG); + + // show hidden labels but in reduced size + clone.select("#labels").selectAll(".hidden").each(function(e) { + const size = d3.select(this).attr("font-size"); + d3.select(this).classed("hidden", false).attr("font-size", rn(size * 0.4, 2)); + }); + + // save group css to style attribute + clone.selectAll("g, #ruler > g > *, #scaleBar > text").each(function(d) { + const compStyle = window.getComputedStyle(this); + let style = ""; + for (let i=0; i < compStyle.length; i++) { + const key = compStyle[i]; + const value = compStyle.getPropertyValue(key); + // Firefox mask hack + if (key === "mask-image" && value !== defaultStyles.getPropertyValue(key)) { + style += "mask-image: url('#shape');"; + continue; + } + if (key === "cursor") continue; // cursor should be default + if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute + if (value === defaultStyles.getPropertyValue(key)) continue; + style += key + ':' + value + ';'; + } + if (style != "") this.setAttribute('style', style); + }); + emptyG.remove(); + + // load fonts as dataURI so they will be available in downloaded svg/png + GFontToDataURI(fontsToLoad).then(cssRules => { + clone.select("defs").append("style").text(cssRules.join('\n')); + const svg_xml = (new XMLSerializer()).serializeToString(clone.node()); + clone.remove(); + const blob = new Blob([svg_xml], {type: 'image/svg+xml;charset=utf-8'}); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.target = "_blank"; + if (type === "png") { + const ratio = svgHeight / svgWidth; + canvas.width = svgWidth * pngResolutionInput.value; + canvas.height = svgHeight * pngResolutionInput.value; + const img = new Image(); + img.src = url; + img.onload = function(){ + window.URL.revokeObjectURL(url); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + link.download = "fantasy_map_" + Date.now() + ".png"; + canvas.toBlob(function(blob) { + link.href = window.URL.createObjectURL(blob); + document.body.appendChild(link); + link.click(); + window.setTimeout(function() {window.URL.revokeObjectURL(link.href);}, 5000); + }); + canvas.style.opacity = 0; + canvas.width = svgWidth; + canvas.height = svgHeight; + } + } else { + link.download = "fantasy_map_" + Date.now() + ".svg"; + link.href = url; + document.body.appendChild(link); + link.click(); + } + console.timeEnd("saveAsImage"); + window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 5000); + }); + } + + // Code from Kaiido's answer: + // https://stackoverflow.com/questions/42402584/how-to-use-google-fonts-in-canvas-when-drawing-dom-objects-in-svg + function GFontToDataURI(url) { + return fetch(url) // first fecth the embed stylesheet page + .then(resp => resp.text()) // we only need the text of it + .then(text => { + let s = document.createElement('style'); + s.innerHTML = text; + document.head.appendChild(s); + let styleSheet = Array.prototype.filter.call( + document.styleSheets, + sS => sS.ownerNode === s)[0]; + let FontRule = rule => { + let src = rule.style.getPropertyValue('src'); + let family = rule.style.getPropertyValue('font-family'); + let url = src.split('url(')[1].split(')')[0]; + return { + rule: rule, + src: src, + url: url.substring(url.length - 1, 1) + }; + }; + let fontRules = [],fontProms = []; + + for (let r of styleSheet.cssRules) { + let fR = FontRule(r); + fontRules.push(fR); + fontProms.push( + fetch(fR.url) // fetch the actual font-file (.woff) + .then(resp => resp.blob()) + .then(blob => { + return new Promise(resolve => { + let f = new FileReader(); + f.onload = e => resolve(f.result); + f.readAsDataURL(blob); + }) + }) + .then(dataURL => { + return fR.rule.cssText.replace(fR.url, dataURL); + }) + ) + } + document.head.removeChild(s); // clean up + return Promise.all(fontProms); // wait for all this has been done + }); + } + + // Save in .map format, based on FileSystem API + function saveMap() { + console.time("saveMap"); + // data convention: 0 - params; 1 - all points; 2 - cells; 3 - manors; 4 - states; + // 5 - svg; 6 - options (see below); 7 - cultures; + // 8 - empty (former nameBase); 9 - empty (former nameBases); 10 - heights; 11 - notes; + // size stats: points = 6%, cells = 36%, manors and states = 2%, svg = 56%; + const date = new Date(); + const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); + const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator"; + const params = version + "|" + license + "|" + dateString + "|" + seed; + const options = customization + "|" + + distanceUnit.value + "|" + distanceScale.value + "|" + areaUnit.value + "|" + + barSize.value + "|" + barLabel.value + "|" + barBackOpacity.value + "|" + barBackColor.value + "|" + + populationRate.value + "|" + urbanization.value; + + // set zoom / transform values to default + svg.attr("width", graphWidth).attr("height", graphHeight); + const transform = d3.zoomTransform(svg.node()); + viewbox.attr("transform", null); + const oceanBack = ocean.select("rect"); + const oceanShift = [oceanBack.attr("x"), oceanBack.attr("y"), oceanBack.attr("width"), oceanBack.attr("height")]; + oceanBack.attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); + + const svg_xml = (new XMLSerializer()).serializeToString(svg.node()); + const line = "\r\n"; + let data = params + line + JSON.stringify(points) + line + JSON.stringify(cells) + line; + data += JSON.stringify(manors) + line + JSON.stringify(states) + line + svg_xml + line + options + line; + data += JSON.stringify(cultures) + line + "" + line + "" + line + heights + line + JSON.stringify(notes) + line; + const dataBlob = new Blob([data], {type: "text/plain"}); + const dataURL = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + link.download = "fantasy_map_" + Date.now() + ".map"; + link.href = dataURL; + document.body.appendChild(link); + link.click(); + + // restore initial values + svg.attr("width", svgWidth).attr("height", svgHeight); + zoom.transform(svg, transform); + oceanBack.attr("x", oceanShift[0]).attr("y", oceanShift[1]).attr("width", oceanShift[2]).attr("height", oceanShift[3]); + + console.timeEnd("saveMap"); + window.setTimeout(function() {window.URL.revokeObjectURL(dataURL);}, 4000); + } + + // Map Loader based on FileSystem API + $("#mapToLoad").change(function() { + console.time("loadMap"); + closeDialogs(); + const fileToLoad = this.files[0]; + this.value = ""; + uploadFile(fileToLoad); + }); + + function uploadFile(file, callback) { + console.time("loadMap"); + const fileReader = new FileReader(); + fileReader.onload = function(fileLoadedEvent) { + const dataLoaded = fileLoadedEvent.target.result; + const data = dataLoaded.split("\r\n"); + // data convention: 0 - params; 1 - all points; 2 - cells; 3 - manors; 4 - states; + // 5 - svg; 6 - options; 7 - cultures; 8 - none; 9 - none; 10 - heights; 11 - notes; + const params = data[0].split("|"); + const mapVersion = params[0] || data[0]; + if (mapVersion !== version) { + let message = `The Map version `; + // mapVersion reference was not added to downloaded map before v. 0.52b, so I cannot support really old files + if (mapVersion.length <= 10) { + message += `(${mapVersion}) does not match the Generator version (${version}). The map will be auto-updated. + In case of critical issues you may send the .map file + to me + or just keep using + an appropriate version + of the Generator`; + } else if (!mapVersion || parseFloat(mapVersion) < 0.54) { + message += `you are trying to load is too old and cannot be updated. Please re-create the map or just keep using + an archived version + of the Generator. Please note the Generator is still on demo and a lot of changes are being made every month`; + } + alertMessage.innerHTML = message; + $("#alert").dialog({title: "Warning", buttons: {OK: function() { + loadDataFromMap(data); + }}}); + } else {loadDataFromMap(data);} + if (mapVersion.length > 10) {console.error("Cannot load map"); } + }; + fileReader.readAsText(file, "UTF-8"); + if (callback) {callback();} + } + + function loadDataFromMap(data) { + closeDialogs(); + // update seed + const params = data[0].split("|"); + if (params[3]) { + seed = params[3]; + optionsSeed.value = seed; + } + + // get options + if (data[0] === "0.52b" || data[0] === "0.53b") { + customization = 0; + } else if (data[6]) { + const options = data[6].split("|"); + customization = +options[0] || 0; + if (options[1]) distanceUnit.value = options[1]; + if (options[2]) distanceScale.value = options[2]; + if (options[3]) areaUnit.value = options[3]; + if (options[4]) barSize.value = options[4]; + if (options[5]) barLabel.value = options[5]; + if (options[6]) barBackOpacity.value = options[6]; + if (options[7]) barBackColor.value = options[7]; + if (options[8]) populationRate.value = options[8]; + if (options[9]) urbanization.value = options[9]; + } + + // replace old svg + svg.remove(); + if (data[0] === "0.52b" || data[0] === "0.53b") { + states = []; // no states data in old maps + document.body.insertAdjacentHTML("afterbegin", data[4]); + } else { + states = JSON.parse(data[4]); + document.body.insertAdjacentHTML("afterbegin", data[5]); + } + + svg = d3.select("svg"); + + // always change graph size to the size of loaded map + const nWidth = +svg.attr("width"), nHeight = +svg.attr("height"); + graphWidth = nWidth; + graphHeight = nHeight; + voronoi = d3.voronoi().extent([[-1, -1],[graphWidth+1, graphHeight+1]]); + zoom.translateExtent([[0, 0],[graphWidth, graphHeight]]).scaleExtent([1, 20]).scaleTo(svg, 1); + viewbox.attr("transform", null); + + // temporary fit loaded svg element to current canvas size + svg.attr("width", svgWidth).attr("height", svgHeight); + if (nWidth !== svgWidth || nHeight !== svgHeight) { + alertMessage.innerHTML = `The loaded map has size ${nWidth} x ${nHeight} pixels, while the current canvas size is ${svgWidth} x ${svgHeight} pixels. + Click "Rescale" to fit the map to the current canvas size. Click "OK" to browse the map without rescaling`; + $("#alert").dialog({title: "Map size conflict", + buttons: { + Rescale: function() { + applyLoadedData(data); + // rescale loaded map + const xRatio = svgWidth / nWidth; + const yRatio = svgHeight / nHeight; + const scaleTo = rn(Math.min(xRatio, yRatio), 4); + // calculate frames to scretch ocean background + const extent = (100 / scaleTo) + "%"; + const xShift = (nWidth * scaleTo - svgWidth) / 2 / scaleTo; + const yShift = (nHeight * scaleTo - svgHeight) / 2 / scaleTo; + svg.select("#ocean").selectAll("rect").attr("x", xShift).attr("y", yShift).attr("width", extent).attr("height", extent); + zoom.translateExtent([[0, 0],[nWidth, nHeight]]).scaleExtent([scaleTo, 20]).scaleTo(svg, scaleTo); + $(this).dialog("close"); + }, + OK: function() { + changeMapSize(); + applyLoadedData(data); + $(this).dialog("close"); + } + } + }); + } else { + applyLoadedData(data); + } + } + + function applyLoadedData(data) { + // redefine variables + defs = svg.select("#deftemp"); + viewbox = svg.select("#viewbox"); + ocean = viewbox.select("#ocean"); + oceanLayers = ocean.select("#oceanLayers"); + oceanPattern = ocean.select("#oceanPattern"); + landmass = viewbox.select("#landmass"); + grid = viewbox.select("#grid"); + overlay = viewbox.select("#overlay"); + terrs = viewbox.select("#terrs"); + cults = viewbox.select("#cults"); + routes = viewbox.select("#routes"); + roads = routes.select("#roads"); + trails = routes.select("#trails"); + rivers = viewbox.select("#rivers"); + terrain = viewbox.select("#terrain"); + regions = viewbox.select("#regions"); + borders = viewbox.select("#borders"); + stateBorders = borders.select("#stateBorders"); + neutralBorders = borders.select("#neutralBorders"); + coastline = viewbox.select("#coastline"); + lakes = viewbox.select("#lakes"); + searoutes = routes.select("#searoutes"); + labels = viewbox.select("#labels"); + icons = viewbox.select("#icons"); + markers = viewbox.select("#markers"); + ruler = viewbox.select("#ruler"); + debug = viewbox.select("#debug"); + + if (!d3.select("#defs-markers").size()) { + let symbol = '?'; + let cont = document.getElementsByTagName("defs"); + cont[0].insertAdjacentHTML("afterbegin", symbol); + markers = viewbox.append("g").attr("id", "markers"); + } + + // version control: ensure required groups are created with correct data + if (!labels.select("#burgLabels").size()) { + labels.append("g").attr("id", "burgLabels"); + $("#labels #capitals, #labels #towns").detach().appendTo($("#burgLabels")); + } + + if (!icons.select("#burgIcons").size()) { + icons.append("g").attr("id", "burgIcons"); + $("#icons #capitals, #icons #towns").detach().appendTo($("#burgIcons")); + icons.select("#burgIcons").select("#capitals").attr("size", 1).attr("fill-opacity", .7).attr("stroke-opacity", 1); + icons.select("#burgIcons").select("#towns").attr("size", .5).attr("fill-opacity", .7).attr("stroke-opacity", 1); + } + + icons.selectAll("g").each(function() { + const size = this.getAttribute("font-size"); + if (size === null || size === undefined) return; + this.removeAttribute("font-size"); + this.setAttribute("size", size); + }); + + icons.select("#burgIcons").selectAll("circle").each(function() { + this.setAttribute("r", this.parentNode.getAttribute("size")); + }); + + icons.selectAll("use").each(function() { + const size = this.parentNode.getAttribute("size"); + if (size === null || size === undefined) return; + this.setAttribute("width", size); + this.setAttribute("height", size); + }); + + if (!labels.select("#countries").size()) { + labels.append("g").attr("id", "countries") + .attr("fill", "#3e3e4b").attr("opacity", 1) + .attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC") + .attr("font-size", 14).attr("data-size", 14); + } + + burgLabels = labels.select("#burgLabels"); + burgIcons = icons.select("#burgIcons"); + + // restore events + svg.call(zoom); + restoreDefaultEvents(); + viewbox.on("touchmove mousemove", moved); + overlay.selectAll("*").call(d3.drag().on("start", elementDrag)); + terrain.selectAll("g").selectAll("g").on("click", editReliefIcon); + labels.selectAll("text").on("click", editLabel); + icons.selectAll("circle, path, use").on("click", editIcon); + burgLabels.selectAll("text").on("click", editBurg); + burgIcons.selectAll("circle, path, use").on("click", editBurg); + rivers.selectAll("path").on("click", editRiver); + routes.selectAll("path").on("click", editRoute); + markers.selectAll("use").on("click", editMarker); + svg.select("#scaleBar").call(d3.drag().on("start", elementDrag)).on("click", editScale); + ruler.selectAll("g").call(d3.drag().on("start", elementDrag)); + ruler.selectAll("g").selectAll("text").on("click", removeParent); + ruler.selectAll(".opisometer").selectAll("circle").call(d3.drag().on("start", opisometerEdgeDrag)); + ruler.selectAll(".linear").selectAll("circle:not(.center)").call(d3.drag().on("drag", rulerEdgeDrag)); + ruler.selectAll(".linear").selectAll("circle.center").call(d3.drag().on("drag", rulerCenterDrag)); + + // update data + const newPoints = []; + riversData = [], queue = [], elSelected = ""; + points = JSON.parse(data[1]); + cells = JSON.parse(data[2]); + manors = JSON.parse(data[3]); + if (data[7]) cultures = JSON.parse(data[7]); + if (data[7] === undefined) generateCultures(); + if (data[11]) notes = JSON.parse(data[11]); + + // place random point + function placePoint() { + const x = Math.floor(Math.random() * graphWidth * 0.8 + graphWidth * 0.1); + const y = Math.floor(Math.random() * graphHeight * 0.8 + graphHeight * 0.1); + return [x, y]; + } + + // ensure each culure has a valid namesbase assigned, if not assign first base + if (!nameBase[0]) applyDefaultNamesData(); + cultures.forEach(function(c) { + const b = c.base; + if (b === undefined) c.base = 0; + if (!nameBase[b] || !nameBases[b]) c.base = 0; + if (c.center === undefined) c.center = placePoint(); + }); + const graphSizeAdj = 90 / Math.sqrt(cells.length, 2); // adjust to different graphSize + + // cells validations + cells.forEach(function(c, d) { + // collect points + newPoints.push(c.data); + + // update old 0-1 height range to a new 0-100 range + if (c.height < 1) c.height = Math.trunc(c.height * 100); + if (c.height === 1 && c.region !== undefined && c.flux !== undefined) c.height = 100; + + // check if there are any unavailable cultures + if (c.culture > cultures.length - 1) { + const center = [c.data[0],c.data[1]]; + const cult = {name:"AUTO_"+c.culture, color:"#ff0000", base:0, center}; + cultures.push(cult); + } + + if (c.height >= 20) { + if (!polygons[d] || !polygons[d].length) return; + // calculate area + if (c.area === undefined || isNaN(c.area)) { + const area = d3.polygonArea(polygons[d]); + c.area = rn(Math.abs(area), 2); + } + // calculate population + if (c.pop === undefined || isNaN(c.pop)) { + let population = 0; + const elevationFactor = Math.pow((100 - c.height) / 100, 3); + population = elevationFactor * c.area * graphSizeAdj; + if (c.region === "neutral") population *= 0.5; + c.pop = rn(population, 1); + } + // if culture is undefined, set to 0 + if (c.culture === undefined || isNaN(c.culture)) c.culture = 0; + } + }); + + land = $.grep(cells, function(e) {return (e.height >= 20);}); + calculateVoronoi(newPoints); + + // get heights Uint8Array + if (data[10]) {heights = new Uint8Array(data[10].split(","));} + else { + heights = new Uint8Array(points.length); + for (let i=0; i < points.length; i++) { + const cell = diagram.find(points[i][0],points[i][1]).index; + heights[i] = cells[cell].height; + } + } + + // restore Heightmap customization mode + if (customization === 1) { + optionsTrigger.click(); + $("#customizeHeightmap, #customizationMenu").slideDown(); + $("#openEditor").slideUp(); + updateHistory(); + customizeTab.click(); + paintBrushes.click(); + tip("The map is in Heightmap customization mode. Please finalize the Heightmap", true); + } + // restore Country Edition mode + if (customization === 2 || customization === 3) tip("The map is in Country Edition mode. Please complete the assignment", true); + + // restore layers state + d3.select("#toggleCultures").classed("buttonoff", !cults.selectAll("path").size()); + d3.select("#toggleHeight").classed("buttonoff", !terrs.selectAll("path").size()); + d3.select("#toggleCountries").classed("buttonoff", regions.style("display") === "none"); + d3.select("#toggleRivers").classed("buttonoff", rivers.style("display") === "none"); + d3.select("#toggleOcean").classed("buttonoff", oceanPattern.style("display") === "none"); + d3.select("#toggleRelief").classed("buttonoff", terrain.style("display") === "none"); + d3.select("#toggleBorders").classed("buttonoff", borders.style("display") === "none"); + d3.select("#toggleIcons").classed("buttonoff", icons.style("display") === "none"); + d3.select("#toggleLabels").classed("buttonoff", labels.style("display") === "none"); + d3.select("#toggleRoutes").classed("buttonoff", routes.style("display") === "none"); + d3.select("#toggleGrid").classed("buttonoff", grid.style("display") === "none"); + + // update map to support some old versions and fetch fonts + labels.selectAll("g").each(function(d) { + const el = d3.select(this); + if (el.attr("id") === "burgLabels") return; + const font = el.attr("data-font"); + if (font && fonts.indexOf(font) === -1) addFonts("https://fonts.googleapis.com/css?family=" + font); + if (!el.attr("data-size")) el.attr("data-size", +el.attr("font-size")); + if (el.style("display") === "none") el.node().style.display = null; + }); + + invokeActiveZooming(); + console.timeEnd("loadMap"); + } + + // get square grid with some jirrering + function getJitteredGrid() { + let sizeMod = rn((graphWidth + graphHeight) / 1500, 2); // screen size modifier + spacing = rn(7.5 * sizeMod / graphSize, 2); // space between points before jirrering + const radius = spacing / 2; // square radius + const jittering = radius * 0.9; // max deviation + const jitter = function() {return Math.random() * 2 * jittering - jittering;}; + let points = []; + for (let y = radius; y < graphHeight; y += spacing) { + for (let x = radius; x < graphWidth; x += spacing) { + let xj = rn(x + jitter(), 2); + let yj = rn(y + jitter(), 2); + points.push([xj, yj]); + } + } + return points; + } + + // Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys + d3.select("body").on("keydown", function() { + const active = document.activeElement.tagName; + if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; + const key = d3.event.keyCode; + const ctrl = d3.event.ctrlKey; + const p = d3.mouse(this); + if (key === 117) $("#randomMap").click(); // "F6" for new map + else if (key === 27) closeDialogs(); // Escape to close all dialogs + else if (key === 79) optionsTrigger.click(); // "O" to toggle options + else if (key === 80) saveAsImage("png"); // "P" to save as PNG + else if (key === 83) saveAsImage("svg"); // "S" to save as SVG + else if (key === 77) saveMap(); // "M" to save MAP file + else if (key === 76) mapToLoad.click(); // "L" to load MAP + else if (key === 32) console.table(cells[diagram.find(p[0],p[1]).index]); // Space to log focused cell data + else if (key === 192) console.log(cells); // "`" to log cells data + else if (key === 66) console.table(manors); // "B" to log burgs data + else if (key === 67) console.table(states); // "C" to log countries data + else if (key === 70) console.table(features); // "F" to log features data + else if (key === 37) zoom.translateBy(svg, 10, 0); // Left to scroll map left + else if (key === 39) zoom.translateBy(svg, -10, 0); // Right to scroll map right + else if (key === 38) zoom.translateBy(svg, 0, 10); // Up to scroll map up + else if (key === 40) zoom.translateBy(svg, 0, -10); // Up to scroll map up + else if (key === 107) zoom.scaleBy(svg, 1.2); // Plus to zoom map up + else if (key === 109) zoom.scaleBy(svg, 0.8); // Minus to zoom map out + else if (key === 48 || key === 96) resetZoom(); // 0 to reset zoom + else if (key === 49 || key === 97) zoom.scaleTo(svg, 1); // 1 to zoom to 1 + else if (key === 50 || key === 98) zoom.scaleTo(svg, 2); // 2 to zoom to 2 + else if (key === 51 || key === 99) zoom.scaleTo(svg, 3); // 3 to zoom to 3 + else if (key === 52 || key === 100) zoom.scaleTo(svg, 4); // 4 to zoom to 4 + else if (key === 53 || key === 101) zoom.scaleTo(svg, 5); // 5 to zoom to 5 + else if (key === 54 || key === 102) zoom.scaleTo(svg, 6); // 6 to zoom to 6 + else if (key === 55 || key === 103) zoom.scaleTo(svg, 7); // 7 to zoom to 7 + else if (key === 56 || key === 104) zoom.scaleTo(svg, 8); // 8 to zoom to 8 + else if (key === 57 || key === 105) zoom.scaleTo(svg, 9); // 9 to zoom to 9 + else if (key === 9) $("#updateFullscreen").click(); // Tab to fit map to fullscreen + else if (ctrl && key === 90) undo.click(); // Ctrl + "Z" to toggle undo + else if (ctrl && key === 89) redo.click(); // Ctrl + "Y" to toggle undo + }); + + // Show help + function showHelp() { + $("#help").dialog({ + title: "About Fantasy Map Generator", + minHeight: 30, width: "auto", maxWidth: 275, resizable: false, + position: {my: "center top+10", at: "bottom", of: this}, + close: unselect + }); + } + + // Toggle Options pane + $("#optionsTrigger").on("click", function() { + if (tooltip.getAttribute("data-main") === "Сlick the arrow button to open options") { + tooltip.setAttribute("data-main", ""); + tooltip.innerHTML = ""; + localStorage.setItem("disable_click_arrow_tooltip", true); + } + if ($("#options").css("display") === "none") { + $("#regenerate").hide(); + $("#options").fadeIn(); + $("#layoutTab").click(); + $("#optionsTrigger").removeClass("icon-right-open glow").addClass("icon-left-open"); + } else { + $("#options").fadeOut(); + $("#optionsTrigger").removeClass("icon-left-open").addClass("icon-right-open"); + } + }); + $("#collapsible").hover(function() { + if ($("#optionsTrigger").hasClass("glow")) return; + if ($("#options").css("display") === "none") { + $("#regenerate").show(); + $("#optionsTrigger").removeClass("glow"); + }}, function() { + $("#regenerate").hide(); + }); + + // move layers on mapLayers dragging (jquery sortable) + function moveLayer(event, ui) { + const el = getLayer(ui.item.attr("id")); + if (el) { + const prev = getLayer(ui.item.prev().attr("id")); + const next = getLayer(ui.item.next().attr("id")); + if (prev) {el.insertAfter(prev);} else if (next) {el.insertBefore(next);} + } + } + + // define connection between option layer buttons and actual svg groups + function getLayer(id) { + if (id === "toggleGrid") {return $("#grid");} + if (id === "toggleOverlay") {return $("#overlay");} + if (id === "toggleHeight") {return $("#terrs");} + if (id === "toggleCultures") {return $("#cults");} + if (id === "toggleRoutes") {return $("#routes");} + if (id === "toggleRivers") {return $("#rivers");} + if (id === "toggleCountries") {return $("#regions");} + if (id === "toggleBorders") {return $("#borders");} + if (id === "toggleRelief") {return $("#terrain");} + if (id === "toggleLabels") {return $("#labels");} + if (id === "toggleIcons") {return $("#icons");} + } + + // UI Button handlers + $("button, a, li, i").on("click", function() { + const id = this.id; + const parent = this.parentNode.id; + if (debug.selectAll(".tag").size()) {debug.selectAll(".tag, .line").remove();} + if (id === "toggleHeight") {toggleHeight();} + if (id === "toggleCountries") {$('#regions').fadeToggle();} + if (id === "toggleCultures") {toggleCultures();} + if (id === "toggleOverlay") {toggleOverlay();} + if (id === "toggleFlux") {toggleFlux();} + if (parent === "mapLayers" || parent === "styleContent") {$(this).toggleClass("buttonoff");} + if (id === "randomMap" || id === "regenerate") { + changeSeed(); + exitCustomization(); + undraw(); + resetZoom(1000); + generate(); + return; + } + if (id === "editCountries") editCountries(); + if (id === "editCultures") editCultures(); + if (id === "editScale" || id === "editScaleCountries" || id === "editScaleBurgs") editScale(); + if (id === "countriesManually") { + customization = 2; + tip("Click to select a country, drag the circle to re-assign", true); + mockRegions(); + let temp = regions.append("g").attr("id", "temp"); + $("#countriesBottom").children().hide(); + $("#countriesManuallyButtons").show(); + // highlight capital cells as it's not allowed to change capital's state that way + states.map(function(s) { + if (s.capital === "neutral" || s.capital === "select") return; + const capital = s.capital; + const index = manors[capital].cell; + temp.append("path") + .attr("data-cell", index).attr("data-state", s.i) + .attr("d", "M" + polygons[index].join("L") + "Z") + .attr("fill", s.color).attr("stroke", "red").attr("stroke-width", .7); + }); + viewbox.style("cursor", "crosshair").call(drag).on("click", changeSelectedOnClick); + } + if (id === "countriesRegenerate") { + customization = 3; + tip("Manually change \"Expansion\" value for a country or click on \"Randomize\" button", true); + mockRegions(); + regions.append("g").attr("id", "temp"); + $("#countriesBottom").children().hide(); + $("#countriesRegenerateButtons").show(); + $(".statePower, .icon-resize-full, .stateCells, .icon-check-empty").toggleClass("hidden"); + $("div[data-sortby='expansion'],div[data-sortby='cells']").toggleClass("hidden"); + } + if (id === "countriesManuallyComplete") { + debug.selectAll(".circle").remove(); + const changedCells = regions.select("#temp").selectAll("path"); + let changedStates = []; + changedCells.each(function() { + const el = d3.select(this); + const cell = +el.attr("data-cell"); + let stateOld = cells[cell].region; + if (stateOld === "neutral") {stateOld = states.length - 1;} + const stateNew = +el.attr("data-state"); + const region = states[stateNew].color === "neutral" ? "neutral" : stateNew; + cells[cell].region = region; + if (cells[cell].manor !== undefined) {manors[cells[cell].manor].region = region;} + changedStates.push(stateNew, stateOld); + }); + changedStates = [...new Set(changedStates)]; + changedStates.map(function(s) {recalculateStateData(s);}); + const last = states.length - 1; + if (states[last].capital === "neutral" && states[last].cells === 0) { + $("#state" + last).remove(); + states.splice(-1); + } + $("#countriesManuallyCancel").click(); + if (changedStates.length) {editCountries();} + } + if (id === "countriesManuallyCancel") { + redrawRegions(); + debug.selectAll(".circle").remove(); + if (grid.style("display") === "inline") {toggleGrid.click();} + if (labels.style("display") === "none") {toggleLabels.click();} + $("#countriesBottom").children().show(); + $("#countriesManuallyButtons, #countriesRegenerateButtons").hide(); + $(".selected").removeClass("selected"); + $("div[data-sortby='expansion'],.statePower, .icon-resize-full").addClass("hidden"); + $("div[data-sortby='cells'],.stateCells, .icon-check-empty").removeClass("hidden"); + customization = 0; + restoreDefaultEvents(); + } + if (id === "countriesApply") {$("#countriesManuallyCancel").click();} + if (id === "countriesRandomize") { + const mod = +powerInput.value * 2; + $(".statePower").each(function(e, i) { + const state = +(this.parentNode.id).slice(5); + if (states[state].capital === "neutral") return; + const power = rn(Math.random() * mod / 2 + 1, 1); + $(this).val(power); + $(this).parent().attr("data-expansion", power); + states[state].power = power; + }); + regenerateCountries(); + } + if (id === "countriesAddM" || id === "countriesAddR" || id === "countriesAddG") { + let i = states.length; + // move neutrals to the last line + if (states[i-1].capital === "neutral") {states[i-1].i = i; i -= 1;} + var name = generateStateName(0); + const color = colors20(i); + states.push({i, color, name, capital: "select", cells: 0, burgs: 0, urbanPopulation: 0, ruralPopulation: 0, area: 0, power: 1}); + states.sort(function(a, b){return a.i - b.i}); + editCountries(); + } + if (id === "countriesRegenerateNames") { + const editor = d3.select("#countriesBody"); + states.forEach(function(s) { + if (s.capital === "neutral") return; + s.name = generateStateName(s.i); + labels.select("#regionLabel"+s.i).text(s.name); + editor.select("#state"+s.i).select(".stateName").attr("value", s.name); + }); + } + if (id === "countriesPercentage") { + var el = $("#countriesEditor"); + if (el.attr("data-type") === "absolute") { + el.attr("data-type", "percentage"); + const totalCells = land.length; + const totalBurgs = +countriesFooterBurgs.innerHTML; + let totalArea = countriesFooterArea.innerHTML; + totalArea = getInteger(totalArea.split(" ")[0]); + const totalPopulation = getInteger(countriesFooterPopulation.innerHTML); + $("#countriesBody > .states").each(function() { + const cells = rn($(this).attr("data-cells") / totalCells * 100); + const burgs = rn($(this).attr("data-burgs") / totalBurgs * 100); + const area = rn($(this).attr("data-area") / totalArea * 100); + const population = rn($(this).attr("data-population") / totalPopulation * 100); + $(this).children().filter(".stateCells").text(cells + "%"); + $(this).children().filter(".stateBurgs").text(burgs + "%"); + $(this).children().filter(".stateArea").text(area + "%"); + $(this).children().filter(".statePopulation").val(population + "%"); + }); + } else { + el.attr("data-type", "absolute"); + editCountries(); + } + } + if (id === "countriesExport") { + if ($(".statePower").length === 0) {return;} + const unit = areaUnit.value === "square" ? distanceUnit.value + "2" : areaUnit.value; + let data = "Country,Capital,Cells,Burgs,Area (" + unit + "),Population\n"; // countries headers + $("#countriesBody > .states").each(function() { + const country = $(this).attr("data-country"); + if (country === "bottom") {data += "neutral,"} else {data += country + ",";} + const capital = $(this).attr("data-capital"); + if (capital === "bottom" || capital === "select") {data += ","} else {data += capital + ",";} + data += $(this).attr("data-cells") + ","; + data += $(this).attr("data-burgs") + ","; + data += $(this).attr("data-area") + ","; + const population = +$(this).attr("data-population"); + data += population + "\n"; + }); + data += "\nBurg,Country,Culture,Population\n"; // burgs headers + manors.map(function(m) { + if (m.region === "removed") return; // skip removed burgs + data += m.name + ","; + const country = m.region === "neutral" ? "neutral" : states[m.region].name; + data += country + ","; + data += cultures[m.culture].name + ","; + const population = m.population * urbanization.value * populationRate.value * 1000; + data += population + "\n"; + }); + const dataBlob = new Blob([data], {type: "text/plain"}); + const url = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + document.body.appendChild(link); + link.download = "countries_data" + Date.now() + ".csv"; + link.href = url; + link.click(); + window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000); + } + + if (id === "burgNamesImport") burgsListToLoad.click(); + + if (id === "removeCountries") { + alertMessage.innerHTML = `Are you sure you want remove all countries?`; + $("#alert").dialog({resizable: false, title: "Remove countries", + buttons: { + Cancel: function() {$(this).dialog("close");}, + Remove: function() { + $(this).dialog("close"); + $("#countriesBody").empty(); + manors.map(function(m) {m.region = "neutral";}); + land.map(function(l) {l.region = "neutral";}); + states.map(function(s) { + const c = +s.capital; + if (isNaN(c)) return; + moveBurgToGroup(c, "towns"); + }); + removeAllLabelsInGroup("countries"); + regions.selectAll("path").remove(); + states = []; + states.push({i: 0, color: "neutral", capital: "neutral", name: "Neutrals"}); + recalculateStateData(0); + if ($("#burgsEditor").is(":visible")) {$("#burgsEditor").dialog("close");} + editCountries(); + } + } + }) + } + if (id === "removeBurgs") { + alertMessage.innerHTML = `Are you sure you want to remove all burgs associated with the country?`; + $("#alert").dialog({resizable: false, title: "Remove associated burgs", + buttons: { + Cancel: function() {$(this).dialog("close");}, + Remove: function() { + $(this).dialog("close"); + const state = +$("#burgsEditor").attr("data-state"); + const region = states[state].capital === "neutral" ? "neutral" : state; + $("#burgsBody").empty(); + manors.map(function(m) { + if (m.region !== region) {return;} + m.region = "removed"; + cells[m.cell].manor = undefined; + labels.select("[data-id='" + m.i + "']").remove(); + icons.selectAll("[data-id='" + m.i + "']").remove(); + }); + states[state].urbanPopulation = 0; + states[state].burgs = 0; + states[state].capital = "select"; + if ($("#countriesEditor").is(":visible")) { + editCountries(); + $("#burgsEditor").dialog("moveToTop"); + } + burgsFooterBurgs.innerHTML = 0; + burgsFooterPopulation.value = 0; + } + } + }); + } + if (id === "changeCapital") { + if ($(this).hasClass("pressed")) { + $(this).removeClass("pressed") + } else { + $(".pressed").removeClass("pressed"); + $(this).addClass("pressed"); + } + } + if (id === "regenerateBurgNames") { + var s = +$("#burgsEditor").attr("data-state"); + $(".burgName").each(function(e, i) { + const b = +(this.parentNode.id).slice(5); + const name = generateName(manors[b].culture); + $(this).val(name); + $(this).parent().attr("data-burg", name); + manors[b].name = name; + labels.select("[data-id='" + b + "']").text(name); + }); + if ($("#countriesEditor").is(":visible")) { + if (states[s].capital === "neutral") {return;} + var c = states[s].capital; + $("#state"+s).attr("data-capital", manors[c].name); + $("#state"+s+" > .stateCapital").val(manors[c].name); + } + } + if (id === "burgAdd") { + var state = +$("#burgsEditor").attr("data-state"); + clickToAdd(); // to load on click event function + $("#addBurg").click().attr("data-state", state); + } + if (id === "toggleScaleBar") {$("#scaleBar").toggleClass("hidden");} + if (id === "addRuler") { + $("#ruler").show(); + const rulerNew = ruler.append("g").attr("class", "linear").call(d3.drag().on("start", elementDrag)); + const factor = rn(1 / Math.pow(scale, 0.3), 1); + const y = Math.floor(Math.random() * graphHeight * 0.5 + graphHeight * 0.25); + const x1 = graphWidth * 0.2, x2 = graphWidth * 0.8; + const dash = rn(30 / distanceScale.value, 2); + rulerNew.append("line").attr("x1", x1).attr("y1", y).attr("x2", x2).attr("y2", y).attr("class", "white").attr("stroke-width", factor); + rulerNew.append("line").attr("x1", x1).attr("y1", y).attr("x2", x2).attr("y2", y).attr("class", "gray").attr("stroke-width", factor).attr("stroke-dasharray", dash); + rulerNew.append("circle").attr("r", 2 * factor).attr("stroke-width", 0.5 * factor).attr("cx", x1).attr("cy", y).attr("data-edge", "left").call(d3.drag().on("drag", rulerEdgeDrag)); + rulerNew.append("circle").attr("r", 2 * factor).attr("stroke-width", 0.5 * factor).attr("cx", x2).attr("cy", y).attr("data-edge", "rigth").call(d3.drag().on("drag", rulerEdgeDrag)); + rulerNew.append("circle").attr("r", 1.2 * factor).attr("stroke-width", 0.3 * factor).attr("cx", graphWidth / 2).attr("cy", y).attr("class", "center").call(d3.drag().on("start", rulerCenterDrag)); + const dist = rn(x2 - x1); + const label = rn(dist * distanceScale.value) + " " + distanceUnit.value; + rulerNew.append("text").attr("x", graphWidth / 2).attr("y", y).attr("dy", -1).attr("data-dist", dist).text(label).text(label).on("click", removeParent).attr("font-size", 10 * factor); + return; + } + if (id === "addOpisometer" || id === "addPlanimeter") { + if ($(this).hasClass("pressed")) { + restoreDefaultEvents(); + $(this).removeClass("pressed"); + } else { + $(this).addClass("pressed"); + viewbox.style("cursor", "crosshair").call(drag); + } + return; + } + if (id === "removeAllRulers") { + if ($("#ruler > g").length < 1) {return;} + alertMessage.innerHTML = `Are you sure you want to remove all placed rulers?`; + $("#alert").dialog({resizable: false, title: "Remove all rulers", + buttons: { + Remove: function() { + $(this).dialog("close"); + $("#ruler > g").remove(); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + return; + } + if (id === "editHeightmap") {$("#customizeHeightmap").slideToggle();} + if (id === "fromScratch") { + alertMessage.innerHTML = "Are you sure you want to clear the map? All progress will be lost"; + $("#alert").dialog({resizable: false, title: "Clear map", + buttons: { + Clear: function() { + closeDialogs(); + undraw(); + placePoints(); + calculateVoronoi(points); + detectNeighbors("grid"); + drawScaleBar(); + customizeHeightmap(); + openBrushesPanel(); + $(this).dialog("close"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + } + if (id === "fromHeightmap") { + const message = `Hightmap is a basic element on which secondary data (rivers, burgs, countries etc) is based. + If you want to significantly change the hightmap, it may be better to clean up all the secondary data + and let the system to re-generate it based on the updated hightmap. In case of minor changes, you can keep the data. + Newly added lands will be considered as neutral. Burgs located on a removed land cells will be deleted. + Rivers and small lakes will be re-gerenated based on updated heightmap. Routes won't be regenerated.`; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Edit Heightmap", + buttons: { + "Clean up": function() { + editHeightmap("clean"); + $(this).dialog("close"); + }, + Keep: function() { + $(this).dialog("close"); + editHeightmap("keep"); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + return; + } + // heightmap customization buttons + if (customization === 1) { + if (id === "paintBrushes") {openBrushesPanel();} + if (id === "rescaleExecute") { + const subject = rescaleLower.value + "-" + rescaleHigher.value; + const sign = conditionSign.value; + let modifier = rescaleModifier.value; + if (sign === "×") {modifyHeights(subject, 0, +modifier);} + if (sign === "÷") {modifyHeights(subject, 0, (1 / modifier));} + if (sign === "+") {modifyHeights(subject, +modifier, 1);} + if (sign === "-") {modifyHeights(subject, (-1 * modifier), 1);} + if (sign === "^") {modifyHeights(subject, 0, "^" + modifier);} + updateHeightmap(); + updateHistory(); + } + if (id === "rescaleButton") { + $("#modifyButtons").children().not("#rescaleButton, .condition").toggle(); + } + if (id === "rescaleCondButton") {$("#modifyButtons").children().not("#rescaleCondButton, #rescaler").toggle();} + if (id === "undo" || id === "templateUndo") {restoreHistory(historyStage - 1);} + if (id === "redo" || id === "templateRedo") {restoreHistory(historyStage + 1);} + if (id === "smoothHeights") { + smoothHeights(4); + updateHeightmap(); + updateHistory(); + } + if (id === "disruptHeights") { + disruptHeights(); + updateHeightmap(); + updateHistory(); + } + if (id === "getMap") getMap(); + if (id === "applyTemplate") { + if ($("#templateEditor").is(":visible")) {return;} + $("#templateEditor").dialog({ + title: "Template Editor", + minHeight: "auto", width: "auto", resizable: false, + position: {my: "right top", at: "right-10 top+10", of: "svg"} + }); + } + if (id === "convertImage") {convertImage();} + if (id === "convertImageGrid") {$("#grid").fadeToggle();} + if (id === "convertImageHeights") {$("#landmass").fadeToggle();} + if (id === "perspectiveView") { + if ($("#perspectivePanel").is(":visible")) return; + $("#perspectivePanel").dialog({ + title: "Perspective View", + width: 520, height: 190, + position: {my: "center center", at: "center center", of: "svg"} + }); + drawPerspective(); + return; + } + } + if (id === "restoreStyle") { + alertMessage.innerHTML = "Are you sure you want to restore default style?"; + $("#alert").dialog({resizable: false, title: "Restore style", + buttons: { + Restore: function() { + applyDefaultStyle(); + $(this).dialog("close"); + }, + Cancel: function() { + $(this).dialog("close"); + } + } + }); + } + if (parent === "mapFilters") { + $("svg").attr("filter", ""); + if ($(this).hasClass('pressed')) { + $("#mapFilters .pressed").removeClass('pressed'); + } else { + $("#mapFilters .pressed").removeClass('pressed'); + $(this).addClass('pressed'); + $("svg").attr("filter", "url(#filter-" + id + ")"); + } + return; + } + if (id === "updateFullscreen") { + mapWidthInput.value = window.innerWidth; + mapHeightInput.value = window.innerHeight; + localStorage.removeItem("mapHeight"); + localStorage.removeItem("mapWidth"); + changeMapSize(); + } + if (id === "zoomExtentDefault") { + zoomExtentMin.value = 1; + zoomExtentMax.value = 20; + zoom.scaleExtent([1, 20]).scaleTo(svg, 1); + } + if (id === "saveButton") {$("#saveDropdown").slideToggle();} + if (id === "loadMap") {mapToLoad.click();} + if (id === "zoomReset") {resetZoom(1000);} + if (id === "zoomPlus") { + scale += 1; + if (scale > 40) {scale = 40;} + invokeActiveZooming(); + } + if (id === "zoomMinus") { + scale -= 1; + if (scale <= 1) {scale = 1; viewX = 0; viewY = 0;} + invokeActiveZooming(); + } + if (id === "styleFontPlus" || id === "styleFontMinus") { + var el = viewbox.select("#"+styleElementSelect.value); + var mod = id === "styleFontPlus" ? 1.1 : 0.9; + el.selectAll("g").each(function() { + const el = d3.select(this); + let size = rn(el.attr("data-size") * mod, 2); + if (size < 2) {size = 2;} + el.attr("data-size", size).attr("font-size", rn((size + (size / scale)) / 2, 2)); + }); + invokeActiveZooming(); + return; + } + if (id === "brushClear") { + if (customization === 1) { + var message = "Are you sure you want to clear the map?"; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Clear map", + buttons: { + Clear: function() { + $(this).dialog("close"); + viewbox.style("cursor", "crosshair").call(drag); + landmassCounter.innerHTML = "0"; + $("#landmass").empty(); + heights = new Uint8Array(heights.length); + // clear history + history = []; + historyStage = 0; + updateHistory(); + redo.disabled = templateRedo.disabled = true; + undo.disabled = templateUndo.disabled = true; + }, + Cancel: function() {$(this).dialog("close");} + } + }); + } else { + start.click(); + } + } + if (id === "templateComplete") getMap(); + if (id === "convertColorsMinus") { + var current = +convertColors.value - 1; + if (current < 4) {current = 3;} + convertColors.value = current; + heightsFromImage(current); + } + if (id === "convertColorsPlus") { + var current = +convertColors.value + 1; + if (current > 255) {current = 256;} + convertColors.value = current; + heightsFromImage(current); + } + if (id === "convertOverlayButton") { + $("#convertImageButtons").children().not(this).not("#convertColors").toggle(); + } + if (id === "convertAutoLum") {autoAssing("lum");} + if (id === "convertAutoHue") {autoAssing("hue");} + if (id === "convertComplete") {completeConvertion();} + }); + + // support save options + $("#saveDropdown > div").click(function() { + const id = this.id; + let dns_allow_popup_message = localStorage.getItem("dns_allow_popup_message"); + if (!dns_allow_popup_message) { + localStorage.clear(); + let message = "Generator uses pop-up window to download files. "; + message += "Please ensure your browser does not block popups. "; + message += "Please check browser settings and turn off adBlocker if it is enabled"; + alertMessage.innerHTML = message; + $("#alert").dialog({title: "File saver. Please enable popups!", + buttons: { + "Don't show again": function() { + localStorage.setItem("dns_allow_popup_message", true); + $(this).dialog("close"); + }, + Close: function() {$(this).dialog("close");} + }, + position: {my: "center", at: "center", of: "svg"} + }); + } + if (id === "saveMap") {saveMap();} + if (id === "saveSVG") {saveAsImage("svg");} + if (id === "savePNG") {saveAsImage("png");} + $("#saveDropdown").slideUp("fast"); + }); + + // lock / unlock option randomization + $("#options i[class^='icon-lock']").click(function() { + $(this).toggleClass("icon-lock icon-lock-open"); + const locked = +$(this).hasClass("icon-lock"); + $(this).attr("data-locked", locked); + const option = (this.id).slice(4, -5).toLowerCase(); + const value = $("#"+option+"Input").val(); + if (locked) {localStorage.setItem(option, value);} + else {localStorage.removeItem(option);} + }); + + function editHeightmap(type) { + closeDialogs(); + const regionData = [],cultureData = []; + if (type !== "clean") { + for (let i = 0; i < points.length; i++) { + let cell = diagram.find(points[i][0],points[i][1]).index; + // if closest cell is a small lake, try to find a land neighbor + if (cells[cell].lake === 2) cells[cell].neighbors.forEach(function(n) { + if (cells[n].height >= 20) {cell = n; } + }); + let region = cells[cell].region; + if (region === undefined) region = -1; + regionData.push(region); + let culture = cells[cell].culture; + if (culture === undefined) culture = -1; + cultureData.push(culture); + } + } else {undraw();} + calculateVoronoi(points); + detectNeighbors("grid"); + drawScaleBar(); + if (type === "keep") { + svg.selectAll("#lakes, #coastline, #terrain, #rivers, #grid, #terrs, #landmass, #ocean, #regions") + .selectAll("path, circle, line").remove(); + svg.select("#shape").remove(); + for (let i = 0; i < points.length; i++) { + if (regionData[i] !== -1) cells[i].region = regionData[i]; + if (cultureData[i] !== -1) cells[i].culture = cultureData[i]; + } + } + mockHeightmap(); + customizeHeightmap(); + openBrushesPanel(); + } + + function openBrushesPanel() { + if ($("#brushesPanel").is(":visible")) {return;} + $("#brushesPanel").dialog({ + title: "Paint Brushes", + minHeight: 40, width: "auto", maxWidth: 200, resizable: false, + position: {my: "right top", at: "right-10 top+10", of: "svg"} + }).on('dialogclose', function() { + restoreDefaultEvents(); + $("#brushesButtons > .pressed").removeClass('pressed'); + }); + + if (modules.openBrushesPanel) return; + modules.openBrushesPanel = true; + + $("#brushesButtons > button").on("click", function() { + const rSlider = $("#brushRadiusLabel, #brushRadius"); + debug.selectAll(".circle, .tag, .line").remove(); + if ($(this).hasClass('pressed')) { + $(this).removeClass('pressed'); + restoreDefaultEvents(); + rSlider.attr("disabled", true).addClass("disabled"); + } else { + $("#brushesButtons > .pressed").removeClass('pressed'); + $(this).addClass('pressed'); + viewbox.style("cursor", "crosshair"); + const id = this.id; + if (id === "brushRange" || id === "brushTrough") {viewbox.on("click", placeLinearFeature);} // on click brushes + else {viewbox.call(drag).on("click", null);} // on drag brushes + if ($(this).hasClass("feature")) {rSlider.attr("disabled", true).addClass("disabled");} + else {rSlider.attr("disabled", false).removeClass("disabled");} + } + }); + } + + function drawPerspective() { + console.time("drawPerspective"); + const width = 320, height = 180; + const wRatio = graphWidth / width, hRatio = graphHeight / height; + const lineCount = 320, lineGranularity = 90; + const perspective = document.getElementById("perspective"); + const pContext = perspective.getContext("2d"); + const lines = []; + let i = lineCount; + while (i--) { + const x = i / lineCount * width | 0; + const canvasPoints = []; + lines.push(canvasPoints); + let j = Math.floor(lineGranularity); + while (j--) { + const y = j / lineGranularity * height | 0; + let index = getCellIndex(x * wRatio, y * hRatio); + let h = heights[index] - 20; + if (h < 1) h = 0; + canvasPoints.push([x, y, h]); + } + } + pContext.clearRect(0, 0, perspective.width, perspective.height); + for (let canvasPoints of lines) { + for (let i = 0; i < canvasPoints.length - 1; i++) { + const pt1 = canvasPoints[i]; + const pt2 = canvasPoints[i + 1]; + const avHeight = (pt1[2] + pt2[2]) / 200; + pContext.beginPath(); + pContext.moveTo(...transformPt(pt1)); + pContext.lineTo(...transformPt(pt2)); + let clr = "rgb(81, 103, 169)"; // water + if (avHeight !== 0) {clr = color(1 - avHeight - 0.2);} + pContext.strokeStyle = clr; + pContext.stroke(); + } + for (let i = 0; i < canvasPoints.length - 1; i++) { + + } + } + console.timeEnd("drawPerspective"); + } + + // get square grid cell index based on coords + function getCellIndex(x, y) { + const index = diagram.find(x, y).index; + // let cellsX = Math.round(graphWidth / spacing); + // let index = Math.ceil(y / spacing) * cellsX + Math.round(x / spacing); + return index; + } + + function transformPt(pt) { + const width = 320, maxHeight = 0.2; + var [x, y] = projectIsometric(pt[0],pt[1]); + return [x + width / 2 + 10, y + 10 - pt[2] * maxHeight]; + } + + function projectIsometric(x, y) { + const scale = 1, yProj = 4; + return [(x - y) * scale, (x + y) / yProj * scale]; + } + + // templateEditor Button handlers + $("#templateTools > button").on("click", function() { + let id = this.id; + id = id.replace("template", ""); + if (id === "Mountain") { + const steps = $("#templateBody > div").length; + if (steps > 0) return; + } + $("#templateBody").attr("data-changed", 1); + $("#templateBody").append('
    ' + id + '
    '); + const el = $("#templateBody div:last-child"); + if (id === "Hill" || id === "Pit" || id === "Range" || id === "Trough") { + var count = ''; + } + if (id === "Hill") { + var dist = ''; + } + if (id === "Add" || id === "Multiply") { + var dist = ''; + } + if (id === "Add") { + var count = ''; + } + if (id === "Multiply") { + var count = ''; + } + if (id === "Smooth") { + var count = ''; + } + if (id === "Strait") { + var count = ''; + } + el.append(''); + $("#templateBody .icon-trash-empty").on("click", function() {$(this).parent().remove();}); + if (dist) el.append(dist); + if (count) el.append(count); + el.find("select.templateElDist").on("input", fireTemplateElDist); + $("#templateBody").attr("data-changed", 1); + }); + + // fireTemplateElDist selector handlers + function fireTemplateElDist() { + if (this.value === "interval") { + const interval = prompt("Populate a height interval (e.g. from 17 to 20), without space, but with hyphen", "17-20"); + if (interval) { + const option = ''; + $(this).append(option).val(interval); + } + } + } + + // templateSelect on change listener + $("#templateSelect").on("input", function() { + const steps = $("#templateBody > div").length; + const changed = +$("#templateBody").attr("data-changed"); + const template = this.value; + if (steps && changed === 1) { + alertMessage.innerHTML = "Are you sure you want to change the base template? All the changes will be lost."; + $("#alert").dialog({resizable: false, title: "Change Template", + buttons: { + Change: function() { + changeTemplate(template); + $(this).dialog("close"); + }, + Cancel: function() { + const prev = $("#templateSelect").attr("data-prev"); + $("#templateSelect").val(prev); + $(this).dialog("close"); + } + } + }); + } + if (steps === 0 || changed === 0) changeTemplate(template); + }); + + function changeTemplate(template) { + $("#templateBody").empty(); + $("#templateSelect").attr("data-prev", template); + if (template === "templateVolcano") { + addStep("Mountain"); + addStep("Add", 10); + addStep("Hill", 5, 0.35); + addStep("Range", 3); + addStep("Trough", -4); + } + if (template === "templateHighIsland") { + addStep("Mountain"); + addStep("Add", 10); + addStep("Range", 6); + addStep("Hill", 12, 0.25); + addStep("Trough", 3); + addStep("Multiply", 0.75, "land"); + addStep("Pit", 1); + addStep("Hill", 3, 0.15); + } + if (template === "templateLowIsland") { + addStep("Mountain"); + addStep("Add", 10); + addStep("Smooth", 2); + addStep("Range", 2); + addStep("Hill", 4, 0.4); + addStep("Hill", 12, 0.2); + addStep("Trough", 8); + addStep("Multiply", 0.35, "land"); + } + if (template === "templateContinents") { + addStep("Mountain"); + addStep("Add", 10); + addStep("Hill", 30, 0.25); + addStep("Strait", "4-7"); + addStep("Pit", 10); + addStep("Trough", 10); + addStep("Multiply", 0.6, "land"); + addStep("Smooth", 2); + addStep("Range", 3); + } + if (template === "templateArchipelago") { + addStep("Mountain"); + addStep("Add", 10); + addStep("Hill", 12, 0.15); + addStep("Range", 8); + addStep("Strait", "2-3"); + addStep("Trough", 15); + addStep("Pit", 10); + addStep("Add", -5, "land"); + addStep("Multiply", 0.7, "land"); + addStep("Smooth", 3); + } + + if (template === "templateAtoll") { + addStep("Mountain"); + addStep("Add", 10, "all"); + addStep("Hill", 2, 0.35); + addStep("Range", 2); + addStep("Smooth", 1); + addStep("Multiply", 0.1, "27-100"); + } + if (template === "templateMainland") { + addStep("Mountain"); + addStep("Add", 10, "all"); + addStep("Hill", 30, 0.2); + addStep("Range", 10); + addStep("Pit", 20); + addStep("Hill", 10, 0.15); + addStep("Trough", 10); + addStep("Multiply", 0.4, "land"); + addStep("Range", 10); + addStep("Smooth", 3); + } + if (template === "templatePeninsulas") { + addStep("Add", 15); + addStep("Hill", 30, 0); + addStep("Range", 5); + addStep("Pit", 15); + addStep("Strait", "15-20"); + } + $("#templateBody").attr("data-changed", 0); + } + + // interprete template function + function addStep(feature, count, dist) { + if (!feature) return; + if (feature === "Mountain") templateMountain.click(); + if (feature === "Hill") templateHill.click(); + if (feature === "Pit") templatePit.click(); + if (feature === "Range") templateRange.click(); + if (feature === "Trough") templateTrough.click(); + if (feature === "Strait") templateStrait.click(); + if (feature === "Add") templateAdd.click(); + if (feature === "Multiply") templateMultiply.click(); + if (feature === "Smooth") templateSmooth.click(); + if (count) {$("#templateBody div:last-child .templateElCount").val(count);} + if (dist !== undefined) { + if (dist !== "land") { + const option = ''; + $("#templateBody div:last-child .templateElDist").append(option); + } + $("#templateBody div:last-child .templateElDist").val(dist); + } + } + + // Execute custom template + $("#templateRun").on("click", function() { + if (customization !== 1) return; + let steps = $("#templateBody > div").length; + if (!steps) return; + heights = new Uint8Array(heights.length); // clean all heights + for (let step=1; step <= steps; step++) { + const type = $("#templateBody div:nth-child(" + step + ")").attr("data-type"); + if (type === "Mountain") {addMountain(); continue;} + let count = $("#templateBody div:nth-child(" + step + ") .templateElCount").val(); + const dist = $("#templateBody div:nth-child(" + step + ") .templateElDist").val(); + if (count) { + if (count[0] !== "-" && count.includes("-")) { + const lim = count.split("-"); + count = Math.floor(Math.random() * (+lim[1] - +lim[0] + 1) + +lim[0]); + } else { + count = +count; // parse string + } + } + if (type === "Hill") {addHill(count, +dist);} + if (type === "Pit") {addPit(count);} + if (type === "Range") {addRange(count);} + if (type === "Trough") {addRange(-1 * count);} + if (type === "Strait") {addStrait(count);} + if (type === "Add") {modifyHeights(dist, count, 1);} + if (type === "Multiply") {modifyHeights(dist, 0, count);} + if (type === "Smooth") {smoothHeights(count);} + } + mockHeightmap(); + updateHistory(); + }); + + // Save custom template as text file + $("#templateSave").on("click", function() { + const steps = $("#templateBody > div").length; + let stepsData = ""; + for (let step=1; step <= steps; step++) { + const element = $("#templateBody div:nth-child(" + step + ")"); + const type = element.attr("data-type"); + let count = $("#templateBody div:nth-child(" + step + ") .templateElCount").val(); + let dist = $("#templateBody div:nth-child(" + step + ") .templateElDist").val(); + if (!count) {count = "0";} + if (!dist) {dist = "0";} + stepsData += type + " " + count + " " + dist + "\r\n"; + } + const dataBlob = new Blob([stepsData], {type: "text/plain"}); + const url = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + link.download = "template_" + Date.now() + ".txt"; + link.href = url; + link.click(); + $("#templateBody").attr("data-changed", 0); + }); + + // Load custom template as text file + $("#templateLoad").on("click", function() {templateToLoad.click();}); + $("#templateToLoad").change(function() { + const fileToLoad = this.files[0]; + this.value = ""; + const fileReader = new FileReader(); + fileReader.onload = function(fileLoadedEvent) { + const dataLoaded = fileLoadedEvent.target.result; + const data = dataLoaded.split("\r\n"); + $("#templateBody").empty(); + if (data.length > 0) { + $("#templateBody").attr("data-changed", 1); + $("#templateSelect").attr("data-prev", "templateCustom").val("templateCustom"); + } + for (let i=0; i < data.length; i++) { + const line = data[i].split(" "); + addStep(line[0],line[1],line[2]); + } + }; + fileReader.readAsText(fileToLoad, "UTF-8"); + }); + + // Image to Heightmap Converter dialog + function convertImage() { + canvas.width = svgWidth; + canvas.height = svgHeight; + // turn off paint brushes drag and cursor + $(".pressed").removeClass('pressed'); + restoreDefaultEvents(); + const div = d3.select("#colorScheme"); + if (div.selectAll("*").size() === 0) { + for (let i = 0; i <= 100; i++) { + let width = i < 20 || i > 70 ? "1px" : "3px"; + if (i === 0) width = "4px"; + const clr = color(1 - i / 100); + const style = "background-color: " + clr + "; width: " + width; + div.append("div").attr("data-color", i).attr("style", style); + } + div.selectAll("*").on("touchmove mousemove", showHeight).on("click", assignHeight); + } + if ($("#imageConverter").is(":visible")) {return;} + $("#imageConverter").dialog({ + title: "Image to Heightmap Converter", + minHeight: 30, width: 260, resizable: false, + position: {my: "right top", at: "right-10 top+10", of: "svg"}}) + .on('dialogclose', function() {completeConvertion();}); + } + + // Load image to convert + $("#convertImageLoad").on("click", function() {imageToLoad.click();}); + $("#imageToLoad").change(function() { + console.time("loadImage"); + // set style + resetZoom(); + grid.attr("stroke-width", .2); + // load image + const file = this.files[0]; + this.value = ""; // reset input value to get triggered if the same file is uploaded + const reader = new FileReader(); + const img = new Image; + // draw image + img.onload = function() { + ctx.drawImage(img, 0, 0, svgWidth, svgHeight); + heightsFromImage(+convertColors.value); + console.timeEnd("loadImage"); + }; + reader.onloadend = function() {img.src = reader.result;}; + reader.readAsDataURL(file); + }); + + function heightsFromImage(count) { + const imageData = ctx.getImageData(0, 0, svgWidth, svgHeight); + const data = imageData.data; + $("#landmass > path, .color-div").remove(); + $("#landmass, #colorsUnassigned").fadeIn(); + $("#colorsAssigned").fadeOut(); + const colors = [], palette = []; + points.map(function(i) { + let x = rn(i[0]), y = rn(i[1]); + if (y == svgHeight) {y--;} + if (x == svgWidth) {x--;} + const p = (x + y * svgWidth) * 4; + const r = data[p], g = data[p + 1], b = data[p + 2]; + colors.push([r, g, b]); + }); + const cmap = MMCQ.quantize(colors, count); + heights = new Uint8Array(points.length); + polygons.map(function(i, d) { + const nearest = cmap.nearest(colors[d]); + const rgb = "rgb(" + nearest[0] + ", " + nearest[1] + ", " + nearest[2] + ")"; + const hex = toHEX(rgb); + if (palette.indexOf(hex) === -1) {palette.push(hex);} + landmass.append("path") + .attr("d", "M" + i.join("L") + "Z").attr("data-i", d) + .attr("fill", hex).attr("stroke", hex); + }); + landmass.selectAll("path").on("click", landmassClicked); + palette.sort(function(a, b) {return d3.lab(a).b - d3.lab(b).b;}).map(function(i) { + $("#colorsUnassigned").append('
    '); + }); + $(".color-div").click(selectColor); + } + + function landmassClicked() { + const color = d3.select(this).attr("fill"); + $("#"+color.slice(1)).click(); + } + + function selectColor() { + landmass.selectAll(".selectedCell").classed("selectedCell", 0); + const el = d3.select(this); + if (el.classed("selectedColor")) { + el.classed("selectedColor", 0); + } else { + $(".selectedColor").removeClass("selectedColor"); + el.classed("selectedColor", 1); + $("#colorScheme .hoveredColor").removeClass("hoveredColor"); + $("#colorsSelectValue").text(0); + if (el.attr("data-height")) { + const height = el.attr("data-height"); + $("#colorScheme div[data-color='" + height + "']").addClass("hoveredColor"); + $("#colorsSelectValue").text(height); + } + const color = "#" + d3.select(this).attr("id"); + landmass.selectAll("path").classed("selectedCell", 0); + landmass.selectAll("path[fill='" + color + "']").classed("selectedCell", 1); + } + } + + function showHeight() { + let el = d3.select(this); + let height = el.attr("data-color"); + $("#colorsSelectValue").text(height); + $("#colorScheme .hoveredColor").removeClass("hoveredColor"); + el.classed("hoveredColor", 1); + } + + function assignHeight() { + const sel = $(".selectedColor")[0]; + const height = +d3.select(this).attr("data-color"); + const rgb = color(1 - height / 100); + const hex = toHEX(rgb); + sel.style.backgroundColor = rgb; + sel.setAttribute("data-height", height); + const cur = "#" + sel.id; + sel.id = hex.substr(1); + landmass.selectAll(".selectedCell").each(function() { + d3.select(this).attr("fill", hex).attr("stroke", hex); + let i = +d3.select(this).attr("data-i"); + heights[i] = height; + }); + const parent = sel.parentNode; + if (parent.id === "colorsUnassigned") { + colorsAssigned.appendChild(sel); + $("#colorsAssigned").fadeIn(); + if ($("#colorsUnassigned .color-div").length < 1) {$("#colorsUnassigned").fadeOut();} + } + if ($("#colorsAssigned .color-div").length > 1) {sortAssignedColors();} + } + + // sort colors based on assigned height + function sortAssignedColors() { + const data = []; + const colors = d3.select("#colorsAssigned").selectAll(".color-div"); + colors.each(function(d) { + const id = d3.select(this).attr("id"); + const height = +d3.select(this).attr("data-height"); + data.push({id, height}); + }); + data.sort(function(a, b) {return a.height - b.height}).map(function(i) { + $("#colorsAssigned").append($("#"+i.id)); + }); + } + + // auto assign color based on luminosity or hue + function autoAssing(type) { + const imageData = ctx.getImageData(0, 0, svgWidth, svgHeight); + const data = imageData.data; + $("#landmass > path, .color-div").remove(); + $("#colorsAssigned").fadeIn(); + $("#colorsUnassigned").fadeOut(); + polygons.forEach(function(i, d) { + let x = rn(i.data[0]), y = rn(i.data[1]); + if (y == svgHeight) y--; + if (x == svgWidth) x--; + const p = (x + y * svgWidth) * 4; + const r = data[p], g = data[p + 1], b = data[p + 2]; + const lab = d3.lab("rgb(" + r + ", " + g + ", " + b + ")"); + if (type === "hue") { + var normalized = rn(normalize(lab.b + lab.a / 2, -50, 200), 2); + } else { + var normalized = rn(normalize(lab.l, 0, 100), 2); + } + const rgb = color(1 - normalized); + const hex = toHEX(rgb); + heights[d] = normalized * 100; + landmass.append("path").attr("d", "M" + i.join("L") + "Z").attr("data-i", d).attr("fill", hex).attr("stroke", hex); + }); + let unique = [...new Set(heights)].sort(); + unique.forEach(function(h) { + const rgb = color(1 - h / 100); + const hex = toHEX(rgb); + $("#colorsAssigned").append('
    '); + }); + $(".color-div").click(selectColor); + } + + function normalize(val, min, max) { + let normalized = (val - min) / (max - min); + if (normalized < 0) {normalized = 0;} + if (normalized > 1) {normalized = 1;} + return normalized; + } + + function completeConvertion() { + mockHeightmap(); + restartHistory(); + $(".color-div").remove(); + $("#colorsAssigned, #colorsUnassigned").fadeOut(); + grid.attr("stroke-width", .1); + canvas.style.opacity = convertOverlay.value = convertOverlayValue.innerHTML = 0; + // turn on paint brushes drag and cursor + viewbox.style("cursor", "crosshair").call(drag); + $("#imageConverter").dialog('close'); + } + + // Clear the map + function undraw() { + viewbox.selectAll("path, circle, line, text, use, #ruler > g").remove(); + defs.selectAll("*").remove(); + landmass.select("rect").remove(); + cells = [],land = [],riversData = [],manors = [],states = [],features = [],queue = []; + } + + // Enter Heightmap Customization mode + function customizeHeightmap() { + customization = 1; + tip('Heightmap customization mode is active. Click on "Complete" to finalize the Heightmap', true); + $("#getMap").removeClass("buttonoff").addClass("glow"); + resetZoom(); + landmassCounter.innerHTML = "0"; + $('#grid').fadeIn(); + $('#toggleGrid').removeClass("buttonoff"); + restartHistory(); + $("#customizationMenu").slideDown(); + $("#openEditor").slideUp(); + } + + // Remove all customization related styles, reset values + function exitCustomization() { + customization = 0; + tip("", true); + canvas.style.opacity = 0; + $("#customizationMenu").slideUp(); + $("#getMap").addClass("buttonoff").removeClass("glow"); + $("#landmass").empty(); + $('#grid').empty().fadeOut(); + $('#toggleGrid').addClass("buttonoff"); + restoreDefaultEvents(); + if (!$("#toggleHeight").hasClass("buttonoff")) {toggleHeight();} + closeDialogs(); + history = []; + historyStage = 0; + $("#customizeHeightmap").slideUp(); + $("#openEditor").slideDown(); + debug.selectAll(".circle, .tag, .line").remove(); + } + + // open editCountries dialog + function editCountries() { + if (cults.selectAll("path").size()) $("#toggleCultures").click(); + if (regions.style("display") === "none") $("#toggleCountries").click(); + layoutPreset.value = "layoutPolitical"; + $("#countriesBody").empty(); + $("#countriesHeader").children().removeClass("icon-sort-name-up icon-sort-name-down icon-sort-number-up icon-sort-number-down"); + let totalArea = 0, totalBurgs = 0, unit, areaConv; + if (areaUnit.value === "square") {unit = " " + distanceUnit.value + "²";} else {unit = " " + areaUnit.value;} + let totalPopulation = 0; + for (let s = 0; s < states.length; s++) { + $("#countriesBody").append('
    '); + const el = $("#countriesBody div:last-child"); + const burgsCount = states[s].burgs; + totalBurgs += burgsCount; + // calculate user-friendly area and population + const area = rn(states[s].area * Math.pow(distanceScale.value, 2)); + totalArea += area; + areaConv = si(area) + unit; + const urban = rn(states[s].urbanPopulation * urbanization.value * populationRate.value); + const rural = rn(states[s].ruralPopulation * populationRate.value); + var population = (urban + rural) * 1000; + totalPopulation += population; + const populationConv = si(population); + const title = '\'Total population: ' + populationConv + '; Rural population: ' + rural + 'K; Urban population: ' + urban + 'K\''; + let neutral = states[s].color === "neutral" || states[s].capital === "neutral"; + // append elements to countriesBody + if (!neutral) { + el.append(''); + el.append(''); + var capital = states[s].capital !== "select" ? manors[states[s].capital].name : "select"; + if (capital === "select") { + el.append(''); + } else { + el.append(''); + el.append(''); + } + el.append(''); + el.append(''); + } else { + el.append(''); + el.append(''); + el.append(''); + el.append(''); + el.append(''); + el.append(''); + } + el.append(''); + el.append('
    ' + states[s].cells + '
    '); + el.append(''); + el.append('
    ' + burgsCount + '
    '); + el.append(''); + el.append('
    ' + areaConv + '
    '); + el.append(''); + el.append(''); + if (!neutral) { + el.append(''); + el.attr("data-country", states[s].name).attr("data-capital", capital).attr("data-expansion", states[s].power).attr("data-cells", states[s].cells) + .attr("data-burgs", states[s].burgs).attr("data-area", area).attr("data-population", population); + } else { + el.attr("data-country", "bottom").attr("data-capital", "bottom").attr("data-expansion", "bottom").attr("data-cells", states[s].cells) + .attr("data-burgs", states[s].burgs).attr("data-area", area).attr("data-population", population); + } + } + // initialize jQuery dialog + if (!$("#countriesEditor").is(":visible")) { + $("#countriesEditor").dialog({ + title: "Countries Editor", + minHeight: "auto", minWidth: Math.min(svgWidth, 390), + position: {my: "right top", at: "right-10 top+10", of: "svg"} + }).on("dialogclose", function() { + if (customization === 2 || customization === 3) { + $("#countriesManuallyCancel").click() + } + }); + } + // restore customization Editor version + if (customization === 3) { + $("div[data-sortby='expansion'],.statePower, .icon-resize-full").removeClass("hidden"); + $("div[data-sortby='cells'],.stateCells, .icon-check-empty").addClass("hidden"); + } else { + $("div[data-sortby='expansion'],.statePower, .icon-resize-full").addClass("hidden"); + $("div[data-sortby='cells'],.stateCells, .icon-check-empty").removeClass("hidden"); + } + // populate total line on footer + countriesFooterCountries.innerHTML = states.length; + if (states[states.length-1].capital === "neutral") {countriesFooterCountries.innerHTML = states.length - 1;} + countriesFooterBurgs.innerHTML = totalBurgs; + countriesFooterArea.innerHTML = si(totalArea) + unit; + countriesFooterPopulation.innerHTML = si(totalPopulation); + // handle events + $("#countriesBody .states").hover(focusOnState, unfocusState); + $(".enlange").click(function() { + const s = +(this.parentNode.id).slice(5); + const capital = states[s].capital; + const l = labels.select("[data-id='" + capital + "']"); + const x = +l.attr("x"), y = +l.attr("y"); + zoomTo(x, y, 8, 1600); + }); + $(".stateName").on("input", function() { + const s = +(this.parentNode.id).slice(5); + states[s].name = this.value; + labels.select("#regionLabel"+s).text(this.value); + if ($("#burgsEditor").is(":visible")) { + if ($("#burgsEditor").attr("data-state") == s) { + const color = ''; + $("div[aria-describedby='burgsEditor'] .ui-dialog-title").text("Burgs of " + this.value).prepend(color); + } + } + }); + $(".states > .stateColor").on("change", function() { + const s = +(this.parentNode.id).slice(5); + states[s].color = this.value; + regions.selectAll(".region"+s).attr("fill", this.value).attr("stroke", this.value); + if ($("#burgsEditor").is(":visible")) { + if ($("#burgsEditor").attr("data-state") == s) { + $(".ui-dialog-title > .stateColor").val(this.value); + } + } + }); + $(".stateCapital").on("input", function() { + const s = +(this.parentNode.id).slice(5); + const capital = states[s].capital; + manors[capital].name = this.value; + labels.select("[data-id='" + capital +"']").text(this.value); + if ($("#burgsEditor").is(":visible")) { + if ($("#burgsEditor").attr("data-state") == s) { + $("#burgs"+capital+" > .burgName").val(this.value); + } + } + }).hover(focusCapital, unfocus); + $(".stateBurgs, .stateBIcon").on("click", editBurgs).hover(focusBurgs, unfocus); + + $("#countriesBody > .states").on("click", function() { + if (customization === 2) { + $(".selected").removeClass("selected"); + $(this).addClass("selected"); + const state = +$(this).attr("id").slice(5); + let color = states[state].color; + if (color === "neutral") {color = "white";} + if (debug.selectAll(".circle").size()) debug.selectAll(".circle").attr("stroke", color); + } + }); + + $(".selectCapital").on("click", function() { + if ($(this).hasClass("pressed")) { + $(this).removeClass("pressed"); + tooltip.setAttribute("data-main", ""); + restoreDefaultEvents(); + } else { + $(this).addClass("pressed"); + viewbox.style("cursor", "crosshair").on("click", selectCapital); + tip("Click on the map to select or create a new capital", true); + } + }); + + function selectCapital() { + const point = d3.mouse(this); + const index = getIndex(point); + const x = rn(point[0], 2), y = rn(point[1], 2); + + if (cells[index].height < 20) { + tip("Cannot place capital on the water! Select a land cell"); + return; + } + const state = +$(".selectCapital.pressed").attr("id").replace("selectCapital", ""); + let oldState = cells[index].region; + if (oldState === "neutral") {oldState = states.length - 1;} + if (cells[index].manor !== undefined) { + // cell has burg + const burg = cells[index].manor; + if (states[oldState].capital === burg) { + tip("Existing capital cannot be selected as a new state capital! Select other cell"); + return; + } else { + // make this burg a new capital + const urbanFactor = 0.9; // for old neutrals + manors[burg].region = state; + if (oldState === "neutral") {manors[burg].population *= (1 / urbanFactor);} + manors[burg].population *= 2; // give capital x2 population bonus + states[state].capital = burg; + moveBurgToGroup(burg, "capitals"); + } + } else { + // free cell -> create new burg for a capital + const closest = cultureTree.find(x, y); + const culture = cultureTree.data().indexOf(closest) || 0; + const name = generateName(culture); + const i = manors.length; + cells[index].manor = i; + states[state].capital = i; + let score = cells[index].score; + if (score <= 0) {score = rn(Math.random(), 2);} + if (cells[index].crossroad) {score += cells[index].crossroad;} // crossroads + if (cells[index].confluence) {score += Math.pow(cells[index].confluence, 0.3);} // confluences + if (cells[index].port !== undefined) {score *= 3;} // port-capital + const population = rn(score, 1); + manors.push({i, cell:index, x, y, region: state, culture, name, population}); + burgIcons.select("#capitals").append("circle").attr("id", "burg"+i).attr("data-id", i).attr("cx", x).attr("cy", y).attr("r", 1).on("click", editBurg); + burgLabels.select("#capitals").append("text").attr("data-id", i).attr("x", x).attr("y", y).attr("dy", "-0.35em").text(name).on("click", editBurg); + } + cells[index].region = state; + cells[index].neighbors.map(function(n) { + if (cells[n].height < 20) {return;} + if (cells[n].manor !== undefined) {return;} + cells[n].region = state; + }); + redrawRegions(); + recalculateStateData(oldState); // re-calc old state data + recalculateStateData(state); // calc new state data + editCountries(); + restoreDefaultEvents(); + } + + $(".statePower").on("input", function() { + const s = +(this.parentNode.id).slice(5); + states[s].power = +this.value; + regenerateCountries(); + }); + $(".statePopulation").on("change", function() { + let s = +(this.parentNode.id).slice(5); + const popOr = +$(this).parent().attr("data-population"); + const popNew = getInteger(this.value); + if (!Number.isInteger(popNew) || popNew < 1000) { + this.value = si(popOr); + return; + } + const change = popNew / popOr; + states[s].urbanPopulation = rn(states[s].urbanPopulation * change, 2); + states[s].ruralPopulation = rn(states[s].ruralPopulation * change, 2); + const urban = rn(states[s].urbanPopulation * urbanization.value * populationRate.value); + const rural = rn(states[s].ruralPopulation * populationRate.value); + const population = (urban + rural) * 1000; + $(this).parent().attr("data-population", population); + this.value = si(population); + let total = 0; + $("#countriesBody > div").each(function(e, i) { + total += +$(this).attr("data-population"); + }); + countriesFooterPopulation.innerHTML = si(total); + if (states[s].capital === "neutral") {s = "neutral";} + manors.map(function(m) { + if (m.region !== s) {return;} + m.population = rn(m.population * change, 2); + }); + }); + // fully remove country + $("#countriesBody .icon-trash-empty").on("click", function() { + const s = +(this.parentNode.id).slice(5); + alertMessage.innerHTML = `Are you sure you want to remove the country? All lands and burgs will become neutral`; + $("#alert").dialog({resizable: false, title: "Remove country", buttons: { + Remove: function() {removeCountry(s); $(this).dialog("close");}, + Cancel: function() {$(this).dialog("close");} + }}); + }); + + function removeCountry(s) { + const cellsCount = states[s].cells; + const capital = +states[s].capital; + if (!isNaN(capital)) moveBurgToGroup(capital, "towns"); + states.splice(s, 1); + states.map(function(s, i) {s.i = i;}); + land.map(function(c) { + if (c.region === s) c.region = "neutral"; + else if (c.region > s) c.region -= 1; + }); + // do only if removed state had cells + if (cellsCount) { + manors.map(function(b) {if (b.region === s) b.region = "neutral";}); + // re-calculate neutral data + const i = states.length; + if (states[i-1].capital !== "neutral") { + states.push({i, color: "neutral", name: "Neutrals", capital: "neutral"}); + } + recalculateStateData(i-1); // re-calc data for neutrals + redrawRegions(); + } + editCountries(); + } + + $("#countriesNeutral, #countriesNeutralNumber").on("change", regenerateCountries); + } + + // burgs list + editor + function editBurgs(context, s) { + if (s === undefined) {s = +(this.parentNode.id).slice(5);} + $("#burgsEditor").attr("data-state", s); + $("#burgsBody").empty(); + $("#burgsHeader").children().removeClass("icon-sort-name-up icon-sort-name-down icon-sort-number-up icon-sort-number-down"); + const region = states[s].capital === "neutral" ? "neutral" : s; + const burgs = $.grep(manors, function (e) { + return (e.region === region); + }); + const populationArray = []; + burgs.map(function(b) { + $("#burgsBody").append('
    '); + const el = $("#burgsBody div:last-child"); + el.append(''); + el.append(''); + el.append(''); + el.append('
    ' + cultures[b.culture].name + '
    '); + let population = b.population * urbanization.value * populationRate.value * 1000; + populationArray.push(population); + population = population > 1e4 ? si(population) : rn(population, -1); + el.append(''); + el.append(''); + const capital = states[s].capital; + let type = "z-burg"; // usual burg by default + if (b.i === capital) {el.append(''); type = "c-capital";} + else {el.append('');} + if (cells[b.cell].port !== undefined) { + el.append(''); + if (type === "c-capital") {type = "a-capital-port";} else {type = "p-port";} + } else { + el.append(''); + } + if (b.i !== capital) {el.append('');} + el.attr("data-burg", b.name).attr("data-culture", cultures[b.culture].name).attr("data-population", b.population).attr("data-type", type); + }); + if (!$("#burgsEditor").is(":visible")) { + $("#burgsEditor").dialog({ + title: "Burgs of " + states[s].name, + minHeight: "auto", width: "auto", + position: {my: "right bottom", at: "right-10 bottom-10", of: "svg"} + }); + const color = ''; + if (region !== "neutral") {$("div[aria-describedby='burgsEditor'] .ui-dialog-title").prepend(color);} + } + // populate total line on footer + burgsFooterBurgs.innerHTML = burgs.length; + burgsFooterCulture.innerHTML = $("#burgsBody div:first-child .burgCulture").text(); + const avPop = rn(d3.mean(populationArray), -1); + burgsFooterPopulation.value = avPop; + $(".enlarge").click(function() { + const b = +(this.parentNode.id).slice(5); + const l = labels.select("[data-id='" + b + "']"); + const x = +l.attr("x"), y = +l.attr("y"); + zoomTo(x, y, 8, 1600); + }); + + $("#burgsBody > div").hover(focusBurg, unfocus); + + $("#burgsBody > div").click(function() { + if (!$("#changeCapital").hasClass("pressed")) return; + const s = +$("#burgsEditor").attr("data-state"); + const newCap = +$(this).attr("id").slice(5); + const oldCap = +states[s].capital; + if (newCap === oldCap) { + tip("This burg is already a capital! Please select a different burg", null, "error"); + return; + } + $("#changeCapital").removeClass("pressed"); + states[s].capital = newCap; + if (!isNaN(oldCap)) moveBurgToGroup(oldCap, "towns"); + recalculateStateData(s); + moveBurgToGroup(newCap, "capitals"); + }); + + $(".burgName").on("input", function() { + const b = +(this.parentNode.id).slice(5); + manors[b].name = this.value; + labels.select("[data-id='" + b + "']").text(this.value); + if (b === s && $("#countriesEditor").is(":visible")) { + $("#state"+s+" > .stateCapital").val(this.value); + } + }); + $(".ui-dialog-title > .stateColor").on("change", function() { + states[s].color = this.value; + regions.selectAll(".region"+s).attr("fill", this.value).attr("stroke", this.value); + if ($("#countriesEditor").is(":visible")) { + $("#state"+s+" > .stateColor").val(this.value); + } + }); + $(".burgPopulation").on("change", function() { + const b = +(this.parentNode.id).slice(5); + const pop = getInteger(this.value); + if (!Number.isInteger(pop) || pop < 10) { + const orig = rn(manors[b].population * urbanization.value * populationRate.value * 1000, 2); + this.value = si(orig); + return; + } + populationRaw = rn(pop / urbanization.value / populationRate.value / 1000, 2); + const change = populationRaw - manors[b].population; + manors[b].population = populationRaw; + $(this).parent().attr("data-population", populationRaw); + this.value = si(pop); + let state = manors[b].region; + if (state === "neutral") {state = states.length - 1;} + states[state].urbanPopulation += change; + updateCountryPopulationUI(state); + const average = states[state].urbanPopulation / states[state].burgs * urbanization.value * populationRate.value * 1000; + burgsFooterPopulation.value = rn(average, -1); + }); + $("#burgsFooterPopulation").on("change", function() { + const state = +$("#burgsEditor").attr("data-state"); + const newPop = +this.value; + const avPop = states[state].urbanPopulation / states[state].burgs * urbanization.value * populationRate.value * 1000; + if (!Number.isInteger(newPop) || newPop < 10) {this.value = rn(avPop, -1); return;} + const change = +this.value / avPop; + $("#burgsBody > div").each(function(e, i) { + const b = +(this.id).slice(5); + const pop = rn(manors[b].population * change, 2); + manors[b].population = pop; + $(this).attr("data-population", pop); + let popUI = pop * urbanization.value * populationRate.value * 1000; + popUI = popUI > 1e4 ? si(popUI) : rn(popUI, -1); + $(this).children().filter(".burgPopulation").val(popUI); + }); + states[state].urbanPopulation = rn(states[state].urbanPopulation * change, 2); + updateCountryPopulationUI(state); + }); + $("#burgsBody .icon-trash-empty").on("click", function() { + alertMessage.innerHTML = `Are you sure you want to remove the burg?`; + const b = +(this.parentNode.id).slice(5); + $("#alert").dialog({resizable: false, title: "Remove burg", + buttons: { + Remove: function() { + $(this).dialog("close"); + const state = +$("#burgsEditor").attr("data-state"); + $("#burgs"+b).remove(); + const cell = manors[b].cell; + manors[b].region = "removed"; + cells[cell].manor = undefined; + states[state].burgs = states[state].burgs - 1; + burgsFooterBurgs.innerHTML = states[state].burgs; + countriesFooterBurgs.innerHTML = +countriesFooterBurgs.innerHTML - 1; + states[state].urbanPopulation = states[state].urbanPopulation - manors[b].population; + const avPop = states[state].urbanPopulation / states[state].burgs * urbanization.value * populationRate.value * 1000; + burgsFooterPopulation.value = rn(avPop, -1); + if ($("#countriesEditor").is(":visible")) { + $("#state"+state+" > .stateBurgs").text(states[state].burgs); + } + labels.select("[data-id='" + b + "']").remove(); + icons.select("[data-id='" + b + "']").remove(); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + } + + // onhover style functions + function focusOnState() { + const s = +(this.id).slice(5); + labels.select("#regionLabel" + s).classed("drag", true); + document.getElementsByClassName("region" + s)[0].style.stroke = "red"; + document.getElementsByClassName("region" + s)[0].setAttribute("filter", "url(#blur1)"); + } + + function unfocusState() { + const s = +(this.id).slice(5); + labels.select("#regionLabel" + s).classed("drag", false); + document.getElementsByClassName("region" + s)[0].style.stroke = "none"; + document.getElementsByClassName("region" + s)[0].setAttribute("filter", null); + } + + function focusCapital() { + const s = +(this.parentNode.id).slice(5); + const capital = states[s].capital; + labels.select("[data-id='" + capital + "']").classed("drag", true); + icons.select("[data-id='" + capital + "']").classed("drag", true); + } + + function focusBurgs() { + const s = +(this.parentNode.id).slice(5); + const stateManors = $.grep(manors, function (e) { + return (e.region === s); + }); + stateManors.map(function(m) { + labels.select("[data-id='" + m.i + "']").classed("drag", true); + icons.select("[data-id='" + m.i + "']").classed("drag", true); + }); + } + + function focusBurg() { + const b = +(this.id).slice(5); + const l = labels.select("[data-id='" + b + "']"); + l.classed("drag", true); + } + + function unfocus() {$(".drag").removeClass("drag");} + + // save dialog position if "stable" dialog window is dragged + $(".stable").on("dialogdragstop", function(event, ui) { + sessionStorage.setItem(this.id, [ui.offset.left, ui.offset.top]); + }); + + // restore saved dialog position on "stable" dialog window open + $(".stable").on("dialogopen", function(event, ui) { + let pos = sessionStorage.getItem(this.id); + if (!pos) {return;} + pos = pos.split(","); + if (pos[0] > $(window).width() - 100 || pos[1] > $(window).width() - 40) {return;} // prevent showing out of screen + const at = `left+${pos[0]} top+${pos[1]}`; + $(this).dialog("option", "position", {my: "left top", at: at, of: "svg"}); + }); + + // open editCultures dialog + function editCultures() { + if (!cults.selectAll("path").size()) $("#toggleCultures").click(); + if (regions.style("display") !== "none") $("#toggleCountries").click(); + layoutPreset.value = "layoutCultural"; + $("#culturesBody").empty(); + $("#culturesHeader").children().removeClass("icon-sort-name-up icon-sort-name-down icon-sort-number-up icon-sort-number-down"); + + // collect data + const cellsC = [],areas = [],rurPops = [],urbPops = []; + const unit = areaUnit.value === "square" ? " " + distanceUnit.value + "²" : " " + areaUnit.value; + land.map(function(l) { + const c = l.culture; + if (c === undefined) return; + cellsC[c] = cellsC[c] ? cellsC[c] + 1 : 1; + areas[c] = areas[c] ? areas[c] + l.area : l.area; + rurPops[c] = rurPops[c] ? rurPops[c] + l.pop : l.pop; + }); + + manors.map(function(m) { + const c = m.culture; + if (isNaN(c)) return; + urbPops[c] = urbPops[c] ? urbPops[c] + m.population : m.population; + }); + + if (!nameBases[0]) applyDefaultNamesData(); + for (let c = 0; c < cultures.length; c++) { + $("#culturesBody").append('
    '); + if (cellsC[c] === undefined) { + cellsC[c] = 0; + areas[c] = 0; + rurPops[c] = 0; + } + if (urbPops[c] === undefined) urbPops[c] = 0; + const area = rn(areas[c] * Math.pow(distanceScale.value, 2)); + const areaConv = si(area) + unit; + const urban = rn(urbPops[c] * +urbanization.value * populationRate.value); + const rural = rn(rurPops[c] * populationRate.value); + const population = (urban + rural) * 1000; + const populationConv = si(population); + const title = '\'Total population: '+populationConv+'; Rural population: '+rural+'K; Urban population: '+urban+'K\''; + let b = cultures[c].base; + if (b >= nameBases.length) b = 0; + const base = nameBases[b].name; + const el = $("#culturesBody div:last-child"); + el.append(''); + el.append(''); + el.append(''); + el.append('
    ' + cellsC[c] + '
    '); + el.append(''); + el.append('
    ' + areaConv + '
    '); + el.append(''); + el.append('
    ' + populationConv + '
    '); + el.append(''); + el.append(''); + if (cultures.length > 1) { + el.append(''); + } + el.attr("data-color", cultures[c].color).attr("data-culture", cultures[c].name) + .attr("data-cells", cellsC[c]).attr("data-area", area).attr("data-population", population).attr("data-base", base); + } + + addCultureBaseOptions(); + drawCultureCenters(); + + let activeCultures = cellsC.reduce(function(s, v) {if(v) {return s + 1;} else {return s;}}, 0); + culturesFooterCultures.innerHTML = activeCultures + "/" + cultures.length; + culturesFooterCells.innerHTML = land.length; + let totalArea = areas.reduce(function(s, v) {return s + v;}); + totalArea = rn(totalArea * Math.pow(distanceScale.value, 2)); + culturesFooterArea.innerHTML = si(totalArea) + unit; + let totalPopulation = rurPops.reduce(function(s, v) {return s + v;}) * urbanization.value; + totalPopulation += urbPops.reduce(function(s, v) {return s + v;}); + culturesFooterPopulation.innerHTML = si(totalPopulation * 1000 * populationRate.value); + + // initialize jQuery dialog + if (!$("#culturesEditor").is(":visible")) { + $("#culturesEditor").dialog({ + title: "Cultures Editor", + minHeight: "auto", minWidth: Math.min(svgWidth, 336), + position: {my: "right top", at: "right-10 top+10", of: "svg"}, + close: function() { + debug.select("#cultureCenters").selectAll("*").remove(); + exitCulturesManualAssignment(); + } + }); + } + + $(".cultures").hover(function() { + const c = +(this.id).slice(7); + debug.select("#cultureCenter"+c).attr("stroke", "#000000e6"); + }, function() { + const c = +(this.id).slice(7); + debug.select("#cultureCenter"+c).attr("stroke", "#00000080"); + }); + + $(".cultures").on("click", function() { + if (customization !== 4) return; + const c = +(this.id).slice(7); + $(".selected").removeClass("selected"); + $(this).addClass("selected"); + let color = cultures[c].color; + debug.selectAll(".circle").attr("stroke", color); + }); + + $(".cultures .stateColor").on("input", function() { + const c = +(this.parentNode.id).slice(7); + const old = cultures[c].color; + cultures[c].color = this.value; + debug.select("#cultureCenter"+c).attr("fill", this.value); + cults.selectAll('[fill="'+old+'"]').attr("fill", this.value).attr("stroke", this.value); + }); + + $(".cultures .cultureName").on("input", function() { + const c = +(this.parentNode.id).slice(7); + cultures[c].name = this.value; + }); + + $(".cultures .icon-arrows-cw").on("click", function() { + const c = +(this.parentNode.id).slice(7); + manors.forEach(function(m) { + if (m.region === "removed") return; + if (m.culture !== c) return; + m.name = generateName(c); + labels.select("[data-id='" + m.i +"']").text(m.name); + }); + }); + + $("#culturesBody .icon-trash-empty").on("click", function() { + const c = +(this.parentNode.id).slice(7); + cultures.splice(c, 1); + const centers = cultures.map(function(c) {return c.center;}); + cultureTree = d3.quadtree(centers); + recalculateCultures("fullRedraw"); + editCultures(); + }); + + if (modules.editCultures) return; + modules.editCultures = true; + + function addCultureBaseOptions() { + $(".cultureBase").each(function() { + const c = +(this.parentNode.id).slice(7); + for (let i=0; i < nameBases.length; i++) { + this.options.add(new Option(nameBases[i].name, i)); + } + this.value = cultures[c].base; + this.addEventListener("change", function() { + cultures[c].base = +this.value; + }) + }); + } + + function drawCultureCenters() { + let cultureCenters = debug.select("#cultureCenters"); + if (cultureCenters.size()) {cultureCenters.selectAll("*").remove();} + else {cultureCenters = debug.append("g").attr("id", "cultureCenters");} + for (let c=0; c < cultures.length; c++) { + cultureCenters.append("circle").attr("id", "cultureCenter"+c) + .attr("cx", cultures[c].center[0]).attr("cy", cultures[c].center[1]) + .attr("r", 6).attr("stroke-width", 2).attr("stroke", "#00000080").attr("fill", cultures[c].color) + .on("mousemove", cultureCenterTip).on("mouseleave", function() {tip("", true)}) + .call(d3.drag().on("start", cultureCenterDrag)); + } + } + + function cultureCenterTip() { + tip('Drag to move culture center and re-calculate cultures', true); + } + + function cultureCenterDrag() { + const el = d3.select(this); + const c = +this.id.slice(13); + + d3.event.on("drag", function() { + const x = d3.event.x, y = d3.event.y; + el.attr("cx", x).attr("cy", y); + cultures[c].center = [x, y]; + const centers = cultures.map(function(c) {return c.center;}); + cultureTree = d3.quadtree(centers); + recalculateCultures(); + }); + } + + $("#culturesPercentage").on("click", function() { + const el = $("#culturesEditor"); + if (el.attr("data-type") === "absolute") { + el.attr("data-type", "percentage"); + const totalCells = land.length; + let totalArea = culturesFooterArea.innerHTML; + totalArea = getInteger(totalArea.split(" ")[0]); + const totalPopulation = getInteger(culturesFooterPopulation.innerHTML); + $("#culturesBody > .cultures").each(function() { + const cells = rn($(this).attr("data-cells") / totalCells * 100); + const area = rn($(this).attr("data-area") / totalArea * 100); + const population = rn($(this).attr("data-population") / totalPopulation * 100); + $(this).children().filter(".stateCells").text(cells + "%"); + $(this).children().filter(".stateArea").text(area + "%"); + $(this).children().filter(".culturePopulation").text(population + "%"); + }); + } else { + el.attr("data-type", "absolute"); + editCultures(); + } + }); + + $("#culturesManually").on("click", function() { + customization = 4; + tip("Click to select a culture, drag the circle to re-assign", true); + $("#culturesBottom").children().hide(); + $("#culturesManuallyButtons").show(); + viewbox.style("cursor", "crosshair").call(drag).on("click", changeSelectedOnClick); + debug.select("#cultureCenters").selectAll("*").remove(); + }); + + $("#culturesManuallyComplete").on("click", function() { + const changed = cults.selectAll("[data-culture]"); + changed.each(function() { + const i = +(this.id).slice(4); + const c = +this.getAttribute("data-culture"); + this.removeAttribute("data-culture"); + cells[i].culture = c; + const manor = cells[i].manor; + if (manor !== undefined) manors[manor].culture = c; + }); + exitCulturesManualAssignment(); + if (changed.size()) editCultures(); + }); + + $("#culturesManuallyCancel").on("click", function() { + cults.selectAll("[data-culture]").each(function() { + const i = +(this.id).slice(4); + const c = cells[i].culture; + this.removeAttribute("data-culture"); + const color = cultures[c].color; + this.setAttribute("fill", color); + this.setAttribute("stroke", color); + }); + exitCulturesManualAssignment(); + drawCultureCenters(); + }); + + function exitCulturesManualAssignment() { + debug.selectAll(".circle").remove(); + $("#culturesBottom").children().show(); + $("#culturesManuallyButtons").hide(); + $(".selected").removeClass("selected"); + customization = 0; + restoreDefaultEvents(); + } + + $("#culturesRandomize").on("click", function() { + const centers = cultures.map(function(c) { + const x = Math.floor(Math.random() * graphWidth * 0.8 + graphWidth * 0.1); + const y = Math.floor(Math.random() * graphHeight * 0.8 + graphHeight * 0.1); + const center = [x, y]; + c.center = center; + return center; + }); + cultureTree = d3.quadtree(centers); + recalculateCultures(); + drawCultureCenters(); + editCultures(); + }); + + $("#culturesExport").on("click", function() { + const unit = areaUnit.value === "square" ? distanceUnit.value + "2" : areaUnit.value; + let data = "Culture,Cells,Area ("+ unit +"),Population,Namesbase\n"; // headers + $("#culturesBody > .cultures").each(function() { + data += $(this).attr("data-culture") + ","; + data += $(this).attr("data-cells") + ","; + data += $(this).attr("data-area") + ","; + data += $(this).attr("data-population") + ","; + data += $(this).attr("data-base") + "\n"; + }); + + const dataBlob = new Blob([data], {type: "text/plain"}); + const url = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + document.body.appendChild(link); + link.download = "cultures_data" + Date.now() + ".csv"; + link.href = url; + link.click(); + window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000); + }); + + $("#culturesRegenerateNames").on("click", function() { + manors.forEach(function(m) { + if (m.region === "removed") return; + const culture = m.culture; + m.name = generateName(culture); + labels.select("[data-id='" + m.i +"']").text(m.name); + }); + }); + + $("#culturesEditNamesBase").on("click", editNamesbase); + + $("#culturesAdd").on("click", function() { + const x = Math.floor(Math.random() * graphWidth * 0.8 + graphWidth * 0.1); + const y = Math.floor(Math.random() * graphHeight * 0.8 + graphHeight * 0.1); + const center = [x, y]; + + let culture, base, name, color; + if (cultures.length < defaultCultures.length) { + // add one of the default cultures + culture = cultures.length; + base = defaultCultures[culture].base; + color = defaultCultures[culture].color; + name = defaultCultures[culture].name; + } else { + // add random culture besed on one of the current ones + culture = rand(cultures.length - 1); + name = generateName(culture); + color = colors20(cultures.length % 20); + base = cultures[culture].base; + } + cultures.push({name, color, base, center}); + const centers = cultures.map(function(c) {return c.center;}); + cultureTree = d3.quadtree(centers); + recalculateCultures(); + editCultures(); + }); + } + + // open editNamesbase dialog + function editNamesbase() { + // update list of bases + const select = document.getElementById("namesbaseSelect"); + for (let i = select.options.length; i < nameBases.length; i++) { + const option = new Option(nameBases[i].name, i); + select.options.add(option); + } + + // restore previous state + const textarea = document.getElementById("namesbaseTextarea"); + let selected = +textarea.getAttribute("data-base"); + if (selected >= nameBases.length) selected = 0; + select.value = selected; + if (textarea.value === "") namesbaseUpdateInputs(selected); + const examples = document.getElementById("namesbaseExamples"); + if (examples.innerHTML === "") namesbaseUpdateExamples(selected); + + // open a dialog + $("#namesbaseEditor").dialog({ + title: "Namesbase Editor", + minHeight: "auto", minWidth: Math.min(svgWidth, 400), + position: {my: "center", at: "center", of: "svg"} + }); + + if (modules.editNamesbase) return; + modules.editNamesbase = true; + + function namesbaseUpdateInputs(selected) { + const textarea = document.getElementById("namesbaseTextarea"); + textarea.value = nameBase[selected].join(", "); + textarea.setAttribute("data-base", selected); + const name = document.getElementById("namesbaseName"); + const method = document.getElementById("namesbaseMethod"); + const min = document.getElementById("namesbaseMin"); + const max = document.getElementById("namesbaseMax"); + const dublication = document.getElementById("namesbaseDouble"); + name.value = nameBases[selected].name; + method.value = nameBases[selected].method; + min.value = nameBases[selected].min; + max.value = nameBases[selected].max; + dublication.value = nameBases[selected].d; + } + + function namesbaseUpdateExamples(selected) { + const examples = document.getElementById("namesbaseExamples"); + let text = ""; + for (let i=0; i < 10; i++) { + const name = generateName(false, selected); + if (name === undefined) { + text = "Cannot generate examples. Please verify the data"; + break; + } + if (i !== 0) text += ", "; + text += name + } + examples.innerHTML = text; + } + + $("#namesbaseSelect").on("change", function() { + const selected = +this.value; + namesbaseUpdateInputs(selected); + namesbaseUpdateExamples(selected); + }); + + $("#namesbaseName").on("input", function() { + const base = +textarea.getAttribute("data-base"); + const select = document.getElementById("namesbaseSelect"); + select.options[base].innerHTML = this.value; + nameBases[base].name = this.value; + }); + + $("#namesbaseTextarea").on("input", function() { + const base = +this.getAttribute("data-base"); + const data = textarea.value.replace(/ /g, "").split(","); + nameBase[base] = data; + if (data.length < 3) { + chain[base] = []; + const examples = document.getElementById("namesbaseExamples"); + examples.innerHTML = "Please provide a correct source data"; + return; + } + const method = document.getElementById("namesbaseMethod").value; + if (method !== "selection") chain[base] = calculateChain(base); + }); + + $("#namesbaseMethod").on("change", function() { + const base = +textarea.getAttribute("data-base"); + nameBases[base].method = this.value; + if (this.value !== "selection") chain[base] = calculateChain(base); + }); + + $("#namesbaseMin").on("change", function() { + const base = +textarea.getAttribute("data-base"); + if (+this.value > nameBases[base].max) { + tip("Minimal length cannot be greated that maximal"); + } else { + nameBases[base].min = +this.value; + } + }); + + $("#namesbaseMax").on("change", function() { + const base = +textarea.getAttribute("data-base"); + if (+this.value < nameBases[base].min) { + tip("Maximal length cannot be less than minimal"); + } else { + nameBases[base].max = +this.value; + } + }); + + $("#namesbaseDouble").on("change", function() { + const base = +textarea.getAttribute("data-base"); + nameBases[base].d = this.value; + }); + + $("#namesbaseDefault").on("click", function() { + alertMessage.innerHTML = `Are you sure you want to restore the default namesbase? + All custom bases will be removed and default ones will be assigned to existing cultures. + Meanwhile existing names will not be changed.`; + $("#alert").dialog({resizable: false, title: "Restore default data", + buttons: { + Restore: function() { + $(this).dialog("close"); + $("#namesbaseEditor").dialog("close"); + const select = document.getElementById("namesbaseSelect"); + select.options.length = 0; + document.getElementById("namesbaseTextarea").value = ""; + document.getElementById("namesbaseTextarea").setAttribute("data-base", 0); + document.getElementById("namesbaseExamples").innerHTML === ""; + applyDefaultNamesData(); + const baseMax = nameBases.length - 1; + cultures.forEach(function(c) {if (c.base > baseMax) c.base = baseMax;}); + chains = {}; + calculateChains(); + editCultures(); + editNamesbase(); + }, + Cancel: function() {$(this).dialog("close");} + } + }); + }); + + $("#namesbaseAdd").on("click", function() { + const base = nameBases.length; + const name = "Base" + base; + const method = document.getElementById("namesbaseMethod").value; + const select = document.getElementById("namesbaseSelect"); + select.options.add(new Option(name, base)); + select.value = base; + nameBases.push({name, method, min: 4, max: 10, d: "", m: 1}); + nameBase.push([]); + document.getElementById("namesbaseName").value = name; + const textarea = document.getElementById("namesbaseTextarea"); + textarea.value = ""; + textarea.setAttribute("data-base", base); + document.getElementById("namesbaseExamples").innerHTML = ""; + chain[base] = []; + editCultures(); + }); + + $("#namesbaseExamples, #namesbaseUpdateExamples").on("click", function() { + const select = document.getElementById("namesbaseSelect"); + namesbaseUpdateExamples(+select.value); + }); + + $("#namesbaseDownload").on("click", function() { + const nameBaseString = JSON.stringify(nameBase) + "\r\n"; + const nameBasesString = JSON.stringify(nameBases); + const dataBlob = new Blob([nameBaseString + nameBasesString],{type:"text/plain"}); + const url = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + link.download = "namebase" + Date.now() + ".txt"; + link.href = url; + link.click(); + }); + + $("#namesbaseUpload").on("click", function() {namesbaseToLoad.click();}); + $("#namesbaseToLoad").change(function() { + const fileToLoad = this.files[0]; + this.value = ""; + const fileReader = new FileReader(); + fileReader.onload = function(fileLoadedEvent) { + const dataLoaded = fileLoadedEvent.target.result; + const data = dataLoaded.split("\r\n"); + if (data[0] && data[1]) { + nameBase = JSON.parse(data[0]); + nameBases = JSON.parse(data[1]); + const select = document.getElementById("namesbaseSelect"); + select.options.length = 0; + document.getElementById("namesbaseTextarea").value = ""; + document.getElementById("namesbaseTextarea").setAttribute("data-base", 0); + document.getElementById("namesbaseExamples").innerHTML === ""; + const baseMax = nameBases.length - 1; + cultures.forEach(function(c) {if (c.base > baseMax) c.base = baseMax;}); + chains = {}; + calculateChains(); + editCultures(); + editNamesbase(); + } else { + tip("Cannot load a namesbase. Please check the data format") + } + }; + fileReader.readAsText(fileToLoad, "UTF-8"); + }); + } + + // open editLegends dialog + function editLegends(id, name) { + // update list of objects + const select = document.getElementById("legendSelect"); + for (let i = select.options.length; i < notes.length; i++) { + let option = new Option(notes[i].id, notes[i].id); + select.options.add(option); + } + + // select an object + if (id) { + let note = notes.find(note => note.id === id); + if (note === undefined) { + if (!name) name = id; + note = {id, name, legend: ""}; + notes.push(note); + let option = new Option(id, id); + select.options.add(option); + } + select.value = id; + legendName.value = note.name; + legendText.value = note.legend; + } + + // open a dialog + $("#legendEditor").dialog({ + title: "Legends Editor", + minHeight: "auto", minWidth: Math.min(svgWidth, 400), + position: {my: "center", at: "center", of: "svg"} + }); + + if (modules.editLegends) return; + modules.editLegends = true; + + // select another object + document.getElementById("legendSelect").addEventListener("change", function() { + let note = notes.find(note => note.id === this.value); + legendName.value = note.name; + legendText.value = note.legend; + }); + + // change note name on input + document.getElementById("legendName").addEventListener("input", function() { + let select = document.getElementById("legendSelect"); + let id = select.value; + let note = notes.find(note => note.id === id); + note.name = this.value; + }); + + // change note text on input + document.getElementById("legendText").addEventListener("input", function() { + let select = document.getElementById("legendSelect"); + let id = select.value; + let note = notes.find(note => note.id === id); + note.legend = this.value; + }); + + // hightlight DOM element + document.getElementById("legendFocus").addEventListener("click", function() { + let select = document.getElementById("legendSelect"); + let element = document.getElementById(select.value); + + // if element is not found + if (element === null) { + const message = "Related element is not found. Would you like to remove the note (legend item)?"; + alertMessage.innerHTML = message; + $("#alert").dialog({resizable: false, title: "Element not found", + buttons: { + Remove: function() {$(this).dialog("close"); removeLegend();}, + Keep: function() {$(this).dialog("close");} + } + }); + return; + } + + // if element is found + highlightElement(element); + }); + + function highlightElement(element) { + if (debug.select(".highlighted").size()) return; // allow only 1 highlight element simultaniosly + let box = element.getBBox(); + let transform = element.getAttribute("transform") || null; + let t = d3.transition().duration(1000).ease(d3.easeBounceOut); + let r = d3.transition().duration(500).ease(d3.easeLinear); + let highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height).attr("transform", transform); + highlight.classed("highlighted", 1) + .transition(t).style("outline-offset", "0px") + .transition(r).style("outline-color", "transparent").remove(); + let tr = parseTransform(transform); + let x = box.x + box.width / 2; + if (tr[0]) x += tr[0]; + let y = box.y + box.height / 2; + if (tr[1]) y += tr[1]; + if (scale >= 2) zoomTo(x, y, scale, 1600); + } + + // download legends object as text file + document.getElementById("legendDownload").addEventListener("click", function() { + const legendString = JSON.stringify(notes); + const dataBlob = new Blob([legendString],{type:"text/plain"}); + const url = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + link.download = "legends" + Date.now() + ".txt"; + link.href = url; + link.click(); + }); + + // upload legends object as text file and parse to json + document.getElementById("legendUpload").addEventListener("click", function() { + document.getElementById("lagendsToLoad").click(); + }); + document.getElementById("lagendsToLoad").addEventListener("change", function() { + const fileToLoad = this.files[0]; + this.value = ""; + const fileReader = new FileReader(); + fileReader.onload = function(fileLoadedEvent) { + const dataLoaded = fileLoadedEvent.target.result; + if (dataLoaded) { + notes = JSON.parse(dataLoaded); + const select = document.getElementById("legendSelect"); + select.options.length = 0; + editLegends(notes[0].id, notes[0].name); + } else { + tip("Cannot load a file. Please check the data format") + } + }; + fileReader.readAsText(fileToLoad, "UTF-8"); + }); + + // remove the legend item + document.getElementById("legendRemove").addEventListener("click", function() { + alertMessage.innerHTML = "Are you sure you want to remove the selected legend?"; + $("#alert").dialog({resizable: false, title: "Remove legend element", + buttons: { + Remove: function() {$(this).dialog("close"); removeLegend();}, + Keep: function() {$(this).dialog("close");} + } + }); + }); + + function removeLegend() { + let select = document.getElementById("legendSelect"); + let index = notes.findIndex(n => n.id === select.value); + notes.splice(index, 1); + select.options.length = 0; + if (notes.length === 0) { + $("#legendEditor").dialog("close"); + return; + } + editLegends(notes[0].id, notes[0].name); + } + + } + + // Map scale and measurements editor + function editScale() { + $("#ruler").fadeIn(); + $("#scaleEditor").dialog({ + title: "Scale Editor", + minHeight: "auto", width: "auto", resizable: false, + position: {my: "center bottom", at: "center bottom-10", of: "svg"} + }); + } + + // update only UI and sorting value in countryEditor screen + function updateCountryPopulationUI(s) { + if ($("#countriesEditor").is(":visible")) { + const urban = rn(states[s].urbanPopulation * +urbanization.value * populationRate.value); + const rural = rn(states[s].ruralPopulation * populationRate.value); + const population = (urban + rural) * 1000; + $("#state"+s).attr("data-population", population); + $("#state"+s).children().filter(".statePopulation").val(si(population)); + } + } + + // update dialogs if measurements are changed + function updateCountryEditors() { + if ($("#countriesEditor").is(":visible")) {editCountries();} + if ($("#burgsEditor").is(":visible")) { + const s = +$("#burgsEditor").attr("data-state"); + editBurgs(this, s); + } + } + + // remove drawn regions and draw all regions again + function redrawRegions() { + regions.selectAll("*").remove(); + borders.selectAll("path").remove(); + removeAllLabelsInGroup("countries"); + drawRegions(); + } + + // remove all labels in group including textPaths + function removeAllLabelsInGroup(group) { + labels.select("#"+group).selectAll("text").each(function() { + defs.select("#textPath_" + this.id).remove(); + this.remove(); + }); + if (group !== "countries") { + labels.select("#"+group).remove(); + updateLabelGroups(); + } + } + + // restore keeped region / burgs / cultures data on edit heightmap completion + function restoreRegions() { + borders.selectAll("path").remove(); + removeAllLabelsInGroup("countries"); + manors.map(function(m) { + const cell = diagram.find(m.x, m.y).index; + if (cells[cell].height < 20) { + // remove manor in ocean + m.region = "removed"; + m.cell = cell; + d3.selectAll("[data-id='" + m.i + "']").remove(); + } else { + m.cell = cell; + cells[cell].manor = m.i; + } + }); + cells.map(function(c) { + if (c.height < 20) { + // no longer a land cell + delete c.region; + delete c.culture; + return; + } + if (c.region === undefined) { + c.region = "neutral"; + if (states[states.length - 1].capital !== "neutral") { + states.push({i: states.length, color: "neutral", capital: "neutral", name: "Neutrals"}); + } + } + if (c.culture === undefined) { + const closest = cultureTree.find(c.data[0],c.data[1]); + c.culture = cultureTree.data().indexOf(closest); + } + }); + states.map(function(s) {recalculateStateData(s.i);}); + drawRegions(); + } + + function regenerateCountries() { + regions.selectAll("*").remove(); + const neutral = neutralInput.value = +countriesNeutral.value; + manors.forEach(function(m) { + if (m.region === "removed") return; + let state = "neutral", closest = neutral; + states.map(function(s) { + if (s.capital === "neutral" || s.capital === "select") return; + const c = manors[s.capital]; + let dist = Math.hypot(c.x - m.x, c.y - m.y) / s.power; + if (cells[m.cell].fn !== cells[c.cell].fn) dist *= 3; + if (dist < closest) {state = s.i; closest = dist;} + }); + m.region = state; + cells[m.cell].region = state; + }); + + defineRegions(); + const temp = regions.append("g").attr("id", "temp"); + land.forEach(function(l) { + if (l.region === undefined) return; + if (l.region === "neutral") return; + const color = states[l.region].color; + temp.append("path") + .attr("data-cell", l.index).attr("data-state", l.region) + .attr("d", "M" + polygons[l.index].join("L") + "Z") + .attr("fill", color).attr("stroke", color); + }); + const neutralCells = $.grep(cells, function(e) {return e.region === "neutral";}); + const last = states.length - 1; + const type = states[last].color; + if (type === "neutral" && !neutralCells.length) { + // remove neutral line + $("#state" + last).remove(); + states.splice(-1); + } + // recalculate data for all countries + states.map(function(s) { + recalculateStateData(s.i); + $("#state"+s.i+" > .stateCells").text(s.cells); + $("#state"+s.i+" > .stateBurgs").text(s.burgs); + const area = rn(s.area * Math.pow(distanceScale.value, 2)); + const unit = areaUnit.value === "square" ? " " + distanceUnit.value + "²" : " " + areaUnit.value; + $("#state"+s.i+" > .stateArea").text(si(area) + unit); + const urban = rn(s.urbanPopulation * urbanization.value * populationRate.value); + const rural = rn(s.ruralPopulation * populationRate.value); + const population = (urban + rural) * 1000; + $("#state"+s.i+" > .statePopulation").val(si(population)); + $("#state"+s.i).attr("data-cells", s.cells).attr("data-burgs", s.burgs) + .attr("data-area", area).attr("data-population", population); + }); + if (type !== "neutral" && neutralCells.length) { + // add neutral line + states.push({i: states.length, color: "neutral", capital: "neutral", name: "Neutrals"}); + recalculateStateData(states.length - 1); + editCountries(); + } + } + + // enter state edit mode + function mockRegions() { + if (grid.style("display") !== "inline") {toggleGrid.click();} + if (labels.style("display") !== "none") {toggleLabels.click();} + stateBorders.selectAll("*").remove(); + neutralBorders.selectAll("*").remove(); + } + + // handle DOM elements sorting on header click + $(".sortable").on("click", function() { + const el = $(this); + // remove sorting for all siglings except of clicked element + el.siblings().removeClass("icon-sort-name-up icon-sort-name-down icon-sort-number-up icon-sort-number-down"); + const type = el.hasClass("alphabetically") ? "name" : "number"; + let state = "no"; + if (el.is("[class*='down']")) {state = "asc";} + if (el.is("[class*='up']")) {state = "desc";} + const sortby = el.attr("data-sortby"); + const list = el.parent().next(); // get list container element (e.g. "countriesBody") + const lines = list.children("div"); // get list elements + if (state === "no" || state === "asc") { // sort desc + el.removeClass("icon-sort-" + type + "-down"); + el.addClass("icon-sort-" + type + "-up"); + lines.sort(function(a, b) { + let an = a.getAttribute("data-" + sortby); + if (an === "bottom") {return 1;} + let bn = b.getAttribute("data-" + sortby); + if (bn === "bottom") {return -1;} + if (type === "number") {an = +an; bn = +bn;} + if (an > bn) {return 1;} + if (an < bn) {return -1;} + return 0; + }); + } + if (state === "desc") { // sort asc + el.removeClass("icon-sort-" + type + "-up"); + el.addClass("icon-sort-" + type + "-down"); + lines.sort(function(a, b) { + let an = a.getAttribute("data-" + sortby); + if (an === "bottom") {return 1;} + let bn = b.getAttribute("data-" + sortby); + if (bn === "bottom") {return -1;} + if (type === "number") {an = +an; bn = +bn;} + if (an < bn) {return 1;} + if (an > bn) {return -1;} + return 0; + }); + } + lines.detach().appendTo(list); + }); + + // load text file with new burg names + $("#burgsListToLoad").change(function() { + const fileToLoad = this.files[0]; + this.value = ""; + const fileReader = new FileReader(); + fileReader.onload = function(fileLoadedEvent) { + const dataLoaded = fileLoadedEvent.target.result; + const data = dataLoaded.split("\r\n"); + if (data.length === 0) {return;} + let change = []; + let message = `Burgs will be renamed as below. Please confirm`; + message += `
    `; + for (let i=0; i < data.length && i < manors.length; i++) { + const v = data[i]; + if (v === "" || v === undefined) {continue;} + if (v === manors[i].name) {continue;} + change.push({i, name: v}); + message += ``; + } + message += `
    IdCurrent nameNew Name
    ${i}${manors[i].name}${v}
    `; + alertMessage.innerHTML = message; + $("#alert").dialog({title: "Burgs bulk renaming", position: {my: "center", at: "center", of: "svg"}, + buttons: { + Cancel: function() {$(this).dialog("close");}, + Confirm: function() { + for (let i=0; i < change.length; i++) { + const id = change[i].i; + manors[id].name = change[i].name; + labels.select("[data-id='" + id + "']").text(change[i].name); + } + $(this).dialog("close"); + updateCountryEditors(); + } + } + }); + }; + fileReader.readAsText(fileToLoad, "UTF-8"); + }); + + // just apply map size that was already set, apply graph size! + function applyMapSize() { + svgWidth = graphWidth = +mapWidthInput.value; + svgHeight = graphHeight = +mapHeightInput.value; + svg.attr("width", svgWidth).attr("height", svgHeight); + // set extent to map borders + 100px to get infinity world reception + voronoi = d3.voronoi().extent([[-1, -1],[graphWidth+1, graphHeight+1]]); + zoom.translateExtent([[0, 0],[graphWidth, graphHeight]]).scaleExtent([1, 20]).scaleTo(svg, 1); + viewbox.attr("transform", null); + ocean.selectAll("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); + } + + // change svg size on manual size change or window resize, do not change graph size + function changeMapSize() { + fitScaleBar(); + svgWidth = +mapWidthInput.value; + svgHeight = +mapHeightInput.value; + svg.attr("width", svgWidth).attr("height", svgHeight); + const width = Math.max(svgWidth, graphWidth); + const height = Math.max(svgHeight, graphHeight); + zoom.translateExtent([[0, 0],[width, height]]); + svg.select("#ocean").selectAll("rect").attr("x", 0) + .attr("y", 0).attr("width", width).attr("height", height); + } + + // fit full-screen map if window is resized + $(window).resize(function(e) { + // trick to prevent resize on download bar opening + if (autoResize === false) return; + mapWidthInput.value = window.innerWidth; + mapHeightInput.value = window.innerHeight; + changeMapSize(); + }); + + // fit ScaleBar to map size + function fitScaleBar() { + const el = d3.select("#scaleBar"); + if (!el.select("rect").size()) return; + const bbox = el.select("rect").node().getBBox(); + let tr = [svgWidth - bbox.width, svgHeight - (bbox.height - 10)]; + if (sessionStorage.getItem("scaleBar")) { + const scalePos = sessionStorage.getItem("scaleBar").split(","); + tr = [+scalePos[0] - bbox.width, +scalePos[1] - bbox.height]; + } + el.attr("transform", "translate(" + rn(tr[0]) + "," + rn(tr[1]) + ")"); + } + + // restore initial style + function applyDefaultStyle() { + viewbox.on("touchmove mousemove", moved); + landmass.attr("opacity", 1).attr("fill", "#eef6fb"); + coastline.attr("opacity", .5).attr("stroke", "#1f3846").attr("stroke-width", .7).attr("filter", "url(#dropShadow)"); + regions.attr("opacity", .4); + stateBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .7).attr("stroke-dasharray", "1.2 1.5").attr("stroke-linecap", "butt"); + neutralBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .5).attr("stroke-dasharray", "1 1.5").attr("stroke-linecap", "butt"); + cults.attr("opacity", .6); + rivers.attr("opacity", 1).attr("fill", "#5d97bb"); + lakes.attr("opacity", .5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", .7); + icons.selectAll("g").attr("opacity", 1).attr("fill", "#ffffff").attr("stroke", "#3e3e4b"); + roads.attr("opacity", .9).attr("stroke", "#d06324").attr("stroke-width", .35).attr("stroke-dasharray", "1.5").attr("stroke-linecap", "butt"); + trails.attr("opacity", .9).attr("stroke", "#d06324").attr("stroke-width", .15).attr("stroke-dasharray", ".8 1.6").attr("stroke-linecap", "butt"); + searoutes.attr("opacity", .8).attr("stroke", "#ffffff").attr("stroke-width", .35).attr("stroke-dasharray", "1 2").attr("stroke-linecap", "round"); + grid.attr("opacity", 1).attr("stroke", "#808080").attr("stroke-width", .1); + ruler.attr("opacity", 1).style("display", "none").attr("filter", "url(#dropShadow)"); + overlay.attr("opacity", .8).attr("stroke", "#808080").attr("stroke-width", .5); + markers.attr("filter", "url(#dropShadow01)"); + + // ocean style + svg.style("background-color", "#000000"); + ocean.attr("opacity", 1); + oceanLayers.select("rect").attr("fill", "#53679f"); + oceanLayers.attr("filter", ""); + oceanPattern.attr("opacity", 1); + oceanLayers.selectAll("path").attr("display", null); + styleOceanPattern.checked = true; + styleOceanLayers.checked = true; + + labels.attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0); + let size = rn(8 - regionsInput.value / 20); + if (size < 3) size = 3; + burgLabels.select("#capitals").attr("fill", "#3e3e4b").attr("opacity", 1).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", size).attr("data-size", size); + burgLabels.select("#towns").attr("fill", "#3e3e4b").attr("opacity", 1).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 3).attr("data-size", 4); + burgIcons.select("#capitals").attr("size", 1).attr("stroke-width", .24).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("fill-opacity", .7).attr("stroke-opacity", 1).attr("opacity", 1); + burgIcons.select("#towns").attr("size", .5).attr("stroke-width", .12).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("fill-opacity", .7).attr("stroke-opacity", 1).attr("opacity", 1); + size = rn(16 - regionsInput.value / 6); + if (size < 6) size = 6; + labels.select("#countries").attr("fill", "#3e3e4b").attr("opacity", 1).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", size).attr("data-size", size); + icons.select("#capital-anchors").attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("stroke-width", 1.2).attr("size", 2); + icons.select("#town-anchors").attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("stroke-width", 1.2).attr("size", 1); + } + + // Style options + $("#styleElementSelect").on("change", function() { + const sel = this.value; + let el = viewbox.select("#"+sel); + if (sel == "ocean") el = oceanLayers.select("rect"); + $("#styleInputs > div").hide(); + + // opacity + $("#styleOpacity, #styleFilter").css("display", "block"); + const opacity = el.attr("opacity") || 1; + styleOpacityInput.value = styleOpacityOutput.value = opacity; + + // filter + if (sel == "ocean") el = oceanLayers; + styleFilterInput.value = el.attr("filter") || ""; + if (sel === "rivers" || sel === "lakes" || sel === "landmass") { + $("#styleFill").css("display", "inline-block"); + styleFillInput.value = styleFillOutput.value = el.attr("fill"); + } + + if (sel === "roads" || sel === "trails" || sel === "searoutes" || sel === "lakes" || sel === "stateBorders" || sel === "neutralBorders" || sel === "grid" || sel === "overlay" || sel === "coastline") { + $("#styleStroke").css("display", "inline-block"); + styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke"); + $("#styleStrokeWidth").css("display", "block"); + const width = el.attr("stroke-width") || ""; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = width; + } + + if (sel === "roads" || sel === "trails" || sel === "searoutes" || sel === "stateBorders" || sel === "neutralBorders" || sel === "overlay") { + $("#styleStrokeDasharray, #styleStrokeLinecap").css("display", "block"); + styleStrokeDasharrayInput.value = el.attr("stroke-dasharray") || ""; + styleStrokeLinecapInput.value = el.attr("stroke-linecap") || "inherit"; + } + + if (sel === "terrs") $("#styleScheme").css("display", "block"); + if (sel === "heightmap") $("#styleScheme").css("display", "block"); + if (sel === "overlay") $("#styleOverlay").css("display", "block"); + + if (sel === "labels") { + $("#styleFill, #styleStroke, #styleStrokeWidth, #styleFontSize").css("display", "inline-block"); + styleFillInput.value = styleFillOutput.value = el.select("g").attr("fill") || "#3e3e4b"; + styleStrokeInput.value = styleStrokeOutput.value = el.select("g").attr("stroke") || "#3a3a3a"; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || 0; + $("#styleLabelGroups").css("display", "inline-block"); + updateLabelGroups(); + } + + if (sel === "ocean") { + $("#styleOcean").css("display", "block"); + styleOceanBack.value = styleOceanBackOutput.value = svg.style("background-color"); + styleOceanFore.value = styleOceanForeOutput.value = oceanLayers.select("rect").attr("fill"); + } + }); + + // update Label Groups displayed on Style tab + function updateLabelGroups() { + if (styleElementSelect.value !== "labels") return; + const cont = d3.select("#styleLabelGroupItems"); + cont.selectAll("button").remove(); + labels.selectAll("g").each(function() { + const el = d3.select(this); + const id = el.attr("id"); + const name = id.charAt(0).toUpperCase() + id.substr(1); + const state = el.classed("hidden"); + if (id === "burgLabels") return; + cont.append("button").attr("id", id).text(name).classed("buttonoff", state).on("click", function() { + // toggle label group on click + if (hideLabels.checked) hideLabels.click(); + const el = d3.select("#"+this.id); + const state = !el.classed("hidden"); + el.classed("hidden", state); + d3.select(this).classed("buttonoff", state); + }); + }); + } + + $("#styleFillInput").on("input", function() { + styleFillOutput.value = this.value; + const el = svg.select("#" + styleElementSelect.value); + if (styleElementSelect.value !== "labels") { + el.attr('fill', this.value); + } else { + el.selectAll("g").attr('fill', this.value); + } + }); + + $("#styleStrokeInput").on("input", function() { + styleStrokeOutput.value = this.value; + const el = svg.select("#"+styleElementSelect.value); + el.attr('stroke', this.value); + }); + + $("#styleStrokeWidthInput").on("input", function() { + styleStrokeWidthOutput.value = this.value; + const el = svg.select("#"+styleElementSelect.value); + el.attr('stroke-width', +this.value); + }); + + $("#styleStrokeDasharrayInput").on("input", function() { + const sel = styleElementSelect.value; + svg.select("#"+sel).attr('stroke-dasharray', this.value); + }); + + $("#styleStrokeLinecapInput").on("change", function() { + const sel = styleElementSelect.value; + svg.select("#"+sel).attr('stroke-linecap', this.value); + }); + + $("#styleOpacityInput").on("input", function() { + styleOpacityOutput.value = this.value; + const sel = styleElementSelect.value; + svg.select("#"+sel).attr('opacity', this.value); + + }); + + $("#styleFilterInput").on("change", function() { + let sel = styleElementSelect.value; + if (sel == "ocean") sel = "oceanLayers"; + const el = svg.select("#"+sel); + el.attr('filter', this.value); + zoom.scaleBy(svg, 1.00001); // enforce browser re-draw + }); + + $("#styleSchemeInput").on("change", function() { + terrs.selectAll("path").remove(); + toggleHeight(); + }); + + $("#styleOverlayType").on("change", function() { + overlay.selectAll("*").remove(); + if (!$("#toggleOverlay").hasClass("buttonoff")) toggleOverlay(); + }); + + $("#styleOverlaySize").on("change", function() { + overlay.selectAll("*").remove(); + if (!$("#toggleOverlay").hasClass("buttonoff")) toggleOverlay(); + styleOverlaySizeOutput.value = this.value; + }); + + function calculateFriendlyOverlaySize() { + let size = styleOverlaySize.value; + if (styleOverlayType.value === "windrose") {styleOverlaySizeFriendly.innerHTML = ""; return;} + if (styleOverlayType.value === "pointyHex" || styleOverlayType.value === "flatHex") size *= Math.cos(30 * Math.PI / 180) * 2; + let friendly = "(" + rn(size * distanceScale.value) + " " + distanceUnit.value + ")"; + styleOverlaySizeFriendly.value = friendly; + } + + $("#styleOceanBack").on("input", function() { + svg.style("background-color", this.value); + styleOceanBackOutput.value = this.value; + }); + + $("#styleOceanFore").on("input", function() { + oceanLayers.select("rect").attr("fill", this.value); + styleOceanForeOutput.value = this.value; + }); + + $("#styleOceanPattern").on("click", function() {oceanPattern.attr("opacity", +this.checked);}); + + $("#styleOceanLayers").on("click", function() { + const display = this.checked ? "block" : "none"; + oceanLayers.selectAll("path").attr("display", display); + }); + + // Other Options handlers + $("input, select").on("input change", function() { + const id = this.id; + if (id === "hideLabels") invokeActiveZooming(); + if (id === "mapWidthInput" || id === "mapHeightInput") { + changeMapSize(); + autoResize = false; + localStorage.setItem("mapWidth", mapWidthInput.value); + localStorage.setItem("mapHeight", mapHeightInput.value); + } + if (id === "sizeInput") { + graphSize = sizeOutput.value = +this.value; + if (graphSize === 3) {sizeOutput.style.color = "red";} + if (graphSize === 2) {sizeOutput.style.color = "yellow";} + if (graphSize === 1) {sizeOutput.style.color = "green";} + // localStorage.setItem("graphSize", this.value); - temp off to always start with size 1 + } + if (id === "templateInput") {localStorage.setItem("template", this.value);} + if (id === "manorsInput") {manorsOutput.value = this.value; localStorage.setItem("manors", this.value);} + if (id === "regionsInput") { + regionsOutput.value = this.value; + let size = rn(6 - this.value / 20); + if (size < 3) {size = 3;} + burgLabels.select("#capitals").attr("data-size", size); + size = rn(18 - this.value / 6); + if (size < 4) {size = 4;} + labels.select("#countries").attr("data-size", size); + localStorage.setItem("regions", this.value); + } + if (id === "powerInput") {powerOutput.value = this.value; localStorage.setItem("power", this.value);} + if (id === "neutralInput") {neutralOutput.value = countriesNeutral.value = this.value; localStorage.setItem("neutal", this.value);} + if (id === "culturesInput") {culturesOutput.value = this.value; localStorage.setItem("cultures", this.value);} + if (id === "precInput") {precOutput.value = +precInput.value; localStorage.setItem("prec", this.value);} + if (id === "swampinessInput") {swampinessOutput.value = this.value; localStorage.setItem("swampiness", this.value);} + if (id === "outlineLayersInput") localStorage.setItem("outlineLayers", this.value); + if (id === "transparencyInput") changeDialogsTransparency(this.value); + if (id === "pngResolutionInput") localStorage.setItem("pngResolution", this.value); + if (id === "zoomExtentMin" || id === "zoomExtentMax") { + zoom.scaleExtent([+zoomExtentMin.value, +zoomExtentMax.value]); + zoom.scaleTo(svg, +this.value); + } + + if (id === "convertOverlay") {canvas.style.opacity = convertOverlayValue.innerHTML = +this.value;} + if (id === "populationRate") { + populationRateOutput.value = si(+populationRate.value * 1000); + updateCountryEditors(); + } + if (id === "urbanization") { + urbanizationOutput.value = this.value; + updateCountryEditors(); + } + if (id === "distanceUnit" || id === "distanceScale" || id === "areaUnit") { + const dUnit = distanceUnit.value; + if (id === "distanceUnit" && dUnit === "custom_name") { + const custom = prompt("Provide a custom name for distance unit"); + if (custom) { + const opt = document.createElement("option"); + opt.value = opt.innerHTML = custom; + distanceUnit.add(opt); + distanceUnit.value = custom; + } else { + this.value = "km"; return; + } + } + const scale = distanceScale.value; + scaleOutput.value = scale + " " + dUnit; + ruler.selectAll("g").each(function() { + let label; + const g = d3.select(this); + const area = +g.select("text").attr("data-area"); + if (area) { + const areaConv = area * Math.pow(scale, 2); // convert area to distanceScale + let unit = areaUnit.value; + if (unit === "square") {unit = dUnit + "²"} else {unit = areaUnit.value;} + label = si(areaConv) + " " + unit; + } else { + const dist = +g.select("text").attr("data-dist"); + label = rn(dist * scale) + " " + dUnit; + } + g.select("text").text(label); + }); + ruler.selectAll(".gray").attr("stroke-dasharray", rn(30 / scale, 2)); + drawScaleBar(); + updateCountryEditors(); + } + if (id === "barSize") { + barSizeOutput.innerHTML = this.value; + $("#scaleBar").removeClass("hidden"); + drawScaleBar(); + } + if (id === "barLabel") { + $("#scaleBar").removeClass("hidden"); + drawScaleBar(); + } + if (id === "barBackOpacity" || id === "barBackColor") { + d3.select("#scaleBar > rect") + .attr("opacity", +barBackOpacity.value) + .attr("fill", barBackColor.value); + $("#scaleBar").removeClass("hidden"); + } + }); + + $("#scaleOutput").change(function() { + if (this.value === "" || isNaN(+this.value) || this.value < 0.01 || this.value > 10) { + tip("Manually entered distance scale should be a number in a [0.01; 10] range"); + this.value = distanceScale.value + " " + distanceUnit.value; + return; + } + distanceScale.value = +this.value; + scaleOutput.value = this.value + " " + distanceUnit.value; + updateCountryEditors(); + }); + + $("#populationRateOutput").change(function() { + if (this.value === "" || isNaN(+this.value) || this.value < 0.001 || this.value > 10) { + tip("Manually entered population rate should be a number in a [0.001; 10] range"); + this.value = si(populationRate.value * 1000); + return; + } + populationRate.value = +this.value; + populationRateOutput.value = si(this.value * 1000); + updateCountryEditors(); + }); + + $("#urbanizationOutput").change(function() { + if (this.value === "" || isNaN(+this.value) || this.value < 0 || this.value > 10) { + tip("Manually entered urbanization rate should be a number in a [0; 10] range"); + this.value = urbanization.value; + return; + } + const val = parseFloat(+this.value); + if (val > 2) urbanization.setAttribute("max", val); + urbanization.value = urbanizationOutput.value = val; + updateCountryEditors(); + }); + + + // lock manually changed option to restrict it randomization + $("#optionsContent input, #optionsContent select").change(function() { + const icon = "lock" + this.id.charAt(0).toUpperCase() + this.id.slice(1); + const el = document.getElementById(icon); + if (!el) return; + el.setAttribute("data-locked", 1); + el.className = "icon-lock"; + }); + + $("#optionsReset").click(restoreDefaultOptions); + + $("#rescaler").change(function() { + const change = rn((+this.value - 5), 2); + modifyHeights("all", change, 1); + updateHeightmap(); + updateHistory(); + rescaler.value = 5; + }); + + $("#layoutPreset").on("change", function() { + const preset = this.value; + $("#mapLayers li").not("#toggleOcean").addClass("buttonoff"); + $("#toggleOcean").removeClass("buttonoff"); + $("#oceanPattern").fadeIn(); + $("#rivers, #terrain, #borders, #regions, #icons, #labels, #routes, #grid, #markers").fadeOut(); + cults.selectAll("path").remove(); + terrs.selectAll("path").remove(); + if (preset === "layoutPolitical") { + toggleRivers.click(); + toggleRelief.click(); + toggleBorders.click(); + toggleCountries.click(); + toggleIcons.click(); + toggleLabels.click(); + toggleRoutes.click(); + toggleMarkers.click(); + } + if (preset === "layoutCultural") { + toggleRivers.click(); + toggleRelief.click(); + toggleBorders.click(); + $("#toggleCultures").click(); + toggleIcons.click(); + toggleLabels.click(); + toggleMarkers.click(); + } + if (preset === "layoutHeightmap") { + $("#toggleHeight").click(); + toggleRivers.click(); + } + }); + + // UI Button handlers + $(".tab > button").on("click", function() { + $(".tabcontent").hide(); + $(".tab > button").removeClass("active"); + $(this).addClass("active"); + const id = this.id; + if (id === "layoutTab") {$("#layoutContent").show();} + if (id === "styleTab") {$("#styleContent").show();} + if (id === "optionsTab") {$("#optionsContent").show();} + if (id === "customizeTab") {$("#customizeContent").show();} + if (id === "aboutTab") {$("#aboutContent").show();} + }); + + // re-load page with provided seed + $("#optionsSeedGenerate").on("click", function() { + if (optionsSeed.value == seed) return; + seed = optionsSeed.value; + const url = new URL(window.location.href); + window.location.href = url.pathname + "?seed=" + seed; + }); + + // Pull request from @evyatron + // https://github.com/Azgaar/Fantasy-Map-Generator/pull/49 + function addDragToUpload() { + document.addEventListener('dragover', function(e) { + e.stopPropagation(); + e.preventDefault(); + $('#map-dragged').show(); + }); + + document.addEventListener('dragleave', function(e) { + $('#map-dragged').hide(); + }); + + document.addEventListener('drop', function(e) { + e.stopPropagation(); + e.preventDefault(); + $('#map-dragged').hide(); + // no files or more than one + if (e.dataTransfer.items == null || e.dataTransfer.items.length != 1) {return;} + const file = e.dataTransfer.items[0].getAsFile(); + // not a .map file + if (file.name.indexOf('.map') == -1) { + alertMessage.innerHTML = 'Please upload a .map file you have previously downloaded'; + $("#alert").dialog({ + resizable: false, title: "Invalid file format", + width: 400, buttons: { + Close: function() { $(this).dialog("close"); } + }, position: {my: "center", at: "center", of: "svg"} + }); + return; + } + // all good - show uploading text and load the map + $("#map-dragged > p").text("Uploading..."); + uploadFile(file, function onUploadFinish() { + $("#map-dragged > p").text("Drop to upload"); + }); + }); + } + } + + function tip(tip, main, error) { + const tooltip = d3.select("#tooltip"); + const reg = "linear-gradient(0.1turn, #ffffff00, #5e5c5c4d, #ffffff00)"; + const red = "linear-gradient(0.1turn, #ffffff00, #c71d1d66, #ffffff00)"; + tooltip.text(tip).style("background", error ? red : reg); + if (main) tooltip.attr("data-main", tip); + } + + window.tip = tip; + + $("#optionsContainer *").on("mouseout", function() { + tooltip.innerHTML = tooltip.getAttribute("data-main"); + }); + + } + }, + template: `
    ` +} diff --git a/vue-solo/src/components/UserInfo.js b/vue-solo/src/components/UserInfo.js new file mode 100644 index 00000000..fa65b697 --- /dev/null +++ b/vue-solo/src/components/UserInfo.js @@ -0,0 +1,4 @@ +export default { + name: `UserInfo`, + template: `

    `, +}; diff --git a/vue-solo/src/components/UserList.js b/vue-solo/src/components/UserList.js new file mode 100644 index 00000000..b12869a0 --- /dev/null +++ b/vue-solo/src/components/UserList.js @@ -0,0 +1,58 @@ +const UserInfo = () => import('./UserInfo.js'); + +export default { + name: `UserList`, + components: { + UserInfo, + }, + data() { + return { + users: [ + { + img: `https://placeimg.com/50/50/people/1`, + name: `Knight Kentigern`, + department: `Dragoons`, + info: `A champion of Lady Menerva, you fair knight have been selected to pursue + the dragon harassing the good people of the Silver Lake district. Along with that mess, + you can aid in the effors to modernize this library and slay the 10K+ line code base.`, + showInfo: false, + }, + ], + showPeople: true, + }; + }, + template: ` +
    +
    +

    Heroes Wanted: Join our Epic Port to Vue JS!

    +
    + +
    +
    +
      +
    • + +
      +

      {{ user.name }}

      +

      {{ user.department }}

      + + + {{ user.info }} + github + +
      +
    • +
    +
    + `, +}; diff --git a/vue-solo/src/favicon.ico b/vue-solo/src/favicon.ico new file mode 100644 index 00000000..c7b9a43c Binary files /dev/null and b/vue-solo/src/favicon.ico differ diff --git a/vue-solo/src/fonts.css b/vue-solo/src/fonts.css new file mode 100644 index 00000000..c1cd22bc --- /dev/null +++ b/vue-solo/src/fonts.css @@ -0,0 +1,175 @@ +@font-face { + font-family: 'Amatic SC'; + font-style: normal; + font-weight: 700; + src: local('Amatic SC Bold'), local('AmaticSC-Bold'), url(https://fonts.gstatic.com/s/amaticsc/v11/TUZ3zwprpvBS1izr_vOMscGKfrUC.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Architects Daughter'; + font-style: normal; + font-weight: 400; + src: local('Architects Daughter Regular'), local('ArchitectsDaughter-Regular'), url(https://fonts.gstatic.com/s/architectsdaughter/v8/RXTgOOQ9AAtaVOHxx0IUBM3t7GjCYufj5TXV5VnA2p8.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Bitter'; + font-style: normal; + font-weight: 400; + src: local('Bitter Regular'), local('Bitter-Regular'), url(https://fonts.gstatic.com/s/bitter/v12/zfs6I-5mjWQ3nxqccMoL2A.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Caesar Dressing'; + font-style: normal; + font-weight: 400; + src: local('Caesar Dressing'), local('CaesarDressing-Regular'), url(https://fonts.gstatic.com/s/caesardressing/v6/yYLx0hLa3vawqtwdswbotmK4vrRHdrz7.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Cinzel'; + font-style: normal; + font-weight: 400; + src: local('Cinzel Regular'), local('Cinzel-Regular'), url(https://fonts.gstatic.com/s/cinzel/v7/zOdksD_UUTk1LJF9z4tURA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Comfortaa'; + font-style: normal; + font-weight: 700; + src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(https://fonts.gstatic.com/s/comfortaa/v12/fND5XPYKrF2tQDwwfWZJI-gdm0LZdjqr5-oayXSOefg.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Dancing Script'; + font-style: normal; + font-weight: 700; + src: local('Dancing Script Bold'), local('DancingScript-Bold'), url(https://fonts.gstatic.com/s/dancingscript/v9/KGBfwabt0ZRLA5W1ywjowUHdOuSHeh0r6jGTOGdAKHA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Fredericka the Great'; + font-style: normal; + font-weight: 400; + src: local('Fredericka the Great'), local('FrederickatheGreat'), url(https://fonts.gstatic.com/s/frederickathegreat/v6/9Bt33CxNwt7aOctW2xjbCstzwVKsIBVV--Sjxbc.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Gloria Hallelujah'; + font-style: normal; + font-weight: 400; + src: local('Gloria Hallelujah'), local('GloriaHallelujah'), url(https://fonts.gstatic.com/s/gloriahallelujah/v9/CA1k7SlXcY5kvI81M_R28cNDay8z-hHR7F16xrcXsJw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Great Vibes'; + font-style: normal; + font-weight: 400; + src: local('Great Vibes'), local('GreatVibes-Regular'), url(https://fonts.gstatic.com/s/greatvibes/v5/6q1c0ofG6NKsEhAc2eh-3Y4P5ICox8Kq3LLUNMylGO4.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'IM Fell English'; + font-style: normal; + font-weight: 400; + src: local('IM FELL English Roman'), local('IM_FELL_English_Roman'), url(https://fonts.gstatic.com/s/imfellenglish/v7/xwIisCqGFi8pff-oa9uSVAkYLEKE0CJQa8tfZYc_plY.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Kaushan Script'; + font-style: normal; + font-weight: 400; + src: local('Kaushan Script'), local('KaushanScript-Regular'), url(https://fonts.gstatic.com/s/kaushanscript/v6/qx1LSqts-NtiKcLw4N03IEd0sm1ffa_JvZxsF_BEwQk.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'MedievalSharp'; + font-style: normal; + font-weight: 400; + src: local('MedievalSharp'), url(https://fonts.gstatic.com/s/medievalsharp/v9/EvOJzAlL3oU5AQl2mP5KdgptMqhwMg.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Metamorphous'; + font-style: normal; + font-weight: 400; + src: local('Metamorphous'), url(https://fonts.gstatic.com/s/metamorphous/v7/Wnz8HA03aAXcC39ZEX5y133EOyqs.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Montez'; + font-style: normal; + font-weight: 400; + src: local('Montez Regular'), local('Montez-Regular'), url(https://fonts.gstatic.com/s/montez/v8/aq8el3-0osHIcFK6bXAPkw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Nova Script'; + font-style: normal; + font-weight: 400; + src: local('Nova Script Regular'), local('NovaScript-Regular'), url(https://fonts.gstatic.com/s/novascript/v10/7Au7p_IpkSWSTWaFWkumvlQKGFw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Orbitron'; + font-style: normal; + font-weight: 400; + src: local('Orbitron Regular'), local('Orbitron-Regular'), url(https://fonts.gstatic.com/s/orbitron/v9/HmnHiRzvcnQr8CjBje6GQvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Satisfy'; + font-style: normal; + font-weight: 400; + src: local('Satisfy Regular'), local('Satisfy-Regular'), url(https://fonts.gstatic.com/s/satisfy/v8/2OzALGYfHwQjkPYWELy-cw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Shadows Into Light'; + font-style: normal; + font-weight: 400; + src: local('Shadows Into Light'), local('ShadowsIntoLight'), url(https://fonts.gstatic.com/s/shadowsintolight/v7/clhLqOv7MXn459PTh0gXYFK2TSYBz0eNcHnp4YqE4Ts.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} + +@font-face { + font-family: 'Uncial Antiqua'; + font-style: normal; + font-weight: 400; + src: local('Uncial Antiqua'), local('UncialAntiqua-Regular'), url(https://fonts.gstatic.com/s/uncialantiqua/v5/N0bM2S5WOex4OUbESzoESK-i-MfWQZQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Underdog'; + font-style: normal; + font-weight: 400; + src: local('Underdog'), local('Underdog-Regular'), url(https://fonts.gstatic.com/s/underdog/v6/CHygV-jCElj7diMroWSlWV8.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Yellowtail'; + font-style: normal; + font-weight: 400; + src: local('Yellowtail Regular'), local('Yellowtail-Regular'), url(https://fonts.gstatic.com/s/yellowtail/v8/GcIHC9QEwVkrA19LJU1qlPk_vArhqVIZ0nv9q090hN8.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; +} diff --git a/vue-solo/src/icons.css b/vue-solo/src/icons.css new file mode 100644 index 00000000..6259bfe2 --- /dev/null +++ b/vue-solo/src/icons.css @@ -0,0 +1,212 @@ +@font-face { + font-family: 'icons'; + src: url('data:application/font-woff2;base64,d09GMgABAAAAAGfIAA8AAAAA2HwAAGdtAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGVgCYJAggCZZwEQgKgt9QgqodC4JSAAE2AiQDhSAEIAWFTQeOfwyBBhtuuzcsbruPgnQn4GlN7VX42Qjb4wBzhrRRCGMcAOA/dNn//39a0jGGGxEgpaX13zrMg0omgg2bCzuqC61GHIb9hDkbvPezcW5mMEMbjTpd5HDcGJ9vUkGa/2ZQRWGHY7Kjz3wCQ/CkfXxrTi655ggdX087L0zeo8K4sLEJD32wIeRD58fzJaerB51yRWDn/pS8+S76r3bRaOIfry112EITXPcVb1MftiySnP9KVkw0mLwJJy4dX3AfUYS+yqo81M/AtpE/yckLT/z+/n/N7H2umB+MTzCqwQlWFJIQn+IX4IHm3nozGUq4zYf+ppwSU3aPWZ6QdepAmDX63Nn3ZlZmvnzi/xKNEaukrsEcqHAIbFNh6wSGoqOmqQ2frvS8s2m4tDjAt81/j+txPkAURH0kitrhhfi88OAUFBUQUNFMTW86NFIzpUOztFIzM0NzWbY2rbbQLnOtNrN+rf1sTfutOVttP2t/ulat2X76Y+5dC54gPjWLTNR76a6/7HJAxOcG5DWYEHy/3+/s1Sf6sekaEhUaIeIhghG7HekLhZ5IGGiI/eRc9LxBRV1FmEhSgWGGZEYw73L3Uy5Tqi5UP+Sm+VUbKYAx3E0FcR3iY7iEQqY0SrBKFovB/pgujchR7nsRCESU/4rbVp9E5w5bh3ns/OvUJLdBsmMMoJFkwRejpQDi2ztMveNhB8oBAdjRbYxQC/nqHBf4XobzYQnxOLWFZ3ePrW3lv9NNA6Dj5eH/R4CFa/+SkFSIme1+oLsCfSjmqAjZYvJI6AodpyZX3qbaXxCCnmQa9G3cMAY7briRYtrUdMxp2Rk+gnp6gIrC9sA8cx6hS/E/5qxXallq+UkKtOzEAyWAwMCnaT5xyrk4V/sqyYFdDhiGdjm8DIb/fZ1fkg1550g2kDkJTC0lRbZehtnd5FE8pZR7d7vur2UBedMpKdNKMbD/v6n2tvcNCAncCK0a/O9EOoJy4hadkp1Lu/Ppf+vuzZ0Z4M0bDIh5IKQBQB4BoPQFAhtAgNoFASVAVKKoDdRKnz/kPAApmQK5fylQ2kD+IHGz5JBjG2LRurMrd99F56b8RWvnNne9m6I21PsHGA9MMrDxghQI+vOkSYHKRTogPCzY6f5AMaE88/zPacluvrTe68PDM2IhKBR5n74s75fk9dqpV2ph65SGjsAQmAlsx6UP3xtjiFRCpDSC5PhPffcYm/0Zn+XaIMoAQQXd9nm/d/ZfCd1r3hWPkqYSZ4CBGWj+3nkK6rTMAXSw5LCz7vil4koQwMesr1jX+CQjAM/N27wBcOM8I6p2e1eZW1qcBxCvhlAoIC5sShY068EDAB50NeYLAGBD5iefAWGYN69ow/MlDXyjLAAkAX9mqUtlyBzKBOziEDwOyETywfbb5tISUwsIcbleBF6cCf21nbkk8MSWiw7bf4c3tPHd2ZPNbm6LSmGSlqTl1eEYFJEr8taoSauDDjvqM6N+8KOf/K0//TtR2GLr5aonxCSkNnf0q/P/VeT6n4u6Vgl2axTP4XuRPVs+QHAtuGtEca9/S57fdOKqcOqKuRKuRbJ860KlhqAE3zJFGBfnFogERmA+CixL/z/Ai8xgNJktVpvd4WJCGReeH4TRcrWOk1RmSudFWdWb7W5/OJ6ac9tdrrf74/l6/+gBEIIRFMMJkqIZluMFUZKVCFRNN0zLdlzPD8IoTtIsL8qqbtquH8ZpXtZtP07ny/V2fwCIMKGMC6m0sc6HmOrctF0/jNO8KHfd9uO87uctBkahc1ps7E8Mn0n41OxPA6IMWoYsw5YRy6hlzDJumbBMWqYs05YZy6xlzjJvWWZZbllhWWlZZVltWWNZa1lnWW/ZYNlo2WTZbNli2WrZZtlu2WHZadll2W3ZY9lr2WfZbzlgOWg5ZDlsOWI5ajlmOW45YTlpOWU5bVmwnLGctZyznLdcsFy0XLJctlyxXLVcsyxarluWLDcsNy23LLctdyx3Lfcs9y0PLA8tjyyPLU8sTy3PLM8tp+TSQhXlRWtUFoejuhiK2mI86os70Vg8iabFbDQv/hcti9+jdTEXbYvFaJ+o0TE5R+fkEl0TEt2Td/RMvtE7+UXftDz6pxXfDLQEEMAgKChDxBDDMLHEMoICBaPEE88YKlSMo0bNBAkkMEkiiUyhQcM0OnTMoEfPLKmkMkc22cyzhjUskEsui+SRxxJrWcsy+eSzQj31rNJAA2vXCA/U7YEHXBPE5tT6ILsDENtT+4PlDsID7jDE3nQ09qfP4mAajcPphziafnyou58gTqa/H6rv//CAW4Qv4McAxMUMxuXsHlczP65nr7iZl8ft7B93c2jcz2g8zOp4nJPjadbH82yKlzktXufN8TZXxftcHR/zlvica+Jr3h3fc+c/+Xm+dPo7tPEs+riDZybx3z2FzoCn/0geP6X5fwpcvHE0jwvkp0Cp6b+LxuqpTpwIqciBtiMCo68owL5iUVECM/ADv7JP42hnym7avfkcRMXlV/D2VRDumgNX13+ruaFg0KbTjKNXbY+mpFk+SwQfCrBAfksbBHq88b5yHqylZViORNMpR+GTreTO5rNjRQ/oQKVpxlKarwJPiafhzk7EKIqKCiqiGLOiK49SVTJ+yCk6pOllErOJbC7sr2UvswNFZa9DfOXSGwtqifju21Bx1yJVqcz31qDl4YjUCxhmGdMyxtROoqwWFDVFQZ1VtEANC0NO/6moUjo8sRutOhrb+LtyGO0sYucPRNPg12o+FAiIC9dYikrj6fZclpgZSVAYku7U9+2ahgqmyVDLfzIhrcXBD4RDJhBha5zHcNoLQZyKHQgvi1FkN/CrgnTFBIoBEhGf0MpLTrwx3iP3JKYZ+RRgpo8y/FNRncvySxcj8mcseQR1yvD0XSiqaW0r39vqD9Z22/k6PH17YzlRrty/HYV08T1LpwqK0jN1a01Oqi/RLYnLKI4KetryorkI1TWZ3yAznom+CWxDDgVStsp9oTHiwBjkSJ1+Gk/vzVtgSlsKku1RdDp3c+dYKuTSu8BUPDqGU28GQlF4xEdZ/s7Wy8bSz+fPSvuw5h+H7pJVrKGQHyKrdwMSGknNPB0prbTOjCKrK6c7//ZgCX97Lda7nvWdSH/SQJ4yZTBtKGM4ayRnNP+NAXeGknERRZFZgUY+9piekD6hQ0UmUDNXLFvZZMZLggG6zmg61jA1fqTtxJq+KfNXiyMnM4rDlZYZ0wY5dPHQDHCzifps+hQFzi+awiUGyjI0fvH824e5p8dlpsMOKs4BdzA00SYHklbZedTn7viTWsCLUETl3PJFFBkdt1GLBCldNvs4vw1qcglh4sJdufhxoQOH9QMcswTSoNJRqXamz8N5nhbgTl592mi5HzV7hko8AV34ZyO5fPd1+Cnfq7GbcrYamPdueUfkkjCifT2l4hTh0cOoQbyBZJwpPaZZzaTX3c6jd6lLbieFLFSmfTp2/QjHnlK9ouCUEUlI/RhTPZo9rjXZuhgJKuuMoTXwaFT910FqmPWw4qFBKaat+7beOQ65VtN04uOsB5SEluJrZUrtNWOW8xhq3FFxt+DNNFDr2zJ6Ou7xvKr20iEHrWYVLq7L15/Q0IF7Hpfduu0L9KO9AS1Bg9n9m3ArtLpMdOhhaO3mJq8m2CI8NPrNb4NLzpHtRFPLnvtu6CinyqvoEVSsOZHXtzu4wILuxzSLoTOPeb1v7x0V11fvhp8Vw8DDumNIecM7euzE1jGx5vF9TQS6Lh3ERFOaX6e95DBUH5liZAqytZhnLtuao+zxUUEcLM5zdpLWFz2cnotGp6jR57I0kTg7ckX6IG8jzo9GlpC8uLOSdaTMw5SqEw2tCloHkDEPU6ZOLFAZbQPImYcpVyceqJzuAsifh6lAJxuginoTQFvzMLWtkx1QWb0LoL15mNrX7R/gj6aO5XBnzE7VO1KzR+m4gdigMDoZtQjpNGvRWdYK5xEe6WKmTZdZm66yNl1nbbqZQeg2Q+guQ+g+Q+48+A8P4PSkqJS1V/sLz2EFui1LyW8CACL+/8fO89xxYxmo6M5s9p4t+TZP1bOVZ/M9wmHmWEgwAwhj/gAMQDkuk8qSvzBaKUZpVwBil3UhWBmYUhKn2NPyeSVc9mIU5VWwTL4kNfHbli8aNqIoJTR5GIbcs+HNR74T6x2BYrLkl3G4ZIHeLF4456hdjO10SFIrAUB6UcK3SqFELKoG/0/frUUiv/zZR5AOyaQBUFoj+k6dMKtYn72oeUnJFa15DSatKQwCHlg3Et3A1xtBUxHSJEb97eUlMY5YlVUl9Rz5y9KOVGttqyBsQFGMDAq2Ag+mHT49Dx/A4XV3B0TaaGN0s7go/jHHm1xu3BKtM+Yyliz+jhoAOJvUROf16GjvwAh7UVo9GmOy81hC1DJUpY6nIE8UrgqBRuM0M81WxGLow7XFntiyLJPkgcPzTXqNj+mcaNDQKDphzlOGvcNyGQYQrGLBsWsQ9SgjAwTrjCkvYW1Z/jFiTEjUm3xLxnWFs4iUsb2lWZiEGxeFBrAdJtqaXRoCb2w+74qjyeVJebAtgopzlT8Uh0EgxA+Y6B+qrtjRcrzAJLsNiYFmioCSSFwlagr+GCypWG87y9HmKyr6GpM5aT7AnRsOQ1Gvy/n1l+OQbuzQZTa+3EUgmUnCTYQwr2tOHOGKmssxCT250okuQ7vPDusLiwBMgkyPv9UAlAtkklKV2gHmGFJJfgvUx3EWpWnu6441VJsMPCJM0BX6w3CUnH4lrZ204FOJKS7Rrs827AlUKPAYpQvJ7cDQDOnmZcVkj8fZ/4oiUMKDmcQDliT2kefJlPfbXC8GBWgrGpLbfVnGGRcprDF0ZQEiFJjEsUgo8bF6fWJKGmWjL60bAM3U5u2sKbRzWJS8N7B15EuZDSr+xRdS64baLShDJCymHimlATCk3pBYWiY6u2itMiZ0wQBoxnEc19JQLGhnjlKKzVueYbTmR3tcc9o9j1xJh7JXj9Z6GGNYoDJZzRM+Bz9QH9k/ij9p/nnps6zZj/Wnzg+jj60/LH55sbtBMUhq0ZJKINfFbmiOWNafTUVNagyAWuLsOcWruqmyIC3b5s2kDY5nxGkui61sUBWweSWVWhggUx2znmWkvWxmgqyYGdQB55JhwgeCq5yjqG1yqhQpwyScmQLQdOG2gIhyZAQMMTmxjMVtUexjeSaQVXsGDekhq7GXNlioMI3kJN9WlqAMFSwrsKWiVL6nZPOyLdJlT1lEtXcUxzFRRgmXM5glKSlfLGrfyc+o6ywrKEf0uTYbCMOio0Brh1NFHe7nqrqpXUZut1mqikAi4ZrtSlU1GJA1G6xGJyf/YdHNWLOsKbFDZkVVCyuscFu5YoIujVbhKs60K9AwOj1zm4oK3U6eoWB6Z+hmERtBY3sJO3ttynM43xeHa5kyRebQ2mW0lpLpGNrAdiU6laFMC9tmXaKyAQOpGgy8onAlhjBBno+bXRiaclxaWcZ61DkGIHpwYTxgPRjTfSF7MEmh4LWDVME2A5yJwgpFmVlSMbpMfoPaMApYl+0sCtOZQM1ti4jCo4dZSikOna/AZEo8j1RZH3pf2rtziBOzzVLh9EHPNnZno7Gyb+dgdhB7dC049HQwWuH+7u11q3WMiStHVzd5Rkx0Uzy9NOS8UTTj67ih68XX4fzKzLXZ1fkBWGpu2CvtrUTluDBl/KXwaW1e8XJBMTGDXLLdfAyhh7IatGFOSlC5RPUxDTAJNG8Pgfd4YO/Bwwiqjbr/EAE2PcGSeIg+a9k3MBh4LX8YVldop1BFdzPTMHrf+b4Oyd/oGuazhnlSqOn9HQDDVAezhriPsxBzoY2uU9Rle1yLASr6MOh5Lb+vTF0huEmDguzexZLu20kpOsRpCBAe3V21WWKznhapQVWGQEActzylfso7EioQCIYQWaBgclWfSPpoUd1Ai14QFoXQvnGKZUd8mgDKRIN5fNVmD8JDKtg2HClJVZMgzYb0WOVf7AtcSi9kN5t+vCPhDcOQCvYGzPLKvrsEZVWrKpgM5eaEHpoVAglvFG6CIZDEFkKp6i5toSxV3aUtl+tHbhIEDQitbgCoFQayp+dIB3Ooz4nRbOh1JxutDeo6jhXsVDqqE32riEHLz6iDlC0wtVfiHF3gM8Dlp1CKNhv+hpGTjfM3PIW59d53woYrHigx67hP8yrXkldsCVAeU+v3qUtIGoYpMeXb5FiAB+EV+5FTER/j0mv4UJuwzxWITFPFn+dkDNOyFTWYSAHluBhTTO4WZPI/V/+xXjM//f//L+3W9CeJWzJh23WTKqD8CzkxJaYzyS9PTAY5+nj+8hRHSbGzFNh1iEi9W4bMSz/IBdJTySfskp8hpPsCR21LxKtkCzwudnoSwW+cbvIwn2/mxxZmSDNrWvQo7i3Vf+QSIZyZ9za9EyTw7ULIy4ckVIVV93NZKyYyZwBhss9GRMj8rf1H2WJLWtaa2rz9iR1BQutjSotP44/aGeIph1JcGH+C8TEmmfhd8yQnuGDNivjJp2j13GY+RLuZan6+dkUycgqRB5LfFr/BURPhv6st3EElW78iuRu8FA1eM30naqqIbCLSo6+UsqiIxKsTZ1QuRA6+FTmkfdDmnnmEG4aHra8Duz4jx2e7wdkiphP0L1hQQat1r3i6PjsYB/Pcl7zb3vFHBlFRDse8M8Ov5/T2dualr+zej7zP9W6IyEo+wWSyl3/166d0L8/P31+PKpVsQIR9VbBJ9dnnANFUqsMqi4iPBttEzJ3MZDn609JVdaUciO6aOJhqjw4c4+SCIYusgkuInIqGi/943R417k4HeAcO2T3WqoOxmtp8iH2cFNYGuDKg0RLN1QtQv+EbtcQMeT4APw8EslMSRJsF7nNJop93jqgQtILkfD8dmDnmg4Uwroa1QC0xIvZgaoinmK8yhpI1quBwGzpJsrtlrfpTzabGV6z/NJ0BWbVcEEmNYIbAKzNBREom4ZjmZVMwHtNP3SgFvjAGDU/so6c0GuSPcITbQITwyyKxuF+6G0WcQWz/+RO4483UtELN9+keOtxBFQRd0AM08kfMWnDkd+R5W3fcjigd/8FpSTsIikfrpnCTSegqq91i5ap2my46wJ07J9Y/raffVZ6dPcsPKb8dY7oedUUn04/cU9hTtiD/Yq/27aB5W+5kTI+YBE2Q+UXjfUoYv9Sbj6+9RgaYnmH60Y0BpXT/hSOevLcWab4BsTXTAZ49xKVezu26HcH/lcsGetXI/tf/c7pqRTHsggL73vS0MQYC0YEQ6SZxN6W7QPKSsSbvoqkvTIDKcI4LFt6Fa7YFIsUoDzZJV5Q9jtEKeZL8NwwoKp3my/sRY4NTvAhQCcOOyepi2nIizD0npDSKo+jbHQRvZR7D8Hh648XidpAS/dbU6xxX0M3ZZ0djmwdXqKh4i4JG9Z9tgDuRovicUxVzb1NKP1ymSnquDr5TABXUSPPiBxgXzux/3hmnn7EFFdXEBMJH05kDpPXASb2uGVHytwxvV7NLS41mPNj+7qzP9uK+jKBYJ820gcnc1trKxs5ba/cjNb9nrhwKZ4Q9lFtJIaaOq+RvbZOOFZMsyUClvtBu3cI4ZCL6G175rvsW4uXBXVOoUJICgLdoCiJ2EScY3zw/wYcwtcJSTi3M2XxvffJkYouHj+8tFrelzJIcTUdlftJ1TCxCgCGbGEhQnczmT60FneiK4XHfyK1tukQ8LHa5MRnLqctWvRzENedqWK/nt34UEOtpVUMjc2oH9HQTg8KwiYx9qADSC5e6WRDqhXqIzhtQdYENzJzFU2vcnh7l4dKBXV+kHJY295yu8o7FGky+JDlY3NyLTOE3HOlVPseglMLrCAZB1crx0ubphUumOfH1jNv0Y6pOvIFRXNP9xi5ePqKwt1pzyhbBVywbHy13/aUrRyIJomleU5Vkaabk9bgJPpELZpJRwj2grZJF7vv6Q0e9ou3fsCDJb0+wUJETUUZA5AWWDA1sWPeS4rAZL1ivKn/ChRMRIBYCrxIg3D1UpGyuRwQ0VJJGLz2CrarYtI9Y+SN+j9WF7eiTbyS5mtT4dyL4pWCs8cnX45w0fJrclvxlPBdkVvVccmKyNaazaUpmBs0XB6AQrnoRGmao/9TtuX13oaN44+ZMx47lvuBBJExXDIc+gtnVeVt/5GyB1HzrFjKRv4scsXn7Vaq7iSm8O6vajjTnKW9rgULLIuIsss+zoW1c2tZ+HQNuIkFSy+8L99jq/XRYg89ceY75WOIS29tTv7rp3hrjYjIReIj34DAr80apMT1Kv5o5lBNGF+GatvmJnbvvhJdM4vjMp2uuQI2x4JjuqbzrKFdNF9TDTe2w49Kz3rWPejhk7A4N6aL9znwmPTAt9jQtUyj+5nKDV1PFon6uOMDVfJoEtZFglscGRzMXdq54xkw7X1qeq/4124t7x00dD4/lL1m/7hFxLHDhTI+u2lzUPWKoxBN+UhDFB9geO+TMfSfScLgNH1kOFMgOiZ8bPoJ0wtyA0uMWeHDJBFrgBcSIFiGCr1ARu+sKv6DlahbwavOxFbGj/NnDRxOwtPD/C4gflAHZ6ChlKckgk7ak5mQUckymcJHwUafiD5MhAIWYQlooka+5W85GqUDgcmdki/THQkRqaxAnT+E4nRur5duNhzw1lyLoWtBZR4ww8tjHgNxuQTOxRkLKN4OCU8MS7F5rBicSk4VLt76vqT2BmHL61KpxCBk6g6z9og5jw5uf7qaUeGh43H/yvbGPj6gna694TEV6e4QmWzbXW9XxZdpmpmNlyqDqB3XepbPdJXXnWywCEghCN1LYY1s4S7ZGeeAw3HZKaHebIJrtA1VKtpWMNOeRbC/kKfFW8REUHTQ8MD9NfZYf1YVYdhofJxiu6MccKxz3/1fdhFB947wxxBqep1bpby6P6eYo9jL2Cb8pJ5iWHJ3nX5ue73FhBsWj9ZjHE88PEke87NJpYBPF3NMRAWu09nIrc2jHmvyxSd/jBvKJzjJ+EjaNV/4EOxoUm4XwhnvAKsDFcZuz6J/XfZag4yK7AHvbt4VXp+J7J+EhYldxj/CPj1zMx5+/PmG3BtbQ/s1CWUstEitXk+uiZ3Kaj4HR5Tqfi4XxUAF85cI3B5pW2oOlrbDQWRpeR9ywVimC9Lt220q710Ne9sPx9HbaA0tqI3k8XBimmzbdhp8j870fzExT8qaYrnqcpQrzfUpvwzYAPOK5wSzICcnIfq86PntNxKJ8hydjjm5LzKXnfLmkqhOjUV3Hp8eFs8pxQ610W8A1sLzYvtVNnNpVDE57B/0ze08vnrPbXifcus72Ht/fw76Rk/dK+YzCBicy8ECYKu6Z/vBkDh9LUGDb8Ea/H52bhHuHc68kjI98EKjDIm2VGRzWcY6hygmoM9t8jZwfyHJ6KCCjFRkwZjz/xaW2Dzlpv81ulSTCeL5KFz62XBepmaArCdt7E45sn/eM/vZbpFOWxr0bNu9Fmir0E6Xar4V2M5uYJokPSi7uUDPrXhV+imfQJkU7NR2ox7oH4OYNEphxrndY3KaaxKhx2cDCuZknH/1FpQCZPF8LlaPeMafEn0kaU6j/UJzEgufQTi1eysU/ky2QbhoEjm7DwudAfbsGG5X44JIjyh9WIPJmtQuKvalMW8q6rTpRnQAhEn0cFQjQAp3ChUU+KkfN4q7heeQB4x/gsd1Qd77wIWXlDiB8pXUfH6nuCatU8VCHCC87ulKcOlXR3XxidZ9+i+0a3cloEd4DX8Pvj8FuLWE31zZflHVeaMZYHh0puOAN22OLjlxL4iZLlj8pBSZsHLXZPYS+12bXi6j5g59kBVBsKvfpjLv7OiRo5MmU18g4R/tjwkFEViohBG2J/rzHZLx3hsUcSLiZqXbvIDNNtljA607UsOCVGwgesp0pzCGrpMXr55yMhZL7V6jLlbuq2prViV7LHv5r1TuM5DeYvESSN4rX6/ETbTy9EUSH3Jhs7rXJ/OMQE9Xy6JuwFtTtxuSX1Rt4KdJMrRmlyTPVZSF6OhNRrpjxU5QyzaZOIj2Ni0+kxBRXsoBLjoKLOQln/qgqvYdQCodvj7tvggWkc42/1QCRxx6egJF2wzTrzwaGLlO5WLDKQDgoCdOi1alQmiOE5kJ1NpLLfokl/TSHb9jsSBboQ9ly1ObCG9g10efv1beQiMyE9JBlJXJAw4TuAh1vMpKm4FgqkI9/ijTmccaBKJJ1AVWkGUUBFH6Stz/zuhFNGq8V6YrVCoQ5FN30RlIhs+jc6PBBwX4Lz6Xnz19BvvDMtff3ebWUIkDxBqZSELiicbZJhtrFwBG8xPMBlIWHrAcQEnfL8RF4qhrShTsIh3gZw+PMfceYBx7P+dpR8tcnNZaTxeQYSlAbEByy2ritiTwdJjzu0u5gIozwjgWPpCf936jejXOuIntkW4fYHgPmQ2oIAMd9zNa/pJIUVoUkIibXjNk2iI3WEKZqwGipHXKwV43vqjb6EadroRFkvZO2/mdyMbIZwaG2xCiucDxnBQUFe8bpZ6EFqW6c2PudtZAl3zuc3u7Z3iCPXUGxITG1EfTsHvRRdKVCgXCYYxGnGOCEnJ7rL1Cwfcdn0KoywRvrsQgBDwnNAtWpPzKbfzWaAT98otZTfa2E1MMNqp54GNo4nO2Ru4qigh7aw1f+T34L7exdlHz44stxxH5xoGceftTA9ynln738YXt9wt/c/7GVil9vJa3f6O+z4pUQouJ64FMM78cYuEvJWMW8cH8SI4LqLR3HK3Wl6myv/osk+8ZHYe5aCb7XJzUin6c07Hbtq+F2u1+gXv1hdP1vjNkoxOZlOp6+WPaK5TMYxWSKP5v6bVO6+2rfg5Y3Y4exTp7+hFTvx6iPZePgnxSaIHiWbKqpomW8mxdD4OJaNwSycWXlIAyqhD/l28paObCHc1XmjmvV0BduBtz7ORA6niw7MCZhKrMlvnk6xXK9EWJOv59McYKED0z4YmZ+M9bJ9+cSZF/kl8hm4Ojnzc0gJm/TKfJFiAaCMnDINUA4CNUy3Z81YWZK9h65nE1eLG5HEPs4NmSA9c6fCjNFsl66nshGcN+1VbVhLZ8n15SjmkuxtJV5GPOY8CQTPLAJycWN7kMnC+kJpK6cvouRil541B+L6Hf2hp8/7skW6bNW550cDrG/00P+CN7pYJ4HyPPI0fM4wnrIilSQBVBDXmrskZQ5RBzDtI/ClZ5JYsp5MvVxeLYLi0rwyfKz79Fx8UjxnsyR28wUVT1ifm3GmvnOVtFQRl+ONpdqQDiSc8zrdRwFg2ez7dotZuQe5tUV+u42pZs+zhkzwIfr/SA8MIlT/AlOp1i+WNuFTxv5SIEozG3Ci7e6pgKyK5s0pFA2+Fir+q5hnSlTobsBEps4s3VQPMio3aogfcv3xleaTAYtc2Swg716P/ITPqYr4ag39eZssVKN+E1Ue+pYLcCUVOw5LuLih5YAyG0KuwP7PWhWo297yj/vv/00xRMpJym5zRBOhoBSUVtDG1oxUnJuwuFyCox5xG6WhfvFbWC6HUrmE4cN/dYEzYCE1ZTvWnVvkHlcTlc0HFtrjF4trBXvLDSKDxRXC4+RYjMA6REtuMzZh28uoNzFhz5EbE0S00IhMH/+L3AkwiAGVFLtwd7yAul9JA7igY+SzmvkWl7sOq4SfEg6faY+gMHuUDojSBzhIAanwZwlxmBWjs0rKKFtOjz+Rn/mjsyuzrcKvOUDaAADMPlagIdTtjmJ4jHoCdchuQGe9u4QnOrrvaLVqjV4KhUvCgwQEag0g7N/mryR8ufP703Mvgi9342Erl79fHZ1VqjYWF+P7j7EXfNeAxxYRpEE7b+zgMfQCyPpOQ3pPR3qrArKSv1tQ31QzR8DlZ9q6PUoGcsFvVtIwSt7IBdXJzU9V+dJEDKV3iwS31xY/R6Yadtp1FcLz7+RMcgueDJ/J8UTjzJuDYc68FjQu/PZiF3fFXNZpRr0MMHv4bfFue9NSrLmr8hN3Jmw3lwdTbUQe2iXYjGUUZ7RJ5VrkoWKk6gboVE8zrvz1xG73i7gMlVmi0K2LRlc6znTuXOcGXwd54PuDds0n+Dz3XeSws8+S6Pm66Jn24a4F2czUeSilGgnCqyWTdUKHUgta6HlQHZVD/MNk2qtwUxgaqxVBe/dJKEShy70ZSr67GHMQJHcZ8JHJKWxUC4OZ3xX7xrVDXF6GxZ2ll14QVQqm700M7Pov+jiMu8/r9Es+S/1n1y/oaX7eRaRu5cYdv6gx+CzpzYbXDG+fZn99m07byAw0L4vsbJibNw6e8u6fgz/omZs1PpmxGobC27Z99vLPKu/v23U1M3r1uuP2Ac8RrY/yvJwg7652fAxuE+rbQGPG1r2aAfARoOpeWsqn8svDZk4zVm0WpcCX5m8ruF5nA864oNHIpNn4OIm61zAol631PQRe37TpiX/Rb1+0X8JL3AuECjy6o0hsWdySQ8KPgnZ0s14XSHj9k97ZgR1w3Tgjz1z9bl2/67X/757bpN1uunwF+mx24N9JGXzuLVi7GUz/qPXyYtRzQddUNAbkgm6odH5TXo+JdcMYUpxwg5YahLjKjFOIC7ZMyQEh7PqMq7IpnopTs8RYcFlw4DsW7A3zoWNqSY8IrAKGgXiEMDKMHzgt+9dY2RjAt/w771w5XoMAnIU+5yA566PsmSETmw5LIISWJ76XUI9qGwWxfv6MvARuIfQqechDL4vpkJEBJfxQStOwHXyimGGMvHYC1Rcmi+GMfLtM6xeJvOV8c9WvjphUJZJZA0mBev8hSkiCQeXKvcBt9lBl3yFSBCDw3niZJD6289lh+Y3hgisIU4zODAKsh46E0UMt+d1AjEGqylyrLjrd1BkC8aPlLF8qz13HazWEyQV5a+Wbx0wK8LZhPF65CXE4nbhKxhQu5MTjuUC4uORhF6GwLEEsjBEiThvE4ACpzxKFMhhWYV8axdMTMBFMLyVDBCrqilgsNWrcRWPk8LbjAfBTC/g3VE5p1mFHA4WF1oUGVkAVsjyr0TgnJlMAtgJ5XOxyhERi4UJZnHlk+BuJuZAlXIXS58K4WWdPWu4veTPTc5aI0gFb2BRfgrgKWXwGRhAhhnZhDXp9UY8IcSGtgMFBmYXyAzHgA4Q584J/vR3ovtO3DHCok3Iv2RhL+lmX7A/6JYy3ItZzjTBwjlkZ2fZMxzwzOCpm3PFqUg1bJpoa6MbJlR0lAY2bIBv3xR98gAcIY6tUppn8Sd3FstEahI6JV+em864Rk9IWB2fxZs/87x+Ux0s/2j//b+Zh8vu/LR2IzcPdBXtyPWuKc+VeNJsAcvSXz2EM5UmwLuSWEH1lJxrD8Qk04D/QP2ZEMzRZ2VzIlxgyLOvlxLsUUFyZ7Yyr00d/jIS/BlKJLrHkZ2KjE5yWgpdGjmxoWZqCc8E5Br26bX7wH7Dvj16IKjpj3XVQjrP3n5hmXUyx+jLC2UyjeLQ1lYFokJmEfv1F8gUx9MPFRCkpzx4ivBYxPnYH3i8QqxIUt6P2K5bK4ILAjBMHEK0lg/Js7PlTe8OQ0Q7kUzEexCqA+36iPBNPobg8wSWnoNfBQ8GBg14GOjHQ3zBFN0w8f7aYjk+Zc6y2/l00f1wbnW7dT+8zvmKw013WJX7htkfdChnBiecltjYRA09ja7RYLvyC36Hly0s9Hjd/ZUp7z7EaXtPN0y0wSba+zZXRbkGMjkcRmrBkGNLI9Vyg2gnWrh+A+47Fqm3XxaDjXrTPmx/8r4WfaYB20267QGj94XLKjpq9lNg9BVzmX64RtnUjDTKmcHP59XNfZnu3eaV/u859ocL+kbXvlv0pI2OlnFWAQsLXZ7dvYlK/MaSROD0xnXCijN4k1uf8vx3Or0RNmqSdG8FI1SvN8EmDUu6MtPOFDHKMdKNaQ/DQIOvQxNckeP9R0h2zMDdBbqkG+nmd/M2bNBnmIPIIMjAmBFjsJLwHsDD/YuR04ON7HmNZtF/sf+kkb06w8kaoXZ7y1zgXNEcT8jXOFzHmHgH2SJkwMTSdINX4WkjJPmaOuEjwnGPIXhxqKCFqAgxQVhh/w7czfFvBdPz8z8C+xYKfjx4sKTkrmI/3s+aErujQEU3TPTRUVomvr+Vwh2TBiPb/qWGTbD5gj4BzGLqZpppoIvgQtZgJ5UoQH+TUrJApDtZhDVOoaIAE6EXwRaULsB9B+64ALfZZ6aLAHkxG+Db1kBHgwcBQECQ+rSVCRc+44lRzBfUuds3gthUyjXJxfcNCw0X2Q4yDgpi37nxE9XlF46f7cMpz4t1Hzh+b5Ct/IGPX/JLvy/5/jf+4MfLX363wcQ+OFi/PR4c8j8sfvU7i9vb74HtnXfau+7h2/chhchIVOiXX+YranXtMcWlcW3gcXGLZX1LZD+2RWopFHcCSbWK/C+/VKyKYlDOsLSHNmUkXIq8ehzj9ggZT0/o3KRlnYFGdWGQHdDDiEAgTA2DevBrUiVlBmtWyD/I69pzGIp9UjwttWZZDA9F+vjQHB+RJExgws6WT9i+mChmlLFhu4QrL27vuAe2t5XKuZLe5kysmNmD+3mPWk1Z6ovPz1f6QvYSJVguD5ZO//mGdSJ5B2T3heyQPeHbn7wdFvQBod5kW388WrIy5c/wUiqHV5PdVlrEWnZ7Mw6MKnnxsS10qMmQ3LeRfig1a70bq4VJfSuPm+r/6eoK4f6qJmMnPxZ9DOxyxqSkb1e7HeVELGSzThjWGXIel9YtT6gi3WpQlEjyV8cFyvPy3p5Ip2x8T7Qz7MT0MlV9AZsm5j1ohC4dSOethH+mi2Bl+vpwHmLnPuorRHoieq7zwi8ZUZQTc5FcsQA2wW1ivaEa1Gup61DONEHG/qDjcGZwMvaSrnpdnzm3dQYPnUTWiQ2GStCQc797fdf/QjDXFXV03O3sAAvXOXGmVwqBZKk5XaKpcjjmz9XWfA4s6vUKBpOhSE29sllvADZ3nzvnHSiYwQR6KeTTLIbJ66dfnpkYQtj4yy98YeBSgNBLrqiMEfKXU+iGiamptqkpOkqjSyAJBNR6uypyMZBsp3SnNP8rxXIUURxI5gUH7ilf51HLn9UUGhiBUXrzHofCeBdcfnSzHUSH0fUe7yF7/D9mUu3+1AUzgSuR8FqPcfDbJ926TRhjyAoQNsVX7b+TECYikOInEyN6nVzMpwPFUpzz3S+xU4Xbtc5H4ktTep6zq61K1sqCiO2T9sd6W3pFiLZ930YRFUNDCnoPwRLMu9++9vqjmspOdAy7Mv/yh0cdwxVtdDe4wOFQ/I/kkbS5X3vVfoW5/02Ft9y5tTRd6qIT9VDu7XGivzFHr47ExT3W/7OJ8SqVoO1r0uW9FdZj8rTcs3qxGoXlmiQjgO6qjJRHFQdoNStKQWtAqUZjCdiY77c5KYnuz3k/jy3JdQhUqS7bY1LTe+VWq7Q3Fj049jUsyfGnJ3ZUacAH+olvvqHbsxYlq4zchQJGTZIcMYkTzmrXAOnH5Butql7s2bjeamuXPCu1K+ZfKhU4FBMsTf6l5yaXHga73xJzOxMOZbtwgxybnRrcnIucQ8nwMuTGOy9dTqsihPVF4cnLLh2EcWm6wzbho+nvb0bsExjyq0fzyf5QsbFJOV9vX8On2khzuD05SnfLO7TlsoagljIGcY4oEZZ4EeECkT4qJSqz2AhtN6LOiRN3K5L3Ycx1ceykAbXtZecpu6guRwhDPZTNxk7tZB2xPnns9d2jub8/NDrU02HoIjtHdh45ZzGEs7IGeHa/5cZhwGNgddYgb6C3d5VZ8aAHt7Sk96MStnrXB5O6n6fM7uZ1s+Fuj4CWgfLUcjvP3iMpzUIm+YEe6b0oIMrR1nuUKHJ0cOhcnUU/4pV9JSvLPclc2R/3zLW1ZiCnuKMdKE7U1nygE6s++aQqoyWPr10bJjYYxCvXqhjZ1mWYB+Qj1+bNQB6gAppBxle52PaT188LUoG3HlZavacXUwn6/yxL5gFGJC0czCJcdfnAHM8D/hKPjSSE+dGzfDMiZy2owWY74ZZhiTiOexwzzcjZQxfTpWJ4Q83UD3cgl4dxjB88fwgm/+YC16DLWAwxmcRAzYy37/YPjjXgI9wHT3qchXrm73+FlrFH/ko5Njwi/u+fzV7H+b2RfGf4louj3S7D/qkCl7lXbDK41OK1rIgE/3hujCuslwe1MFNbUDKpPhvmbCBUEBhSCj0J8PUQRKDATp/vRiUFz/X6yHbP5W8zxNTVLiJMZ75voJxmmIH9FtqndR43PfAbqQE8mpv//3E7k2nhYTku2WJngeCSMzrZPoxFcmkkNUCLXvdLoCn57ssS/BBm7/24b00s63ogNcLP/Na8Iosi5OZhd4g34pNvk6Gecg5e7+QNuINaCx0l1wxNnvYcdUw2exgmLFNSkjLDElrwM63GMfmF55mhyRp3E2yZkpHiM8pCafZyV5zeSQBysCkW2CS7rYhWpgt/FCrTo4NJBo6p5+F1tepOgmql1Cfb3QCZWq9l1NrO19iwn+/cfhpsqPVJzDZjIuWagH8o40pcp5QL+CYJGCn1J2X6ZPmsQpsMSHnhFOm711EnnxD+WMXVgkqXtuaFCq9K0+t3oZQS2So2GKpe/+I2Mcw22+fg9hrwQm31ML6mQfbdqukJTYTcjPGXpDAEST58Oac6WBnP8ON049DXy0PHIHu5AOrBjwmW8+xRIyKJbACe+W+1JM1LyUG0HD9Ll+4QCTbAx5jUS0sNsuLo4mVKz8QoJecsKYtIF0W/ToEHsrRz9k4XrtantqW5DvBScR7dF4iG3ym5nraWfW+3b89MsfOjnPCBHLk53Z2t8Frcq6NeR3+qrJEjFSakg5Xgll1SIg+Xh5eUxK+uxX3gPLu4QnT+iziJ0WibLUPDnNubmsbGmpjpS+AtLTtWnaK6I/nDFw8pRNXVYa19x6qqjvdtCPDu6/Ne9F80Gpf8l8hC/soDPH6+rdAonv70s6dSXGzy/aOacuiv37eUNpAmJ7vp0NoMV4v5QpxamuR6xWM8lxR/LuOfCmYy1T1uGBFX/6PhFkLHzRbXjLUQPDg5OcCLicoW6w1bxi6w0mTY/Ey6V9dBtbnF9N6TKytMsPIXrfY6VJAk3t9F1FjiGHXEEYhe9KklRWxkmJRx2aGwsrbio9+2FsK5tYRXnh5Jry2PNialUeOfeWnQZlJn1wYx43OMEOCiy2UpUQp6gUomhk503wepKaTn9ihfZzvDBZx5ioxWXIIRe9QIkIGMRIyQ6Wo/vFtMqAlOo6VeXPkLShWdk0sZ8leJpqyyaFoJoYtANfpYpEIRzaBNpONDVeLaW4VGqqQaKsHXSEy0tacoPKHXKskEw6sV1Vml/inB+5dJguU0ue5K49B52HB1d1KNFH16YXAKKRVa0/sljInK/UB87xt9gb6yYU9Q7sfHwM9656dCpKSPuPHov+dMifmb3V4LANfeBGNkvky+wd39G1OJIo0efCnIYFuGcbt2KxE3hmDrJld3QEcnzzC/v7iEF76tq5NHGc7Ixcra2jSD4ciP7BtuiTuzU4CS9vaJ9rZi0FPJamt02n0i22YRM48k+mPpdnPTR7BpQhWtoouQkdbWbl63O6+dJ6Kro9WwAdnb2jLoMfjLLD8T+f48+8F0ussjCkbokgk2Xb6s1f7cmjpZAhljwV1Gs1n0/hGdNtnACBvnjcEncZCyMK+z80RPjgdSdIPjNNgjl5Uitagc/uHsFb3aRwdmgtSSAlYGTR1Zc5MwducaeLCldZCHwvXhaNNkwEYGcvJTxcDyHnYMbKCfqewR42E2n4v+IScSfe4Q9Yg5dwmWElOZiB7tMKf/+9OqOfqH1tnc0JDB0sEmGH8fndl5507nTN3VKXQsxIfkkqmlG9JbA07wBifjhnfM4QPPbpkXWgdp2eDUt65It2vMJSzSopzpdYMi6mh5lfXpHLrBkU7zGNFPkxQuXtecodFzhZ9yPX2lK3aEmm0ZrAxvc2uF3C8BlK/aGK2ic4bNHFoh9dPxFy4X0kYgLOBa2NJ1VVOW0rWqRp+zna4BzhwMz3XThQ+Wfydk3f4XXfQktz/cliVPOu1h6LvyP9EVjaIjshA2edShp7h1dsdWcVW0zZDkVqFfQSV11Ijortab5RicM/cYQsG8xWt4kqBKQ7pF2v8in1SUgzexlEt0euSoG1tET7iWERmiWnPZ/nfHkgS7Siv+7fuvYCobH4PDo9wbtQoXNKrk6HC2Ny6cxbbf7eVKBTiRa21a944Eg/m4KlL4GlRAAytmbh1ZYEkrg3+ln0uZkmMZ95NP+URjVEoG21xuID52GUbvDgM0HDnkW5NLaZ9+31p4lI/8jCIHsffRI/8XvS2HjKcR+3UVMko53WH86Nh/okP22Q6TKehdeBH9wrZhlXJkFzl9CPy2/+3/YmH6H16kIJvXxitAolujb9erqzNVTndeP2o7WzDZ0NZQdE8UMJ05HcA5VebqvwSmX80CxVKS6+EyTsD0D1kk0wG8ntWFMW7dOh/Qw2XPV1XmqcuyKipWm0sHBizngQprZqk6b/ymaXpUb2klmw5ApiOFo4YOfcx1iCf6whAlv/ONXFzd2qpUVrOrRQpCDQfv8oRx33r1QSeJM//s2aIV5KtxLm4ebAJhFzKuU9/FGgHzrK++cmXTaUqMbwQIFT+v5uLKq/e7dmSuWe9jkx5d+lqCDXsFlcrW1uoo453baBMy7Fm1MDcqdLnrsiatM6MMz3A54ZLKPujCwGdmf7QmVOaT79yD+hf4b/pfqs79cZkTNPLwIQ0aeANhJDnAMxzWTtogQXgwtKgQmMhvSifkJw0taNFm6I+Q+0LN99EhYj4x+QgJJk55eRaLkoEuGWHTQg/oiWjslVBF+dczqby/yNqMiVl3JJWTs+ob73F9m1YLDrmem+R3HwJ0uvxuHbDr8JkMTaokYhBeQOngMcGfd3gXoAVd56HdYFJnzuoQsVcdZwZ/ivOlN47vC3INqtmmxWfP5k3s2UODS2n8+KpTpz4pqINVW/fV55+/mHrBns59UchrY5t8HA0PZ9kzubNF6ybj/GfM4OPVj83gzJ9DD1/GCPkuFqin/NS/G3QG+xtCo8IQoAL9jnQKozg0AhQSu9VDYHhwe07szpujP21c9dG9v95u0oS9M8vd+zAeUm7tf9fIL9a+5Xzpj2r3328Pib/MfHwdQ31Eun/LPfVgQY4U8RXrsaserQoTGAJid3T/pKkPWYQMakEs3wgoeQqTxYPombj5RjM46UMCGaERZlJid1xtLgfeL4hQ6HAo6Yohh4JuJ1oA6AbgRxx5eo6S3kMQt/AnFAac1UTybY0va94488gN25JjKMbGFH+mKwsqXZoN1Mm1UgTaML0tz1O7Ce1clXdnpez2RmKBiehVnYGCIxUklLcjybzY8LA5ig0P6ZXY0LBGoeE8bGiJdR2Hb8iRZ+dI78/cmRZ2t/Eq9EBVM3smt5kzgxfM5cPsDxxX9hL7E1sR/1GzKCFB9ImQXKVhy6ma5vKP5WtYvBx2+PX+O2dIzquXw53P9tTln838cl+dVM5v3o+tn7VofV6YvdtO3rHpfXxybp7woFBVAibTwoscLE+MYlIy1oQkBfK3ZiPBedvjU5piS3npV/p+nX+necr3v2QJ343FQ++Axv52wtrzV5u94lCNxCjM/8tr+4xnZGdQ8C5WA9n5GIu8iqPhZ7uUx67n/zsRUZTEXpARBWs7xa37xj0Upd9zAUe230v2aV7xi52Gr5NuZm6yJ4W76czeXcniG6559nmwlxz/LBt9PQx2cj44XnGvBV8rEt7j/kvu62dciDhagGN18cZEjsrlvh/ddp8SMG0GY6SrkJltd1ocTxyvtbpMlz/KtnYxHyEhTp12jllv9re2GuFB2NTS4hsMwBkew0Q7owbiO+ER22ZiyWCsY19pY5xOI1dGhxpJXUfmAufO51vyXzxiJ+2KOj06Op+h3xUKBEpnOXKE1CgevXJlvjEOKr/h6yLKWHlnV3ksyE8u/hVPs47B7oaV3kkBD8K/Nx3aTXpcXnVOE2HHHKPnFLzXFTntCetmaOSZy5NKvEByC0zQH6RfCxEyBNtjKkrIUH6msnJZeEbHYICZr4oMMFkK8lV00R8IXTzoPeV9p08JJh12mlSbLYEUC7bY8jY6Gvc6bnTqLG8V62WuN//0Oi7d86yLS8eme6LKoQEhsoxLjj+vSt3FfabbeQXfvEXfESsn64RUo6hoRWB3T489kIGDFKLusrrJ9B3fkXO+KCm+qj0IjQZxvraQlumW7jDc+2uFejIXMoqk2ulsEJw2S/UiBTH3JPVGwORiaY17qlP3KA3k0nWwiaaii56sy8LkQM7E3cR+ftJXWa/apQZy0XBqdXUl7d1gddJJYjNIWnqGiyu/ATvbwBIYpZOgqoSXiDwcFopeWPsNdyU3yEEOdS5ydmtw2uwLBuYbCJjcm5mfQOo7ov+hiRmC+3cYD0ysiHyM2SlVrw3ZSFE4eIhtefrlr+LN1F3FGRYlabjQwll+f2iyx+u0Y/Jnf+bXzZAdemphhz91TJ7xOjo0OdHYwbEUDhNNGZaizkhbXVraAG+gadBjEIerrOzmdTfZefZbtwY8fv2rmW0Inzw3Gu8uQ8M8cW+Lmk6caJr7U3CgKTCor4//Vh9/bfBa8l8yGucCfxJVoaVXotn2n4PXHQufCz4fWrgexi7kGLpdIXs5BxrF7189sacVKVQes+1pA6eQHfYVtz/y/QhfFl9J+jfOa7KdXi48p6xlF7HXUpbEyzIEkwPHJH46deuy7WMMOLJ+rV+N2RWDIXO5Pj4jhDSHQwEbh4a2bKZZABnQQ4u5n1AfH/Ktb5nCCBL15HqJfEfMcTF+opo0sFxK2Sfb/Cb6ePRKrrZp7LUyui/ab3t51u4lnyMhkr9jrr4+227HHhAfFxf1ivvEUPFQFeT6hnjRt76lCiMGFHrW+MvN90sjpqJHviTsDpA1mJRiGWnaXpS5CLPwGSnkzlrYfI8ewu9TSZu2f1GrT7m+eGCU2Nquq/o3ea9uNNnuQndhTXN+u62e2yxYYCDc2FpfD5sxqNprfZznTZN+GJW6+3o3PehY01AR7xIULm0Dz8y4ijVaYJNWH6PMKmWESwiS7P9DIAnjG28PuldWpFi/du6c9z/SiW7rLKJ9X7n1/xCw4AnFyXW/eh3Qg+j0Ldyt6pwu74bEWu31y1F+G8eZzkXebbvKTWj+VUWGUYblCBc9YHYzkI5Y6CiNrApFErhP1+idB2cGz9jfOrU3cipqb8vU/udnQ1WqUlCpweYnJ+fhtVJ11L9m2TOBM/gb3h+4JqaejBVg98kPghtvZWcDpgMK7Zhjd0MTVIWg5tWjkCevx0ulkSL9GqmwOt1gRRux1oD0LOHGvTX1dJOPDA3RsIPdzKk/EXqa9HLTVJi/N80pvmAv+ja9JJ0uqOesiM9vNttVktTnUbBX6WTKFxJV4hyPrtkJo3+00kWI9fq0yyTZ4hrn1Pa2129tEKFnLiZtkxJHvIoSj+OELVcgtW7u9kB53RvWV8nTmb3cN6o3iK3rQVd1ofU0g5bK21q+bTHTJrXcdjI3d4Bn8BjwkYc5yudhjalhJ+MLoGRRYWJx8aHS4sL6bKLV6AStG4fshHXQCGHcqXbiGcyA4gNJdX0H/pOR08uGGFLfNwnrbkAj6dFynKMYRTD6hNK5MPRS8BLtTgpcfg98q7rqEnJnru2NnRiK1izixg0RvCWQOf4Cxo+ItrnYVH5DKntSrYJNfxgZBlilbm1tO6s6+5uorwyfboUFxwVw5RN+eJ+FYYJVQSq6AdAeXnbixF0uKtSTHwoezraQfPfSZN5/yX9++7Zvt23DBKt+QxOqSigFlUqsJVmeh9dqa0PLEbroCYaMT2bDwolhWERj45PJIB11vzLzZIfrST3fsMwvNzXZzesmL23DxIarjJ8z4B1PnnSHBLJEfoLiIkPc29MIjV/n9wMnM+qclpuHDcnDcmnvqE7L2t82usl/oOLWumxhrwVmdE0jixcHwpeeuBVjhdlYV9pDGnOZ4cFuDzl8E6YiHlwRd7t0M6siJ00megTnYNnwo0flNnxInAKM7dN3w3l46lDKqs77o2z44YRrPjboJ7m2H+fKKohOBmidVzW+9mDS4UspY3GqG6omlS2m/cbo7VzPJ1VPVXWI2qZGD0arejZWH8jSW+miP9CD0kaYJl47wIYi9WoU+UNuQhKvsuXe46n6GKJvHh7afqpW6JnU4oZwRJ3ZQOEix2ubEWFI9RsmeO+4vCZRkSpX5RcDLbH5MtoDiTkh5iUEVnZ01ihKRXFmfgUgzQ14+0pwrN+LjlV2hqrKIcKmHD0hbnndJ1uQ6Kcq9ExuUNCZXmkaUDlu3TiGfVE9dtVaU3zm8wxp752s6rNnK8cqrONhpdqNrb/8JzXyiFyem2uCTTt3Pp8ts8Qrlff/KmIwMvmeStpryK6RENAFND7ee7JAVaDyDo1vjQQZ8mA/czAj2OwXbHAi42R8bBXGJ4GVXmnKdUiIzr3tpho9gWATTpr1kM6hM3WGp4tRIRPLD3GenVHoNTJgltYny71yaJqrF6ZDtm4Z9obI69hxlex6H228syvTwbwAujqH+WgPbmbHWdjRv1X72/hClUs6SVtQz6jrv1BDa7cNxkN8ebLbDcQ9eaouQho+MXm7TZ8ArSF9rM3pjcos6Wi/29mOvdfS9gC7rytcZKDQTbTZIiUHv59UplVoi2VUog2gVWhOD7KCjSKrKb1aKNmUpvz++4NQX+dGCIwxJ65ZVg5t7dh7Xe2mKJaE7QEdp6O0Pk/vkhP3txzybqCqmAUdbQX0WqdihWRlMpQYNgRiYegB+9AX501MIzYXq47RsmjFnZ3FjLr8xejRvPJ0ZUuLBrJDZ9Tx6rvxKud8p7CguKOjEJBtN61+Lwq3y4rbO9bkwF3bwXudbQXga+b5i2Sakos1baF9fAGolQor09MrhfXVQlO6qFIWxZmv7B62rQt7q8subMo222zg5zXbL4DVtfjhhpqv1TcoB1TfX9rQiG7x9hDG43bIcugxfgPqKbdUd0uQnuttjVmRNsU/yZxcHurtIGXAYeAXjL/IjgFKwlsbYkeqeTfGnt/brGAoNBoFXbFXU49bYJBmYBybWEYSfxH79hYUyKMV08dpcmyT/PJI27bpr+ceYnkKoy7vY3Wheuqh6o1qakqtfrghhfKZPKQYf9iqEuz0YqvUTmirG1QghJ5P6fr4N+p3spCzdWSx1ExrtRW+kEl/+E5XxcuXzkUxYnKIIwTrZyfmQPbyRI+q4N8zMgDOYwoeJZo8l6nOjDmeTD4NCExYjAc37BJ6Uw5Wz6RHZURVzgDtaBhxofIsx6k4sRl+/NvEPjKhNDTBpzEmT6tq23kgWRmX790AJGScqeXZeSqnUHdRsIg8z8h1Z51zE5FtbiFnTpq86aInRbCJBrON4j8NnGZ3tI9rIDk5xbOHjeV4romoclJ4iB2e2PhAzZ8Cfu/KZ04RydqIZmRD9Gt/t4Dp60WBMVL49MkjEbZ2SckRJEGyCkW6K7fxhiluKHeDk98J2WO7aNqkEfDsiOvTSSRJsIP1SLjyZyad7SYKFZGOpHHuu7x+zA1uBleV7zvmQVq4Vosn9xu0kWy2asXPtcrIgB386ZWTyHiw2GDoW+X4s7+gZB3tBPiubCocQRSwsa9PCSvsS3br1r/eKcGau30O7XeKdpoqOz6OtEychlLwNonQaGrVN8iIhxNiR76xPdRhPiIfgX/YVJiPQeBt48JUscpLyJ8+nPV00R8LydLW+wnWVH3yCfhb/yfzYH8/dv50v3Xrdw+XnlphBXSQ/VUvXp6dnRr99IyCIU9JmefMLT1cZmii3sebx5m/r21FdnP32LCBPjPZc+ylUlsBK90iF9m8da3rdPHWpPcsJvXaocLW8fH0JpORESmNuZscB+fmfGEal+wYP0S4EbRl/NI3NwKnljzms1TIDqGUQ5RWIQ4aWQPZddWzixKmVpxrPZ6PtHRSUGi077YRuWCZjUhCSUQbCS2EeHCFkbDkyaLh72LTgcOFJ285IilOvRrn+aRYdWx2ZXEsIqsDeBxh0aRkugqpLA650YU6Uk5+fQikZaTKCXIXmX1BlgR7dsw7x6o+QGUAbhmrilvtpdDDPQl2xB59pJ8+ew/p+bMaufA4HXEgsNnObbdhHdW1l8DK7cEQZZXggdNnE8fQgjLryTNptquuZ+Q8AUO1NvDz7dvPgjtrVRgZ4GlduC6ZFQCWDny93ABEPM9ojmCOAMD/K45gE/bXxcDfh+f7HrN77j6PgSV2L5htmPHuexqwlEwTM4aEOVgU85xl3VVMfP56m7GK2HVEfx4zVpAYs3GySn1xdxn3YlPZiIhisazJFgyTxF0w/UdsDEv3gtZgxgbqdDzy6FhttrnZzcUmv8PqRbnGu8e0YQO/ZJN9gvk/qIIoBHhw+V7f7UZWYPEkt8tlj5omiCR8ufgD/9AHa0ZYHDGZ8wryA4nfmbQ7z+UvBf1nnP6rol8lOgWOyffHCkwaeDb2hLPoDYl8wIoH637gtUsmHuJy8cfUeAUZg+CRkClsw1zmB9vM/T6QcA6gSwTdLYo3/5ZSLAnyxciEI8Y7WwVgaqzZn9ITfylLYudeyfBH/fWhut+gV1D2tyHyYZA1Ur7QW7aemxVp2YT2lKYDFERsNGS0XHeELuTA/h9B4eyYXVWqU7ql2TSerPsKBXMuGBSmFXfuIac7RgT2ADNOizCUoSSatp0Vj9b9fpcNtfZq4rsTJmiLzsZEEptKhkrUgR162/U1Xa1NgyVbXlHjWBdUjXvGGs++Ptrf2QxsS1dEniRoRvWzkE6tvji2IDhAO2qwHLhXE9SMhx+em7doXofLJCeJtWeSXWCmPTKd1YWgdFjbGD7nVCHKuEGd+A7AK/xAQpbd/u6J8AvcY094nNU586jAVjxe97E79Pj5JBR++81YWp6zqP9kUtlPQ+jZKRmh7nENy8BrOlAdK46i128wpOVfIbU9rU06Lb7zx/KLVMko+pa+tkXb0qn6FgVaGgSqTwmJxoBsJy3PqgCMfr9GRFQIL/Co9dv5zh8T45UUIRr4g1GXHXNY0+1PHWAzEInHfzxgPTM9mVjve8ajBixS9oJHwMPgfnAfuBWsBAvBfBDL/zyBisHKEDAZXA4Ih5Ev4h14H/wGvA5+CUaAIqAhcFgnE0uhu36SViyLdRZB/vD3XQj1UP8X5X8IAsRPhRaiVWCFOUDdMJWsNio1dh3Ipq4DjWp4gLzvg4QL7w/tdTGFznR4cQAZUGGha6zC6kBlsHfERnFDxMdFXBKIu4XSa1sFZNQgwP9LRv93xQf/e/WMyy44axX/RiEBTCAmDAg8UPerhssndZN/0AToQgtQQkrtslHjLjmKx6Zt5GkoXGeL1AX61sedqnsUiOL1mZDe9EwxmakxTcfxBKJvrfUR5XavztpsEI+0xkyziy4gsa/OnC01yOMuPP5bOLd/+q1sQn8uZmixzPde27FLnn7U1NCETe6GsWE8MLKNPESTkffJ1vKCzLaPR2vZrHNyBPUvJRwah0NkGHgcLflXtApAOooAT/w+8fvLc7/rObZJAuJR5HIVeJQs5mnIwOPwuDtEdNeGoLAqNOOFb4u2x2hn4JY7G1Gom8MFtXTBIMHFkkMYEG5Qm2ADqwEO1x5tcKRg/Z5eByMWxIz3+aVSbN5P0PC+zLSwjI2QhQ64IMcGYUlbd2qWttKG9FWlco9dRKJQrTcibou/NVUrbaRc6e6TJ2HylBlz+yvPOnM73XlliqdY3Ty8NH6xOIxW5HTjRm74vOELRxRWo1s5+SIzRITzQczR8va7slivvMzPqBz5ZJjdTtehsinz0rMfnaw8UqpjDY67qBF3M4O/o62nbGR6egx9hYwc3vM5oMNlKLr5dRHjZCTgh8XCDj83F4853LAJ7TohElwQ80saUTyu/PMrNpNbYiFqDYFc4m4XnhLf25kW60IYvj3viKe+Y6mKw46VhXsJg07Dp0DugOeLEG4LRWPZRppb+oaHeRwcINjzm6KqJlje3tqmWW3zJch6BTwecZCpwUBfKATDipWAK5z5TRtlawCItNpQbmRCJCp2Azj8XdKFWx/d7tRG6RheDIXYD30kyW4qxulvwnLkU3vurTLECV06lAmOHdM7tF4DsaNoCw7QoW5eGbViNttpU1KCj74qsBCDZLfHP9gKZDBhAzTwQMAUdgXda4tOA/xKS6+tX5XF9FAyt1kweo9imWtDHNYcIIVnh6+PwBM4YKrJMyoNDm07tRDBf22S4fwgxP50P52PmalBEX5GkQRxauhLMLtSSJVK93FgdkEzgmcv5amdX4ulVbOBksWgNn+1NLCw+tXoNA3P+TTrdGpDGpemDWNkBEMe8bl1f1CEhGSQ4wzYjMGzXLWSZaP73NpQhhs8g2BWBK7UBQGclZTsiAMpxLBY/Cw30oUfrCHVu1oIq1g7UTUstCKMpQPiYrM7rTy22M2Nmgjaoog6+rYkyo4XMk2QD12knFuPUcYurYFHVu7QQkNki5UELbliItyw4lUKoQrL66zOtc65WsqJIfzYNIT6/QjLQig+6DnmZcMAA831meiLCL/yqiZca+l/fAkbZ67gVWSR74LH5QiSx8LI8UgisIj4HOG9TPYPAVajJFafS37hl+naO3Kd/XJ9cb7pWwYFsOe5Oewb1nkzL1XdqUDx4hL5QOwS5VT6GocxE06pdQEcNdYKKTLr5LfA6IY1aRfLKBNGKfgcItjpHgTPDUZykmdN7t82xDf1cmAgrHNwwtXtQYBW17uT3sKcqG0eMVBvhcLlC22gYrC6qnTXsOAy3SuTF0TSyFpw1IENNJSWUZV0LIO4WFr9kriN95qsTXsR4SGVSLRJMQH/byugJ05EaEj+/uTE06IfiCMtrP6JMIqHMr/u4johkn14Lu4juY4unEv4eKoj0bBgFPbe+rI8FQ/9iW5f8zGnMQTzYIZlpV3jpXLs6ZtK05MDpCHXZDCinLUyY+jzsDJNfR/qfe6npMEJmiXfS0krmDiada9GFYAFA//+DsFqvb+9vID+mr9+F7g2/nrebxe1cv+VtlRn3Wn8Vj2TYEsL+CNLDFI29cxI8Vp7PvwmNSjzMKS0RKY/E2xH8A1IAKlwzGJzBdTUCpK8F6BsbyXybEI3/WX2xxzLEEV5ScYjpItIwfMPzsPKYm6+9Eo/OIaileoSmphlH4QT5SWcFaDKFwEfBznxaW7etSW5cF1xmn65K300qe8vfuytj23LoFlvdkiZuulgzUK+2dGaGQC/Kn5IaCZCWNug9+kO7rOffrK9EXiOZciSyNMU8X6/+Yz6F0kSBx+PX/vNLIMD1WQYPFI2o+NIKo/TKmFubWV4I6sGSZHK3ONwdt+UO4AtDzUT8TCbj3gCnhgY4M7KyfUYwjyAb5okqn0TmhCSsEerNTPYyQ/P9PjDoVzBPwCVu3sPLqQxRMl+XNkGnMQ1gkO+OkQG8xfCzEQhOoXQxraZznu6lrjnRS3368tfXzsHN8EqPS8IG/jhAHsed6VehZwwgTBBTUlccpAkMJdJDooJbkHTB1OVV1TUlqqZIAkH6jhQyK/hn0+rvfFBEToDUbv7GqwUSOvgVnbvo03bFIVslcYEWzPnqkPb5x4pLjQ7G4P3vPhiIRbQd8/0VaYmKb0REsi/nyRYjNQGYAi6cweGYtw9nj9WuFs9SOlKBj1ex+tx0DG4wb0wjPKG7ESA5MEfCuCDYir9MC7RodQE1kBDEuv3q3dk38C0/+uz+/dOHt+3d/26grWZZsff2qmuCKWU4MUAmWdcg+uVHJvmIP1fkrAYgtfMKxXm+ayCJzP7zuh71f+R3HpIMLEy4Aw14EtFLXTmQEx3LEa7nqjRICN0xnyVhabpJA00smFvxVpCcIjd7p7M2QrF2jfNb82kzGAaSQwMrr9knYrgwldIBE1XGf9gUUnn0uVXvpsTQQi9ZCfwjRk6Sxu+3W1ts2GFLQw2HAWPsYKcSUndD0cJJvEWv2dcc310/GTLs1VltStzrkcRzyDhoiwR7v1TiHoVFI8C9e1cQqcWxEKvDt6K8oZC5XCVak0+CHMxeCWv/jCk8sv5E4Y1WPtLtg8aujZbTTbIDlgbrB1oPjQ/GM7BeWDE0MfhCtzLH21D5ZgpzXEopz451qQ7A6iuEu+C8zWQOx73zcLM4ODgkusxYrfm75MAD4EVQ/qf8sRLykKk40vbrtmC4J+KEifzJEstZFml3kmqbEqiY8uvKE3I+j5fy1+uTo52tj1XFlgKu7fjaV0RazcycMepOIO9nhnTD/TOMg6hGOKYGjQcI2o6JhrbMZTrfH43IXGMpXue+6uQfomn323H9q41d+AWw4wS+6Z901m0uc3TOHQvrwwIuukc4JK1ScvEr1tk+uJSmGEfxYHp4RCt2HwS5Dv/h8sjmJM1GmFdoe8Ctkdd7hVRDPSlfwUu8estJP+78k2VJ2T9fZCMMS2NNPBok4Skyi0Aa1EO0vlnoF/L1/JeBUb/f9zZ0rXEbu/R8ZRJd+eERRg2AMPZDX7jqcYYQk6trgUT5uf7AIWxCjM7rZbRQO+fdt2+q0q11dskjrJl5tp6fPCYO79cl6QO6UMNvbZYtaHawbBzVV9qYR0iLUg0XjEM0x3fqsUewJ6P27nZbYggrvIQ1B6tfuQ6MG9W8uMdcnsWQgg3ImHjDTwI800zpzxdD1XbqabFkQOLOoXBuo9EyNAlBdPCiZ0weGBJxfe2SQemD5I4xK6D2GH+E9gxmu0oGb5fwvsNVNo/XwLkk9DxcowqtESAHuhXCgUcrZ1Pbmg+rDTY2GwlU3uk6U5sHLlXugWLZ8AHT9P+49a1h/1uUxYqS5NA2FNPPA1P2UcW2WJSrcxh/GCDWn+cIA8tM58k8geSRAcKOnhNAlkFwDFXwDDfKQeIa6cIE5xQglc8c6LTxEU914WTkpWzzu4NYpxfhqZxOsbGYTymY4UuzXmJzRUD7icGs+HCU2r/0rDB7j5auqpILIU97te2aRNXfTOaQNEnK2Uh8iXwIAymxW+GH8jII3T5NCOOZyERXTPpXLqsuptrIkhE+7qaqID5rhzq+90zGMmNdg5+BYGuk0ClZdFxKtPSQBHImJtp1HeAAeYQ9RhusFXrOXm46W6pVPQocN5dS61sOJHjHrMziniYu46VmDiteNqHJIGNAqvvS1plq41mtPbzy9yjSKBytJN+k2Wl1Y1mAA3sS3s5/hRgj2t72CYrn+Oc5N1x2v8Ou8shYh9ooEpY2fMR8uY4kJ5r6dlIv7b9Ph0cOlcGwAasGEqmA67PxDMHtbhRJWFK5X6eS5m+jYlfhKRwp63C2fzOWzrpagm9P0D15Qmbbsv2Ua+GbWDB8zJyWd1JyKleUECPCRatkSwpjomtAzXTldK6bGpCiYfh8ZNMfgbv/fdiaadrKIa3gH57ZqVbc0vkEvjr5UAnvfWT5rebxmk3BkYq6tQEcpuaEOY6ZPFCQxq5MURXJ/seH6RwmuY98+xfq9Oit6rEGU6mw6n64AqXUYHYX14f7q+vTk/2djd8y5wTIssYZ3juxf6LS04WGNQZMvm18ugjGVIXBlD7KJlOxzZB2aiBZ1qvNHNyALqGyMcwIlGR0gEF1VVUKQvqINJzp9QJV+THZAJBakhj0/PMQ2TWU9V1dS7wFzu3H5iyqhdcF3eUTFRaEbxuL/w6IY/vJpH8V6drWcQci4aYSVWSGtGLznDMESri1UyxkaIyxpOj7TitwMwPNZ4F+PVy3FdFsvIFwZOJC1VPwB+bbAaqcTr5g2oi2olTEc+sQ48Sx+6J5+F5ay2WzBOa8Mjl7BTxqKcUiB86bvZ5S61uBkoT0kp6yFCQzS9ahFpkS7q/04E8nr7gWtB+HLXna0nWJJZ5vjGggwcYdPOWF5rbTy447DFAKxEPUuLOxgo8Bo+NGhwtTKJGwR5Teen6cJZfOgvKv44SUgS7o8CLziawfEIuDoHOIzRMVOMbnRuHzNlWKo1Ch5qQ68tVIjZpD4FQxXqDoY2nMNwSsWU2Pu/uBDvTo1FwvUePAxluM9CnFhR+1UQh48DDTp96x+6g8oMNyvYSsI9PQtploUrLulZsSBsOOQy9D7/bLWfCfFXDju3TCcc2jVc0ZLW8agMcsiG98881pLoIPRFXEUt1jx52/T1fyQvQNoEcWOH6x4eUH983XF1e4yTwHMvQFAmW3qHp6+G6PqUyWzlMb5x1+H1bmDt9BPNNdGmcJOhkd8s+ua7BrbIq/FtonG0emzQfZsOBm8Y83pz/Aha/wNXQTvtsy3L4YM0zeelXa59jZWnMVvfe57v+XpH0Ar/1pcCW8jSN3XyvykNiG6XqZ3kyBcfDfrfd4MZuu4EpDufBvl1Drb9boqxnfYTwiZvPSIa+gVKsTInNhsH2IbPpDlcZRRUx5slWZZaOaI0hU3MmFxIbuacT+J1qSqbYIeKd7h5yMxJ3dDLk5tPZ1TVR4CKj0T+Exreg2ptg5Uuymk9M+gIdq85GtGJbo21Jle0NuoLtaWaRMI0Wakq8VF5QmXGZLqSv/DoC8J/Ewq9hnL6CYXz3UFfFUC0W2qChITpfDaoSYho56WSpAaL7+EDTk9FuHEv5I10WGQq9y7OZHrp0/ZvOVqD1p8cGPgEGLb6T1lKwogsFpXpPTdeuvEu5bDROpzI6G3oHFxNzql8mpBpy29SGtuSHUmgJlWuDoO+2dqWkI+IpH9m/McBl0cAIiyWW21UEAh7mrUhKNEBD1hpaLZ5Z1+CiLRrNTsL5ANshdRD1QjQnPN9bBdo8zW3SWAmh6cOiGVOfp+5sqOq6ChVQF764sDdQ18YtgaYAfrt2LaM6d13Bid2iuUkwzdYCBt3BZrcNMIjYKATb1XpOdBUs/xJBNKGjWBiUp0gRtE4bXwbGRhC668ugEogh1++TiAu0R43Eep7LidvZRVLU19pnDcZn1oHvLPqD0we5yr30WZDfBjsD0Jd4Kh0KataO9J6rJuNNslqzkqEt0Kqd7RNt21GpqeSqCpRhhh8pSJSiJ4iM3OpFU0t3jLOPV6l53l2y282709F8U+eHYq+zJF6Gge/+yGN8wg5kz3qA0bn0gPiT3cXXJlfTLOWGJ9N5pCOSOFAcKibhr0nlc2GqdZ+gchbYG1CFFRi6YhhLrWPnLY7ooXBtlhAqohM0alOB1NGDnGWZ0rFW3g0iLu1JaPpmCHn7vEFZvtDkcnsSJQeEgbT0F31qIRU860cXB0qdf1TCqWPkwKmZGQ9LvcmfjxbHe1mkR2ajgeIiIIRHtPPn+93t5fHeru/qmsQ8hV+/GkJ5z9JvNYz5OcxuS6fLzLoBOVZeNWHAOA3j2lo8YMaOPodfdcCE2aZ5zX9+sovG5NaBjRBxlQPyWtQhA0D5kMRR5HmUDh344cT4OBbQc1Jkuzt8HD9KHe+TfbQ2vtXSC32BT0UVZ/PBbw9vY3Qp3MOV26jlUlQPcfFMqJxqDTRku4hnSzcDE8IzvfNiz+k5Lch+Vvmupas8R4PVlNCokv2uliOCRHJ1aKYCTPVsIp6LPT5tOCH9ky+/DV0FgqR/VGEativCAbRCIG665fBQRdWg6QYYUVSXrABDo41Dbxb3yU/bqMskYl1w9s08HsaOKb8JJsWVaig9sJCpeRMz6smQUXuuXbKWte/DUY3EWxD8x/u+KqbLl0UQMRxAXWTUkSSFz+VCitfnKc+733eIszZ+uz05PjzY33VsiV/P06Seqvdz1mLTEI9PjNgxIiKPDRBHNTJ20cAlI/F8rf9xUCmQi7aWpSnHRPlTA6NmBn2xrtw1riCoJMTVCfpsd8QSxwrAm5viUB7i9TKkZJx4GV5yl1YnY8EabkC1H/fGBmf97deBp02MOj8JfdP6+wmSRT7MWvLXND8frdOsX1PxwEQv+gIiqL73/w2tYQ8QCxwo+kLe15rdNrHsCsz9cufoYOd893xrw7V5jqGa7X5XFUouI3Wy+2eoMHMFrEF5BWGIT+H+Av5jpOVllnixH4d6RLV+yik01DNrW7L3CPI2OEidu4Y6JjDMA+YNH3ojAxvv2hFCg3XV3dsFrfne583PRPzRhSSwDEl0zXFfFlkahYJhZ9rD0dhO2lWYoh6uDLFp7T1IqxF8tTQvP/oKdQyg4UizdvRY2pHMeyLuHDO01L1ynSaMOgt5ZFTb/1+cHyjcWk6exJOtJ5qrn9ye/I4bynjRMbZBQLtp/G54HZrtITH+XQc8FCgefF6py6GznkngegCeqUbP/BYa8SR+e/IX0MkbcNM+ufkLaIcXHfvFX0CPFZAPB6Or1h49nEPxYesXXjP0bGVeTbOteZtMLyPDMN2Zkwfz2QT9jH1eVNH/2ij2pDrusNv5otEOgik14efbjMVnvhTMUp9ln29zFbgRBJh1yG2KIsvi+Jtds5fnOrZlGroLVAO5jGjmej7ut+ula8/N6XjY7NG32xa1ryqz3KdVLLelyX9hNvA/mdMS4TSA2WLsHjxeB0t0svxKMovmDLPIr20bKUcmilmrVK5qr7EK0QirWU9fm6Bs7+GXRFPSdmIxUTM3kyL5Y6a1ulofUEqpqdp18qUNBzOFMA4iawinCeDRwWAfJnW/nY51ofGm8YbmeuQzuG32eUqH5t1f9M8nJ2UXcs04a6a2DPoJl93DYNtQs98OFjvzGxHzGH4z5LOSPk18LUyP4+TT2T4Jx72vDvZ8VxY5hjyjzjr3S3vYb6p1hJ1JC8c34m0x7h/EqQmbJzptSQxFGwfZC7aU6DOvUkDfZ4sT1Foeh2meThdOgMtHQ+MZ9HHtTj+u/riwrUqt0sTnji3WvwvvPvh4FaAh3ktRDjNBLQuXLXsKsRTh66thwyQgPx2cRGX6L1bEuI9OKNEZNLHqdSwhUPr1EENuolUFZn8aeLahKyLHEM+rGfMuowI7Pv56ChSjYb+UCJfH6LnDhYO5ShbDUc+3zNBYWYPbM/GQMk2iALvzB+0Hx0F+Sf02NQK05aho1wIQkw1FeTD5TdGJ1U2WKB+Ymi+7xDlOR/T9du32W/3IHw2WjWrfFr/SWJHKZJrEs+c2f7rDj7A9RzfEg/X/F+oAj6E3aRWWviNSX7b5TOy7X8SJf9dBQ59pxCvQmMtn8IhsyTLsefwMymGT+lOebHUy09DRB2wdlkQ0sG1NaIEWh4VvmQx2IAiqfhWmb+pG/xZ53sE7hvn6HOJB824+StYqbbcCuX9+vL0+P9zdXl+cne5sbW64jmVirzaQ2+V8Ou43JCUEN1kj/Q5tzgwoA4kXQJzE3Hwkvg0SDBD3rdweEOghuc7V5CynXUc8SrA6yEgGEB6zf1htHTlNu/Tot4PftAAAvHx096S24NLX/8VBMKMBAA8SYiuAtHHAcRLwLrAMbvjxS1hm1ED2u0dBlCeL2qUP3bTf+jbq7YbvAIAtSu/jOa0bF94fteyn9z9b+q+WQrSoa6xG0e6lWF44q0V5urJn+xt9tGdryxYtLYnntE6iTnRZ9FF5Ovl5KqcvW7VoLK/rys84T+3y9N9ctVi+zuWRhjXcmuUq8sBdd0n5Iyu80FqZ1hst5i2uic/1hfB8xx7OD3f7+/stepQrH13nut06cxbKSi//WBmkaB7Cco0Wi0vWS+8BovHS292GYf/KceJ2LxFa5JZFDT1fNjTKKuvz4wN/g/6hfKOKFSAJlHJg80LM/wOyWgGWERxLRrFsGYPjUNn+pXEIeTQuHJ8aHxP+5v9+cv8Bi7BeYLaPO0aQRTeSRJKMR1aAUv0Yo2mUbAxZ+SbgecD6U8hwt7+9ylKAJj3/iTI9P7TjaBfw4ipgqQZj8pR7tfRiWmgOK32tXF9xd14T4urjS/f/9JTh0BMXWrw9BIfk3blV+0Wz6qr3cE6xe/hk3noEpKdno0LRq7e6+IH3ybyNMfbcjX1X5kF/LjbOhPoj6MDqJyf9JqIfJ/WrPV5w2E58LdItjJ96xhe57yGkvjPpOb68cS561+eVFZRiEN64iWXsC/qO5I4/ehGswmRpnEhoPEj0ouZx4+S8O7F+cdMPY0ahRPSnNo4omnHlrT+0+VYk/xPjC/RKWVE13WA0mS1Wm93hYkIZF54fhNFytY6TVGZK50VZ1Zvtbn84nppz212ut/vj+Xr/6AEQghEUwwmSohmW4wVRkhVV0w3Tsh3X84MwipM0y4uyqpu264dxmpd124/T+XK93R8AIkwo40IqbazzIaY6N23XD+M0L+u2H+d1P+/3JwPXtMTHBI1ZoSUVdgDsv1B8sbZEc+7h6InueuNSRj0memMuT1pjNv/AcJ34Ox5vEqBXiz0UIbWb+YSjx8WwVfu3jbSEoaWsPmRn6MA2B6Jcr9VTslqL0/11IjJdl6b17u+uX63FT17iIw4C/0gcFsvbQn1HazKudjRj+4iJbua4yAU+iQcOXAIRUbIFQLkPWfokUV+gkjNbW1G83ELI2ALGj/DVtAWYssVCbBdzsZDZxXpDHH19my8HLMbXZRSgzxY39pkJMvdbzaOTMkMP6i5hLGFdJ0rgllH7whQVkWdIa7uJ/7CSoEveMnRX+MZVmgi44BfEns5XsgK03MKHzQFs3oGjfQcz5PwLsIYn/EgxhyyWTuA7CtP15mtPS+yvkOMVsXonO7Ek3clMgNyd7oMJ1O6I9N0R4cePZJ2bHYv9CkwpZMDyPpLhOBP7+JKKvSxl4w7YmD6I9TzHGnXmEnzlAoTgIlVySEMwFc93/u316rjjK9CWeBhu8IreTpLIaoUwCcLySmKAV14dA5VhtGrINlZ3xhDPgft8tdZmEnLu9e+oCfaILl82xTUx3HvId5XnHe0j1iUDAsoioD4lvqzDYQVRyFfSiZDmI9fokKiyiuPbnr+r7+8ORAySc3yH6qp1ZA9t3AVH/1go7BHBySvJYR7tkFuNWE52IcbyfaqrX9nziVniVoNw5Sw763lmLR+o7252rCNSrgscHrCjLqwpb75cO38730KCa+reUdydbefrpi3+393qyBYxYNgDB1MXoH1PAgY9IqJ7FFDu7cH/BTd4s9cparUDObprgqvrI/kAH11My4kErV5yVi7HGmbWHtrUVGJGXU8eVCRIE7RhWdSjB2JfprpgqOPvmheKwYK34PA1/e0o7vzhODRUbM8StOdU3djVI6n6uyLmoNBDCZoFhR9ymccUa22ugeaVGNuTgNX/51wAgD6/gX8NznCA4CbzORBBc+utA8BDSDaM6yfDZIx4hgsw8JjQ+QrmOgDiS4IWAcmvvwCHXwRgkz7jnKQpQ1KB9r1whbgDAO11Kd+YETx8rf88TMVIIPcyvLR+XIsDAxwC') format('woff2'); + font-weight: normal; + font-style: normal; +} + + [class^="icon-"]:before, [class*=" icon-"]:before, [class^="icon-"]:after, [class*=" icon-"]:after { + font-family: "icons"; + font-style: normal; + font-weight: normal; + speak: none; + display: inline-block; + text-decoration: inherit; + width: 1em; + text-align: center; + font-size: 1em; + margin: -1px; + padding: 0; + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + line-height: 1em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +/* Font Awesome icons */ +.icon-pencil:before { content: '\e800'; } /* '' */ +.icon-font:before { content: '\e801'; } /* '' */ +.icon-arrows-cw:before { content: '\e802'; } /* '' */ +.icon-doc:before { content: '\e803'; } /* '' */ +.icon-trash-empty:before { content: '\e804'; } /* '' */ +.icon-ok:before { content: '\e805'; } /* '' */ +.icon-ok-circled:before { content: '\e806'; } /* '' */ +.icon-ok-circled2:before { content: '\e807'; } /* '' */ +.icon-link:before { content: '\e808'; } /* '' */ +.icon-globe:before { content: '\e809'; } /* '' */ +.icon-plus:before { content: '\e80a'; } /* '' */ +.icon-plus-circled:before { content: '\e80b'; } /* '' */ +.icon-minus-circled:before { content: '\e80c'; } /* '' */ +.icon-minus:before { content: '\e80d'; } /* '' */ +.icon-text-height:before { content: '\e80e'; } /* '' */ +.icon-adjust:before { content: '\e80f'; } /* '' */ +.icon-tag:before { content: '\e810'; } /* '' */ +.icon-tags:before { content: '\e811'; } /* '' */ +.icon-logout:before { content: '\e812'; } /* '' */ +.icon-download:before { content: '\e813'; } /* '' */ +.icon-down-circled2:before { content: '\e814'; } /* '' */ +.icon-upload:before { content: '\e815'; } /* '' */ +.icon-up-circled2:before { content: '\e816'; } /* '' */ +.icon-cancel-circled2:before { content: '\e817'; } /* '' */ +.icon-cancel-circled:before { content: '\e818'; } /* '' */ +.icon-cancel:before { content: '\e819'; } /* '' */ +.icon-check:before { content: '\e81a'; } /* '' */ +.icon-align-left:before { content: '\e81b'; } /* '' */ +.icon-align-center:before { content: '\e81c'; } /* '' */ +.icon-align-right:before { content: '\e81d'; } /* '' */ +.icon-align-justify:before { content: '\e81e'; } /* '' */ +.icon-star:before { content: '\e81f'; } /* '' */ +.icon-star-empty:before { content: '\e820'; } /* '' */ +.icon-search:before { content: '\e821'; } /* '' */ +.icon-mail:before { content: '\e822'; } /* '' */ +.icon-eye:before { content: '\e823'; } /* '' */ +.icon-eye-off:before { content: '\e824'; } /* '' */ +.icon-pin:before { content: '\e825'; } /* '' */ +.icon-lock-open:before { content: '\e826'; } /* '' */ +.icon-lock:before { content: '\e827'; } /* '' */ +.icon-attach:before { content: '\e828'; } /* '' */ +.icon-home:before { content: '\e829'; } /* '' */ +.icon-info-circled:before { content: '\e82a'; } /* '' */ +.icon-help-circled:before { content: '\e82b'; } /* '' */ +.icon-shuffle:before { content: '\e82c'; } /* '' */ +.icon-ccw:before { content: '\e82d'; } /* '' */ +.icon-cw:before { content: '\e82e'; } /* '' */ +.icon-play:before { content: '\e82f'; } /* '' */ +.icon-play-circled2:before { content: '\e830'; } /* '' */ +.icon-down-big:before { content: '\e831'; } /* '' */ +.icon-left-big:before { content: '\e832'; } /* '' */ +.icon-right-big:before { content: '\e833'; } /* '' */ +.icon-up-big:before { content: '\e834'; } /* '' */ +.icon-up-open:before { content: '\e835'; } /* '' */ +.icon-right-open:before { content: '\e836'; } /* '' */ +.icon-left-open:before { content: '\e837'; } /* '' */ +.icon-down-open:before { content: '\e838'; } /* '' */ +.icon-cloud:before { content: '\e839'; } /* '' */ +.icon-text-width:before { content: '\e83a'; } /* '' */ +.icon-italic:before { content: '\e83b'; } /* '' */ +.icon-bold:before { content: '\e83c'; } /* '' */ +.icon-retweet:before { content: '\e83d'; } /* '' */ +.icon-user:before { content: '\e83e'; } /* '' */ +.icon-users:before { content: '\e83f'; } /* '' */ +.icon-flag:before { content: '\e840'; } /* '' */ +.icon-heart:before { content: '\e841'; } /* '' */ +.icon-heart-empty:before { content: '\e842'; } /* '' */ +.icon-edit:before { content: '\e843'; } /* '' */ +.icon-export:before { content: '\e844'; } /* '' */ +.icon-cog:before { content: '\e845'; } /* '' */ +.icon-cog-alt:before { content: '\e846'; } /* '' */ +.icon-wrench:before { content: '\e847'; } /* '' */ +.icon-resize-vertical:before { content: '\e848'; } /* '' */ +.icon-resize-small:before { content: '\e849'; } /* '' */ +.icon-resize-full:before { content: '\e84a'; } /* '' */ +.icon-resize-horizontal:before { content: '\e84b'; } /* '' */ +.icon-target:before { content: '\e84c'; } /* '' */ +.icon-signal:before { content: '\e84d'; } /* '' */ +.icon-umbrella:before { content: '\e84e'; } /* '' */ +.icon-leaf:before { content: '\e84f'; } /* '' */ +.icon-book:before { content: '\e850'; } /* '' */ +.icon-asterisk:before { content: '\e851'; } /* '' */ +.icon-chart-bar:before { content: '\e852'; } /* '' */ +.icon-key:before { content: '\e853'; } /* '' */ +.icon-hammer:before { content: '\e854'; } /* '' */ +.icon-town-hall:before { content: '\e855'; } /* '' */ +.icon-move:before { content: '\f047'; } /* '' */ +.icon-link-ext:before { content: '\f08e'; } /* '' */ +.icon-check-empty:before { content: '\f096'; } /* '' */ +.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */ +.icon-docs:before { content: '\f0c5'; } /* '' */ +.icon-list-bullet:before { content: '\f0ca'; } /* '' */ +.icon-mail-alt:before { content: '\f0e0'; } /* '' */ +.icon-sitemap:before { content: '\f0e8'; } /* '' */ +.icon-exchange:before { content: '\f0ec'; } /* '' */ +.icon-download-cloud:before { content: '\f0ed'; } /* '' */ +.icon-upload-cloud:before { content: '\f0ee'; } /* '' */ +.icon-plus-squared:before { content: '\f0fe'; } /* '' */ +.icon-circle-empty:before { content: '\f10c'; } /* '' */ +.icon-folder-empty:before { content: '\f114'; } /* '' */ +.icon-folder-open-empty:before { content: '\f115'; } /* '' */ +.icon-flag-empty:before { content: '\f11d'; } /* '' */ +.icon-star-half-alt:before { content: '\f123'; } /* '' */ +.icon-fork:before { content: '\f126'; } /* '' */ +.icon-unlink:before { content: '\f127'; } /* '' */ +.icon-help:before { content: '\f128'; } /* '' */ +.icon-info:before { content: '\f129'; } /* '' */ +.icon-eraser:before { content: '\f12d'; } /* '' */ +.icon-rocket:before { content: '\f135'; } /* '' */ +.icon-anchor:before { content: '\f13d'; } /* '' */ +.icon-lock-open-alt:before { content: '\f13e'; } /* '' */ +.icon-play-circled:before { content: '\f144'; } /* '' */ +.icon-minus-squared:before { content: '\f146'; } /* '' */ +.icon-minus-squared-alt:before { content: '\f147'; } /* '' */ +.icon-level-up:before { content: '\f148'; } /* '' */ +.icon-level-down:before { content: '\f149'; } /* '' */ +.icon-ok-squared:before { content: '\f14a'; } /* '' */ +.icon-pencil-squared:before { content: '\f14b'; } /* '' */ +.icon-expand:before { content: '\f150'; } /* '' */ +.icon-collapse:before { content: '\f151'; } /* '' */ +.icon-expand-right:before { content: '\f152'; } /* '' */ +.icon-sort-alt-up:before { content: '\f160'; } /* '' */ +.icon-sort-alt-down:before { content: '\f161'; } /* '' */ +.icon-female:before { content: '\f182'; } /* '' */ +.icon-male:before { content: '\f183'; } /* '' */ +.icon-sun:before { content: '\f185'; } /* '' */ +.icon-box:before { content: '\f187'; } /* '' */ +.icon-bug:before { content: '\f188'; } /* '' */ +.icon-right-circled2:before { content: '\f18e'; } /* '' */ +.icon-left-circled2:before { content: '\f190'; } /* '' */ +.icon-collapse-left:before { content: '\f191'; } /* '' */ +.icon-dot-circled:before { content: '\f192'; } /* '' */ +.icon-plus-squared-alt:before { content: '\f196'; } /* '' */ +.icon-bank:before { content: '\f19c'; } /* '' */ +.icon-child:before { content: '\f1ae'; } /* '' */ +.icon-tree:before { content: '\f1bb'; } /* '' */ +.icon-history:before { content: '\f1da'; } /* '' */ +.icon-header:before { content: '\f1dc'; } /* '' */ +.icon-sliders:before { content: '\f1de'; } /* '' */ +.icon-trash:before { content: '\f1f8'; } /* '' */ +.icon-brush:before { content: '\f1fc'; } /* '' */ +.icon-chart-area:before { content: '\f1fe'; } /* '' */ +.icon-chart-pie:before { content: '\f200'; } /* '' */ +.icon-chart-line:before { content: '\f201'; } /* '' */ +.icon-user-secret:before { content: '\f21b'; } /* '' */ +.icon-venus:before { content: '\f221'; } /* '' */ +.icon-mars:before { content: '\f222'; } /* '' */ +.icon-venus-mars:before { content: '\f228'; } /* '' */ +.icon-neuter:before { content: '\f22c'; } /* '' */ +.icon-user-plus:before { content: '\f234'; } /* '' */ +.icon-user-times:before { content: '\f235'; } /* '' */ +.icon-object-ungroup:before { content: '\f248'; } /* '' */ +.icon-clone:before { content: '\f24d'; } /* '' */ +.icon-hourglass-1:before { content: '\f251'; } /* '' */ +.icon-hand-grab-o:before { content: '\f255'; } /* '' */ +.icon-hand-paper-o:before { content: '\f256'; } /* '' */ +.icon-calendar-check-o:before { content: '\f274'; } /* '' */ +.icon-map-pin:before { content: '\f276'; } /* '' */ +.icon-map-signs:before { content: '\f277'; } /* '' */ +.icon-map-o:before { content: '\f278'; } /* '' */ +.icon-map:before { content: '\f279'; } /* '' */ +.icon-fort-awesome:before { content: '\f286'; } /* '' */ +.icon-percent:before { content: '\f295'; } /* '' */ + +/* Amended FA icons */ +.icon-sort-name-up:after { font-size: 9px; content: '\f15d'; } +.icon-sort-name-down:after { font-size: 9px; content: '\f15e'; } +.icon-sort-number-up:after { font-size: 9px; content: '\f162'; } +.icon-sort-number-down:after { font-size: 9px; content: '\f163'; } + +/* Custom icons */ +.icon-w:before { font-style: italic; content: 'w:'; } +.icon-f:before { font-style: italic; content: 'f:'; } +.icon-n:before { font-style: italic; content: 'n:'; } +.icon-i:before { font-style: italic; content: 'i:'; } +.icon-s:before { font-style: italic; content: 's:'; } +.icon-r:before { font-style: italic; content: 'r:'; } +.icon-a:before { font-style: italic; content: 'a:'; } +.icon-smooth:before {font-weight: bold; content: '∼'; } +.icon-disrupt:before {font-weight: bold; content: '෴'; } +.icon-if:before {font-style: italic; font-weight: bold; content: 'if'; } +.icon-arc:before {font-weight: bold; font-size: 1.2em; content: '⌒'; } diff --git a/vue-solo/src/images/Facebook.png b/vue-solo/src/images/Facebook.png new file mode 100644 index 00000000..3d249fd9 Binary files /dev/null and b/vue-solo/src/images/Facebook.png differ diff --git a/vue-solo/src/images/Pinterest.png b/vue-solo/src/images/Pinterest.png new file mode 100644 index 00000000..fcc85914 Binary files /dev/null and b/vue-solo/src/images/Pinterest.png differ diff --git a/vue-solo/src/images/Reddit.png b/vue-solo/src/images/Reddit.png new file mode 100644 index 00000000..4637f3a4 Binary files /dev/null and b/vue-solo/src/images/Reddit.png differ diff --git a/vue-solo/src/images/Tumblr.png b/vue-solo/src/images/Tumblr.png new file mode 100644 index 00000000..2b65ddad Binary files /dev/null and b/vue-solo/src/images/Tumblr.png differ diff --git a/vue-solo/src/images/Twitter.png b/vue-solo/src/images/Twitter.png new file mode 100644 index 00000000..05e0c2c2 Binary files /dev/null and b/vue-solo/src/images/Twitter.png differ diff --git a/vue-solo/src/images/favicon-16x16.png b/vue-solo/src/images/favicon-16x16.png new file mode 100644 index 00000000..ddd75b4a Binary files /dev/null and b/vue-solo/src/images/favicon-16x16.png differ diff --git a/vue-solo/src/images/favicon-32x32.png b/vue-solo/src/images/favicon-32x32.png new file mode 100644 index 00000000..e1815ea8 Binary files /dev/null and b/vue-solo/src/images/favicon-32x32.png differ diff --git a/vue-solo/src/images/preview.png b/vue-solo/src/images/preview.png new file mode 100644 index 00000000..aaa8459c Binary files /dev/null and b/vue-solo/src/images/preview.png differ diff --git a/vue-solo/src/index.css b/vue-solo/src/index.css new file mode 100644 index 00000000..ab702933 --- /dev/null +++ b/vue-solo/src/index.css @@ -0,0 +1,1428 @@ +@font-face { + font-family: 'Almendra SC'; + font-style: normal; + font-weight: 400; + src: local('Almendra SC Regular'), local('AlmendraSC-Regular'), url(https://fonts.gstatic.com/s/almendrasc/v8/Iure6Yx284eebowr7hbyTaZOrLQ.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@media print { + div, canvas { + display: none; + } +} + +body { + margin: 0; + border: 0; + font-family: Helvetica, Arial, sans-serif; +} + +svg { + position: absolute; + background-color: #53679f; +} + +canvas { + position: absolute; + pointer-events: none; +} + +input, button, select, a { + outline: none; +} + +button, select, a { + cursor: pointer; +} + +.pointer { + cursor: pointer; +} + +#terrs { + stroke-width: 0.7; + stroke-linejoin: round; + mask: url(#shape); + mask-mode: alpha; +} + +#cults { + stroke-width: 4; + mask: url(#shape); + mask-mode: alpha; + pointer-events: none; +} + +#grid { + display: none; + fill: none; +} + +#landmass { + mask: url(#shape); + mask-clip: no-clip; +} + +#lakes, +#oceanLayers { + fill-rule: evenodd; +} + +#coastline { + fill: none; + stroke-linejoin: round; +} + +#regions { + stroke-width: 2; + stroke: none; + fill-rule: evenodd; + stroke-linejoin: round; + mask: url(#shape); + mask-mode: alpha; + pointer-events: none; +} + +#rivers { + stroke: none; + mask: url(#shape); + cursor: pointer; +} + +#icons { + cursor: pointer; +} + +#terrain { + mask: url(#shape); + mask-mode: alpha; + cursor: pointer; +} + +#hills { + stroke-width: 0.1px; + fill: #999999; +} + +#mounts { + stroke-width: 0.1px; + fill: white; +} + +.strokes { + stroke-width: 0.08px; + width: 2px; + stroke: #5c5c70; + stroke-dasharray: 0.5, 0.7; + stroke-linecap: round; +} + +#borders { + fill: none; +} + +#routes { + fill: none; + cursor: pointer; +} + +#roads, #trails { + mask: url(#shape); + mask-mode: alpha; +} + +#swamps { + stroke-width: 0.05px; + fill: none; + stroke: #5c5c70; +} + +#forests { + stroke-width: 0.1px; + stroke: #5c5c70; +} + +#options .pressed { + background-color: #916e7f; + font-style: italic; +} + +#options i { + cursor: pointer; + color: #382830; + font-size: 9px; +} + +#labelEditor div, #markerEditor div { + display: inline-block; +} + +#labelEditor span { + cursor: pointer; +} + +#labelGroupSelect { + width: 146px; + height: 20px; +} + +#labelGroupInput { + display: none; + width: 142px; +} + +#labelText { + width: 160px; +} + +#labelFontSelect { + width: 129px; +} + +#labelFontInput { + width: 125px; +} + +#textPath { + stroke: #3e3e4b; + stroke-width: .5; + fill: none; +} + +#textPathControl { + stroke: #3e3e4b; + stroke-width: .5; + fill: #ffff00; + cursor: row-resize; +} + +div > input[type="color"].editColor { + height: 18px; + width: 20px; + padding: 0; + cursor: pointer; +} + +div > input[type="range"].editRange { + width: 80px; +} + +div > input[type="number"].editNumber { + width: 44px; +} + +#riverScale { + width: 40px; +} + +#riverAngle, #riverWidthInput, #riverIncrement { + width: 70px; +} + +.editButtonS { + display: none; + cursor: pointer; +} + +.editValue { + display: none; + cursor: default; + font-size: small; + width: 34px; +} + +#riverEditor > *, +#routeEditor > *, +#iconEditor > *, +#reliefEditor > *, +#burgEditor * { + display: inline-block; +} + +#labels { + text-anchor: middle; + dominant-baseline: central; + text-shadow: 0 0 4px white; + cursor: pointer; +} + +#burgLabels { + dominant-baseline: alphabetic; +} + +#routeLength { + background-color: #f3f3f3; + border: 1px solid #a5a5a5; + padding: 3px; + font-size: 11px; + cursor: default; +} + +.tag { + fill: #fffa90; + stroke: #333333; + stroke-width: 1.4px; +} + +.line { + stroke: #373737; + stroke-width: 1px; + stroke-dasharray: 6; + stroke-linecap: butt; +} + +.circle { + stroke-width: 1px; + fill: none; + stroke-dasharray: 6; + stroke-linecap: butt; +} + +circle.drag { + stroke: #9f3237; +} + +text.drag { + text-shadow: 0 0 1px red; +} + +.draggable { + cursor: move; +} + +.ui-dialog, #optionsContainer { + -moz-user-select: none; + user-select: none; +} + +#options { + margin: 10px; + display: none; + font-size: smaller; + font-family: monospace; + position: absolute; + border: solid 1px #5e4fa2; +} + +.tab { + overflow: hidden; + border-bottom: 1px solid #5d4651; + height: 28px; +} + +button.options { + background-color: #997c89; + font-family: monospace; + font-weight: bold; + float: left; + border: none; + padding: 8px 14px; + transition: 0.2s; + font-size: 1em; +} + +#options p { + font-style: italic; + font-weight: bold; +} + +#aboutContent { + text-align: justify; +} + +#aboutContent p { + font-style: italic; + font-weight: normal; +} + +#aboutContent a { + color: #1d1b1c; + font-weight: bold; +} + +#options input[type="color"], +#convertImageDialog input[type="color"] { + width: 38px; + padding: 0; + border: 0; + background: none; + cursor: pointer; +} + +#options input[type="range"] { + width: 120px; + height: 2px; + background: #ffffff; + top: -2px; + position: relative; + appearance: none; + -webkit-appearance: none; +} + +#options input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + border-radius: 15%; + width: 10px; + height: 10px; + background: #916e7f; + border: 1px solid #5d4651; + cursor: pointer; +} + +#options input[type="range"]::-moz-range-thumb { + -moz-appearance: none; + border-radius: 15%; + width: 10px; + height: 10px; + background: #916e7f; + border: 1px solid #5d4651; + cursor: pointer; +} + +#options select { + height: 14px; + width: 122px; + border: 0; + font-size: smaller; + font-family: monospace; + cursor: pointer; +} + +#options .buttonoff { + background-color: #b6b4b440; + color: grey; +} + +#sticked button { + background-color: rgba(153, 124, 137, 0); + padding: 0; + margin: 1px 17px; +} + +#collapsible { + margin: 10px; + border: 1px solid transparent; + position: absolute; + z-index: 2; +} + +#collapsible>button { + height: 28px; +} + +#optionsTrigger { + width: 19px; + font-size: 9px; + padding: 0; +} + +#regenerate { + display: none; + padding: 0px 8px; +} + +.glow { + animation: glowing 3s infinite; +} + +@keyframes glowing { + 0% { + box-shadow: 0 0 -4px #ded2d8; + } + 50% { + box-shadow: 0 0 6px #ded2d8; + } + 100% { + box-shadow: 0 0 -4px #ded2d8; + } +} + +button.options:hover { + background-color: #806070; + color: white; +} + +button.active { + background-color: #916e7f; + color: white; +} + +#layoutTab { + margin-left: 19px; +} + +.tabcontent { + display: none; + padding: 0 6px 2px 12px; + opacity: 0.8; + max-width: 290px; +} + +.tabcontent button { + background-color: #916e7f; + font-family: monospace; + border: none; + padding: 5px 8px; + margin: 4px 0; + transition: 0.1s; + font-size: 1em; +} + +.tabcontent button:hover { + background-color: #a8879d; +} + +#mapLayers { + display: inline-block; + padding: 0; + margin: 0; +} + +fieldset { + border: 1px solid #5d4651; +} + +.tabcontent li { + list-style-type: none; + background-color: #916e7f; + cursor: pointer; + padding: 5px 8px; + margin: 4px; + transition: 0.1s; + float: left; +} + +.tabcontent li:hover { + background-color: #a8879d; +} + +.tabcontent li.solid { + color: #42383f; +} + +p { + margin-bottom: 0; +} + +#optionsContainer span { + cursor: default; +} + +.pairedNumber { + width: 36px; + line-height: 16px; + height: 10px; + font-size: smaller; + font-family: monospace; +} + +#cellInfo>div { + margin: 5px; + display: inline-block; + vertical-align: top; +} + +#cellInfo>div:nth-child(2) { + width: 45%; +} + +#customizeOptions { + margin: 2px 0; +} + +#tooltip { + position: fixed; + display: none; + text-align: center; + bottom: 0.5vw; + width: 70%; + left: 15%; + cursor: default; + text-shadow: 1px 1px 2px #1d0e0f; + color: #ffffff; + font-size: calc(10px + 0.5vw); + pointer-events: none; + white-space: pre-line; +} + +#optionsContent table td:nth-of-type(1) { + width: 8px; +} + +#optionsContent table td:nth-of-type(2) { + width: 126px; +} + +#optionsContent table td:nth-of-type(4) { + text-align: right; + width: 18px; +} + +.overflow-div { + height: 300px; + overflow-y: auto; + user-select: text; +} + +.overflow-table { + width: 100%; + font-size: smaller; + text-align: center; +} + +#sizeOutput { + color: green; +} + +#icons { + stroke: #0d0d0d; + fill: grey; +} + +.setColors { + display: inline-block; +} + +body button.noicon { + width: 24px; + height: 20px; + margin: 1px; + padding: 1px 6px; + float: left; + font-family: Copperplate, monospace; +} + +#brushesPanel>div, +#templateEditor>div { + margin: 2px 0; +} + +#templateEditor #templateTools { + display: inline-block; + margin-bottom: -3px; +} + +#templateSelect { + width: 150px; +} + +#templateBody>div { + border: 1px solid #a3a3a3; + border-radius: 1px; + background-image: linear-gradient(to right, #ffffff 0%, #fafafa 51%, #ebebeb 100%); + margin: 1px 1px; + width: 226px; + padding: 0px 2px; + height: 12px; + font-size: 10px; +} + +#templateBody>div:hover { + border-color: #808080; + background-image: linear-gradient(to right, #fcfcfc 0%, #ededed 51%, #dedede 100%); +} + +#templateBody span { + display: inline-block; + margin: 0 1px; + float: right; + cursor: pointer; +} + +#templateBody span:hover { + color: #297cb8; +} + +#templateBody label { + float: right; + margin-right: 4px; +} + +#templateBody label:first-of-type { + margin-right: 12px; +} + +#templateBody input { + width: 40px; + height: 10px; + border: none; + font-family: monospace; +} + +#templateBody select { + border: 0; + background-color: rgba(255, 255, 255, 0); + width: 58px; + cursor: pointer; +} + +.controlPoints { + fill: #ff0000; + stroke: #841f1f; + stroke-width: 0.1; + cursor: move; + opacity: .8; +} + +.drag-trigger { + border-left: 12px solid transparent; + border-right: 12px solid #916e7f; + border-top: 12px solid transparent; + position: absolute; + right: 0; + top: 100%; + margin-top: -12px; +} + +.drag-trigger:hover { + cursor: move; + border-right-color: #5e4fa2; +} + +#styleInputs > div { + display: none; + line-height: 8px; +} + +#styleInputs #styleOcean, +#styleInputs #styleOpacity, +#styleInputs #styleFilter { + display: block; +} + +#styleInputs .whiteButton { + padding: 0 6px; + margin: 0 2px; + border: 1px #827c7f solid; + background-color: #ffffff; +} + +#restoreStyle { + cursor: pointer; + font-size: xx-small; +} + +#styleLabelGroups { + margin-top: 6px; + display: block; +} + +#styleLabelGroups button { + display: inline-block; + margin: 5px 3px 0 3px; + padding: 2px 6px; +} + +.pureInput { + display: inline-block; + width: 50px; + height: 10px; + font-size: smaller; + font-family: monospace; +} + +.tint { + filter: sepia(1) hue-rotate(200deg); +} + +.color-div { + width: 32px; + height: 12px; + display: inline-block; + margin: 1px 2px; + border: 1px #c5c5c5 groove; + cursor: pointer; +} + +#colorsSelect div { + height: 18px; + display: inline-block; + cursor: pointer; +} + +.color-div:hover { + border-color: red; +} + +.hoveredColor { + box-shadow: 0 0 1px 1px #717171; +} + +.selectedColor { + outline: 2px solid #f87b66; +} + +#colorScheme { + margin: 6px 1px 4px 1px; +} + +#colorsSelectValue { + font-size: larger; + position: relative; + font-family: monospace; + font-weight: bold; + top: -3px; +} + +.selectedCell { + stroke-width: 1; + stroke: #da3126; +} + +.ui-dialog input { + height: 14px; +} + +.ui-dialog button.pressed { + box-shadow: inset 1px 1px 0 0 #ccc; + border-color: #a6a6da; + background-color: #ecd8d8; +} + +.ui-dialog input[type="range"] { + outline: none; + height: 2px; + background: #d4d4d4; + top: -4px; + position: relative; + appearance: none; + -webkit-appearance: none; +} + +.ui-dialog input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + border-radius: 15%; + width: 10px; + height: 10px; + background: #e9e9e9; + border: 1px solid #9b9b9b; + cursor: pointer; +} + +.ui-dialog input[type="range"]::-moz-range-thumb { + appearance: none; + border-radius: 15%; + width: 10px; + height: 10px; + background: #e9e9e9; + border: 1px solid #9b9b9b; + cursor: pointer; +} + +.ui-dialog input[type="number"] { + width: 28px; + height: 12px; + cursor: pointer; +} + +.ui-dialog .disabled { + opacity: 0.2; +} + +.ui-dialog .disabled::slider-thumb { + opacity: 0.2; +} + +.ui-dialog .disabled::-moz-range-thumb { + opacity: 0.2; +} + +.ui-dialog:disabled { + cursor: default; +} + +div.slider { + width: 40em; + margin-top: 0.2em; +} + +div.slider .ui-slider-handle { + width: 3em; + height: 1.6em; + top: 50%; + margin-top: -.8em; + text-align: center; + line-height: 1.6em; +} + +#saveDropdown { + display: none; + position: absolute; + left: 29%; + top: 100%; + border: 1px solid #5e4fa2; + background-color: #a4879b; + width: 44px; +} + +#saveDropdown>div { + padding: 2px 4px; + cursor: pointer; +} + +#saveDropdown>div:hover { + color: white; +} + +#brushPower, +#brushRadius { + width: 88px; +} + +#rescaleHigher, +#rescaleLower, +#rescaleModifier { + width: 40px; +} + +#rescaler { + width: 175px; + top: -2px; +} + +.italic { + font-style: italic; +} + +.hidden { + display: none; +} + +.sortable { + font-weight: bold; + font-size: 10px; + cursor: pointer; + display: inline-block; +} + +.totalLine { + color: #666666; + font-style: italic; + font-size: 10px; + margin-bottom: 3px; +} + +.totalLine>div { + display: inline-block; +} + +div.states { + border: 1px solid #d4d4d4; + background-image: linear-gradient(to right, #fafafa80 0%, #f0f0f080 50%, #c8c8c880 100%); + margin: 1px 0; + padding: 0 2px; + font-size: 10px; +} + +div.states:hover { + border: 1px solid #c4c4c4; + background-image: linear-gradient(to right, #dedede 100%, #f2f2f2 50%, #fcfcfc 0%); +} + +div.states * { + display: inline-block; +} + +div.states sup { + display: inline-block; +} + +div.states>input { + width: 60px; + background: none; + border: 0; +} + +div.states>input.stateColor { + width: 13px; + height: 17px; + padding: 0px; + margin-right: -1px; + border: 0; + background: none; + cursor: pointer; +} + +div.states div { + width: 32px; +} + +div.states .statePower { + width: 32px; + line-height: 14px; +} + +div.states .stateBurgs { + width: 24px; +} + +div.states>.stateArea { + width: 50px; +} + +div.states>.statePopulation { + width: 30px; +} + +div.states .stateBurgs, +div.states .stateBIcon, +div.states .icon-trash-empty { + cursor: pointer; +} + +div.states>[class^="icon-"] { + color: #6e5e66; + padding: 0 1px 0 7px; +} + +div.states>[class="icon-arrows-cw"] { + color: #67575c; + padding: 0 2px 0 0; + font-size: 9px; + cursor: pointer; +} + +div.states>.before { + color: #6e5e66; + padding: 0 1px 0 0; +} + +div.states>.small { + font-size: 9px; +} + +div.states>.cultureName { + width: 50px; +} + +div.states>.culturePopulation { + width: 40px; +} + +div.states>.cultureBase { + width: 46px; + cursor: pointer; + border: 0; + background-color: transparent; + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; +} + +#burgsBody, +#countriesBody { + overflow: auto; + max-height: 362px; +} + +#countriesBody { + min-width: 366px; +} + +#burgsBody { + min-width: 260px; +} + +div.states .burgName, +div.states .burgCulture { + width: 56px; +} + +div.states .burgPopulation { + width: 30px; +} + +#burgsFooterPopulation { + border: 0; + width: 50px; + color: #666666; + font-style: italic; + line-height: 14px; +} + +div.states .enlange { + cursor: pointer; +} + +#countriesEditor div>.hidden { + display: none; +} + +.placeholder { + opacity: 0; +} + +span.ui-dialog-title>input.stateColor { + width: 14px; + height: 18px; + border: 0; + background: none; + cursor: pointer; +} + +div.states.selected { + border: 1px solid #b28585; + background-image: linear-gradient(to right, #e5dada 100%, #f2f2f2 51%, #fcfcfc 0%); +} + +div.states button.selectCapital { + margin: -1px 21px 0 7px; + padding: 0px 3px; +} + +#scaleBody { + margin-left: 14px; +} + +#scaleBody>div>* { + display: inline-block; + font-size: 11px; +} + +#scaleBody>div>div { + width: 100px; +} + +#scaleBody>div>select { + width: 110px; + border: 1px solid #e9e9e9; +} + +#scaleBody>div>input[type="text"] { + width: 110px; + border: 0; +} + +#scaleBody>div>input[type="range"] { + width: 80px; +} + +#scaleBody>div>input.output { + width: 30px; +} + +.scaleHeader { + margin-left: -10px; + font-weight: bold; + font-style: italic; + margin-top: 6px; +} + +#ruler { + cursor: move; +} + +#ruler circle { + stroke: #4e5a69; + fill: yellow; +} + +#ruler .white { + stroke: white; +} + +#ruler .gray { + stroke: #3d3d3d; +} + +#ruler text { + font-family: tahoma; + fill: #3d3d3d; + stroke: none; + text-anchor: middle; + dominant-baseline: ideographic; + text-shadow: 0 0 4px white; + cursor: pointer; +} + +#ruler .opisometer { + fill: none; +} + +#ruler .planimeter { + fill: lightblue; + fill-rule: evenodd; + fill-opacity: 0.5; + stroke: #737373; +} + +#scaleBar { + stroke: none; + fill: none; + cursor: move; +} + +#scaleBar text { + fill: #353540; + text-anchor: middle; + font-family: Georgia; +} + +#scaleBottom { + margin: 6px 0 0 6px; +} + +#barBackColor { + width: 24px; + height: 16px; + padding: 0px; + border: 0; + background: none; + cursor: pointer; +} + +#overlay { + fill: none; +} + +#loading { + color: #fff5da; + text-align: center; + text-shadow: 0px 1px 4px #4c3a35; + max-width: 780px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + cursor: default; + -moz-user-select: none; + user-select: none; +} + +#title_name { + text-align: left; + font-size: 2em; + margin-left: 5%; +} + +#title { + font-size: 4.5em; + margin: -12px 0 -6px 0; +} + +#version { + text-align: right; + font-size: 1.2em; +} + +#initial { + fill: none; + stroke: black; +} + +#init-rose { + stroke-dasharray: 1; + /*animation: spin 30s infinite ease-in-out;*/ + opacity: .7; + transform: translate(50%, 50%); +} + +@keyframes spin { + 0% { + transform: translate(50%, 50%) rotate(0deg); + } + 100% { + transform: translate(50%, 50%) rotate(359deg); + } +} + +#loading-text span, +#uploading-map span { + animation-name: blink; + animation-duration: 3s; + animation-iteration-count: infinite; + animation-fill-mode: both; +} + +#loading-text span:nth-child(2) { + animation-delay: 1s; +} + +#loading-text span:nth-child(3) { + animation-delay: 2s; +} + +@keyframes blink { + 0% { + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + opacity: .1; + } +} + +ul.share-buttons li { + display: inline; + float: none; + padding: 4px; + background: 0; +} + +ul.share-buttons img { + width: 16px; +} + +input[type="checkbox"] { + display: none; +} + +.checkbox, +.checkbox-label { + margin: 5px; + cursor: pointer; +} + +.checkbox+.checkbox-label:before { + content: ''; + background: #ece6eb; + border-radius: 1px; + display: inline-block; + vertical-align: text-top; + width: 7px; + height: 7px; + padding: 2px; + margin-right: 3px; +} + +.checkbox:checked+.checkbox-label:before { + background: #997c89; + transition: .2s; + box-shadow: inset 0px 0px 0px 2px #ece6ea; +} + +#map-dragged { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 10; + pointer-events: none; + text-align: center; + background: rgba(0, 0, 0, .5); +} + +#map-dragged p { + font-size: 2.4em; + color: #fff5da; + text-shadow: 0px 1px 4px #4c3a35; +} + +#map-dragged p:before { + content: ''; + display: inline-block; + vertical-align: middle; + height: 100%; +} + +#cultureCenters circle:hover { + stroke: #000000b3; + cursor: pointer; +} + +div.textual select, +div.textual textarea { + font-size: 10px; + max-width: 366px; + font-family: Copperplate, monospace; + outline: none; +} + +div.textual input { + font-size: 10px; + font-family: Copperplate, monospace; + outline: none; + height: 12px; +} + +div.textual fieldset { + margin: 3px 3px 5px 0; + border-style: dashed; +} + +div.textual span, .textual legend { + font-size: 9px; + font-weight: bold; +} + +#namesbaseExamples { + font-family: Copperplate, monospace; + cursor: pointer; +} + +#namesbaseName { + width: 80px; +} + +#namesbaseMin, #namesbaseMax { + width: 33px; +} + +#namesbaseDouble { + width: 40px; +} + +#markers { + cursor: pointer; + font-family: monospace; + -moz-user-select: none; + user-select: none; + text-anchor: middle; +} + +#markerEditor > button { + vertical-align: top; +} + +#markerIconTable { + font-size: 12px; + cursor: pointer; +} + +#markerIconTable td:active { + transform: translate(0px, 1px); +} + +#markerIconTable td.selected { + outline: 1px solid #9b9b9b; +} + +.highlighted { + outline-width: 2px; + outline-style: dashed; + outline-color: #0da6ff; + outline-offset: 100px; + fill: none; +} + +div#legend { + display: none; + position: fixed; + width: 25vw; + right: 1vw; + top: 1vw; + font-size: 0.9em; + border: 1px solid #5e4fa2; + background: #cdb99040; + box-shadow: 2px 2px 5px -3px #3a2804; + white-space: pre-line; + -moz-user-select: none; + user-select: none; +} + +div#legendHeader { + font-weight: bold; + padding: 0 0 4px 14px; + border-bottom: 1px solid #5e4fa2; +} + +div#legendBody { + padding: 0 10px; +} diff --git a/vue-solo/src/index.html b/vue-solo/src/index.html new file mode 100644 index 00000000..5607b3f8 --- /dev/null +++ b/vue-solo/src/index.html @@ -0,0 +1,1305 @@ + + + + + + + + + Azgaar's Fantasy Map Generator + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Port + + + + + + + + ? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    Azgaar's
    +
    Fantasy Map Generator
    +
    v. 0.60b
    +

    LOADING...

    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    +
    +
    + +
    Сlick the arrow button to open options
    + + +
    +
    + diff --git a/vue-solo/src/libs/d3-scale-chromatic.v1.min.js b/vue-solo/src/libs/d3-scale-chromatic.v1.min.js new file mode 100644 index 00000000..d28ea375 --- /dev/null +++ b/vue-solo/src/libs/d3-scale-chromatic.v1.min.js @@ -0,0 +1,2 @@ +// https://d3js.org/d3-scale-chromatic/ Version 1.2.0. Copyright 2018 Mike Bostock. +!function(f,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("d3-interpolate"),require("d3-color")):"function"==typeof define&&define.amd?define(["exports","d3-interpolate","d3-color"],e):e(f.d3=f.d3||{},f.d3,f.d3)}(this,function(f,e,d){"use strict";function c(f){for(var e=f.length/6|0,d=new Array(e),c=0;c1)&&(f-=Math.floor(f));var e=Math.abs(f-.5);return Af.h=360*f-100,Af.s=1.5-1.5*e,Af.l=.8-.9*e,Af+""},f.interpolateWarm=yf,f.interpolateCool=wf,f.interpolateViridis=Bf,f.interpolateMagma=Gf,f.interpolateInferno=Pf,f.interpolatePlasma=Rf,Object.defineProperty(f,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/vue-solo/src/libs/d3.v4.min.js b/vue-solo/src/libs/d3.v4.min.js new file mode 100644 index 00000000..607d1879 --- /dev/null +++ b/vue-solo/src/libs/d3.v4.min.js @@ -0,0 +1,2 @@ +// https://d3js.org Version 4.13.0. Copyright 2018 Mike Bostock. +(function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})})(this,function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){return 1===t.length&&(t=function(t){return function(e,r){return n(t(e),r)}}(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}function r(t,n){return[t,n]}function i(t){return null===t?NaN:+t}function o(t,n){var e,r,o=t.length,u=0,a=-1,c=0,s=0;if(null==n)for(;++a1)return s/(u-1)}function u(t,n){var e=o(t,n);return e?Math.sqrt(e):e}function a(t,n){var e,r,i,o=t.length,u=-1;if(null==n){for(;++u=e)for(r=i=e;++ue&&(r=e),i=e)for(r=i=e;++ue&&(r=e),i0)return[t];if((r=n0)for(t=Math.ceil(t/u),n=Math.floor(n/u),o=new Array(i=Math.ceil(n-t+1));++a=0?(o>=Hs?10:o>=js?5:o>=Xs?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=Hs?10:o>=js?5:o>=Xs?2:1)}function p(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=Hs?i*=10:o>=js?i*=5:o>=Xs&&(i*=2),n=1)return+e(t[r-1],r-1,t);var r,o=(r-1)*n,u=Math.floor(o),a=+e(t[u],u,t);return a+(+e(t[u+1],u+1,t)-a)*(o-u)}}function g(t){for(var n,e,r,i=t.length,o=-1,u=0;++o=0;)for(n=(r=t[i]).length;--n>=0;)e[--u]=r[n];return e}function _(t,n){var e,r,i=t.length,o=-1;if(null==n){for(;++o=e)for(r=e;++oe&&(r=e)}else for(;++o=e)for(r=e;++oe&&(r=e);return r}function y(t){if(!(i=t.length))return[];for(var n=-1,e=_(t,m),r=new Array(e);++n=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),tf.hasOwnProperty(n)?{space:tf[n],local:t}:t}function A(t){var n=E(t);return(n.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===Ks&&n.documentElement.namespaceURI===Ks?n.createElement(t):n.createElementNS(e,t)}})(n)}function C(){}function z(t){return null==t?C:function(){return this.querySelector(t)}}function P(){return[]}function R(t){return null==t?P:function(){return this.querySelectorAll(t)}}function L(t){return new Array(t.length)}function q(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function D(t,n,e,r,i,o){for(var u,a=0,c=n.length,s=o.length;an?1:t>=n?0:NaN}function F(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function I(t,n){return t.style.getPropertyValue(n)||F(t).getComputedStyle(t,null).getPropertyValue(n)}function Y(t){return t.trim().split(/^|\s+/)}function B(t){return t.classList||new H(t)}function H(t){this._node=t,this._names=Y(t.getAttribute("class")||"")}function j(t,n){for(var e=B(t),r=-1,i=n.length;++r>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1)):(n=df.exec(t))?At(parseInt(n[1],16)):(n=vf.exec(t))?new Rt(n[1],n[2],n[3],1):(n=gf.exec(t))?new Rt(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=_f.exec(t))?Ct(n[1],n[2],n[3],n[4]):(n=yf.exec(t))?Ct(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=mf.exec(t))?Lt(n[1],n[2]/100,n[3]/100,1):(n=xf.exec(t))?Lt(n[1],n[2]/100,n[3]/100,n[4]):bf.hasOwnProperty(t)?At(bf[t]):"transparent"===t?new Rt(NaN,NaN,NaN,0):null}function At(t){return new Rt(t>>16&255,t>>8&255,255&t,1)}function Ct(t,n,e,r){return r<=0&&(t=n=e=NaN),new Rt(t,n,e,r)}function zt(t){return t instanceof St||(t=Et(t)),t?(t=t.rgb(),new Rt(t.r,t.g,t.b,t.opacity)):new Rt}function Pt(t,n,e,r){return 1===arguments.length?zt(t):new Rt(t,n,e,null==r?1:r)}function Rt(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Lt(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Dt(t,n,e,r)}function qt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Dt)return new Dt(t.h,t.s,t.l,t.opacity);if(t instanceof St||(t=Et(t)),!t)return new Dt;if(t instanceof Dt)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&c<1?0:u,new Dt(u,a,c,t.opacity)}(t):new Dt(t,n,e,null==r?1:r)}function Dt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Ut(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Ot(t){if(t instanceof It)return new It(t.l,t.a,t.b,t.opacity);if(t instanceof Vt){var n=t.h*wf;return new It(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof Rt||(t=zt(t));var e=jt(t.r),r=jt(t.g),i=jt(t.b),o=Yt((.4124564*e+.3575761*r+.1804375*i)/Tf),u=Yt((.2126729*e+.7151522*r+.072175*i)/Nf);return new It(116*u-16,500*(o-u),200*(u-Yt((.0193339*e+.119192*r+.9503041*i)/kf)),t.opacity)}function Ft(t,n,e,r){return 1===arguments.length?Ot(t):new It(t,n,e,null==r?1:r)}function It(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Yt(t){return t>Cf?Math.pow(t,1/3):t/Af+Sf}function Bt(t){return t>Ef?t*t*t:Af*(t-Sf)}function Ht(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function jt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Xt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Vt)return new Vt(t.h,t.c,t.l,t.opacity);t instanceof It||(t=Ot(t));var n=Math.atan2(t.b,t.a)*Mf;return new Vt(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}(t):new Vt(t,n,e,null==r?1:r)}function Vt(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function $t(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Wt)return new Wt(t.h,t.s,t.l,t.opacity);t instanceof Rt||(t=zt(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Df*r+Lf*n-qf*e)/(Df+Lf-qf),o=r-i,u=(Rf*(e-i)-zf*o)/Pf,a=Math.sqrt(u*u+o*o)/(Rf*i*(1-i)),c=a?Math.atan2(u,o)*Mf-120:NaN;return new Wt(c<0?c+360:c,a,i,t.opacity)}(t):new Wt(t,n,e,null==r?1:r)}function Wt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Zt(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Gt(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=r180||e<-180?e-360*Math.round(e/360):e):Jt(isNaN(t)?n:t)}function nn(t){return 1==(t=+t)?en:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):Jt(isNaN(n)?e:n)}}function en(t,n){var e=n-t;return e?Kt(t,e):Jt(isNaN(t)?n:t)}function rn(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;eo&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:an(e,r)})),o=$f.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:an(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:an(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,c),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:an(t,e)},{i:a-2,x:an(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,c),o=u=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--cl}function Tn(){pl=(hl=vl.now())+dl,cl=sl=0;try{Mn()}finally{cl=0,function(){var t,n,e=Yf,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Yf=n);Bf=t,kn(r)}(),pl=0}}function Nn(){var t=vl.now(),n=t-hl;n>ll&&(dl-=n,hl=t)}function kn(t){if(!cl){sl&&(sl=clearTimeout(sl));t-pl>24?(t<1/0&&(sl=setTimeout(Tn,t-vl.now()-dl)),fl&&(fl=clearInterval(fl))):(fl||(hl=vl.now(),fl=setInterval(Nn,ll)),cl=1,gl(Tn))}}function Sn(t,n,e){var r=new bn;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r}function En(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};(function(t,n,e){function r(c){var s,f,l,h;if(e.state!==xl)return o();for(s in a)if((h=a[s]).name===e.name){if(h.state===wl)return Sn(r);h.state===Ml?(h.state=Nl,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete a[s]):+sml)throw new Error("too late; already scheduled");return e}function Cn(t,n){var e=zn(t,n);if(e.state>bl)throw new Error("too late; already started");return e}function zn(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Pn(t,n){var e,r,i,o=t.__transition,u=!0;if(o){n=null==n?null:n+"";for(i in o)(e=o[i]).name===n?(r=e.state>bl&&e.stateMath.abs(t[1]-D[1])?b=!0:x=!0),D=t,m=!0,$n(),o()}function o(){var t;switch(_=D[0]-q[0],y=D[1]-q[1],T){case th:case Kl:N&&(_=Math.max(C-a,Math.min(P-p,_)),s=a+_,d=p+_),k&&(y=Math.max(z-l,Math.min(R-v,y)),h=l+y,g=v+y);break;case nh:N<0?(_=Math.max(C-a,Math.min(P-a,_)),s=a+_,d=p):N>0&&(_=Math.max(C-p,Math.min(P-p,_)),s=a,d=p+_),k<0?(y=Math.max(z-l,Math.min(R-l,y)),h=l+y,g=v):k>0&&(y=Math.max(z-v,Math.min(R-v,y)),h=l,g=v+y);break;case eh:N&&(s=Math.max(C,Math.min(P,a-_*N)),d=Math.max(C,Math.min(P,p+_*N))),k&&(h=Math.max(z,Math.min(R,l-y*k)),g=Math.max(z,Math.min(R,v+y*k)))}d0&&(a=s-_),k<0?v=g-y:k>0&&(l=h-y),T=th,F.attr("cursor",uh.selection),o());break;default:return}$n()},!0).on("keyup.brush",function(){switch(t.event.keyCode){case 16:L&&(x=b=L=!1,o());break;case 18:T===eh&&(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=nh,o());break;case 32:T===th&&(t.event.altKey?(N&&(p=d-_*N,a=s+_*N),k&&(v=g-y*k,l=h+y*k),T=eh):(N<0?p=d:N>0&&(a=s),k<0?v=g:k>0&&(l=h),T=nh),F.attr("cursor",uh[M]),o());break;default:return}$n()},!0).on("mousemove.brush",e,!0).on("mouseup.brush",u,!0);_t(t.event.view)}Vn(),Pn(w),r.call(w),U.start()}}function a(){var t=this.__brush||{selection:null};return t.extent=s.apply(this,arguments),t.dim=n,t}var c,s=Gn,f=Zn,l=N(e,"start","brush","end"),h=6;return e.move=function(t,e){t.selection?t.on("start.brush",function(){i(this,arguments).beforestart().start()}).on("interrupt.brush end.brush",function(){i(this,arguments).end()}).tween("brush",function(){function t(t){u.selection=1===t&&Jn(s)?null:f(t),r.call(o),a.brush()}var o=this,u=o.__brush,a=i(o,arguments),c=u.selection,s=n.input("function"==typeof e?e.apply(this,arguments):e,u.extent),f=fn(c,s);return c&&s?t:t(1)}):t.each(function(){var t=arguments,o=this.__brush,u=n.input("function"==typeof e?e.apply(this,t):e,o.extent),a=i(this,t).beforestart();Pn(this),o.selection=null==u||Jn(u)?null:u,r.call(this),a.start().brush().end()})},o.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting&&(this.starting=!1,this.emit("start")),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(t){it(new function(t,n,e){this.target=t,this.type=n,this.selection=e}(e,t,n.output(this.state.selection)),l.apply,l,[t,this.that,this.args])}},e.extent=function(t){return arguments.length?(s="function"==typeof t?t:Xn([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),e):s},e.filter=function(t){return arguments.length?(f="function"==typeof t?t:Xn(!!t),e):f},e.handleSize=function(t){return arguments.length?(h=+t,e):h},e.on=function(){var t=l.on.apply(l,arguments);return t===l?e:t},e}function te(t){return function(){return t}}function ne(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function ee(){return new ne}function re(t){return t.source}function ie(t){return t.target}function oe(t){return t.radius}function ue(t){return t.startAngle}function ae(t){return t.endAngle}function ce(){}function se(t,n){var e=new ce;if(t instanceof ce)t.each(function(t,n){e.set(n,t)});else if(Array.isArray(t)){var r,i=-1,o=t.length;if(null==n)for(;++i=u?s=!0:(e=t.charCodeAt(a++))===Nh?f=!0:e===kh&&(f=!0,t.charCodeAt(a)===Nh&&++a),t.slice(r+1,n-1).replace(/""/g,'"')}for(;a=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u,i=p,!(p=p[l=f<<1|s]))return i[l]=d,t;if(a=+t._x.call(null,p.data),c=+t._y.call(null,p.data),n===a&&e===c)return d.next=p,i?i[l]=d:t._root=d,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(s=n>=(o=(v+_)/2))?v=o:_=o,(f=e>=(u=(g+y)/2))?g=u:y=u}while((l=f<<1|s)==(h=(c>=u)<<1|a>=o));return i[h]=p,i[l]=d,t}function be(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function we(t){return t[0]}function Me(t){return t[1]}function Te(t,n,e){var r=new Ne(null==n?we:n,null==e?Me:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Ne(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function ke(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}function Se(t){return t.x+t.vx}function Ee(t){return t.y+t.vy}function Ae(t){return t.index}function Ce(t,n){var e=t.get(n);if(!e)throw new Error("missing: "+n);return e}function ze(t){return t.x}function Pe(t){return t.y}function Re(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Le(t){return(t=Re(Math.abs(t)))?t[1]:NaN}function qe(t,n){var e=Re(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}function De(t){return new Ue(t)}function Ue(t){if(!(n=Bh.exec(t)))throw new Error("invalid format: "+t);var n,e=n[1]||" ",r=n[2]||">",i=n[3]||"-",o=n[4]||"",u=!!n[5],a=n[6]&&+n[6],c=!!n[7],s=n[8]&&+n[8].slice(1),f=n[9]||"";"n"===f?(c=!0,f="g"):Yh[f]||(f=""),(u||"0"===e&&"="===r)&&(u=!0,e="0",r="="),this.fill=e,this.align=r,this.sign=i,this.symbol=o,this.zero=u,this.width=a,this.comma=c,this.precision=s,this.type=f}function Oe(t){return t}function Fe(t){function n(t){function n(t){var n,r,u,f=g,x=_;if("c"===v)x=y(t)+x,t="";else{var b=(t=+t)<0;if(t=y(Math.abs(t),d),b&&0==+t&&(b=!1),f=(b?"("===s?s:"-":"-"===s||"("===s?"":s)+f,x=("s"===v?jh[8+Oh/3]:"")+x+(b&&"("===s?")":""),m)for(n=-1,r=t.length;++n(u=t.charCodeAt(n))||u>57){x=(46===u?i+t.slice(n+1):t.slice(n))+x,t=t.slice(0,n);break}}p&&!l&&(t=e(t,1/0));var w=f.length+t.length+x.length,M=w>1)+f+t+x+M.slice(w);break;default:t=M+f+t+x}return o(t)}var a=(t=De(t)).fill,c=t.align,s=t.sign,f=t.symbol,l=t.zero,h=t.width,p=t.comma,d=t.precision,v=t.type,g="$"===f?r[0]:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",_="$"===f?r[1]:/[%p]/.test(v)?u:"",y=Yh[v],m=!v||/[defgprs%]/.test(v);return d=null==d?v?6:12:/[gprs]/.test(v)?Math.max(1,Math.min(21,d)):Math.max(0,Math.min(20,d)),n.toString=function(){return t+""},n}var e=t.grouping&&t.thousands?function(t,n){return function(e,r){for(var i=e.length,o=[],u=0,a=t[0],c=0;i>0&&a>0&&(c+a+1>r&&(a=Math.max(1,r-c)),o.push(e.substring(i-=a,i+a)),!((c+=a+1)>r));)a=t[u=(u+1)%t.length];return o.reverse().join(n)}}(t.grouping,t.thousands):Oe,r=t.currency,i=t.decimal,o=t.numerals?function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}}(t.numerals):Oe,u=t.percent||"%";return{format:n,formatPrefix:function(t,e){var r=n((t=De(t),t.type="f",t)),i=3*Math.max(-8,Math.min(8,Math.floor(Le(e)/3))),o=Math.pow(10,-i),u=jh[8+i/3];return function(t){return r(o*t)+u}}}}function Ie(n){return Hh=Fe(n),t.format=Hh.format,t.formatPrefix=Hh.formatPrefix,Hh}function Ye(t){return Math.max(0,-Le(Math.abs(t)))}function Be(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Le(n)/3)))-Le(Math.abs(t)))}function He(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Le(n)-Le(t))+1}function je(){return new Xe}function Xe(){this.reset()}function Ve(t,n,e){var r=t.s=n+e,i=r-n,o=r-i;t.t=n-o+(e-i)}function $e(t){return t>1?0:t<-1?Np:Math.acos(t)}function We(t){return t>1?kp:t<-1?-kp:Math.asin(t)}function Ze(t){return(t=Fp(t/2))*t}function Ge(){}function Qe(t,n){t&&jp.hasOwnProperty(t.type)&&jp[t.type](t,n)}function Je(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i=0?1:-1,i=r*e,o=Lp(n),u=Fp(n),a=Zh*u,c=Wh*o+a*Lp(i),s=a*r*Fp(i);Xp.add(Rp(s,c)),$h=t,Wh=o,Zh=u}function or(t){return[Rp(t[1],t[0]),We(t[2])]}function ur(t){var n=t[0],e=t[1],r=Lp(e);return[r*Lp(n),r*Fp(n),Fp(e)]}function ar(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function cr(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function sr(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function fr(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function lr(t){var n=Yp(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}function hr(t,n){ip.push(op=[Gh=t,Jh=t]),nKh&&(Kh=n)}function pr(t,n){var e=ur([t*Cp,n*Cp]);if(rp){var r=cr(rp,e),i=cr([r[1],-r[0],0],r);lr(i),i=or(i);var o,u=t-tp,a=u>0?1:-1,c=i[0]*Ap*a,s=zp(u)>180;s^(a*tpKh&&(Kh=o):(c=(c+360)%360-180,s^(a*tpKh&&(Kh=n))),s?tmr(Gh,Jh)&&(Jh=t):mr(t,Jh)>mr(Gh,Jh)&&(Gh=t):Jh>=Gh?(tJh&&(Jh=t)):t>tp?mr(Gh,t)>mr(Gh,Jh)&&(Jh=t):mr(t,Jh)>mr(Gh,Jh)&&(Gh=t)}else ip.push(op=[Gh=t,Jh=t]);nKh&&(Kh=n),rp=e,tp=t}function dr(){Zp.point=pr}function vr(){op[0]=Gh,op[1]=Jh,Zp.point=hr,rp=null}function gr(t,n){if(rp){var e=t-tp;Wp.add(zp(e)>180?e+(e>0?360:-360):e)}else np=t,ep=n;$p.point(t,n),pr(t,n)}function _r(){$p.lineStart()}function yr(){gr(np,ep),$p.lineEnd(),zp(Wp)>Mp&&(Gh=-(Jh=180)),op[0]=Gh,op[1]=Jh,rp=null}function mr(t,n){return(n-=t)<0?n+360:n}function xr(t,n){return t[0]-n[0]}function br(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nNp?t-Ep:t<-Np?t+Ep:t,n]}function qr(t,n,e){return(t%=Ep)?n||e?Rr(Ur(t),Or(n,e)):Ur(t):n||e?Or(n,e):Lr}function Dr(t){return function(n,e){return n+=t,[n>Np?n-Ep:n<-Np?n+Ep:n,e]}}function Ur(t){var n=Dr(t);return n.invert=Dr(-t),n}function Or(t,n){function e(t,n){var e=Lp(n),a=Lp(t)*e,c=Fp(t)*e,s=Fp(n),f=s*r+a*i;return[Rp(c*o-f*u,a*r-s*i),We(f*o+c*u)]}var r=Lp(t),i=Fp(t),o=Lp(n),u=Fp(n);return e.invert=function(t,n){var e=Lp(n),a=Lp(t)*e,c=Fp(t)*e,s=Fp(n),f=s*o-c*u;return[Rp(c*o+s*u,a*r+f*i),We(f*r-a*i)]},e}function Fr(t){function n(n){return n=t(n[0]*Cp,n[1]*Cp),n[0]*=Ap,n[1]*=Ap,n}return t=qr(t[0]*Cp,t[1]*Cp,t.length>2?t[2]*Cp:0),n.invert=function(n){return n=t.invert(n[0]*Cp,n[1]*Cp),n[0]*=Ap,n[1]*=Ap,n},n}function Ir(t,n,e,r,i,o){if(e){var u=Lp(n),a=Fp(n),c=r*e;null==i?(i=n+r*Ep,o=n-c/2):(i=Yr(u,i),o=Yr(u,o),(r>0?io)&&(i+=r*Ep));for(var s,f=i;r>0?f>o:f1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function Hr(t,n){return zp(t[0]-n[0])=0;--o)i.point((f=s[o])[0],f[1]);else r(h.x,h.p.x,-1,i);h=h.p}s=(h=h.o).z,p=!p}while(!h.v);i.lineEnd()}}}function Vr(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,T=M*w,N=T>Np,k=d*x;if(cd.add(Rp(k*M*Fp(T),v*b+k*Lp(T))),o+=N?w+M*Ep:w,N^h>=e^y>=e){var S=cr(ur(l),ur(_));lr(S);var E=cr(i,S);lr(E);var A=(N^w>=0?-1:1)*We(E[2]);(r>A||r===A&&(S[0]||S[1]))&&(u+=N^w>=0?1:-1)}}return(o<-Mp||o0){for(m||(i.polygonStart(),m=!0),i.lineStart(),t=0;t1&&2&o&&u.push(u.pop().concat(u.shift())),p.push(u.filter(Zr))}var h,p,d,v=n(i),_=Br(),y=n(_),m=!1,x={point:o,lineStart:a,lineEnd:c,polygonStart:function(){x.point=s,x.lineStart=f,x.lineEnd=l,p=[],h=[]},polygonEnd:function(){x.point=o,x.lineStart=a,x.lineEnd=c,p=g(p);var t=$r(h,r);p.length?(m||(i.polygonStart(),m=!0),Xr(p,Gr,t,e,i)):t&&(m||(i.polygonStart(),m=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),m&&(i.polygonEnd(),m=!1),p=h=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}};return x}}function Zr(t){return t.length>1}function Gr(t,n){return((t=t.x)[0]<0?t[1]-kp-Mp:kp-t[1])-((n=n.x)[0]<0?n[1]-kp-Mp:kp-n[1])}function Qr(t){function n(t,n){return Lp(t)*Lp(n)>i}function e(t,n,e){var r=[1,0,0],o=cr(ur(t),ur(n)),u=ar(o,o),a=o[0],c=u-a*a;if(!c)return!e&&t;var s=i*u/c,f=-i*a/c,l=cr(r,o),h=fr(r,s);sr(h,fr(o,f));var p=l,d=ar(h,p),v=ar(p,p),g=d*d-v*(ar(h,h)-1);if(!(g<0)){var _=Yp(g),y=fr(p,(-d-_)/v);if(sr(y,h),y=or(y),!e)return y;var m,x=t[0],b=n[0],w=t[1],M=n[1];b0^y[1]<(zp(y[0]-x)Np^(x<=y[0]&&y[0]<=b)){var k=fr(p,(-d+_)/v);return sr(k,h),[y,or(k)]}}}function r(n,e){var r=u?t:Np-t,i=0;return n<-r?i|=1:n>r&&(i|=2),e<-r?i|=4:e>r&&(i|=8),i}var i=Lp(t),o=6*Cp,u=i>0,a=zp(i)>Mp;return Wr(n,function(t){var i,o,c,s,f;return{lineStart:function(){s=c=!1,f=1},point:function(l,h){var p,d=[l,h],v=n(l,h),g=u?v?0:r(l,h):v?r(l+(l<0?Np:-Np),h):0;if(!i&&(s=c=v)&&t.lineStart(),v!==c&&(!(p=e(i,d))||Hr(i,p)||Hr(d,p))&&(d[0]+=Mp,d[1]+=Mp,v=n(d[0],d[1])),v!==c)f=0,v?(t.lineStart(),p=e(d,i),t.point(p[0],p[1])):(p=e(i,d),t.point(p[0],p[1]),t.lineEnd()),i=p;else if(a&&i&&u^v){var _;g&o||!(_=e(d,i,!0))||(f=0,u?(t.lineStart(),t.point(_[0][0],_[0][1]),t.point(_[1][0],_[1][1]),t.lineEnd()):(t.point(_[1][0],_[1][1]),t.lineEnd(),t.lineStart(),t.point(_[0][0],_[0][1])))}!v||i&&Hr(i,d)||t.point(d[0],d[1]),i=d,c=v,o=g},lineEnd:function(){c&&t.lineEnd(),i=null},clean:function(){return f|(s&&c)<<1}}},function(n,e,r,i){Ir(i,t,o,r,n,e)},u?[0,-t]:[-Np,t-Np])}function Jr(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,a,s){var f=0,l=0;if(null==i||(f=u(i,a))!==(l=u(o,a))||c(i,o)<0^a>0)do{s.point(0===f||3===f?t:e,f>1?r:n)}while((f=(f+a+4)%4)!==l);else s.point(o[0],o[1])}function u(r,i){return zp(r[0]-t)0?0:3:zp(r[0]-e)0?2:1:zp(r[1]-n)0?1:0:i>0?3:2}function a(t,n){return c(t.x,n.x)}function c(t,n){var e=u(t,1),r=u(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(u){function c(t,n){i(t,n)&&w.point(t,n)}function s(o,u){var a=i(o,u);if(l&&h.push([o,u]),x)p=o,d=u,v=a,x=!1,a&&(w.lineStart(),w.point(o,u));else if(a&&m)w.point(o,u);else{var c=[_=Math.max(ld,Math.min(fd,_)),y=Math.max(ld,Math.min(fd,y))],s=[o=Math.max(ld,Math.min(fd,o)),u=Math.max(ld,Math.min(fd,u))];!function(t,n,e,r,i,o){var u,a=t[0],c=t[1],s=0,f=1,l=n[0]-a,h=n[1]-c;if(u=e-a,l||!(u>0)){if(u/=l,l<0){if(u0){if(u>f)return;u>s&&(s=u)}if(u=i-a,l||!(u<0)){if(u/=l,l<0){if(u>f)return;u>s&&(s=u)}else if(l>0){if(u0)){if(u/=h,h<0){if(u0){if(u>f)return;u>s&&(s=u)}if(u=o-c,h||!(u<0)){if(u/=h,h<0){if(u>f)return;u>s&&(s=u)}else if(h>0){if(u0&&(t[0]=a+s*l,t[1]=c+s*h),f<1&&(n[0]=a+f*l,n[1]=c+f*h),!0}}}}}(c,s,t,n,e,r)?a&&(w.lineStart(),w.point(o,u),b=!1):(m||(w.lineStart(),w.point(c[0],c[1])),w.point(s[0],s[1]),a||w.lineEnd(),b=!1)}_=o,y=u,m=a}var f,l,h,p,d,v,_,y,m,x,b,w=u,M=Br(),T={point:c,lineStart:function(){T.point=s,l&&l.push(h=[]),x=!0,m=!1,_=y=NaN},lineEnd:function(){f&&(s(p,d),v&&m&&M.rejoin(),f.push(M.result())),T.point=c,m&&w.lineEnd()},polygonStart:function(){w=M,f=[],l=[],b=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=l.length;er&&(h-o)*(r-u)>(p-u)*(t-o)&&++n:p<=r&&(h-o)*(r-u)<(p-u)*(t-o)&&--n;return n}(),e=b&&n,i=(f=g(f)).length;(e||i)&&(u.polygonStart(),e&&(u.lineStart(),o(null,null,1,u),u.lineEnd()),i&&Xr(f,a,n,o,u),u.polygonEnd()),w=u,f=l=h=null}};return T}}function Kr(){pd.point=pd.lineEnd=Ge}function ti(t,n){Qp=t*=Cp,Jp=Fp(n*=Cp),Kp=Lp(n),pd.point=ni}function ni(t,n){t*=Cp;var e=Fp(n*=Cp),r=Lp(n),i=zp(t-Qp),o=Lp(i),u=r*Fp(i),a=Kp*e-Jp*r*o,c=Jp*e+Kp*r*o;hd.add(Rp(Yp(u*u+a*a),c)),Qp=t,Jp=e,Kp=r}function ei(t){return hd.reset(),tr(t,pd),+hd}function ri(t,n){return dd[0]=t,dd[1]=n,ei(vd)}function ii(t,n){return!(!t||!_d.hasOwnProperty(t.type))&&_d[t.type](t,n)}function oi(t,n){return 0===ri(t,n)}function ui(t,n){var e=ri(t[0],t[1]);return ri(t[0],n)+ri(n,t[1])<=e+Mp}function ai(t,n){return!!$r(t.map(ci),si(n))}function ci(t){return(t=t.map(si)).pop(),t}function si(t){return[t[0]*Cp,t[1]*Cp]}function fi(t,n,e){var r=f(t,n-Mp,e).concat(n);return function(t){return r.map(function(n){return[t,n]})}}function li(t,n,e){var r=f(t,n-Mp,e).concat(n);return function(t){return r.map(function(n){return[n,t]})}}function hi(){function t(){return{type:"MultiLineString",coordinates:n()}}function n(){return f(qp(o/_)*_,i,_).map(p).concat(f(qp(s/y)*y,c,y).map(d)).concat(f(qp(r/v)*v,e,v).filter(function(t){return zp(t%_)>Mp}).map(l)).concat(f(qp(a/g)*g,u,g).filter(function(t){return zp(t%y)>Mp}).map(h))}var e,r,i,o,u,a,c,s,l,h,p,d,v=10,g=v,_=90,y=360,m=2.5;return t.lines=function(){return n().map(function(t){return{type:"LineString",coordinates:t}})},t.outline=function(){return{type:"Polygon",coordinates:[p(o).concat(d(c).slice(1),p(i).reverse().slice(1),d(s).reverse().slice(1))]}},t.extent=function(n){return arguments.length?t.extentMajor(n).extentMinor(n):t.extentMinor()},t.extentMajor=function(n){return arguments.length?(o=+n[0][0],i=+n[1][0],s=+n[0][1],c=+n[1][1],o>i&&(n=o,o=i,i=n),s>c&&(n=s,s=c,c=n),t.precision(m)):[[o,s],[i,c]]},t.extentMinor=function(n){return arguments.length?(r=+n[0][0],e=+n[1][0],a=+n[0][1],u=+n[1][1],r>e&&(n=r,r=e,e=n),a>u&&(n=a,a=u,u=n),t.precision(m)):[[r,a],[e,u]]},t.step=function(n){return arguments.length?t.stepMajor(n).stepMinor(n):t.stepMinor()},t.stepMajor=function(n){return arguments.length?(_=+n[0],y=+n[1],t):[_,y]},t.stepMinor=function(n){return arguments.length?(v=+n[0],g=+n[1],t):[v,g]},t.precision=function(n){return arguments.length?(m=+n,l=fi(a,u,90),h=li(r,e,m),p=fi(s,c,90),d=li(o,i,m),t):m},t.extentMajor([[-180,-90+Mp],[180,90-Mp]]).extentMinor([[-180,-80-Mp],[180,80+Mp]])}function pi(t){return t}function di(){xd.point=vi}function vi(t,n){xd.point=gi,td=ed=t,nd=rd=n}function gi(t,n){md.add(rd*t-ed*n),ed=t,rd=n}function _i(){gi(td,nd)}function yi(t,n){kd+=t,Sd+=n,++Ed}function mi(){qd.point=xi}function xi(t,n){qd.point=bi,yi(ud=t,ad=n)}function bi(t,n){var e=t-ud,r=n-ad,i=Yp(e*e+r*r);Ad+=i*(ud+t)/2,Cd+=i*(ad+n)/2,zd+=i,yi(ud=t,ad=n)}function wi(){qd.point=yi}function Mi(){qd.point=Ni}function Ti(){ki(id,od)}function Ni(t,n){qd.point=ki,yi(id=ud=t,od=ad=n)}function ki(t,n){var e=t-ud,r=n-ad,i=Yp(e*e+r*r);Ad+=i*(ud+t)/2,Cd+=i*(ad+n)/2,zd+=i,Pd+=(i=ad*t-ud*n)*(ud+t),Rd+=i*(ad+n),Ld+=3*i,yi(ud=t,ad=n)}function Si(t){this._context=t}function Ei(t,n){Bd.point=Ai,Ud=Fd=t,Od=Id=n}function Ai(t,n){Fd-=t,Id-=n,Yd.add(Yp(Fd*Fd+Id*Id)),Fd=t,Id=n}function Ci(){this._string=[]}function zi(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function Pi(t){return function(n){var e=new Ri;for(var r in t)e[r]=t[r];return e.stream=n,e}}function Ri(){}function Li(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),tr(e,t.stream(Nd)),n(Nd.result()),null!=r&&t.clipExtent(r),t}function qi(t,n,e){return Li(t,function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),u=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,a=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([u,a])},e)}function Di(t,n,e){return qi(t,[[0,0],n],e)}function Ui(t,n,e){return Li(t,function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,u=-i*e[0][1];t.scale(150*i).translate([o,u])},e)}function Oi(t,n,e){return Li(t,function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],u=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,u])},e)}function Fi(t,n){return+n?function(t,n){function e(r,i,o,u,a,c,s,f,l,h,p,d,v,g){var _=s-r,y=f-i,m=_*_+y*y;if(m>4*n&&v--){var x=u+h,b=a+p,w=c+d,M=Yp(x*x+b*b+w*w),T=We(w/=M),N=zp(zp(w)-1)n||zp((_*A+y*C)/m-.5)>.3||u*h+a*p+c*d2?t[2]%360*Cp:0,r()):[x*Ap,b*Ap,w*Ap]},n.precision=function(t){return arguments.length?(E=Fi(e,S=t*t),i()):Yp(S)},n.fitExtent=function(t,e){return qi(n,t,e)},n.fitSize=function(t,e){return Di(n,t,e)},n.fitWidth=function(t,e){return Ui(n,t,e)},n.fitHeight=function(t,e){return Oi(n,t,e)},function(){return o=t.apply(this,arguments),n.invert=o.invert&&function(t){return(t=s.invert((t[0]-u)/v,(a-t[1])/v))&&[t[0]*Ap,t[1]*Ap]},r()}}function Bi(t){var n=0,e=Np/3,r=Yi(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Cp,e=t[1]*Cp):[n*Ap,e*Ap]},i}function Hi(t,n){function e(t,n){var e=Yp(o-2*i*Fp(n))/i;return[e*Fp(t*=i),u-e*Lp(t)]}var r=Fp(t),i=(r+Fp(n))/2;if(zp(i)0?n<-kp+Mp&&(n=-kp+Mp):n>kp-Mp&&(n=kp-Mp);var e=o/Op(Gi(n),i);return[e*Fp(i*t),o-e*Lp(i*t)]}var r=Lp(t),i=t===n?Fp(t):Up(r/Lp(n))/Up(Gi(n)/Gi(t)),o=r*Op(Gi(t),i)/i;return i?(e.invert=function(t,n){var e=o-n,r=Ip(i)*Yp(t*t+e*e);return[Rp(t,zp(e))/i*Ip(e),2*Pp(Op(o/r,1/i))-kp]},e):Wi}function Ji(t,n){return[t,n]}function Ki(t,n){function e(t,n){var e=o-n,r=i*t;return[e*Fp(r),o-e*Lp(r)]}var r=Lp(t),i=t===n?Fp(t):(r-Lp(n))/(n-t),o=r/i+t;return zp(i)=0;)n+=e[r].value;else n=1;t.value=n}function fo(t,n){var e,r,i,o,u,a=new vo(t),c=+t.value&&(a.value=t.value),s=[a];for(null==n&&(n=lo);e=s.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)s.push(r=e.children[o]=new vo(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(po)}function lo(t){return t.children}function ho(t){t.data=t.data.data}function po(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function vo(t){this.data=t,this.depth=this.height=0,this.parent=null}function go(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Wd.call(t))).length,o=[];r0&&e*e>r*r+i*i}function mo(t,n){for(var e=0;ee*e+r*r}function To(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function No(t){this._=t,this.next=null,this.previous=null}function ko(t){if(!(i=t.length))return 0;var n,e,r,i,o,u,a,c,s,f,l;if(n=t[0],n.x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;wo(e,n,r=t[2]),n=new No(n),e=new No(e),r=new No(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;ah&&(h=a),g=f*f*v,(p=Math.max(h/g,g/l))>d){f-=a;break}d=p}_.push(u={value:f,dice:c1&&Vo(t[e[r-2]],t[e[r-1]],t[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function Zo(t){this._size=t,this._call=this._error=null,this._tasks=[],this._data=[],this._waiting=this._active=this._ended=this._start=0}function Go(t){if(!t._start)try{(function(t){for(;t._start=t._waiting&&t._active=0;)if((e=t._tasks[r])&&(t._tasks[r]=null,e.abort))try{e.abort()}catch(n){}t._active=NaN,Jo(t)}function Jo(t){if(!t._active&&t._call){var n=t._data;t._data=void 0,t._call(t._error,n)}}function Ko(t){if(null==t)t=1/0;else if(!((t=+t)>=1))throw new Error("invalid concurrency");return new Zo(t)}function tu(){return Math.random()}function nu(t,n){function e(t){var n,e=s.status;if(!e&&function(t){var n=t.responseType;return n&&"text"!==n?t.response:t.responseText}(s)||e>=200&&e<300||304===e){if(o)try{n=o.call(r,s)}catch(t){return void a.call("error",r,t)}else n=s;a.call("load",r,n)}else a.call("error",r,t)}var r,i,o,u,a=N("beforesend","progress","load","error"),c=se(),s=new XMLHttpRequest,f=null,l=null,h=0;if("undefined"==typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(t)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=e:s.onreadystatechange=function(t){s.readyState>3&&e(t)},s.onprogress=function(t){a.call("progress",r,t)},r={header:function(t,n){return t=(t+"").toLowerCase(),arguments.length<2?c.get(t):(null==n?c.remove(t):c.set(t,n+""),r)},mimeType:function(t){return arguments.length?(i=null==t?null:t+"",r):i},responseType:function(t){return arguments.length?(u=t,r):u},timeout:function(t){return arguments.length?(h=+t,r):h},user:function(t){return arguments.length<1?f:(f=null==t?null:t+"",r)},password:function(t){return arguments.length<1?l:(l=null==t?null:t+"",r)},response:function(t){return o=t,r},get:function(t,n){return r.send("GET",t,n)},post:function(t,n){return r.send("POST",t,n)},send:function(n,e,o){return s.open(n,t,!0,f,l),null==i||c.has("accept")||c.set("accept",i+",*/*"),s.setRequestHeader&&c.each(function(t,n){s.setRequestHeader(n,t)}),null!=i&&s.overrideMimeType&&s.overrideMimeType(i),null!=u&&(s.responseType=u),h>0&&(s.timeout=h),null==o&&"function"==typeof e&&(o=e,e=null),null!=o&&1===o.length&&(o=function(t){return function(n,e){t(null==n?e:null)}}(o)),null!=o&&r.on("error",o).on("load",function(t){o(null,t)}),a.call("beforesend",r,s),s.send(null==e?null:e),r},abort:function(){return s.abort(),r},on:function(){var t=a.on.apply(a,arguments);return t===a?r:t}},null!=n){if("function"!=typeof n)throw new Error("invalid callback: "+n);return r.get(n)}return r}function eu(t,n){return function(e,r){var i=nu(e).mimeType(t).response(n);if(null!=r){if("function"!=typeof r)throw new Error("invalid callback: "+r);return i.get(r)}return i}}function ru(t,n){return function(e,r,i){arguments.length<3&&(i=r,r=null);var o=nu(e).mimeType(t);return o.row=function(t){return arguments.length?o.response(function(t,n){return function(e){return t(e.responseText,n)}}(n,r=t)):r},o.row(r),i?o.get(i):o}}function iu(t){function n(n){var o=n+"",u=e.get(o);if(!u){if(i!==yv)return i;e.set(o,u=r.push(n))}return t[(u-1)%t.length]}var e=se(),r=[],i=yv;return t=null==t?[]:_v.call(t),n.domain=function(t){if(!arguments.length)return r.slice();r=[],e=se();for(var i,o,u=-1,a=t.length;++u2?lu:fu,o=u=null,r}function r(n){return(o||(o=i(a,c,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=n?0:t>=e?1:r(t)}}}(t):t,s)))(+n)}var i,o,u,a=mv,c=mv,s=fn,f=!1;return r.invert=function(t){return(u||(u=i(c,a,su,f?function(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}(n):n)))(+t)},r.domain=function(t){return arguments.length?(a=gv.call(t,cu),e()):a.slice()},r.range=function(t){return arguments.length?(c=_v.call(t),e()):c.slice()},r.rangeRound=function(t){return c=_v.call(t),s=ln,e()},r.clamp=function(t){return arguments.length?(f=!!t,e()):f},r.interpolate=function(t){return arguments.length?(s=t,e()):s},e()}function du(n){var e=n.domain;return n.ticks=function(t){var n=e();return l(n[0],n[n.length-1],null==t?10:t)},n.tickFormat=function(n,r){return function(n,e,r){var i,o=n[0],u=n[n.length-1],a=p(o,u,null==e?10:e);switch((r=De(null==r?",f":r)).type){case"s":var c=Math.max(Math.abs(o),Math.abs(u));return null!=r.precision||isNaN(i=Be(a,c))||(r.precision=i),t.formatPrefix(r,c);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=He(a,Math.max(Math.abs(o),Math.abs(u))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=Ye(a))||(r.precision=i-2*("%"===r.type))}return t.format(r)}(e(),n,r)},n.nice=function(t){null==t&&(t=10);var r,i=e(),o=0,u=i.length-1,a=i[o],c=i[u];return c0?r=h(a=Math.floor(a/r)*r,c=Math.ceil(c/r)*r,t):r<0&&(r=h(a=Math.ceil(a*r)/r,c=Math.floor(c*r)/r,t)),r>0?(i[o]=Math.floor(a/r)*r,i[u]=Math.ceil(c/r)*r,e(i)):r<0&&(i[o]=Math.ceil(a*r)/r,i[u]=Math.floor(c*r)/r,e(i)),n},n}function vu(){var t=pu(su,an);return t.copy=function(){return hu(t,vu())},du(t)}function gu(){function t(t){return+t}var n=[0,1];return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=gv.call(e,cu),t):n.slice()},t.copy=function(){return gu().domain(n)},du(t)}function _u(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],u=t[i];return u0){for(;pc)break;g.push(h)}}else for(;p=1;--f)if(!((h=s*f)c)break;g.push(h)}}else g=l(p,d,Math.min(d-p,v)).map(u);return n?g.reverse():g},e.tickFormat=function(n,r){if(null==r&&(r=10===i?".0e":","),"function"!=typeof r&&(r=t.format(r)),n===1/0)return r;null==n&&(n=10);var a=Math.max(1,i*n/e.ticks().length);return function(t){var n=t/u(Math.round(o(t)));return n*i0?o[n-1]:r[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},t.copy=function(){return Eu().domain([e,r]).range(u)},du(t)}function Au(){function t(t){if(t<=t)return e[Os(n,t,0,r)]}var n=[.5],e=[0,1],r=1;return t.domain=function(i){return arguments.length?(n=_v.call(i),r=Math.min(n.length,e.length-1),t):n.slice()},t.range=function(i){return arguments.length?(e=_v.call(i),r=Math.min(n.length,e.length-1),t):e.slice()},t.invertExtent=function(t){var r=e.indexOf(t);return[n[r-1],n[r]]},t.copy=function(){return Au().domain(n).range(e)},t}function Cu(t,n,e,r){function i(n){return t(n=new Date(+n)),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n0))return a;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(u=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return xv.setTime(+n),bv.setTime(+r),t(xv),t(bv),Math.floor(e(xv,bv))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}function zu(t){return Cu(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Tv)/Nv})}function Pu(t){return Cu(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/Nv})}function Ru(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Lu(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function qu(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function Du(t){function n(t,n){return function(e){var r,i,o,u=[],a=-1,c=0,s=t.length;for(e instanceof Date||(e=new Date(+e));++a53)return null;"w"in u||(u.w=1),"Z"in u?(i=(o=(i=Lu(qu(u.y))).getUTCDay())>4||0===o?og.ceil(i):og(i),i=eg.offset(i,7*(u.V-1)),u.y=i.getUTCFullYear(),u.m=i.getUTCMonth(),u.d=i.getUTCDate()+(u.w+6)%7):(i=(o=(i=n(qu(u.y))).getDay())>4||0===o?qv.ceil(i):qv(i),i=Pv.offset(i,7*(u.V-1)),u.y=i.getFullYear(),u.m=i.getMonth(),u.d=i.getDate()+(u.w+6)%7)}else("W"in u||"U"in u)&&("w"in u||(u.w="u"in u?u.u%7:"W"in u?1:0),o="Z"in u?Lu(qu(u.y)).getUTCDay():n(qu(u.y)).getDay(),u.m=0,u.d="W"in u?(u.w+6)%7+7*u.W-(o+5)%7:u.w+7*u.U-(o+6)%7);return"Z"in u?(u.H+=u.Z/100|0,u.M+=u.Z%100,Lu(u)):n(u)}}function r(t,n,e,r){for(var i,o,u=0,a=n.length,c=e.length;u=c)return-1;if(37===(i=n.charCodeAt(u++))){if(i=n.charAt(u++),!(o=T[i in Mg?n.charAt(u++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}var i=t.dateTime,o=t.date,u=t.time,a=t.periods,c=t.days,s=t.shortDays,f=t.months,l=t.shortMonths,h=Fu(a),p=Iu(a),d=Fu(c),v=Iu(c),g=Fu(s),_=Iu(s),y=Fu(f),m=Iu(f),x=Fu(l),b=Iu(l),w={a:function(t){return s[t.getDay()]},A:function(t){return c[t.getDay()]},b:function(t){return l[t.getMonth()]},B:function(t){return f[t.getMonth()]},c:null,d:ua,e:ua,f:la,H:aa,I:ca,j:sa,L:fa,m:ha,M:pa,p:function(t){return a[+(t.getHours()>=12)]},Q:Ya,s:Ba,S:da,u:va,U:ga,V:_a,w:ya,W:ma,x:null,X:null,y:xa,Y:ba,Z:wa,"%":Ia},M={a:function(t){return s[t.getUTCDay()]},A:function(t){return c[t.getUTCDay()]},b:function(t){return l[t.getUTCMonth()]},B:function(t){return f[t.getUTCMonth()]},c:null,d:Ma,e:Ma,f:Ea,H:Ta,I:Na,j:ka,L:Sa,m:Aa,M:Ca,p:function(t){return a[+(t.getUTCHours()>=12)]},Q:Ya,s:Ba,S:za,u:Pa,U:Ra,V:La,w:qa,W:Da,x:null,X:null,y:Ua,Y:Oa,Z:Fa,"%":Ia},T={a:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.w=_[r[0].toLowerCase()],e+r[0].length):-1},A:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=v[r[0].toLowerCase()],e+r[0].length):-1},b:function(t,n,e){var r=x.exec(n.slice(e));return r?(t.m=b[r[0].toLowerCase()],e+r[0].length):-1},B:function(t,n,e){var r=y.exec(n.slice(e));return r?(t.m=m[r[0].toLowerCase()],e+r[0].length):-1},c:function(t,n,e){return r(t,i,n,e)},d:Gu,e:Gu,f:ea,H:Ju,I:Ju,j:Qu,L:na,m:Zu,M:Ku,p:function(t,n,e){var r=h.exec(n.slice(e));return r?(t.p=p[r[0].toLowerCase()],e+r[0].length):-1},Q:ia,s:oa,S:ta,u:Bu,U:Hu,V:ju,w:Yu,W:Xu,x:function(t,n,e){return r(t,o,n,e)},X:function(t,n,e){return r(t,u,n,e)},y:$u,Y:Vu,Z:Wu,"%":ra};return w.x=n(o,w),w.X=n(u,w),w.c=n(i,w),M.x=n(o,M),M.X=n(u,M),M.c=n(i,M),{format:function(t){var e=n(t+="",w);return e.toString=function(){return t},e},parse:function(t){var n=e(t+="",Ru);return n.toString=function(){return t},n},utcFormat:function(t){var e=n(t+="",M);return e.toString=function(){return t},e},utcParse:function(t){var n=e(t,Lu);return n.toString=function(){return t},n}}}function Uu(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o68?1900:2e3),e+r[0].length):-1}function Wu(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function Zu(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function Gu(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function Qu(t,n,e){var r=Tg.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Ju(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Ku(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function ta(t,n,e){var r=Tg.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function na(t,n,e){var r=Tg.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ea(t,n,e){var r=Tg.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function ra(t,n,e){var r=Ng.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function ia(t,n,e){var r=Tg.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function oa(t,n,e){var r=Tg.exec(n.slice(e));return r?(t.Q=1e3*+r[0],e+r[0].length):-1}function ua(t,n){return Uu(t.getDate(),n,2)}function aa(t,n){return Uu(t.getHours(),n,2)}function ca(t,n){return Uu(t.getHours()%12||12,n,2)}function sa(t,n){return Uu(1+Pv.count(Gv(t),t),n,3)}function fa(t,n){return Uu(t.getMilliseconds(),n,3)}function la(t,n){return fa(t,n)+"000"}function ha(t,n){return Uu(t.getMonth()+1,n,2)}function pa(t,n){return Uu(t.getMinutes(),n,2)}function da(t,n){return Uu(t.getSeconds(),n,2)}function va(t){var n=t.getDay();return 0===n?7:n}function ga(t,n){return Uu(Lv.count(Gv(t),t),n,2)}function _a(t,n){var e=t.getDay();return t=e>=4||0===e?Ov(t):Ov.ceil(t),Uu(Ov.count(Gv(t),t)+(4===Gv(t).getDay()),n,2)}function ya(t){return t.getDay()}function ma(t,n){return Uu(qv.count(Gv(t),t),n,2)}function xa(t,n){return Uu(t.getFullYear()%100,n,2)}function ba(t,n){return Uu(t.getFullYear()%1e4,n,4)}function wa(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+Uu(n/60|0,"0",2)+Uu(n%60,"0",2)}function Ma(t,n){return Uu(t.getUTCDate(),n,2)}function Ta(t,n){return Uu(t.getUTCHours(),n,2)}function Na(t,n){return Uu(t.getUTCHours()%12||12,n,2)}function ka(t,n){return Uu(1+eg.count(xg(t),t),n,3)}function Sa(t,n){return Uu(t.getUTCMilliseconds(),n,3)}function Ea(t,n){return Sa(t,n)+"000"}function Aa(t,n){return Uu(t.getUTCMonth()+1,n,2)}function Ca(t,n){return Uu(t.getUTCMinutes(),n,2)}function za(t,n){return Uu(t.getUTCSeconds(),n,2)}function Pa(t){var n=t.getUTCDay();return 0===n?7:n}function Ra(t,n){return Uu(ig.count(xg(t),t),n,2)}function La(t,n){var e=t.getUTCDay();return t=e>=4||0===e?cg(t):cg.ceil(t),Uu(cg.count(xg(t),t)+(4===xg(t).getUTCDay()),n,2)}function qa(t){return t.getUTCDay()}function Da(t,n){return Uu(og.count(xg(t),t),n,2)}function Ua(t,n){return Uu(t.getUTCFullYear()%100,n,2)}function Oa(t,n){return Uu(t.getUTCFullYear()%1e4,n,4)}function Fa(){return"+0000"}function Ia(){return"%"}function Ya(t){return+t}function Ba(t){return Math.floor(+t/1e3)}function Ha(n){return bg=Du(n),t.timeFormat=bg.format,t.timeParse=bg.parse,t.utcFormat=bg.utcFormat,t.utcParse=bg.utcParse,bg}function ja(t){return new Date(t)}function Xa(t){return t instanceof Date?+t:+new Date(+t)}function Va(t,n,r,i,o,u,a,c,s){function f(e){return(a(e)=1?i_:t<=-1?-i_:Math.asin(t)}function Ja(t){return t.innerRadius}function Ka(t){return t.outerRadius}function tc(t){return t.startAngle}function nc(t){return t.endAngle}function ec(t){return t&&t.padAngle}function rc(t,n,e,r,i,o,u){var a=t-e,c=n-r,s=(u?o:-o)/n_(a*a+c*c),f=s*c,l=-s*a,h=t+f,p=n+l,d=e+f,v=r+l,g=(h+d)/2,_=(p+v)/2,y=d-h,m=v-p,x=y*y+m*m,b=i-o,w=h*v-d*p,M=(m<0?-1:1)*n_(Jg(0,b*b*x-w*w)),T=(w*m-y*M)/x,N=(-w*y-m*M)/x,k=(w*m+y*M)/x,S=(-w*y+m*M)/x,E=T-g,A=N-_,C=k-g,z=S-_;return E*E+A*A>C*C+z*z&&(T=k,N=S),{cx:T,cy:N,x01:-f,y01:-l,x11:T*(i/b-1),y11:N*(i/b-1)}}function ic(t){this._context=t}function oc(t){return new ic(t)}function uc(t){return t[0]}function ac(t){return t[1]}function cc(){function t(t){var a,c,s,f=t.length,l=!1;for(null==i&&(u=o(s=ee())),a=0;a<=f;++a)!(a=f;--l)s.point(g[l],_[l]);s.lineEnd(),s.areaEnd()}v&&(g[n]=+e(h,n,t),_[n]=+i(h,n,t),s.point(r?+r(h,n,t):g[n],o?+o(h,n,t):_[n]))}if(p)return s=null,p+""||null}function n(){return cc().defined(u).curve(c).context(a)}var e=uc,r=null,i=Ga(0),o=ac,u=Ga(!0),a=null,c=oc,s=null;return t.x=function(n){return arguments.length?(e="function"==typeof n?n:Ga(+n),r=null,t):e},t.x0=function(n){return arguments.length?(e="function"==typeof n?n:Ga(+n),t):e},t.x1=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:Ga(+n),t):r},t.y=function(n){return arguments.length?(i="function"==typeof n?n:Ga(+n),o=null,t):i},t.y0=function(n){return arguments.length?(i="function"==typeof n?n:Ga(+n),t):i},t.y1=function(n){return arguments.length?(o=null==n?null:"function"==typeof n?n:Ga(+n),t):o},t.lineX0=t.lineY0=function(){return n().x(e).y(i)},t.lineY1=function(){return n().x(e).y(o)},t.lineX1=function(){return n().x(r).y(i)},t.defined=function(n){return arguments.length?(u="function"==typeof n?n:Ga(!!n),t):u},t.curve=function(n){return arguments.length?(c=n,null!=a&&(s=c(a)),t):c},t.context=function(n){return arguments.length?(null==n?a=s=null:s=c(a=n),t):a},t}function fc(t,n){return nt?1:n>=t?0:NaN}function lc(t){return t}function hc(t){this._curve=t}function pc(t){function n(n){return new hc(t(n))}return n._curve=t,n}function dc(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(pc(t)):n()._curve},t}function vc(){return dc(cc().curve(u_))}function gc(){var t=sc().curve(u_),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return dc(e())},delete t.lineX0,t.lineEndAngle=function(){return dc(r())},delete t.lineX1,t.lineInnerRadius=function(){return dc(i())},delete t.lineY0,t.lineOuterRadius=function(){return dc(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(pc(t)):n()._curve},t}function _c(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function yc(t){return t.source}function mc(t){return t.target}function xc(t){function n(){var n,a=a_.call(arguments),c=e.apply(this,a),s=r.apply(this,a);if(u||(u=n=ee()),t(u,+i.apply(this,(a[0]=c,a)),+o.apply(this,a),+i.apply(this,(a[0]=s,a)),+o.apply(this,a)),n)return u=null,n+""||null}var e=yc,r=mc,i=uc,o=ac,u=null;return n.source=function(t){return arguments.length?(e=t,n):e},n.target=function(t){return arguments.length?(r=t,n):r},n.x=function(t){return arguments.length?(i="function"==typeof t?t:Ga(+t),n):i},n.y=function(t){return arguments.length?(o="function"==typeof t?t:Ga(+t),n):o},n.context=function(t){return arguments.length?(u=null==t?null:t,n):u},n}function bc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function wc(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function Mc(t,n,e,r,i){var o=_c(n,e),u=_c(n,e=(e+i)/2),a=_c(r,e),c=_c(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(u[0],u[1],a[0],a[1],c[0],c[1])}function Tc(){}function Nc(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function kc(t){this._context=t}function Sc(t){this._context=t}function Ec(t){this._context=t}function Ac(t,n){this._basis=new kc(t),this._beta=n}function Cc(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function zc(t,n){this._context=t,this._k=(1-n)/6}function Pc(t,n){this._context=t,this._k=(1-n)/6}function Rc(t,n){this._context=t,this._k=(1-n)/6}function Lc(t,n,e){var r=t._x1,i=t._y1,o=t._x2,u=t._y2;if(t._l01_a>e_){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>e_){var s=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*s+t._x1*t._l23_2a-n*t._l12_2a)/f,u=(u*s+t._y1*t._l23_2a-e*t._l12_2a)/f}t._context.bezierCurveTo(r,i,o,u,t._x2,t._y2)}function qc(t,n){this._context=t,this._alpha=n}function Dc(t,n){this._context=t,this._alpha=n}function Uc(t,n){this._context=t,this._alpha=n}function Oc(t){this._context=t}function Fc(t){return t<0?-1:1}function Ic(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0),a=(o*i+u*r)/(r+i);return(Fc(o)+Fc(u))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs(a))||0}function Yc(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Bc(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function Hc(t){this._context=t}function jc(t){this._context=new Xc(t)}function Xc(t){this._context=t}function Vc(t){this._context=t}function $c(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),u=new Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,u=t[n[0]],a=u.length;o=0;)e[n]=n;return e}function Qc(t,n){return t[n]}function Jc(t){var n=t.map(Kc);return Gc(t).sort(function(t,e){return n[t]-n[e]})}function Kc(t){for(var n,e=0,r=-1,i=t.length;++r0)){if(o/=h,h<0){if(o0){if(o>l)return;o>f&&(f=o)}if(o=r-c,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>f&&(f=o)}else if(h>0){if(o0)){if(o/=p,p<0){if(o0){if(o>l)return;o>f&&(f=o)}if(o=i-s,p||!(o<0)){if(o/=p,p<0){if(o>l)return;o>f&&(f=o)}else if(p>0){if(o0||l<1)||(f>0&&(t[0]=[c+f*h,s+f*p]),l<1&&(t[1]=[c+l*h,s+l*p]),!0)}}}}}function hs(t,n,e,r,i){var o=t[1];if(o)return!0;var u,a,c=t[0],s=t.left,f=t.right,l=s[0],h=s[1],p=f[0],d=f[1],v=(l+p)/2,g=(h+d)/2;if(d===h){if(v=r)return;if(l>p){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]1)if(l>p){if(c){if(c[1]>=i)return}else c=[(e-a)/u,e];o=[(i-a)/u,i]}else{if(c){if(c[1]=r)return}else c=[n,u*n+a];o=[r,u*r+a]}else{if(c){if(c[0]=-I_)){var p=c*c+s*s,d=f*f+l*l,v=(l*p-s*d)/h,g=(c*d-f*p)/h,_=U_.pop()||new function(){is(this),this.x=this.y=this.arc=this.site=this.cy=null};_.arc=t,_.site=i,_.x=v+u,_.y=(_.cy=g+a)+Math.sqrt(v*v+g*g),t.circle=_;for(var y=null,m=q_._;m;)if(_.yF_)a=a.L;else{if(!((i=o-function(t,n){var e=t.N;if(e)return ws(e,n);var r=t.site;return r[1]===n?r[0]:1/0}(a,u))>F_)){r>-F_?(n=a.P,e=a):i>-F_?(n=a,e=a.N):n=e=a;break}if(!a.R){n=a;break}a=a.R}(function(t){L_[t.index]={site:t,halfedges:[]}})(t);var c=ys(t);if(R_.insert(n,c),n||e){if(n===e)return _s(n),e=ys(n.site),R_.insert(c,e),c.edge=e.edge=cs(n.site,c.site),gs(n),void gs(e);if(e){_s(n),_s(e);var s=n.site,f=s[0],l=s[1],h=t[0]-f,p=t[1]-l,d=e.site,v=d[0]-f,g=d[1]-l,_=2*(h*g-p*v),y=h*h+p*p,m=v*v+g*g,x=[(g*y-p*m)/_+f,(h*m-v*y)/_+l];fs(e.edge,s,d,x),c.edge=cs(s,t,null,x),e.edge=cs(t,d,null,x),gs(n),gs(e)}else c.edge=cs(n.site,c.site)}}function ws(t,n){var e=t.site,r=e[0],i=e[1],o=i-n;if(!o)return r;var u=t.P;if(!u)return-1/0;var a=(e=u.site)[0],c=e[1],s=c-n;if(!s)return a;var f=a-r,l=1/o-1/s,h=f/s;return l?(-h+Math.sqrt(h*h-2*l*(f*f/(-2*s)-c+s/2+i-o/2)))/l+r:(r+a)/2}function Ms(t,n,e){return(t[0]-e[0])*(n[1]-t[1])-(t[0]-n[0])*(e[1]-t[1])}function Ts(t,n){return n[1]-t[1]||n[0]-t[0]}function Ns(t,n){var e,r,i,o=t.sort(Ts).pop();for(D_=[],L_=new Array(t.length),R_=new rs,q_=new rs;;)if(i=P_,o&&(!i||o[1]F_||Math.abs(i[0][1]-i[1][1])>F_)||delete D_[o]})(u,a,c,s),function(t,n,e,r){var i,o,u,a,c,s,f,l,h,p,d,v,g=L_.length,_=!0;for(i=0;iF_||Math.abs(v-h)>F_)&&(c.splice(a,0,D_.push(ss(u,p,Math.abs(d-t)F_?[t,Math.abs(l-t)F_?[Math.abs(h-r)F_?[e,Math.abs(l-e)F_?[Math.abs(h-n)r?(r+i)/2:Math.min(0,r)||Math.max(0,i),u>o?(o+u)/2:Math.min(0,o)||Math.max(0,u))}var Us=e(n),Os=Us.right,Fs=Us.left,Is=Array.prototype,Ys=Is.slice,Bs=Is.map,Hs=Math.sqrt(50),js=Math.sqrt(10),Xs=Math.sqrt(2),Vs=Array.prototype.slice,$s=1,Ws=2,Zs=3,Gs=4,Qs=1e-6,Js={value:function(){}};k.prototype=N.prototype={constructor:k,on:function(t,n){var e,r=this._,i=function(t,n){return t.trim().split(/^|\s+/).map(function(t){var e="",r=t.indexOf(".");if(r>=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}})}(t+"",r),o=-1,u=i.length;{if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++o0)for(var e,r,i=new Array(e),o=0;o=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var af={};if(t.event=null,"undefined"!=typeof document){"onmouseenter"in document.documentElement||(af={mouseenter:"mouseover",mouseleave:"mouseout"})}var cf=[null];ut.prototype=at.prototype={constructor:ut,select:function(t){"function"!=typeof t&&(t=z(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(y=g[x])&&++x=0;)(r=i[o])&&(u&&u!==r.nextSibling&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=O);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?function(t){return function(){this.style.removeProperty(t)}}:"function"==typeof n?function(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}:function(t,n,e){return function(){this.style.setProperty(t,n,e)}})(t,n,null==e?"":e)):I(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?function(t){return function(){delete this[t]}}:"function"==typeof n?function(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}:function(t,n){return function(){this[t]=n}})(t,n)):this.node()[t]},classed:function(t,n){var e=Y(t+"");if(arguments.length<2){for(var r=B(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}(t+""),u=o.length;if(!(arguments.length<2)){for(a=n?rt:et,null==e&&(e=!1),r=0;r=240?t-240:t+120,i,r),Ut(t,i,r),Ut(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var wf=Math.PI/180,Mf=180/Math.PI,Tf=.95047,Nf=1,kf=1.08883,Sf=4/29,Ef=6/29,Af=3*Ef*Ef,Cf=Ef*Ef*Ef;Nt(It,Ft,kt(St,{brighter:function(t){return new It(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new It(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return t=Nf*Bt(t),n=Tf*Bt(n),e=kf*Bt(e),new Rt(Ht(3.2404542*n-1.5371385*t-.4985314*e),Ht(-.969266*n+1.8760108*t+.041556*e),Ht(.0556434*n-.2040259*t+1.0572252*e),this.opacity)}})),Nt(Vt,Xt,kt(St,{brighter:function(t){return new Vt(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new Vt(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Ot(this).rgb()}}));var zf=-.29227,Pf=-.90649,Rf=1.97294,Lf=Rf*Pf,qf=1.78277*Rf,Df=1.78277*zf- -.14861*Pf;Nt(Wt,$t,kt(St,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Wt(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Wt(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*wf,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new Rt(255*(n+e*(-.14861*r+1.78277*i)),255*(n+e*(zf*r+Pf*i)),255*(n+e*(Rf*r)),this.opacity)}}));var Uf,Of,Ff,If,Yf,Bf,Hf=function t(n){function e(t,n){var e=r((t=Pt(t)).r,(n=Pt(n)).r),i=r(t.g,n.g),o=r(t.b,n.b),u=en(t.opacity,n.opacity);return function(n){return t.r=e(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}var r=nn(n);return e.gamma=t,e}(1),jf=rn(Gt),Xf=rn(Qt),Vf=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,$f=new RegExp(Vf.source,"g"),Wf=180/Math.PI,Zf={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},Gf=pn(function(t){return"none"===t?Zf:(Uf||(Uf=document.createElement("DIV"),Of=document.documentElement,Ff=document.defaultView),Uf.style.transform=t,t=Ff.getComputedStyle(Of.appendChild(Uf),null).getPropertyValue("transform"),Of.removeChild(Uf),t=t.slice(7,-1).split(","),hn(+t[0],+t[1],+t[2],+t[3],+t[4],+t[5]))},"px, ","px)","deg)"),Qf=pn(function(t){return null==t?Zf:(If||(If=document.createElementNS("http://www.w3.org/2000/svg","g")),If.setAttribute("transform",t),(t=If.transform.baseVal.consolidate())?(t=t.matrix,hn(t.a,t.b,t.c,t.d,t.e,t.f)):Zf)},", ",")",")"),Jf=Math.SQRT2,Kf=2,tl=4,nl=1e-12,el=gn(tn),rl=gn(en),il=_n(tn),ol=_n(en),ul=yn(tn),al=yn(en),cl=0,sl=0,fl=0,ll=1e3,hl=0,pl=0,dl=0,vl="object"==typeof performance&&performance.now?performance:Date,gl="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};bn.prototype=wn.prototype={constructor:bn,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?mn():+e)+(null==n?0:+n),this._next||Bf===this||(Bf?Bf._next=this:Yf=this,Bf=this),this._call=t,this._time=e,kn()},stop:function(){this._call&&(this._call=null,this._time=1/0,kn())}};var _l=N("start","end","interrupt"),yl=[],ml=0,xl=1,bl=2,wl=3,Ml=4,Tl=5,Nl=6,kl=at.prototype.constructor,Sl=0,El=at.prototype;qn.prototype=Dn.prototype={constructor:qn,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=z(t));for(var r=this._groups,i=r.length,o=new Array(i),u=0;u=0&&(t=t.slice(0,n)),!t||"start"===t})}(n)?An:Cn;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}(e,t,n))},attr:function(t,n){var e=E(t),r="transform"===e?Qf:Ln;return this.attrTween(t,"function"==typeof n?(e.local?function(t,n,e){var r,i,o;return function(){var u,a=e(this);if(null!=a)return(u=this.getAttributeNS(t.space,t.local))===a?null:u===r&&a===i?o:o=n(r=u,i=a);this.removeAttributeNS(t.space,t.local)}}:function(t,n,e){var r,i,o;return function(){var u,a=e(this);if(null!=a)return(u=this.getAttribute(t))===a?null:u===r&&a===i?o:o=n(r=u,i=a);this.removeAttribute(t)}})(e,r,Rn(this,"attr."+t,n)):null==n?(e.local?function(t){return function(){this.removeAttributeNS(t.space,t.local)}}:function(t){return function(){this.removeAttribute(t)}})(e):(e.local?function(t,n,e){var r,i;return function(){var o=this.getAttributeNS(t.space,t.local);return o===e?null:o===r?i:i=n(r=o,e)}}:function(t,n,e){var r,i;return function(){var o=this.getAttribute(t);return o===e?null:o===r?i:i=n(r=o,e)}})(e,r,n+""))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=E(t);return this.tween(e,(r.local?function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttributeNS(t.space,t.local,r(n))}}return e._value=n,e}:function(t,n){function e(){var e=this,r=n.apply(e,arguments);return r&&function(n){e.setAttribute(t,r(n))}}return e._value=n,e})(r,n))},style:function(t,n,e){var r="transform"==(t+="")?Gf:Ln;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=I(this,t),u=(this.style.removeProperty(t),I(this,t));return o===u?null:o===e&&u===r?i:i=n(e=o,r=u)}}(t,r)).on("end.style."+t,function(t){return function(){this.style.removeProperty(t)}}(t)):this.styleTween(t,"function"==typeof n?function(t,n,e){var r,i,o;return function(){var u=I(this,t),a=e(this);return null==a&&(this.style.removeProperty(t),a=I(this,t)),u===a?null:u===r&&a===i?o:o=n(r=u,i=a)}}(t,r,Rn(this,"style."+t,n)):function(t,n,e){var r,i;return function(){var o=I(this,t);return o===e?null:o===r?i:i=n(r=o,e)}}(t,r,n+""),e)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){function r(){var r=this,i=n.apply(r,arguments);return i&&function(n){r.style.setProperty(t,i(n),e)}}return r._value=n,r}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(Rn(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=zn(this.node(),e).tween,o=0,u=i.length;o1e-6)if(Math.abs(f*a-c*s)>1e-6&&i){var h=e-o,p=r-u,d=a*a+c*c,v=h*h+p*p,g=Math.sqrt(d),_=Math.sqrt(l),y=i*Math.tan((yh-Math.acos((d+l-v)/(2*g*_)))/2),m=y/_,x=y/g;Math.abs(m-1)>1e-6&&(this._+="L"+(t+m*s)+","+(n+m*f)),this._+="A"+i+","+i+",0,0,"+ +(f*h>s*p)+","+(this._x1=t+x*a)+","+(this._y1=n+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n;var u=(e=+e)*Math.cos(r),a=e*Math.sin(r),c=t+u,s=n+a,f=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+s:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-s)>1e-6)&&(this._+="L"+c+","+s),e&&(l<0&&(l=l%mh+mh),l>xh?this._+="A"+e+","+e+",0,1,"+f+","+(t-u)+","+(n-a)+"A"+e+","+e+",0,1,"+f+","+(this._x1=c)+","+(this._y1=s):l>1e-6&&(this._+="A"+e+","+e+",0,"+ +(l>=yh)+","+f+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};ce.prototype=se.prototype={constructor:ce,has:function(t){return"$"+t in this},get:function(t){return this["$"+t]},set:function(t,n){return this["$"+t]=n,this},remove:function(t){var n="$"+t;return n in this&&delete this[n]},clear:function(){for(var t in this)"$"===t[0]&&delete this[t]},keys:function(){var t=[];for(var n in this)"$"===n[0]&&t.push(n.slice(1));return t},values:function(){var t=[];for(var n in this)"$"===n[0]&&t.push(this[n]);return t},entries:function(){var t=[];for(var n in this)"$"===n[0]&&t.push({key:n.slice(1),value:this[n]});return t},size:function(){var t=0;for(var n in this)"$"===n[0]&&++t;return t},empty:function(){for(var t in this)if("$"===t[0])return!1;return!0},each:function(t){for(var n in this)"$"===n[0]&&t(this[n],n.slice(1),this)}};var bh=se.prototype;de.prototype=ve.prototype={constructor:de,has:bh.has,add:function(t){return t+="",this["$"+t]=t,this},remove:bh.remove,clear:bh.clear,values:bh.keys,size:bh.size,empty:bh.empty,each:bh.each};var wh={},Mh={},Th=34,Nh=10,kh=13,Sh=_e(","),Eh=Sh.parse,Ah=Sh.parseRows,Ch=Sh.format,zh=Sh.formatRows,Ph=_e("\t"),Rh=Ph.parse,Lh=Ph.parseRows,qh=Ph.format,Dh=Ph.formatRows,Uh=Te.prototype=Ne.prototype;Uh.copy=function(){var t,n,e=new Ne(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=ke(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=ke(n));return e},Uh.add=function(t){var n=+this._x.call(null,t),e=+this._y.call(null,t);return xe(this.cover(n,e),n,e,t)},Uh.addAll=function(t){var n,e,r,i,o=t.length,u=new Array(o),a=new Array(o),c=1/0,s=1/0,f=-1/0,l=-1/0;for(e=0;ef&&(f=r),il&&(l=i));for(ft||t>i||r>n||n>o))return this;var u,a,c=i-e,s=this._root;switch(a=(n<(r+o)/2)<<1|t<(e+i)/2){case 0:do{u=new Array(4),u[a]=s,s=u}while(c*=2,i=e+c,o=r+c,t>i||n>o);break;case 1:do{u=new Array(4),u[a]=s,s=u}while(c*=2,e=i-c,o=r+c,e>t||n>o);break;case 2:do{u=new Array(4),u[a]=s,s=u}while(c*=2,i=e+c,r=o-c,t>i||r>n);break;case 3:do{u=new Array(4),u[a]=s,s=u}while(c*=2,e=i-c,r=o-c,e>t||r>n)}this._root&&this._root.length&&(this._root=s)}return this._x0=e,this._y0=r,this._x1=i,this._y1=o,this},Uh.data=function(){var t=[];return this.visit(function(n){if(!n.length)do{t.push(n.data)}while(n=n.next)}),t},Uh.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},Uh.find=function(t,n,e){var r,i,o,u,a,c,s,f=this._x0,l=this._y0,h=this._x1,p=this._y1,d=[],v=this._root;for(v&&d.push(new be(v,f,l,h,p)),null==e?e=1/0:(f=t-e,l=n-e,h=t+e,p=n+e,e*=e);c=d.pop();)if(!(!(v=c.node)||(i=c.x0)>h||(o=c.y0)>p||(u=c.x1)=_)<<1|t>=g)&&(c=d[d.length-1],d[d.length-1]=d[d.length-1-s],d[d.length-1-s]=c)}else{var y=t-+this._x.call(null,v.data),m=n-+this._y.call(null,v.data),x=y*y+m*m;if(x=(a=(d+g)/2))?d=a:g=a,(f=u>=(c=(v+_)/2))?v=c:_=c,n=p,!(p=p[l=f<<1|s]))return this;if(!p.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;p.data!==t;)if(r=p,!(p=p.next))return this;return(i=p.next)&&delete p.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(p=n[0]||n[1]||n[2]||n[3])&&p===(n[3]||n[2]||n[1]||n[0])&&!p.length&&(e?e[h]=p:this._root=p),this):(this._root=i,this)},Uh.removeAll=function(t){for(var n=0,e=t.length;n0&&(o=0)}return o>0?t.slice(0,o)+t.slice(e+1):t},"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return qe(100*t,n)},r:qe,s:function(t,n){var e=Re(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Oh=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Re(t,Math.max(0,n+o-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},Bh=/^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;De.prototype=Ue.prototype,Ue.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+this.type};var Hh,jh=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];Ie({decimal:".",thousands:",",grouping:[3],currency:["$",""]}),Xe.prototype={constructor:Xe,reset:function(){this.s=this.t=0},add:function(t){Ve(wp,t,this.t),Ve(this,wp.s,this.s),this.s?this.t+=wp.t:this.s=wp.t},valueOf:function(){return this.s}};var Xh,Vh,$h,Wh,Zh,Gh,Qh,Jh,Kh,tp,np,ep,rp,ip,op,up,ap,cp,sp,fp,lp,hp,pp,dp,vp,gp,_p,yp,mp,xp,bp,wp=new Xe,Mp=1e-6,Tp=1e-12,Np=Math.PI,kp=Np/2,Sp=Np/4,Ep=2*Np,Ap=180/Np,Cp=Np/180,zp=Math.abs,Pp=Math.atan,Rp=Math.atan2,Lp=Math.cos,qp=Math.ceil,Dp=Math.exp,Up=Math.log,Op=Math.pow,Fp=Math.sin,Ip=Math.sign||function(t){return t>0?1:t<0?-1:0},Yp=Math.sqrt,Bp=Math.tan,Hp={Feature:function(t,n){Qe(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++rMp?Kh=90:Wp<-Mp&&(Qh=-90),op[0]=Gh,op[1]=Jh}},Gp={sphere:Ge,point:wr,lineStart:Tr,lineEnd:Sr,polygonStart:function(){Gp.lineStart=Er,Gp.lineEnd=Ar},polygonEnd:function(){Gp.lineStart=Tr,Gp.lineEnd=Sr}};Lr.invert=Lr;var Qp,Jp,Kp,td,nd,ed,rd,id,od,ud,ad,cd=je(),sd=Wr(function(){return!0},function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,u){var a=o>0?Np:-Np,c=zp(o-e);zp(c-Np)0?kp:-kp),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),t.point(o,r),n=0):i!==a&&c>=Np&&(zp(e-i)Mp?Pp((Fp(n)*(o=Lp(r))*Fp(e)-Fp(r)*(i=Lp(n))*Fp(t))/(i*o*u)):(n+r)/2}(e,r,o,u),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),n=0),t.point(e=o,r=u),i=a},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}},function(t,n,e,r){var i;if(null==t)i=e*kp,r.point(-Np,i),r.point(0,i),r.point(Np,i),r.point(Np,0),r.point(Np,-i),r.point(0,-i),r.point(-Np,-i),r.point(-Np,0),r.point(-Np,i);else if(zp(t[0]-n[0])>Mp){var o=t[0]Md&&(Md=t),nTd&&(Td=n)},lineStart:Ge,lineEnd:Ge,polygonStart:Ge,polygonEnd:Ge,result:function(){var t=[[bd,wd],[Md,Td]];return Md=Td=-(wd=bd=1/0),t}},kd=0,Sd=0,Ed=0,Ad=0,Cd=0,zd=0,Pd=0,Rd=0,Ld=0,qd={point:yi,lineStart:mi,lineEnd:wi,polygonStart:function(){qd.lineStart=Mi,qd.lineEnd=Ti},polygonEnd:function(){qd.point=yi,qd.lineStart=mi,qd.lineEnd=wi},result:function(){var t=Ld?[Pd/Ld,Rd/Ld]:zd?[Ad/zd,Cd/zd]:Ed?[kd/Ed,Sd/Ed]:[NaN,NaN];return kd=Sd=Ed=Ad=Cd=zd=Pd=Rd=Ld=0,t}};Si.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,Ep)}},result:Ge};var Dd,Ud,Od,Fd,Id,Yd=je(),Bd={point:Ge,lineStart:function(){Bd.point=Ei},lineEnd:function(){Dd&&Ai(Ud,Od),Bd.point=Ge},polygonStart:function(){Dd=!0},polygonEnd:function(){Dd=null},result:function(){var t=+Yd;return Yd.reset(),t}};Ci.prototype={_radius:4.5,_circle:zi(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=zi(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},Ri.prototype={constructor:Ri,point:function(t,n){this.stream.point(t,n)},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()}};var Hd=16,jd=Lp(30*Cp),Xd=Pi({point:function(t,n){this.stream.point(t*Cp,n*Cp)}}),Vd=Vi(function(t){return Yp(2/(1+t))});Vd.invert=$i(function(t){return 2*We(t/2)});var $d=Vi(function(t){return(t=$e(t))&&t/Fp(t)});$d.invert=$i(function(t){return t}),Wi.invert=function(t,n){return[t,2*Pp(Dp(n))-kp]},Ji.invert=Ji,to.invert=$i(Pp),eo.invert=function(t,n){var e,r=n,i=25;do{var o=r*r,u=o*o;r-=e=(r*(1.007226+o*(.015085+u*(.028874*o-.044475-.005916*u)))-n)/(1.007226+o*(.045255+u*(.259866*o-.311325-.005916*11*u)))}while(zp(e)>Mp&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},ro.invert=$i(We),io.invert=$i(function(t){return 2*Pp(t)}),oo.invert=function(t,n){return[-n,2*Pp(Dp(t))-kp]},vo.prototype=fo.prototype={constructor:vo,count:function(){return this.eachAfter(so)},each:function(t){var n,e,r,i,o=this,u=[o];do{for(n=u.reverse(),u=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},sum:function(t){return this.eachAfter(function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e})},sort:function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){var t=[];return this.each(function(n){t.push(n)}),t},leaves:function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},links:function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n},copy:function(){return fo(this).eachBefore(ho)}};var Wd=Array.prototype.slice,Zd="$",Gd={depth:-1},Qd={};Ho.prototype=Object.create(vo.prototype);var Jd=(1+Math.sqrt(5))/2,Kd=function t(n){function e(t,e,r,i,o){Xo(n,t,e,r,i,o)}return e.ratio=function(n){return t((n=+n)>1?n:1)},e}(Jd),tv=function t(n){function e(t,e,r,i,o){if((u=t._squarify)&&u.ratio===n)for(var u,a,c,s,f,l=-1,h=u.length,p=t.value;++l1?n:1)},e}(Jd),nv=[].slice,ev={};Zo.prototype=Ko.prototype={constructor:Zo,defer:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("defer after await");if(null!=this._error)return this;var n=nv.call(arguments,1);return n.push(t),++this._waiting,this._tasks.push(n),Go(this),this},abort:function(){return null==this._error&&Qo(this,new Error("abort")),this},await:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=function(n,e){t.apply(null,[n].concat(e))},Jo(this),this},awaitAll:function(t){if("function"!=typeof t)throw new Error("invalid callback");if(this._call)throw new Error("multiple await");return this._call=t,Jo(this),this}};var rv=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(tu),iv=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(tu),ov=function t(n){function e(){var t=iv.source(n).apply(this,arguments);return function(){return Math.exp(t())}}return e.source=t,e}(tu),uv=function t(n){function e(t){return function(){for(var e=0,r=0;r0?t>1?Cu(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):wv:null};var Mv=wv.range,Tv=6e4,Nv=6048e5,kv=Cu(function(t){t.setTime(1e3*Math.floor(t/1e3))},function(t,n){t.setTime(+t+1e3*n)},function(t,n){return(n-t)/1e3},function(t){return t.getUTCSeconds()}),Sv=kv.range,Ev=Cu(function(t){t.setTime(Math.floor(t/Tv)*Tv)},function(t,n){t.setTime(+t+n*Tv)},function(t,n){return(n-t)/Tv},function(t){return t.getMinutes()}),Av=Ev.range,Cv=Cu(function(t){var n=t.getTimezoneOffset()*Tv%36e5;n<0&&(n+=36e5),t.setTime(36e5*Math.floor((+t-n)/36e5)+n)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getHours()}),zv=Cv.range,Pv=Cu(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Tv)/864e5},function(t){return t.getDate()-1}),Rv=Pv.range,Lv=zu(0),qv=zu(1),Dv=zu(2),Uv=zu(3),Ov=zu(4),Fv=zu(5),Iv=zu(6),Yv=Lv.range,Bv=qv.range,Hv=Dv.range,jv=Uv.range,Xv=Ov.range,Vv=Fv.range,$v=Iv.range,Wv=Cu(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),Zv=Wv.range,Gv=Cu(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});Gv.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Cu(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};var Qv=Gv.range,Jv=Cu(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*Tv)},function(t,n){return(n-t)/Tv},function(t){return t.getUTCMinutes()}),Kv=Jv.range,tg=Cu(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getUTCHours()}),ng=tg.range,eg=Cu(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/864e5},function(t){return t.getUTCDate()-1}),rg=eg.range,ig=Pu(0),og=Pu(1),ug=Pu(2),ag=Pu(3),cg=Pu(4),sg=Pu(5),fg=Pu(6),lg=ig.range,hg=og.range,pg=ug.range,dg=ag.range,vg=cg.range,gg=sg.range,_g=fg.range,yg=Cu(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),mg=yg.range,xg=Cu(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});xg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Cu(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var bg,wg=xg.range,Mg={"-":"",_:" ",0:"0"},Tg=/^\s*\d+/,Ng=/^%/,kg=/[\\^$*+?|[\]().{}]/g;Ha({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var Sg="%Y-%m-%dT%H:%M:%S.%LZ",Eg=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(Sg),Ag=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(Sg),Cg=1e3,zg=60*Cg,Pg=60*zg,Rg=24*Pg,Lg=7*Rg,qg=30*Rg,Dg=365*Rg,Ug=$a("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),Og=$a("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"),Fg=$a("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"),Ig=$a("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"),Yg=al($t(300,.5,0),$t(-240,.5,1)),Bg=al($t(-100,.75,.35),$t(80,1.5,.8)),Hg=al($t(260,.75,.35),$t(80,1.5,.8)),jg=$t(),Xg=Wa($a("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),Vg=Wa($a("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),$g=Wa($a("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),Wg=Wa($a("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),Zg=Math.abs,Gg=Math.atan2,Qg=Math.cos,Jg=Math.max,Kg=Math.min,t_=Math.sin,n_=Math.sqrt,e_=1e-12,r_=Math.PI,i_=r_/2,o_=2*r_;ic.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var u_=pc(oc);hc.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var a_=Array.prototype.slice,c_={draw:function(t,n){var e=Math.sqrt(n/r_);t.moveTo(e,0),t.arc(0,0,e,0,o_)}},s_={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},f_=Math.sqrt(1/3),l_=2*f_,h_={draw:function(t,n){var e=Math.sqrt(n/l_),r=e*f_;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},p_=Math.sin(r_/10)/Math.sin(7*r_/10),d_=Math.sin(o_/10)*p_,v_=-Math.cos(o_/10)*p_,g_={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=d_*e,i=v_*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var u=o_*o/5,a=Math.cos(u),c=Math.sin(u);t.lineTo(c*e,-a*e),t.lineTo(a*r-c*i,c*r+a*i)}t.closePath()}},__={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},y_=Math.sqrt(3),m_={draw:function(t,n){var e=-Math.sqrt(n/(3*y_));t.moveTo(0,2*e),t.lineTo(-y_*e,-e),t.lineTo(y_*e,-e),t.closePath()}},x_=Math.sqrt(3)/2,b_=1/Math.sqrt(12),w_=3*(b_/2+1),M_={draw:function(t,n){var e=Math.sqrt(n/w_),r=e/2,i=e*b_,o=r,u=e*b_+e,a=-o,c=u;t.moveTo(r,i),t.lineTo(o,u),t.lineTo(a,c),t.lineTo(-.5*r-x_*i,x_*r+-.5*i),t.lineTo(-.5*o-x_*u,x_*o+-.5*u),t.lineTo(-.5*a-x_*c,x_*a+-.5*c),t.lineTo(-.5*r+x_*i,-.5*i-x_*r),t.lineTo(-.5*o+x_*u,-.5*u-x_*o),t.lineTo(-.5*a+x_*c,-.5*c-x_*a),t.closePath()}},T_=[c_,s_,h_,__,g_,m_,M_];kc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Nc(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Nc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Sc.prototype={areaStart:Tc,areaEnd:Tc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Nc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ec.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Nc(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ac.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],u=t[e]-i,a=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*u),this._beta*n[c]+(1-this._beta)*(o+r*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var N_=function t(n){function e(t){return 1===n?new kc(t):new Ac(t,n)}return e.beta=function(n){return t(+n)},e}(.85);zc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Cc(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Cc(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var k_=function t(n){function e(t){return new zc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Pc.prototype={areaStart:Tc,areaEnd:Tc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Cc(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var S_=function t(n){function e(t){return new Pc(t,n)}return e.tension=function(n){return t(+n)},e}(0);Rc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Cc(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var E_=function t(n){function e(t){return new Rc(t,n)}return e.tension=function(n){return t(+n)},e}(0);qc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Lc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var A_=function t(n){function e(t){return n?new qc(t,n):new zc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Dc.prototype={areaStart:Tc,areaEnd:Tc,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Lc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var C_=function t(n){function e(t){return n?new Dc(t,n):new Pc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Uc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Lc(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var z_=function t(n){function e(t){return n?new Uc(t,n):new Rc(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);Oc.prototype={areaStart:Tc,areaEnd:Tc,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t=+t,n=+n,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},Hc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Bc(this,this._t0,Yc(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var e=NaN;if(t=+t,n=+n,t!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,Bc(this,Yc(this,e=Ic(this,t,n)),e);break;default:Bc(this,this._t0,e=Ic(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=e}}},(jc.prototype=Object.create(Hc.prototype)).point=function(t,n){Hc.prototype.point.call(this,n,t)},Xc.prototype={moveTo:function(t,n){this._context.moveTo(n,t)},closePath:function(){this._context.closePath()},lineTo:function(t,n){this._context.lineTo(n,t)},bezierCurveTo:function(t,n,e,r,i,o){this._context.bezierCurveTo(n,t,r,e,o,i)}},Vc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,n=this._y,e=t.length;if(e)if(this._line?this._context.lineTo(t[0],n[0]):this._context.moveTo(t[0],n[0]),2===e)this._context.lineTo(t[1],n[1]);else for(var r=$c(t),i=$c(n),o=0,u=1;u=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}},rs.prototype={constructor:rs,insert:function(t,n){var e,r,i;if(t){if(n.P=t,n.N=t.N,t.N&&(t.N.P=n),t.N=n,t.R){for(t=t.R;t.L;)t=t.L;t.L=n}else t.R=n;e=t}else this._?(t=as(this._),n.P=null,n.N=t,t.P=t.L=n,e=t):(n.P=n.N=null,this._=n,e=null);for(n.L=n.R=null,n.U=e,n.C=!0,t=n;e&&e.C;)e===(r=e.U).L?(i=r.R)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.R&&(os(this,e),e=(t=e).U),e.C=!1,r.C=!0,us(this,r)):(i=r.L)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.L&&(us(this,e),e=(t=e).U),e.C=!1,r.C=!0,os(this,r)),e=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var n,e,r,i=t.U,o=t.L,u=t.R;if(e=o?u?as(u):o:u,i?i.L===t?i.L=e:i.R=e:this._=e,o&&u?(r=e.C,e.C=t.C,e.L=o,o.U=e,e!==u?(i=e.U,e.U=t.U,t=e.R,i.L=t,e.R=u,u.U=e):(e.U=i,i=e,t=e.R)):(r=t.C,t=e),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((n=i.R).C&&(n.C=!1,i.C=!0,os(this,i),n=i.R),n.L&&n.L.C||n.R&&n.R.C){n.R&&n.R.C||(n.L.C=!1,n.C=!0,us(this,n),n=i.R),n.C=i.C,i.C=n.R.C=!1,os(this,i),t=this._;break}}else if((n=i.L).C&&(n.C=!1,i.C=!0,us(this,i),n=i.L),n.L&&n.L.C||n.R&&n.R.C){n.L&&n.L.C||(n.R.C=!1,n.C=!0,os(this,n),n=i.L),n.C=i.C,i.C=n.L.C=!1,us(this,i),t=this._;break}n.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var P_,R_,L_,q_,D_,U_=[],O_=[],F_=1e-6,I_=1e-12;Ns.prototype={constructor:Ns,polygons:function(){var t=this.edges;return this.cells.map(function(n){var e=n.halfedges.map(function(e){return ds(n,t[e])});return e.data=n.site.data,e})},triangles:function(){var t=[],n=this.edges;return this.cells.forEach(function(e,r){if(o=(i=e.halfedges).length)for(var i,o,u,a=e.site,c=-1,s=n[i[o-1]],f=s.left===a?s.right:s.left;++c=a)return null;var c=t-i.site[0],s=n-i.site[1],f=c*c+s*s;do{i=o.cells[r=u],u=null,i.halfedges.forEach(function(e){var r=o.edges[e],a=r.left;if(a!==i.site&&a||(a=r.right)){var c=t-a[0],s=n-a[1],l=c*c+s*s;lt?1:n>=t?0:NaN},t.deviation=u,t.extent=a,t.histogram=function(){function t(t){var i,o,u=t.length,a=new Array(u);for(i=0;il;)h.pop(),--d;var v,g=new Array(d+1);for(i=0;i<=d;++i)(v=g[i]=[]).x0=i>0?h[i-1]:s,v.x1=i=e)for(r=e;++or&&(r=e)}else for(;++o=e)for(r=e;++or&&(r=e);return r},t.mean=function(t,n){var e,r=t.length,o=r,u=-1,a=0;if(null==n)for(;++u=o.length)return null!=e&&n.sort(e),null!=r?r(n):n;for(var c,s,f,l=-1,h=n.length,p=o[i++],d=se(),v=u();++lo.length)return t;var i,a=u[e-1];return null!=r&&e>=o.length?i=t.entries():(i=[],t.each(function(t,r){i.push({key:r,values:n(t,e)})})),null!=a?i.sort(function(t,n){return a(t.key,n.key)}):i}var e,r,i,o=[],u=[];return i={object:function(n){return t(n,0,fe,le)},map:function(n){return t(n,0,he,pe)},entries:function(e){return n(t(e,0,he,pe),0)},key:function(t){return o.push(t),i},sortKeys:function(t){return u[o.length-1]=t,i},sortValues:function(t){return e=t,i},rollup:function(t){return r=t,i}}},t.set=ve,t.map=se,t.keys=function(t){var n=[];for(var e in t)n.push(e);return n},t.values=function(t){var n=[];for(var e in t)n.push(t[e]);return n},t.entries=function(t){var n=[];for(var e in t)n.push({key:e,value:t[e]});return n},t.color=Et,t.rgb=Pt,t.hsl=qt,t.lab=Ft,t.hcl=Xt,t.cubehelix=$t,t.dispatch=N,t.drag=function(){function n(t){t.on("mousedown.drag",e).filter(g).on("touchstart.drag",o).on("touchmove.drag",u).on("touchend.drag touchcancel.drag",a).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function e(){if(!h&&p.apply(this,arguments)){var n=c("mouse",d.apply(this,arguments),pt,this,arguments);n&&(ct(t.event.view).on("mousemove.drag",r,!0).on("mouseup.drag",i,!0),_t(t.event.view),vt(),l=!1,s=t.event.clientX,f=t.event.clientY,n("start"))}}function r(){if(gt(),!l){var n=t.event.clientX-s,e=t.event.clientY-f;l=n*n+e*e>x}_.mouse("drag")}function i(){ct(t.event.view).on("mousemove.drag mouseup.drag",null),yt(t.event.view,l),gt(),_.mouse("end")}function o(){if(p.apply(this,arguments)){var n,e,r=t.event.changedTouches,i=d.apply(this,arguments),o=r.length;for(n=0;nc+p||is+p||or.index){var d=c-a.x-a.vx,v=s-a.y-a.vy,g=d*d+v*v;gt.r&&(t.r=t[n].r)}function r(){if(i){var n,e,r=i.length;for(o=new Array(r),n=0;n=f)){(t.data!==o||t.next)&&(0===i&&(i=me(),p+=i*i),0===c&&(c=me(),p+=c*c),p1?(null==n?l.remove(t):l.set(t,i(n)),o):l.get(t)},find:function(n,e,r){var i,o,u,a,c,s=0,f=t.length;for(null==r?r=1/0:r*=r,s=0;s1?(p.on(t,n),o):p.on(t)}}},t.forceX=function(t){function n(t){for(var n,e=0,u=r.length;emr(r[0],r[1])&&(r[1]=i[1]),mr(i[0],r[1])>mr(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(u=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(a=mr(r[1],i[0]))>u&&(u=a,Gh=i[0],Jh=r[1])}return ip=op=null,Gh===1/0||Qh===1/0?[[NaN,NaN],[NaN,NaN]]:[[Gh,Qh],[Jh,Kh]]},t.geoCentroid=function(t){up=ap=cp=sp=fp=lp=hp=pp=dp=vp=gp=0,tr(t,Gp);var n=dp,e=vp,r=gp,i=n*n+e*e+r*r;return i=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?f:c).invert(t)},t.stream=function(t){return e&&r===t?e:e=function(t){var n=t.length;return{point:function(e,r){for(var i=-1;++i2?t[2]+90:90]):(t=e(),[t[0],t[1],t[2]-90])},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=oo,t.geoRotation=Fr,t.geoStream=tr,t.geoTransform=function(t){return{stream:Pi(t)}},t.cluster=function(){function t(t){var o,u=0;t.eachAfter(function(t){var e=t.children;e?(t.x=function(t){return t.reduce(ao,0)/t.length}(e),t.y=function(t){return 1+t.reduce(co,0)}(e)):(t.x=o?u+=n(t,o):0,t.y=0,o=t)});var a=function(t){for(var n;n=t.children;)t=n[0];return t}(t),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(t),s=a.x-n(a,c)/2,f=c.x+n(c,a)/2;return t.eachAfter(i?function(n){n.x=(n.x-t.x)*e,n.y=(t.y-n.y)*r}:function(n){n.x=(n.x-s)/(f-s)*e,n.y=(1-(t.y?n.y/t.y:1))*r})}var n=uo,e=1,r=1,i=!1;return t.separation=function(e){return arguments.length?(n=e,t):n},t.size=function(n){return arguments.length?(i=!1,e=+n[0],r=+n[1],t):i?null:[e,r]},t.nodeSize=function(n){return arguments.length?(i=!0,e=+n[0],r=+n[1],t):i?[e,r]:null},t},t.hierarchy=fo,t.pack=function(){function t(t){return t.x=e/2,t.y=r/2,n?t.eachBefore(zo(n)).eachAfter(Po(i,.5)).eachBefore(Ro(1)):t.eachBefore(zo(Co)).eachAfter(Po(Eo,1)).eachAfter(Po(i,t.r/Math.min(e,r))).eachBefore(Ro(Math.min(e,r)/(2*t.r))),t}var n=null,e=1,r=1,i=Eo;return t.radius=function(e){return arguments.length?(n=function(t){return null==t?null:So(t)}(e),t):n},t.size=function(n){return arguments.length?(e=+n[0],r=+n[1],t):[e,r]},t.padding=function(n){return arguments.length?(i="function"==typeof n?n:Ao(+n),t):i},t},t.packSiblings=function(t){return ko(t),t},t.packEnclose=go,t.partition=function(){function t(t){var o=t.height+1;return t.x0=t.y0=r,t.x1=n,t.y1=e/o,t.eachBefore(function(t,n){return function(e){e.children&&qo(e,e.x0,t*(e.depth+1)/n,e.x1,t*(e.depth+2)/n);var i=e.x0,o=e.y0,u=e.x1-r,a=e.y1-r;u0)throw new Error("cycle");return o}var n=Do,e=Uo;return t.id=function(e){return arguments.length?(n=So(e),t):n},t.parentId=function(n){return arguments.length?(e=So(n),t):e},t},t.tree=function(){function t(t){var c=function(t){for(var n,e,r,i,o,u=new Ho(t,0),a=[u];n=a.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)a.push(e=n.children[i]=new Ho(r[i],i)),e.parent=n;return(u.parent=new Ho(null,0)).children=[u],u}(t);if(c.eachAfter(n),c.parent.m=-c.z,c.eachBefore(e),a)t.eachBefore(r);else{var s=t,f=t,l=t;t.eachBefore(function(t){t.xf.x&&(f=t),t.depth>l.depth&&(l=t)});var h=s===f?1:i(s,f)/2,p=h-s.x,d=o/(f.x+h+p),v=u/(l.depth||1);t.eachBefore(function(t){t.x=(t.x+p)*d,t.y=t.depth*v})}return t}function n(t){var n=t.children,e=t.parent.children,r=t.i?e[t.i-1]:null;if(n){(function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)})(t);var o=(n[0].z+n[n.length-1].z)/2;r?(t.z=r.z+i(t._,r._),t.m=t.z-o):t.z=o}else r&&(t.z=r.z+i(t._,r._));t.parent.A=function(t,n,e){if(n){for(var r,o=t,u=t,a=n,c=o.parent.children[0],s=o.m,f=u.m,l=a.m,h=c.m;a=Io(a),o=Fo(o),a&&o;)c=Fo(c),(u=Io(u)).a=t,(r=a.z+l-o.z-s+i(a._,o._))>0&&(Yo(Bo(a,t,e),t,r),s+=r,f+=r),l+=a.m,s+=o.m,h+=c.m,f+=u.m;a&&!Io(u)&&(u.t=a,u.m+=l-f),o&&!Fo(c)&&(c.t=o,c.m+=s-h,e=t)}return e}(t,r,t.parent.A||e[0])}function e(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function r(t){t.x*=o,t.y=t.depth*u}var i=Oo,o=1,u=1,a=null;return t.separation=function(n){return arguments.length?(i=n,t):i},t.size=function(n){return arguments.length?(a=!1,o=+n[0],u=+n[1],t):a?null:[o,u]},t.nodeSize=function(n){return arguments.length?(a=!0,o=+n[0],u=+n[1],t):a?[o,u]:null},t},t.treemap=function(){function t(t){return t.x0=t.y0=0,t.x1=i,t.y1=o,t.eachBefore(n),u=[0],r&&t.eachBefore(Lo),t}function n(t){var n=u[t.depth],r=t.x0+n,i=t.y0+n,o=t.x1-n,h=t.y1-n;o=n-1){var s=c[t];return s.x0=r,s.y0=i,s.x1=u,void(s.y1=a)}for(var l=f[t],h=e/2+l,p=t+1,d=n-1;p>>1;f[v]a-i){var y=(r*_+u*g)/e;o(t,p,g,r,i,y,a),o(p,n,_,y,i,u,a)}else{var m=(i*_+a*g)/e;o(t,p,g,r,i,u,m),o(p,n,_,r,m,u,a)}}var u,a,c=t.children,s=c.length,f=new Array(s+1);for(f[0]=a=u=0;u=0;--n)s.push(t[r[o[n]][2]]);for(n=+a;na!=s>a&&u<(c-e)*(a-r)/(s-r)+e&&(f=!f),c=e,s=r;return f},t.polygonLength=function(t){for(var n,e,r=-1,i=t.length,o=t[i-1],u=o[0],a=o[1],c=0;++r1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return jg.h=360*t-100,jg.s=1.5-1.5*n,jg.l=.8-.9*n,jg+""},t.interpolateWarm=Bg,t.interpolateCool=Hg,t.interpolateViridis=Xg,t.interpolateMagma=Vg,t.interpolateInferno=$g,t.interpolatePlasma=Wg,t.scaleSequential=Za,t.create=function(t){return ct(A(t).call(document.documentElement))},t.creator=A,t.local=st,t.matcher=of,t.mouse=pt,t.namespace=E,t.namespaces=tf,t.clientPoint=ht,t.select=ct,t.selectAll=function(t){return"string"==typeof t?new ut([document.querySelectorAll(t)],[document.documentElement]):new ut([null==t?[]:t],cf)},t.selection=at,t.selector=z,t.selectorAll=R,t.style=I,t.touch=dt,t.touches=function(t,n){null==n&&(n=lt().touches);for(var e=0,r=n?n.length:0,i=new Array(r);eh;if(c||(c=t=ee()),le_)if(d>o_-e_)c.moveTo(l*Qg(h),l*t_(h)),c.arc(0,0,l,h,p,!v),f>e_&&(c.moveTo(f*Qg(p),f*t_(p)),c.arc(0,0,f,p,h,v));else{var g,_,y=h,m=p,x=h,b=p,w=d,M=d,T=a.apply(this,arguments)/2,N=T>e_&&(i?+i.apply(this,arguments):n_(f*f+l*l)),k=Kg(Zg(l-f)/2,+r.apply(this,arguments)),S=k,E=k;if(N>e_){var A=Qa(N/f*t_(T)),C=Qa(N/l*t_(T));(w-=2*A)>e_?(A*=v?1:-1,x+=A,b-=A):(w=0,x=b=(h+p)/2),(M-=2*C)>e_?(C*=v?1:-1,y+=C,m-=C):(M=0,y=m=(h+p)/2)}var z=l*Qg(y),P=l*t_(y),R=f*Qg(b),L=f*t_(b);if(k>e_){var q=l*Qg(m),D=l*t_(m),U=f*Qg(x),O=f*t_(x);if(de_?function(t,n,e,r,i,o,u,a){var c=e-t,s=r-n,f=u-i,l=a-o,h=(f*(n-o)-l*(t-i))/(l*c-f*s);return[t+h*c,n+h*s]}(z,P,U,O,q,D,R,L):[R,L],I=z-F[0],Y=P-F[1],B=q-F[0],H=D-F[1],j=1/t_(function(t){return t>1?0:t<-1?r_:Math.acos(t)}((I*B+Y*H)/(n_(I*I+Y*Y)*n_(B*B+H*H)))/2),X=n_(F[0]*F[0]+F[1]*F[1]);S=Kg(k,(f-X)/(j-1)),E=Kg(k,(l-X)/(j+1))}}M>e_?E>e_?(g=rc(U,O,z,P,l,E,v),_=rc(q,D,R,L,l,E,v),c.moveTo(g.cx+g.x01,g.cy+g.y01),Ee_&&w>e_?S>e_?(g=rc(R,L,q,D,f,-S,v),_=rc(z,P,U,O,f,-S,v),c.lineTo(g.cx+g.x01,g.cy+g.y01),S0&&(p+=l);for(null!=e?d.sort(function(t,n){return e(v[t],v[n])}):null!=r&&d.sort(function(n,e){return r(t[n],t[e])}),a=0,s=p?(_-h*m)/p:0;a0?l*s:0)+m,v[c]={data:t[c],index:a,value:l,startAngle:g,endAngle:f,padAngle:y};return v}var n=lc,e=fc,r=null,i=Ga(0),o=Ga(o_),u=Ga(0);return t.value=function(e){return arguments.length?(n="function"==typeof e?e:Ga(+e),t):n},t.sortValues=function(n){return arguments.length?(e=n,r=null,t):e},t.sort=function(n){return arguments.length?(r=n,e=null,t):r},t.startAngle=function(n){return arguments.length?(i="function"==typeof n?n:Ga(+n),t):i},t.endAngle=function(n){return arguments.length?(o="function"==typeof n?n:Ga(+n),t):o},t.padAngle=function(n){return arguments.length?(u="function"==typeof n?n:Ga(+n),t):u},t},t.areaRadial=gc,t.radialArea=gc,t.lineRadial=vc,t.radialLine=vc,t.pointRadial=_c,t.linkHorizontal=function(){return xc(bc)},t.linkVertical=function(){return xc(wc)},t.linkRadial=function(){var t=xc(Mc);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.symbol=function(){function t(){var t;if(r||(r=t=ee()),n.apply(this,arguments).draw(r,+e.apply(this,arguments)),t)return r=null,t+""||null}var n=Ga(c_),e=Ga(64),r=null;return t.type=function(e){return arguments.length?(n="function"==typeof e?e:Ga(e),t):n},t.size=function(n){return arguments.length?(e="function"==typeof n?n:Ga(+n),t):e},t.context=function(n){return arguments.length?(r=null==n?null:n,t):r},t},t.symbols=T_,t.symbolCircle=c_,t.symbolCross=s_,t.symbolDiamond=h_,t.symbolSquare=__,t.symbolStar=g_,t.symbolTriangle=m_,t.symbolWye=M_,t.curveBasisClosed=function(t){return new Sc(t)},t.curveBasisOpen=function(t){return new Ec(t)},t.curveBasis=function(t){return new kc(t)},t.curveBundle=N_,t.curveCardinalClosed=S_,t.curveCardinalOpen=E_,t.curveCardinal=k_,t.curveCatmullRomClosed=C_,t.curveCatmullRomOpen=z_,t.curveCatmullRom=A_,t.curveLinearClosed=function(t){return new Oc(t)},t.curveLinear=oc,t.curveMonotoneX=function(t){return new Hc(t)},t.curveMonotoneY=function(t){return new jc(t)},t.curveNatural=function(t){return new Vc(t)},t.curveStep=function(t){return new Wc(t,.5)},t.curveStepAfter=function(t){return new Wc(t,1)},t.curveStepBefore=function(t){return new Wc(t,0)},t.stack=function(){function t(t){var o,u,a=n.apply(this,arguments),c=t.length,s=a.length,f=new Array(s);for(o=0;o0){for(var e,r,i,o=0,u=t[0].length;o1)for(var e,r,i,o,u,a,c=0,s=t[n[0]].length;c=0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=u,r[0]=u+=i):r[0]=o},t.stackOffsetNone=Zc,t.stackOffsetSilhouette=function(t,n){if((e=t.length)>0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,u=1;uxl&&e.name===n)return new qn([[t]],Jl,n,+r)}return null},t.interrupt=Pn,t.voronoi=function(){function t(t){return new Ns(t.map(function(r,i){var o=[Math.round(n(r,i,t)/F_)*F_,Math.round(e(r,i,t)/F_)*F_];return o.index=i,o.data=r,o}),r)}var n=ns,e=es,r=null;return t.polygons=function(n){return t(n).polygons()},t.links=function(n){return t(n).links()},t.triangles=function(n){return t(n).triangles()},t.x=function(e){return arguments.length?(n="function"==typeof e?e:ts(+e),t):n},t.y=function(n){return arguments.length?(e="function"==typeof n?n:ts(+n),t):e},t.extent=function(n){return arguments.length?(r=null==n?null:[[+n[0][0],+n[0][1]],[+n[1][0],+n[1][1]]],t):r&&[[r[0][0],r[0][1]],[r[1][0],r[1][1]]]},t.size=function(n){return arguments.length?(r=null==n?null:[[0,0],[+n[0],+n[1]]],t):r&&[r[1][0]-r[0][0],r[1][1]-r[0][1]]},t},t.zoom=function(){function n(t){t.property("__zoom",Rs).on("wheel.zoom",c).on("mousedown.zoom",s).on("dblclick.zoom",f).filter(x).on("touchstart.zoom",l).on("touchmove.zoom",h).on("touchend.zoom touchcancel.zoom",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function e(t,n){return(n=Math.max(b[0],Math.min(b[1],n)))===t.k?t:new Ss(n,t.x,t.y)}function r(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new Ss(t.k,r,i)}function i(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function o(t,n,e){t.on("start.zoom",function(){u(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){u(this,arguments).end()}).tween("zoom",function(){var t=arguments,r=u(this,t),o=_.apply(this,t),a=e||i(o),c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),s=this.__zoom,f="function"==typeof n?n.apply(this,t):n,l=T(s.invert(a).concat(c/s.k),f.invert(a).concat(c/f.k));return function(t){if(1===t)t=f;else{var n=l(t),e=c/n[2];t=new Ss(e,a[0]-n[0]*e,a[1]-n[1]*e)}r.zoom(null,t)}})}function u(t,n){for(var e,r=0,i=k.length;rC}n.zoom("mouse",y(r(n.that.__zoom,n.mouse[0]=pt(n.that),n.mouse[1]),n.extent,w))},!0).on("mouseup.zoom",function(){e.on("mousemove.zoom mouseup.zoom",null),yt(t.event.view,n.moved),Cs(),n.end()},!0),i=pt(this),o=t.event.clientX,a=t.event.clientY;_t(t.event.view),As(),n.mouse=[i,this.__zoom.invert(i)],Pn(this),n.start()}}function f(){if(g.apply(this,arguments)){var i=this.__zoom,u=pt(this),a=i.invert(u),c=i.k*(t.event.shiftKey?.5:2),s=y(r(e(i,c),u,a),_.apply(this,arguments),w);Cs(),M>0?ct(this).transition().duration(M).call(o,s,u):ct(this).call(n.transform,s)}}function l(){if(g.apply(this,arguments)){var n,e,r,i,o=u(this,arguments),a=t.event.changedTouches,c=a.length;for(As(),e=0;e=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), +a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), +void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,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"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("