diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..01c7fd7fd
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,15 @@
+module.exports = {
+ env: {
+ browser: true,
+ es2021: true,
+ },
+ extends: [
+ 'airbnb-base',
+ ],
+ parserOptions: {
+ ecmaVersion: 12,
+ sourceType: 'module',
+ },
+ rules: {
+ },
+};
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..37944d1df
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,151 @@
+# Created by https://www.toptal.com/developers/gitignore/api/macos,node,vscode
+# Edit at https://www.toptal.com/developers/gitignore?templates=macos,node,vscode
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Node ###
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+.env*.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+### vscode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# End of https://www.toptal.com/developers/gitignore/api/macos,node,vscode
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..5f17fec9e
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,142 @@
+# ๐ ์งํ์ฒ ๋
ธ์ ๋ ๋ฏธ์
+
+## ๐ ๋ฏธ์
์๊ฐ
+์งํ์ฒ ์ญ, ๋
ธ์ , ๊ตฌ๊ฐ์ ๊ด๋ฆฌ ํ๊ณ ์กฐํํ๋ ์๋น์ค
+
+## โ๏ธ ์คํ ๋ฐฉ๋ฒ
+```
+git clone https://github.com/sunhpark42/javascript-subway-map-precourse.git; cd javascript-subway-map-precourse; git checkout sunhpark42; npx http-server
+```
+* ๋ง์ฝ http-server ๊ฐ ์๋ค๋ฉด `npm install http-server`๋ฅผ ์ค์นํ๊ณ ์ฌ ์ํ.
+
+## ๐ป ๊ฐ๋ฐ
+### ๊ฐ๋ฐ๊ธฐ๊ฐ
+ * 2020.12.09 - 2020.12.15
+### ์ฌ์ฉ ์ธ์ด
+
+
+
+
+
+## โ ๊ธฐ๋ฅ ๋ช
์ธ
+### ๋ฉ๋ด
+1. ์ด๋ฒคํธ ๊ด๋ฆฌ
+ - [x] '1. ์ญ ๊ด๋ฆฌ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ญ ๊ด๋ฆฌ ๋ทฐ๋ฅผ ์์ฒญํ๋ค.
+ - [x] '2. ๋
ธ์ ๊ด๋ฆฌ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๋
ธ์ ๊ด๋ฆฌ ๋ทฐ๋ฅผ ์์ฒญํ๋ค.
+ - [x] '3. ๊ตฌ๊ฐ ๊ด๋ฆฌ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๊ตฌ๊ฐ ๊ด๋ฆฌ ๋ทฐ๋ฅผ ์์ฒญํ๋ค.
+ - [x] '4. ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๋ทฐ๋ฅผ ์์ฒญํ๋ค.
+2. ํ๋ฉด ํ์
+ - [x] ์ญ ๊ด๋ฆฌ ๋ทฐ๋ฅผ ํ์ํ๋ค.
+ - [x] ๋
ธ์ ๊ด๋ฆฌ ๋ทฐ๋ฅผ ํ์ํ๋ค.
+ - [x] ๊ตฌ๊ฐ ๊ด๋ฆฌ ๋ทฐ๋ฅผ ํ์ํ๋ค.
+ - [x] ์งํ์ฒ ๋
ธ์ ๋ ์ถ๋ ฅ ๋ทฐ๋ฅผ ํ์ํ๋ค.
+
+### ์งํ์ฒ ์ญ ๊ด๋ฆฌ ๊ธฐ๋ฅ
+1. ์ด๋ฒคํธ ๊ด๋ฆฌ (Controller)
+ - [x] '์ญ ์ถ๊ฐ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ญ ๋ชฉ๋ก ์ถ๊ฐ์, ์ญ ๋ชฉ๋ก ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+ - [x] ์์ธ ์ฌํญ์ ํด๋นํ๋ ๊ฒฝ์ฐ, ์๋ฌ๋ฉ์ธ์ง ์ถ๋ ฅ ๋ฐ ์ญ ์
๋ ฅ ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๊ณ , ์ถ๊ฐ ๋ฐ ๋ชฉ๋ก ๊ฐฑ์ ์ ํ์ง ์๋๋ค.
+ - [x] '์ญ์ ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์ ์์ฒญํ๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์์ 'ํ์ธ' ๋ฒํผ์ ๋๋ฅด๋ฉด ์ญ ๋ชฉ๋ก์์ ํด๋น ์ญ ์ญ์ ์, ์ญ ๋ชฉ๋ก ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์์ '์ทจ์' ๋ฒํผ์ ๋๋ฅด๋ฉด ์๋ฌด๊ฒ๋ ํ์ง์๋๋ค.
+2. ํ๋ฉด ํ์ (View)
+ - [x] ์ ์ฒด ํ๋ฉด์ ํ์ํ๋ค (์ญ ์
๋ ฅ ๋ฐ ์ญ ๋ชฉ๋ก)
+ - [x] ์ญ ์
๋ ฅ ํ๋ฉด์ ๊ฐฑ์ ํ๋ค.
+ - [x] ์ญ ๋ชฉ๋ก ํ๋ฉด์ ๊ฐฑ์ ํ๋ค.
+ - [x] ์
๋ ฅ๋ ๊ฐ์ด ์๋ชป๋์์์ ๊ฒฝ๊ณ ํ๋ ์ฐฝ์ ๋์ด๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์ ๋์ด๋ค.
+ - [x] ํ์ธ ๋ฐ ์ทจ์ ๋ฒํผ
+3. ์ฃผ์ ๊ธฐ๋ฅ (Model)
+ - [x] ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์ญ ์ด๋ฆ์ด ์ ํจํ์ง ํ์ธํ๋ค.
+ - [x] ์์ธ ์ฒ๋ฆฌ
+ 1. ์ญ ์ด๋ฆ์ด 2์ ๋ฏธ๋ง์ธ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ.
+ 2. ์ญ ์ด๋ฆ์ ๊ณต๋ฐฑ์ด ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ 3. ์ญ ์ด๋ฆ์ ๋ชจ์์ด๋ ์์๋ง์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ ๋ถ๋ถ์ด ์์ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ.
+ 4. ์ญ ์ด๋ฆ์ ์๋ฌธ์ด ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ(ํ๊ตญ ์งํ์ฒ ๋ก ๊ฐ์ ํจ)
+ 5. ์ญ ์ด๋ฆ์ ํน์ ๋ฌธ์๊ฐ ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ - [x] ๊ธฐ์กด ์ญ ๋ชฉ๋ก์ ์ค๋ณต๋ ์ด๋ฆ์ด ์๋์ง ํ์ธํ๋ค.
+ - [x] ์ฌ์ฉ์๊ฐ ์ญ์ ํ๊ธฐ๋ก ์ ํํ ์ญ์ด ๋
ธ์ ์ ์๋์ง ํ์ธํ๋ค.
+ - [x] ์ญ์ ์ญ ๋ชฉ๋ก์ ์ถ๊ฐํ๋ค.
+ - [x] ํด๋น ์ญ์ด ํฌํจ๋ ๋
ธ์ ์ด ์ต์ ์ญ ์กฐ๊ฑด (2๊ฐ ์ด์)์ ๋ง์กฑํ๋์ง ํ์ธ
+ - [x] ์ญ์ ์ญ ๋ชฉ๋ก์์ ์ญ์ ํ๋ค.
+ - [x] ์ญ์ ํด๋น ์ญ์ด ํฌํจ๋ ๋
ธ์ ์์ ์ญ์ ํ๋ค.
+ - [x] ๊ณ ๋ คํด์ผ ํ ์ฌํญ
+ - ๋ง์ฝ, ์ญ์ด ํ๋๋ ์๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊ฒ์ธ์ง?
+ - ์ต์ ์ญ์ ๊ฐ์๋ฅผ ์ง์ ํ ๊ฒ์ธ์ง
+ - ์ญ์ ํ์ ๋ ๊ฐ ๋
ธ์ ์ 2๊ฐ ์ดํ๊ฐ ๋๋ ๊ณณ์ด ์์ผ๋ฉด ์ญ์ ๋ฅผ ํ์ง ์์ ๊ฒ์ธ์ง. => ์ฑํ
+ - ์ญ์ ๊ฐ ๋์ง ์๋ ๊ฒฝ์ฐ ๊ฒฝ๊ณ ์ฐฝ์ ๋์ธ ๊ฒ์ธ์ง? => ์ฑํ
+
+### ์งํ์ฒ ๋
ธ์ ๊ด๋ฆฌ ๊ธฐ๋ฅ
+1. ์ด๋ฒคํธ ๊ด๋ฆฌ (Controller)
+ - [x] '๋
ธ์ ์ถ๊ฐ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๋
ธ์ ๋ชฉ๋ก ์ถ๊ฐ์, ๋
ธ์ ๋ชฉ๋ก ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+ - [x] '์ญ์ ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์ ์์ฒญํ๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์์ 'ํ์ธ' ๋ฒํผ์ ๋๋ฅด๋ฉด ๋
ธ์ ๋ชฉ๋ก์์ ํด๋น ๋
ธ์ ์ญ์ ์, ๋
ธ์ ๋ชฉ๋ก ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์์ '์ทจ์' ๋ฒํผ์ ๋๋ฅด๋ฉด ์๋ฌด๊ฒ๋ ํ์ง ์๋๋ค.
+2. ํ๋ฉด ํ์ (View)
+ - [x] ์ ์ฒด ํ๋ฉด์ ํ์ํ๋ค. (๋
ธ์ ์
๋ ฅ ๋ฐ ๋
ธ์ ๋ชฉ๋ก)
+ - [x] ๋
ธ์ ์
๋ ฅ ํ๋ฉด์ ๊ฐฑ์ ํ๋ค.
+ - [x] ๋
ธ์ ๋ชฉ๋ก ํ๋ฉด์ ๊ฐฑ์ ํ๋ค.
+ - [x] ์
๋ ฅ๋ ๊ฐ์ด ์๋ชป๋์์์ ๊ฒฝ๊ณ ํ๋ ์ฐฝ์ ๋์ด๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์ ๋์ด๋ค.
+ * ํ์ธ ๋ฐ ์ทจ์ ๋ฒํผ
+3. ์ฃผ์ ๊ธฐ๋ฅ (Model)
+ - [x] ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋
ธ์ ์ด๋ฆ์ด ์ ํจํ์ง ํ์ธํ๋ค.
+ - [x] ์์ธ ์ฒ๋ฆฌ
+ 1. ๋
ธ์ ์ด๋ฆ์ด 2์ ๋ฏธ๋ง์ธ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ 2. ๋
ธ์ ์ด๋ฆ์ ๊ณต๋ฐฑ์ด ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ 3. ๋
ธ์ ์ด๋ฆ์ ๋ชจ์์ด๋ ์์๋ง์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ ๋ถ๋ถ์ด ์์ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ 4. ๋
ธ์ ์ด๋ฆ์ ์๋ฌธ์ด ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ (ํ๊ตญ ์งํ์ฒ ๋ก ๊ฐ์ ํจ)
+ 5. ๋
ธ์ ์ด๋ฆ์ ํน์ ๋ฌธ์๊ฐ ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ - [x] ์์ธ ์ฌํญ์ ํด๋นํ๋ ๊ฒฝ์ฐ ์ญ ์
๋ ฅ ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๊ณ , ์ถ๊ฐ ๋ฐ ๋ชฉ๋ก ๊ฐฑ์ ์ ํ์ง ์๋๋ค.
+ - [x] ๊ธฐ์กด ๋
ธ์ ๋ชฉ๋ก์ ์ค๋ณต๋ ์ด๋ฆ์ด ์๋์ง ํ์ธํ๋ค.
+ - [x] ์ํ ์ข
์ ์ญ๊ณผ ํํ ์ข
์ ์ญ์ด ๊ฐ์ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ ํ๋ค. (๋
ธ์ ์ ์ญ์ด ํ๋๋ฟ์ธ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ)
+ - [x] ๋
ธ์ ์ ๋
ธ์ ๋ชฉ๋ก์ ์ถ๊ฐํ๋ค.
+ - [x] ๋
ธ์ ์ ๋
ธ์ ๋ชฉ๋ก์์ ์ญ์ ํ๋ค.
+ - [x] ํด๋น ๋
ธ์ ์ ๋ํ ์ ๋ณด๋ฅผ ํด๋น ๋
ธ์ ์ ํฌํจ๋์ด ์๋ ์ญ์ ์ ๋ณด์์ ์ญ์ ํ๋ค.
+
+### ์งํ์ฒ ๊ตฌ๊ฐ ๊ด๋ฆฌ ๊ธฐ๋ฅ
+1. ์ด๋ฒคํธ ๊ด๋ฆฌ (Controller)
+ - [x] ๊ฐ '๋
ธ์ ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด ํด๋น ๋
ธ์ ๊ด๋ฆฌ ํ๋ฉด์ ๋์์ค ๊ฒ์ ์์ฒญํ๋ค.
+ - [x] ๋
ธ์ ๊ด๋ฆฌ ํ๋ฉด์์ '๋ฑ๋ก' ๋ฒํผ์ ํด๋ฆญํ ๊ฒฝ์ฐ ๊ตฌ๊ฐ ๋ชฉ๋ก ์ถ๊ฐ์, ๊ตฌ๊ฐ ๋ชฉ๋ก ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+ - [x] ๋
ธ์ ๊ด๋ฆฌ ํ๋ฉด์์ '๋
ธ์ ์์ ์ ๊ฑฐ' ๋ฒํผ์ ํด๋ฆญํ ๊ฒฝ์ฐ ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์ ์์ฒญํ๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์์ 'ํ์ธ' ๋ฒํผ์ ๋๋ฅด๋ฉด ๋
ธ์ ๋ชฉ๋ก์์ ํด๋น ๊ตฌ๊ฐ ์ญ์ ์, ๊ตฌ๊ฐ ๋ชฉ๋ก ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์์ '์ทจ์' ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ตฌ๊ฐ ์
๋ ฅ ํ๋ฉด ๊ฐฑ์ ์ ์์ฒญํ๋ค.
+2. ํ๋ฉด ํ์ (View)
+ - [x] ์์ ํ ๋
ธ์ ํ๋ฉด์ ํ์ํ๋ค.
+ - [x] ๋
ธ์ ๊ด๋ฆฌ ํ๋ฉด์ ํ์ํ๋ค.
+ - [x] ๋
ธ์ ๊ด๋ฆฌ ํ๋ฉด์ ๊ฐฑ์ ํ๋ค.
+ - [x] ์
๋ ฅ๋ ๊ฐ์ด ์๋ชป๋์์์ ๊ฒฝ๊ณ ํ๋ ์ฐฝ์ ๋์ด๋ค.
+ - [x] ์ญ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์ฐฝ์ ๋์ด๋ค.
+ * ํ์ธ ๋ฐ ์ทจ์ ๋ฒํผ ์ถ๊ฐ
+3. ์ฃผ์ ๊ธฐ๋ฅ (Model)
+ - [x] ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ์์ ๊ฐ์ด ์ ํจํ์ง ํ์ธํ๋ค.
+ - [x] ์์ธ ์ฒ๋ฆฌ
+ 1. ์์๊ฐ ์์์ธ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ 2. ์์๊ฐ ์ซ์๊ฐ ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ
+ 3. ์์๊ฐ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ (์. 2๊น์ง ์๋๋ฐ 4์ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ)
+ - [x] ๊ธฐ์กด ๋
ธ์ ์ ์ค๋ณต๋ ์ด๋ฆ์ด ์๋์ง ํ์ธํ๋ค.(์ค๋ณต๋ ์ด๋ฆ์ด ์๋ ๊ฒฝ์ฐ ์์ธ์ฒ๋ฆฌ)
+ - [x] ๋
ธ์ ์ ํฌํจ๋ ์ญ์ด ๋๊ฐ ์ดํ์ธ์ง ํ์ธํ๋ค.
+ - [x] ๋
ธ์ ์ ์ญ์ ์ถ๊ฐํ๋ค.
+ - [x] ๋
ธ์ ์์ ์ญ์ ์ญ์ ํ๋ค.
+
+### ์งํ์ฒ ๋
ธ์ ๋ ๊ด๋ฆฌ ๊ธฐ๋ฅ
+1. ํ๋ฉดํ์ (View)
+ - [x] ์ญ ๋ชฉ๋ก์ ์ถ๋ ฅํ๋ค.
+
+### ๊ธฐ๋ฅ ์ฐ๊ฒฐ
+- [x] ๊ธฐ๋ฅ ์ฐ๊ฒฐ
+
+### ๊ธฐํ
+- [x] ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ๊ฐ์ด ์๋ ๊ฒฝ์ฐ default ๊ฐ ์ฝ์
, ์๋ ๊ฒฝ์ฐ ๊ธฐ์กด์ ์คํ ๋ฆฌ์ง ๊ฐ์ ์ด์ฉ.
+ - ์ด๊ฒฝ์ฐ ๋ ์ค ํ๋์ ๊ฐ์ด ์๋ ๊ฒฝ์ฐ์ ๋ชจ๋ ๋ํดํธ ๊ฐ์ผ๋ก ์ค์ ํ๋๋ก ํจ.
+ - ์ญ, ๋
ธ์ ๋ด์ ์ญ ๋ชฉ๋ก๊ฐ์ ์ฐ๊ฒฐ์ฑ ์ ์ง๋ฅผ ์ํจ.
+- [x] ์ญ - ๋
ธ์ ๊ฐ ์์กด์ฑ ์ถ๊ฐํ๊ธฐ
+ - [x] ์ญ์ด ์ญ์ ๋ ๊ฒฝ์ฐ ํด๋น ์ญ์ด ํฌํจ๋ ๋
ธ์ ์์ ์ญ ์ญ์
+- [x] ๊ฐ ์ํฉ๋ณ ์๋ฌ ๋ฉ์ธ์ง ์ถ๊ฐ
+
+## โ๏ธ ์ปค๋ฐ ๊ท์น
+* feat : ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
+* fix : ๋ฒ๊ทธ ์์
+* docs : ๋ฌธ์์ ์์
+* style : ์ฝ๋์ ์์ ์์ด ์คํ์ผ๋ง ๋ณ๊ฒฝ
+* refactor : ์ฝ๋๋ฅผ ๋ฆฌํํ ๋ง
+* test : Test ๊ด๋ จํ ์ฝ๋์ ์ถ๊ฐ, ์์
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..43c5df79f
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1522 @@
+{
+ "name": "javascript-subway-map-precourse",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ }
+ }
+ },
+ "@eslint/eslintrc": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
+ "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ }
+ },
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-includes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz",
+ "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1",
+ "get-intrinsic": "^1.0.1",
+ "is-string": "^1.0.5"
+ }
+ },
+ "array.prototype.flat": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
+ "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1"
+ }
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "call-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz",
+ "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.0"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "confusing-browser-globals": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz",
+ "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
+ "dev": true
+ },
+ "contains-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "debug": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+ "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "7.15.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz",
+ "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@eslint/eslintrc": "^0.2.2",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.2.0",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^6.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz",
+ "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.2"
+ }
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+ "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "resolve": "^1.13.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
+ "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "pkg-dir": "^2.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.22.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
+ "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.1",
+ "array.prototype.flat": "^1.2.3",
+ "contains-path": "^0.1.0",
+ "debug": "^2.6.9",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "^0.3.4",
+ "eslint-module-utils": "^2.6.0",
+ "has": "^1.0.3",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.1",
+ "read-pkg-up": "^2.0.0",
+ "resolve": "^1.17.0",
+ "tsconfig-paths": "^3.9.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "isarray": "^1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
+ "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
+ "dev": true
+ },
+ "espree": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+ "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "file-entry-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz",
+ "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz",
+ "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz",
+ "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+ "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
+ "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
+ "dev": true
+ },
+ "is-core-module": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
+ "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-negative-zero": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+ "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "object-inspect": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
+ "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz",
+ "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1",
+ "has": "^1.0.3"
+ }
+ },
+ "object.values": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz",
+ "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1",
+ "has": "^1.0.3"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "regexpp": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
+ "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.1.0",
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "semver": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+ "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
+ "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz",
+ "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz",
+ "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "tsconfig-paths": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+ "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
+ "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..1f229c938
--- /dev/null
+++ b/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "javascript-subway-map-precourse",
+ "version": "1.0.0",
+ "description": "WooWa_course season 3, last mission ",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sunhpark42/javascript-subway-map-precourse.git"
+ },
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/sunhpark42/javascript-subway-map-precourse/issues"
+ },
+ "homepage": "https://github.com/sunhpark42/javascript-subway-map-precourse#readme",
+ "devDependencies": {
+ "eslint": "^7.15.0",
+ "eslint-config-airbnb-base": "^14.2.1",
+ "eslint-plugin-import": "^2.22.1"
+ }
+}
diff --git a/src/LineManger/LineErrorMsg.js b/src/LineManger/LineErrorMsg.js
new file mode 100644
index 000000000..a90657b35
--- /dev/null
+++ b/src/LineManger/LineErrorMsg.js
@@ -0,0 +1,60 @@
+export default class LineErrorMsg {
+ static error(number) {
+ const errorMsg = [
+ this.lengthError(),
+ this.spaceError(),
+ this.koranCharacterError(),
+ this.notKoreanError(),
+ this.specialCharError(),
+ this.duplicateError(),
+ this.onlyNumberError(),
+ this.sameStartEndError(),
+ this.cancelDeleteError(),
+ ];
+ return errorMsg[number * (-1)];
+ }
+
+ /*
+ * INPUT ERROR
+ */
+
+ static lengthError() {
+ return '๋
ธ์ ์ด๋ฆ์ 2๊ธ์ ์ด์์ผ๋ก ์์ฑํด ์ฃผ์ธ์.\n';
+ }
+
+ static spaceError() {
+ return '๋
ธ์ ์ด๋ฆ ์ฌ์ด์๋ ๊ณต๋ฐฑ์ด ๋ค์ด๊ฐ ์ ์์ต๋๋ค.\n';
+ }
+
+ static koranCharacterError() {
+ return '์์ ํ ํ๊ธ ๋จ์ด๋ก ์
๋ ฅํด ์ฃผ์ธ์.\nex. ใ
์ญ (x) ๋ค์ญ (o)\n';
+ }
+
+ static notKoreanError() {
+ return '๋
ธ์ ์ด๋ฆ์ ํ๊ธ๋ก๋ง ์์ฑํ ์ ์์ต๋๋ค.\n';
+ }
+
+ static specialCharError() {
+ return '๋
ธ์ ์ด๋ฆ์๋ ํน์ ๋ฌธ์๊ฐ ๋ค์ด ๊ฐ ์ ์์ต๋๋ค.\n';
+ }
+
+ static duplicateError() {
+ return '์ด๋ฏธ ํด๋น ์ด๋ฆ์ ๋
ธ์ ์ด ์กด์ฌํฉ๋๋ค.\n';
+ }
+
+ static onlyNumberError() {
+ return '๋
ธ์ ์ด๋ฆ์ ์ซ์๋ก๋ง ์ด๋ฃจ์ด ์ง ์ ์์ต๋๋ค.\n';
+ }
+
+ static sameStartEndError() {
+ return '์ํ์ ์ข
์ ๊ณผ ํํ์ ์ข
์ ์ ๊ฐ์ ์ ์์ต๋๋ค.\n';
+ }
+
+ /*
+ * DELETE ERROR
+ */
+
+ static cancelDeleteError() {
+ return '์ญ์ ๋ฅผ ์ทจ์ํ์ต๋๋ค.';
+ }
+}
diff --git a/src/LineManger/LineManagerController.js b/src/LineManger/LineManagerController.js
new file mode 100644
index 000000000..9fa540628
--- /dev/null
+++ b/src/LineManger/LineManagerController.js
@@ -0,0 +1,45 @@
+import LineManagerView from './LineManagerView.js';
+import LineManagerModel from './LineManagerModel.js';
+
+export default class LineManagerController {
+ static buttonEventController() {
+ document.addEventListener('click', (event) => {
+ const eventId = event.target.id;
+ const eventClassName = event.target.className;
+ if (eventId === 'line-add-button') {
+ this.addButtonClicked();
+ }
+ if (eventClassName === 'line-delete-button') {
+ const button = event.path[0];
+ this.deleteButtonClicked(button);
+ }
+ });
+ }
+
+ static addButtonClicked() {
+ const line = document.getElementById('line-name-input').value;
+ const lineStart = document.getElementById('line-start-station-selector').value;
+ const lineEnd = document.getElementById('line-end-station-selector').value;
+ const isValid = LineManagerModel.isValidInput(line, lineStart, lineEnd);
+ if (isValid !== 1) {
+ LineManagerView.alertError(isValid);
+ LineManagerView.lineInputView();
+ return;
+ }
+ LineManagerModel.add(line, lineStart, lineEnd);
+ LineManagerView.lineInputView();
+ LineManagerView.lineTableView();
+ }
+
+ static deleteButtonClicked(button) {
+ const buttons = document.getElementsByClassName('line-delete-button');
+ const buttonsArray = Array.from(buttons);
+ const line = buttons[buttonsArray.indexOf(button)].dataset.deleteTarget;
+ if (!LineManagerView.confirmDelete()) {
+ LineManagerView.alertError(-8);
+ return;
+ }
+ LineManagerModel.delete(line);
+ LineManagerView.lineTableView();
+ }
+}
diff --git a/src/LineManger/LineManagerModel.js b/src/LineManger/LineManagerModel.js
new file mode 100644
index 000000000..063eabf3f
--- /dev/null
+++ b/src/LineManger/LineManagerModel.js
@@ -0,0 +1,93 @@
+export default class LineManagerModel {
+ /*
+ * INPUT CHECK
+ */
+ static isValidInput(line, lineStart, lineEnd) {
+ let isValid = 1;
+ const validationCheckFunction = [
+ this.isValidLength(line),
+ this.isSpace(line),
+ this.iskoreanCharacterError(line),
+ this.isAlpha(line),
+ this.isSpecialChar(line),
+ this.isDuplicated(line),
+ this.isOnlyNumber(line),
+ this.isSame(lineStart, lineEnd),
+ ];
+ validationCheckFunction.forEach((func, index) => {
+ if (func && isValid === 1) {
+ isValid = (-1) * index;
+ }
+ });
+ return isValid;
+ }
+
+ static isValidLength(line) {
+ return line.length < 2;
+ }
+
+ static isSpace(line) {
+ return line.indexOf(' ') > -1;
+ }
+
+ static iskoreanCharacterError(line) {
+ return /[^๊ฐ-ํฃa-z0-9]/.test(line);
+ }
+
+ static isAlpha(line) {
+ return /[a-z]/.test(line);
+ }
+
+ static isSpecialChar(line) {
+ return /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(line);
+ }
+
+ static isDuplicated(line) {
+ const lines = Object.keys(JSON.parse(localStorage.getItem('Lines')));
+ return lines.indexOf(line) > -1;
+ }
+
+ static isOnlyNumber(line) {
+ return !(/[^0-9]/.test(line));
+ }
+
+ static isSame(lineStart, lineEnd) {
+ return lineStart === lineEnd;
+ }
+
+ /*
+ * Add
+ */
+ static add(line, lineStart, lineEnd) {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+
+ lines[line] = { "stations": [lineStart, lineEnd] };
+ if (stations[lineStart].lines.indexOf(line) < 0) {
+ stations[lineStart].lines.push(line);
+ }
+ if (stations[lineEnd].lines.indexOf(line) < 0) {
+ stations[lineEnd].lines.push(line);
+ }
+
+ localStorage.setItem('Lines', JSON.stringify(lines));
+ localStorage.setItem('Stations', JSON.stringify(stations));
+ }
+
+ /*
+ * DELETE
+ */
+ static delete(line) {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ const stationsIncludedByLine = lines[line].stations;
+
+ stationsIncludedByLine.forEach((station) => {
+ stations[station].lines.splice(stations[station].lines.indexOf(line), 1);
+ });
+ delete lines[line];
+
+ localStorage.setItem('Stations', JSON.stringify(stations));
+ localStorage.setItem('Lines', JSON.stringify(lines));
+ }
+}
diff --git a/src/LineManger/LineManagerView.js b/src/LineManger/LineManagerView.js
new file mode 100644
index 000000000..53e5a5a02
--- /dev/null
+++ b/src/LineManger/LineManagerView.js
@@ -0,0 +1,60 @@
+import LineErrorMsg from './LineErrorMsg.js';
+
+export default class LineManagerView {
+ static view() {
+ document.getElementById('sub-view-container').innerHTML = `
+ ๋
ธ์ ์ด๋ฆ
+ ๐ ์งํ์ฒ ๋
ธ์ ๋ชฉ๋ก
+
+
+
+ `;
+ this.lineInputView();
+ this.lineTableView();
+ }
+
+ static lineInputView() {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ document.getElementById('line-input').innerHTML = `
+
+ ์ํ ์ข
์
+
+
+ ํํ ์ข
์
+
+
+ `;
+ }
+
+ static lineTableView() {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ document.getElementById('line-table').innerHTML = `
+
+
+ | ๋
ธ์ ์ด๋ฆ |
+ ์ํ ์ข
์ ์ญ |
+ ํํ ์ข
์ ์ญ |
+ ์ค์ |
+
+ ${Object.keys(lines).map((line) => `
+
+ | ${line} |
+ ${lines[line].stations[0]} |
+ ${lines[line].stations[(lines[line].stations.length - 1)]} |
+ |
+
`).join('')}
+
`;
+ }
+
+ static alertError(errorNum) {
+ alert(LineErrorMsg.error(errorNum));
+ }
+
+ static confirmDelete() {
+ return window.confirm('์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?');
+ }
+}
\ No newline at end of file
diff --git a/src/MapPrintManager/MapPrintManagerView.js b/src/MapPrintManager/MapPrintManagerView.js
new file mode 100644
index 000000000..35eadda4e
--- /dev/null
+++ b/src/MapPrintManager/MapPrintManagerView.js
@@ -0,0 +1,13 @@
+export default class MapPrintManagerView {
+ static view() {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ document.getElementById('sub-view-container').innerHTML = `
+ ${Object.keys(lines).map((line) => `
+
${line}
+
+ ${lines[line].stations.map((station) => `- ${station}
`).join('')}
+
`).join('')}
+
+ `;
+ }
+}
diff --git a/src/Menu/MenuController.js b/src/Menu/MenuController.js
new file mode 100644
index 000000000..8252cb6b6
--- /dev/null
+++ b/src/Menu/MenuController.js
@@ -0,0 +1,21 @@
+import MenuView from './MenuView.js';
+
+export default class MenuController {
+ static buttonEventController() {
+ document.addEventListener('click', (event) => {
+ const eventId = event.target.id;
+ if (eventId === 'station-manager-button') {
+ MenuView.stationManagerView();
+ }
+ if (eventId === 'line-manager-button') {
+ MenuView.lineManagerView();
+ }
+ if (eventId === 'section-manager-button') {
+ MenuView.sectionManagerView();
+ }
+ if (eventId === 'map-print-manager-button') {
+ MenuView.mapPrintManagerView();
+ }
+ });
+ }
+}
diff --git a/src/Menu/MenuView.js b/src/Menu/MenuView.js
new file mode 100644
index 000000000..b5c96e47c
--- /dev/null
+++ b/src/Menu/MenuView.js
@@ -0,0 +1,32 @@
+import StationManagerView from '../StationManger/StationManagerView.js'
+import LineManagerView from '../LineManger/LineManagerView.js';
+import SectionManagerView from '../SectionManager/SectionManagerView.js';
+import MapPrintManagerView from '../MapPrintManager/MapPrintManagerView.js';
+
+export default class MenuView {
+ static menuButtonListView() {
+ document.getElementById('app').innerHTML += `
+
+
+
+
+
+ `;
+ }
+
+ static stationManagerView() {
+ StationManagerView.view();
+ }
+
+ static lineManagerView() {
+ LineManagerView.view();
+ }
+
+ static sectionManagerView() {
+ SectionManagerView.view();
+ }
+
+ static mapPrintManagerView() {
+ MapPrintManagerView.view();
+ }
+}
diff --git a/src/SectionManager/SectionErrorMsg.js b/src/SectionManager/SectionErrorMsg.js
new file mode 100644
index 000000000..1bc1bff03
--- /dev/null
+++ b/src/SectionManager/SectionErrorMsg.js
@@ -0,0 +1,35 @@
+export default class SectionErrorMsg {
+ static error(number) {
+ const errorMsg = [
+ this.lengthError(),
+ this.duplicateError(),
+ this.stationNumberError(),
+ this.cancelDeleteError(),
+ ];
+ return errorMsg[number * (-1)];
+ }
+
+ /*
+ * INPUT ERROR
+ */
+
+ static lengthError() {
+ return '์์๋ 0 ์ด์์ ์ ์๋ง ์
๋ ฅํ ์ ์์ต๋๋ค.\n์ต๋ ์
๋ ฅ ์์ ๋ฒํธ๋ ๋ง์ง๋ง ์์ ๋ฒํธ + 1 ์
๋๋ค.\n';
+ }
+
+ static duplicateError() {
+ return '์ด๋ฏธ ํด๋น ์ญ์ด ๋
ธ์ ์ ์กด์ฌํฉ๋๋ค.\n';
+ }
+
+ /*
+ * DELETE ERROR
+ */
+
+ static stationNumberError() {
+ return 'ํฌํจ๋ ๋
ธ์ ์ด 2๊ฐ ์ดํ๋ก ์ญ ์ญ์ ๊ฐ ๋ถ๊ฐ๋ฅ ํฉ๋๋ค.';
+ }
+
+ static cancelDeleteError() {
+ return '์ญ์ ๋ฅผ ์ทจ์ํ์ต๋๋ค.';
+ }
+}
diff --git a/src/SectionManager/SectionManagerController.js b/src/SectionManager/SectionManagerController.js
new file mode 100644
index 000000000..73c74f507
--- /dev/null
+++ b/src/SectionManager/SectionManagerController.js
@@ -0,0 +1,62 @@
+import SectionManagerView from './SectionManagerView.js';
+import SectionManagerModel from './SectionManagerModel.js';
+
+export default class SectionManagerController {
+ static buttonEventController() {
+ let line = null;
+ document.addEventListener('click', (event) => {
+ const eventId = event.target.id;
+ const eventClassName = event.target.className;
+ if (eventId === 'section-add-button') {
+ this.addButtonClicked(line);
+ } else if (eventClassName === 'section-delete-button') {
+ const button = event.path[0];
+ this.deleteButtonClicked(line, button);
+ } else if (eventClassName === 'section-line-menu-button') {
+ const button = event.path[0];
+ line = this.sectionLineMenuClicked(button);
+ }
+ });
+ }
+
+ static addButtonClicked(line) {
+ const index = document.getElementById('section-order-input').value;
+ const station = document.getElementById('section-station-selector').value;
+ const isValid = SectionManagerModel.isValidInput(line, index, station);
+ if (isValid === 1) {
+ SectionManagerModel.add(line, station, index);
+ SectionManagerView.sectionInputView(line);
+ SectionManagerView.sectionTableView(line);
+ }
+ if (isValid !== 1) {
+ SectionManagerView.alertInputError(isValid);
+ SectionManagerView.sectionInputView(line);
+ }
+ }
+
+ static deleteButtonClicked(line, button) {
+ const buttons = document.getElementsByClassName('section-delete-button');
+ const buttonsArray = Array.from(buttons);
+ const station = buttons[buttonsArray.indexOf(button)].dataset.deleteTarget;
+ const isValid = SectionManagerModel.checkNumOfStations(line, station);
+ if (isValid === false) {
+ SectionManagerView.alertInputError(-2);
+ return;
+ }
+ if (!SectionManagerView.confirmDelete()) {
+ SectionManagerView.alertInputError(-3);
+ return;
+ }
+ SectionManagerModel.delete(line, station);
+ SectionManagerView.sectionTableView(line);
+ }
+
+ static sectionLineMenuClicked(button) {
+ const buttons = document.getElementsByClassName('section-line-menu-button');
+ const buttonsArray = Array.from(buttons);
+ const line = buttons[buttonsArray.indexOf(button)].dataset.menu;
+ SectionManagerView.sectionInputView(line);
+ SectionManagerView.sectionTableView(line);
+ return line;
+ }
+}
diff --git a/src/SectionManager/SectionManagerModel.js b/src/SectionManager/SectionManagerModel.js
new file mode 100644
index 000000000..25d4d6b0b
--- /dev/null
+++ b/src/SectionManager/SectionManagerModel.js
@@ -0,0 +1,57 @@
+export default class SectionManagerModel {
+ static isValidInput(line, index, station) {
+ if (!SectionManagerModel.isValidNumber(line, index)) {
+ return 0;
+ }
+ if (SectionManagerModel.isDuplicated(line, station)) {
+ return -1;
+ }
+ return 1;
+ }
+
+ static isValidNumber(line, number) {
+ return (number !== '') && this.isValidRange(line, number) && this.isNumber(number);
+ }
+
+ static isValidRange(line, number) {
+ const linesLength = JSON.parse(localStorage.getItem('Lines'))[line].stations.length;
+ return linesLength + 1 >= number && number >= 0;
+ }
+
+ static isNumber(number) {
+ return !/[^0-9]/.test(`${number}`);
+ }
+
+ static isDuplicated(line, station) {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const stationIncludedByLine = lines[line].stations;
+ return stationIncludedByLine.indexOf(station) > -1;
+ }
+
+ static add(line, station, index) {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const stationIncludedByLine = lines[line].stations;
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+
+ if (stations[station].lines.indexOf(line) < 0) {
+ stations[station].lines.push(line);
+ }
+ stationIncludedByLine.splice(index, 0, station);
+ lines[line].stations = stationIncludedByLine;
+
+ localStorage.setItem('Stations', JSON.stringify(stations));
+ localStorage.setItem('Lines', JSON.stringify(lines));
+ }
+
+ static checkNumOfStations(line) {
+ return JSON.parse(localStorage.getItem('Lines'))[line].stations.length > 2;
+ }
+
+ static delete(line, station) {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const stationIncludedByLine = JSON.parse(localStorage.getItem('Lines'))[line].stations;
+ stationIncludedByLine.splice(stationIncludedByLine.indexOf(station), 1);
+ lines[line].stations = stationIncludedByLine;
+ localStorage.setItem('Lines', JSON.stringify(lines));
+ }
+}
diff --git a/src/SectionManager/SectionManagerView.js b/src/SectionManager/SectionManagerView.js
new file mode 100644
index 000000000..88fbd302c
--- /dev/null
+++ b/src/SectionManager/SectionManagerView.js
@@ -0,0 +1,59 @@
+import SectionErrorMsg from './SectionErrorMsg.js';
+
+export default class SectionManagerView {
+ static view() {
+ document.getElementById('sub-view-container').innerHTML = `
+
+
+
+
+ `;
+ this.sectionSelectView();
+ }
+
+ static sectionSelectView() {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ document.getElementById('section-select').innerHTML = `
+ ๊ตฌ๊ฐ์ ์์ ํ ๋
ธ์ ์ ์ ํํด์ฃผ์ธ์.
+ ${Object.keys(lines).map((line) => ` `).join('')}
+ `;
+ }
+
+ static sectionInputView(line) {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ document.getElementById('section-input').innerHTML = `
+ ${line} ๊ด๋ฆฌ
+ ๊ตฌ๊ฐ ๋ฑ๋ก
+
+
+ `;
+ }
+
+ static sectionTableView(line) {
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const stations = lines[line].stations;
+ document.getElementById('section-table').innerHTML = `
+
+ | ์์ |
+ ์ด๋ฆ |
+ ์ค์ |
+
+ ${stations.map((station) => `
+
+ | ${stations.indexOf(station)} |
+ ${station} |
+ |
+
`).join('')}
+
`;
+ }
+
+ static alertInputError(errorNum) {
+ alert(SectionErrorMsg.error(errorNum));
+ }
+
+ static confirmDelete() {
+ return window.confirm('์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?');
+ }
+}
diff --git a/src/StationManger/StationErrorMsg.js b/src/StationManger/StationErrorMsg.js
new file mode 100644
index 000000000..5bbdfef2a
--- /dev/null
+++ b/src/StationManger/StationErrorMsg.js
@@ -0,0 +1,59 @@
+export default class StationErrorMsg {
+ static error(number) {
+ const errorMsg = [
+ this.lengthError(),
+ this.spaceError(),
+ this.koranCharacterError(),
+ this.notKoreanError(),
+ this.specialCharError(),
+ this.duplicateError(),
+ this.onlyNumberError(),
+ this.stationNumberError(),
+ this.cancelDeleteError(),
+ ];
+ return errorMsg[number * (-1)];
+ }
+
+ /*
+ * INPUT ERROR
+ */
+ static lengthError() {
+ return '์ญ ์ด๋ฆ์ 2๊ธ์ ์ด์์ผ๋ก ์์ฑํด ์ฃผ์ธ์.\n';
+ }
+
+ static spaceError() {
+ return '์ญ ์ด๋ฆ ์ฌ์ด์๋ ๊ณต๋ฐฑ์ด ๋ค์ด๊ฐ ์ ์์ต๋๋ค.\n';
+ }
+
+ static koranCharacterError() {
+ return '์์ ํ ํ๊ธ ๋จ์ด๋ก ์
๋ ฅํด ์ฃผ์ธ์.\nex. ใ
์ญ (x) ๋ค์ญ (o)\n';
+ }
+
+ static notKoreanError() {
+ return '์ญ ์ด๋ฆ์ ํ๊ธ๋ก๋ง ์์ฑํ ์ ์์ต๋๋ค.\n';
+ }
+
+ static specialCharError() {
+ return '์ญ ์ด๋ฆ์๋ ํน์ ๋ฌธ์๊ฐ ๋ค์ด ๊ฐ ์ ์์ต๋๋ค.\n';
+ }
+
+ static duplicateError() {
+ return '์ด๋ฏธ ํด๋น ์ด๋ฆ์ ์ญ์ด ์กด์ฌํฉ๋๋ค.\n';
+ }
+
+ static onlyNumberError() {
+ return '์ญ ์ด๋ฆ์ ์ซ์๋ก๋ง ์ด๋ฃจ์ด ์ง ์ ์์ต๋๋ค.\n';
+ }
+
+ /*
+ * DELETE ERROR
+ */
+
+ static stationNumberError() {
+ return 'ํด๋น ์ญ์ด ํฌํจ๋ ๋
ธ์ ์ค, ํฌํจ ๋ ์ญ์ด 3๊ฐ ๋ฏธ๋ง์ธ ์ญ์ด ์์ด ์ญ์ ๊ฐ ๋ถ๊ฐ๋ฅ ํฉ๋๋ค.';
+ }
+
+ static cancelDeleteError() {
+ return '์ญ์ ๋ฅผ ์ทจ์ํ์ต๋๋ค.';
+ }
+}
diff --git a/src/StationManger/StationManagerController.js b/src/StationManger/StationManagerController.js
new file mode 100644
index 000000000..e58a70ca9
--- /dev/null
+++ b/src/StationManger/StationManagerController.js
@@ -0,0 +1,48 @@
+import StationManagerView from './StationManagerView.js';
+import StationManagerModel from './StationManagerModel.js';
+
+export default class StationManagerController {
+ static buttonEventController() {
+ document.addEventListener('click', (event) => {
+ const eventId = event.target.id;
+ const eventClassName = event.target.className;
+ if (eventId === 'station-add-button') {
+ this.addButtonClicked();
+ }
+ if (eventClassName === 'station-delete-button') {
+ const button = event.path[0];
+ this.deleteButtonClicked(button);
+ }
+ });
+ }
+
+ static addButtonClicked() {
+ const station = document.getElementById('station-name-input').value;
+ const isValid = StationManagerModel.isValidInput(station);
+ if (isValid !== 1) {
+ StationManagerView.alertError(isValid);
+ StationManagerView.stationInputView();
+ return;
+ }
+ StationManagerModel.add(station);
+ StationManagerView.stationInputView();
+ StationManagerView.stationTableView();
+ }
+
+ static deleteButtonClicked(button) {
+ const buttons = document.getElementsByClassName('station-delete-button');
+ const buttonsArray = Array.from(buttons);
+ const station = buttons[buttonsArray.indexOf(button)].dataset.deleteTarget;
+ const isValid = StationManagerModel.checkAfterDelete(station);
+ if (isValid !== 1) {
+ StationManagerView.alertError(isValid);
+ return;
+ }
+ if (!StationManagerView.confirmDelete()) {
+ StationManagerView.alertError(-8);
+ return;
+ }
+ StationManagerModel.delete(station);
+ StationManagerView.stationTableView();
+ }
+}
diff --git a/src/StationManger/StationManagerModel.js b/src/StationManger/StationManagerModel.js
new file mode 100644
index 000000000..1d0d289b3
--- /dev/null
+++ b/src/StationManger/StationManagerModel.js
@@ -0,0 +1,95 @@
+export default class StationManagerModel {
+ /*
+ * INPUT CHECK
+ */
+
+ static isValidInput(station) {
+ let isValid = 1;
+ const validationCheckFunction = [
+ this.isValidLength(station),
+ this.isSpace(station),
+ this.iskoreanCharacterError(station),
+ this.isAlpha(station),
+ this.isSpecialChar(station),
+ this.isDuplicated(station),
+ this.isOnlyNumber(station),
+ ];
+ validationCheckFunction.forEach((func, index) => {
+ if (func && isValid === 1) {
+ isValid = (-1) * index;
+ }
+ });
+ return isValid;
+ }
+
+ static isValidLength(station) {
+ return station.length < 2;
+ }
+
+ static isSpace(station) {
+ return station.indexOf(' ') > -1;
+ }
+
+ static iskoreanCharacterError(station) {
+ return /[^๊ฐ-ํฃa-x0-9]/.test(station);
+ }
+
+ static isAlpha(station) {
+ return /[a-z]/.test(station);
+ }
+
+ static isSpecialChar(station) {
+ return /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(station);
+ }
+
+ static isDuplicated(station) {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ return Object.keys(stations).indexOf(station) > -1;
+ }
+
+ static isOnlyNumber(station) {
+ return !(/[^0-9]/.test(station));
+ }
+
+ /*
+ * Add
+ */
+
+ static add(station) {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ stations[station] = { "lines": [] };
+ localStorage.setItem('Stations', JSON.stringify(stations));
+ }
+
+ /*
+ * DELETE
+ */
+
+ static delete(station) {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const linesIncludingStation = stations[station].lines;
+
+ linesIncludingStation.forEach((line) => {
+ lines[line].stations.splice(lines[line].stations.indexOf(station), 1);
+ });
+ delete stations[station];
+
+ localStorage.setItem('Lines', JSON.stringify(lines));
+ localStorage.setItem('Stations', JSON.stringify(stations));
+ }
+
+ static checkAfterDelete(station) {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ const lines = JSON.parse(localStorage.getItem('Lines'));
+ const linesIncludingStation = stations[station].lines;
+
+ let isValid = 1;
+ linesIncludingStation.forEach((line) => {
+ if (lines[line].stations.length <= 2) {
+ isValid = -7;
+ }
+ });
+ return isValid;
+ }
+}
diff --git a/src/StationManger/StationManagerView.js b/src/StationManger/StationManagerView.js
new file mode 100644
index 000000000..e9d40c809
--- /dev/null
+++ b/src/StationManger/StationManagerView.js
@@ -0,0 +1,49 @@
+import StationErrorMsg from './StationErrorMsg.js';
+
+export default class StationManagerView {
+ static view() {
+ this.stationInitView();
+ this.stationInputView();
+ this.stationTableView();
+ }
+
+ static stationInitView() {
+ document.getElementById('sub-view-container').innerHTML = `
+
+
+ `;
+ }
+
+ static stationInputView() {
+ document.getElementById('station-input-view').innerHTML = `
+ ์ญ ์ด๋ฆ
+
+
+ `;
+ }
+
+ static stationTableView() {
+ const stations = JSON.parse(localStorage.getItem('Stations'));
+ document.getElementById('station-table-view').innerHTML = `
+ ๐ ์งํ์ฒ ์ญ ๋ชฉ๋ก
+
+
+ | ์ญ ์ด๋ฆ |
+ ์ค์ |
+
+ ${Object.keys(stations).map((station) => `
+
+ | ${station} |
+ |
+
`).join('')}
+
`;
+ }
+
+ static alertError(errorNum) {
+ alert(StationErrorMsg.error(errorNum));
+ }
+
+ static confirmDelete() {
+ return window.confirm('์ ๋ง๋ก ์ญ์ ํ์๊ฒ ์ต๋๊น?');
+ }
+}
diff --git a/src/index.js b/src/index.js
index e69de29bb..d3514520a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,42 @@
+import MenuView from './Menu/MenuView.js';
+import MenuController from './Menu/MenuController.js';
+import StationManagerController from './StationManger/StationManagerController.js';
+import LineManagerController from './LineManger/LineManagerController.js';
+import SectionManagerController from './SectionManager/SectionManagerController.js';
+
+export default class SubwayManager {
+ constructor() {
+ if (this.checkLocalStorage()) {
+ this.setLocalStrageDefault();
+ }
+ MenuView.menuButtonListView();
+ MenuController.buttonEventController();
+ SectionManagerController.buttonEventController();
+ LineManagerController.buttonEventController();
+ StationManagerController.buttonEventController();
+ }
+
+ checkLocalStorage() {
+ const stations = localStorage.getItem('Stations');
+ const lines = localStorage.getItem('Lines');
+ return !(stations && lines) || (stations === '{}') || (lines === '{}');
+ }
+
+ setLocalStrageDefault() {
+ const stationDefault = {
+ "์ธ์ฒ": { "lines": ["1ํธ์ ", "3ํธ์ "] },
+ "์์ธ์ญ": { "lines": ["1ํธ์ ", "2ํธ์ "] },
+ "์์์ฐ": { "lines": ["1ํธ์ ", "3ํธ์ "] },
+ "์ ๋๋ฆผ": { "lines": ["2ํธ์ "] }
+ };
+ const lineDefault = {
+ "1ํธ์ ": { "stations": ["์ธ์ฒ", "์์ธ์ญ", "์์์ฐ"] },
+ "2ํธ์ ": { "stations": ["์ ๋๋ฆผ", "์์ธ์ญ"] },
+ "3ํธ์ ": { "stations": ["์์์ฐ", "์ธ์ฒ"] }
+ };
+ localStorage.setItem('Stations', JSON.stringify(stationDefault));
+ localStorage.setItem('Lines', JSON.stringify(lineDefault));
+ }
+}
+
+new SubwayManager();