├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .vscode
├── settings.json
└── tasks.json
├── FUNDING.yml
├── LICENSE
├── Logo.svg
├── README.md
├── docs
├── CNAME
├── GD-quick-overview.gif
├── favicon.ico
├── gtr-cof.css
└── index.html
├── jsconfig.json
├── package-lock.json
├── package.json
├── src
├── chord-interval-module.ts
├── cof-module.ts
├── cookie-module.ts
├── events-module.ts
├── gtr-cof.ts
├── gtr-module.ts
├── menu-module.ts
├── midi-module.ts
├── mod-module.ts
├── modes-module.ts
├── music-module.ts
├── permalink-module.ts
├── scale-family-module.ts
├── settings-module.ts
├── state-module.ts
├── tonics-module.ts
└── tuning-module.ts
└── tsconfig.json
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on: [push]
3 | jobs:
4 | build-and-deploy:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - uses: actions/setup-node@v1
9 | - run: npm ci
10 | - run: tsc --build tsconfig.json
11 | - run: mkdir docs/src
12 | - run: cp src/*.ts docs/src
13 | - run: echo "Run number=$GITHUB_RUN_NUMBER, Actor=$GITHUB_ACTOR, Sha=$GITHUB_SHA" > docs/version.txt
14 | - name: Deploy
15 | uses: peaceiris/actions-gh-pages@v3
16 | with:
17 | github_token: ${{ secrets.GITHUB_TOKEN }}
18 | publish_dir: ./docs
19 | publish_branch: publish
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | debug.log
4 | gtr-cof.js
5 | gtr-cof.js.map
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "typescript",
6 | "tsconfig": "tsconfig.json",
7 | "group": {
8 | "kind": "build",
9 | "isDefault": true
10 | }
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # repo: mikehadlow/gtr-cof
2 | # filename: FUNDING.YML
3 |
4 | github: mikehadlow
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Mike Hadlow
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Guitar Dashboard
2 | An interactive music theory dashboard for guitarists. http://guitardashboard.com/
3 |
4 | The aim is to provide a graphical representation of music theory elements (scales, modes, chords etc) mapped to a guitar fretboard.
5 |
6 | ## Developing with VS Code
7 |
8 | Guitar Dashboard is written in Typescript using VS Code. Make all code changes in the src/*.ts files. Compilation outputs to the docs folder, do not edit the *.js or *.js.map files in this directory. They are included in the source repository because the website is hosted in GitHub pages which does not support Typescript compilation.
9 |
10 | 1. Clone or fork-and-clone this repository.
11 | 2. File -> Open folder at the root directory of the cloned repository.
12 | 4. To develop locally using lite-server:
13 | - npm install
14 | - npm start
15 | 5. Browse to http://localhost:10001/
16 | 6. Edit the src/*.ts, index.html and gtr-cof.css files.
17 | 8. Commit, push to GitHub and create a pull request :)
18 |
19 | ## Developing without VS Code
20 |
21 | First, make sure you have TypeScript installed. If not, `npm install -g typescript` will do the trick.
22 |
23 | 1. Clone the repo and go into it
24 | 2. Run `npm install`
25 | 3. Open a shell and run `tsc --watch` so that the sources are always rebuilt automatically on source changes
26 | 4. Open another shell and run `npm start` in it so that results will be visible in a browser
27 | 5. Browse to http://localhost:10001/
28 | 6. Edit the src/*.ts, index.html and gtr-cof.css files.
29 | 7. Commit, push to GitHub and create a pull request :)
30 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | guitardashboard.com
--------------------------------------------------------------------------------
/docs/GD-quick-overview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikehadlow/gtr-cof/7d108d983dc439d81f2fae7472810bc0b9440c36/docs/GD-quick-overview.gif
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikehadlow/gtr-cof/7d108d983dc439d81f2fae7472810bc0b9440c36/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/gtr-cof.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
3 | font-size: 12pt;
4 | margin: 0px;
5 | }
6 |
7 | text {
8 | pointer-events: none;
9 | }
10 |
11 | div {
12 | margin: 0px;
13 | padding: 0px;
14 | }
15 |
16 | .header {
17 | margin: 0;
18 | padding: 0;
19 | overflow: hidden;
20 | background-color: #333;
21 | }
22 |
23 | footer {
24 | margin: 0;
25 | padding: 0px 16px;
26 | overflow: hidden;
27 | color: white;
28 | background-color: #333;
29 | }
30 |
31 | .title {
32 | font-weight: bold;
33 | font-size: 14pt;
34 | margin: 0pt;
35 | }
36 |
37 | .menu {
38 | display: inline-block;
39 | color: white;
40 | text-align: center;
41 | padding: 14px 16px;
42 | text-decoration: none;
43 | cursor: default;
44 | }
45 |
46 | .dropdown {
47 | display: inline-block;
48 | }
49 |
50 | .dropdown:hover {
51 | background-color: #444;
52 | }
53 |
54 | .dropdown-content {
55 | display: none;
56 | position: absolute;
57 | background-color: #f9f9f9;
58 | color: black;
59 | min-width: 160px;
60 | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
61 | text-align: left;
62 | z-index: 1;
63 | }
64 |
65 | .with-padding {
66 | padding: 12px 16px;
67 | }
68 |
69 | /* Links inside the dropdown */
70 | .dropdown-content-item {
71 | color: black;
72 | padding: 12px 16px;
73 | text-decoration: none;
74 | display: block;
75 | cursor: default;
76 | }
77 |
78 | /* Change color of dropdown links on hover */
79 | .dropdown-content-item:hover {
80 | background-color: #f1f1f1;
81 | }
82 |
83 | .dropdown-content-visible {
84 | display: block;
85 | }
86 |
87 | .content {
88 | padding: 10px 10px 10px 10px;
89 | }
90 |
91 | /* Circle of Fifths */
92 | .note-segment {
93 | fill: lightgrey;
94 | stroke: none;
95 | }
96 | .note-segment-tonic {
97 | fill: yellow;
98 | stroke: black;
99 | stroke-width: 3px;
100 | }
101 | .note-segment-scale {
102 | fill: white;
103 | stroke: black;
104 | stroke-width: 3px;
105 | }
106 | .note-segment-text {
107 | font-size: 35px;
108 | text-anchor: middle;
109 | fill: black;
110 | }
111 | .degree-segment {
112 | fill: none;
113 | stroke: none;
114 | }
115 | .degree-segment-selected {
116 | fill: white;
117 | stroke: black;
118 | stroke-width: 2px;
119 | }
120 | .degree-segment-text {
121 | font-size: 20px;
122 | text-anchor: middle;
123 | fill: black;
124 | }
125 | .chord-segment {
126 | fill: none;
127 | stroke: none;
128 | }
129 | .chord-segment-dim {
130 | fill: lightcoral;
131 | stroke: black;
132 | stroke-width: 2px;
133 | }
134 | .chord-segment-aug {
135 | fill: lightsalmon;
136 | stroke: black;
137 | stroke-width: 2px;
138 | }
139 | .chord-segment-minor {
140 | fill: lightblue;
141 | stroke: black;
142 | stroke-width: 2px;
143 | }
144 | .chord-segment-major {
145 | fill: lightgreen;
146 | stroke: black;
147 | stroke-width: 2px;
148 | }
149 | .chord-segment-note {
150 | fill: none;
151 | stroke: none;
152 | }
153 | .interval-note-selected {
154 | stroke: black;
155 | stroke-width: 2px;
156 | }
157 | .interval-segment {
158 | fill: white;
159 | stroke: none;
160 | }
161 | .interval-note {
162 | fill: none;
163 | stroke: none;
164 | }
165 | /* Modes */
166 | .mode-button {
167 | fill: white;
168 | stroke: black;
169 | }
170 | .mode-button-selected {
171 | fill: yellow;
172 | }
173 | .mode-text {
174 | font-size: 15;
175 | text-anchor: left;
176 | fill: black;
177 | }
178 |
179 | /* Tonics */
180 | .tonic-button {
181 | fill: white;
182 | stroke: black;
183 | }
184 | .tonic-button-grey {
185 | fill: grey;
186 | }
187 | .tonic-button-selected {
188 | fill: yellow;
189 | }
190 | .tonic-text {
191 | font-size: 15;
192 | text-anchor: left;
193 | fill: black;
194 | }
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Guitar Chord Finder, Free Interactive Tool | Guitar Dashboard
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
83 |
84 |
85 |
91 |
92 |
95 |
96 |
97 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6"
4 | },
5 | }
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gtr-cof",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/d3": {
8 | "version": "3.5.44",
9 | "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.44.tgz",
10 | "integrity": "sha512-hFEcf03YGJ2uQoDYEp3nFD5mXWxly5kf6KOstuOQFEs9sUCN7kNlKhcYkpZ3gK6PiHz4XRLkoHa80NVCJNeLBw==",
11 | "dev": true
12 | },
13 | "@types/webmidi": {
14 | "version": "2.0.4",
15 | "resolved": "https://registry.npmjs.org/@types/webmidi/-/webmidi-2.0.4.tgz",
16 | "integrity": "sha512-ouA837zUpSzURsUGvcnay6JwUz2B/UoJP3Lkacnz60y+rul8/auVOFS9LSO6v0gdNbGvRkbHEpayD8PbO7IKhw==",
17 | "dev": true
18 | },
19 | "accepts": {
20 | "version": "1.3.7",
21 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
22 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
23 | "dev": true,
24 | "requires": {
25 | "mime-types": "~2.1.24",
26 | "negotiator": "0.6.2"
27 | }
28 | },
29 | "after": {
30 | "version": "0.8.2",
31 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
32 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
33 | "dev": true
34 | },
35 | "ansi-regex": {
36 | "version": "3.0.0",
37 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
38 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
39 | "dev": true
40 | },
41 | "ansi-styles": {
42 | "version": "3.2.1",
43 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
44 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
45 | "dev": true,
46 | "requires": {
47 | "color-convert": "^1.9.0"
48 | }
49 | },
50 | "anymatch": {
51 | "version": "3.1.1",
52 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
53 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
54 | "dev": true,
55 | "requires": {
56 | "normalize-path": "^3.0.0",
57 | "picomatch": "^2.0.4"
58 | }
59 | },
60 | "arraybuffer.slice": {
61 | "version": "0.0.7",
62 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
63 | "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
64 | "dev": true
65 | },
66 | "async": {
67 | "version": "1.5.2",
68 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
69 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
70 | "dev": true
71 | },
72 | "async-each-series": {
73 | "version": "0.1.1",
74 | "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz",
75 | "integrity": "sha1-dhfBkXQB/Yykooqtzj266Yr+tDI=",
76 | "dev": true
77 | },
78 | "async-limiter": {
79 | "version": "1.0.1",
80 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
81 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
82 | "dev": true
83 | },
84 | "axios": {
85 | "version": "0.19.0",
86 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
87 | "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
88 | "dev": true,
89 | "requires": {
90 | "follow-redirects": "1.5.10",
91 | "is-buffer": "^2.0.2"
92 | },
93 | "dependencies": {
94 | "follow-redirects": {
95 | "version": "1.5.10",
96 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
97 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
98 | "dev": true,
99 | "requires": {
100 | "debug": "=3.1.0"
101 | }
102 | }
103 | }
104 | },
105 | "backo2": {
106 | "version": "1.0.2",
107 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
108 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
109 | "dev": true
110 | },
111 | "balanced-match": {
112 | "version": "1.0.0",
113 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
114 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
115 | "dev": true
116 | },
117 | "base64-arraybuffer": {
118 | "version": "0.1.4",
119 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
120 | "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=",
121 | "dev": true
122 | },
123 | "base64id": {
124 | "version": "1.0.0",
125 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
126 | "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
127 | "dev": true
128 | },
129 | "batch": {
130 | "version": "0.6.1",
131 | "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
132 | "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
133 | "dev": true
134 | },
135 | "better-assert": {
136 | "version": "1.0.2",
137 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
138 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
139 | "dev": true,
140 | "requires": {
141 | "callsite": "1.0.0"
142 | }
143 | },
144 | "binary-extensions": {
145 | "version": "2.1.0",
146 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
147 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
148 | "dev": true
149 | },
150 | "blob": {
151 | "version": "0.0.5",
152 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
153 | "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
154 | "dev": true
155 | },
156 | "brace-expansion": {
157 | "version": "1.1.11",
158 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
159 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
160 | "dev": true,
161 | "requires": {
162 | "balanced-match": "^1.0.0",
163 | "concat-map": "0.0.1"
164 | }
165 | },
166 | "braces": {
167 | "version": "3.0.2",
168 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
169 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
170 | "dev": true,
171 | "requires": {
172 | "fill-range": "^7.0.1"
173 | }
174 | },
175 | "browser-sync": {
176 | "version": "2.26.13",
177 | "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.26.13.tgz",
178 | "integrity": "sha512-JPYLTngIzI+Dzx+StSSlMtF+Q9yjdh58HW6bMFqkFXuzQkJL8FCvp4lozlS6BbECZcsM2Gmlgp0uhEjvl18X4w==",
179 | "dev": true,
180 | "requires": {
181 | "browser-sync-client": "^2.26.13",
182 | "browser-sync-ui": "^2.26.13",
183 | "bs-recipes": "1.3.4",
184 | "bs-snippet-injector": "^2.0.1",
185 | "chokidar": "^3.4.1",
186 | "connect": "3.6.6",
187 | "connect-history-api-fallback": "^1",
188 | "dev-ip": "^1.0.1",
189 | "easy-extender": "^2.3.4",
190 | "eazy-logger": "3.1.0",
191 | "etag": "^1.8.1",
192 | "fresh": "^0.5.2",
193 | "fs-extra": "3.0.1",
194 | "http-proxy": "^1.18.1",
195 | "immutable": "^3",
196 | "localtunnel": "^2.0.0",
197 | "micromatch": "^4.0.2",
198 | "opn": "5.3.0",
199 | "portscanner": "2.1.1",
200 | "qs": "6.2.3",
201 | "raw-body": "^2.3.2",
202 | "resp-modifier": "6.0.2",
203 | "rx": "4.1.0",
204 | "send": "0.16.2",
205 | "serve-index": "1.9.1",
206 | "serve-static": "1.13.2",
207 | "server-destroy": "1.0.1",
208 | "socket.io": "2.1.1",
209 | "ua-parser-js": "^0.7.18",
210 | "yargs": "^15.4.1"
211 | },
212 | "dependencies": {
213 | "ansi-regex": {
214 | "version": "5.0.0",
215 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
216 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
217 | "dev": true
218 | },
219 | "ansi-styles": {
220 | "version": "4.3.0",
221 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
222 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
223 | "dev": true,
224 | "requires": {
225 | "color-convert": "^2.0.1"
226 | }
227 | },
228 | "cliui": {
229 | "version": "6.0.0",
230 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
231 | "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
232 | "dev": true,
233 | "requires": {
234 | "string-width": "^4.2.0",
235 | "strip-ansi": "^6.0.0",
236 | "wrap-ansi": "^6.2.0"
237 | }
238 | },
239 | "color-convert": {
240 | "version": "2.0.1",
241 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
242 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
243 | "dev": true,
244 | "requires": {
245 | "color-name": "~1.1.4"
246 | }
247 | },
248 | "color-name": {
249 | "version": "1.1.4",
250 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
251 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
252 | "dev": true
253 | },
254 | "emoji-regex": {
255 | "version": "8.0.0",
256 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
257 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
258 | "dev": true
259 | },
260 | "find-up": {
261 | "version": "4.1.0",
262 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
263 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
264 | "dev": true,
265 | "requires": {
266 | "locate-path": "^5.0.0",
267 | "path-exists": "^4.0.0"
268 | }
269 | },
270 | "get-caller-file": {
271 | "version": "2.0.5",
272 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
273 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
274 | "dev": true
275 | },
276 | "is-fullwidth-code-point": {
277 | "version": "3.0.0",
278 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
279 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
280 | "dev": true
281 | },
282 | "locate-path": {
283 | "version": "5.0.0",
284 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
285 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
286 | "dev": true,
287 | "requires": {
288 | "p-locate": "^4.1.0"
289 | }
290 | },
291 | "p-locate": {
292 | "version": "4.1.0",
293 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
294 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
295 | "dev": true,
296 | "requires": {
297 | "p-limit": "^2.2.0"
298 | }
299 | },
300 | "path-exists": {
301 | "version": "4.0.0",
302 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
303 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
304 | "dev": true
305 | },
306 | "require-main-filename": {
307 | "version": "2.0.0",
308 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
309 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
310 | "dev": true
311 | },
312 | "string-width": {
313 | "version": "4.2.0",
314 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
315 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
316 | "dev": true,
317 | "requires": {
318 | "emoji-regex": "^8.0.0",
319 | "is-fullwidth-code-point": "^3.0.0",
320 | "strip-ansi": "^6.0.0"
321 | }
322 | },
323 | "strip-ansi": {
324 | "version": "6.0.0",
325 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
326 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
327 | "dev": true,
328 | "requires": {
329 | "ansi-regex": "^5.0.0"
330 | }
331 | },
332 | "wrap-ansi": {
333 | "version": "6.2.0",
334 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
335 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
336 | "dev": true,
337 | "requires": {
338 | "ansi-styles": "^4.0.0",
339 | "string-width": "^4.1.0",
340 | "strip-ansi": "^6.0.0"
341 | }
342 | },
343 | "yargs": {
344 | "version": "15.4.1",
345 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
346 | "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
347 | "dev": true,
348 | "requires": {
349 | "cliui": "^6.0.0",
350 | "decamelize": "^1.2.0",
351 | "find-up": "^4.1.0",
352 | "get-caller-file": "^2.0.1",
353 | "require-directory": "^2.1.1",
354 | "require-main-filename": "^2.0.0",
355 | "set-blocking": "^2.0.0",
356 | "string-width": "^4.2.0",
357 | "which-module": "^2.0.0",
358 | "y18n": "^4.0.0",
359 | "yargs-parser": "^18.1.2"
360 | }
361 | },
362 | "yargs-parser": {
363 | "version": "18.1.3",
364 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
365 | "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
366 | "dev": true,
367 | "requires": {
368 | "camelcase": "^5.0.0",
369 | "decamelize": "^1.2.0"
370 | }
371 | }
372 | }
373 | },
374 | "browser-sync-client": {
375 | "version": "2.26.13",
376 | "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.26.13.tgz",
377 | "integrity": "sha512-p2VbZoYrpuDhkreq+/Sv1MkToHklh7T1OaIntDwpG6Iy2q/XkBcgwPcWjX+WwRNiZjN8MEehxIjEUh12LweLmQ==",
378 | "dev": true,
379 | "requires": {
380 | "etag": "1.8.1",
381 | "fresh": "0.5.2",
382 | "mitt": "^1.1.3",
383 | "rxjs": "^5.5.6"
384 | },
385 | "dependencies": {
386 | "rxjs": {
387 | "version": "5.5.12",
388 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz",
389 | "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==",
390 | "dev": true,
391 | "requires": {
392 | "symbol-observable": "1.0.1"
393 | }
394 | }
395 | }
396 | },
397 | "browser-sync-ui": {
398 | "version": "2.26.13",
399 | "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.26.13.tgz",
400 | "integrity": "sha512-6NJ/pCnhCnBMzaty1opWo7ipDmFAIk8U71JMQGKJxblCUaGfdsbF2shf6XNZSkXYia1yS0vwKu9LIOzpXqQZCA==",
401 | "dev": true,
402 | "requires": {
403 | "async-each-series": "0.1.1",
404 | "connect-history-api-fallback": "^1",
405 | "immutable": "^3",
406 | "server-destroy": "1.0.1",
407 | "socket.io-client": "^2.0.4",
408 | "stream-throttle": "^0.1.3"
409 | }
410 | },
411 | "bs-recipes": {
412 | "version": "1.3.4",
413 | "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz",
414 | "integrity": "sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU=",
415 | "dev": true
416 | },
417 | "bs-snippet-injector": {
418 | "version": "2.0.1",
419 | "resolved": "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz",
420 | "integrity": "sha1-YbU5PxH1JVntEgaTEANDtu2wTdU=",
421 | "dev": true
422 | },
423 | "bytes": {
424 | "version": "3.1.0",
425 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
426 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
427 | "dev": true
428 | },
429 | "callsite": {
430 | "version": "1.0.0",
431 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
432 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
433 | "dev": true
434 | },
435 | "camelcase": {
436 | "version": "5.3.1",
437 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
438 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
439 | "dev": true
440 | },
441 | "chalk": {
442 | "version": "2.4.2",
443 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
444 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
445 | "dev": true,
446 | "requires": {
447 | "ansi-styles": "^3.2.1",
448 | "escape-string-regexp": "^1.0.5",
449 | "supports-color": "^5.3.0"
450 | },
451 | "dependencies": {
452 | "supports-color": {
453 | "version": "5.5.0",
454 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
455 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
456 | "dev": true,
457 | "requires": {
458 | "has-flag": "^3.0.0"
459 | }
460 | }
461 | }
462 | },
463 | "chokidar": {
464 | "version": "3.4.3",
465 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
466 | "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
467 | "dev": true,
468 | "requires": {
469 | "anymatch": "~3.1.1",
470 | "braces": "~3.0.2",
471 | "fsevents": "~2.1.2",
472 | "glob-parent": "~5.1.0",
473 | "is-binary-path": "~2.1.0",
474 | "is-glob": "~4.0.1",
475 | "normalize-path": "~3.0.0",
476 | "readdirp": "~3.5.0"
477 | }
478 | },
479 | "cliui": {
480 | "version": "4.1.0",
481 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
482 | "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
483 | "dev": true,
484 | "requires": {
485 | "string-width": "^2.1.1",
486 | "strip-ansi": "^4.0.0",
487 | "wrap-ansi": "^2.0.0"
488 | }
489 | },
490 | "code-point-at": {
491 | "version": "1.1.0",
492 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
493 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
494 | "dev": true
495 | },
496 | "color-convert": {
497 | "version": "1.9.3",
498 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
499 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
500 | "dev": true,
501 | "requires": {
502 | "color-name": "1.1.3"
503 | }
504 | },
505 | "color-name": {
506 | "version": "1.1.3",
507 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
508 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
509 | "dev": true
510 | },
511 | "commander": {
512 | "version": "2.20.3",
513 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
514 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
515 | "dev": true
516 | },
517 | "component-bind": {
518 | "version": "1.0.0",
519 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
520 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
521 | "dev": true
522 | },
523 | "component-emitter": {
524 | "version": "1.3.0",
525 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
526 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
527 | "dev": true
528 | },
529 | "component-inherit": {
530 | "version": "0.0.3",
531 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
532 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
533 | "dev": true
534 | },
535 | "concat-map": {
536 | "version": "0.0.1",
537 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
538 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
539 | "dev": true
540 | },
541 | "concurrently": {
542 | "version": "4.0.1",
543 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.0.1.tgz",
544 | "integrity": "sha512-D8UI+mlI/bfvrA57SeKOht6sEpb01dKk+8Yee4fbnkk1Ue8r3S+JXoEdFZIpzQlXJGtnxo47Wvvg/kG4ba3U6Q==",
545 | "dev": true,
546 | "requires": {
547 | "chalk": "^2.4.1",
548 | "date-fns": "^1.23.0",
549 | "lodash": "^4.17.10",
550 | "read-pkg": "^4.0.1",
551 | "rxjs": "6.2.2",
552 | "spawn-command": "^0.0.2-1",
553 | "supports-color": "^4.5.0",
554 | "tree-kill": "^1.1.0",
555 | "yargs": "^12.0.1"
556 | }
557 | },
558 | "connect": {
559 | "version": "3.6.6",
560 | "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
561 | "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
562 | "dev": true,
563 | "requires": {
564 | "debug": "2.6.9",
565 | "finalhandler": "1.1.0",
566 | "parseurl": "~1.3.2",
567 | "utils-merge": "1.0.1"
568 | },
569 | "dependencies": {
570 | "debug": {
571 | "version": "2.6.9",
572 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
573 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
574 | "dev": true,
575 | "requires": {
576 | "ms": "2.0.0"
577 | }
578 | }
579 | }
580 | },
581 | "connect-history-api-fallback": {
582 | "version": "1.6.0",
583 | "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
584 | "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
585 | "dev": true
586 | },
587 | "connect-logger": {
588 | "version": "0.0.1",
589 | "resolved": "https://registry.npmjs.org/connect-logger/-/connect-logger-0.0.1.tgz",
590 | "integrity": "sha1-TZmZeKHSC7RgjnzUNNdBZSJVF0s=",
591 | "dev": true,
592 | "requires": {
593 | "moment": "*"
594 | }
595 | },
596 | "cookie": {
597 | "version": "0.3.1",
598 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
599 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
600 | "dev": true
601 | },
602 | "cross-spawn": {
603 | "version": "6.0.5",
604 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
605 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
606 | "dev": true,
607 | "requires": {
608 | "nice-try": "^1.0.4",
609 | "path-key": "^2.0.1",
610 | "semver": "^5.5.0",
611 | "shebang-command": "^1.2.0",
612 | "which": "^1.2.9"
613 | }
614 | },
615 | "d3": {
616 | "version": "3.5.17",
617 | "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
618 | "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=",
619 | "dev": true
620 | },
621 | "date-fns": {
622 | "version": "1.30.1",
623 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
624 | "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
625 | "dev": true
626 | },
627 | "debug": {
628 | "version": "3.1.0",
629 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
630 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
631 | "dev": true,
632 | "requires": {
633 | "ms": "2.0.0"
634 | }
635 | },
636 | "decamelize": {
637 | "version": "1.2.0",
638 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
639 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
640 | "dev": true
641 | },
642 | "depd": {
643 | "version": "1.1.2",
644 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
645 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
646 | "dev": true
647 | },
648 | "destroy": {
649 | "version": "1.0.4",
650 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
651 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
652 | "dev": true
653 | },
654 | "dev-ip": {
655 | "version": "1.0.1",
656 | "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz",
657 | "integrity": "sha1-p2o+0YVb56ASu4rBbLgPPADcKPA=",
658 | "dev": true
659 | },
660 | "dlv": {
661 | "version": "1.1.3",
662 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
663 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
664 | "dev": true
665 | },
666 | "easy-extender": {
667 | "version": "2.3.4",
668 | "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz",
669 | "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==",
670 | "dev": true,
671 | "requires": {
672 | "lodash": "^4.17.10"
673 | }
674 | },
675 | "eazy-logger": {
676 | "version": "3.1.0",
677 | "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-3.1.0.tgz",
678 | "integrity": "sha512-/snsn2JqBtUSSstEl4R0RKjkisGHAhvYj89i7r3ytNUKW12y178KDZwXLXIgwDqLW6E/VRMT9qfld7wvFae8bQ==",
679 | "dev": true,
680 | "requires": {
681 | "tfunk": "^4.0.0"
682 | }
683 | },
684 | "ee-first": {
685 | "version": "1.1.1",
686 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
687 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
688 | "dev": true
689 | },
690 | "emoji-regex": {
691 | "version": "7.0.3",
692 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
693 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
694 | "dev": true
695 | },
696 | "encodeurl": {
697 | "version": "1.0.2",
698 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
699 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
700 | "dev": true
701 | },
702 | "end-of-stream": {
703 | "version": "1.4.4",
704 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
705 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
706 | "dev": true,
707 | "requires": {
708 | "once": "^1.4.0"
709 | }
710 | },
711 | "engine.io": {
712 | "version": "3.2.1",
713 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
714 | "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
715 | "dev": true,
716 | "requires": {
717 | "accepts": "~1.3.4",
718 | "base64id": "1.0.0",
719 | "cookie": "0.3.1",
720 | "debug": "~3.1.0",
721 | "engine.io-parser": "~2.1.0",
722 | "ws": "~3.3.1"
723 | },
724 | "dependencies": {
725 | "base64-arraybuffer": {
726 | "version": "0.1.5",
727 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
728 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
729 | "dev": true
730 | },
731 | "engine.io-parser": {
732 | "version": "2.1.3",
733 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
734 | "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
735 | "dev": true,
736 | "requires": {
737 | "after": "0.8.2",
738 | "arraybuffer.slice": "~0.0.7",
739 | "base64-arraybuffer": "0.1.5",
740 | "blob": "0.0.5",
741 | "has-binary2": "~1.0.2"
742 | }
743 | },
744 | "ws": {
745 | "version": "3.3.3",
746 | "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
747 | "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
748 | "dev": true,
749 | "requires": {
750 | "async-limiter": "~1.0.0",
751 | "safe-buffer": "~5.1.0",
752 | "ultron": "~1.1.0"
753 | }
754 | }
755 | }
756 | },
757 | "engine.io-client": {
758 | "version": "3.4.4",
759 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
760 | "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
761 | "dev": true,
762 | "requires": {
763 | "component-emitter": "~1.3.0",
764 | "component-inherit": "0.0.3",
765 | "debug": "~3.1.0",
766 | "engine.io-parser": "~2.2.0",
767 | "has-cors": "1.1.0",
768 | "indexof": "0.0.1",
769 | "parseqs": "0.0.6",
770 | "parseuri": "0.0.6",
771 | "ws": "~6.1.0",
772 | "xmlhttprequest-ssl": "~1.5.4",
773 | "yeast": "0.1.2"
774 | }
775 | },
776 | "engine.io-parser": {
777 | "version": "2.2.1",
778 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
779 | "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
780 | "dev": true,
781 | "requires": {
782 | "after": "0.8.2",
783 | "arraybuffer.slice": "~0.0.7",
784 | "base64-arraybuffer": "0.1.4",
785 | "blob": "0.0.5",
786 | "has-binary2": "~1.0.2"
787 | }
788 | },
789 | "error-ex": {
790 | "version": "1.3.2",
791 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
792 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
793 | "dev": true,
794 | "requires": {
795 | "is-arrayish": "^0.2.1"
796 | }
797 | },
798 | "escape-html": {
799 | "version": "1.0.3",
800 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
801 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
802 | "dev": true
803 | },
804 | "escape-string-regexp": {
805 | "version": "1.0.5",
806 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
807 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
808 | "dev": true
809 | },
810 | "etag": {
811 | "version": "1.8.1",
812 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
813 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
814 | "dev": true
815 | },
816 | "eventemitter3": {
817 | "version": "4.0.7",
818 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
819 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
820 | "dev": true
821 | },
822 | "execa": {
823 | "version": "1.0.0",
824 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
825 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
826 | "dev": true,
827 | "requires": {
828 | "cross-spawn": "^6.0.0",
829 | "get-stream": "^4.0.0",
830 | "is-stream": "^1.1.0",
831 | "npm-run-path": "^2.0.0",
832 | "p-finally": "^1.0.0",
833 | "signal-exit": "^3.0.0",
834 | "strip-eof": "^1.0.0"
835 | }
836 | },
837 | "fill-range": {
838 | "version": "7.0.1",
839 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
840 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
841 | "dev": true,
842 | "requires": {
843 | "to-regex-range": "^5.0.1"
844 | }
845 | },
846 | "finalhandler": {
847 | "version": "1.1.0",
848 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
849 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
850 | "dev": true,
851 | "requires": {
852 | "debug": "2.6.9",
853 | "encodeurl": "~1.0.1",
854 | "escape-html": "~1.0.3",
855 | "on-finished": "~2.3.0",
856 | "parseurl": "~1.3.2",
857 | "statuses": "~1.3.1",
858 | "unpipe": "~1.0.0"
859 | },
860 | "dependencies": {
861 | "debug": {
862 | "version": "2.6.9",
863 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
864 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
865 | "dev": true,
866 | "requires": {
867 | "ms": "2.0.0"
868 | }
869 | }
870 | }
871 | },
872 | "find-up": {
873 | "version": "3.0.0",
874 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
875 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
876 | "dev": true,
877 | "requires": {
878 | "locate-path": "^3.0.0"
879 | }
880 | },
881 | "follow-redirects": {
882 | "version": "1.13.0",
883 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
884 | "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==",
885 | "dev": true
886 | },
887 | "fresh": {
888 | "version": "0.5.2",
889 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
890 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
891 | "dev": true
892 | },
893 | "fs-extra": {
894 | "version": "3.0.1",
895 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz",
896 | "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=",
897 | "dev": true,
898 | "requires": {
899 | "graceful-fs": "^4.1.2",
900 | "jsonfile": "^3.0.0",
901 | "universalify": "^0.1.0"
902 | }
903 | },
904 | "fsevents": {
905 | "version": "2.1.3",
906 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
907 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
908 | "dev": true,
909 | "optional": true
910 | },
911 | "function-bind": {
912 | "version": "1.1.1",
913 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
914 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
915 | "dev": true
916 | },
917 | "get-caller-file": {
918 | "version": "1.0.3",
919 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
920 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
921 | "dev": true
922 | },
923 | "get-stream": {
924 | "version": "4.1.0",
925 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
926 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
927 | "dev": true,
928 | "requires": {
929 | "pump": "^3.0.0"
930 | }
931 | },
932 | "glob-parent": {
933 | "version": "5.1.1",
934 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
935 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
936 | "dev": true,
937 | "requires": {
938 | "is-glob": "^4.0.1"
939 | }
940 | },
941 | "graceful-fs": {
942 | "version": "4.2.4",
943 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
944 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
945 | "dev": true
946 | },
947 | "has": {
948 | "version": "1.0.3",
949 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
950 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
951 | "dev": true,
952 | "requires": {
953 | "function-bind": "^1.1.1"
954 | }
955 | },
956 | "has-ansi": {
957 | "version": "2.0.0",
958 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
959 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
960 | "dev": true,
961 | "requires": {
962 | "ansi-regex": "^2.0.0"
963 | },
964 | "dependencies": {
965 | "ansi-regex": {
966 | "version": "2.1.1",
967 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
968 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
969 | "dev": true
970 | }
971 | }
972 | },
973 | "has-binary2": {
974 | "version": "1.0.3",
975 | "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
976 | "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
977 | "dev": true,
978 | "requires": {
979 | "isarray": "2.0.1"
980 | }
981 | },
982 | "has-cors": {
983 | "version": "1.1.0",
984 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
985 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
986 | "dev": true
987 | },
988 | "has-flag": {
989 | "version": "3.0.0",
990 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
991 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
992 | "dev": true
993 | },
994 | "hosted-git-info": {
995 | "version": "2.8.9",
996 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
997 | "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
998 | "dev": true
999 | },
1000 | "http-errors": {
1001 | "version": "1.7.3",
1002 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
1003 | "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
1004 | "dev": true,
1005 | "requires": {
1006 | "depd": "~1.1.2",
1007 | "inherits": "2.0.4",
1008 | "setprototypeof": "1.1.1",
1009 | "statuses": ">= 1.5.0 < 2",
1010 | "toidentifier": "1.0.0"
1011 | },
1012 | "dependencies": {
1013 | "statuses": {
1014 | "version": "1.5.0",
1015 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1016 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
1017 | "dev": true
1018 | }
1019 | }
1020 | },
1021 | "http-proxy": {
1022 | "version": "1.18.1",
1023 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
1024 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
1025 | "dev": true,
1026 | "requires": {
1027 | "eventemitter3": "^4.0.0",
1028 | "follow-redirects": "^1.0.0",
1029 | "requires-port": "^1.0.0"
1030 | }
1031 | },
1032 | "iconv-lite": {
1033 | "version": "0.4.24",
1034 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1035 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1036 | "dev": true,
1037 | "requires": {
1038 | "safer-buffer": ">= 2.1.2 < 3"
1039 | }
1040 | },
1041 | "immutable": {
1042 | "version": "3.8.2",
1043 | "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
1044 | "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
1045 | "dev": true
1046 | },
1047 | "indexof": {
1048 | "version": "0.0.1",
1049 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
1050 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
1051 | "dev": true
1052 | },
1053 | "inherits": {
1054 | "version": "2.0.4",
1055 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1056 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1057 | "dev": true
1058 | },
1059 | "invert-kv": {
1060 | "version": "2.0.0",
1061 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
1062 | "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
1063 | "dev": true
1064 | },
1065 | "is-arrayish": {
1066 | "version": "0.2.1",
1067 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
1068 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
1069 | "dev": true
1070 | },
1071 | "is-binary-path": {
1072 | "version": "2.1.0",
1073 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1074 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1075 | "dev": true,
1076 | "requires": {
1077 | "binary-extensions": "^2.0.0"
1078 | }
1079 | },
1080 | "is-buffer": {
1081 | "version": "2.0.4",
1082 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
1083 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
1084 | "dev": true
1085 | },
1086 | "is-core-module": {
1087 | "version": "2.0.0",
1088 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz",
1089 | "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==",
1090 | "dev": true,
1091 | "requires": {
1092 | "has": "^1.0.3"
1093 | }
1094 | },
1095 | "is-extglob": {
1096 | "version": "2.1.1",
1097 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1098 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
1099 | "dev": true
1100 | },
1101 | "is-fullwidth-code-point": {
1102 | "version": "2.0.0",
1103 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
1104 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
1105 | "dev": true
1106 | },
1107 | "is-glob": {
1108 | "version": "4.0.1",
1109 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
1110 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
1111 | "dev": true,
1112 | "requires": {
1113 | "is-extglob": "^2.1.1"
1114 | }
1115 | },
1116 | "is-number": {
1117 | "version": "7.0.0",
1118 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1119 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1120 | "dev": true
1121 | },
1122 | "is-number-like": {
1123 | "version": "1.0.8",
1124 | "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz",
1125 | "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==",
1126 | "dev": true,
1127 | "requires": {
1128 | "lodash.isfinite": "^3.3.2"
1129 | }
1130 | },
1131 | "is-stream": {
1132 | "version": "1.1.0",
1133 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
1134 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
1135 | "dev": true
1136 | },
1137 | "is-wsl": {
1138 | "version": "1.1.0",
1139 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
1140 | "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
1141 | "dev": true
1142 | },
1143 | "isarray": {
1144 | "version": "2.0.1",
1145 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
1146 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
1147 | "dev": true
1148 | },
1149 | "isexe": {
1150 | "version": "2.0.0",
1151 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1152 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
1153 | "dev": true
1154 | },
1155 | "json-parse-better-errors": {
1156 | "version": "1.0.2",
1157 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
1158 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
1159 | "dev": true
1160 | },
1161 | "jsonfile": {
1162 | "version": "3.0.1",
1163 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
1164 | "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=",
1165 | "dev": true,
1166 | "requires": {
1167 | "graceful-fs": "^4.1.6"
1168 | }
1169 | },
1170 | "lcid": {
1171 | "version": "2.0.0",
1172 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
1173 | "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
1174 | "dev": true,
1175 | "requires": {
1176 | "invert-kv": "^2.0.0"
1177 | }
1178 | },
1179 | "limiter": {
1180 | "version": "1.1.5",
1181 | "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
1182 | "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==",
1183 | "dev": true
1184 | },
1185 | "lite-server": {
1186 | "version": "1.3.4",
1187 | "resolved": "https://registry.npmjs.org/lite-server/-/lite-server-1.3.4.tgz",
1188 | "integrity": "sha1-0B8xvqGJb1K9rkdw3nVv8L9xW50=",
1189 | "dev": true,
1190 | "requires": {
1191 | "browser-sync": "^2.11.1",
1192 | "connect-history-api-fallback": "^1.1.0",
1193 | "connect-logger": "0.0.1",
1194 | "yargs": "^3.32.0"
1195 | },
1196 | "dependencies": {
1197 | "ansi-regex": {
1198 | "version": "2.1.1",
1199 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
1200 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
1201 | "dev": true
1202 | },
1203 | "camelcase": {
1204 | "version": "2.1.1",
1205 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
1206 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
1207 | "dev": true
1208 | },
1209 | "cliui": {
1210 | "version": "3.2.0",
1211 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
1212 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
1213 | "dev": true,
1214 | "requires": {
1215 | "string-width": "^1.0.1",
1216 | "strip-ansi": "^3.0.1",
1217 | "wrap-ansi": "^2.0.0"
1218 | }
1219 | },
1220 | "invert-kv": {
1221 | "version": "1.0.0",
1222 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
1223 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
1224 | "dev": true
1225 | },
1226 | "is-fullwidth-code-point": {
1227 | "version": "1.0.0",
1228 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
1229 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
1230 | "dev": true,
1231 | "requires": {
1232 | "number-is-nan": "^1.0.0"
1233 | }
1234 | },
1235 | "lcid": {
1236 | "version": "1.0.0",
1237 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
1238 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
1239 | "dev": true,
1240 | "requires": {
1241 | "invert-kv": "^1.0.0"
1242 | }
1243 | },
1244 | "os-locale": {
1245 | "version": "1.4.0",
1246 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
1247 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
1248 | "dev": true,
1249 | "requires": {
1250 | "lcid": "^1.0.0"
1251 | }
1252 | },
1253 | "string-width": {
1254 | "version": "1.0.2",
1255 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
1256 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
1257 | "dev": true,
1258 | "requires": {
1259 | "code-point-at": "^1.0.0",
1260 | "is-fullwidth-code-point": "^1.0.0",
1261 | "strip-ansi": "^3.0.0"
1262 | }
1263 | },
1264 | "strip-ansi": {
1265 | "version": "3.0.1",
1266 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1267 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1268 | "dev": true,
1269 | "requires": {
1270 | "ansi-regex": "^2.0.0"
1271 | }
1272 | },
1273 | "y18n": {
1274 | "version": "3.2.2",
1275 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
1276 | "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
1277 | "dev": true
1278 | },
1279 | "yargs": {
1280 | "version": "3.32.0",
1281 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
1282 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
1283 | "dev": true,
1284 | "requires": {
1285 | "camelcase": "^2.0.1",
1286 | "cliui": "^3.0.3",
1287 | "decamelize": "^1.1.1",
1288 | "os-locale": "^1.4.0",
1289 | "string-width": "^1.0.1",
1290 | "window-size": "^0.1.4",
1291 | "y18n": "^3.2.0"
1292 | }
1293 | }
1294 | }
1295 | },
1296 | "localtunnel": {
1297 | "version": "2.0.0",
1298 | "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.0.tgz",
1299 | "integrity": "sha512-g6E0aLgYYDvQDxIjIXkgJo2+pHj3sGg4Wz/XP3h2KtZnRsWPbOQY+hw1H8Z91jep998fkcVE9l+kghO+97vllg==",
1300 | "dev": true,
1301 | "requires": {
1302 | "axios": "0.19.0",
1303 | "debug": "4.1.1",
1304 | "openurl": "1.1.1",
1305 | "yargs": "13.3.0"
1306 | },
1307 | "dependencies": {
1308 | "ansi-regex": {
1309 | "version": "4.1.0",
1310 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
1311 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
1312 | "dev": true
1313 | },
1314 | "cliui": {
1315 | "version": "5.0.0",
1316 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
1317 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
1318 | "dev": true,
1319 | "requires": {
1320 | "string-width": "^3.1.0",
1321 | "strip-ansi": "^5.2.0",
1322 | "wrap-ansi": "^5.1.0"
1323 | }
1324 | },
1325 | "debug": {
1326 | "version": "4.1.1",
1327 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
1328 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
1329 | "dev": true,
1330 | "requires": {
1331 | "ms": "^2.1.1"
1332 | }
1333 | },
1334 | "get-caller-file": {
1335 | "version": "2.0.5",
1336 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
1337 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
1338 | "dev": true
1339 | },
1340 | "ms": {
1341 | "version": "2.1.2",
1342 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1343 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
1344 | "dev": true
1345 | },
1346 | "require-main-filename": {
1347 | "version": "2.0.0",
1348 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
1349 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
1350 | "dev": true
1351 | },
1352 | "string-width": {
1353 | "version": "3.1.0",
1354 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
1355 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
1356 | "dev": true,
1357 | "requires": {
1358 | "emoji-regex": "^7.0.1",
1359 | "is-fullwidth-code-point": "^2.0.0",
1360 | "strip-ansi": "^5.1.0"
1361 | }
1362 | },
1363 | "strip-ansi": {
1364 | "version": "5.2.0",
1365 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
1366 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
1367 | "dev": true,
1368 | "requires": {
1369 | "ansi-regex": "^4.1.0"
1370 | }
1371 | },
1372 | "wrap-ansi": {
1373 | "version": "5.1.0",
1374 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
1375 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
1376 | "dev": true,
1377 | "requires": {
1378 | "ansi-styles": "^3.2.0",
1379 | "string-width": "^3.0.0",
1380 | "strip-ansi": "^5.0.0"
1381 | }
1382 | },
1383 | "yargs": {
1384 | "version": "13.3.0",
1385 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
1386 | "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
1387 | "dev": true,
1388 | "requires": {
1389 | "cliui": "^5.0.0",
1390 | "find-up": "^3.0.0",
1391 | "get-caller-file": "^2.0.1",
1392 | "require-directory": "^2.1.1",
1393 | "require-main-filename": "^2.0.0",
1394 | "set-blocking": "^2.0.0",
1395 | "string-width": "^3.0.0",
1396 | "which-module": "^2.0.0",
1397 | "y18n": "^4.0.0",
1398 | "yargs-parser": "^13.1.1"
1399 | }
1400 | },
1401 | "yargs-parser": {
1402 | "version": "13.1.2",
1403 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
1404 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
1405 | "dev": true,
1406 | "requires": {
1407 | "camelcase": "^5.0.0",
1408 | "decamelize": "^1.2.0"
1409 | }
1410 | }
1411 | }
1412 | },
1413 | "locate-path": {
1414 | "version": "3.0.0",
1415 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
1416 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
1417 | "dev": true,
1418 | "requires": {
1419 | "p-locate": "^3.0.0",
1420 | "path-exists": "^3.0.0"
1421 | }
1422 | },
1423 | "lodash": {
1424 | "version": "4.17.21",
1425 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
1426 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
1427 | "dev": true
1428 | },
1429 | "lodash.isfinite": {
1430 | "version": "3.3.2",
1431 | "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz",
1432 | "integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=",
1433 | "dev": true
1434 | },
1435 | "map-age-cleaner": {
1436 | "version": "0.1.3",
1437 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
1438 | "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
1439 | "dev": true,
1440 | "requires": {
1441 | "p-defer": "^1.0.0"
1442 | }
1443 | },
1444 | "mem": {
1445 | "version": "4.3.0",
1446 | "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
1447 | "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
1448 | "dev": true,
1449 | "requires": {
1450 | "map-age-cleaner": "^0.1.1",
1451 | "mimic-fn": "^2.0.0",
1452 | "p-is-promise": "^2.0.0"
1453 | }
1454 | },
1455 | "micromatch": {
1456 | "version": "4.0.2",
1457 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
1458 | "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
1459 | "dev": true,
1460 | "requires": {
1461 | "braces": "^3.0.1",
1462 | "picomatch": "^2.0.5"
1463 | }
1464 | },
1465 | "mime": {
1466 | "version": "1.4.1",
1467 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
1468 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
1469 | "dev": true
1470 | },
1471 | "mime-db": {
1472 | "version": "1.44.0",
1473 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
1474 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
1475 | "dev": true
1476 | },
1477 | "mime-types": {
1478 | "version": "2.1.27",
1479 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
1480 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
1481 | "dev": true,
1482 | "requires": {
1483 | "mime-db": "1.44.0"
1484 | }
1485 | },
1486 | "mimic-fn": {
1487 | "version": "2.1.0",
1488 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
1489 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
1490 | "dev": true
1491 | },
1492 | "minimatch": {
1493 | "version": "3.0.4",
1494 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1495 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1496 | "dev": true,
1497 | "requires": {
1498 | "brace-expansion": "^1.1.7"
1499 | }
1500 | },
1501 | "mitt": {
1502 | "version": "1.2.0",
1503 | "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz",
1504 | "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==",
1505 | "dev": true
1506 | },
1507 | "moment": {
1508 | "version": "2.29.1",
1509 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
1510 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
1511 | "dev": true
1512 | },
1513 | "ms": {
1514 | "version": "2.0.0",
1515 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1516 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
1517 | "dev": true
1518 | },
1519 | "negotiator": {
1520 | "version": "0.6.2",
1521 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
1522 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
1523 | "dev": true
1524 | },
1525 | "nice-try": {
1526 | "version": "1.0.5",
1527 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
1528 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
1529 | "dev": true
1530 | },
1531 | "normalize-package-data": {
1532 | "version": "2.5.0",
1533 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
1534 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
1535 | "dev": true,
1536 | "requires": {
1537 | "hosted-git-info": "^2.1.4",
1538 | "resolve": "^1.10.0",
1539 | "semver": "2 || 3 || 4 || 5",
1540 | "validate-npm-package-license": "^3.0.1"
1541 | }
1542 | },
1543 | "normalize-path": {
1544 | "version": "3.0.0",
1545 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1546 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1547 | "dev": true
1548 | },
1549 | "npm-run-path": {
1550 | "version": "2.0.2",
1551 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
1552 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
1553 | "dev": true,
1554 | "requires": {
1555 | "path-key": "^2.0.0"
1556 | }
1557 | },
1558 | "number-is-nan": {
1559 | "version": "1.0.1",
1560 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
1561 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
1562 | "dev": true
1563 | },
1564 | "object-component": {
1565 | "version": "0.0.3",
1566 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
1567 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
1568 | "dev": true
1569 | },
1570 | "on-finished": {
1571 | "version": "2.3.0",
1572 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1573 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
1574 | "dev": true,
1575 | "requires": {
1576 | "ee-first": "1.1.1"
1577 | }
1578 | },
1579 | "once": {
1580 | "version": "1.4.0",
1581 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1582 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1583 | "dev": true,
1584 | "requires": {
1585 | "wrappy": "1"
1586 | }
1587 | },
1588 | "openurl": {
1589 | "version": "1.1.1",
1590 | "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz",
1591 | "integrity": "sha1-OHW0sO96UsFW8NtB1GCduw+Us4c=",
1592 | "dev": true
1593 | },
1594 | "opn": {
1595 | "version": "5.3.0",
1596 | "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
1597 | "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
1598 | "dev": true,
1599 | "requires": {
1600 | "is-wsl": "^1.1.0"
1601 | }
1602 | },
1603 | "os-locale": {
1604 | "version": "3.1.0",
1605 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
1606 | "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
1607 | "dev": true,
1608 | "requires": {
1609 | "execa": "^1.0.0",
1610 | "lcid": "^2.0.0",
1611 | "mem": "^4.0.0"
1612 | }
1613 | },
1614 | "p-defer": {
1615 | "version": "1.0.0",
1616 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
1617 | "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
1618 | "dev": true
1619 | },
1620 | "p-finally": {
1621 | "version": "1.0.0",
1622 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
1623 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
1624 | "dev": true
1625 | },
1626 | "p-is-promise": {
1627 | "version": "2.1.0",
1628 | "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
1629 | "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
1630 | "dev": true
1631 | },
1632 | "p-limit": {
1633 | "version": "2.3.0",
1634 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
1635 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
1636 | "dev": true,
1637 | "requires": {
1638 | "p-try": "^2.0.0"
1639 | }
1640 | },
1641 | "p-locate": {
1642 | "version": "3.0.0",
1643 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
1644 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
1645 | "dev": true,
1646 | "requires": {
1647 | "p-limit": "^2.0.0"
1648 | }
1649 | },
1650 | "p-try": {
1651 | "version": "2.2.0",
1652 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
1653 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
1654 | "dev": true
1655 | },
1656 | "parse-json": {
1657 | "version": "4.0.0",
1658 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
1659 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
1660 | "dev": true,
1661 | "requires": {
1662 | "error-ex": "^1.3.1",
1663 | "json-parse-better-errors": "^1.0.1"
1664 | }
1665 | },
1666 | "parseqs": {
1667 | "version": "0.0.6",
1668 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
1669 | "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==",
1670 | "dev": true
1671 | },
1672 | "parseuri": {
1673 | "version": "0.0.6",
1674 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
1675 | "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==",
1676 | "dev": true
1677 | },
1678 | "parseurl": {
1679 | "version": "1.3.3",
1680 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1681 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1682 | "dev": true
1683 | },
1684 | "path-exists": {
1685 | "version": "3.0.0",
1686 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
1687 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
1688 | "dev": true
1689 | },
1690 | "path-key": {
1691 | "version": "2.0.1",
1692 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
1693 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
1694 | "dev": true
1695 | },
1696 | "path-parse": {
1697 | "version": "1.0.6",
1698 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
1699 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
1700 | "dev": true
1701 | },
1702 | "picomatch": {
1703 | "version": "2.2.2",
1704 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
1705 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
1706 | "dev": true
1707 | },
1708 | "pify": {
1709 | "version": "3.0.0",
1710 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
1711 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
1712 | "dev": true
1713 | },
1714 | "portscanner": {
1715 | "version": "2.1.1",
1716 | "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz",
1717 | "integrity": "sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y=",
1718 | "dev": true,
1719 | "requires": {
1720 | "async": "1.5.2",
1721 | "is-number-like": "^1.0.3"
1722 | }
1723 | },
1724 | "pump": {
1725 | "version": "3.0.0",
1726 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
1727 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
1728 | "dev": true,
1729 | "requires": {
1730 | "end-of-stream": "^1.1.0",
1731 | "once": "^1.3.1"
1732 | }
1733 | },
1734 | "qs": {
1735 | "version": "6.2.3",
1736 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz",
1737 | "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=",
1738 | "dev": true
1739 | },
1740 | "range-parser": {
1741 | "version": "1.2.1",
1742 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1743 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1744 | "dev": true
1745 | },
1746 | "raw-body": {
1747 | "version": "2.4.1",
1748 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
1749 | "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==",
1750 | "dev": true,
1751 | "requires": {
1752 | "bytes": "3.1.0",
1753 | "http-errors": "1.7.3",
1754 | "iconv-lite": "0.4.24",
1755 | "unpipe": "1.0.0"
1756 | }
1757 | },
1758 | "read-pkg": {
1759 | "version": "4.0.1",
1760 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
1761 | "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
1762 | "dev": true,
1763 | "requires": {
1764 | "normalize-package-data": "^2.3.2",
1765 | "parse-json": "^4.0.0",
1766 | "pify": "^3.0.0"
1767 | }
1768 | },
1769 | "readdirp": {
1770 | "version": "3.5.0",
1771 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
1772 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
1773 | "dev": true,
1774 | "requires": {
1775 | "picomatch": "^2.2.1"
1776 | }
1777 | },
1778 | "require-directory": {
1779 | "version": "2.1.1",
1780 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
1781 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
1782 | "dev": true
1783 | },
1784 | "require-main-filename": {
1785 | "version": "1.0.1",
1786 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
1787 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
1788 | "dev": true
1789 | },
1790 | "requires-port": {
1791 | "version": "1.0.0",
1792 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
1793 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
1794 | "dev": true
1795 | },
1796 | "resolve": {
1797 | "version": "1.18.1",
1798 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
1799 | "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==",
1800 | "dev": true,
1801 | "requires": {
1802 | "is-core-module": "^2.0.0",
1803 | "path-parse": "^1.0.6"
1804 | }
1805 | },
1806 | "resp-modifier": {
1807 | "version": "6.0.2",
1808 | "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz",
1809 | "integrity": "sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08=",
1810 | "dev": true,
1811 | "requires": {
1812 | "debug": "^2.2.0",
1813 | "minimatch": "^3.0.2"
1814 | },
1815 | "dependencies": {
1816 | "debug": {
1817 | "version": "2.6.9",
1818 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1819 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1820 | "dev": true,
1821 | "requires": {
1822 | "ms": "2.0.0"
1823 | }
1824 | }
1825 | }
1826 | },
1827 | "rx": {
1828 | "version": "4.1.0",
1829 | "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz",
1830 | "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=",
1831 | "dev": true
1832 | },
1833 | "rxjs": {
1834 | "version": "6.2.2",
1835 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz",
1836 | "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==",
1837 | "dev": true,
1838 | "requires": {
1839 | "tslib": "^1.9.0"
1840 | }
1841 | },
1842 | "safe-buffer": {
1843 | "version": "5.1.2",
1844 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1845 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
1846 | "dev": true
1847 | },
1848 | "safer-buffer": {
1849 | "version": "2.1.2",
1850 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1851 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1852 | "dev": true
1853 | },
1854 | "semver": {
1855 | "version": "5.7.1",
1856 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1857 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1858 | "dev": true
1859 | },
1860 | "send": {
1861 | "version": "0.16.2",
1862 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
1863 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
1864 | "dev": true,
1865 | "requires": {
1866 | "debug": "2.6.9",
1867 | "depd": "~1.1.2",
1868 | "destroy": "~1.0.4",
1869 | "encodeurl": "~1.0.2",
1870 | "escape-html": "~1.0.3",
1871 | "etag": "~1.8.1",
1872 | "fresh": "0.5.2",
1873 | "http-errors": "~1.6.2",
1874 | "mime": "1.4.1",
1875 | "ms": "2.0.0",
1876 | "on-finished": "~2.3.0",
1877 | "range-parser": "~1.2.0",
1878 | "statuses": "~1.4.0"
1879 | },
1880 | "dependencies": {
1881 | "debug": {
1882 | "version": "2.6.9",
1883 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1884 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1885 | "dev": true,
1886 | "requires": {
1887 | "ms": "2.0.0"
1888 | }
1889 | },
1890 | "http-errors": {
1891 | "version": "1.6.3",
1892 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
1893 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
1894 | "dev": true,
1895 | "requires": {
1896 | "depd": "~1.1.2",
1897 | "inherits": "2.0.3",
1898 | "setprototypeof": "1.1.0",
1899 | "statuses": ">= 1.4.0 < 2"
1900 | }
1901 | },
1902 | "inherits": {
1903 | "version": "2.0.3",
1904 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1905 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
1906 | "dev": true
1907 | },
1908 | "setprototypeof": {
1909 | "version": "1.1.0",
1910 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
1911 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
1912 | "dev": true
1913 | },
1914 | "statuses": {
1915 | "version": "1.4.0",
1916 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
1917 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
1918 | "dev": true
1919 | }
1920 | }
1921 | },
1922 | "serve-index": {
1923 | "version": "1.9.1",
1924 | "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
1925 | "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
1926 | "dev": true,
1927 | "requires": {
1928 | "accepts": "~1.3.4",
1929 | "batch": "0.6.1",
1930 | "debug": "2.6.9",
1931 | "escape-html": "~1.0.3",
1932 | "http-errors": "~1.6.2",
1933 | "mime-types": "~2.1.17",
1934 | "parseurl": "~1.3.2"
1935 | },
1936 | "dependencies": {
1937 | "debug": {
1938 | "version": "2.6.9",
1939 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1940 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1941 | "dev": true,
1942 | "requires": {
1943 | "ms": "2.0.0"
1944 | }
1945 | },
1946 | "http-errors": {
1947 | "version": "1.6.3",
1948 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
1949 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
1950 | "dev": true,
1951 | "requires": {
1952 | "depd": "~1.1.2",
1953 | "inherits": "2.0.3",
1954 | "setprototypeof": "1.1.0",
1955 | "statuses": ">= 1.4.0 < 2"
1956 | }
1957 | },
1958 | "inherits": {
1959 | "version": "2.0.3",
1960 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1961 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
1962 | "dev": true
1963 | },
1964 | "setprototypeof": {
1965 | "version": "1.1.0",
1966 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
1967 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
1968 | "dev": true
1969 | },
1970 | "statuses": {
1971 | "version": "1.5.0",
1972 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1973 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
1974 | "dev": true
1975 | }
1976 | }
1977 | },
1978 | "serve-static": {
1979 | "version": "1.13.2",
1980 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
1981 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
1982 | "dev": true,
1983 | "requires": {
1984 | "encodeurl": "~1.0.2",
1985 | "escape-html": "~1.0.3",
1986 | "parseurl": "~1.3.2",
1987 | "send": "0.16.2"
1988 | }
1989 | },
1990 | "server-destroy": {
1991 | "version": "1.0.1",
1992 | "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
1993 | "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=",
1994 | "dev": true
1995 | },
1996 | "set-blocking": {
1997 | "version": "2.0.0",
1998 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
1999 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
2000 | "dev": true
2001 | },
2002 | "setprototypeof": {
2003 | "version": "1.1.1",
2004 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
2005 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
2006 | "dev": true
2007 | },
2008 | "shebang-command": {
2009 | "version": "1.2.0",
2010 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
2011 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
2012 | "dev": true,
2013 | "requires": {
2014 | "shebang-regex": "^1.0.0"
2015 | }
2016 | },
2017 | "shebang-regex": {
2018 | "version": "1.0.0",
2019 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
2020 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
2021 | "dev": true
2022 | },
2023 | "signal-exit": {
2024 | "version": "3.0.3",
2025 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
2026 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
2027 | "dev": true
2028 | },
2029 | "socket.io": {
2030 | "version": "2.1.1",
2031 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
2032 | "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
2033 | "dev": true,
2034 | "requires": {
2035 | "debug": "~3.1.0",
2036 | "engine.io": "~3.2.0",
2037 | "has-binary2": "~1.0.2",
2038 | "socket.io-adapter": "~1.1.0",
2039 | "socket.io-client": "2.1.1",
2040 | "socket.io-parser": "~3.2.0"
2041 | },
2042 | "dependencies": {
2043 | "base64-arraybuffer": {
2044 | "version": "0.1.5",
2045 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
2046 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
2047 | "dev": true
2048 | },
2049 | "component-emitter": {
2050 | "version": "1.2.1",
2051 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
2052 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
2053 | "dev": true
2054 | },
2055 | "engine.io-client": {
2056 | "version": "3.2.1",
2057 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
2058 | "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
2059 | "dev": true,
2060 | "requires": {
2061 | "component-emitter": "1.2.1",
2062 | "component-inherit": "0.0.3",
2063 | "debug": "~3.1.0",
2064 | "engine.io-parser": "~2.1.1",
2065 | "has-cors": "1.1.0",
2066 | "indexof": "0.0.1",
2067 | "parseqs": "0.0.5",
2068 | "parseuri": "0.0.5",
2069 | "ws": "~3.3.1",
2070 | "xmlhttprequest-ssl": "~1.5.4",
2071 | "yeast": "0.1.2"
2072 | }
2073 | },
2074 | "engine.io-parser": {
2075 | "version": "2.1.3",
2076 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
2077 | "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
2078 | "dev": true,
2079 | "requires": {
2080 | "after": "0.8.2",
2081 | "arraybuffer.slice": "~0.0.7",
2082 | "base64-arraybuffer": "0.1.5",
2083 | "blob": "0.0.5",
2084 | "has-binary2": "~1.0.2"
2085 | }
2086 | },
2087 | "parseqs": {
2088 | "version": "0.0.5",
2089 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
2090 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
2091 | "dev": true,
2092 | "requires": {
2093 | "better-assert": "~1.0.0"
2094 | }
2095 | },
2096 | "parseuri": {
2097 | "version": "0.0.5",
2098 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
2099 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
2100 | "dev": true,
2101 | "requires": {
2102 | "better-assert": "~1.0.0"
2103 | }
2104 | },
2105 | "socket.io-client": {
2106 | "version": "2.1.1",
2107 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
2108 | "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
2109 | "dev": true,
2110 | "requires": {
2111 | "backo2": "1.0.2",
2112 | "base64-arraybuffer": "0.1.5",
2113 | "component-bind": "1.0.0",
2114 | "component-emitter": "1.2.1",
2115 | "debug": "~3.1.0",
2116 | "engine.io-client": "~3.2.0",
2117 | "has-binary2": "~1.0.2",
2118 | "has-cors": "1.1.0",
2119 | "indexof": "0.0.1",
2120 | "object-component": "0.0.3",
2121 | "parseqs": "0.0.5",
2122 | "parseuri": "0.0.5",
2123 | "socket.io-parser": "~3.2.0",
2124 | "to-array": "0.1.4"
2125 | }
2126 | },
2127 | "socket.io-parser": {
2128 | "version": "3.2.0",
2129 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
2130 | "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
2131 | "dev": true,
2132 | "requires": {
2133 | "component-emitter": "1.2.1",
2134 | "debug": "~3.1.0",
2135 | "isarray": "2.0.1"
2136 | }
2137 | },
2138 | "ws": {
2139 | "version": "3.3.3",
2140 | "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
2141 | "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
2142 | "dev": true,
2143 | "requires": {
2144 | "async-limiter": "~1.0.0",
2145 | "safe-buffer": "~5.1.0",
2146 | "ultron": "~1.1.0"
2147 | }
2148 | }
2149 | }
2150 | },
2151 | "socket.io-adapter": {
2152 | "version": "1.1.2",
2153 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
2154 | "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==",
2155 | "dev": true
2156 | },
2157 | "socket.io-client": {
2158 | "version": "2.3.1",
2159 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.1.tgz",
2160 | "integrity": "sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ==",
2161 | "dev": true,
2162 | "requires": {
2163 | "backo2": "1.0.2",
2164 | "component-bind": "1.0.0",
2165 | "component-emitter": "~1.3.0",
2166 | "debug": "~3.1.0",
2167 | "engine.io-client": "~3.4.0",
2168 | "has-binary2": "~1.0.2",
2169 | "indexof": "0.0.1",
2170 | "parseqs": "0.0.6",
2171 | "parseuri": "0.0.6",
2172 | "socket.io-parser": "~3.3.0",
2173 | "to-array": "0.1.4"
2174 | }
2175 | },
2176 | "socket.io-parser": {
2177 | "version": "3.3.1",
2178 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
2179 | "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
2180 | "dev": true,
2181 | "requires": {
2182 | "component-emitter": "~1.3.0",
2183 | "debug": "~3.1.0",
2184 | "isarray": "2.0.1"
2185 | }
2186 | },
2187 | "spawn-command": {
2188 | "version": "0.0.2-1",
2189 | "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
2190 | "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=",
2191 | "dev": true
2192 | },
2193 | "spdx-correct": {
2194 | "version": "3.1.1",
2195 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
2196 | "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
2197 | "dev": true,
2198 | "requires": {
2199 | "spdx-expression-parse": "^3.0.0",
2200 | "spdx-license-ids": "^3.0.0"
2201 | }
2202 | },
2203 | "spdx-exceptions": {
2204 | "version": "2.3.0",
2205 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
2206 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
2207 | "dev": true
2208 | },
2209 | "spdx-expression-parse": {
2210 | "version": "3.0.1",
2211 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
2212 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
2213 | "dev": true,
2214 | "requires": {
2215 | "spdx-exceptions": "^2.1.0",
2216 | "spdx-license-ids": "^3.0.0"
2217 | }
2218 | },
2219 | "spdx-license-ids": {
2220 | "version": "3.0.6",
2221 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
2222 | "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
2223 | "dev": true
2224 | },
2225 | "statuses": {
2226 | "version": "1.3.1",
2227 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
2228 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
2229 | "dev": true
2230 | },
2231 | "stream-throttle": {
2232 | "version": "0.1.3",
2233 | "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz",
2234 | "integrity": "sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM=",
2235 | "dev": true,
2236 | "requires": {
2237 | "commander": "^2.2.0",
2238 | "limiter": "^1.0.5"
2239 | }
2240 | },
2241 | "string-width": {
2242 | "version": "2.1.1",
2243 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
2244 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
2245 | "dev": true,
2246 | "requires": {
2247 | "is-fullwidth-code-point": "^2.0.0",
2248 | "strip-ansi": "^4.0.0"
2249 | }
2250 | },
2251 | "strip-ansi": {
2252 | "version": "4.0.0",
2253 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
2254 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
2255 | "dev": true,
2256 | "requires": {
2257 | "ansi-regex": "^3.0.0"
2258 | }
2259 | },
2260 | "strip-eof": {
2261 | "version": "1.0.0",
2262 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
2263 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
2264 | "dev": true
2265 | },
2266 | "supports-color": {
2267 | "version": "4.5.0",
2268 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
2269 | "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
2270 | "dev": true,
2271 | "requires": {
2272 | "has-flag": "^2.0.0"
2273 | },
2274 | "dependencies": {
2275 | "has-flag": {
2276 | "version": "2.0.0",
2277 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
2278 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
2279 | "dev": true
2280 | }
2281 | }
2282 | },
2283 | "symbol-observable": {
2284 | "version": "1.0.1",
2285 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
2286 | "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
2287 | "dev": true
2288 | },
2289 | "tfunk": {
2290 | "version": "4.0.0",
2291 | "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-4.0.0.tgz",
2292 | "integrity": "sha512-eJQ0dGfDIzWNiFNYFVjJ+Ezl/GmwHaFTBTjrtqNPW0S7cuVDBrZrmzUz6VkMeCR4DZFqhd4YtLwsw3i2wYHswQ==",
2293 | "dev": true,
2294 | "requires": {
2295 | "chalk": "^1.1.3",
2296 | "dlv": "^1.1.3"
2297 | },
2298 | "dependencies": {
2299 | "ansi-regex": {
2300 | "version": "2.1.1",
2301 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
2302 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
2303 | "dev": true
2304 | },
2305 | "ansi-styles": {
2306 | "version": "2.2.1",
2307 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
2308 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
2309 | "dev": true
2310 | },
2311 | "chalk": {
2312 | "version": "1.1.3",
2313 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
2314 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
2315 | "dev": true,
2316 | "requires": {
2317 | "ansi-styles": "^2.2.1",
2318 | "escape-string-regexp": "^1.0.2",
2319 | "has-ansi": "^2.0.0",
2320 | "strip-ansi": "^3.0.0",
2321 | "supports-color": "^2.0.0"
2322 | }
2323 | },
2324 | "strip-ansi": {
2325 | "version": "3.0.1",
2326 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
2327 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
2328 | "dev": true,
2329 | "requires": {
2330 | "ansi-regex": "^2.0.0"
2331 | }
2332 | },
2333 | "supports-color": {
2334 | "version": "2.0.0",
2335 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
2336 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
2337 | "dev": true
2338 | }
2339 | }
2340 | },
2341 | "to-array": {
2342 | "version": "0.1.4",
2343 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
2344 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
2345 | "dev": true
2346 | },
2347 | "to-regex-range": {
2348 | "version": "5.0.1",
2349 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
2350 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
2351 | "dev": true,
2352 | "requires": {
2353 | "is-number": "^7.0.0"
2354 | }
2355 | },
2356 | "toidentifier": {
2357 | "version": "1.0.0",
2358 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
2359 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
2360 | "dev": true
2361 | },
2362 | "tree-kill": {
2363 | "version": "1.2.2",
2364 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
2365 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
2366 | "dev": true
2367 | },
2368 | "tslib": {
2369 | "version": "1.14.1",
2370 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
2371 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
2372 | "dev": true
2373 | },
2374 | "typescript": {
2375 | "version": "4.0.5",
2376 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
2377 | "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
2378 | "dev": true
2379 | },
2380 | "ua-parser-js": {
2381 | "version": "0.7.28",
2382 | "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz",
2383 | "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==",
2384 | "dev": true
2385 | },
2386 | "ultron": {
2387 | "version": "1.1.1",
2388 | "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
2389 | "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
2390 | "dev": true
2391 | },
2392 | "universalify": {
2393 | "version": "0.1.2",
2394 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
2395 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
2396 | "dev": true
2397 | },
2398 | "unpipe": {
2399 | "version": "1.0.0",
2400 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
2401 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
2402 | "dev": true
2403 | },
2404 | "utils-merge": {
2405 | "version": "1.0.1",
2406 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
2407 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
2408 | "dev": true
2409 | },
2410 | "validate-npm-package-license": {
2411 | "version": "3.0.4",
2412 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
2413 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
2414 | "dev": true,
2415 | "requires": {
2416 | "spdx-correct": "^3.0.0",
2417 | "spdx-expression-parse": "^3.0.0"
2418 | }
2419 | },
2420 | "which": {
2421 | "version": "1.3.1",
2422 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
2423 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
2424 | "dev": true,
2425 | "requires": {
2426 | "isexe": "^2.0.0"
2427 | }
2428 | },
2429 | "which-module": {
2430 | "version": "2.0.0",
2431 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
2432 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
2433 | "dev": true
2434 | },
2435 | "window-size": {
2436 | "version": "0.1.4",
2437 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
2438 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=",
2439 | "dev": true
2440 | },
2441 | "wrap-ansi": {
2442 | "version": "2.1.0",
2443 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
2444 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
2445 | "dev": true,
2446 | "requires": {
2447 | "string-width": "^1.0.1",
2448 | "strip-ansi": "^3.0.1"
2449 | },
2450 | "dependencies": {
2451 | "ansi-regex": {
2452 | "version": "2.1.1",
2453 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
2454 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
2455 | "dev": true
2456 | },
2457 | "is-fullwidth-code-point": {
2458 | "version": "1.0.0",
2459 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
2460 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
2461 | "dev": true,
2462 | "requires": {
2463 | "number-is-nan": "^1.0.0"
2464 | }
2465 | },
2466 | "string-width": {
2467 | "version": "1.0.2",
2468 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
2469 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
2470 | "dev": true,
2471 | "requires": {
2472 | "code-point-at": "^1.0.0",
2473 | "is-fullwidth-code-point": "^1.0.0",
2474 | "strip-ansi": "^3.0.0"
2475 | }
2476 | },
2477 | "strip-ansi": {
2478 | "version": "3.0.1",
2479 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
2480 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
2481 | "dev": true,
2482 | "requires": {
2483 | "ansi-regex": "^2.0.0"
2484 | }
2485 | }
2486 | }
2487 | },
2488 | "wrappy": {
2489 | "version": "1.0.2",
2490 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2491 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
2492 | "dev": true
2493 | },
2494 | "ws": {
2495 | "version": "6.1.4",
2496 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
2497 | "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
2498 | "dev": true,
2499 | "requires": {
2500 | "async-limiter": "~1.0.0"
2501 | }
2502 | },
2503 | "xmlhttprequest-ssl": {
2504 | "version": "1.5.5",
2505 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
2506 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
2507 | "dev": true
2508 | },
2509 | "y18n": {
2510 | "version": "4.0.1",
2511 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
2512 | "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
2513 | "dev": true
2514 | },
2515 | "yargs": {
2516 | "version": "12.0.5",
2517 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
2518 | "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
2519 | "dev": true,
2520 | "requires": {
2521 | "cliui": "^4.0.0",
2522 | "decamelize": "^1.2.0",
2523 | "find-up": "^3.0.0",
2524 | "get-caller-file": "^1.0.1",
2525 | "os-locale": "^3.0.0",
2526 | "require-directory": "^2.1.1",
2527 | "require-main-filename": "^1.0.1",
2528 | "set-blocking": "^2.0.0",
2529 | "string-width": "^2.0.0",
2530 | "which-module": "^2.0.0",
2531 | "y18n": "^3.2.1 || ^4.0.0",
2532 | "yargs-parser": "^11.1.1"
2533 | }
2534 | },
2535 | "yargs-parser": {
2536 | "version": "11.1.1",
2537 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
2538 | "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
2539 | "dev": true,
2540 | "requires": {
2541 | "camelcase": "^5.0.0",
2542 | "decamelize": "^1.2.0"
2543 | }
2544 | },
2545 | "yeast": {
2546 | "version": "0.1.2",
2547 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
2548 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
2549 | "dev": true
2550 | }
2551 | }
2552 | }
2553 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gtr-cof",
3 | "version": "1.0.0",
4 | "description": "gtr-cof",
5 | "scripts": {
6 | "lite": "lite-server --baseDir=\"docs\" --port 10001",
7 | "start": "npm run lite",
8 | "dev": "concurrently --raw --kill-others 'npm start' 'tsc -w'"
9 | },
10 | "author": "Mike Hadlow",
11 | "license": "MIT",
12 | "devDependencies": {
13 | "@types/d3": "^3.0.0",
14 | "@types/webmidi": "^2.0.2",
15 | "concurrently": "4.0.1",
16 | "d3": "^3.0.0",
17 | "lite-server": "^1.3.1",
18 | "typescript": "^4.0.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/chord-interval-module.ts:
--------------------------------------------------------------------------------
1 | namespace chordInterval {
2 |
3 | let buttons: d3.Selection;
4 | let toggle: number = 0;
5 |
6 | export function init(): void {
7 |
8 | let radius = 10;
9 | let pad = 2;
10 |
11 | let svg = d3.select("#modes");
12 | let intervals = svg
13 | .append("g")
14 | .attr("transform", "translate(0, 240)");
15 |
16 | let gs = intervals.selectAll("g")
17 | .data([0,1,2,3,4,5,6], function (i) { return i.toString(); })
18 | .enter()
19 | .append("g")
20 | .attr("transform", function (d, i) { return "translate(" + (i * (radius * 2 + pad) + pad) + ", 0)"; });
21 |
22 | buttons = gs
23 | .append("circle")
24 | .attr("cx", radius)
25 | .attr("cy", radius)
26 | .attr("r", radius)
27 | .attr("strokeWidth", 2)
28 | .attr("class", "mode-button")
29 | .on("click", onClick);
30 |
31 | gs
32 | .append("text")
33 | .attr("x", radius)
34 | .attr("y", radius + 5)
35 | .attr("text-anchor", "middle")
36 | .text(function (x) { return x + 1; });
37 |
38 | events.chordIntervalChange.subscribe(update);
39 | }
40 |
41 | function onClick(x:number) {
42 | let updatedToggle = toggle ^ (2**x);
43 | let chordIntervals = [0,1,2,3,4,5,6].filter(x => (2**x & updatedToggle) === 2**x);
44 | events.chordIntervalChange.publish({ chordIntervals: chordIntervals });
45 | }
46 |
47 | export function update(event: events.ChordIntervalChangeEvent): void {
48 | toggle = 0;
49 | event.chordIntervals.forEach(x => toggle = toggle + 2**x);
50 | buttons
51 | .data(event.chordIntervals, function (m) { return m.toString(); })
52 | .attr("class", "mode-button mode-button-selected")
53 | .exit()
54 | .attr("class", "mode-button");
55 | }
56 |
57 | interface button {
58 | readonly id: number,
59 | selected: boolean
60 | }
61 | }
--------------------------------------------------------------------------------
/src/cof-module.ts:
--------------------------------------------------------------------------------
1 |
2 | namespace cof {
3 |
4 | interface NoteCircleState {
5 | noteSegments: d3.Selection;
6 | noteText: d3.Selection;
7 | intervalSegments: d3.Selection;
8 | intervalText: d3.Selection;
9 | intervalNotes: d3.Selection;
10 | chordText: d3.Selection;
11 | chordSegments: d3.Selection;
12 | chordNotes: d3.Selection;
13 | }
14 |
15 | export class NoteCircle {
16 | indexer: (x: Segment) => string = (x) => x.index + "";
17 |
18 | constructor(svg: d3.Selection, noteIndexes: number[], label: string) {
19 | let state = this.draw(svg, rotate(noteIndexes, 3), label);
20 | let setCToNoonSubscriptionIndex = -1;
21 |
22 | events.scaleChange.subscribe(scaleChnaged => {
23 | this.update(scaleChnaged, state);
24 |
25 | setCToNoonSubscriptionIndex = events.setCToNoon.resubscribe(setCToNoonEvent => {
26 | let offset = setCToNoonEvent.isC ? 3 : 0;
27 | svg.selectAll("*").remove();
28 | state = this.draw(svg, rotate(noteIndexes, offset), label);
29 | this.update(scaleChnaged, state);
30 | }, setCToNoonSubscriptionIndex);
31 | });
32 | }
33 |
34 | draw (svg: d3.Selection, noteIndexes: number[], label: string) : NoteCircleState {
35 | let pad = 50;
36 |
37 | let chordRadius = 240;
38 | let noteRadius = 200;
39 | let degreeRadius = 135;
40 | let innerRadius = 90;
41 |
42 | let cof = svg
43 | .append("g")
44 | .attr("transform", "translate(" + (noteRadius + pad) + ", " + (noteRadius + pad) + ")");
45 |
46 | cof.append("text")
47 | .attr("text-anchor", "middle")
48 | .attr("x", 0)
49 | .attr("y", 0)
50 | .text(label)
51 |
52 | let segments = generateSegments(noteIndexes);
53 |
54 | let noteArc = d3.svg.arc()
55 | .innerRadius(degreeRadius)
56 | .outerRadius(noteRadius);
57 |
58 | let degreeArc = d3.svg.arc()
59 | .innerRadius(innerRadius)
60 | .outerRadius(degreeRadius);
61 |
62 | let chordArc = d3.svg.arc()
63 | .innerRadius(noteRadius)
64 | .outerRadius(chordRadius);
65 |
66 | let noteSegments = cof.append("g").selectAll("path")
67 | .data(segments, this.indexer)
68 | .enter()
69 | .append("path")
70 | .attr("d", noteArc)
71 | .attr("class", "note-segment")
72 | .on("click", handleNoteClick);
73 |
74 | let noteText = cof.append("g").selectAll("text")
75 | .data(segments)
76 | .enter()
77 | .append("text")
78 | .attr("x", function (x) { return noteArc.centroid(x)[0]; })
79 | .attr("y", function (x) { return noteArc.centroid(x)[1] + 11; })
80 | .text("")
81 | .attr("class", "note-segment-text");
82 |
83 | let intervalSegments = cof.append("g").selectAll("path")
84 | .data(segments, this.indexer)
85 | .enter()
86 | .append("path")
87 | .attr("d", degreeArc)
88 | .attr("class", "interval-segment")
89 | .on("click", handleIntervalClick);
90 |
91 | let intervalNotes = cof.append("g").selectAll("circle")
92 | .data(segments, this.indexer)
93 | .enter()
94 | .append("circle")
95 | .style("pointer-events", "none")
96 | .attr("r", 25)
97 | .attr("cx", function (x) { return degreeArc.centroid(x)[0]; })
98 | .attr("cy", function (x) { return degreeArc.centroid(x)[1]; })
99 | .attr("class", "interval-note")
100 |
101 | let intervalText = cof.append("g").selectAll("text")
102 | .data(segments, this.indexer)
103 | .enter()
104 | .append("text")
105 | .attr("x", function (x) { return degreeArc.centroid(x)[0]; })
106 | .attr("y", function (x) { return degreeArc.centroid(x)[1] + 8; })
107 | .text("")
108 | .attr("class", "degree-segment-text");
109 |
110 | let chordSegments = cof.append("g").selectAll("path")
111 | .data(segments, this.indexer)
112 | .enter()
113 | .append("path")
114 | .attr("d", chordArc)
115 | .attr("class", "chord-segment")
116 | .on("click", handleChordClick);
117 |
118 | let chordNotes = cof.append("g").selectAll("circle")
119 | .data(segments, this.indexer)
120 | .enter()
121 | .append("circle")
122 | .style("pointer-events", "none")
123 | .attr("r", 28)
124 | .attr("cx", function (x) { return chordArc.centroid(x)[0]; })
125 | .attr("cy", function (x) { return chordArc.centroid(x)[1]; })
126 | .attr("class", "chord-segment-note");
127 |
128 | let chordText = cof.append("g").selectAll("text")
129 | .data(segments, this.indexer)
130 | .enter()
131 | .append("text")
132 | .attr("x", function (x) { return chordArc.centroid(x)[0]; })
133 | .attr("y", function (x) { return chordArc.centroid(x)[1] + 8; })
134 | .text("")
135 | .attr("class", "degree-segment-text");
136 |
137 | return {
138 | noteSegments: noteSegments,
139 | noteText: noteText,
140 | intervalSegments: intervalSegments,
141 | intervalNotes: intervalNotes,
142 | intervalText: intervalText,
143 | chordSegments: chordSegments,
144 | chordNotes: chordNotes,
145 | chordText: chordText
146 | };
147 | }
148 |
149 | update(scaleChnaged: events.ScaleChangedEvent, state: NoteCircleState): void {
150 | let data: Segment[] = scaleChnaged.nodes.map(node => {
151 | startAngle: 0,
152 | endAngle: 0,
153 | scaleNote: {},
154 | index: node.scaleNote.note.index,
155 | node: node
156 | });
157 |
158 | state.noteSegments
159 | .data(data, this.indexer)
160 | .attr("class", (d, i) => "note-segment " +
161 | (d.node.scaleNote.isScaleNote ? ((i === 0) ? "note-segment-tonic" : "note-segment-scale") : ""));
162 |
163 | state.noteText
164 | .data(data, this.indexer)
165 | .text(d => d.node.scaleNote.note.label);
166 |
167 | state.intervalSegments
168 | .data(data, this.indexer)
169 | .attr("class", d => d.node.scaleNote.isScaleNote ? "degree-segment-selected" : "interval-segment");
170 |
171 | state.intervalText
172 | .data(data, this.indexer)
173 | .text(d => d.node.intervalName);
174 |
175 | state.intervalNotes
176 | .data(data, this.indexer)
177 | .attr("class", d => d.node.toggle ? "interval-note-selected" : "interval-note")
178 | .style("fill", d => d.node.toggle ? "#" + d.node.chordInterval.colour.toString(16) : "none")
179 | .style("stroke-width", d => d.node.midiToggle ? "20px" : "2px")
180 | .style("stroke", d => d.node.midiToggle ? "OrangeRed" : d.node.toggle ? "black" : "none");
181 |
182 | state.chordText
183 | .data(data, this.indexer)
184 | .text(d => d.node.scaleNote.chord!.romanNumeral + "");
185 |
186 | state.chordSegments
187 | .data(data, this.indexer)
188 | .attr("class", d => d.node.scaleNote.isScaleNote ? getChordSegmentClass(d.node.scaleNote.chord!) : "chord-segment");
189 |
190 | state.chordNotes
191 | .data(data, this.indexer)
192 | .attr("class", d => d.node.isChordRoot ? getChordSegmentClass(d.node.scaleNote.chord!) : "chord-segment-note");
193 | }
194 | }
195 |
196 | function getChordSegmentClass(chord: music.Chord): string {
197 | if (chord.type === music.ChordType.Diminished) return "chord-segment-dim";
198 | if (chord.type === music.ChordType.Augmented) return "chord-segment-aug";
199 | if (chord.type === music.ChordType.Minor) return "chord-segment-minor";
200 | if (chord.type === music.ChordType.Major) return "chord-segment-major";
201 | throw "Unexpected ChordType";
202 | }
203 |
204 | function generateSegments(fifths: number[]): Segment[] {
205 | let count = fifths.length;
206 | let items: Array = [];
207 | let angle = (Math.PI * (2 / count));
208 | for (let i: number = 0; i < count; i++) {
209 | let itemAngle = (angle * i) - (angle / 2);
210 | items.push({
211 | startAngle: itemAngle,
212 | endAngle: itemAngle + angle,
213 | index: fifths[i],
214 | node: music.nullNode
215 | });
216 | }
217 | return items;
218 | }
219 |
220 | function handleNoteClick(segment: Segment, i: number): void {
221 | events.tonicChange.publish({
222 | noteSpec: replaceDoubleSharpsAndFlatsWithEquivalentNote(segment.node.scaleNote.note)
223 | });
224 | }
225 |
226 | function replaceDoubleSharpsAndFlatsWithEquivalentNote(noteSpec: music.NoteSpec): music.NoteSpec {
227 | if(Math.abs(noteSpec.offset) > 1) {
228 | let naturalId = noteSpec.natural.id;
229 | let newNaturalId = (noteSpec.offset > 0)
230 | ? naturalId + 1 % 7
231 | : naturalId == 0 ? 6 : naturalId - 1;
232 | let newNatural = music.naturals.filter(x => x.id === newNaturalId)[0];
233 | return music.createNoteSpec(newNatural.index, noteSpec.index)
234 | }
235 | return noteSpec;
236 | }
237 |
238 | function handleChordClick(segment: Segment, i: number): void {
239 | events.chordChange.publish({ chordIndex: segment.node.scaleNote.note.index });
240 | }
241 |
242 | function handleIntervalClick(segment: Segment, i: number): void {
243 | events.toggle.publish({ index: segment.node.scaleNote.note.index });
244 | }
245 |
246 | function rotate(array: number[], offset: number): number[] {
247 | let newArray: number[] = [];
248 | for(let item of array) {
249 | newArray.push((item + offset) % 12);
250 | }
251 | return newArray;
252 | }
253 |
254 | interface Segment {
255 | readonly startAngle: number;
256 | readonly endAngle: number;
257 | readonly index: number;
258 | readonly node: music.Node;
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/src/cookie-module.ts:
--------------------------------------------------------------------------------
1 | namespace cookies {
2 |
3 | let cookieName = "gtr-cof-state-v4";
4 |
5 | export function init(): void {
6 | events.stateChange.subscribe(bakeCookie2);
7 | }
8 |
9 | function bakeCookie2(stateChange: events.StateChangedEvent): void {
10 | let json = JSON.stringify(stateChange.state);
11 | document.cookie = cookieName + "=" + json + ";";
12 | }
13 |
14 | export function readCookie2(): state.State {
15 | let result = document.cookie.match(new RegExp(cookieName + '=([^;]+)'));
16 | if(result != null)
17 | {
18 | let state: state.State = JSON.parse(result[1]);
19 | return state;
20 | }
21 |
22 | return null;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/events-module.ts:
--------------------------------------------------------------------------------
1 | namespace events {
2 | export class Bus {
3 | private listeners: Array<(x:T)=>void> = [];
4 | private name: string;
5 |
6 | // name should be the name of the exported variable in 'events' that the bus instance is assigned to.
7 | constructor(name: string) {
8 | this.name = name;
9 | }
10 |
11 | public subscribe(listener: (x:T)=>void): void {
12 | this.listeners.push(listener);
13 | }
14 |
15 | // first call should be passed index = -1
16 | public resubscribe(listener: (x:T)=>void, index: number): number {
17 | if(index === -1) {
18 | return this.listeners.push(listener) - 1;
19 | }
20 | this.listeners[index] = listener;
21 | return index;
22 | }
23 |
24 | public publish(event: T): void {
25 | //console.log("Published event: '" + this.name + "'")
26 | for (let listener of this.listeners) {
27 | listener(event);
28 | }
29 | }
30 | }
31 |
32 | function genericName(type: { new(): U; }): string {
33 | return type.constructor.toString();
34 | }
35 |
36 | export let stateChange: Bus = new Bus("stateChange");
37 |
38 | export interface StateChangedEvent {
39 | readonly state: state.State;
40 | }
41 |
42 | export let scaleChange: Bus = new Bus("scaleChange");
43 |
44 | export interface ScaleChangedEvent {
45 | readonly nodes: music.Node[];
46 | readonly mode: music.Mode;
47 | }
48 |
49 | export let tonicChange: Bus = new Bus("tonicChange");
50 |
51 | export interface TonicChangedEvent {
52 | readonly noteSpec: music.NoteSpec;
53 | }
54 |
55 | export let modeChange: Bus = new Bus("modeChange");
56 |
57 | export interface ModeChangedEvent {
58 | readonly mode: music.Mode;
59 | }
60 |
61 | export let chordChange: Bus = new Bus("chordChange");
62 |
63 | export interface ChordChangeEvent {
64 | readonly chordIndex: number;
65 | }
66 |
67 | export let toggle: Bus = new Bus("toggle");
68 |
69 | export interface ToggleEvent {
70 | readonly index: number;
71 | }
72 |
73 | export let tuningChange: Bus = new Bus("tuningChange");
74 |
75 | export interface TuningChangedEvent {
76 | readonly index: number;
77 | }
78 |
79 | export let leftHandedChange: Bus = new Bus("leftHandedChange");
80 |
81 | export interface LeftHandedFretboardEvent {
82 | readonly isLeftHanded: boolean;
83 | }
84 |
85 | export let flipNutChange: Bus = new Bus("flipNutChange");
86 |
87 | export interface FlipNutEvent {
88 | readonly isNutFlipped: boolean;
89 | }
90 |
91 | export let fretboardLabelChange: Bus = new Bus("fretboardLabelChange");
92 |
93 | export interface FretboardLabelChangeEvent {
94 | readonly labelType: FretboardLabelType;
95 | }
96 |
97 | export enum FretboardLabelType {
98 | None,
99 | NoteName,
100 | Interval
101 | }
102 |
103 | export let chordIntervalChange: Bus = new Bus("chordIntervalChange");
104 |
105 | export interface ChordIntervalChangeEvent {
106 | readonly chordIntervals: number[];
107 | }
108 |
109 | export let scaleFamilyChange: Bus = new Bus("scaleFamilyChange");
110 |
111 | export interface ScaleFamilyChangeEvent {
112 | readonly scaleFamily: music.ScaleFamily;
113 | }
114 |
115 | export let midiNote: Bus = new Bus("midiNoteEvent");
116 |
117 | export interface MidiNoteEvent {
118 | readonly toggledIndexes: number;
119 | }
120 |
121 | export let setCToNoon: Bus = new Bus("setCToNoonEvent");
122 |
123 | export interface SetCToNoonEvent {
124 | readonly isC: boolean;
125 | }
126 | }
--------------------------------------------------------------------------------
/src/gtr-cof.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | menu.init();
4 | tonics.init();
5 | modes.init(music.scaleFamily[0]);
6 | chordInterval.init();
7 | let chromatic = new cof.NoteCircle(d3.select("#chromatic"), music.chromatic(), "Chromatic");
8 | let circleOfFifths = new cof.NoteCircle(d3.select("#cof"), music.fifths(), "Circle of Fifths");
9 | gtr.init();
10 | tuning.init();
11 | scaleFamily.init();
12 | settings.init();
13 | permalink.init();
14 | state.init();
15 | cookies.init();
--------------------------------------------------------------------------------
/src/gtr-module.ts:
--------------------------------------------------------------------------------
1 |
2 | namespace gtr {
3 |
4 | let currentTuning: tuning.Tuning;
5 | let currentState: events.ScaleChangedEvent;
6 | let notes: d3.Selection;
7 | let noteLabels: d3.Selection;
8 | let numberOfFrets = 16;
9 | let fretboardElement: SVGGElement;
10 | let isLeftHanded: boolean = false;
11 | let isNutFlipped: boolean = false;
12 | let fretboardLabelType: events.FretboardLabelType = events.FretboardLabelType.NoteName;
13 |
14 | let stringGap = 40;
15 | let fretGap = 70;
16 | let fretWidth = 5;
17 | let noteRadius = 15;
18 | let pad = 20;
19 |
20 | function indexer(stringNote: StringNote): string {
21 | return stringNote.index + "_" + stringNote.octave;
22 | }
23 |
24 | export function init() {
25 | events.tuningChange.subscribe(handleTuningChange);
26 | events.scaleChange.subscribe(update);
27 | events.leftHandedChange.subscribe(handleLeftHandedChanged);
28 | events.flipNutChange.subscribe(handleFlipNutChanged);
29 | events.fretboardLabelChange.subscribe(handleLabelChange);
30 | }
31 |
32 | function handleTuningChange(tuningChangedEvent: events.TuningChangedEvent): void {
33 | let newTuning = tuning.tunings.find(x => x.index == tuningChangedEvent.index);
34 | updateFretboard(newTuning);
35 | }
36 |
37 | function handleLeftHandedChanged(lhEvent: events.LeftHandedFretboardEvent) {
38 | isLeftHanded = lhEvent.isLeftHanded;
39 | if(currentTuning != null) {
40 | updateFretboard(currentTuning);
41 | }
42 | }
43 |
44 | function setHandedness()
45 | {
46 | if(isLeftHanded) {
47 | fretboardElement.transform.baseVal.getItem(0).setTranslate(1200, 0);
48 | fretboardElement.transform.baseVal.getItem(1).setScale(-1, 1);
49 | noteLabels
50 | .attr("transform", (d, i) => "translate(0, 0) scale(-1, 1)")
51 | .attr("x", (d, i) => -(i * fretGap + pad + 30))
52 | } else {
53 | fretboardElement.transform.baseVal.getItem(0).setTranslate(0, 0);
54 | fretboardElement.transform.baseVal.getItem(1).setScale(1, 1);
55 | noteLabels
56 | .attr("transform", (d, i) => "translate(0, 0) scale(1, 1)")
57 | .attr("x", (d, i) => (i * fretGap + pad + 30))
58 | }
59 | }
60 |
61 | function handleFlipNutChanged(fnEvent: events.FlipNutEvent) {
62 | isNutFlipped = fnEvent.isNutFlipped;
63 | if(currentTuning != null) {
64 | updateFretboard(currentTuning);
65 | }
66 | }
67 |
68 | function handleLabelChange(lcEvent: events.FretboardLabelChangeEvent) {
69 | fretboardLabelType = lcEvent.labelType;
70 | setLabels();
71 | }
72 |
73 | function setLabels()
74 | {
75 | function setNoteName(note: StringNote): string {
76 | return note.node.scaleNote.isScaleNote || note.node.toggle ? note.node.scaleNote.note.label : "";
77 | }
78 |
79 | function setInterval(note: StringNote): string {
80 | return note.node.scaleNote.isScaleNote || note.node.toggle ? note.node.intervalName : "";
81 | }
82 |
83 | switch (fretboardLabelType) {
84 | case events.FretboardLabelType.None:
85 | noteLabels.text("");
86 | break;
87 | case events.FretboardLabelType.NoteName:
88 | noteLabels.text(setNoteName)
89 | break;
90 | case events.FretboardLabelType.Interval:
91 | noteLabels.text(setInterval);
92 | break;
93 | }
94 | }
95 |
96 | function updateFretboard(tuningInfo: tuning.Tuning): void {
97 |
98 | currentTuning = tuningInfo;
99 | let fretData: Array = getFretData(numberOfFrets);
100 | let dots: Array<[number, number]> = tuningInfo.dots;
101 |
102 | d3.selectAll("#gtr > *").remove();
103 | let svg = d3.select("#gtr");
104 | svg.append("text")
105 | .attr("class", "mode-text")
106 | .attr("x", 30)
107 | .attr("y", 11)
108 | .text(tuningInfo.tuning + " "
109 | + tuningInfo.description
110 | + (isLeftHanded ? ", Left Handed" : "")
111 | + (isNutFlipped ? ", Nut Flipped" : ""));
112 | let gtr = svg.append("g").attr("transform", "translate(0, 0) scale(1, 1)");
113 | fretboardElement = gtr.node();
114 |
115 | // frets
116 | gtr.append("g").selectAll("rect")
117 | .data(fretData)
118 | .enter()
119 | .append("rect")
120 | .attr("x", function (d, i) { return (i + 1) * fretGap + pad - fretWidth; })
121 | .attr("y", pad + stringGap / 2 - fretWidth)
122 | .attr("width", fretWidth)
123 | .attr("height", stringGap * (tuningInfo.notes.length - 1) + (fretWidth * 2))
124 | .attr("fill", function (d, i) { return i === 0 ? "black" : "none"; })
125 | .attr("stroke", "grey")
126 | .attr("stroke-width", 1);
127 |
128 | // dots
129 | gtr.append("g").selectAll("circle")
130 | .data(dots)
131 | .enter()
132 | .append("circle")
133 | .attr("r", 10)
134 | .attr("cx", function (d) { return d[0] * fretGap + pad + 30 + (d[1] * 10); })
135 | .attr("cy", function (d) { return (tuningInfo.notes.length) * stringGap + pad + 15; })
136 | .attr("fill", "lightgrey")
137 | .attr("stroke", "none");
138 |
139 | let strings = gtr.append("g").selectAll("g")
140 | .data(isNutFlipped ? tuningInfo.notes.slice() : tuningInfo.notes.slice().reverse(), (_, i) => i + "")
141 | .enter()
142 | .append("g")
143 | .attr("transform", function (d, i) { return "translate(0, " + ((i * stringGap) + pad) + ")"; });
144 |
145 | // string lines
146 | strings
147 | .append("line")
148 | .attr("x1", pad + fretGap)
149 | .attr("y1", stringGap / 2)
150 | .attr("x2", pad + (fretGap * numberOfFrets) + 20)
151 | .attr("y2", stringGap / 2)
152 | .attr("stroke", "black")
153 | .attr("stroke-width", 2);
154 |
155 | notes = strings
156 | .selectAll("circle")
157 | .data(function (d) { return allNotesFrom(d, numberOfFrets); }, indexer)
158 | .enter()
159 | .append("circle")
160 | .attr("r", noteRadius)
161 | .attr("cy", stringGap / 2)
162 | .attr("cx", function (d, i) { return i * fretGap + pad + 30 })
163 | .on("click", d => events.toggle.publish({ index: d.index }));
164 |
165 | noteLabels = strings
166 | .selectAll("text")
167 | .data(function (d) { return allNotesFrom(d, numberOfFrets); }, indexer)
168 | .enter()
169 | .append("text")
170 | .attr("transform", "translate(0, 0) scale(1, 1)")
171 | .attr("text-anchor", "middle")
172 | .attr("x", (d, i) => i * fretGap + pad + 30)
173 | .attr("y", (stringGap / 2) + 5)
174 | .text("");
175 |
176 | setHandedness();
177 |
178 | if(currentState != null) {
179 | update(currentState);
180 | }
181 | }
182 |
183 | function update(stateChange: events.ScaleChangedEvent): void {
184 |
185 | let hasToggledNotes = stateChange.nodes.some(x => x.toggle);
186 |
187 | let fill = function (d: StringNote): string {
188 | return d.node.toggle
189 | ? "white"
190 | : d.node.scaleNote.isScaleNote
191 | ? d.node.scaleNote.noteNumber === 0
192 | ? hasToggledNotes ? "white" : "yellow"
193 | : "white"
194 | : "rgba(255, 255, 255, 0.01)";
195 | };
196 |
197 | let stroke = function (d: StringNote): string {
198 | return d.node.midiToggle ? "OrangeRed"
199 | : d.node.toggle ? "#" + d.node.chordInterval.colour.toString(16)
200 | : hasToggledNotes ? "none"
201 | : d.node.scaleNote.isScaleNote ? "grey" : "none";
202 | };
203 |
204 | let strokeWidth = function (d: StringNote): number {
205 | return d.node.midiToggle ? 10
206 | : d.node.toggle ? 4
207 | : d.node.scaleNote.isScaleNote ? 2
208 | : 0;
209 | };
210 |
211 | let data = repeatTo(stateChange.nodes, numberOfFrets);
212 |
213 | notes
214 | .data(data, indexer)
215 | .attr("fill", fill)
216 | .attr("stroke", stroke)
217 | .attr("stroke-width", strokeWidth);
218 |
219 | noteLabels.data(data, indexer)
220 | setLabels();
221 | currentState = stateChange;
222 | }
223 |
224 | function allNotesFrom(index: number, numberOfNotes: number): Array {
225 | let items: Array = [];
226 |
227 | for (let i = 0; i < numberOfNotes; i++) {
228 | items.push({
229 | octave: Math.floor((i + 1) / 12),
230 | index: (i + index) % 12,
231 | node: music.nullNode
232 | });
233 | }
234 |
235 | return items;
236 | }
237 |
238 | function getFretData(numberOfFrets: number): Array {
239 | let data: Array = [];
240 | for (let i = 0; i < numberOfFrets; i++) {
241 | data.push(i);
242 | }
243 | return data;
244 | }
245 |
246 | function repeatTo(nodes: music.Node[], count: number): StringNote[] {
247 | let stringNotes: StringNote[] = [];
248 | for(let i=0; i <= Math.floor(count / 12); i++) {
249 | stringNotes = stringNotes.concat(nodes.map(x => {
250 | octave: i,
251 | index: x.scaleNote.note.index,
252 | node: x
253 | }));
254 | }
255 | return stringNotes;
256 | }
257 |
258 | interface StringNote {
259 | readonly octave: number;
260 | readonly index: number;
261 | readonly node: music.Node;
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/src/menu-module.ts:
--------------------------------------------------------------------------------
1 | namespace menu {
2 | export function init() : void {
3 |
4 | let menuItems = document.getElementsByClassName("menu");
5 | for(let menuItem of menuItems) {
6 | menuItem.addEventListener("click", onMenuClick)
7 | }
8 |
9 | // close open menu when document is clicked outside
10 | document.addEventListener("mouseup", (event: MouseEvent) => {
11 | let targetElement = event.target;
12 | if(targetElement.closest(".dropdown-content") === null && targetElement.closest(".menu") === null) {
13 | let contentElements = document.getElementsByClassName("dropdown-content");
14 | for(let contentElement of contentElements) {
15 | if(contentElement.classList.contains("dropdown-content-visible")) {
16 | contentElement.classList.remove("dropdown-content-visible");
17 | }
18 | }
19 | }
20 | });
21 | }
22 |
23 | function onMenuClick(event: MouseEvent) : void {
24 | let menuElement = event.target;
25 | let currentContentElement = menuElement.parentElement.querySelector(".dropdown-content")
26 |
27 | let contentElements = document.getElementsByClassName("dropdown-content");
28 | for(let contentElement of contentElements) {
29 | if(contentElement === currentContentElement) {
30 | currentContentElement.classList.toggle("dropdown-content-visible");
31 | }
32 | else {
33 | contentElement.classList.remove("dropdown-content-visible");
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/midi-module.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | namespace midiControl {
4 |
5 | // bit flag for on/off MIDI notes
6 | let currentToggledIndexes: number = 0;
7 |
8 | export function init(): void {
9 | let nav: Navigator = window.navigator;
10 |
11 | if(!nav.requestMIDIAccess) {
12 | console.log("Browser does not support MIDI.");
13 | return;
14 | }
15 |
16 | nav.requestMIDIAccess()
17 | .then((midiAccess) => {
18 | console.log("MIDI Ready!");
19 | for(let entry of midiAccess.inputs) {
20 | entry[1].onmidimessage = onMidiMessage;
21 | }
22 | })
23 | .catch((error) => {
24 | console.log("Error accessing MIDI devices: " + error);
25 | });
26 | }
27 |
28 | function onMidiMessage(midiEvent: WebMidi.MIDIMessageEvent): void {
29 | let data = midiEvent.data;
30 | if(data.length === 3) {
31 | let status = data[0];
32 | // command is the four most significant bits of the status byte.
33 | let command = status >>> 4;
34 | //let octave = Math.trunc(data[1] / 12);
35 | // MIDI starts with C0 = 0, but guitar dashboard index 0 = A, so add three to the midi note number.
36 | let index = (data[1] + 3) % 12;
37 | if(command === 0x9) {
38 | // MIDI note on.
39 | currentToggledIndexes = currentToggledIndexes | 2**index;
40 | }
41 | if(command === 0x8) {
42 | // MIDI note off.
43 | currentToggledIndexes = currentToggledIndexes & ~(2**index);
44 | }
45 | events.midiNote.publish({ toggledIndexes: currentToggledIndexes });
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/mod-module.ts:
--------------------------------------------------------------------------------
1 | namespace mod {
2 |
3 | export class Mod {
4 |
5 | size: number = 0;
6 | items: T[];
7 | start: number = 0;
8 |
9 | constructor(items: T[]) {
10 | this.items = items;
11 | this.size = items.length;
12 | }
13 |
14 | setStart(start: number): void {
15 | this.start = start % this.size;
16 | }
17 |
18 | itemAt(index: number) : T {
19 | return this.items[(this.start + index) % this.size];
20 | }
21 |
22 | toArray(): T[] {
23 | let newArray: T[] = [];
24 | for(let i=0; i(items: U[]): [T, U][] {
31 | let theseItems: T[] = this.toArray();
32 | return zip(theseItems, items);
33 | }
34 |
35 | merge3(items2: U[], items3: V[]): [T, U, V][] {
36 | let theseItems: T[] = this.toArray();
37 | return zip3(theseItems, items2, items3);
38 | }
39 | }
40 |
41 | export function zip(a:A[], b:B[]): [A,B][] {
42 | if(a.length != b.length) {
43 | throw "Cannot merge arrays of different lengths";
44 | }
45 | return a.map((x, i) => <[A,B]>[x, b[i]]);
46 | }
47 |
48 | export function zip3(a:A[], b:B[], c:C[]): [A,B,C][] {
49 | if(a.length != b.length || a.length != c.length) {
50 | throw "Cannot merge arrays of different lengths";
51 | }
52 | return a.map((x, i) => <[A,B,C]>[x, b[i], c[i]]);
53 | }
54 |
55 | export function diff(size: number, a: number, b: number) : number {
56 | let ax = a % size;
57 | let bx = b % size;
58 | if(ax == bx) return 0;
59 |
60 | let d1 = bx - ax;
61 | let d2 = 0;
62 |
63 | if(d1 > 0) {
64 | d2 = -((ax + size) - bx);
65 | }
66 | else {
67 | d2 = (bx + size) - ax;
68 | }
69 | return Math.abs(d1) > Math.abs(d2) ? d2 : d1;
70 | }
71 | }
72 |
73 | let modTest = new mod.Mod([0,1,2,3,4,5]);
--------------------------------------------------------------------------------
/src/modes-module.ts:
--------------------------------------------------------------------------------
1 |
2 | namespace modes {
3 |
4 | let buttons: d3.Selection;
5 | let modes: d3.Selection;
6 |
7 | export function init(scaleFamily: music.ScaleFamily): void {
8 | let svg = d3.select("#modes");
9 | modes = svg
10 | .append("g")
11 | .attr("transform", "translate(0, 280)");
12 |
13 | drawButtons(scaleFamily);
14 |
15 | events.modeChange.subscribe(update);
16 | events.scaleFamilyChange.subscribe(handleScaleFamilyChangedEvent);
17 | }
18 |
19 | function drawButtons(scaleFamily: music.ScaleFamily): void {
20 | let pad = 5;
21 | let buttonHeight = 25;
22 |
23 | modes.selectAll("g").remove();
24 | let gs = modes.selectAll("g").data(scaleFamily.modes, index);
25 |
26 | gs
27 | .exit()
28 | .remove();
29 |
30 | gs
31 | .enter()
32 | .append("g")
33 | .attr("transform", (d, i) => "translate(0, " + (i * (buttonHeight + pad) + pad) + ")");
34 |
35 | buttons = gs
36 | .append("rect")
37 | .attr("x", pad)
38 | .attr("y", 0)
39 | .attr("strokeWidth", 2)
40 | .attr("width", 150)
41 | .attr("height", 25)
42 | .attr("class", "mode-button")
43 | .on("click", (d) => events.modeChange.publish({ mode: d }));
44 |
45 | gs
46 | .append("text")
47 | .attr("x", pad + 10)
48 | .attr("y", 17)
49 | .text((x) => x.name)
50 | .attr("class", "mode-text");
51 |
52 | let defaultMode = scaleFamily.modes.find(x => x.index == scaleFamily.defaultModeIndex);
53 | highlightActiveMode(defaultMode);
54 | }
55 |
56 | function update(modeChange: events.ModeChangedEvent): void {
57 | highlightActiveMode(modeChange.mode);
58 | }
59 |
60 | function highlightActiveMode(mode: music.Mode): void {
61 | let modes: Array = [mode];
62 | buttons
63 | .data(modes, index)
64 | .attr("class", "mode-button mode-button-selected")
65 | .exit()
66 | .attr("class", "mode-button")
67 | }
68 |
69 | function handleScaleFamilyChangedEvent(scaleFamilyChangedEvent: events.ScaleFamilyChangeEvent) {
70 | drawButtons(scaleFamilyChangedEvent.scaleFamily);
71 | }
72 |
73 | function index(mode: music.Mode): string {
74 | return mode.index.toString();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/music-module.ts:
--------------------------------------------------------------------------------
1 |
2 | namespace music {
3 |
4 | export enum IntervalType {
5 | Nat,
6 | Maj,
7 | Min,
8 | Aug,
9 | Dim
10 | };
11 |
12 | export let intervalName: {[key: string]: string} = {};
13 | intervalName[IntervalType.Nat] = "";
14 | intervalName[IntervalType.Maj] = "M";
15 | intervalName[IntervalType.Min] = "m";
16 | intervalName[IntervalType.Aug] = "A";
17 | intervalName[IntervalType.Dim] = "d";
18 |
19 | export interface Interval {
20 | readonly ord: number;
21 | readonly type: IntervalType;
22 | readonly colour: number;
23 | };
24 |
25 | export let getIntervalName: (x: Interval) => string = interval => intervalName[interval.type] + (interval.ord + 1);
26 |
27 | export let intervals: mod.Mod = new mod.Mod([
28 | [{ ord: 0, type: IntervalType.Nat, colour: 0xf44b42 }, { ord: 1, type: IntervalType.Dim, colour: 0xf44b42 }],
29 | [{ ord: 1, type: IntervalType.Min, colour: 0xf48942 }, { ord: 0, type: IntervalType.Aug, colour: 0xf48942 }],
30 | [{ ord: 1, type: IntervalType.Maj, colour: 0xf4bf42 }, { ord: 2, type: IntervalType.Dim, colour: 0xf4bf42 }],
31 | [{ ord: 2, type: IntervalType.Min, colour: 0xf4ee42 }, { ord: 1, type: IntervalType.Aug, colour: 0xf4ee42 }],
32 | [{ ord: 2, type: IntervalType.Maj, colour: 0x8cf442 }, { ord: 3, type: IntervalType.Dim, colour: 0x8cf442 }],
33 | [{ ord: 3, type: IntervalType.Nat, colour: 0x42f4bf }, { ord: 2, type: IntervalType.Aug, colour: 0x42f4bf }],
34 | [{ ord: 4, type: IntervalType.Dim, colour: 0x42d4f4 }, { ord: 3, type: IntervalType.Aug, colour: 0x42d4f4 }],
35 | [{ ord: 4, type: IntervalType.Nat, colour: 0x429ef4 }, { ord: 5, type: IntervalType.Dim, colour: 0x429ef4 }],
36 | [{ ord: 5, type: IntervalType.Min, colour: 0xe542f4 }, { ord: 4, type: IntervalType.Aug, colour: 0xe542f4 }],
37 | [{ ord: 5, type: IntervalType.Maj, colour: 0xf44289 }, { ord: 6, type: IntervalType.Dim, colour: 0xf44289 }],
38 | [{ ord: 6, type: IntervalType.Min, colour: 0xff8282 }, { ord: 5, type: IntervalType.Aug, colour: 0xff8282 }],
39 | [{ ord: 6, type: IntervalType.Maj, colour: 0xff82fc }, { ord: 0, type: IntervalType.Dim, colour: 0xff82fc }],
40 | ]);
41 |
42 | export interface ScaleFamily {
43 | readonly index: number;
44 | readonly name: string;
45 | readonly intervals: mod.Mod;
46 | readonly modes: Mode[];
47 | readonly defaultModeIndex: number;
48 | };
49 |
50 | export function notesInScaleFamily(scaleFamily: ScaleFamily): number {
51 | return scaleFamily.intervals.items.filter(x => x).length;
52 | }
53 |
54 | let diatonicModes: Mode[] = [
55 | { name: 'Lydian', index: 5 },
56 | { name: 'Major / Ionian', index: 0 },
57 | { name: 'Mixolydian', index: 7 },
58 | { name: 'Dorian', index: 2 },
59 | { name: 'N Minor / Aeolian', index: 9 },
60 | { name: 'Phrygian', index: 4 },
61 | { name: 'Locrian', index: 11 },
62 | ];
63 |
64 | let harmonicMinorModes: Mode[] = [
65 | { name: 'Lydian ♯2', index: 5 },
66 | { name: 'Ionian ♯5', index: 0 },
67 | { name: 'Superlocrian', index: 8 },
68 | { name: 'Dorian ♯4', index: 2 },
69 | { name: 'Harmonic Minor', index: 9 },
70 | { name: 'Phrygian Dominant', index: 4 },
71 | { name: 'Locrian ♯6', index: 11 },
72 | ];
73 |
74 | let jazzMinorModes: Mode[] = [
75 | { name: 'Lydian Dominant', index: 5 },
76 | { name: 'Jazz Minor', index: 0 },
77 | { name: 'Mixolydian ♭6', index: 7 },
78 | { name: 'Assyrian', index: 2 },
79 | { name: 'Locrian ♮2', index: 9 },
80 | { name: 'Lydian Augmented', index: 3 },
81 | { name: 'Altered scale', index: 11 },
82 | ];
83 |
84 | export let scaleFamily: ScaleFamily[] = [
85 | { index: 0, name: "diatonic", intervals: new mod.Mod([true, false, true, false, true, true, false, true, false, true, false, true]), modes: diatonicModes, defaultModeIndex: 0 },
86 | { index: 1, name: "harmonic minor", intervals: new mod.Mod([true, false, true, false, true, true, false, false, true, true, false, true]), modes: harmonicMinorModes, defaultModeIndex: 9 },
87 | { index: 2, name: "jazz minor", intervals: new mod.Mod([true, false, true, true, false, true, false, true, false, true, false, true]), modes: jazzMinorModes, defaultModeIndex: 0 },
88 | { index: 3, name: "whole tone", intervals: new mod.Mod([true, false, true, false, true, false, true, false, true, false, true, false]), modes: [{ name: 'Whole Tone', index: 0}], defaultModeIndex: 0 },
89 | { index: 4, name: "diminished", intervals: new mod.Mod([true, false, true, true, false, true, true, false, true, true, false, true]), modes: [{ name: 'Diminished', index: 0}], defaultModeIndex: 0 }
90 | ];
91 |
92 | // root diatonic scale is major
93 | export let diatonic: mod.Mod = new mod.Mod([true, false, true, false, true, true, false, true, false, true, false, true]);
94 | export let indexList: mod.Mod = new mod.Mod([0,1,2,3,4,5,6,7,8,9,10,11]);
95 |
96 | export interface NoteSpec {
97 | readonly natural: Natural;
98 | readonly index: number;
99 | readonly offset: number;
100 | readonly label: string;
101 | }
102 |
103 | export function createNoteSpec(naturalIndex: number, index: number): NoteSpec {
104 | let natural = naturals.filter(x => x.index === naturalIndex)[0];
105 | if(! naturals.some(x => x.index === naturalIndex)) {
106 | throw "naturalIndex is not valid: " + naturalIndex;
107 | }
108 |
109 | let offset = mod.diff(12, naturalIndex, index);
110 | if(Math.abs(offset) > 2) {
111 | throw "offset between naturalIndex: " + naturalIndex + ", and index: " + index + ", is invalid: " + offset;
112 | }
113 |
114 | let noteLabel = noteLabels.filter(x => x.offset === offset)[0];
115 |
116 | return {
117 | natural: natural,
118 | index: index,
119 | offset: offset,
120 | label: natural.label + noteLabel.label
121 | };
122 | }
123 |
124 | export interface Natural {
125 | id: number, // order of the number in the natural set.
126 | index: number, // number against the fixed chromatic series
127 | label: string // the natural name, e.g: 'A'
128 | }
129 |
130 | // fixed index:
131 | // 0 1 2 3 4 5 6 7 8 9 10 11
132 | // A B C D E F G
133 | export let naturals: Natural[] = [
134 | { id: 0, index: 0, label: "A" },
135 | { id: 1, index: 2, label: "B" },
136 | { id: 2, index: 3, label: "C" },
137 | { id: 3, index: 5, label: "D" },
138 | { id: 4, index: 7, label: "E" },
139 | { id: 5, index: 8, label: "F" },
140 | { id: 6, index: 10, label: "G" }
141 | ];
142 |
143 | let naturalList = new mod.Mod(naturals);
144 |
145 | interface NoteName {
146 | readonly name: string;
147 | readonly index: number;
148 | }
149 |
150 | export let noteNames: NoteName[] = [
151 | { name: "A", index: 0 },
152 | { name: "A♯", index: 1 },
153 | { name: "A♭", index: 11 },
154 |
155 | { name: "B", index: 2 },
156 | { name: "B♯", index: 3 },
157 | { name: "B♭", index: 1 },
158 |
159 | { name: "C", index: 3 },
160 | { name: "C♯", index: 4 },
161 | { name: "C♭", index: 2 },
162 |
163 | { name: "D", index: 5 },
164 | { name: "D♯", index: 6 },
165 | { name: "D♭", index: 4 },
166 |
167 | { name: "E", index: 7 },
168 | { name: "E♯", index: 8 },
169 | { name: "E♭", index: 6 },
170 |
171 | { name: "F", index: 8 },
172 | { name: "F♯", index: 9 },
173 | { name: "F♭", index: 7 },
174 |
175 | { name: "G", index: 10 },
176 | { name: "G♯", index: 11 },
177 | { name: "G♭", index: 9 },
178 | ];
179 |
180 | interface NoteLabel {
181 | readonly offset: number;
182 | readonly label: string;
183 | }
184 |
185 | let noteLabels: Array = [
186 | { offset: 0, label: '' },
187 | { offset: 1, label: '♯' },
188 | { offset: 2, label: 'x' },
189 | { offset: -1, label: '♭' },
190 | { offset: -2, label: '♭♭' },
191 | ];
192 |
193 | export interface Mode {
194 | readonly name: string;
195 | readonly index: number;
196 | };
197 |
198 | export interface ScaleSpec {
199 | noteSpec: NoteSpec;
200 | mode: Mode;
201 | }
202 |
203 | export function createScaleSpec(index:number, naturalIndex:number, modeIndex:number): ScaleSpec {
204 | return {
205 | noteSpec: createNoteSpec(naturalIndex, index),
206 | mode: scaleFamily[0].modes[modeIndex]
207 | };
208 | }
209 |
210 | export enum ChordType { Major, Minor, Diminished, Augmented };
211 |
212 | export interface Chord {
213 | readonly romanNumeral: string;
214 | readonly type: ChordType;
215 | }
216 |
217 | export interface ScaleNote {
218 | readonly note: NoteSpec;
219 | readonly interval: Interval;
220 | readonly intervalName: string;
221 | readonly isScaleNote: boolean;
222 | readonly noteNumber: number;
223 | chord?: Chord;
224 | };
225 |
226 | export interface Node {
227 | readonly scaleNote: ScaleNote;
228 | readonly chordInterval: Interval;
229 | readonly intervalName: string;
230 | readonly isChordRoot: boolean;
231 | readonly toggle: boolean;
232 | readonly midiToggle: boolean;
233 | }
234 |
235 | export let nullNode: Node = {
236 | scaleNote: {
237 | note: {
238 | natural: {
239 | id: 0,
240 | index: 0,
241 | label: ""
242 | },
243 | index: 0,
244 | offset: 0,
245 | label: ""
246 | },
247 | interval: {
248 | ord: 0,
249 | type: 0,
250 | colour: 0
251 | },
252 | intervalName: "",
253 | isScaleNote: false,
254 | noteNumber: 0
255 | },
256 | chordInterval: {
257 | ord: 0,
258 | type: 0,
259 | colour: 0
260 | },
261 | intervalName: "",
262 | isChordRoot: false,
263 | toggle: false,
264 | midiToggle: false
265 | };
266 |
267 | export function generateScaleShim(
268 | noteSpec: NoteSpec,
269 | mode: Mode,
270 | chordIndex: number,
271 | chordIntervals: number[],
272 | toggledIndexes: number,
273 | toggledMidiNotes: number,
274 | scaleFamily: ScaleFamily): Node[] {
275 |
276 | let scale = generateScale(noteSpec, mode, scaleFamily);
277 | mod.zip(scale, generateChordNumbers(scale, mode, scaleFamily.intervals)).forEach(x => x[0].chord = x[1]);
278 | if(chordIndex === -1) {
279 | return generateNodes(scale, mode, scale[0].note.index, chordIntervals, toggledIndexes, toggledMidiNotes, scaleFamily.intervals);
280 | }
281 | else {
282 | return generateNodes(scale, mode, chordIndex, chordIntervals, toggledIndexes, toggledMidiNotes, scaleFamily.intervals, true);
283 | }
284 | }
285 |
286 | export function generateScale(noteSpec: NoteSpec, mode: Mode, scaleFamily: ScaleFamily): ScaleNote[] {
287 | indexList.setStart(noteSpec.index);
288 | naturalList.setStart(noteSpec.natural.id);
289 | scaleFamily.intervals.setStart(mode.index);
290 | intervals.setStart(0);
291 | let workingSet = indexList.merge3(buildScaleCounter(scaleFamily.intervals.toArray()), intervals.toArray());
292 | let isSevenNoteScale = notesInScaleFamily(scaleFamily) == 7;
293 |
294 | return workingSet.map(item => {
295 | let index = item[0];
296 | let isScaleNote = item[1][0];
297 |
298 | let noteNumber:number;
299 | let natural:Natural;
300 | let activeInterval:Interval;
301 |
302 | if(isScaleNote && isSevenNoteScale) {
303 | noteNumber = item[1][1];
304 | natural = naturalList.itemAt(noteNumber);
305 | activeInterval = item[2].filter(x => x.ord == noteNumber)[0];
306 | if(activeInterval == null) {
307 | activeInterval = item[2][0];
308 | }
309 | }
310 | else {
311 | activeInterval = item[2][0];
312 | noteNumber = isScaleNote ? item[1][1] : activeInterval.ord;
313 | natural = naturalList.itemAt(activeInterval.ord);
314 | }
315 |
316 | // console.log("index: " + index + ", isScaleNote: " + isScaleNote
317 | // + ", noteNumber: " + noteNumber + ", natural.index: " + natural.index
318 | // + ", natural.label: " + natural.label
319 | // + ", interval: " + getIntervalName(activeInterval))
320 |
321 | return {
322 | note: createNoteSpec(natural.index, index),
323 | interval: activeInterval,
324 | intervalName: getIntervalName(activeInterval),
325 | isScaleNote: isScaleNote,
326 | noteNumber: noteNumber
327 | };
328 | });
329 | }
330 |
331 | // generateNodes creates an 'outer' sliding interval ring that can change with
332 | // chord selections.
333 | export function generateNodes(
334 | scaleNotes: ScaleNote[],
335 | mode:Mode,
336 | chordIndex: number,
337 | chordIntervals: number[],
338 | toggledIndexes: number,
339 | toggledMidiNotes: number,
340 | scaleFamily: mod.Mod,
341 | chordSelected: boolean = false
342 | ): Node[] {
343 | let chordIndexOffset = ((chordIndex + 12) - scaleNotes[0].note.index) % 12;
344 | intervals.setStart(12 - chordIndexOffset);
345 | scaleFamily.setStart(mode.index);
346 | let startAt = scaleNotes.filter(x => x.note.index === chordIndex)[0].noteNumber;
347 | let workingSet = intervals.merge3(
348 | scaleNotes,
349 | buildScaleCounter(scaleFamily.toArray(), startAt));
350 |
351 | return workingSet.map(item => {
352 | let chordIntervalCandidates = item[0];
353 | let scaleNote = item[1];
354 | let scaleCounter = item[2];
355 | let activeInterval = scaleNote.isScaleNote
356 | ? chordIntervalCandidates.filter(x => x.ord === scaleCounter[1])[0]
357 | : chordIntervalCandidates[0];
358 | if(activeInterval == null) {
359 | activeInterval = chordIntervalCandidates[0];
360 | }
361 |
362 | // if(chordSelected) {
363 | // console.log("chordIndex: " + chordIndex +
364 | // ", scaleNote.isScaleNote: " + scaleNote.isScaleNote +
365 | // ", scaleNote.notenumber: " + scaleNote.noteNumber +
366 | // ", scaleCounter: " + scaleCounter +
367 | // ", activeInterval: " + getIntervalName(activeInterval) +
368 | // ", toggle: " + calculateToggle(activeInterval, scaleNote, chordSelected, toggledIndexes, chordIntervals));
369 | // }
370 |
371 | return {
372 | scaleNote: scaleNote,
373 | chordInterval: activeInterval,
374 | intervalName: getIntervalName(activeInterval),
375 | isChordRoot: chordSelected && activeInterval.ord === 0 && activeInterval.type === 0,
376 | toggle: calculateToggle(activeInterval, scaleNote, chordSelected, toggledIndexes, chordIntervals),
377 | midiToggle: (toggledMidiNotes & (2**scaleNote.note.index)) != 0
378 | };
379 | });
380 | }
381 |
382 | function buildScaleCounter(diatonic: boolean[], startAt:number = 0): [boolean, number][] {
383 | let noteCount = diatonic.filter(x => x).length;
384 | let i=(noteCount - startAt) % noteCount;
385 | return diatonic.map(isNote => {
386 | if(isNote) {
387 | let value = <[boolean, number]>[true, i];
388 | i = (i+1) % noteCount;
389 | return value;
390 | }
391 | return <[boolean, number]>[false, 0];
392 | });
393 | }
394 |
395 | let romanNumeral: Array = ['i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii'];
396 |
397 | export function generateChordNumbers(scaleNotes: ScaleNote[], mode: Mode, scaleFamily: mod.Mod): Chord[] {
398 | return scaleNotes.map((scaleNote, i) => {
399 | if(scaleNote.isScaleNote) {
400 | let roman = romanNumeral[scaleNote.noteNumber];
401 | let nodes = generateNodes(scaleNotes, mode, scaleNote.note.index, [], 0, 0, scaleFamily);
402 | let diminished = "";
403 | let type: ChordType = ChordType.Minor;
404 | // does it have a diminished 5th?
405 | if(nodes.some(x => x.scaleNote.isScaleNote && x.chordInterval.ord === 4 && x.chordInterval.type === IntervalType.Dim)) {
406 | diminished = "°";
407 | type = ChordType.Diminished;
408 | }
409 | // does it have an augmented 5th?
410 | else if(nodes.some(x => x.scaleNote.isScaleNote && x.chordInterval.ord === 4 && x.chordInterval.type === IntervalType.Aug)) {
411 | diminished = "+";
412 | type = ChordType.Augmented;
413 | }
414 | // does it have a major 3rd?
415 | else if(nodes.some(x => x.scaleNote.isScaleNote && x.chordInterval.ord === 2 && x.chordInterval.type === IntervalType.Maj)) {
416 | roman = roman.toLocaleUpperCase();
417 | type = ChordType.Major;
418 | }
419 | return {
420 | romanNumeral: roman + diminished,
421 | type: type
422 | };
423 | }
424 |
425 | return {
426 | romanNumeral: "",
427 | type: ChordType.Major
428 | };
429 | });
430 | }
431 |
432 | export function calculateToggle(
433 | activeInterval: Interval,
434 | scaleNote: ScaleNote,
435 | chordSelected: boolean,
436 | toggledIndexes: number,
437 | chordIntervals: number[]
438 | ): boolean {
439 | if(toggledIndexes === 0) {
440 | return chordSelected && scaleNote.isScaleNote && chordIntervals.some(x => activeInterval.ord === x);
441 | }
442 | return (toggledIndexes & (2**scaleNote.note.index)) != 0;
443 | }
444 |
445 | export function fifths(): Array {
446 | let indexes: Array = [];
447 | let current: number = 0;
448 | for (let i: number = 0; i < 12; i++) {
449 | indexes.push(current);
450 | current = (current + 7) % 12;
451 | }
452 | return indexes;
453 | }
454 |
455 | export function chromatic(): Array {
456 | let indexes: Array = [];
457 | for (let i: number = 0; i < 12; i++) {
458 | indexes.push(i);
459 | }
460 | return indexes;
461 | }
462 | }
--------------------------------------------------------------------------------
/src/permalink-module.ts:
--------------------------------------------------------------------------------
1 | namespace permalink {
2 |
3 | let currentState: state.State = null;
4 |
5 | export function init(): void {
6 | events.stateChange.subscribe(x => currentState = x.state);
7 | }
8 |
9 | export function populatePermalinkText(): void {
10 | let permalink = generatePermalink();
11 | let inputbox = document.getElementById("permalink-text") as HTMLInputElement
12 | inputbox.value = permalink;
13 | inputbox.focus;
14 | inputbox.select;
15 | inputbox.setSelectionRange(0, 99999);
16 | document.execCommand("copy");
17 | }
18 |
19 | // create querystring from state
20 | export function generatePermalink(): string {
21 | if(currentState === null) {
22 | throw "No stateChange event published before querystring requested";
23 | }
24 |
25 | let params = new URLSearchParams();
26 |
27 | // only copy state that's different from default
28 | Object.keys(currentState).forEach(key => {
29 | if(currentState[key] !== state.defaultState[key]) {
30 | params.append(key, currentState[key]);
31 | }
32 | });
33 |
34 | return `${location.protocol}//${location.host}${location.pathname}?${params.toString()}`;
35 | }
36 |
37 | // update state from querystring
38 | export function getState(existingState: state.State): state.State {
39 |
40 | let queryString = location.search;
41 | let params = new URLSearchParams(queryString);
42 |
43 | Object.keys(existingState).forEach(x => {
44 | let value = params.get(x);
45 | if(value == null) return;
46 |
47 | switch (typeof existingState[x]) {
48 | case 'boolean':
49 | existingState[x] = (value === "true");
50 | break;
51 | case 'number':
52 | existingState[x] = parseInt(value);
53 | break;
54 | case 'object':
55 | existingState[x] = JSON.parse("[" + value + "]");
56 | break;
57 | case 'string':
58 | existingState[x] = value;
59 | break;
60 | }
61 |
62 | console.log(`${x} -> ${value}, ${typeof existingState[x]}, ${existingState[x]}`);
63 | });
64 |
65 | return existingState;
66 | }
67 |
68 | // test function
69 | export function getCurrentState(): void {
70 | let newState = getState(currentState);
71 | }
72 | }
--------------------------------------------------------------------------------
/src/scale-family-module.ts:
--------------------------------------------------------------------------------
1 | namespace scaleFamily {
2 |
3 | export function init() {
4 | d3.select("#scale-dropdown")
5 | .selectAll("div")
6 | .data(music.scaleFamily)
7 | .enter()
8 | .append("div")
9 | .attr("class", "dropdown-content-item")
10 | .on("click", x => raiseScaleFamilyChangedEvent(x))
11 | .text(x => x.name);
12 | }
13 |
14 | function raiseScaleFamilyChangedEvent(scaleFamily: music.ScaleFamily): void{
15 | events.scaleFamilyChange.publish({
16 | scaleFamily: scaleFamily
17 | });
18 | }
19 | }
--------------------------------------------------------------------------------
/src/settings-module.ts:
--------------------------------------------------------------------------------
1 |
2 | namespace settings {
3 |
4 | export function init(): void {
5 | events.leftHandedChange.subscribe(e => {
6 | let checkbox = document.getElementById("left-handed-checkbox")
7 | checkbox.checked = e.isLeftHanded;
8 | });
9 | events.flipNutChange.subscribe(e => {
10 | let checkbox = document.getElementById("flip-nut-checkbox")
11 | checkbox.checked = e.isNutFlipped;
12 | });
13 | events.setCToNoon.subscribe(e => {
14 | let checkbox = document.getElementById("set-c-to-noon-checkbox")
15 | checkbox.checked = e.isC;
16 | });
17 | events.fretboardLabelChange.subscribe(e => {
18 | let selected = "fb-note-text" + String(e.labelType);
19 | let radio = document.getElementById(selected);
20 | radio.checked = true;
21 | });
22 | }
23 |
24 | export function onLeftHandedClick(e:HTMLInputElement) {
25 | events.leftHandedChange.publish({ isLeftHanded: e.checked });
26 | }
27 |
28 | export function onFlipNut(e:HTMLInputElement) {
29 | events.flipNutChange.publish( { isNutFlipped: e.checked });
30 | }
31 |
32 | export function onSetCToNoon(e:HTMLInputElement) {
33 | events.setCToNoon.publish({ isC: e.checked });
34 | }
35 |
36 | export function onFbNoteTextClick(e:HTMLInputElement) {
37 | events.fretboardLabelChange.publish({ labelType: parseInt(e.value) })
38 | }
39 | }
--------------------------------------------------------------------------------
/src/state-module.ts:
--------------------------------------------------------------------------------
1 | namespace state {
2 |
3 | export interface State {
4 | index: number;
5 | naturalIndex: number;
6 | chordIndex: number;
7 | chordIntervals: number[];
8 | toggledIndexes: number;
9 | scaleFamilyIndex: number;
10 | modeIndex: number;
11 | midiToggledIndexes: number;
12 | isLeftHanded: boolean;
13 | isNutFlipped: boolean;
14 | fretboardLabelType: events.FretboardLabelType;
15 | circleIsCNoon: boolean;
16 | tuningIndex: number;
17 | }
18 |
19 | // default initial state
20 | export const defaultState: State = {
21 | index: 3, // C
22 | naturalIndex: 3, // C
23 | chordIndex: -1, // no chord
24 | chordIntervals: [0, 2, 4], // standard triad
25 | toggledIndexes: 0, // index bitflag
26 | scaleFamilyIndex: 0, // diatornic
27 | modeIndex: 0, // major
28 | midiToggledIndexes: 0,
29 | isLeftHanded: false,
30 | isNutFlipped: false,
31 | fretboardLabelType: events.FretboardLabelType.NoteName,
32 | circleIsCNoon: true,
33 | tuningIndex: 0,
34 | }
35 |
36 | let current: State = {
37 | index: defaultState.index,
38 | naturalIndex: defaultState.naturalIndex,
39 | chordIndex: defaultState.chordIndex,
40 | chordIntervals: defaultState.chordIntervals,
41 | toggledIndexes: defaultState.toggledIndexes,
42 | scaleFamilyIndex: defaultState.scaleFamilyIndex,
43 | modeIndex: defaultState.modeIndex,
44 | midiToggledIndexes: defaultState.midiToggledIndexes,
45 | isLeftHanded: defaultState.isLeftHanded,
46 | isNutFlipped: defaultState.isNutFlipped,
47 | fretboardLabelType: defaultState.fretboardLabelType,
48 | circleIsCNoon: defaultState.circleIsCNoon,
49 | tuningIndex: defaultState.tuningIndex,
50 | };
51 |
52 | export function init() {
53 |
54 | try{
55 | let cookieState = cookies.readCookie2();
56 | if(cookieState !== null) {
57 | current = cookieState;
58 | }
59 | }
60 | catch(e) {
61 | // ignore the invalid cookie:
62 | }
63 |
64 | // update current state based on querystring.
65 | current = permalink.getState(current);
66 |
67 | // lets remember this while we reset everything.
68 | let tempChordIndex = current.chordIndex;
69 | let tempToggledIndexes = current.toggledIndexes;
70 |
71 | let scaleFamily = music.scaleFamily.find(x => x.index == current.scaleFamilyIndex);
72 | if(!scaleFamily) {
73 | throw "scaleFamily is " + scaleFamily + ", current.scaleFamilyIndex = " + current.scaleFamilyIndex;
74 | }
75 | let mode = scaleFamily.modes.find(x => x.index == current.modeIndex);
76 | if(!mode) {
77 | throw "mode is " + mode + "current.modeIndex" + current.modeIndex;
78 | }
79 |
80 | // publish scale and mode
81 | events.scaleFamilyChange.publish({ scaleFamily: scaleFamily });
82 | events.modeChange.publish({ mode: mode });
83 | events.chordIntervalChange.publish( { chordIntervals: current.chordIntervals });
84 |
85 | // subscriptions
86 | events.tonicChange.subscribe(tonicChanged);
87 | events.modeChange.subscribe(modeChanged);
88 | events.chordChange.subscribe(chordChanged);
89 | events.toggle.subscribe(toggle);
90 | events.chordIntervalChange.subscribe(chordIntervalChanged);
91 | events.scaleFamilyChange.subscribe(scaleFamilyChanged);
92 | events.midiNote.subscribe(midiNote);
93 |
94 | // publish tonic and chord
95 | events.tonicChange.publish({ noteSpec: music.createNoteSpec(current.naturalIndex, current.index) });
96 | events.chordChange.publish({ chordIndex: tempChordIndex });
97 | // restore toggles
98 | current.toggledIndexes = tempToggledIndexes;
99 | updateScale();
100 |
101 | // publish settings
102 | events.leftHandedChange.publish({ isLeftHanded: current.isLeftHanded });
103 | events.flipNutChange.publish( { isNutFlipped: current.isNutFlipped });
104 | events.fretboardLabelChange.publish({ labelType: current.fretboardLabelType })
105 | events.setCToNoon.publish({ isC: current.circleIsCNoon });
106 | events.tuningChange.publish({ index: current.tuningIndex });
107 |
108 | // subscribe to settings changes
109 | events.leftHandedChange.subscribe(leftHandedChange);
110 | events.flipNutChange.subscribe(flipNutChange);
111 | events.fretboardLabelChange.subscribe(fretboardLabelChange)
112 | events.setCToNoon.subscribe(setCToNoon);
113 | events.tuningChange.subscribe(tuningChange);
114 | }
115 |
116 | function tonicChanged(tonicChangedEvent: events.TonicChangedEvent): void {
117 | current.index = tonicChangedEvent.noteSpec.index;
118 | current.naturalIndex = tonicChangedEvent.noteSpec.natural.index;
119 | current.chordIndex = -1;
120 | updateScale();
121 | }
122 |
123 | function modeChanged(modeChangedEvent: events.ModeChangedEvent): void {
124 | current.modeIndex = modeChangedEvent.mode.index;
125 | current.chordIndex = -1;
126 | updateScale();
127 | }
128 |
129 | function chordChanged(chordChangedEvent: events.ChordChangeEvent): void {
130 | if(chordChangedEvent.chordIndex === current.chordIndex) {
131 | current.chordIndex = -1
132 | }
133 | else {
134 | current.chordIndex = chordChangedEvent.chordIndex;
135 | }
136 | current.toggledIndexes = 0;
137 | updateScale();
138 | }
139 |
140 | function toggle(toggleEvent: events.ToggleEvent): void {
141 | current.toggledIndexes = current.toggledIndexes ^ 2**toggleEvent.index;
142 | updateScale();
143 | }
144 |
145 | function chordIntervalChanged(chordIntervalChangedEvent: events.ChordIntervalChangeEvent): void {
146 | current.chordIntervals = chordIntervalChangedEvent.chordIntervals;
147 | current.toggledIndexes = 0;
148 | updateScale();
149 | }
150 |
151 | function scaleFamilyChanged(scaleFamilyChangedEvent: events.ScaleFamilyChangeEvent): void {
152 | current.scaleFamilyIndex = scaleFamilyChangedEvent.scaleFamily.index;
153 | current.modeIndex = scaleFamilyChangedEvent.scaleFamily.defaultModeIndex;
154 | current.chordIndex = -1
155 | updateScale();
156 | }
157 |
158 | function midiNote(midiNoteEvent: events.MidiNoteEvent): void {
159 | current.midiToggledIndexes = midiNoteEvent.toggledIndexes;
160 | updateScale();
161 | }
162 |
163 | // setttings event handlers
164 |
165 | function leftHandedChange(leftHandedChangeEvent: events.LeftHandedFretboardEvent): void {
166 | current.isLeftHanded = leftHandedChangeEvent.isLeftHanded;
167 | publishStateChange();
168 | }
169 |
170 | function flipNutChange(flipNutChangeEvent: events.FlipNutEvent): void {
171 | current.isNutFlipped = flipNutChangeEvent.isNutFlipped;
172 | publishStateChange();
173 | }
174 |
175 | function fretboardLabelChange(fretboardLabelChangeEvent: events.FretboardLabelChangeEvent): void {
176 | current.fretboardLabelType = fretboardLabelChangeEvent.labelType;
177 | publishStateChange();
178 | }
179 |
180 | function setCToNoon(setCToNoonEvent: events.SetCToNoonEvent): void {
181 | current.circleIsCNoon = setCToNoonEvent.isC;
182 | publishStateChange();
183 | }
184 |
185 | function tuningChange(tuningChangedEvent: events.TuningChangedEvent): void {
186 | current.tuningIndex = tuningChangedEvent.index;
187 | publishStateChange();
188 | }
189 |
190 | function updateScale(): void {
191 |
192 | let scaleFamily = music.scaleFamily.find(x => x.index == current.scaleFamilyIndex);
193 | if(!scaleFamily) {
194 | throw "scaleFamily is " + scaleFamily + ", current.scaleFamilyIndex = " + current.scaleFamilyIndex;
195 | }
196 | let mode = scaleFamily.modes.find(x => x.index == current.modeIndex);
197 | if(!mode) {
198 | throw "mode is " + mode + "current.modeIndex" + current.modeIndex;
199 | }
200 | let noteSpec = music.createNoteSpec(current.naturalIndex, current.index);
201 |
202 | let nodes = music.generateScaleShim(
203 | noteSpec,
204 | mode,
205 | current.chordIndex,
206 | current.chordIntervals,
207 | current.toggledIndexes,
208 | current.midiToggledIndexes,
209 | scaleFamily);
210 |
211 | // update togges, because a chord may have been generated.
212 | current.toggledIndexes = nodes
213 | .filter(x => x.toggle)
214 | .map(x => x.scaleNote.note.index)
215 | .reduce((a, b) => a + 2**b, 0);
216 |
217 | events.scaleChange.publish({
218 | nodes: nodes,
219 | mode: mode
220 | });
221 |
222 | publishStateChange();
223 | }
224 |
225 | function publishStateChange(): void {
226 | events.stateChange.publish({
227 | state: current
228 | });
229 | }
230 | }
--------------------------------------------------------------------------------
/src/tonics-module.ts:
--------------------------------------------------------------------------------
1 |
2 | namespace tonics {
3 |
4 | let buttons: d3.Selection;
5 |
6 | interface ButtonData {
7 | readonly noteSpec: music.NoteSpec;
8 | };
9 |
10 | function bg(natural: music.Natural): Array {
11 | let flatIndex = natural.index == 0 ? 11 : natural.index - 1;
12 | let sharpIndex = (natural.index + 1) % 12;
13 | return [
14 | { noteSpec: music.createNoteSpec(natural.index, flatIndex) },
15 | { noteSpec: music.createNoteSpec(natural.index, natural.index) },
16 | { noteSpec: music.createNoteSpec(natural.index, sharpIndex) }
17 | ];
18 | }
19 |
20 | export function init(): void {
21 | let pad = 5;
22 | let buttonHeight = 25;
23 | let svg = d3.select("#modes");
24 |
25 | let tonics = svg.append("g");
26 |
27 | let gs = tonics.selectAll("g")
28 | .data(music.naturals)
29 | .enter()
30 | .append("g")
31 | .attr("transform", function (d, i) { return "translate(0, " + (i * (buttonHeight + pad) + pad) + ")"; })
32 | .selectAll("g")
33 | .data(d => bg(d), indexer)
34 | .enter()
35 | .append("g")
36 | .attr("transform", function (d, i) { return "translate(" + (i * 55) + ", 0)"; });
37 |
38 | buttons = gs
39 | .append("rect")
40 | .attr("x", pad)
41 | .attr("y", 0)
42 | .attr("strokeWidth", 2)
43 | .attr("width", 40)
44 | .attr("height", 25)
45 | .attr("class", d => isSameNoteAsNatural(d.noteSpec) ? "tonic-button tonic-button-grey" : "tonic-button")
46 | .on("click", d => events.tonicChange.publish({ noteSpec: d.noteSpec }));
47 |
48 | gs
49 | .append("text")
50 | .attr("x", pad + 10)
51 | .attr("y", 17)
52 | .text(function (x) { return x.noteSpec.label; })
53 | .attr("class", "tonic-text");
54 |
55 | events.tonicChange.subscribe(listener);
56 | }
57 |
58 | function listener(tonicChanged: events.TonicChangedEvent): void {
59 | let ds: Array = [{
60 | noteSpec: tonicChanged.noteSpec
61 | }];
62 | buttons
63 | .data(ds, indexer)
64 | .attr("class", "tonic-button tonic-button-selected")
65 | .exit()
66 | .attr("class", d => isSameNoteAsNatural(d.noteSpec) ? "tonic-button tonic-button-grey" : "tonic-button");
67 | }
68 |
69 | function indexer(d: ButtonData): string {
70 | return d.noteSpec.label;
71 | }
72 |
73 | function isSameNoteAsNatural(noteSpec: music.NoteSpec): boolean {
74 | return music.naturals.some(x => x.index === noteSpec.index && x.index != noteSpec.natural.index);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/tuning-module.ts:
--------------------------------------------------------------------------------
1 | namespace tuning {
2 |
3 | let guitarDots: Array<[number, number]> = [
4 | [3, 0], // [fret, position]
5 | [5, 0],
6 | [7, 0],
7 | [9, 0],
8 | [12, -1],
9 | [12, 1],
10 | [15, 0]
11 | ];
12 |
13 | // Viola/violin for beginners.
14 | let violaDots: Array<[number, number]> = [
15 | [2, 0], // 1st finger
16 | [4, 0], // 2nd finger
17 | [5, 0], // 3rd finger
18 | [7, 0], // 4th finger
19 | [12, -1],
20 | [12, 1]
21 | ];
22 |
23 | interface TuningInfo {
24 | readonly tuning: string;
25 | readonly dots: Array<[number, number]>;
26 | readonly description: string;
27 | }
28 |
29 | export interface Tuning {
30 | readonly index: number;
31 | readonly tuning: string;
32 | readonly dots: Array<[number, number]>;
33 | readonly description: string;
34 | readonly notes: Array;
35 | }
36 |
37 | let tuningInfos: Array = [
38 | { tuning: "EADGBE", dots: guitarDots, description: "Guitar Standard" },
39 | { tuning: "EADGCF", dots: guitarDots, description: "All Fourths" },
40 | { tuning: "CGDAEB", dots: guitarDots, description: "All Fifths" },
41 | { tuning: "BFBFBF", dots: guitarDots, description: "Augmented Fourths" },
42 | { tuning: "DADGBE", dots: guitarDots, description: "Guitar Drop D" },
43 | { tuning: "DADGAD", dots: guitarDots, description: "Celtic" },
44 | { tuning: "CGDAEG", dots: guitarDots, description: "Guitar Fripp NST" },
45 | { tuning: "BEADGBE", dots: guitarDots, description: "Guitar 7 string" },
46 | { tuning: "DABEAB", dots: guitarDots, description: "Guitar Portuguese" },
47 | { tuning: "DGDGBD", dots: guitarDots, description: "Guitar Open G" },
48 | { tuning: "EADGDG", dots: guitarDots, description: "Guitar Convert" },
49 | { tuning: "E♭A♭D♭G♭B♭E♭", dots: guitarDots, description: "Guitar E♭ (Hendrix)" },
50 |
51 | { tuning: "BEADF♯B", dots: guitarDots, description: "Baritone B" },
52 | { tuning: "ADGCEA", dots: guitarDots, description: "Baritone A" },
53 |
54 | { tuning: "EADG", dots: guitarDots, description: "Bass Standard" },
55 | { tuning: "DADG", dots: guitarDots, description: "Bass Drop D" },
56 | { tuning: "EADGC", dots: guitarDots, description: "Bass 5 Strings Standard High" },
57 | { tuning: "BEADG", dots: guitarDots, description: "Bass 5 Strings Standard Low" },
58 | { tuning: "BEADGC", dots: guitarDots, description: "Bass 6 Strings Standard" },
59 | { tuning: "BEADGCF", dots: guitarDots, description: "Bass 7 Strings Standard" },
60 |
61 | { tuning: "DGBD", dots: guitarDots, description: "Banjo" },
62 | { tuning: "DGBD", dots: guitarDots, description: "Cavaquinho"},
63 | { tuning: "GCEA", dots: guitarDots, description: "Ukulele C" },
64 | { tuning: "CGDA", dots: violaDots, description: "Cello" },
65 | { tuning: "GDAE", dots: violaDots, description: "Violin" },
66 | { tuning: "CGDA", dots: violaDots, description: "Viola" },
67 | ]
68 |
69 | export let tunings: Array = [];
70 |
71 | export function parseTuning(tuning: string) : Array {
72 | let tokens: Array = [];
73 | let result: Array = [];
74 |
75 | let tokenIndex = 0;
76 | let lastWasChar = false;
77 |
78 | for(let i:number =0; i < tuning.length; i++) {
79 | let noteChar = tuning.charAt(i);
80 | if("ABCDEFG".indexOf(noteChar) >= 0) {
81 | tokens[tokenIndex] = noteChar;
82 | tokenIndex++;
83 | lastWasChar = true;
84 | }
85 | else if("♯♭".indexOf(noteChar) >= 0 && lastWasChar) {
86 | tokens[tokenIndex-1] = tokens[tokenIndex-1] + noteChar;
87 | lastWasChar = false;
88 | }
89 | else {
90 | throw "Invalid tuning char";
91 | }
92 | }
93 |
94 | for(let token of tokens){
95 | let noteName = music.noteNames.filter(x => x.name === token);
96 | if(noteName.length != 1) {
97 | throw "Invalid token";
98 | }
99 | result.push(noteName[0].index);
100 | }
101 |
102 | return result;
103 | }
104 |
105 | export function init() {
106 |
107 | let index: number = 0;
108 | for(let info of tuningInfos) {
109 | let tuning: Tuning = {
110 | index: index,
111 | tuning: info.tuning,
112 | dots: info.dots,
113 | description: info.description,
114 | notes: parseTuning(info.tuning)
115 | };
116 | tunings.push(tuning);
117 | index++;
118 | }
119 |
120 | d3.select("#tuning-dropdown")
121 | .selectAll("div")
122 | .data(tunings)
123 | .enter()
124 | .append("div")
125 | .attr("class", "dropdown-content-item")
126 | .on("click", x => raiseTuningChangedEvent(x))
127 | .text(x => x.tuning + " " + x.description);
128 |
129 | raiseTuningChangedEvent(tunings[0]);
130 | }
131 |
132 | function raiseTuningChangedEvent(tuning: Tuning): void{
133 | events.tuningChange.publish({
134 | index: tuning.index
135 | });
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "none",
5 | "sourceMap": true,
6 | "outDir": "docs",
7 | "outFile": "docs/gtr-cof.js",
8 | "types": []
9 | },
10 | "files": [
11 | "src/menu-module.ts",
12 | "src/mod-module.ts",
13 | "src/events-module.ts",
14 | "src/cookie-module.ts",
15 | "src/music-module.ts",
16 | "src/state-module.ts",
17 | "src/cof-module.ts",
18 | "src/tonics-module.ts",
19 | "src/chord-interval-module.ts",
20 | "src/modes-module.ts",
21 | "src/gtr-module.ts",
22 | "src/tuning-module.ts",
23 | "src/settings-module.ts",
24 | "src/scale-family-module.ts",
25 | "src/midi-module.ts",
26 | "src/permalink-module.ts",
27 | "src/gtr-cof.ts"
28 | ]
29 | }
--------------------------------------------------------------------------------