diff --git a/.codacy.yml b/.codacy.yml index 0c0f9901..0549c8f5 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -1,2 +1,3 @@ exclude_paths: - '**/3rdparty/**' + - '**/engine/js/cvat-core.min.js' diff --git a/.dockerignore b/.dockerignore index f05ada92..44c9d2c9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,6 @@ /.vscode /db.sqlite3 /keys +**/node_modules +cvat-ui +cvat-canvas diff --git a/.eslintrc.js b/.eslintrc.js index 48ef698e..a8731c42 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,9 +24,11 @@ "eslint:recommended", "plugin:security/recommended", "plugin:no-unsanitized/DOM", - "airbnb", + "airbnb-base", ], "rules": { + "no-await-in-loop": [0], + "global-require": [0], "no-new": [0], "class-methods-use-this": [0], "no-restricted-properties": [0, { @@ -41,5 +43,11 @@ // This rule actual for user input data on the node.js environment mainly. "security/detect-object-injection": 0, "indent": ["warn", 4], + // recently added to airbnb + "max-classes-per-file": [0], + // it was opposite before and our code has been written according to previous rule + "arrow-parens": [0], + // object spread is a modern ECMA standard. Let's do not use it without babel + "prefer-object-spread": [0], }, }; diff --git a/.gitattributes b/.gitattributes index 7c0b591b..f41e91d8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,6 +15,7 @@ LICENSE text *.conf text *.mimetypes text *.sh text eol=lf +components/openvino/eula.cfg text eol=lf *.avi binary *.bmp binary diff --git a/.gitignore b/.gitignore index 2b7daa67..c30ade19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Project Specific /data/ +/models/ /share/ /static/ /db.sqlite3 @@ -15,7 +16,11 @@ node_modules # Ignore temporary files docker-compose.override.yml -/.vscode __pycache__ *.pyc ._* + +# Ignore development npm files +node_modules + +.DS_Store diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..effa6735 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,420 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. This option is deprecated +# and it will be removed in Pylint 2.0. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +disable=all +enable= E0001,E0100,E0101,E0102,E0103,E0104,E0105,E0106,E0107,E0110, + E0113,E0114,E0115,E0116,E0117,E0108,E0202,E0203,E0211,E0236, + E0238,E0239,E0240,E0241,E0301,E0302,E0601,E0603,E0604,E0701, + E0702,E0703,E0704,E0710,E0711,E0712,E1003,E1102,E1111,E0112, + E1120,E1121,E1123,E1124,E1125,E1126,E1127,E1132,E1200,E1201, + E1205,E1206,E1300,E1301,E1302,E1303,E1304,E1305,E1306, + C0123,C0200,C0303,C1001, + W0101,W0102,W0104,W0105,W0106,W0107,W0108,W0109,W0110,W0120, + W0122,W0124,W0150,W0199,W0221,W0222,W0233,W0404,W0410,W0601, + W0602,W0604,W0611,W0612,W0622,W0623,W0702,W0705,W0711,W1300, + W1301,W1302,W1303,,W1305,W1306,W1307 + R0102,R0201,R0202,R0203 + + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +#disable=old-octal-literal,basestring-builtin,no-absolute-import,old-division,coerce-method,long-suffix,reload-builtin,unichr-builtin,indexing-exception,raising-string,dict-iter-method,metaclass-assignment,filter-builtin-not-iterating,import-star-module-level,next-method-called,cmp-method,raw_input-builtin,old-raise-syntax,cmp-builtin,apply-builtin,getslice-method,input-builtin,backtick,coerce-builtin,range-builtin-not-iterating,xrange-builtin,using-cmp-argument,buffer-builtin,hex-method,execfile-builtin,unpacking-in-except,standarderror-builtin,round-builtin,nonzero-method,unicode-builtin,reduce-builtin,file-builtin,dict-view-method,old-ne-operator,print-statement,suppressed-message,oct-method,useless-suppression,delslice-method,long-builtin,setslice-method,zip-builtin-not-iterating,map-builtin-not-iterating,intern-builtin,parameter-unpacking + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". This option is deprecated +# and it will be removed in Pylint 2.0. +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.travis.yml b/.travis.yml index 722d2e0a..f4218109 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ before_script: - docker-compose -f docker-compose.yml -f docker-compose.ci.yml up --build -d script: - - docker exec -it cvat /bin/bash -c 'tests/node_modules/.bin/karma start tests/karma.conf.js' - + - docker exec -it cvat /bin/bash -c 'python3 manage.py test cvat/apps/engine' + - docker exec -it cvat /bin/bash -c 'cd cvat-core && npm install && npm run test && npm run coveralls' diff --git a/.vscode/launch.json b/.vscode/launch.json index d2abc94a..ee8bb4fb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,24 @@ "version": "0.2.0", "configurations": [ { - "name": "server", + "type": "chrome", + "request": "launch", + "preLaunchTask": "ui.js: server", + "name": "ui.js: debug", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}/cvat-ui", + "sourceMaps": true, + "sourceMapPathOverrides": { + "webpack://cvat/./*": "${workspaceFolder}/cvat-core/*", + "webpack:///./*": "${webRoot}/*", + "webpack:///src/*": "${webRoot}/*", + "webpack:///*": "*", + "webpack:///./~/*": "${webRoot}/node_modules/*" + }, + "smartStep": true, + }, + { + "name": "server: django", "type": "python", "request": "launch", "stopOnEntry": false, @@ -19,10 +36,11 @@ "127.0.0.1:7000" ], "django": true, - "cwd": "${workspaceFolder}" + "cwd": "${workspaceFolder}", + "console": "internalConsole" }, { - "name": "client", + "name": "server: chrome", "type": "chrome", "request": "launch", "url": "http://localhost:7000/", @@ -35,7 +53,7 @@ } }, { - "name": "RQ - default", + "name": "server: RQ - default", "type": "python", "request": "launch", "stopOnEntry": false, @@ -50,10 +68,11 @@ ], "django": true, "cwd": "${workspaceFolder}", - "env": {} + "env": {}, + "console": "internalConsole" }, { - "name": "RQ - low", + "name": "server: RQ - low", "type": "python", "request": "launch", "justMyCode": false, @@ -68,10 +87,11 @@ ], "django": true, "cwd": "${workspaceFolder}", - "env": {} + "env": {}, + "console": "internalConsole" }, { - "name": "git", + "name": "server: git", "type": "python", "request": "launch", "justMyCode": false, @@ -83,10 +103,11 @@ ], "django": true, "cwd": "${workspaceFolder}", - "env": {} + "env": {}, + "console": "internalConsole" }, { - "name": "migrate", + "name": "server: migrate", "type": "python", "request": "launch", "justMyCode": false, @@ -98,10 +119,11 @@ ], "django": true, "cwd": "${workspaceFolder}", - "env": {} + "env": {}, + "console": "internalConsole" }, { - "name": "tests", + "name": "server: tests", "type": "python", "request": "launch", "justMyCode": false, @@ -116,18 +138,45 @@ ], "django": true, "cwd": "${workspaceFolder}", - "env": {} + "env": {}, + "console": "internalConsole" + }, + { + "name": "core.js: debug", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}/cvat-core", + "runtimeExecutable": "node", + "runtimeArgs": [ + "--nolazy", + "--inspect-brk=9230", + "src/api.js" + ], + "port": 9230 }, + { + "type": "node", + "request": "launch", + "name": "jest debug", + "program": "${workspaceFolder}/cvat-core/node_modules/.bin/jest", + "args": [ + "--config", + "${workspaceFolder}/cvat-core/jest.config.js" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + } ], "compounds": [ { - "name": "debugging", + "name": "server: debug", "configurations": [ - "client", - "server", - "RQ - default", - "RQ - low", - "git", + "server: chrome", + "server: django", + "server: RQ - default", + "server: RQ - low", + "server: git", ] } ] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..4cb0fb70 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "python.pythonPath": ".env/bin/python", + "eslint.enable": true, + "eslint.validate": [ + "javascript", + "typescript" + ], + "eslint.workingDirectories": [ + { + "directory": "./cvat-core", + "changeProcessCWD": true + }, + { + "directory": "./cvat-canvas", + "changeProcessCWD": true + }, + { + "directory": ".", + "changeProcessCWD": true + } + ], + "python.linting.pylintEnabled": true +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..10c34d90 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,14 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "ui.js: server", + "type": "npm", + "script": "start", + "path": "cvat-ui/", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c891694c..de92930e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2019-10-12 +### Added +- A converter to YOLO format +- Installation guide +- Linear interpolation for a single point +- Video frame filter +- Running functional tests for REST API during a build +- Admins are no longer limited to a subset of python commands in the auto annotation application +- Remote data source (list of URLs to create an annotation task) +- Auto annotation using Faster R-CNN with Inception v2 (utils/open_model_zoo) +- Auto annotation using Pixel Link mobilenet v2 - text detection (utils/open_model_zoo) +- Ability to create a custom extractors for unsupported media types +- Added in PDF extractor +- Added in a command line model manager tester +- Ability to dump/load annotations in several formats from UI (CVAT, Pascal VOC, YOLO, MS COCO, png mask, TFRecord) +- Auth for REST API (api/v1/auth/): login, logout, register, ... +- Preview for the new CVAT UI (dashboard only) is available: http://localhost:9080/ + +### Changed +- Outside and keyframe buttons in the side panel for all interpolation shapes (they were only for boxes before) +- Improved error messages on the client side (#511) + +### Removed +- "Flip images" has been removed. UI now contains rotation features. + +### Fixed +- Incorrect width of shapes borders in some cases +- Annotation parser for tracks with a start frame less than the first segment frame +- Interpolation on the server near outside frames +- Dump for case when task name has a slash +- Auto annotation fail for multijob tasks +- Installation of CVAT with OpenVINO on the Windows platform +- Background color was always black in utils/mask/converter.py +- Exception in attribute annotation mode when a label are switched to a value without any attributes +- Handling of wrong labelamp json file in auto annotation () +- No default attributes in dumped annotation () +- Required field "Frame Filter" on admin page during a task modifying (#666) +- Dump annotation errors for a task with several segments (#610, #500) +- Invalid label parsing during a task creating (#628) +- Button "Open Task" in the annotation view +- Creating a video task with 0 overlap + +### Security +- Upgraded Django, djangorestframework, and other packages + ## [0.4.2] - 2019-06-03 ### Fixed - Fixed interaction with the server share in the auto annotation plugin @@ -37,9 +82,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Django 2.1.5 (security fix, https://nvd.nist.gov/vuln/detail/CVE-2019-3498) - Several scenarious which cause code 400 after undo/redo/save have been fixed (#315) -### Security -- - ## [0.3.0] - 2018-12-29 ### Added - Ability to copy Object URL and Frame URL via object context menu and player context menu respectively. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2533f7f..e2c6e88d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,7 +53,7 @@ $ code . - Reload Visual Studio Code -- Select `CVAT Debugging` configuration and start debugging (F5) +- Select `server: debug` configuration and start debugging (F5) You have done! Now it is possible to insert breakpoints and debug server and client of the tool. diff --git a/Dockerfile b/Dockerfile index 3f9f3e97..a4c0e655 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,15 +4,16 @@ ARG http_proxy ARG https_proxy ARG no_proxy ARG socks_proxy +ARG TZ ENV TERM=xterm \ http_proxy=${http_proxy} \ https_proxy=${https_proxy} \ no_proxy=${no_proxy} \ - socks_proxy=${socks_proxy} - -ENV LANG='C.UTF-8' \ - LC_ALL='C.UTF-8' + socks_proxy=${socks_proxy} \ + LANG='C.UTF-8' \ + LC_ALL='C.UTF-8' \ + TZ=${TZ} ARG USER ARG DJANGO_CONFIGURATION @@ -38,10 +39,14 @@ RUN apt-get update && \ libsasl2-dev \ python3-dev \ python3-pip \ + tzdata \ unzip \ unrar \ p7zip-full \ vim && \ + pip3 install -U setuptools && \ + ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \ + dpkg-reconfigure -f noninteractive tzdata && \ add-apt-repository --remove ppa:mc3man/gstffmpeg-keep -y && \ add-apt-repository --remove ppa:mc3man/xerus-media -y && \ rm -rf /var/lib/apt/lists/* @@ -57,15 +62,12 @@ COPY components /tmp/components # OpenVINO toolkit support ARG OPENVINO_TOOLKIT ENV OPENVINO_TOOLKIT=${OPENVINO_TOOLKIT} +ENV REID_MODEL_DIR=${HOME}/reid RUN if [ "$OPENVINO_TOOLKIT" = "yes" ]; then \ - /tmp/components/openvino/install.sh; \ - fi - -# CUDA support -ARG CUDA_SUPPORT -ENV CUDA_SUPPORT=${CUDA_SUPPORT} -RUN if [ "$CUDA_SUPPORT" = "yes" ]; then \ - /tmp/components/cuda/install.sh; \ + /tmp/components/openvino/install.sh && \ + mkdir ${REID_MODEL_DIR} && \ + wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/person-reidentification-retail-0079/FP32/person-reidentification-retail-0079.xml -O reid/reid.xml && \ + wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/person-reidentification-retail-0079/FP32/person-reidentification-retail-0079.bin -O reid/reid.bin; \ fi # Tensorflow annotation support @@ -102,7 +104,9 @@ RUN if [ "$WITH_TESTS" = "yes" ]; then \ # Install and initialize CVAT, copy all necessary files COPY cvat/requirements/ /tmp/requirements/ COPY supervisord.conf mod_wsgi.conf wait-for-it.sh manage.py ${HOME}/ -RUN pip3 install --no-cache-dir -r /tmp/requirements/${DJANGO_CONFIGURATION}.txt +RUN pip3 install --no-cache-dir -r /tmp/requirements/${DJANGO_CONFIGURATION}.txt +# pycocotools package is impossible to install with its dependencies by one pip install command +RUN pip3 install --no-cache-dir pycocotools==2.0.0 # Install git application dependencies RUN apt-get update && \ @@ -117,18 +121,17 @@ RUN apt-get update && \ echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ProxyCommand='nc -X 5 -x ${socks_proxy} %h %p'\"" >> ${HOME}/.bashrc; \ fi -# Download model for re-identification app -ENV REID_MODEL_DIR=${HOME}/reid -RUN if [ "$OPENVINO_TOOLKIT" = "yes" ]; then \ - mkdir ${HOME}/reid && \ - wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/person-reidentification-retail-0079/FP32/person-reidentification-retail-0079.xml -O reid/reid.xml && \ - wget https://download.01.org/openvinotoolkit/2018_R5/open_model_zoo/person-reidentification-retail-0079/FP32/person-reidentification-retail-0079.bin -O reid/reid.bin; \ +# CUDA support +ARG CUDA_SUPPORT +ENV CUDA_SUPPORT=${CUDA_SUPPORT} +RUN if [ "$CUDA_SUPPORT" = "yes" ]; then \ + /tmp/components/cuda/install.sh; \ fi # TODO: CHANGE URL ARG WITH_DEXTR ENV WITH_DEXTR=${WITH_DEXTR} -ENV DEXTR_MODEL_DIR=${HOME}/models/dextr +ENV DEXTR_MODEL_DIR=${HOME}/dextr RUN if [ "$WITH_DEXTR" = "yes" ]; then \ mkdir ${DEXTR_MODEL_DIR} -p && \ wget https://download.01.org/openvinotoolkit/models_contrib/cvat/dextr_model_v1.zip -O ${DEXTR_MODEL_DIR}/dextr.zip && \ @@ -137,8 +140,11 @@ RUN if [ "$WITH_DEXTR" = "yes" ]; then \ COPY ssh ${HOME}/.ssh COPY cvat/ ${HOME}/cvat +COPY cvat-core/ ${HOME}/cvat-core COPY tests ${HOME}/tests -RUN patch -p1 < ${HOME}/cvat/apps/engine/static/engine/js/3rdparty.patch +# Binary option is necessary to correctly apply the patch on Windows platform. +# https://unix.stackexchange.com/questions/239364/how-to-fix-hunk-1-failed-at-1-different-line-endings-message +RUN patch --binary -p1 < ${HOME}/cvat/apps/engine/static/engine/js/3rdparty.patch RUN chown -R ${USER}:${USER} . # RUN all commands below as 'django' user diff --git a/README.md b/README.md index 19face9f..945d9f10 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,51 @@ # Computer Vision Annotation Tool (CVAT) [![Build Status](https://travis-ci.org/opencv/cvat.svg?branch=develop)](https://travis-ci.org/opencv/cvat) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/840351da141e4eaeac6476fd19ec0a33)](https://app.codacy.com/app/nmanovic/cvat?utm_source=github.com&utm_medium=referral&utm_content=opencv/cvat&utm_campaign=Badge_Grade_Settings) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/840351da141e4eaeac6476fd19ec0a33)](https://app.codacy.com/app/cvat/cvat?utm_source=github.com&utm_medium=referral&utm_content=opencv/cvat&utm_campaign=Badge_Grade_Dashboard) [![Gitter chat](https://badges.gitter.im/opencv-cvat/gitter.png)](https://gitter.im/opencv-cvat) +[![Coverage Status](https://coveralls.io/repos/github/opencv/cvat/badge.svg?branch=)](https://coveralls.io/github/opencv/cvat?branch=develop) -CVAT is completely re-designed and re-implemented version of [Video Annotation Tool from Irvine, California](http://carlvondrick.com/vatic/) tool. It is free, online, interactive video and image annotation tool for computer vision. It is being used by our team to annotate million of objects with different properties. Many UI and UX decisions are based on feedbacks from professional data annotation team. +CVAT is free, online, interactive video and image annotation tool for computer vision. It is being used by our team to annotate million of objects with different properties. Many UI and UX decisions are based on feedbacks from professional data annotation team. ![CVAT screenshot](cvat/apps/documentation/static/documentation/images/cvat.jpg) ## Documentation -- [User's guide](cvat/apps/documentation/user_guide.md) -- [XML annotation format](cvat/apps/documentation/xml_format.md) -- [AWS Deployment Guide](cvat/apps/documentation/AWS-Deployment-Guide.md) -- [Questions](#questions) +- [Installation guide](cvat/apps/documentation/installation.md) +- [User's guide](cvat/apps/documentation/user_guide.md) +- [Django REST API documentation](#rest-api) +- [XML annotation format](cvat/apps/documentation/xml_format.md) +- [AWS Deployment Guide](cvat/apps/documentation/AWS-Deployment-Guide.md) +- [Questions](#questions) ## Screencasts -- [Introduction](https://youtu.be/L9_IvUIHGwM) -- [Annotation mode](https://youtu.be/6h7HxGL6Ct4) -- [Interpolation mode](https://youtu.be/U3MYDhESHo4) -- [Attribute mode](https://youtu.be/UPNfWl8Egd8) -- [Segmentation mode](https://youtu.be/Fh8oKuSUIPs) -- [Tutorial for polygons](https://www.youtube.com/watch?v=XTwfXDh4clI) -- [Semi-automatic segmentation](https://www.youtube.com/watch?v=vnqXZ-Z-VTQ) +- [Introduction](https://youtu.be/L9_IvUIHGwM) +- [Annotation mode](https://youtu.be/6h7HxGL6Ct4) +- [Interpolation mode](https://youtu.be/U3MYDhESHo4) +- [Attribute mode](https://youtu.be/UPNfWl8Egd8) +- [Segmentation mode](https://youtu.be/Fh8oKuSUIPs) +- [Tutorial for polygons](https://www.youtube.com/watch?v=XTwfXDh4clI) +- [Semi-automatic segmentation](https://www.youtube.com/watch?v=vnqXZ-Z-VTQ) + +## Supported annotation formats + +Format selection is possible after clicking on the Upload annotation / Dump annotation button. + +| Annotation format | Dumper | Loader | +| ---------------------------------------------------------------------------------- | ------ | ------ | +| [CVAT XML v1.1 for images](cvat/apps/documentation/xml_format.md#annotation) | X | X | +| [CVAT XML v1.1 for a video](cvat/apps/documentation/xml_format.md#interpolation) | X | X | +| [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X | +| [YOLO](https://pjreddie.com/darknet/yolo/) | X | X | +| [MS COCO Object Detection](http://cocodataset.org/#format-data) | X | X | +| PNG mask | X | | +| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X | ## Links -- [Intel AI blog: New Computer Vision Tool Accelerates Annotation of Digital Images and Video](https://www.intel.ai/introducing-cvat) -- [Intel Software: Computer Vision Annotation Tool: A Universal Approach to Data Annotation](https://software.intel.com/en-us/articles/computer-vision-annotation-tool-a-universal-approach-to-data-annotation) -- [VentureBeat: Intel open-sources CVAT, a toolkit for data labeling](https://venturebeat.com/2019/03/05/intel-open-sources-cvat-a-toolkit-for-data-labeling/) +- [Intel AI blog: New Computer Vision Tool Accelerates Annotation of Digital Images and Video](https://www.intel.ai/introducing-cvat) +- [Intel Software: Computer Vision Annotation Tool: A Universal Approach to Data Annotation](https://software.intel.com/en-us/articles/computer-vision-annotation-tool-a-universal-approach-to-data-annotation) +- [VentureBeat: Intel open-sources CVAT, a toolkit for data labeling](https://venturebeat.com/2019/03/05/intel-open-sources-cvat-a-toolkit-for-data-labeling/) ## Online Demo @@ -36,124 +53,29 @@ CVAT is completely re-designed and re-implemented version of [Video Annotation T After you click the link above: -- Click on "GO TO WORKSPACE" and the CVAT environment will load up -- The environment is backed by a K80 GPU +- Click on "GO TO WORKSPACE" and the CVAT environment will load up +- The environment is backed by a K80 GPU If you have any questions, please contact Onepanel directly at support@onepanel.io. If you are in the Onepanel application, you can also use the chat icon in the bottom right corner. -## LICENSE - -Code released under the [MIT License](https://opensource.org/licenses/MIT). - -## INSTALLATION - -The instructions below should work for `Ubuntu 16.04`. It will probably work on other Operating Systems such as `Windows` and `macOS`, but may require minor modifications. - -### Install [Docker CE](https://www.docker.com/community-edition) or [Docker EE](https://www.docker.com/enterprise-edition) from official site - -Please read official manual [here](https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/). - -### Install docker-compose (1.19.0 or newer) - -```bash -sudo pip install docker-compose -``` - -### Build docker images - -To build all necessary docker images run `docker-compose build` command. By default, in production mode the tool uses PostgreSQL as database, Redis for caching. - -### Run docker containers - -To start default container run `docker-compose up -d` command. Go to [localhost:8080](http://localhost:8080/). You should see a login page. - -### You can include any additional components. Just add corresponding docker-compose file to build or run command: - -```bash -# Build image with CUDA and OpenVINO support -docker-compose -f docker-compose.yml -f components/cuda/docker-compose.cuda.yml -f components/openvino/docker-compose.openvino.yml build - -# Run containers with CUDA and OpenVINO support -docker-compose -f docker-compose.yml -f components/cuda/docker-compose.cuda.yml -f components/openvino/docker-compose.openvino.yml up -d -``` - -### Additional optional components +## REST API -- [Auto annotation using DL models in OpenVINO toolkit format](cvat/apps/auto_annotation/README.md) -- [Analytics: management and monitoring of data annotation team](components/analytics/README.md) -- [TF Object Detection API: auto annotation](components/tf_annotation/README.md) -- [Support for NVIDIA GPUs](components/cuda/README.md) -- [Semi-automatic segmentation with Deep Extreme Cut](cvat/apps/dextr_segmentation/README.md) +Automatically generated Swagger documentation for Django REST API is +available on ``/api/swagger`` (default: ``localhost:8080/api/swagger``). -### Create superuser account - -You can [register a user](http://localhost:8080/auth/register) but by default it will not have rights even to view list of tasks. Thus you should create a superuser. The superuser can use admin panel to assign correct groups to the user. Please use the command below: - -```bash -docker exec -it cvat bash -ic '/usr/bin/python3 ~/manage.py createsuperuser' -``` - -Type your login/password for the superuser [on the login page](http://localhost:8080/auth/login) and press **Login** button. Now you should be able to create a new annotation task. Please read documentation for more details. - -### Stop all containers - -The command below will stop and remove containers, networks, volumes, and images -created by `up`. - -```bash -docker-compose down -``` - -### Advanced settings - -If you want to access you instance of CVAT outside of your localhost you should specify [ALLOWED_HOSTS](https://docs.djangoproject.com/en/2.0/ref/settings/#allowed-hosts) environment variable. The best way to do that is to create [docker-compose.override.yml](https://docs.docker.com/compose/extends/) and put all your extra settings here. - -```yml -version: "2.3" - -services: - cvat: - environment: - ALLOWED_HOSTS: .example.com - ports: - - "80:8080" -``` -### Annotation logs - -It is possible to proxy annotation logs from client to ELK. To do that run the following command below: - -```bash -docker-compose -f docker-compose.yml -f components/analytics/docker-compose.analytics.yml up -d --build -``` - - -### Share path - -You can use a share storage for data uploading during you are creating a task. To do that you can mount it to CVAT docker container. Example of docker-compose.override.yml for this purpose: - -```yml -version: "2.3" - -services: - cvat: - environment: - CVAT_SHARE_URL: "Mounted from /mnt/share host directory" - volumes: - - cvat_share:/home/django/share:ro +## LICENSE -volumes: - cvat_share: - driver_opts: - type: none - device: /mnt/share - o: bind -``` -You can change the share device path to your actual share. For user convenience we have defined the enviroment variable $CVAT_SHARE_URL. This variable contains a text (url for example) which will be being shown in the client-share browser. +Code released under the [MIT License](https://opensource.org/licenses/MIT). ## Questions -CVAT usage related questions or unclear concepts can be posted in our [Gitter chat](https://gitter.im/opencv-cvat) for **quick replies** from contributors and other users. +CVAT usage related questions or unclear concepts can be posted in our +[Gitter chat](https://gitter.im/opencv-cvat) for **quick replies** from +contributors and other users. -However, if you have a feature request or a bug report that can reproduced, feel free to open an issue (with steps to reproduce the bug if it's a bug report). +However, if you have a feature request or a bug report that can reproduced, +feel free to open an issue (with steps to reproduce the bug if it's a bug +report). -If you are not sure or just want to browse other users common questions, [Gitter chat](https://gitter.im/opencv-cvat) is the way to go. +If you are not sure or just want to browse other users common questions, +[Gitter chat](https://gitter.im/opencv-cvat) is the way to go. \ No newline at end of file diff --git a/components/cuda/install.sh b/components/cuda/install.sh index dd689f77..2cda99fc 100755 --- a/components/cuda/install.sh +++ b/components/cuda/install.sh @@ -16,7 +16,7 @@ echo "deb http://developer.download.nvidia.com/compute/machine-learning/repos/ub CUDA_VERSION=9.0.176 NCCL_VERSION=2.1.15 -CUDNN_VERSION=7.0.5.15 +CUDNN_VERSION=7.6.2.24 CUDA_PKG_VERSION="9-0=${CUDA_VERSION}-1" echo 'export PATH=/usr/local/nvidia/bin:/usr/local/cuda/bin:${PATH}' >> ${HOME}/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64:${LD_LIBRARY_PATH}' >> ${HOME}/.bashrc @@ -32,3 +32,6 @@ apt-get update && apt-get install -y --no-install-recommends --allow-unauthentic ln -s cuda-9.0 /usr/local/cuda && \ rm -rf /var/lib/apt/lists/* \ /etc/apt/sources.list.d/nvidia-ml.list /etc/apt/sources.list.d/cuda.list + +pip3 uninstall -y tensorflow +pip3 install --no-cache-dir tensorflow-gpu==1.12.3 diff --git a/components/openvino/install.sh b/components/openvino/install.sh index 51277772..fdd08a96 100755 --- a/components/openvino/install.sh +++ b/components/openvino/install.sh @@ -26,6 +26,7 @@ apt-get update && apt-get install -y sudo cpio && \ if [ -f "install_cv_sdk_dependencies.sh" ]; then ./install_cv_sdk_dependencies.sh; \ else ./install_openvino_dependencies.sh; fi && SUDO_FORCE_REMOVE=yes apt-get remove -y sudo + cat ../eula.cfg >> silent.cfg ./install.sh -s silent.cfg diff --git a/components/tf_annotation/install.sh b/components/tf_annotation/install.sh index bdd1c6e2..fc5ed6b6 100755 --- a/components/tf_annotation/install.sh +++ b/components/tf_annotation/install.sh @@ -12,9 +12,4 @@ tar -xzf model.tar.gz && rm model.tar.gz && \ mv faster_rcnn_inception_resnet_v2_atrous_coco_2018_01_28 ${HOME}/rcnn && cd ${HOME} && \ mv rcnn/frozen_inference_graph.pb rcnn/inference_graph.pb -if [[ "$CUDA_SUPPORT" = "yes" ]] -then - pip3 install --no-cache-dir tensorflow-gpu==1.7.0 -else - pip3 install --no-cache-dir tensorflow==1.7.0 -fi +# tensorflow is installed globally diff --git a/cvat-canvas/.eslintrc.js b/cvat-canvas/.eslintrc.js new file mode 100644 index 00000000..10edfa70 --- /dev/null +++ b/cvat-canvas/.eslintrc.js @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +module.exports = { + 'env': { + 'node': true, + 'browser': true, + 'es6': true, + }, + 'parserOptions': { + 'parser': '@typescript-eslint/parser', + 'ecmaVersion': 6, + }, + 'plugins': [ + '@typescript-eslint', + 'import', + ], + 'extends': [ + 'plugin:@typescript-eslint/recommended', + 'airbnb-typescript/base', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + ], + 'rules': { + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/indent': ['warn', 4], + 'no-plusplus': 0, + 'no-restricted-syntax': [ + 0, + { + 'selector': 'ForOfStatement' + } + ], + 'no-continue': 0, + 'func-names': 0, + 'no-console': 0, // this rule deprecates console.log, console.warn etc. because 'it is not good in production code' + 'lines-between-class-members': 0, + 'import/prefer-default-export': 0, // works incorrect with interfaces + 'newline-per-chained-call': 0, // makes code uglier + }, + 'settings': { + 'import/resolver': { + 'node': { + 'extensions': ['.ts', '.js', '.json'], + }, + }, + }, +}; diff --git a/cvat-canvas/.gitignore b/cvat-canvas/.gitignore new file mode 100644 index 00000000..e11d1cb7 --- /dev/null +++ b/cvat-canvas/.gitignore @@ -0,0 +1,2 @@ +src/*.js +dist diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md new file mode 100644 index 00000000..e35f47d3 --- /dev/null +++ b/cvat-canvas/README.md @@ -0,0 +1,198 @@ +# Module CVAT-CANVAS + +## Description +The CVAT module written in TypeScript language. +It presents a canvas to viewing, drawing and editing of annotations. + +## Commands +- Building of the module from sources in the ```dist``` directory: + +```bash +npm run build +npm run build -- --mode=development # without a minification +``` + +- Updating of a module version: +```bash +npm version patch # updated after minor fixes +npm version minor # updated after major changes which don't affect API compatibility with previous versions +npm version major # updated after major changes which affect API compatibility with previous versions +``` + +## Using + +Canvas itself handles: +- Shape context menu (PKM) +- Image moving (mousedrag) +- Image resizing (mousewheel) +- Image fit (dblclick) +- Remove point (PKM) +- Polyshape editing (Shift + LKM) + +### API Methods + +```ts + enum Rotation { + ANTICLOCKWISE90, + CLOCKWISE90, + } + + interface DrawData { + enabled: boolean; + shapeType?: string; + numberOfPoints?: number; + initialState?: any; + crosshair?: boolean; + } + + interface GroupData { + enabled: boolean; + resetGroup?: boolean; + } + + interface MergeData { + enabled: boolean; + } + + interface SplitData { + enabled: boolean; + } + + interface DrawnData { + shapeType: string; + points: number[]; + objectType?: string; + occluded?: boolean; + attributes?: [index: number]: string; + label?: Label; + color?: string; + } + + interface Canvas { + html(): HTMLDivElement; + setup(frameData: any, objectStates: any[]): void; + activate(clientID: number, attributeID?: number): void; + rotate(rotation: Rotation, remember?: boolean): void; + focus(clientID: number, padding?: number): void; + fit(): void; + grid(stepX: number, stepY: number): void; + + draw(drawData: DrawData): void; + group(groupData: GroupData): void; + split(splitData: SplitData): void; + merge(mergeData: MergeData): void; + select(objectState: any): void; + + cancel(): void; + } +``` + +### API CSS + +- All drawn objects (shapes, tracks) have an id ```cvat_canvas_shape_{objectState.clientID}``` +- Drawn shapes and tracks have classes ```cvat_canvas_shape```, + ```cvat_canvas_shape_activated```, + ```cvat_canvas_shape_grouping```, + ```cvat_canvas_shape_merging```, + ```cvat_canvas_shape_drawing```, + ```cvat_canvas_shape_occluded``` +- Drawn texts have the class ```cvat_canvas_text``` +- Tags have the class ```cvat_canvas_tag``` +- Canvas image has ID ```cvat_canvas_image``` +- Grid on the canvas has ID ```cvat_canvas_grid_pattern``` +- Crosshair during a draw has class ```cvat_canvas_crosshair``` + +### Events + +Standard JS events are used. +```js + - canvas.setup + - canvas.activated => ObjectState + - canvas.deactivated + - canvas.moved => {states: ObjectState[], x: number, y: number} + - canvas.find => {states: ObjectState[], x: number, y: number} + - canvas.drawn => {state: DrawnData} + - canvas.edited => {state: ObjectState, points: number[]} + - canvas.splitted => {state: ObjectState} + - canvas.groupped => {states: ObjectState[]} + - canvas.merged => {states: ObjectState[]} + - canvas.canceled +``` + +### WEB +```js + // Create an instance of a canvas + const canvas = new window.canvas.Canvas(); + + // Put canvas to a html container + htmlContainer.appendChild(canvas.html()); + + // Next you can use its API methods. For example: + canvas.rotate(window.Canvas.Rotation.CLOCKWISE90); + canvas.draw({ + enabled: true, + shapeType: 'rectangle', + crosshair: true, + }); +``` + +### TypeScript +- Add to ```tsconfig.json```: +```json + "compilerOptions": { + "paths": { + "cvat-canvas.node": ["3rdparty/cvat-canvas.node"] + } + } +``` + +- ```3rdparty``` directory contains both ```cvat-canvas.node.js``` and ```cvat-canvas.node.d.ts```. +- Add alias to ```webpack.config.js```: +```js +module.exports = { + resolve: { + alias: { + 'cvat-canvas.node': path.resolve(__dirname, '3rdparty/cvat-canvas.node.js'), + } + } +} +``` + +Than you can use it in TypeScript: +```ts + import * as CANVAS from 'cvat-canvas.node'; + // Create an instance of a canvas + const canvas = new CANVAS.Canvas(); + + // Put canvas to a html container + htmlContainer.appendChild(canvas.html()); + + // Next you can use its API methods. For example: + canvas.rotate(CANVAS.Rotation.CLOCKWISE90); + canvas.draw({ + enabled: true, + shapeType: 'rectangle', + crosshair: true, + }); +``` + +## States + + ![](images/states.svg) + +## API Reaction + +| | IDLE | GROUPING | SPLITTING | DRAWING | MERGING | EDITING | +|------------|------|----------|-----------|---------|---------|---------| +| html() | + | + | + | + | + | + | +| setup() | + | + | + | + | + | - | +| activate() | + | - | - | - | - | - | +| rotate() | + | + | + | + | + | + | +| focus() | + | + | + | + | + | + | +| fit() | + | + | + | + | + | + | +| grid() | + | + | + | + | + | + | +| draw() | + | - | - | - | - | - | +| split() | + | - | + | - | - | - | +| group | + | + | - | - | - | - | +| merge() | + | - | - | - | + | - | +| cancel() | - | + | + | + | + | + | diff --git a/cvat-canvas/images/states.svg b/cvat-canvas/images/states.svg new file mode 100644 index 00000000..85f673cc --- /dev/null +++ b/cvat-canvas/images/states.svg @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + FREE + + + + GROUPING + + + SPLITTING + + + DRAWING + + + MERGING + + + EDITING + + + BROKER + + + canvas.drawn + + + canvas.merged + + + canvas.edited + + + canvas.splitted + + + canvas.grouped + + + + merge(false) + cancel() + + + cancel() + + + split(false) + cancel() + + + group(false) + cancel() + + + cancel() + + + group(true) + + + internal transition + + + merge(true) + + + split(true) + + + draw() + + + + + + go to state + + + go from state + + + + emitted events + + + + background + + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IDLE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json new file mode 100644 index 00000000..a9b19472 --- /dev/null +++ b/cvat-canvas/package.json @@ -0,0 +1,41 @@ +{ + "name": "cvat-canvas", + "version": "0.1.0", + "description": "Part of Computer Vision Annotation Tool which presents its canvas library", + "main": "src/canvas.ts", + "scripts": { + "build": "tsc && webpack --config ./webpack.config.js", + "server": "nodemon --watch config --exec 'webpack-dev-server --config ./webpack.config.js --mode=development --open'" + }, + "author": "Intel", + "license": "MIT", + "dependencies": { + "svg.draggable.js": "2.2.2", + "svg.draw.js": "^2.0.3", + "svg.js": "2.7.1", + "svg.resize.js": "1.4.3", + "svg.select.js": "3.0.1" + }, + "devDependencies": { + "@babel/cli": "^7.5.5", + "@babel/core": "^7.5.5", + "@babel/preset-env": "^7.5.5", + "@babel/preset-typescript": "^7.3.3", + "@types/node": "^12.6.8", + "@typescript-eslint/eslint-plugin": "^1.13.0", + "@typescript-eslint/parser": "^1.13.0", + "babel-loader": "^8.0.6", + "css-loader": "^3.2.0", + "dts-bundle-webpack": "^1.0.2", + "eslint": "^6.1.0", + "eslint-config-airbnb-typescript": "^4.0.1", + "eslint-config-typescript-recommended": "^1.4.17", + "eslint-plugin-import": "^2.18.2", + "nodemon": "^1.19.1", + "style-loader": "^1.0.0", + "typescript": "^3.5.3", + "webpack": "^4.36.1", + "webpack-cli": "^3.3.6", + "webpack-dev-server": "^3.7.2" + } +} diff --git a/cvat-canvas/src/css/canvas.css b/cvat-canvas/src/css/canvas.css new file mode 100644 index 00000000..4d931ed0 --- /dev/null +++ b/cvat-canvas/src/css/canvas.css @@ -0,0 +1,148 @@ +.cvat_canvas_hidden { + display: none; +} + +.cvat_canvas_shape { + fill-opacity: 0.05; + stroke-opacity: 1; +} + +polyline.cvat_canvas_shape { + fill-opacity: 0; + stroke-opacity: 1; +} + +.cvat_canvas_text { + font-weight: bold; + font-size: 1.2em; + fill: white; + cursor: default; + font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif; + text-shadow: 0px 0px 4px black; + user-select: none; + pointer-events: none; +} + +.cvat_canvas_crosshair { + stroke: red; +} + +.cvat_canvas_shape_activated { + fill-opacity: 0.3; +} + +.cvat_canvas_shape_grouping { + fill: darkmagenta; + fill-opacity: 0.5; +} + +polyline.cvat_canvas_shape_grouping { + stroke: darkmagenta; + stroke-opacity: 1; +} + +.cvat_canvas_shape_merging { + fill: blue; + fill-opacity: 0.5; +} + +polyline.cvat_canvas_shape_splitting { + stroke: dodgerblue; + stroke-opacity: 1; +} + +.cvat_canvas_shape_splitting { + fill: dodgerblue; + fill-opacity: 0.5; +} + +polyline.cvat_canvas_shape_merging { + stroke: blue; + stroke-opacity: 1; +} + +.cvat_canvas_shape_drawing { + fill-opacity: 0.1; + stroke-opacity: 1; + fill: white; + stroke: black; +} + +.cvat_canvas_shape_occluded { + stroke-dasharray: 5; +} + +.svg_select_boundingRect { + opacity: 0; + pointer-events: none; +} + +#cvat_canvas_wrapper { + width: 100%; + height: 93%; + border-radius: 5px; + background-color: white; + overflow: hidden; + position: relative; +} + +#cvat_canvas_loading_animation { + z-index: 1; + position: absolute; + width: 100%; + height: 100%; +} + +#cvat_canvas_loading_circle { + fill-opacity: 0; + stroke: #09c; + stroke-width: 3px; + stroke-dasharray: 50; + animation: loadingAnimation 1s linear infinite; +} + +#cvat_canvas_text_content { + position: absolute; + z-index: 3; + pointer-events: none; + width: 100%; + height: 100%; + pointer-events: none; +} + +#cvat_canvas_background { + position: absolute; + z-index: 0; + background-repeat: no-repeat; + width: 100%; + height: 100%; + box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.75); +} + +#cvat_canvas_grid { + position: absolute; + z-index: 2; + pointer-events: none; + width: 100%; + height: 100%; + pointer-events: none; +} + +#cvat_canvas_grid_pattern { + opacity: 1; + stroke: white; +} + +#cvat_canvas_content { + position: absolute; + z-index: 2; + outline: 10px solid black; + width: 100%; + height: 100%; +} + +@keyframes loadingAnimation { + 0% {stroke-dashoffset: 1; stroke: #09c;} + 50% {stroke-dashoffset: 100; stroke: #f44;} + 100% {stroke-dashoffset: 300; stroke: #09c;} +} \ No newline at end of file diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts new file mode 100644 index 00000000..e934b41b --- /dev/null +++ b/cvat-canvas/src/typescript/canvas.ts @@ -0,0 +1,120 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +import { + Rotation, + DrawData, + MergeData, + SplitData, + GroupData, + CanvasModel, + CanvasModelImpl, +} from './canvasModel'; + +import { + Master, +} from './master'; + +import { + CanvasController, + CanvasControllerImpl, +} from './canvasController'; + +import { + CanvasView, + CanvasViewImpl, +} from './canvasView'; + + +import '../css/canvas.css'; + + +interface Canvas { + html(): HTMLDivElement; + setup(frameData: any, objectStates: any[]): void; + activate(clientID: number, attributeID?: number): void; + rotate(rotation: Rotation, remember?: boolean): void; + focus(clientID: number, padding?: number): void; + fit(): void; + grid(stepX: number, stepY: number): void; + + draw(drawData: DrawData): void; + group(groupData: GroupData): void; + split(splitData: SplitData): void; + merge(mergeData: MergeData): void; + select(objectState: any): void; + + cancel(): void; +} + +class CanvasImpl implements Canvas { + private model: CanvasModel & Master; + private controller: CanvasController; + private view: CanvasView; + + public constructor() { + this.model = new CanvasModelImpl(); + this.controller = new CanvasControllerImpl(this.model); + this.view = new CanvasViewImpl(this.model, this.controller); + } + + public html(): HTMLDivElement { + return this.view.html(); + } + + public setup(frameData: any, objectStates: any[]): void { + this.model.setup(frameData, objectStates); + } + + public activate(clientID: number, attributeID: number = null): void { + this.model.activate(clientID, attributeID); + } + + public rotate(rotation: Rotation, remember: boolean = false): void { + this.model.rotate(rotation, remember); + } + + public focus(clientID: number, padding: number = 0): void { + this.model.focus(clientID, padding); + } + + public fit(): void { + this.model.fit(); + } + + public grid(stepX: number, stepY: number): void { + this.model.grid(stepX, stepY); + } + + public draw(drawData: DrawData): void { + this.model.draw(drawData); + } + + public split(splitData: SplitData): void { + this.model.split(splitData); + } + + public group(groupData: GroupData): void { + this.model.group(groupData); + } + + public merge(mergeData: MergeData): void { + this.model.merge(mergeData); + } + + public select(objectState: any): void { + this.model.select(objectState); + } + + public cancel(): void { + this.model.cancel(); + } +} + + +export { + CanvasImpl as Canvas, + Rotation, +}; diff --git a/cvat-canvas/src/typescript/canvasController.ts b/cvat-canvas/src/typescript/canvasController.ts new file mode 100644 index 00000000..30914a56 --- /dev/null +++ b/cvat-canvas/src/typescript/canvasController.ts @@ -0,0 +1,147 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +import { + CanvasModel, + Geometry, + Position, + FocusData, + ActiveElement, + DrawData, + MergeData, + SplitData, + GroupData, + Mode, +} from './canvasModel'; + +export interface CanvasController { + readonly objects: any[]; + readonly focusData: FocusData; + readonly activeElement: ActiveElement; + readonly drawData: DrawData; + readonly mergeData: MergeData; + readonly splitData: SplitData; + readonly groupData: GroupData; + readonly selected: any; + mode: Mode; + geometry: Geometry; + + zoom(x: number, y: number, direction: number): void; + draw(drawData: DrawData): void; + merge(mergeData: MergeData): void; + split(splitData: SplitData): void; + group(groupData: GroupData): void; + enableDrag(x: number, y: number): void; + drag(x: number, y: number): void; + disableDrag(): void; + + fit(): void; +} + +export class CanvasControllerImpl implements CanvasController { + private model: CanvasModel; + private lastDragPosition: Position; + private isDragging: boolean; + + public constructor(model: CanvasModel) { + this.model = model; + } + + public zoom(x: number, y: number, direction: number): void { + this.model.zoom(x, y, direction); + } + + public fit(): void { + this.model.fit(); + } + + public enableDrag(x: number, y: number): void { + this.lastDragPosition = { + x, + y, + }; + this.isDragging = true; + } + + public drag(x: number, y: number): void { + if (this.isDragging) { + const topOffset: number = y - this.lastDragPosition.y; + const leftOffset: number = x - this.lastDragPosition.x; + this.lastDragPosition = { + x, + y, + }; + this.model.move(topOffset, leftOffset); + } + } + + public disableDrag(): void { + this.isDragging = false; + } + + public draw(drawData: DrawData): void { + this.model.draw(drawData); + } + + public merge(mergeData: MergeData): void { + this.model.merge(mergeData); + } + + public split(splitData: SplitData): void { + this.model.split(splitData); + } + + public group(groupData: GroupData): void { + this.model.group(groupData); + } + + public get geometry(): Geometry { + return this.model.geometry; + } + + public set geometry(geometry: Geometry) { + this.model.geometry = geometry; + } + + public get objects(): any[] { + return this.model.objects; + } + + public get focusData(): FocusData { + return this.model.focusData; + } + + public get activeElement(): ActiveElement { + return this.model.activeElement; + } + + public get drawData(): DrawData { + return this.model.drawData; + } + + public get mergeData(): MergeData { + return this.model.mergeData; + } + + public get splitData(): SplitData { + return this.model.splitData; + } + + public get groupData(): GroupData { + return this.model.groupData; + } + + public get selected(): any { + return this.model.selected; + } + + public set mode(value: Mode) { + this.model.mode = value; + } + + public get mode(): Mode { + return this.model.mode; + } +} diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts new file mode 100644 index 00000000..5fbb2ab1 --- /dev/null +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -0,0 +1,504 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +// Disable till full implementation +/* eslint class-methods-use-this: "off" */ + +import { MasterImpl } from './master'; + + +export interface Size { + width: number; + height: number; +} + +export interface Position { + x: number; + y: number; +} + +export interface Geometry { + image: Size; + canvas: Size; + grid: Size; + top: number; + left: number; + scale: number; + offset: number; + angle: number; +} + +export interface FocusData { + clientID: number; + padding: number; +} + +export interface ActiveElement { + clientID: number; + attributeID: number; +} + +export interface DrawData { + enabled: boolean; + shapeType?: string; + numberOfPoints?: number; + initialState?: any; + crosshair?: boolean; +} + +export interface EditData { + enabled: boolean; + state: any; + pointID: number; +} + +export interface GroupData { + enabled: boolean; +} + +export interface MergeData { + enabled: boolean; +} + +export interface SplitData { + enabled: boolean; +} + +export enum FrameZoom { + MIN = 0.1, + MAX = 10, +} + +export enum Rotation { + ANTICLOCKWISE90, + CLOCKWISE90, +} + +export enum UpdateReasons { + IMAGE = 'image', + OBJECTS = 'objects', + ZOOM = 'zoom', + FIT = 'fit', + MOVE = 'move', + GRID = 'grid', + FOCUS = 'focus', + ACTIVATE = 'activate', + DRAW = 'draw', + MERGE = 'merge', + SPLIT = 'split', + GROUP = 'group', + SELECT = 'select', + CANCEL = 'cancel', +} + +export enum Mode { + IDLE = 'idle', + DRAG = 'drag', + RESIZE = 'resize', + DRAW = 'draw', + EDIT = 'edit', + MERGE = 'merge', + SPLIT = 'split', + GROUP = 'group', +} + +export interface CanvasModel { + readonly image: string; + readonly objects: any[]; + readonly gridSize: Size; + readonly focusData: FocusData; + readonly activeElement: ActiveElement; + readonly drawData: DrawData; + readonly mergeData: MergeData; + readonly splitData: SplitData; + readonly groupData: GroupData; + readonly selected: any; + geometry: Geometry; + mode: Mode; + + zoom(x: number, y: number, direction: number): void; + move(topOffset: number, leftOffset: number): void; + + setup(frameData: any, objectStates: any[]): void; + activate(clientID: number, attributeID: number): void; + rotate(rotation: Rotation, remember: boolean): void; + focus(clientID: number, padding: number): void; + fit(): void; + grid(stepX: number, stepY: number): void; + + draw(drawData: DrawData): void; + group(groupData: GroupData): void; + split(splitData: SplitData): void; + merge(mergeData: MergeData): void; + select(objectState: any): void; + + cancel(): void; +} + +export class CanvasModelImpl extends MasterImpl implements CanvasModel { + private data: { + activeElement: ActiveElement; + angle: number; + canvasSize: Size; + image: string; + imageOffset: number; + imageSize: Size; + focusData: FocusData; + gridSize: Size; + left: number; + objects: any[]; + rememberAngle: boolean; + scale: number; + top: number; + drawData: DrawData; + mergeData: MergeData; + groupData: GroupData; + splitData: SplitData; + selected: any; + mode: Mode; + }; + + public constructor() { + super(); + + this.data = { + activeElement: { + clientID: null, + attributeID: null, + }, + angle: 0, + canvasSize: { + height: 0, + width: 0, + }, + image: '', + imageOffset: 0, + imageSize: { + height: 0, + width: 0, + }, + focusData: { + clientID: 0, + padding: 0, + }, + gridSize: { + height: 100, + width: 100, + }, + left: 0, + objects: [], + rememberAngle: false, + scale: 1, + top: 0, + drawData: { + enabled: false, + shapeType: null, + numberOfPoints: null, + initialState: null, + }, + mergeData: { + enabled: false, + }, + groupData: { + enabled: false, + }, + splitData: { + enabled: false, + }, + selected: null, + mode: null, + }; + } + + public zoom(x: number, y: number, direction: number): void { + const oldScale: number = this.data.scale; + const newScale: number = direction > 0 ? oldScale * 6 / 5 : oldScale * 5 / 6; + this.data.scale = Math.min(Math.max(newScale, FrameZoom.MIN), FrameZoom.MAX); + + const { angle } = this.data; + + const mutiplier = Math.sin(angle * Math.PI / 180) + Math.cos(angle * Math.PI / 180); + if ((angle / 90) % 2) { + // 90, 270, .. + this.data.top += mutiplier * ((x - this.data.imageSize.width / 2) + * (oldScale / this.data.scale - 1)) * this.data.scale; + this.data.left -= mutiplier * ((y - this.data.imageSize.height / 2) + * (oldScale / this.data.scale - 1)) * this.data.scale; + } else { + this.data.left += mutiplier * ((x - this.data.imageSize.width / 2) + * (oldScale / this.data.scale - 1)) * this.data.scale; + this.data.top += mutiplier * ((y - this.data.imageSize.height / 2) + * (oldScale / this.data.scale - 1)) * this.data.scale; + } + + this.notify(UpdateReasons.ZOOM); + } + + public move(topOffset: number, leftOffset: number): void { + this.data.top += topOffset; + this.data.left += leftOffset; + this.notify(UpdateReasons.MOVE); + } + + public setup(frameData: any, objectStates: any[]): void { + frameData.data( + (): void => { + this.data.image = ''; + this.notify(UpdateReasons.IMAGE); + }, + ).then((data: string): void => { + this.data.imageSize = { + height: (frameData.height as number), + width: (frameData.width as number), + }; + + if (!this.data.rememberAngle) { + this.data.angle = 0; + } + + this.data.image = data; + this.notify(UpdateReasons.IMAGE); + this.data.objects = objectStates; + this.notify(UpdateReasons.OBJECTS); + }).catch((exception: any): void => { + throw exception; + }); + } + + public activate(clientID: number, attributeID: number): void { + if (this.data.mode !== Mode.IDLE) { + // Exception or just return? + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + + this.data.activeElement = { + clientID, + attributeID, + }; + + this.notify(UpdateReasons.ACTIVATE); + } + + public rotate(rotation: Rotation, remember: boolean = false): void { + if (rotation === Rotation.CLOCKWISE90) { + this.data.angle += 90; + } else { + this.data.angle -= 90; + } + + this.data.angle %= 360; + this.data.rememberAngle = remember; + this.fit(); + } + + public focus(clientID: number, padding: number): void { + this.data.focusData = { + clientID, + padding, + }; + + this.notify(UpdateReasons.FOCUS); + } + + public fit(): void { + const { angle } = this.data; + + if ((angle / 90) % 2) { + // 90, 270, .. + this.data.scale = Math.min( + this.data.canvasSize.width / this.data.imageSize.height, + this.data.canvasSize.height / this.data.imageSize.width, + ); + } else { + this.data.scale = Math.min( + this.data.canvasSize.width / this.data.imageSize.width, + this.data.canvasSize.height / this.data.imageSize.height, + ); + } + + this.data.scale = Math.min( + Math.max(this.data.scale, FrameZoom.MIN), + FrameZoom.MAX, + ); + + this.data.top = (this.data.canvasSize.height / 2 - this.data.imageSize.height / 2); + this.data.left = (this.data.canvasSize.width / 2 - this.data.imageSize.width / 2); + + this.notify(UpdateReasons.FIT); + } + + public grid(stepX: number, stepY: number): void { + this.data.gridSize = { + height: stepY, + width: stepX, + }; + + this.notify(UpdateReasons.GRID); + } + + public draw(drawData: DrawData): void { + if (![Mode.IDLE, Mode.DRAW].includes(this.data.mode)) { + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + + if (drawData.enabled) { + if (this.data.drawData.enabled) { + throw new Error('Drawing has been already started'); + } else if (!drawData.shapeType && !drawData.initialState) { + throw new Error('A shape type is not specified'); + } else if (typeof (drawData.numberOfPoints) !== 'undefined') { + if (drawData.shapeType === 'polygon' && drawData.numberOfPoints < 3) { + throw new Error('A polygon consists of at least 3 points'); + } else if (drawData.shapeType === 'polyline' && drawData.numberOfPoints < 2) { + throw new Error('A polyline consists of at least 2 points'); + } + } + } + + this.data.drawData = { ...drawData }; + if (this.data.drawData.initialState) { + this.data.drawData.shapeType = this.data.drawData.initialState.shapeType; + } + this.notify(UpdateReasons.DRAW); + } + + public split(splitData: SplitData): void { + if (![Mode.IDLE, Mode.SPLIT].includes(this.data.mode)) { + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + + if (this.data.splitData.enabled && splitData.enabled) { + return; + } + + if (!this.data.splitData.enabled && !splitData.enabled) { + return; + } + + this.data.splitData = { ...splitData }; + this.notify(UpdateReasons.SPLIT); + } + + public group(groupData: GroupData): void { + if (![Mode.IDLE, Mode.GROUP].includes(this.data.mode)) { + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + + if (this.data.groupData.enabled && groupData.enabled) { + return; + } + + if (!this.data.groupData.enabled && !groupData.enabled) { + return; + } + + this.data.groupData = { ...groupData }; + this.notify(UpdateReasons.GROUP); + } + + public merge(mergeData: MergeData): void { + if (![Mode.IDLE, Mode.MERGE].includes(this.data.mode)) { + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + + if (this.data.mergeData.enabled && mergeData.enabled) { + return; + } + + if (!this.data.mergeData.enabled && !mergeData.enabled) { + return; + } + + this.data.mergeData = { ...mergeData }; + this.notify(UpdateReasons.MERGE); + } + + public select(objectState: any): void { + this.data.selected = objectState; + this.notify(UpdateReasons.SELECT); + this.data.selected = null; + } + + public cancel(): void { + this.notify(UpdateReasons.CANCEL); + } + + public get geometry(): Geometry { + return { + angle: this.data.angle, + canvas: { ...this.data.canvasSize }, + image: { ...this.data.imageSize }, + grid: { ...this.data.gridSize }, + left: this.data.left, + offset: this.data.imageOffset, + scale: this.data.scale, + top: this.data.top, + }; + } + + public set geometry(geometry: Geometry) { + this.data.angle = geometry.angle; + this.data.canvasSize = { ...geometry.canvas }; + this.data.imageSize = { ...geometry.image }; + this.data.gridSize = { ...geometry.grid }; + this.data.left = geometry.left; + this.data.top = geometry.top; + this.data.imageOffset = geometry.offset; + this.data.scale = geometry.scale; + + this.data.imageOffset = Math.floor(Math.max( + this.data.canvasSize.height / FrameZoom.MIN, + this.data.canvasSize.width / FrameZoom.MIN, + )); + } + + public get image(): string { + return this.data.image; + } + + public get objects(): any[] { + return this.data.objects; + } + + public get gridSize(): Size { + return { ...this.data.gridSize }; + } + + public get focusData(): FocusData { + return { ...this.data.focusData }; + } + + public get activeElement(): ActiveElement { + return { ...this.data.activeElement }; + } + + public get drawData(): DrawData { + return { ...this.data.drawData }; + } + + public get mergeData(): MergeData { + return { ...this.data.mergeData }; + } + + public get splitData(): SplitData { + return { ...this.data.splitData }; + } + + public get groupData(): GroupData { + return { ...this.data.groupData }; + } + + public get selected(): any { + return this.data.selected; + } + + public set mode(value: Mode) { + this.data.mode = value; + } + + public get mode(): Mode { + return this.data.mode; + } +} diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts new file mode 100644 index 00000000..d9c57233 --- /dev/null +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -0,0 +1,1086 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +import * as SVG from 'svg.js'; + +import 'svg.draggable.js'; +import 'svg.resize.js'; +import 'svg.select.js'; + +import { CanvasController } from './canvasController'; +import { Listener, Master } from './master'; +import { DrawHandler, DrawHandlerImpl } from './drawHandler'; +import { EditHandler, EditHandlerImpl } from './editHandler'; +import { MergeHandler, MergeHandlerImpl } from './mergeHandler'; +import { SplitHandler, SplitHandlerImpl } from './splitHandler'; +import { GroupHandler, GroupHandlerImpl } from './groupHandler'; +import consts from './consts'; +import { + translateToSVG, + translateFromSVG, + translateBetweenSVG, + pointsToArray, + displayShapeSize, + ShapeSizeElement, +} from './shared'; +import { + CanvasModel, + Geometry, + UpdateReasons, + FocusData, + FrameZoom, + ActiveElement, + DrawData, + MergeData, + SplitData, + GroupData, + Mode, + Size, +} from './canvasModel'; + +export interface CanvasView { + html(): HTMLDivElement; +} + +interface ShapeDict { + [index: number]: SVG.Shape; +} + +interface TextDict { + [index: number]: SVG.Text; +} + +function darker(color: string, percentage: number): string { + const R = Math.round(parseInt(color.slice(1, 3), 16) * (1 - percentage / 100)); + const G = Math.round(parseInt(color.slice(3, 5), 16) * (1 - percentage / 100)); + const B = Math.round(parseInt(color.slice(5, 7), 16) * (1 - percentage / 100)); + + const rHex = Math.max(0, R).toString(16); + const gHex = Math.max(0, G).toString(16); + const bHex = Math.max(0, B).toString(16); + + return `#${rHex.length === 1 ? `0${rHex}` : rHex}` + + `${gHex.length === 1 ? `0${gHex}` : gHex}` + + `${bHex.length === 1 ? `0${bHex}` : bHex}`; +} + +export class CanvasViewImpl implements CanvasView, Listener { + private loadingAnimation: SVGSVGElement; + private text: SVGSVGElement; + private adoptedText: SVG.Container; + private background: SVGSVGElement; + private grid: SVGSVGElement; + private content: SVGSVGElement; + private adoptedContent: SVG.Container; + private canvas: HTMLDivElement; + private gridPath: SVGPathElement; + private gridPattern: SVGPatternElement; + private controller: CanvasController; + private svgShapes: ShapeDict; + private svgTexts: TextDict; + private geometry: Geometry; + private drawHandler: DrawHandler; + private editHandler: EditHandler; + private mergeHandler: MergeHandler; + private splitHandler: SplitHandler; + private groupHandler: GroupHandler; + private activeElement: { + state: any; + attributeID: number; + }; + + private set mode(value: Mode) { + this.controller.mode = value; + } + + private get mode(): Mode { + return this.controller.mode; + } + + private onDrawDone(data: object): void { + if (data) { + const event: CustomEvent = new CustomEvent('canvas.drawn', { + bubbles: false, + cancelable: true, + detail: { + // eslint-disable-next-line new-cap + state: data, + }, + }); + + this.canvas.dispatchEvent(event); + } else { + const event: CustomEvent = new CustomEvent('canvas.canceled', { + bubbles: false, + cancelable: true, + }); + + this.canvas.dispatchEvent(event); + } + + this.controller.draw({ + enabled: false, + }); + + this.mode = Mode.IDLE; + } + + private onEditDone(state: any, points: number[]): void { + if (state && points) { + const event: CustomEvent = new CustomEvent('canvas.edited', { + bubbles: false, + cancelable: true, + detail: { + state, + points, + }, + }); + + this.canvas.dispatchEvent(event); + } else { + const event: CustomEvent = new CustomEvent('canvas.canceled', { + bubbles: false, + cancelable: true, + }); + + this.canvas.dispatchEvent(event); + } + + this.mode = Mode.IDLE; + } + + private onMergeDone(objects: any[]): void { + if (objects) { + const event: CustomEvent = new CustomEvent('canvas.merged', { + bubbles: false, + cancelable: true, + detail: { + states: objects, + }, + }); + + this.canvas.dispatchEvent(event); + } else { + const event: CustomEvent = new CustomEvent('canvas.canceled', { + bubbles: false, + cancelable: true, + }); + + this.canvas.dispatchEvent(event); + } + + this.controller.merge({ + enabled: false, + }); + + this.mode = Mode.IDLE; + } + + private onSplitDone(object: any): void { + if (object) { + const event: CustomEvent = new CustomEvent('canvas.splitted', { + bubbles: false, + cancelable: true, + detail: { + state: object, + frame: object.frame, + }, + }); + + this.canvas.dispatchEvent(event); + } else { + const event: CustomEvent = new CustomEvent('canvas.canceled', { + bubbles: false, + cancelable: true, + }); + + this.canvas.dispatchEvent(event); + } + + this.controller.split({ + enabled: false, + }); + + this.mode = Mode.IDLE; + } + + private onGroupDone(objects: any[]): void { + if (objects) { + const event: CustomEvent = new CustomEvent('canvas.groupped', { + bubbles: false, + cancelable: true, + detail: { + states: objects, + }, + }); + + this.canvas.dispatchEvent(event); + } else { + const event: CustomEvent = new CustomEvent('canvas.canceled', { + bubbles: false, + cancelable: true, + }); + + this.canvas.dispatchEvent(event); + } + + this.controller.group({ + enabled: false, + }); + + this.mode = Mode.IDLE; + } + + private onFindObject(e: MouseEvent): void { + if (e.which === 1 || e.which === 0) { + const [x, y] = translateToSVG(this.background, [e.clientX, e.clientY]); + const event: CustomEvent = new CustomEvent('canvas.find', { + bubbles: false, + cancelable: true, + detail: { + x, + y, + states: this.controller.objects, + }, + }); + + this.canvas.dispatchEvent(event); + + e.preventDefault(); + } + } + + private selectize(value: boolean, shape: SVG.Element): void { + const self = this; + + function dblClickHandler(e: MouseEvent): void { + const pointID = Array.prototype.indexOf + .call((e.target as HTMLElement).parentElement.children, e.target); + + if (self.activeElement) { + if (e.ctrlKey) { + const { points } = self.activeElement.state; + self.onEditDone( + self.activeElement.state, + points.slice(0, pointID * 2).concat(points.slice(pointID * 2 + 2)), + ); + } else if (e.shiftKey) { + self.mode = Mode.EDIT; + const { state } = self.activeElement; + self.deactivate(); + self.editHandler.edit({ + enabled: true, + state, + pointID, + }); + } + } + } + + if (value) { + (shape as any).selectize(value, { + deepSelect: true, + pointSize: 2 * consts.BASE_POINT_SIZE / self.geometry.scale, + rotationPoint: false, + pointType(cx: number, cy: number): SVG.Circle { + const circle: SVG.Circle = this.nested + .circle(this.options.pointSize) + .stroke('black') + .fill(shape.node.getAttribute('fill') || 'inherit') + .center(cx, cy) + .attr({ + 'stroke-width': consts.POINTS_STROKE_WIDTH / self.geometry.scale, + }); + + circle.node.addEventListener('mouseenter', (): void => { + circle.attr({ + 'stroke-width': consts.POINTS_SELECTED_STROKE_WIDTH / self.geometry.scale, + }); + + circle.node.addEventListener('dblclick', dblClickHandler); + circle.addClass('cvat_canvas_selected_point'); + }); + + circle.node.addEventListener('mouseleave', (): void => { + circle.attr({ + 'stroke-width': consts.POINTS_STROKE_WIDTH / self.geometry.scale, + }); + + circle.node.removeEventListener('dblclick', dblClickHandler); + circle.removeClass('cvat_canvas_selected_point'); + }); + + return circle; + }, + }); + } else { + (shape as any).selectize(false, { + deepSelect: true, + }); + } + } + + public constructor(model: CanvasModel & Master, controller: CanvasController) { + this.controller = controller; + this.svgShapes = {}; + this.svgTexts = {}; + this.activeElement = null; + this.mode = Mode.IDLE; + + // Create HTML elements + this.loadingAnimation = window.document + .createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.text = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.adoptedText = (SVG.adopt((this.text as any as HTMLElement)) as SVG.Container); + this.background = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + + this.grid = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.gridPath = window.document.createElementNS('http://www.w3.org/2000/svg', 'path'); + this.gridPattern = window.document.createElementNS('http://www.w3.org/2000/svg', 'pattern'); + + this.content = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this.adoptedContent = (SVG.adopt((this.content as any as HTMLElement)) as SVG.Container); + + this.canvas = window.document.createElement('div'); + + const loadingCircle: SVGCircleElement = window.document + .createElementNS('http://www.w3.org/2000/svg', 'circle'); + const gridDefs: SVGDefsElement = window.document + .createElementNS('http://www.w3.org/2000/svg', 'defs'); + const gridRect: SVGRectElement = window.document + .createElementNS('http://www.w3.org/2000/svg', 'rect'); + + // Setup loading animation + this.loadingAnimation.setAttribute('id', 'cvat_canvas_loading_animation'); + loadingCircle.setAttribute('id', 'cvat_canvas_loading_circle'); + loadingCircle.setAttribute('r', '30'); + loadingCircle.setAttribute('cx', '50%'); + loadingCircle.setAttribute('cy', '50%'); + + // Setup grid + this.grid.setAttribute('id', 'cvat_canvas_grid'); + this.grid.setAttribute('version', '2'); + this.gridPath.setAttribute('d', 'M 1000 0 L 0 0 0 1000'); + this.gridPath.setAttribute('fill', 'none'); + this.gridPath.setAttribute('stroke-width', '1.5'); + this.gridPattern.setAttribute('id', 'cvat_canvas_grid_pattern'); + this.gridPattern.setAttribute('width', '100'); + this.gridPattern.setAttribute('height', '100'); + this.gridPattern.setAttribute('patternUnits', 'userSpaceOnUse'); + gridRect.setAttribute('width', '100%'); + gridRect.setAttribute('height', '100%'); + gridRect.setAttribute('fill', 'url(#cvat_canvas_grid_pattern)'); + + // Setup content + this.text.setAttribute('id', 'cvat_canvas_text_content'); + this.background.setAttribute('id', 'cvat_canvas_background'); + this.content.setAttribute('id', 'cvat_canvas_content'); + + // Setup wrappers + this.canvas.setAttribute('id', 'cvat_canvas_wrapper'); + + // Unite created HTML elements together + this.loadingAnimation.appendChild(loadingCircle); + this.grid.appendChild(gridDefs); + this.grid.appendChild(gridRect); + + gridDefs.appendChild(this.gridPattern); + this.gridPattern.appendChild(this.gridPath); + + this.canvas.appendChild(this.loadingAnimation); + this.canvas.appendChild(this.text); + this.canvas.appendChild(this.background); + this.canvas.appendChild(this.grid); + this.canvas.appendChild(this.content); + + + // A little hack to get size after first mounting + // http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/ + const self = this; + const canvasFirstMounted = (event: AnimationEvent): void => { + if (event.animationName === 'loadingAnimation') { + const { geometry } = this.controller; + geometry.canvas = { + height: self.canvas.clientHeight, + width: self.canvas.clientWidth, + }; + + this.controller.geometry = geometry; + this.geometry = geometry; + self.canvas.removeEventListener('animationstart', canvasFirstMounted); + } + }; + + this.canvas.addEventListener('animationstart', canvasFirstMounted); + + // Setup API handlers + this.drawHandler = new DrawHandlerImpl( + this.onDrawDone.bind(this), + this.adoptedContent, + this.adoptedText, + this.background, + ); + this.editHandler = new EditHandlerImpl( + this.onEditDone.bind(this), + this.adoptedContent, + this.background, + ); + this.mergeHandler = new MergeHandlerImpl( + this.onMergeDone.bind(this), + this.onFindObject.bind(this), + this.adoptedContent, + ); + this.splitHandler = new SplitHandlerImpl( + this.onSplitDone.bind(this), + this.onFindObject.bind(this), + this.adoptedContent, + ); + this.groupHandler = new GroupHandlerImpl( + this.onGroupDone.bind(this), + (): any[] => this.controller.objects, + this.onFindObject.bind(this), + this.adoptedContent, + ); + + + // Setup event handlers + this.content.addEventListener('dblclick', (e: MouseEvent): void => { + if (e.ctrlKey || e.shiftKey) return; + self.controller.fit(); + e.preventDefault(); + }); + + this.content.addEventListener('mousedown', (event): void => { + if ((event.which === 1 && this.mode === Mode.IDLE) || (event.which === 2)) { + self.controller.enableDrag(event.clientX, event.clientY); + + event.preventDefault(); + } + }); + + window.document.addEventListener('mouseup', (event): void => { + if (event.which === 1 || event.which === 2) { + self.controller.disableDrag(); + } + }); + + this.content.addEventListener('wheel', (event): void => { + const point = translateToSVG(self.background, [event.clientX, event.clientY]); + self.controller.zoom(point[0], point[1], event.deltaY > 0 ? -1 : 1); + event.preventDefault(); + }); + + this.content.addEventListener('mousemove', (e): void => { + self.controller.drag(e.clientX, e.clientY); + + if (this.mode !== Mode.IDLE) return; + if (e.ctrlKey || e.shiftKey) return; + + const [x, y] = translateToSVG(this.background, [e.clientX, e.clientY]); + const event: CustomEvent = new CustomEvent('canvas.moved', { + bubbles: false, + cancelable: true, + detail: { + x, + y, + states: this.controller.objects, + }, + }); + + this.canvas.dispatchEvent(event); + }); + + this.content.oncontextmenu = (): boolean => false; + model.subscribe(this); + } + + public notify(model: CanvasModel & Master, reason: UpdateReasons): void { + function transform(): void { + // Transform canvas + for (const obj of [this.background, this.grid, this.loadingAnimation, this.content]) { + obj.style.transform = `scale(${this.geometry.scale}) rotate(${this.geometry.angle}deg)`; + } + + // Transform grid + this.gridPath.setAttribute('stroke-width', `${consts.BASE_GRID_WIDTH / (this.geometry.scale)}px`); + + // Transform all shape points + for (const element of window.document.getElementsByClassName('svg_select_points')) { + element.setAttribute( + 'stroke-width', + `${consts.POINTS_STROKE_WIDTH / this.geometry.scale}`, + ); + element.setAttribute( + 'r', + `${consts.BASE_POINT_SIZE / this.geometry.scale}`, + ); + } + + for (const element of + window.document.getElementsByClassName('cvat_canvas_selected_point')) { + element.setAttribute( + 'stroke-width', + `${+element.getAttribute('stroke-width') * 2}`, + ); + } + + // Transform all drawn shapes + for (const key in this.svgShapes) { + if (Object.prototype.hasOwnProperty.call(this.svgShapes, key)) { + const object = this.svgShapes[key]; + if (object.attr('stroke-width')) { + object.attr({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + }); + } + } + } + + // Transform all text + for (const key in this.svgShapes) { + if (Object.prototype.hasOwnProperty.call(this.svgShapes, key) + && Object.prototype.hasOwnProperty.call(this.svgTexts, key)) { + this.updateTextPosition( + this.svgTexts[key], + this.svgShapes[key], + ); + } + } + + // Transform handlers + this.drawHandler.transform(this.geometry); + this.editHandler.transform(this.geometry); + } + + function resize(): void { + for (const obj of [this.background, this.grid, this.loadingAnimation]) { + obj.style.width = `${this.geometry.image.width}px`; + obj.style.height = `${this.geometry.image.height}px`; + } + + for (const obj of [this.content, this.text]) { + obj.style.width = `${this.geometry.image.width + this.geometry.offset * 2}px`; + obj.style.height = `${this.geometry.image.height + this.geometry.offset * 2}px`; + } + } + + function move(): void { + for (const obj of [this.background, this.grid, this.loadingAnimation]) { + obj.style.top = `${this.geometry.top}px`; + obj.style.left = `${this.geometry.left}px`; + } + + for (const obj of [this.content, this.text]) { + obj.style.top = `${this.geometry.top - this.geometry.offset}px`; + obj.style.left = `${this.geometry.left - this.geometry.offset}px`; + } + + // Transform handlers + this.drawHandler.transform(this.geometry); + this.editHandler.transform(this.geometry); + } + + function computeFocus(focusData: FocusData): void { + // This computation cann't be done in the model because of lack of data + const object = this.svgShapes[focusData.clientID]; + if (!object) { + return; + } + + // First of all, compute and apply scale + + let scale = null; + const bbox: SVG.BBox = object.bbox(); + if ((this.geometry.angle / 90) % 2) { + // 90, 270, .. + scale = Math.min(Math.max(Math.min( + this.geometry.canvas.width / bbox.height, + this.geometry.canvas.height / bbox.width, + ), FrameZoom.MIN), FrameZoom.MAX); + } else { + scale = Math.min(Math.max(Math.min( + this.geometry.canvas.width / bbox.width, + this.geometry.canvas.height / bbox.height, + ), FrameZoom.MIN), FrameZoom.MAX); + } + + this.geometry = { ...this.geometry, scale }; + transform.call(this); + + const [x, y] = translateFromSVG(this.content, [ + bbox.x + bbox.width / 2, + bbox.y + bbox.height / 2, + ]); + + const [cx, cy] = [ + this.canvas.clientWidth / 2 + this.canvas.offsetLeft, + this.canvas.clientHeight / 2 + this.canvas.offsetTop, + ]; + + const dragged = { + ...this.geometry, + top: this.geometry.top + cy - y, + left: this.geometry.left + cx - x, + scale, + }; + + this.controller.geometry = dragged; + this.geometry = dragged; + move.call(this); + } + + function setupObjects(objects: any[]): void { + const ctm = this.content.getScreenCTM() + .inverse().multiply(this.background.getScreenCTM()); + + this.deactivate(); + + // TODO: Compute difference + + // Instead of simple clearing let's remove all objects properly + for (const id of Object.keys(this.svgShapes)) { + if (id in this.svgTexts) { + this.svgTexts[id].remove(); + } + + this.svgShapes[id].remove(); + } + + this.svgTexts = {}; + this.svgShapes = {}; + + this.addObjects(ctm, objects); + // TODO: Update objects + // TODO: Delete objects + } + + this.geometry = this.controller.geometry; + if (reason === UpdateReasons.IMAGE) { + if (!model.image.length) { + this.loadingAnimation.classList.remove('cvat_canvas_hidden'); + } else { + this.loadingAnimation.classList.add('cvat_canvas_hidden'); + this.background.style.backgroundImage = `url("${model.image}")`; + move.call(this); + resize.call(this); + transform.call(this); + } + } else if (reason === UpdateReasons.ZOOM || reason === UpdateReasons.FIT) { + move.call(this); + transform.call(this); + } else if (reason === UpdateReasons.MOVE) { + move.call(this); + } else if (reason === UpdateReasons.OBJECTS) { + setupObjects.call(this, this.controller.objects); + const event: CustomEvent = new CustomEvent('canvas.setup'); + this.canvas.dispatchEvent(event); + } else if (reason === UpdateReasons.GRID) { + const size: Size = this.geometry.grid; + this.gridPattern.setAttribute('width', `${size.width}`); + this.gridPattern.setAttribute('height', `${size.height}`); + } else if (reason === UpdateReasons.FOCUS) { + computeFocus.call(this, this.controller.focusData); + } else if (reason === UpdateReasons.ACTIVATE) { + this.activate(this.controller.activeElement); + } else if (reason === UpdateReasons.DRAW) { + const data: DrawData = this.controller.drawData; + if (data.enabled) { + this.mode = Mode.DRAW; + this.deactivate(); + } + this.drawHandler.draw(data, this.geometry); + } else if (reason === UpdateReasons.MERGE) { + const data: MergeData = this.controller.mergeData; + if (data.enabled) { + this.mode = Mode.MERGE; + this.deactivate(); + } + this.mergeHandler.merge(data); + } else if (reason === UpdateReasons.SPLIT) { + const data: SplitData = this.controller.splitData; + if (data.enabled) { + this.mode = Mode.SPLIT; + this.deactivate(); + } + this.splitHandler.split(data); + } else if (reason === UpdateReasons.GROUP) { + const data: GroupData = this.controller.groupData; + if (data.enabled) { + this.mode = Mode.GROUP; + this.deactivate(); + } + this.groupHandler.group(data); + } else if (reason === UpdateReasons.SELECT) { + if (this.mode === Mode.MERGE) { + this.mergeHandler.select(this.controller.selected); + } else if (this.mode === Mode.SPLIT) { + this.splitHandler.select(this.controller.selected); + } else if (this.mode === Mode.GROUP) { + this.groupHandler.select(this.controller.selected); + } + } else if (reason === UpdateReasons.CANCEL) { + if (this.mode === Mode.DRAW) { + this.drawHandler.cancel(); + } else if (this.mode === Mode.MERGE) { + this.mergeHandler.cancel(); + } else if (this.mode === Mode.SPLIT) { + this.splitHandler.cancel(); + } else if (this.mode === Mode.GROUP) { + this.groupHandler.cancel(); + } else if (this.mode === Mode.EDIT) { + this.editHandler.cancel(); + } + } + } + + public html(): HTMLDivElement { + return this.canvas; + } + + private addObjects(ctm: SVGMatrix, states: any[]): void { + for (const state of states) { + if (state.objectType === 'tag') { + this.addTag(state); + } else { + const points: number[] = (state.points as number[]); + const translatedPoints: number[] = []; + for (let i = 0; i <= points.length - 1; i += 2) { + let point: SVGPoint = this.background.createSVGPoint(); + point.x = points[i]; + point.y = points[i + 1]; + point = point.matrixTransform(ctm); + translatedPoints.push(point.x, point.y); + } + + // TODO: Use enums after typification cvat-core + if (state.shapeType === 'rectangle') { + this.svgShapes[state.clientID] = this + .addRect(translatedPoints, state); + } else { + const stringified = translatedPoints.reduce( + (acc: string, val: number, idx: number): string => { + if (idx % 2) { + return `${acc}${val} `; + } + + return `${acc}${val},`; + }, '', + ); + + if (state.shapeType === 'polygon') { + this.svgShapes[state.clientID] = this + .addPolygon(stringified, state); + } else if (state.shapeType === 'polyline') { + this.svgShapes[state.clientID] = this + .addPolyline(stringified, state); + } else if (state.shapeType === 'points') { + this.svgShapes[state.clientID] = this + .addPoints(stringified, state); + } + } + + // TODO: Use enums after typification cvat-core + if (state.visibility === 'all') { + this.svgTexts[state.clientID] = this.addText(state); + this.updateTextPosition( + this.svgTexts[state.clientID], + this.svgShapes[state.clientID], + ); + } + } + } + } + + private deactivate(): void { + if (this.activeElement) { + const { state } = this.activeElement; + const shape = this.svgShapes[this.activeElement.state.clientID]; + shape.removeClass('cvat_canvas_shape_activated'); + + (shape as any).draggable(false); + + if (state.shapeType !== 'points') { + this.selectize(false, shape); + } + + (shape as any).resize(false); + + // Hide text only if it is hidden by settings + const text = this.svgTexts[state.clientID]; + if (text && state.visibility === 'shape') { + text.remove(); + delete this.svgTexts[state.clientID]; + } + this.activeElement = null; + } + } + + private activate(activeElement: ActiveElement): void { + // Check if other element have been already activated + if (this.activeElement) { + // Check if it is the same element + if (this.activeElement.state.clientID === activeElement.clientID) { + return; + } + + // Deactivate previous element + this.deactivate(); + } + + const state = this.controller.objects + .filter((el): boolean => el.clientID === activeElement.clientID)[0]; + this.activeElement = { + attributeID: activeElement.attributeID, + state, + }; + + const shape = this.svgShapes[activeElement.clientID]; + shape.addClass('cvat_canvas_shape_activated'); + let text = this.svgTexts[activeElement.clientID]; + // Draw text if it's hidden by default + if (!text && state.visibility === 'shape') { + text = this.addText(state); + this.svgTexts[state.clientID] = text; + this.updateTextPosition( + text, + shape, + ); + } + + const self = this; + this.content.append(shape.node); + (shape as any).draggable().on('dragstart', (): void => { + this.mode = Mode.DRAG; + if (text) { + text.addClass('cvat_canvas_hidden'); + } + }).on('dragend', (e: CustomEvent): void => { + if (text) { + text.removeClass('cvat_canvas_hidden'); + self.updateTextPosition( + text, + shape, + ); + } + + this.mode = Mode.IDLE; + + const p1 = e.detail.handler.startPoints.point; + const p2 = e.detail.p; + const delta = 1; + if (Math.sqrt(((p1.x - p2.x) ** 2) + ((p1.y - p2.y) ** 2)) >= delta) { + const points = pointsToArray( + shape.attr('points') || `${shape.attr('x')},${shape.attr('y')} ` + + `${shape.attr('x') + shape.attr('width')},` + + `${shape.attr('y') + shape.attr('height')}`, + ); + + this.onEditDone(state, translateBetweenSVG(this.content, this.background, points)); + } + }); + + if (state.shapeType !== 'points') { + this.selectize(true, shape); + } + + let shapeSizeElement: ShapeSizeElement = null; + let resized = false; + (shape as any).resize().on('resizestart', (): void => { + this.mode = Mode.RESIZE; + if (state.shapeType === 'rectangle') { + shapeSizeElement = displayShapeSize(this.adoptedContent, this.adoptedText); + } + resized = false; + if (text) { + text.addClass('cvat_canvas_hidden'); + } + }).on('resizing', (): void => { + resized = true; + if (shapeSizeElement) { + shapeSizeElement.update(shape); + } + }).on('resizedone', (): void => { + if (shapeSizeElement) { + shapeSizeElement.rm(); + } + + if (text) { + text.removeClass('cvat_canvas_hidden'); + self.updateTextPosition( + text, + shape, + ); + } + + this.mode = Mode.IDLE; + + if (resized) { + const points = pointsToArray( + shape.attr('points') || `${shape.attr('x')},${shape.attr('y')} ` + + `${shape.attr('x') + shape.attr('width')},` + + `${shape.attr('y') + shape.attr('height')}`, + ); + + this.onEditDone(state, translateBetweenSVG(this.content, this.background, points)); + } + }); + } + + // Update text position after corresponding box has been moved, resized, etc. + private updateTextPosition(text: SVG.Text, shape: SVG.Shape): void { + let box = (shape.node as any).getBBox(); + + // Translate the whole box to the client coordinate system + const [x1, y1, x2, y2]: number[] = translateFromSVG(this.content, [ + box.x, + box.y, + box.x + box.width, + box.y + box.height, + ]); + + box = { + x: Math.min(x1, x2), + y: Math.min(y1, y2), + width: Math.max(x1, x2) - Math.min(x1, x2), + height: Math.max(y1, y2) - Math.min(y1, y2), + }; + + // Find the best place for a text + let [clientX, clientY]: number[] = [box.x + box.width, box.y]; + if (clientX + (text.node as any as SVGTextElement) + .getBBox().width + consts.TEXT_MARGIN > this.canvas.offsetWidth) { + ([clientX, clientY] = [box.x, box.y]); + } + + // Translate back to text SVG + const [x, y]: number[] = translateToSVG(this.text, [ + clientX + consts.TEXT_MARGIN, + clientY + consts.TEXT_MARGIN, + ]); + + // Finally draw a text + text.move(x, y); + for (const tspan of (text.lines() as any).members) { + tspan.attr('x', text.attr('x')); + } + } + + private addText(state: any): SVG.Text { + const { label, clientID, attributes } = state; + const attrNames = label.attributes.reduce((acc: any, val: any): void => { + acc[val.id] = val.name; + return acc; + }, {}); + + return this.adoptedText.text((block): void => { + block.tspan(`${label.name} ${clientID}`).style('text-transform', 'uppercase'); + for (const attrID of Object.keys(attributes)) { + block.tspan(`${attrNames[attrID]}: ${attributes[attrID]}`).attr({ + attrID, + dy: '1em', + x: 0, + }); + } + }).move(0, 0).addClass('cvat_canvas_text'); + } + + private addRect(points: number[], state: any): SVG.Rect { + const [xtl, ytl, xbr, ybr] = points; + const rect = this.adoptedContent.rect().size(xbr - xtl, ybr - ytl).attr({ + clientID: state.clientID, + 'color-rendering': 'optimizeQuality', + id: `cvat_canvas_shape_${state.clientID}`, + fill: state.color, + 'shape-rendering': 'geometricprecision', + stroke: darker(state.color, 50), + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + zOrder: state.zOrder, + }).move(xtl, ytl) + .addClass('cvat_canvas_shape'); + + if (state.occluded) { + rect.addClass('cvat_canvas_shape_occluded'); + } + + return rect; + } + + private addPolygon(points: string, state: any): SVG.Polygon { + const polygon = this.adoptedContent.polygon(points).attr({ + clientID: state.clientID, + 'color-rendering': 'optimizeQuality', + id: `cvat_canvas_shape_${state.clientID}`, + fill: state.color, + 'shape-rendering': 'geometricprecision', + stroke: darker(state.color, 50), + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + zOrder: state.zOrder, + }).addClass('cvat_canvas_shape'); + + if (state.occluded) { + polygon.addClass('cvat_canvas_shape_occluded'); + } + + return polygon; + } + + private addPolyline(points: string, state: any): SVG.PolyLine { + const polyline = this.adoptedContent.polyline(points).attr({ + clientID: state.clientID, + 'color-rendering': 'optimizeQuality', + id: `cvat_canvas_shape_${state.clientID}`, + fill: state.color, + 'shape-rendering': 'geometricprecision', + stroke: darker(state.color, 50), + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + zOrder: state.zOrder, + }).addClass('cvat_canvas_shape'); + + if (state.occluded) { + polyline.addClass('cvat_canvas_shape_occluded'); + } + + return polyline; + } + + private addPoints(points: string, state: any): SVG.PolyLine { + const shape = this.adoptedContent.polyline(points).attr({ + 'color-rendering': 'optimizeQuality', + 'pointer-events': 'none', + 'shape-rendering': 'geometricprecision', + 'stroke-width': 0, + fill: state.color, // to right fill property when call SVG.Shape::clone() + }).style({ + opacity: 0, + }); + + this.selectize(true, shape); + + const group = shape.remember('_selectHandler').nested + .addClass('cvat_canvas_shape').attr({ + clientID: state.clientID, + zOrder: state.zOrder, + id: `cvat_canvas_shape_${state.clientID}`, + fill: state.color, + }).style({ + 'fill-opacity': 1, + }); + group.bbox = shape.bbox.bind(shape); + group.clone = shape.clone.bind(shape); + + shape.remove = (): SVG.PolyLine => { + this.selectize(false, shape); + shape.constructor.prototype.remove.call(shape); + return shape; + }; + + return shape; + } + + /* eslint-disable-next-line */ + private addTag(state: any): void { + console.log(state); + } +} diff --git a/cvat-canvas/src/typescript/consts.ts b/cvat-canvas/src/typescript/consts.ts new file mode 100644 index 00000000..69a926d1 --- /dev/null +++ b/cvat-canvas/src/typescript/consts.ts @@ -0,0 +1,24 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +const BASE_STROKE_WIDTH = 2; +const BASE_GRID_WIDTH = 1; +const BASE_POINT_SIZE = 5; +const TEXT_MARGIN = 10; +const AREA_THRESHOLD = 9; +const SIZE_THRESHOLD = 3; +const POINTS_STROKE_WIDTH = 1.5; +const POINTS_SELECTED_STROKE_WIDTH = 4; + +export default { + BASE_STROKE_WIDTH, + BASE_GRID_WIDTH, + BASE_POINT_SIZE, + TEXT_MARGIN, + AREA_THRESHOLD, + SIZE_THRESHOLD, + POINTS_STROKE_WIDTH, + POINTS_SELECTED_STROKE_WIDTH, +}; diff --git a/cvat-canvas/src/typescript/drawHandler.ts b/cvat-canvas/src/typescript/drawHandler.ts new file mode 100644 index 00000000..0c27618b --- /dev/null +++ b/cvat-canvas/src/typescript/drawHandler.ts @@ -0,0 +1,568 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +import * as SVG from 'svg.js'; +import consts from './consts'; +import 'svg.draw.js'; +import './svg.patch'; + +import { + DrawData, + Geometry, +} from './canvasModel'; + +import { + translateToSVG, + translateBetweenSVG, + displayShapeSize, + ShapeSizeElement, + pointsToString, + pointsToArray, + BBox, + Box, +} from './shared'; + +export interface DrawHandler { + draw(drawData: DrawData, geometry: Geometry): void; + cancel(): void; +} + +export class DrawHandlerImpl implements DrawHandler { + // callback is used to notify about creating new shape + private onDrawDone: (data: object) => void; + private canvas: SVG.Container; + private text: SVG.Container; + private background: SVGSVGElement; + private crosshair: { + x: SVG.Line; + y: SVG.Line; + }; + private drawData: DrawData; + private geometry: Geometry; + + // we should use any instead of SVG.Shape because svg plugins cannot change declared interface + // so, methods like draw() just undefined for SVG.Shape, but nevertheless they exist + private drawInstance: any; + private shapeSizeElement: ShapeSizeElement; + + private getFinalRectCoordinates(bbox: BBox): number[] { + const frameWidth = this.geometry.image.width; + const frameHeight = this.geometry.image.height; + + let [xtl, ytl, xbr, ybr] = translateBetweenSVG( + this.canvas.node as any as SVGSVGElement, + this.background, + [bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height], + ); + + xtl = Math.min(Math.max(xtl, 0), frameWidth); + xbr = Math.min(Math.max(xbr, 0), frameWidth); + ytl = Math.min(Math.max(ytl, 0), frameHeight); + ybr = Math.min(Math.max(ybr, 0), frameHeight); + + return [xtl, ytl, xbr, ybr]; + } + + private getFinalPolyshapeCoordinates(targetPoints: number[]): { + points: number[]; + box: Box; + } { + const points = translateBetweenSVG( + this.canvas.node as any as SVGSVGElement, + this.background, + targetPoints, + ); + + const box = { + xtl: Number.MAX_SAFE_INTEGER, + ytl: Number.MAX_SAFE_INTEGER, + xbr: Number.MAX_SAFE_INTEGER, + ybr: Number.MAX_SAFE_INTEGER, + }; + + const frameWidth = this.geometry.image.width; + const frameHeight = this.geometry.image.height; + for (let i = 0; i < points.length - 1; i += 2) { + points[i] = Math.min(Math.max(points[i], 0), frameWidth); + points[i + 1] = Math.min(Math.max(points[i + 1], 0), frameHeight); + + box.xtl = Math.min(box.xtl, points[i]); + box.ytl = Math.min(box.ytl, points[i + 1]); + box.xbr = Math.max(box.xbr, points[i]); + box.ybr = Math.max(box.ybr, points[i + 1]); + } + + return { + points, + box, + }; + } + + private addCrosshair(): void { + this.crosshair = { + x: this.canvas.line(0, 0, this.canvas.node.clientWidth, 0).attr({ + 'stroke-width': consts.BASE_STROKE_WIDTH / (2 * this.geometry.scale), + zOrder: Number.MAX_SAFE_INTEGER, + }).addClass('cvat_canvas_crosshair'), + y: this.canvas.line(0, 0, 0, this.canvas.node.clientHeight).attr({ + 'stroke-width': consts.BASE_STROKE_WIDTH / (2 * this.geometry.scale), + zOrder: Number.MAX_SAFE_INTEGER, + }).addClass('cvat_canvas_crosshair'), + }; + } + + private removeCrosshair(): void { + this.crosshair.x.remove(); + this.crosshair.y.remove(); + this.crosshair = null; + } + + private release(): void { + this.canvas.off('mousedown.draw'); + this.canvas.off('mousemove.draw'); + this.canvas.off('click.draw'); + + if (this.drawInstance) { + // Draw plugin isn't activated when draw from initialState + // So, we don't need to use any draw events + if (!this.drawData.initialState) { + this.drawInstance.off('drawdone'); + this.drawInstance.off('drawstop'); + this.drawInstance.draw('stop'); + } + + this.drawInstance.remove(); + this.drawInstance = null; + } + + if (this.shapeSizeElement) { + this.shapeSizeElement.rm(); + this.shapeSizeElement = null; + } + + if (this.crosshair) { + this.removeCrosshair(); + } + } + + private initDrawing(): void { + if (this.drawData.crosshair) { + this.addCrosshair(); + } + } + + private closeDrawing(): void { + if (this.drawInstance) { + // Draw plugin isn't activated when draw from initialState + // So, we don't need to use any draw events + if (!this.drawData.initialState) { + const { drawInstance } = this; + this.drawInstance = null; + if (this.drawData.shapeType === 'rectangle') { + drawInstance.draw('cancel'); + } else { + drawInstance.draw('done'); + } + this.drawInstance = drawInstance; + this.release(); + } else { + this.release(); + this.onDrawDone(null); + } + + // here is a cycle + // onDrawDone => controller => model => view => closeDrawing + // one call of closeDrawing is unuseful, but it's okey + } + } + + private drawBox(): void { + this.drawInstance = this.canvas.rect(); + this.drawInstance.draw({ + snapToGrid: 0.1, + }).addClass('cvat_canvas_shape_drawing').attr({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + z_order: Number.MAX_SAFE_INTEGER, + }).on('drawupdate', (): void => { + this.shapeSizeElement.update(this.drawInstance); + }).on('drawstop', (e: Event): void => { + const bbox = (e.target as SVGRectElement).getBBox(); + const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox); + + if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) { + this.onDrawDone({ + shapeType: this.drawData.shapeType, + points: [xtl, ytl, xbr, ybr], + }); + } else { + this.onDrawDone(null); + } + }); + } + + private drawPolyshape(): void { + this.drawInstance.attr({ + z_order: Number.MAX_SAFE_INTEGER, + }); + + let size = this.drawData.numberOfPoints; + const sizeDecrement = function sizeDecrement(): void { + if (!--size) { + this.drawInstance.draw('done'); + } + }.bind(this); + + const sizeIncrement = function sizeIncrement(): void { + size++; + }; + + if (this.drawData.numberOfPoints) { + this.drawInstance.on('drawstart', sizeDecrement); + this.drawInstance.on('drawpoint', sizeDecrement); + this.drawInstance.on('undopoint', sizeIncrement); + } + + // Add ability to cancel the latest drawn point + const handleUndo = function handleUndo(e: MouseEvent): void { + if (e.which === 3) { + e.stopPropagation(); + e.preventDefault(); + this.drawInstance.draw('undo'); + } + }.bind(this); + this.canvas.on('mousedown.draw', handleUndo); + + // Add ability to draw shapes by sliding + // We need to remember last drawn point + // to implementation of slide drawing + const lastDrawnPoint: { + x: number; + y: number; + } = { + x: null, + y: null, + }; + + const handleSlide = function handleSlide(e: MouseEvent): void { + // TODO: Use enumeration after typification cvat-core + if (e.shiftKey && ['polygon', 'polyline'].includes(this.drawData.shapeType)) { + if (lastDrawnPoint.x === null || lastDrawnPoint.y === null) { + this.drawInstance.draw('point', e); + } else { + const deltaTreshold = 15; + const delta = Math.sqrt( + ((e.clientX - lastDrawnPoint.x) ** 2) + + ((e.clientY - lastDrawnPoint.y) ** 2), + ); + if (delta > deltaTreshold) { + this.drawInstance.draw('point', e); + } + } + } + }.bind(this); + this.canvas.on('mousemove.draw', handleSlide); + + // We need scale just drawn points + const self = this; + this.drawInstance.on('drawstart drawpoint', (e: CustomEvent): void => { + self.transform(self.geometry); + lastDrawnPoint.x = e.detail.event.clientX; + lastDrawnPoint.y = e.detail.event.clientY; + }); + + this.drawInstance.on('drawdone', (e: CustomEvent): void => { + const targetPoints = pointsToArray((e.target as SVGElement).getAttribute('points')); + + const { + points, + box, + } = this.getFinalPolyshapeCoordinates(targetPoints); + + if (this.drawData.shapeType === 'polygon' + && ((box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD) + && points.length >= 3 * 2) { + this.onDrawDone({ + shapeType: this.drawData.shapeType, + points, + }); + } else if (this.drawData.shapeType === 'polyline' + && ((box.xbr - box.xtl) >= consts.SIZE_THRESHOLD + || (box.ybr - box.ytl) >= consts.SIZE_THRESHOLD) + && points.length >= 2 * 2) { + this.onDrawDone({ + shapeType: this.drawData.shapeType, + points, + }); + } else if (this.drawData.shapeType === 'points' + && (e.target as any).getAttribute('points') !== '0,0') { + this.onDrawDone({ + shapeType: this.drawData.shapeType, + points, + }); + } else { + this.onDrawDone(null); + } + }); + } + + private drawPolygon(): void { + this.drawInstance = (this.canvas as any).polygon().draw({ + snapToGrid: 0.1, + }).addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + }); + + this.drawPolyshape(); + } + + private drawPolyline(): void { + this.drawInstance = (this.canvas as any).polyline().draw({ + snapToGrid: 0.1, + }).addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + 'fill-opacity': 0, + }); + + this.drawPolyshape(); + } + + private drawPoints(): void { + this.drawInstance = (this.canvas as any).polygon().draw({ + snapToGrid: 0.1, + }).addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': 0, + opacity: 0, + }); + + this.drawPolyshape(); + } + + private pastePolyshape(): void { + this.canvas.on('click.draw', (e: MouseEvent): void => { + const targetPoints = (e.target as SVGElement) + .getAttribute('points') + .split(/[,\s]/g) + .map((coord): number => +coord); + + const { points } = this.getFinalPolyshapeCoordinates(targetPoints); + this.release(); + this.onDrawDone({ + shapeType: this.drawData.shapeType, + points, + occluded: this.drawData.initialState.occluded, + attributes: { ...this.drawData.initialState.attributes }, + label: this.drawData.initialState.label, + color: this.drawData.initialState.color, + }); + }); + } + + // Common settings for rectangle and polyshapes + private pasteShape(): void { + this.drawInstance.attr({ + z_order: Number.MAX_SAFE_INTEGER, + }); + + this.canvas.on('mousemove.draw', (e: MouseEvent): void => { + const [x, y] = translateToSVG( + this.canvas.node as any as SVGSVGElement, + [e.clientX, e.clientY], + ); + + const bbox = this.drawInstance.bbox(); + this.drawInstance.move(x - bbox.width / 2, y - bbox.height / 2); + }); + } + + private pasteBox(box: BBox): void { + this.drawInstance = (this.canvas as any).rect(box.width, box.height) + .move(box.x, box.y) + .addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + }); + this.pasteShape(); + + this.canvas.on('click.draw', (e: MouseEvent): void => { + const bbox = (e.target as SVGRectElement).getBBox(); + const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox); + this.release(); + this.onDrawDone({ + shapeType: this.drawData.shapeType, + points: [xtl, ytl, xbr, ybr], + occluded: this.drawData.initialState.occluded, + attributes: { ...this.drawData.initialState.attributes }, + label: this.drawData.initialState.label, + color: this.drawData.initialState.color, + }); + }); + } + + + private pastePolygon(points: string): void { + this.drawInstance = (this.canvas as any).polygon(points) + .addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + }); + this.pasteShape(); + this.pastePolyshape(); + } + + private pastePolyline(points: string): void { + this.drawInstance = (this.canvas as any).polyline(points) + .addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale, + }); + this.pasteShape(); + this.pastePolyshape(); + } + + private pastePoints(points: string): void { + this.drawInstance = (this.canvas as any).polyline(points) + .addClass('cvat_canvas_shape_drawing').style({ + 'stroke-width': 0, + }); + this.pasteShape(); + this.pastePolyshape(); + } + + private startDraw(): void { + // TODO: Use enums after typification cvat-core + if (this.drawData.initialState) { + if (this.drawData.shapeType === 'rectangle') { + const [xtl, ytl, xbr, ybr] = translateBetweenSVG( + this.background, + this.canvas.node as any as SVGSVGElement, + this.drawData.initialState.points, + ); + + this.pasteBox({ + x: xtl, + y: ytl, + width: xbr - xtl, + height: ybr - ytl, + }); + } else { + const points = translateBetweenSVG( + this.background, + this.canvas.node as any as SVGSVGElement, + this.drawData.initialState.points, + ); + + const stringifiedPoints = pointsToString(points); + + if (this.drawData.shapeType === 'polygon') { + this.pastePolygon(stringifiedPoints); + } else if (this.drawData.shapeType === 'polyline') { + this.pastePolyline(stringifiedPoints); + } else if (this.drawData.shapeType === 'points') { + this.pastePoints(stringifiedPoints); + } + } + } else if (this.drawData.shapeType === 'rectangle') { + this.drawBox(); + // Draw instance was initialized after drawBox(); + this.shapeSizeElement = displayShapeSize(this.canvas, this.text); + } else if (this.drawData.shapeType === 'polygon') { + this.drawPolygon(); + } else if (this.drawData.shapeType === 'polyline') { + this.drawPolyline(); + } else if (this.drawData.shapeType === 'points') { + this.drawPoints(); + } + } + + public constructor( + onDrawDone: (data: object) => void, + canvas: SVG.Container, + text: SVG.Container, + background: SVGSVGElement, + ) { + this.onDrawDone = onDrawDone; + this.canvas = canvas; + this.text = text; + this.background = background; + this.drawData = null; + this.geometry = null; + this.crosshair = null; + this.drawInstance = null; + + this.canvas.on('mousemove.crosshair', (e: MouseEvent): void => { + if (this.crosshair) { + const [x, y] = translateToSVG( + this.canvas.node as any as SVGSVGElement, + [e.clientX, e.clientY], + ); + + this.crosshair.x.attr({ + y1: y, + y2: y, + }); + + this.crosshair.y.attr({ + x1: x, + x2: x, + }); + } + }); + } + + public transform(geometry: Geometry): void { + this.geometry = geometry; + + if (this.shapeSizeElement && this.drawInstance && this.drawData.shapeType === 'rectangle') { + this.shapeSizeElement.update(this.drawInstance); + } + + if (this.crosshair) { + this.crosshair.x.attr({ + 'stroke-width': consts.BASE_STROKE_WIDTH / (2 * geometry.scale), + }); + this.crosshair.y.attr({ + 'stroke-width': consts.BASE_STROKE_WIDTH / (2 * geometry.scale), + }); + } + + if (this.drawInstance) { + this.drawInstance.draw('transform'); + this.drawInstance.style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / geometry.scale, + }); + + const paintHandler = this.drawInstance.remember('_paintHandler'); + + for (const point of (paintHandler as any).set.members) { + point.style( + 'stroke-width', + `${consts.POINTS_STROKE_WIDTH / geometry.scale}`, + ); + point.attr( + 'r', + `${consts.BASE_POINT_SIZE / geometry.scale}`, + ); + } + } + } + + public draw(drawData: DrawData, geometry: Geometry): void { + this.geometry = geometry; + + if (drawData.enabled) { + this.drawData = drawData; + this.initDrawing(); + this.startDraw(); + } else { + this.closeDrawing(); + this.drawData = drawData; + } + } + + public cancel(): void { + this.release(); + this.onDrawDone(null); + // here is a cycle + // onDrawDone => controller => model => view => closeDrawing + // one call of closeDrawing is unuseful, but it's okey + } +} diff --git a/cvat-canvas/src/typescript/editHandler.ts b/cvat-canvas/src/typescript/editHandler.ts new file mode 100644 index 00000000..bc0dc823 --- /dev/null +++ b/cvat-canvas/src/typescript/editHandler.ts @@ -0,0 +1,343 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +import * as SVG from 'svg.js'; +import 'svg.select.js'; + +import consts from './consts'; +import { + translateFromSVG, + translateBetweenSVG, + pointsToArray, +} from './shared'; +import { + EditData, + Geometry, +} from './canvasModel'; + +export interface EditHandler { + edit(editData: EditData): void; + transform(geometry: Geometry): void; + cancel(): void; +} + +export class EditHandlerImpl implements EditHandler { + private onEditDone: (state: any, points: number[]) => void; + private geometry: Geometry; + private canvas: SVG.Container; + private background: SVGSVGElement; + private editData: EditData; + private editedShape: SVG.Shape; + private editLine: SVG.PolyLine; + private clones: SVG.Polygon[]; + + private startEdit(): void { + // get started coordinates + const [clientX, clientY] = translateFromSVG( + this.canvas.node as any as SVGSVGElement, + this.editedShape.attr('points').split(' ')[this.editData.pointID].split(','), + ); + + // Add ability to edit shapes by sliding + // We need to remember last drawn point + // to implementation of slide drawing + const lastDrawnPoint: { + x: number; + y: number; + } = { + x: null, + y: null, + }; + + const handleSlide = function handleSlide(e: MouseEvent): void { + if (e.shiftKey) { + if (lastDrawnPoint.x === null || lastDrawnPoint.y === null) { + this.editLine.draw('point', e); + } else { + const deltaTreshold = 15; + const delta = Math.sqrt( + ((e.clientX - lastDrawnPoint.x) ** 2) + + ((e.clientY - lastDrawnPoint.y) ** 2), + ); + if (delta > deltaTreshold) { + this.editLine.draw('point', e); + } + } + } + }.bind(this); + this.canvas.on('mousemove.draw', handleSlide); + + this.editLine = (this.canvas as any).polyline().draw({ + snapToGrid: 0.1, + }).addClass('cvat_canvas_shape_drawing').style({ + 'pointer-events': 'none', + 'fill-opacity': 0, + }).on('drawstart drawpoint', (e: CustomEvent): void => { + this.transform(this.geometry); + lastDrawnPoint.x = e.detail.event.clientX; + lastDrawnPoint.y = e.detail.event.clientY; + }); + + if (this.editData.state.shapeType === 'points') { + this.editLine.style('stroke-width', 0); + } else { + // generate mouse event + const dummyEvent = new MouseEvent('mousedown', { + bubbles: true, + cancelable: true, + clientX, + clientY, + }); + (this.editLine as any).draw('point', dummyEvent); + } + } + + private stopEdit(e: MouseEvent): void { + function selectPolygon(shape: SVG.Polygon): void { + const points = translateBetweenSVG( + this.canvas.node as any as SVGSVGElement, + this.background, + pointsToArray(shape.attr('points')), + ); + + const { state } = this.editData; + this.edit({ + enabled: false, + }); + this.onEditDone(state, points); + } + + if (!this.editLine) { + return; + } + + // Get stop point and all points + const stopPointID = Array.prototype.indexOf + .call((e.target as HTMLElement).parentElement.children, e.target); + const oldPoints = this.editedShape.attr('points').trim().split(' '); + const linePoints = this.editLine.attr('points').trim().split(' '); + + if (this.editLine.attr('points') === '0,0') { + this.cancel(); + return; + } + + // Compute new point array + const [start, stop] = [this.editData.pointID, stopPointID] + .sort((a, b): number => +a - +b); + + if (this.editData.state.shapeType === 'polygon') { + if (start !== this.editData.pointID) { + linePoints.reverse(); + } + + const firstPart = oldPoints.slice(0, start) + .concat(linePoints) + .concat(oldPoints.slice(stop + 1)); + + linePoints.reverse(); + const secondPart = oldPoints.slice(start + 1, stop) + .concat(linePoints); + + if (firstPart.length < 3 || secondPart.length < 3) { + this.cancel(); + return; + } + + for (const points of [firstPart, secondPart]) { + this.clones.push(this.canvas.polygon(points.join(' ')) + .attr('fill', this.editedShape.attr('fill')) + .style('fill-opacity', '0.5') + .addClass('cvat_canvas_shape')); + } + + for (const clone of this.clones) { + clone.on('click', selectPolygon.bind(this, clone)); + clone.on('mouseenter', (): void => { + clone.addClass('cvat_canvas_shape_splitting'); + }).on('mouseleave', (): void => { + clone.removeClass('cvat_canvas_shape_splitting'); + }); + } + + (this.editLine as any).draw('stop'); + this.editLine.remove(); + this.editLine = null; + + return; + } + + let points = null; + if (this.editData.state.shapeType === 'polyline') { + if (start !== this.editData.pointID) { + linePoints.reverse(); + } + points = oldPoints.slice(0, start) + .concat(linePoints) + .concat(oldPoints.slice(stop + 1)); + } else { + points = oldPoints.concat(linePoints.slice(0, -1)); + } + + points = translateBetweenSVG( + this.canvas.node as any as SVGSVGElement, + this.background, + pointsToArray(points.join(' ')), + ); + + const { state } = this.editData; + this.edit({ + enabled: false, + }); + this.onEditDone(state, points); + } + + private setupPoints(enabled: boolean): void { + const self = this; + const stopEdit = self.stopEdit.bind(self); + + if (enabled) { + (this.editedShape as any).selectize(true, { + deepSelect: true, + pointSize: 2 * consts.BASE_POINT_SIZE / self.geometry.scale, + rotationPoint: false, + pointType(cx: number, cy: number): SVG.Circle { + const circle: SVG.Circle = this.nested + .circle(this.options.pointSize) + .stroke('black') + .fill(self.editedShape.attr('fill') || 'inherit') + .center(cx, cy) + .attr({ + 'stroke-width': consts.POINTS_STROKE_WIDTH / self.geometry.scale, + }); + + circle.node.addEventListener('mouseenter', (): void => { + circle.attr({ + 'stroke-width': consts.POINTS_SELECTED_STROKE_WIDTH / self.geometry.scale, + }); + + circle.node.addEventListener('click', stopEdit); + circle.addClass('cvat_canvas_selected_point'); + }); + + circle.node.addEventListener('mouseleave', (): void => { + circle.attr({ + 'stroke-width': consts.POINTS_STROKE_WIDTH / self.geometry.scale, + }); + + circle.node.removeEventListener('click', stopEdit); + circle.removeClass('cvat_canvas_selected_point'); + }); + + return circle; + }, + }); + } else { + (this.editedShape as any).selectize(false, { + deepSelect: true, + }); + } + } + + private release(): void { + this.canvas.off('mousemove.draw'); + + if (this.editedShape) { + this.setupPoints(false); + this.editedShape.remove(); + this.editedShape = null; + } + + if (this.editLine) { + (this.editLine as any).draw('stop'); + this.editLine.remove(); + this.editLine = null; + } + + if (this.clones.length) { + for (const clone of this.clones) { + clone.remove(); + } + this.clones = []; + } + } + + private initEditing(): void { + this.editedShape = this.canvas + .select(`#cvat_canvas_shape_${this.editData.state.clientID}`) + .first().clone(); + this.setupPoints(true); + this.startEdit(); + // draw points for this with selected and start editing till another point is clicked + // click one of two parts to remove (in case of polygon only) + + // else we can start draw polyline + // after we have got shape and points, we are waiting for second point pressed on this shape + } + + private closeEditing(): void { + this.release(); + } + + public constructor( + onEditDone: (state: any, points: number[]) => void, + canvas: SVG.Container, + background: SVGSVGElement, + ) { + this.onEditDone = onEditDone; + this.canvas = canvas; + this.background = background; + this.editData = null; + this.editedShape = null; + this.editLine = null; + this.geometry = null; + this.clones = []; + } + + public edit(editData: any): void { + if (editData.enabled) { + if (editData.state.shapeType !== 'rectangle') { + this.editData = editData; + this.initEditing(); + } else { + this.cancel(); + } + } else { + this.closeEditing(); + this.editData = editData; + } + } + + public cancel(): void { + this.release(); + this.onEditDone(null, null); + } + + public transform(geometry: Geometry): void { + this.geometry = geometry; + + if (this.editLine) { + (this.editLine as any).draw('transform'); + if (this.editData.state.shapeType !== 'points') { + this.editLine.style({ + 'stroke-width': consts.BASE_STROKE_WIDTH / geometry.scale, + }); + } + + const paintHandler = this.editLine.remember('_paintHandler'); + + for (const point of (paintHandler as any).set.members) { + point.style( + 'stroke-width', + `${consts.POINTS_STROKE_WIDTH / geometry.scale}`, + ); + point.attr( + 'r', + `${consts.BASE_POINT_SIZE / geometry.scale}`, + ); + } + } + } +} diff --git a/cvat-canvas/src/typescript/groupHandler.ts b/cvat-canvas/src/typescript/groupHandler.ts new file mode 100644 index 00000000..9b6f1ec3 --- /dev/null +++ b/cvat-canvas/src/typescript/groupHandler.ts @@ -0,0 +1,208 @@ +import * as SVG from 'svg.js'; +import { GroupData } from './canvasModel'; + +import { + translateToSVG, +} from './shared'; + + +export interface GroupHandler { + group(groupData: GroupData): void; + select(state: any): void; + cancel(): void; +} + +export class GroupHandlerImpl implements GroupHandler { + // callback is used to notify about grouping end + private onGroupDone: (objects: any[]) => void; + private getStates: () => any[]; + private onFindObject: (event: MouseEvent) => void; + private onSelectStart: (event: MouseEvent) => void; + private onSelectUpdate: (event: MouseEvent) => void; + private onSelectStop: (event: MouseEvent) => void; + private selectionRect: SVG.Rect; + private startSelectionPoint: { + x: number; + y: number; + }; + private canvas: SVG.Container; + private initialized: boolean; + private states: any[]; + private highlightedShapes: Record; + + private getSelectionBox(event: MouseEvent): { + xtl: number; + ytl: number; + xbr: number; + ybr: number; + } { + const point = translateToSVG( + (this.canvas.node as any as SVGSVGElement), + [event.clientX, event.clientY], + ); + const stopSelectionPoint = { + x: point[0], + y: point[1], + }; + + return { + xtl: Math.min(this.startSelectionPoint.x, stopSelectionPoint.x), + ytl: Math.min(this.startSelectionPoint.y, stopSelectionPoint.y), + xbr: Math.max(this.startSelectionPoint.x, stopSelectionPoint.x), + ybr: Math.max(this.startSelectionPoint.y, stopSelectionPoint.y), + }; + } + + private release(): void { + this.canvas.node.removeEventListener('click', this.onFindObject); + this.canvas.node.removeEventListener('mousedown', this.onSelectStart); + this.canvas.node.removeEventListener('mousemove', this.onSelectUpdate); + this.canvas.node.removeEventListener('mouseup', this.onSelectStop); + this.canvas.node.removeEventListener('mouseleave', this.onSelectStop); + + for (const state of this.states) { + const shape = this.highlightedShapes[state.clientID]; + shape.removeClass('cvat_canvas_shape_grouping'); + } + this.states = []; + this.highlightedShapes = {}; + this.initialized = false; + this.selectionRect = null; + this.startSelectionPoint = { + x: null, + y: null, + }; + } + + private initGrouping(): void { + this.canvas.node.addEventListener('click', this.onFindObject); + this.canvas.node.addEventListener('mousedown', this.onSelectStart); + this.canvas.node.addEventListener('mousemove', this.onSelectUpdate); + this.canvas.node.addEventListener('mouseup', this.onSelectStop); + this.canvas.node.addEventListener('mouseleave', this.onSelectStop); + + this.initialized = true; + } + + private closeGrouping(): void { + if (this.initialized) { + const { states } = this; + this.release(); + + if (states.length) { + this.onGroupDone(states); + } else { + this.onGroupDone(null); + } + } + } + + public constructor( + onGroupDone: (objects: any[]) => void, + getStates: () => any[], + onFindObject: (event: MouseEvent) => void, + canvas: SVG.Container, + ) { + this.onGroupDone = onGroupDone; + this.getStates = getStates; + this.onFindObject = onFindObject; + this.canvas = canvas; + this.states = []; + this.highlightedShapes = {}; + this.selectionRect = null; + this.startSelectionPoint = { + x: null, + y: null, + }; + + this.onSelectStart = function (event: MouseEvent): void { + if (!this.selectionRect) { + const point = translateToSVG(this.canvas.node, [event.clientX, event.clientY]); + this.startSelectionPoint = { + x: point[0], + y: point[1], + }; + + this.selectionRect = this.canvas.rect().addClass('cvat_canvas_shape_grouping'); + this.selectionRect.attr({ ...this.startSelectionPoint }); + } + }.bind(this); + + this.onSelectUpdate = function (event: MouseEvent): void { + // called on mousemove + if (this.selectionRect) { + const box = this.getSelectionBox(event); + + this.selectionRect.attr({ + x: box.xtl, + y: box.ytl, + width: box.xbr - box.xtl, + height: box.ybr - box.ytl, + }); + } + }.bind(this); + + this.onSelectStop = function (event: MouseEvent): void { + // called on mouseup, mouseleave + if (this.selectionRect) { + this.selectionRect.remove(); + this.selectionRect = null; + + const box = this.getSelectionBox(event); + const shapes = (this.canvas.select('.cvat_canvas_shape') as any).members; + for (const shape of shapes) { + // TODO: Doesn't work properly for groups + const bbox = shape.bbox(); + const clientID = shape.attr('clientID'); + if (bbox.x > box.xtl && bbox.y > box.ytl + && bbox.x + bbox.width < box.xbr + && bbox.y + bbox.height < box.ybr + && !(clientID in this.highlightedShapes)) { + const objectState = this.getStates() + .filter((state: any): boolean => state.clientID === clientID)[0]; + + if (objectState) { + this.states.push(objectState); + this.highlightedShapes[clientID] = shape; + (shape as any).addClass('cvat_canvas_shape_grouping'); + } + } + } + } + }.bind(this); + } + + /* eslint-disable-next-line */ + public group(groupData: GroupData): void { + if (groupData.enabled) { + this.initGrouping(); + } else { + this.closeGrouping(); + } + } + + public select(objectState: any): void { + const stateIndexes = this.states.map((state): number => state.clientID); + const includes = stateIndexes.indexOf(objectState.clientID); + if (includes !== -1) { + const shape = this.highlightedShapes[objectState.clientID]; + this.states.splice(includes, 1); + if (shape) { + delete this.highlightedShapes[objectState.clientID]; + shape.removeClass('cvat_canvas_shape_grouping'); + } + } else { + const shape = this.canvas.select(`#cvat_canvas_shape_${objectState.clientID}`).first(); + if (shape) { + this.states.push(objectState); + this.highlightedShapes[objectState.clientID] = shape; + shape.addClass('cvat_canvas_shape_grouping'); + } + } + } + + public cancel(): void { + this.release(); + this.onGroupDone(null); + } +} diff --git a/cvat-canvas/src/typescript/master.ts b/cvat-canvas/src/typescript/master.ts new file mode 100644 index 00000000..42e9a5e0 --- /dev/null +++ b/cvat-canvas/src/typescript/master.ts @@ -0,0 +1,45 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +export interface Master { + subscribe(listener: Listener): void; + unsubscribe(listener: Listener): void; + unsubscribeAll(): void; + notify(reason: string): void; +} + +export interface Listener { + notify(master: Master, reason: string): void; +} + +export class MasterImpl implements Master { + private listeners: Listener[]; + + public constructor() { + this.listeners = []; + } + + public subscribe(listener: Listener): void { + this.listeners.push(listener); + } + + public unsubscribe(listener: Listener): void { + for (let i = 0; i < this.listeners.length; i++) { + if (this.listeners[i] === listener) { + this.listeners.splice(i, 1); + } + } + } + + public unsubscribeAll(): void { + this.listeners = []; + } + + public notify(reason: string): void { + for (const listener of this.listeners) { + listener.notify(this, reason); + } + } +} diff --git a/cvat-canvas/src/typescript/mergeHandler.ts b/cvat-canvas/src/typescript/mergeHandler.ts new file mode 100644 index 00000000..a19961d8 --- /dev/null +++ b/cvat-canvas/src/typescript/mergeHandler.ts @@ -0,0 +1,133 @@ +import * as SVG from 'svg.js'; +import { MergeData } from './canvasModel'; + +export interface MergeHandler { + merge(mergeData: MergeData): void; + select(state: any): void; + cancel(): void; +} + + +export class MergeHandlerImpl implements MergeHandler { + // callback is used to notify about merging end + private onMergeDone: (objects: any[]) => void; + private onFindObject: (event: MouseEvent) => void; + private canvas: SVG.Container; + private initialized: boolean; + private states: any[]; // are being merged + private highlightedShapes: Record; + private constraints: { + labelID: number; + shapeType: string; + }; + + private addConstraints(): void { + const shape = this.states[0]; + this.constraints = { + labelID: shape.label.id, + shapeType: shape.shapeType, + }; + } + + private removeConstraints(): void { + this.constraints = null; + } + + private checkConstraints(state: any): boolean { + return !this.constraints || (state.label.id === this.constraints.labelID + && state.shapeType === this.constraints.shapeType); + } + + private release(): void { + this.removeConstraints(); + this.canvas.node.removeEventListener('click', this.onFindObject); + for (const state of this.states) { + const shape = this.highlightedShapes[state.clientID]; + shape.removeClass('cvat_canvas_shape_merging'); + } + this.states = []; + this.highlightedShapes = {}; + this.initialized = false; + } + + private initMerging(): void { + this.canvas.node.addEventListener('click', this.onFindObject); + this.initialized = true; + } + + private closeMerging(): void { + if (this.initialized) { + const { states } = this; + this.release(); + + if (states.length > 1) { + this.onMergeDone(states); + } else { + this.onMergeDone(null); + // here is a cycle + // onMergeDone => controller => model => view => closeMerging + // one call of closeMerging is unuseful, but it's okey + } + } + } + + public constructor( + onMergeDone: (objects: any[]) => void, + onFindObject: (event: MouseEvent) => void, + canvas: SVG.Container, + ) { + this.onMergeDone = onMergeDone; + this.onFindObject = onFindObject; + this.canvas = canvas; + this.states = []; + this.highlightedShapes = {}; + this.constraints = null; + this.initialized = false; + } + + public merge(mergeData: MergeData): void { + if (mergeData.enabled) { + this.initMerging(); + } else { + this.closeMerging(); + } + } + + public select(objectState: any): void { + const stateIndexes = this.states.map((state): number => state.clientID); + const stateFrames = this.states.map((state): number => state.frame); + const includes = stateIndexes.indexOf(objectState.clientID); + if (includes !== -1) { + const shape = this.highlightedShapes[objectState.clientID]; + this.states.splice(includes, 1); + if (shape) { + delete this.highlightedShapes[objectState.clientID]; + shape.removeClass('cvat_canvas_shape_merging'); + } + + if (!this.states.length) { + this.removeConstraints(); + } + } else { + const shape = this.canvas.select(`#cvat_canvas_shape_${objectState.clientID}`).first(); + if (shape && this.checkConstraints(objectState) + && !stateFrames.includes(objectState.frame)) { + this.states.push(objectState); + this.highlightedShapes[objectState.clientID] = shape; + shape.addClass('cvat_canvas_shape_merging'); + + if (this.states.length === 1) { + this.addConstraints(); + } + } + } + } + + public cancel(): void { + this.release(); + this.onMergeDone(null); + // here is a cycle + // onMergeDone => controller => model => view => closeMerging + // one call of closeMerging is unuseful, but it's okey + } +} diff --git a/cvat-canvas/src/typescript/shared.ts b/cvat-canvas/src/typescript/shared.ts new file mode 100644 index 00000000..37cecd2b --- /dev/null +++ b/cvat-canvas/src/typescript/shared.ts @@ -0,0 +1,113 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +import * as SVG from 'svg.js'; +import consts from './consts'; + +export interface ShapeSizeElement { + sizeElement: any; + update(shape: SVG.Shape): void; + rm(): void; +} + +export interface Box { + xtl: number; + ytl: number; + xbr: number; + ybr: number; +} + +export interface BBox { + width: number; + height: number; + x: number; + y: number; +} + +// Translate point array from the client coordinate system +// to a coordinate system of a canvas +export function translateFromSVG(svg: SVGSVGElement, points: number[]): number[] { + const output = []; + const transformationMatrix = svg.getScreenCTM(); + let pt = svg.createSVGPoint(); + for (let i = 0; i < points.length - 1; i += 2) { + pt.x = points[i]; + pt.y = points[i + 1]; + pt = pt.matrixTransform(transformationMatrix); + output.push(pt.x, pt.y); + } + + return output; +} + +// Translate point array from a coordinate system of a canvas +// to the client coordinate system +export function translateToSVG(svg: SVGSVGElement, points: number[]): number[] { + const output = []; + const transformationMatrix = svg.getScreenCTM().inverse(); + let pt = svg.createSVGPoint(); + for (let i = 0; i < points.length; i += 2) { + pt.x = points[i]; + pt.y = points[i + 1]; + pt = pt.matrixTransform(transformationMatrix); + output.push(pt.x, pt.y); + } + + return output; +} + +// Translate point array from the first canvas coordinate system +// to another +export function translateBetweenSVG( + from: SVGSVGElement, + to: SVGSVGElement, + points: number[], +): number[] { + return translateToSVG(to, translateFromSVG(from, points)); +} + +export function pointsToString(points: number[]): string { + return points.reduce((acc, val, idx): string => { + if (idx % 2) { + return `${acc},${val}`; + } + + return `${acc} ${val}`; + }, ''); +} + +export function pointsToArray(points: string): number[] { + return points.trim().split(/[,\s]+/g) + .map((coord: string): number => +coord); +} + +export function displayShapeSize( + shapesContainer: SVG.Container, + textContainer: SVG.Container, +): ShapeSizeElement { + const shapeSize: ShapeSizeElement = { + sizeElement: textContainer.text('').font({ + weight: 'bolder', + }).fill('white').addClass('cvat_canvas_text'), + update(shape: SVG.Shape): void{ + const bbox = shape.bbox(); + const text = `${bbox.width.toFixed(1)}x${bbox.height.toFixed(1)}`; + const [x, y]: number[] = translateToSVG( + textContainer.node as any as SVGSVGElement, + translateFromSVG((shapesContainer.node as any as SVGSVGElement), [bbox.x, bbox.y]), + ); + this.sizeElement.clear().plain(text) + .move(x + consts.TEXT_MARGIN, y + consts.TEXT_MARGIN); + }, + rm(): void { + if (this.sizeElement) { + this.sizeElement.remove(); + this.sizeElement = null; + } + }, + }; + + return shapeSize; +} diff --git a/cvat-canvas/src/typescript/splitHandler.ts b/cvat-canvas/src/typescript/splitHandler.ts new file mode 100644 index 00000000..46e5b646 --- /dev/null +++ b/cvat-canvas/src/typescript/splitHandler.ts @@ -0,0 +1,98 @@ +import * as SVG from 'svg.js'; +import { SplitData } from './canvasModel'; + +export interface SplitHandler { + split(splitData: SplitData): void; + select(state: any): void; + cancel(): void; +} + +export class SplitHandlerImpl implements SplitHandler { + // callback is used to notify about splitting end + private onSplitDone: (object: any) => void; + private onFindObject: (event: MouseEvent) => void; + private canvas: SVG.Container; + private highlightedShape: SVG.Shape; + private initialized: boolean; + private splitDone: boolean; + + private resetShape(): void { + if (this.highlightedShape) { + this.highlightedShape.removeClass('cvat_canvas_shape_splitting'); + this.highlightedShape.off('click.split'); + this.highlightedShape = null; + } + } + + private release(): void { + if (this.initialized) { + this.resetShape(); + this.canvas.node.removeEventListener('mousemove', this.onFindObject); + this.initialized = false; + } + } + + private initSplitting(): void { + this.canvas.node.addEventListener('mousemove', this.onFindObject); + this.initialized = true; + this.splitDone = false; + } + + private closeSplitting(): void { + // Split done is true if an object was splitted + // Split also can be called with { enabled: false } without splitting an object + if (!this.splitDone) { + this.onSplitDone(null); + } + this.release(); + } + + public constructor( + onSplitDone: (object: any) => void, + onFindObject: (event: MouseEvent) => void, + canvas: SVG.Container, + ) { + this.onSplitDone = onSplitDone; + this.onFindObject = onFindObject; + this.canvas = canvas; + this.highlightedShape = null; + this.initialized = false; + this.splitDone = false; + } + + public split(splitData: SplitData): void { + if (splitData.enabled) { + this.initSplitting(); + } else { + this.closeSplitting(); + } + } + + public select(state: any): void { + if (state.objectType === 'track') { + const shape = this.canvas.select(`#cvat_canvas_shape_${state.clientID}`).first(); + if (shape && shape !== this.highlightedShape) { + this.resetShape(); + this.highlightedShape = shape; + this.highlightedShape.addClass('cvat_canvas_shape_splitting'); + this.canvas.node.append(this.highlightedShape.node); + this.highlightedShape.on('click.split', (): void => { + this.splitDone = true; + this.onSplitDone(state); + }, { + once: true, + }); + } + } else { + this.resetShape(); + } + } + + public cancel(): void { + this.release(); + this.onSplitDone(null); + // here is a cycle + // onSplitDone => controller => model => view => closeSplitting + // one call of closeMerging is unuseful, but it's okey + } +} diff --git a/cvat-canvas/src/typescript/svg.patch.ts b/cvat-canvas/src/typescript/svg.patch.ts new file mode 100644 index 00000000..98cacbf3 --- /dev/null +++ b/cvat-canvas/src/typescript/svg.patch.ts @@ -0,0 +1,172 @@ +import * as SVG from 'svg.js'; + +/* eslint-disable */ + +import 'svg.draggable.js'; +import 'svg.resize.js'; +import 'svg.select.js'; +import 'svg.draw.js'; + +// Update constructor +const originalDraw = SVG.Element.prototype.draw; +SVG.Element.prototype.draw = function constructor(...args: any): any { + let handler = this.remember('_paintHandler'); + if (!handler) { + originalDraw.call(this, ...args); + handler = this.remember('_paintHandler'); + handler.set = new SVG.Set(); + } else { + originalDraw.call(this, ...args); + } + + return this; +}; +for (const key of Object.keys(originalDraw)) { + SVG.Element.prototype.draw[key] = originalDraw[key]; +} + +// Create undo for polygones and polylines +function undo(): void { + if (this.set.length()) { + this.set.members.splice(-1, 1)[0].remove(); + this.el.array().value.splice(-2, 1); + this.el.plot(this.el.array()); + this.el.fire('undopoint'); + } +} + +SVG.Element.prototype.draw.extend('polyline', Object.assign({}, + SVG.Element.prototype.draw.plugins.polyline, + { + undo: undo, + }, +)); + +SVG.Element.prototype.draw.extend('polygon', Object.assign({}, + SVG.Element.prototype.draw.plugins.polygon, + { + undo: undo, + }, +)); + + +// Create transform for rect, polyline and polygon +function transform(): void { + this.m = this.el.node.getScreenCTM().inverse(); + this.offset = { x: window.pageXOffset, y: window.pageYOffset }; +} + +SVG.Element.prototype.draw.extend('rect', Object.assign({}, + SVG.Element.prototype.draw.plugins.rect, + { + transform: transform, + }, +)); + +SVG.Element.prototype.draw.extend('polyline', Object.assign({}, + SVG.Element.prototype.draw.plugins.polyline, + { + transform: transform, + }, +)); + +SVG.Element.prototype.draw.extend('polygon', Object.assign({}, + SVG.Element.prototype.draw.plugins.polygon, + { + transform: transform, + }, +)); + +// Fix method drawCircles +function drawCircles(): void { + const array = this.el.array().valueOf(); + + this.set.each(function (): void { + this.remove(); + }); + + this.set.clear(); + + for (let i = 0; i < array.length - 1; ++i) { + [this.p.x] = array[i]; + [, this.p.y] = array[i]; + + const p = this.p.matrixTransform( + this.parent.node.getScreenCTM() + .inverse() + .multiply(this.el.node.getScreenCTM()), + ); + + this.set.add( + this.parent + .circle(5) + .stroke({ + width: 1, + }).fill('#ccc') + .center(p.x, p.y), + ); + } +} + +SVG.Element.prototype.draw.extend('line', Object.assign({}, + SVG.Element.prototype.draw.plugins.line, + { + drawCircles: drawCircles, + } +)); + +SVG.Element.prototype.draw.extend('polyline', Object.assign({}, + SVG.Element.prototype.draw.plugins.polyline, + { + drawCircles: drawCircles, + } +)); + +SVG.Element.prototype.draw.extend('polygon', Object.assign({}, + SVG.Element.prototype.draw.plugins.polygon, + { + drawCircles: drawCircles, + } +)); + +// Fix method drag +const originalDraggable = SVG.Element.prototype.draggable; +SVG.Element.prototype.draggable = function constructor(...args: any): any { + let handler = this.remember('_draggable'); + if (!handler) { + originalDraggable.call(this, ...args); + handler = this.remember('_draggable'); + handler.drag = function(e: any) { + this.m = this.el.node.getScreenCTM().inverse(); + return handler.constructor.prototype.drag.call(this, e); + } + } else { + originalDraggable.call(this, ...args); + } + + return this; +}; +for (const key of Object.keys(originalDraggable)) { + SVG.Element.prototype.draggable[key] = originalDraggable[key]; +} + +// Fix method resize +const originalResize = SVG.Element.prototype.resize; +SVG.Element.prototype.resize = function constructor(...args: any): any { + let handler = this.remember('_resizeHandler'); + if (!handler) { + originalResize.call(this, ...args); + handler = this.remember('_resizeHandler'); + handler.update = function(e: any) { + this.m = this.el.node.getScreenCTM().inverse(); + return handler.constructor.prototype.update.call(this, e); + } + } else { + originalResize.call(this, ...args); + } + + return this; +}; +for (const key of Object.keys(originalResize)) { + SVG.Element.prototype.resize[key] = originalResize[key]; +} diff --git a/cvat-canvas/tsconfig.json b/cvat-canvas/tsconfig.json new file mode 100644 index 00000000..f919ec6c --- /dev/null +++ b/cvat-canvas/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "emitDeclarationOnly": true, + "module": "es6", + "target": "es6", + "noImplicitAny": true, + "preserveConstEnums": true, + "declaration": true, + "moduleResolution": "node", + "declarationDir": "dist/declaration", + "paths": { + "cvat-canvas.node": ["dist/cvat-canvas.node"] + } + }, + "include": [ + "src/typescript/*.ts" + ] +} diff --git a/cvat-canvas/webpack.config.js b/cvat-canvas/webpack.config.js new file mode 100644 index 00000000..aa68cf61 --- /dev/null +++ b/cvat-canvas/webpack.config.js @@ -0,0 +1,93 @@ +/* eslint-disable */ +const path = require('path'); +const DtsBundleWebpack = require('dts-bundle-webpack') + +const nodeConfig = { + target: 'node', + mode: 'production', + devtool: 'source-map', + entry: './src/typescript/canvas.ts', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'cvat-canvas.node.js', + library: 'canvas', + libraryTarget: 'commonjs', + }, + resolve: { + extensions: ['.ts', '.js', '.json'], + }, + module: { + rules: [{ + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + ['@babel/preset-env'], + ['@babel/typescript'], + ], + sourceType: 'unambiguous', + }, + }, + }, { + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }], + }, + plugins: [ + new DtsBundleWebpack({ + name: 'cvat-canvas.node', + main: 'dist/declaration/canvas.d.ts', + out: '../cvat-canvas.node.d.ts', + }), + ] +}; + +const webConfig = { + target: 'web', + mode: 'production', + devtool: 'source-map', + entry: './src/typescript/canvas.ts', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'cvat-canvas.js', + library: 'canvas', + libraryTarget: 'window', + }, + devServer: { + contentBase: path.join(__dirname, 'dist'), + compress: false, + inline: true, + port: 3000, + }, + resolve: { + extensions: ['.ts', '.js', '.json'], + }, + module: { + rules: [{ + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + ['@babel/preset-env'], + ['@babel/typescript'], + ], + sourceType: 'unambiguous', + }, + }, + }, { + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }], + }, + plugins: [ + new DtsBundleWebpack({ + name: 'cvat-canvas', + main: 'dist/declaration/canvas.d.ts', + out: '../cvat-canvas.d.ts', + }), + ] +}; + +module.exports = [webConfig, nodeConfig] diff --git a/cvat-core/.eslintrc.js b/cvat-core/.eslintrc.js new file mode 100644 index 00000000..ff0d3398 --- /dev/null +++ b/cvat-core/.eslintrc.js @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 Intel Corporation + * + * SPDX-License-Identifier: MIT + */ + + module.exports = { + "env": { + "node": false, + "browser": true, + "es6": true, + "jquery": true, + "qunit": true, + }, + "parserOptions": { + "parser": "babel-eslint", + "sourceType": "module", + "ecmaVersion": 2018, + }, + "plugins": [ + "security", + "no-unsanitized", + "no-unsafe-innerhtml", + ], + "extends": [ + "eslint:recommended", + "plugin:security/recommended", + "plugin:no-unsanitized/DOM", + "airbnb-base", + ], + "rules": { + "no-await-in-loop": [0], + "global-require": [0], + "no-new": [0], + "class-methods-use-this": [0], + "no-restricted-properties": [0, { + "object": "Math", + "property": "pow", + }], + "no-plusplus": [0], + "no-param-reassign": [0], + "no-underscore-dangle": ["error", { "allowAfterThis": true }], + "no-restricted-syntax": [0, {"selector": "ForOfStatement"}], + "no-continue": [0], + "no-unsafe-innerhtml/no-unsafe-innerhtml": 1, + // This rule actual for user input data on the node.js environment mainly. + "security/detect-object-injection": 0, + "indent": ["warn", 4], + "no-useless-constructor": 0, + "func-names": [0], + "valid-typeof": [0], + "no-console": [0], // this rule deprecates console.log, console.warn etc. because "it is not good in production code" + }, +}; diff --git a/cvat-core/.gitignore b/cvat-core/.gitignore new file mode 100644 index 00000000..80b08369 --- /dev/null +++ b/cvat-core/.gitignore @@ -0,0 +1,6 @@ +docs +node_modules +reports +package-lock.json +yarn.lock +dist diff --git a/cvat-core/README.md b/cvat-core/README.md new file mode 100644 index 00000000..5f00bc03 --- /dev/null +++ b/cvat-core/README.md @@ -0,0 +1,39 @@ +# Module CVAT-CORE + +## Description +This CVAT module is a clien-side JavaScipt library to management of objects, frames, logs, etc. +It contains the core logic of the Computer Vision Annotation Tool. + +### Commands + +- Dependencies installation +```bash +npm install +``` + +- Building the module from sources in the ```dist``` directory: +```bash +npm run build +npm run build -- --mode=development # without a minification +``` + +- Building the documentation in the ```docs``` directory: +```bash +npm run-script docs +``` + +- Running of tests: +```bash +npm run-script test +``` + +- Updating of a module version: +```bash +npm version patch # updated after minor fixes +npm version minor # updated after major changes which don't affect API compatibility with previous versions +npm version major # updated after major changes which affect API compatibility with previous versions +``` + +Visual studio code configurations: +- cvat.js debug starts debugging with entrypoint api.js +- cvat.js test builds library and runs entrypoint tests.js diff --git a/cvat-core/jest.config.js b/cvat-core/jest.config.js new file mode 100644 index 00000000..c7712052 --- /dev/null +++ b/cvat-core/jest.config.js @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation + * SPDX-License-Identifier: MIT + */ + +/* global + require:false +*/ + +const { defaults } = require('jest-config'); + +module.exports = { + coverageDirectory: 'reports/coverage', + coverageReporters: ['lcov'], + moduleFileExtensions: [ + ...defaults.moduleFileExtensions, + 'ts', + 'tsx', + ], + reporters: [ + 'default', + ['jest-junit', { outputDirectory: 'reports/junit' }], + ], + testMatch: [ + '**/tests/**/*.js', + ], + testPathIgnorePatterns: [ + '/node_modules/', + '/tests/mocks/*', + ], + automock: false, +}; diff --git a/cvat-core/jsdoc.config.js b/cvat-core/jsdoc.config.js new file mode 100644 index 00000000..a51208c5 --- /dev/null +++ b/cvat-core/jsdoc.config.js @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation + * SPDX-License-Identifier: MIT + */ + + +module.exports = { + plugins: [], + recurseDepth: 10, + source: { + includePattern: '.+\\.js(doc|x)?$', + excludePattern: '(^|\\/|\\\\)_', + }, + sourceType: 'module', + tags: { + allowUnknownTags: false, + dictionaries: ['jsdoc', 'closure'], + }, + templates: { + cleverLinks: false, + monospaceLinks: false, + default: { + outputSourceFiles: false, + }, + }, +}; diff --git a/cvat-core/package.json b/cvat-core/package.json new file mode 100644 index 00000000..deab3a7f --- /dev/null +++ b/cvat-core/package.json @@ -0,0 +1,45 @@ +{ + "name": "cvat-core.js", + "version": "0.1.0", + "description": "Part of Computer Vision Tool which presents an interface for client-side integration", + "main": "babel.config.js", + "scripts": { + "build": "webpack", + "test": "jest --config=jest.config.js --coverage", + "docs": "jsdoc --readme README.md src/*.js -p -c jsdoc.config.js -d docs", + "coveralls": "cat ./reports/coverage/lcov.info | coveralls" + }, + "author": "Intel", + "license": "MIT", + "devDependencies": { + "@babel/cli": "^7.4.4", + "@babel/core": "^7.4.4", + "@babel/preset-env": "^7.4.4", + "airbnb": "0.0.2", + "babel-eslint": "^10.0.1", + "babel-loader": "^8.0.6", + "core-js": "^3.0.1", + "coveralls": "^3.0.5", + "eslint": "6.1.0", + "eslint-config-airbnb-base": "14.0.0", + "eslint-plugin-import": "2.18.2", + "eslint-plugin-no-unsafe-innerhtml": "^1.0.16", + "eslint-plugin-no-unsanitized": "^3.0.2", + "eslint-plugin-security": "^1.4.0", + "jest": "^24.8.0", + "jest-junit": "^6.4.0", + "jsdoc": "^3.6.2", + "webpack": "^4.31.0", + "webpack-cli": "^3.3.2" + }, + "dependencies": { + "axios": "^0.18.0", + "browser-or-node": "^1.2.1", + "error-stack-parser": "^2.0.2", + "form-data": "^2.5.0", + "jest-config": "^24.8.0", + "js-cookie": "^2.2.0", + "platform": "^1.3.5", + "store": "^2.0.12" + } +} diff --git a/cvat-core/src/annotation-format.js b/cvat-core/src/annotation-format.js new file mode 100644 index 00000000..0bf8cfdf --- /dev/null +++ b/cvat-core/src/annotation-format.js @@ -0,0 +1,235 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +(() => { + /** + * Class representing an annotation loader + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class Loader { + constructor(initialData) { + const data = { + display_name: initialData.display_name, + format: initialData.format, + handler: initialData.handler, + version: initialData.version, + }; + + Object.defineProperties(this, { + name: { + /** + * @name name + * @type {string} + * @memberof module:API.cvat.classes.Loader + * @readonly + * @instance + */ + get: () => data.display_name, + }, + format: { + /** + * @name format + * @type {string} + * @memberof module:API.cvat.classes.Loader + * @readonly + * @instance + */ + get: () => data.format, + }, + handler: { + /** + * @name handler + * @type {string} + * @memberof module:API.cvat.classes.Loader + * @readonly + * @instance + */ + get: () => data.handler, + }, + version: { + /** + * @name version + * @type {string} + * @memberof module:API.cvat.classes.Loader + * @readonly + * @instance + */ + get: () => data.version, + }, + }); + } + } + + /** + * Class representing an annotation dumper + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class Dumper { + constructor(initialData) { + const data = { + display_name: initialData.display_name, + format: initialData.format, + handler: initialData.handler, + version: initialData.version, + }; + + Object.defineProperties(this, { + name: { + /** + * @name name + * @type {string} + * @memberof module:API.cvat.classes.Dumper + * @readonly + * @instance + */ + get: () => data.display_name, + }, + format: { + /** + * @name format + * @type {string} + * @memberof module:API.cvat.classes.Dumper + * @readonly + * @instance + */ + get: () => data.format, + }, + handler: { + /** + * @name handler + * @type {string} + * @memberof module:API.cvat.classes.Dumper + * @readonly + * @instance + */ + get: () => data.handler, + }, + version: { + /** + * @name version + * @type {string} + * @memberof module:API.cvat.classes.Dumper + * @readonly + * @instance + */ + get: () => data.version, + }, + }); + } + } + + /** + * Class representing an annotation format + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class AnnotationFormat { + constructor(initialData) { + const data = { + created_date: initialData.created_date, + updated_date: initialData.updated_date, + id: initialData.id, + owner: initialData.owner, + name: initialData.name, + handler_file: initialData.handler_file, + }; + + data.dumpers = initialData.dumpers.map(el => new Dumper(el)); + data.loaders = initialData.loaders.map(el => new Loader(el)); + + // Now all fields are readonly + Object.defineProperties(this, { + id: { + /** + * @name id + * @type {integer} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => data.id, + }, + owner: { + /** + * @name owner + * @type {integer} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => data.owner, + }, + name: { + /** + * @name name + * @type {string} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => data.name, + }, + createdDate: { + /** + * @name createdDate + * @type {string} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => data.created_date, + }, + updatedDate: { + /** + * @name updatedDate + * @type {string} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => data.updated_date, + }, + handlerFile: { + /** + * @name handlerFile + * @type {string} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => data.handler_file, + }, + loaders: { + /** + * @name loaders + * @type {module:API.cvat.classes.Loader[]} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => [...data.loaders], + }, + dumpers: { + /** + * @name dumpers + * @type {module:API.cvat.classes.Dumper[]} + * @memberof module:API.cvat.classes.AnnotationFormat + * @readonly + * @instance + */ + get: () => [...data.dumpers], + }, + }); + } + } + + module.exports = { + AnnotationFormat, + Loader, + Dumper, + }; +})(); diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js new file mode 100644 index 00000000..5e14974b --- /dev/null +++ b/cvat-core/src/annotations-collection.js @@ -0,0 +1,763 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const { + RectangleShape, + PolygonShape, + PolylineShape, + PointsShape, + RectangleTrack, + PolygonTrack, + PolylineTrack, + PointsTrack, + Track, + Shape, + Tag, + objectStateFactory, + } = require('./annotations-objects'); + const { checkObjectType } = require('./common'); + const Statistics = require('./statistics'); + const { Label } = require('./labels'); + const { + DataError, + ArgumentError, + ScriptingError, + } = require('./exceptions'); + + const { + ObjectShape, + ObjectType, + } = require('./enums'); + const ObjectState = require('./object-state'); + + const colors = [ + '#0066FF', '#AF593E', '#01A368', '#FF861F', '#ED0A3F', '#FF3F34', '#76D7EA', + '#8359A3', '#FBE870', '#C5E17A', '#03BB85', '#FFDF00', '#8B8680', '#0A6B0D', + '#8FD8D8', '#A36F40', '#F653A6', '#CA3435', '#FFCBA4', '#FF99CC', '#FA9D5A', + '#FFAE42', '#A78B00', '#788193', '#514E49', '#1164B4', '#F4FA9F', '#FED8B1', + '#C32148', '#01796F', '#E90067', '#FF91A4', '#404E5A', '#6CDAE7', '#FFC1CC', + '#006A93', '#867200', '#E2B631', '#6EEB6E', '#FFC800', '#CC99BA', '#FF007C', + '#BC6CAC', '#DCCCD7', '#EBE1C2', '#A6AAAE', '#B99685', '#0086A7', '#5E4330', + '#C8A2C8', '#708EB3', '#BC8777', '#B2592D', '#497E48', '#6A2963', '#E6335F', + '#00755E', '#B5A895', '#0048ba', '#EED9C4', '#C88A65', '#FF6E4A', '#87421F', + '#B2BEB5', '#926F5B', '#00B9FB', '#6456B7', '#DB5079', '#C62D42', '#FA9C44', + '#DA8A67', '#FD7C6E', '#93CCEA', '#FCF686', '#503E32', '#FF5470', '#9DE093', + '#FF7A00', '#4F69C6', '#A50B5E', '#F0E68C', '#FDFF00', '#F091A9', '#FFFF66', + '#6F9940', '#FC74FD', '#652DC1', '#D6AEDD', '#EE34D2', '#BB3385', '#6B3FA0', + '#33CC99', '#FFDB00', '#87FF2A', '#6EEB6E', '#FFC800', '#CC99BA', '#7A89B8', + '#006A93', '#867200', '#E2B631', '#D9D6CF', + ]; + + function shapeFactory(shapeData, clientID, injection) { + const { type } = shapeData; + const color = colors[clientID % colors.length]; + + let shapeModel = null; + switch (type) { + case 'rectangle': + shapeModel = new RectangleShape(shapeData, clientID, color, injection); + break; + case 'polygon': + shapeModel = new PolygonShape(shapeData, clientID, color, injection); + break; + case 'polyline': + shapeModel = new PolylineShape(shapeData, clientID, color, injection); + break; + case 'points': + shapeModel = new PointsShape(shapeData, clientID, color, injection); + break; + default: + throw new DataError( + `An unexpected type of shape "${type}"`, + ); + } + + return shapeModel; + } + + + function trackFactory(trackData, clientID, injection) { + if (trackData.shapes.length) { + const { type } = trackData.shapes[0]; + const color = colors[clientID % colors.length]; + + let trackModel = null; + switch (type) { + case 'rectangle': + trackModel = new RectangleTrack(trackData, clientID, color, injection); + break; + case 'polygon': + trackModel = new PolygonTrack(trackData, clientID, color, injection); + break; + case 'polyline': + trackModel = new PolylineTrack(trackData, clientID, color, injection); + break; + case 'points': + trackModel = new PointsTrack(trackData, clientID, color, injection); + break; + default: + throw new DataError( + `An unexpected type of track "${type}"`, + ); + } + + return trackModel; + } + + console.warn('The track without any shapes had been found. It was ignored.'); + return null; + } + + class Collection { + constructor(data) { + this.startFrame = data.startFrame; + this.stopFrame = data.stopFrame; + this.frameMeta = data.frameMeta; + + this.labels = data.labels.reduce((labelAccumulator, label) => { + labelAccumulator[label.id] = label; + return labelAccumulator; + }, {}); + + this.shapes = {}; // key is a frame + this.tags = {}; // key is a frame + this.tracks = []; + this.objects = {}; // key is a client id + this.count = 0; + this.flush = false; + this.collectionZ = {}; // key is a frame, {max, min} are values + this.groups = { + max: 0, + }; // it is an object to we can pass it as an argument by a reference + this.injection = { + labels: this.labels, + collectionZ: this.collectionZ, + groups: this.groups, + frameMeta: this.frameMeta, + }; + } + + import(data) { + for (const tag of data.tags) { + const clientID = ++this.count; + const tagModel = new Tag(tag, clientID, this.injection); + this.tags[tagModel.frame] = this.tags[tagModel.frame] || []; + this.tags[tagModel.frame].push(tagModel); + this.objects[clientID] = tagModel; + } + + for (const shape of data.shapes) { + const clientID = ++this.count; + const shapeModel = shapeFactory(shape, clientID, this.injection); + this.shapes[shapeModel.frame] = this.shapes[shapeModel.frame] || []; + this.shapes[shapeModel.frame].push(shapeModel); + this.objects[clientID] = shapeModel; + } + + for (const track of data.tracks) { + const clientID = ++this.count; + const trackModel = trackFactory(track, clientID, this.injection); + // The function can return null if track doesn't have any shapes. + // In this case a corresponded message will be sent to the console + if (trackModel) { + this.tracks.push(trackModel); + this.objects[clientID] = trackModel; + } + } + + return this; + } + + export() { + const data = { + tracks: this.tracks.filter(track => !track.removed) + .map(track => track.toJSON()), + shapes: Object.values(this.shapes) + .reduce((accumulator, value) => { + accumulator.push(...value); + return accumulator; + }, []).filter(shape => !shape.removed) + .map(shape => shape.toJSON()), + tags: Object.values(this.tags).reduce((accumulator, value) => { + accumulator.push(...value); + return accumulator; + }, []).filter(tag => !tag.removed) + .map(tag => tag.toJSON()), + }; + + return data; + } + + get(frame) { + const { tracks } = this; + const shapes = this.shapes[frame] || []; + const tags = this.tags[frame] || []; + + const objects = tracks.concat(shapes).concat(tags).filter(object => !object.removed); + // filtering here + + const objectStates = []; + for (const object of objects) { + const stateData = object.get(frame); + if (stateData.outside && !stateData.keyframe) { + continue; + } + + const objectState = objectStateFactory.call(object, frame, stateData); + objectStates.push(objectState); + } + + return objectStates; + } + + merge(objectStates) { + checkObjectType('shapes for merge', objectStates, null, Array); + if (!objectStates.length) return; + const objectsForMerge = objectStates.map((state) => { + checkObjectType('object state', state, null, ObjectState); + const object = this.objects[state.clientID]; + if (typeof (object) === 'undefined') { + throw new ArgumentError( + 'The object has not been saved yet. Call ObjectState.put([state]) before you can merge it', + ); + } + return object; + }); + + const keyframes = {}; // frame: position + const { label, shapeType } = objectStates[0]; + if (!(label.id in this.labels)) { + throw new ArgumentError( + `Unknown label for the task: ${label.id}`, + ); + } + + if (!Object.values(ObjectShape).includes(shapeType)) { + throw new ArgumentError( + `Got unknown shapeType "${shapeType}"`, + ); + } + + const labelAttributes = label.attributes.reduce((accumulator, attribute) => { + accumulator[attribute.id] = attribute; + return accumulator; + }, {}); + + for (let i = 0; i < objectsForMerge.length; i++) { + // For each state get corresponding object + const object = objectsForMerge[i]; + const state = objectStates[i]; + if (state.label.id !== label.id) { + throw new ArgumentError( + `All shape labels are expected to be ${label.name}, but got ${state.label.name}`, + ); + } + + if (state.shapeType !== shapeType) { + throw new ArgumentError( + `All shapes are expected to be ${shapeType}, but got ${state.shapeType}`, + ); + } + + // If this object is shape, get it position and save as a keyframe + if (object instanceof Shape) { + // Frame already saved and it is not outside + if (object.frame in keyframes && !keyframes[object.frame].outside) { + throw new ArgumentError( + 'Expected only one visible shape per frame', + ); + } + + keyframes[object.frame] = { + type: shapeType, + frame: object.frame, + points: [...object.points], + occluded: object.occluded, + zOrder: object.zOrder, + outside: false, + attributes: Object.keys(object.attributes).reduce((accumulator, attrID) => { + // We save only mutable attributes inside a keyframe + if (attrID in labelAttributes && labelAttributes[attrID].mutable) { + accumulator.push({ + spec_id: +attrID, + value: object.attributes[attrID], + }); + } + return accumulator; + }, []), + }; + + // Push outside shape after each annotation shape + // Any not outside shape rewrites it + if (!((object.frame + 1) in keyframes)) { + keyframes[object.frame + 1] = JSON + .parse(JSON.stringify(keyframes[object.frame])); + keyframes[object.frame + 1].outside = true; + keyframes[object.frame + 1].frame++; + } + } else if (object instanceof Track) { + // If this object is track, iterate through all its + // keyframes and push copies to new keyframes + const attributes = {}; // id:value + for (const keyframe of Object.keys(object.shapes)) { + const shape = object.shapes[keyframe]; + // Frame already saved and it is not outside + if (keyframe in keyframes && !keyframes[keyframe].outside) { + // This shape is outside and non-outside shape already exists + if (shape.outside) { + continue; + } + + throw new ArgumentError( + 'Expected only one visible shape per frame', + ); + } + + // We do not save an attribute if it has the same value + // We save only updates + let updatedAttributes = false; + for (const attrID in shape.attributes) { + if (!(attrID in attributes) + || attributes[attrID] !== shape.attributes[attrID]) { + updatedAttributes = true; + attributes[attrID] = shape.attributes[attrID]; + } + } + + keyframes[keyframe] = { + type: shapeType, + frame: +keyframe, + points: [...shape.points], + occluded: shape.occluded, + outside: shape.outside, + zOrder: shape.zOrder, + attributes: updatedAttributes ? Object.keys(attributes) + .reduce((accumulator, attrID) => { + accumulator.push({ + spec_id: +attrID, + value: attributes[attrID], + }); + + return accumulator; + }, []) : [], + }; + } + } else { + throw new ArgumentError( + `Trying to merge unknown object type: ${object.constructor.name}. ` + + 'Only shapes and tracks are expected.', + ); + } + } + + let firstNonOutside = false; + for (const frame of Object.keys(keyframes).sort((a, b) => +a - +b)) { + // Remove all outside frames at the begin + firstNonOutside = firstNonOutside || keyframes[frame].outside; + if (!firstNonOutside && keyframes[frame].outside) { + delete keyframes[frame]; + } else { + break; + } + } + + const clientID = ++this.count; + const track = { + frame: Math.min.apply(null, Object.keys(keyframes).map(frame => +frame)), + shapes: Object.values(keyframes), + group: 0, + label_id: label.id, + attributes: Object.keys(objectStates[0].attributes) + .reduce((accumulator, attrID) => { + if (!labelAttributes[attrID].mutable) { + accumulator.push({ + spec_id: +attrID, + value: objectStates[0].attributes[attrID], + }); + } + + return accumulator; + }, []), + }; + + const trackModel = trackFactory(track, clientID, this.injection); + this.tracks.push(trackModel); + this.objects[clientID] = trackModel; + + // Remove other shapes + for (const object of objectsForMerge) { + object.removed = true; + if (typeof (object.resetCache) === 'function') { + object.resetCache(); + } + } + } + + split(objectState, frame) { + checkObjectType('object state', objectState, null, ObjectState); + checkObjectType('frame', frame, 'integer', null); + + const object = this.objects[objectState.clientID]; + if (typeof (object) === 'undefined') { + throw new ArgumentError( + 'The object has not been saved yet. Call annotations.put([state]) before', + ); + } + + if (objectState.objectType !== ObjectType.TRACK) { + return; + } + + const keyframes = Object.keys(object.shapes).sort((a, b) => +a - +b); + if (frame <= +keyframes[0] || frame > keyframes[keyframes.length - 1]) { + return; + } + + const labelAttributes = object.label.attributes.reduce((accumulator, attribute) => { + accumulator[attribute.id] = attribute; + return accumulator; + }, {}); + + const exported = object.toJSON(); + const position = { + type: objectState.shapeType, + points: [...objectState.points], + occluded: objectState.occluded, + outside: objectState.outside, + zOrder: 0, + attributes: Object.keys(objectState.attributes) + .reduce((accumulator, attrID) => { + if (!labelAttributes[attrID].mutable) { + accumulator.push({ + spec_id: +attrID, + value: objectState.attributes[attrID], + }); + } + + return accumulator; + }, []), + frame, + }; + + const prev = { + frame: exported.frame, + group: 0, + label_id: exported.label_id, + attributes: exported.attributes, + shapes: [], + }; + + const next = JSON.parse(JSON.stringify(prev)); + next.frame = frame; + + next.shapes.push(JSON.parse(JSON.stringify(position))); + exported.shapes.map((shape) => { + delete shape.id; + if (shape.frame < frame) { + prev.shapes.push(JSON.parse(JSON.stringify(shape))); + } else if (shape.frame > frame) { + next.shapes.push(JSON.parse(JSON.stringify(shape))); + } + + return shape; + }); + prev.shapes.push(position); + prev.shapes[prev.shapes.length - 1].outside = true; + + let clientID = ++this.count; + const prevTrack = trackFactory(prev, clientID, this.injection); + this.tracks.push(prevTrack); + this.objects[clientID] = prevTrack; + + clientID = ++this.count; + const nextTrack = trackFactory(next, clientID, this.injection); + this.tracks.push(nextTrack); + this.objects[clientID] = nextTrack; + + // Remove source object + object.removed = true; + object.resetCache(); + } + + group(objectStates, reset) { + checkObjectType('shapes for group', objectStates, null, Array); + + const objectsForGroup = objectStates.map((state) => { + checkObjectType('object state', state, null, ObjectState); + const object = this.objects[state.clientID]; + if (typeof (object) === 'undefined') { + throw new ArgumentError( + 'The object has not been saved yet. Call annotations.put([state]) before', + ); + } + return object; + }); + + const groupIdx = reset ? 0 : ++this.groups.max; + for (const object of objectsForGroup) { + object.group = groupIdx; + if (typeof (object.resetCache) === 'function') { + object.resetCache(); + } + } + + return groupIdx; + } + + clear() { + this.shapes = {}; + this.tags = {}; + this.tracks = []; + this.objects = {}; // by id + this.count = 0; + + this.flush = true; + } + + statistics() { + const labels = {}; + const skeleton = { + rectangle: { + shape: 0, + track: 0, + }, + polygon: { + shape: 0, + track: 0, + }, + polyline: { + shape: 0, + track: 0, + }, + points: { + shape: 0, + track: 0, + }, + tags: 0, + manually: 0, + interpolated: 0, + total: 0, + }; + + const total = JSON.parse(JSON.stringify(skeleton)); + for (const label of Object.values(this.labels)) { + const { name } = label; + labels[name] = JSON.parse(JSON.stringify(skeleton)); + } + + for (const object of Object.values(this.objects)) { + let objectType = null; + if (object instanceof Shape) { + objectType = 'shape'; + } else if (object instanceof Track) { + objectType = 'track'; + } else if (object instanceof Tag) { + objectType = 'tag'; + } else { + throw new ScriptingError( + `Unexpected object type: "${objectType}"`, + ); + } + + const label = object.label.name; + if (objectType === 'tag') { + labels[label].tags++; + labels[label].manually++; + labels[label].total++; + } else { + const { shapeType } = object; + labels[label][shapeType][objectType]++; + + if (objectType === 'track') { + const keyframes = Object.keys(object.shapes) + .sort((a, b) => +a - +b).map(el => +el); + + let prevKeyframe = keyframes[0]; + let visible = false; + + for (const keyframe of keyframes) { + if (visible) { + const interpolated = keyframe - prevKeyframe - 1; + labels[label].interpolated += interpolated; + labels[label].total += interpolated; + } + visible = !object.shapes[keyframe].outside; + prevKeyframe = keyframe; + + if (visible) { + labels[label].manually++; + labels[label].total++; + } + } + + const lastKey = keyframes[keyframes.length - 1]; + if (lastKey !== this.stopFrame && !object.shapes[lastKey].outside) { + const interpolated = this.stopFrame - lastKey; + labels[label].interpolated += interpolated; + labels[label].total += interpolated; + } + } else { + labels[label].manually++; + labels[label].total++; + } + } + } + + for (const label of Object.keys(labels)) { + for (const key of Object.keys(labels[label])) { + if (typeof (labels[label][key]) === 'object') { + for (const objectType of Object.keys(labels[label][key])) { + total[key][objectType] += labels[label][key][objectType]; + } + } else { + total[key] += labels[label][key]; + } + } + } + + return new Statistics(labels, total); + } + + put(objectStates) { + checkObjectType('shapes for put', objectStates, null, Array); + const constructed = { + shapes: [], + tracks: [], + tags: [], + }; + + function convertAttributes(accumulator, attrID) { + const specID = +attrID; + const value = this.attributes[attrID]; + + checkObjectType('attribute id', specID, 'integer', null); + checkObjectType('attribute value', value, 'string', null); + + accumulator.push({ + spec_id: specID, + value, + }); + + return accumulator; + } + + for (const state of objectStates) { + checkObjectType('object state', state, null, ObjectState); + checkObjectType('state client ID', state.clientID, 'undefined', null); + checkObjectType('state frame', state.frame, 'integer', null); + checkObjectType('state attributes', state.attributes, null, Object); + checkObjectType('state label', state.label, null, Label); + + const attributes = Object.keys(state.attributes) + .reduce(convertAttributes.bind(state), []); + const labelAttributes = state.label.attributes.reduce((accumulator, attribute) => { + accumulator[attribute.id] = attribute; + return accumulator; + }, {}); + + // Construct whole objects from states + if (state.objectType === 'tag') { + constructed.tags.push({ + attributes, + frame: state.frame, + label_id: state.label.id, + group: 0, + }); + } else { + checkObjectType('state occluded', state.occluded, 'boolean', null); + checkObjectType('state points', state.points, null, Array); + + for (const coord of state.points) { + checkObjectType('point coordinate', coord, 'number', null); + } + + if (!Object.values(ObjectShape).includes(state.shapeType)) { + throw new ArgumentError( + 'Object shape must be one of: ' + + `${JSON.stringify(Object.values(ObjectShape))}`, + ); + } + + if (state.objectType === 'shape') { + constructed.shapes.push({ + attributes, + frame: state.frame, + group: 0, + label_id: state.label.id, + occluded: state.occluded || false, + points: [...state.points], + type: state.shapeType, + z_order: 0, + }); + } else if (state.objectType === 'track') { + constructed.tracks.push({ + attributes: attributes + .filter(attr => !labelAttributes[attr.spec_id].mutable), + frame: state.frame, + group: 0, + label_id: state.label.id, + shapes: [{ + attributes: attributes + .filter(attr => labelAttributes[attr.spec_id].mutable), + frame: state.frame, + occluded: state.occluded || false, + outside: false, + points: [...state.points], + type: state.shapeType, + z_order: 0, + }], + }); + } else { + throw new ArgumentError( + 'Object type must be one of: ' + + `${JSON.stringify(Object.values(ObjectType))}`, + ); + } + } + } + + // Add constructed objects to a collection + this.import(constructed); + } + + select(objectStates, x, y) { + checkObjectType('shapes for select', objectStates, null, Array); + checkObjectType('x coordinate', x, 'number', null); + checkObjectType('y coordinate', y, 'number', null); + + let minimumDistance = null; + let minimumState = null; + for (const state of objectStates) { + checkObjectType('object state', state, null, ObjectState); + if (state.outside) continue; + + const object = this.objects[state.clientID]; + if (typeof (object) === 'undefined') { + throw new ArgumentError( + 'The object has not been saved yet. Call annotations.put([state]) before', + ); + } + + const distance = object.constructor.distance(state.points, x, y); + if (distance !== null && (minimumDistance === null || distance < minimumDistance)) { + minimumDistance = distance; + minimumState = state; + } + } + + return { + state: minimumState, + distance: minimumDistance, + }; + } + } + + module.exports = Collection; +})(); diff --git a/cvat-core/src/annotations-objects.js b/cvat-core/src/annotations-objects.js new file mode 100644 index 00000000..a9206a44 --- /dev/null +++ b/cvat-core/src/annotations-objects.js @@ -0,0 +1,1547 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const ObjectState = require('./object-state'); + const { + checkObjectType, + isEnum, + } = require('./common'); + const { + ObjectShape, + ObjectType, + AttributeType, + VisibleState, + } = require('./enums'); + + const { + DataError, + ArgumentError, + ScriptingError, + } = require('./exceptions'); + + const { Label } = require('./labels'); + + // Called with the Annotation context + function objectStateFactory(frame, data) { + const objectState = new ObjectState(data); + + objectState.hidden = { + save: this.save.bind(this, frame, objectState), + delete: this.delete.bind(this), + up: this.up.bind(this, frame, objectState), + down: this.down.bind(this, frame, objectState), + }; + + return objectState; + } + + function checkNumberOfPoints(shapeType, points) { + if (shapeType === ObjectShape.RECTANGLE) { + if (points.length / 2 !== 2) { + throw new DataError( + `Rectangle must have 2 points, but got ${points.length / 2}`, + ); + } + } else if (shapeType === ObjectShape.POLYGON) { + if (points.length / 2 < 3) { + throw new DataError( + `Polygon must have at least 3 points, but got ${points.length / 2}`, + ); + } + } else if (shapeType === ObjectShape.POLYLINE) { + if (points.length / 2 < 2) { + throw new DataError( + `Polyline must have at least 2 points, but got ${points.length / 2}`, + ); + } + } else if (shapeType === ObjectShape.POINTS) { + if (points.length / 2 < 1) { + throw new DataError( + `Points must have at least 1 points, but got ${points.length / 2}`, + ); + } + } else { + throw new ArgumentError( + `Unknown value of shapeType has been recieved ${shapeType}`, + ); + } + } + + function checkShapeArea(shapeType, points) { + const MIN_SHAPE_LENGTH = 3; + const MIN_SHAPE_AREA = 9; + + if (shapeType === ObjectShape.POINTS) { + return true; + } + + let xmin = Number.MAX_SAFE_INTEGER; + let xmax = Number.MIN_SAFE_INTEGER; + let ymin = Number.MAX_SAFE_INTEGER; + let ymax = Number.MIN_SAFE_INTEGER; + + for (let i = 0; i < points.length - 1; i += 2) { + xmin = Math.min(xmin, points[i]); + xmax = Math.max(xmax, points[i]); + ymin = Math.min(ymin, points[i + 1]); + ymax = Math.max(ymax, points[i + 1]); + } + + if (shapeType === ObjectShape.POLYLINE) { + const length = Math.max( + xmax - xmin, + ymax - ymin, + ); + + return length >= MIN_SHAPE_LENGTH; + } + + const area = (xmax - xmin) * (ymax - ymin); + return area >= MIN_SHAPE_AREA; + } + + function validateAttributeValue(value, attr) { + const { values } = attr; + const type = attr.inputType; + + if (typeof (value) !== 'string') { + throw new ArgumentError( + `Attribute value is expected to be string, but got ${typeof (value)}`, + ); + } + + if (type === AttributeType.NUMBER) { + return +value >= +values[0] + && +value <= +values[1] + && !((+value - +values[0]) % +values[2]); + } + + if (type === AttributeType.CHECKBOX) { + return ['true', 'false'].includes(value.toLowerCase()); + } + + return values.includes(value); + } + + class Annotation { + constructor(data, clientID, injection) { + this.taskLabels = injection.labels; + this.clientID = clientID; + this.serverID = data.id; + this.group = data.group; + this.label = this.taskLabels[data.label_id]; + this.frame = data.frame; + this.removed = false; + this.lock = false; + this.attributes = data.attributes.reduce((attributeAccumulator, attr) => { + attributeAccumulator[attr.spec_id] = attr.value; + return attributeAccumulator; + }, {}); + this.appendDefaultAttributes(this.label); + + injection.groups.max = Math.max(injection.groups.max, this.group); + } + + appendDefaultAttributes(label) { + const labelAttributes = label.attributes; + for (const attribute of labelAttributes) { + if (!(attribute.id in this.attributes)) { + this.attributes[attribute.id] = attribute.defaultValue; + } + } + } + + delete(force) { + if (!this.lock || force) { + this.removed = true; + } + + return true; + } + } + + class Drawn extends Annotation { + constructor(data, clientID, color, injection) { + super(data, clientID, injection); + + this.frameMeta = injection.frameMeta; + this.collectionZ = injection.collectionZ; + this.visibility = VisibleState.SHAPE; + + this.color = color; + this.shapeType = null; + } + + _getZ(frame) { + this.collectionZ[frame] = this.collectionZ[frame] || { + max: 0, + min: 0, + }; + + return this.collectionZ[frame]; + } + + save() { + throw new ScriptingError( + 'Is not implemented', + ); + } + + get() { + throw new ScriptingError( + 'Is not implemented', + ); + } + + toJSON() { + throw new ScriptingError( + 'Is not implemented', + ); + } + + // Increase ZOrder within frame + up(frame, objectState) { + const z = this._getZ(frame); + z.max++; + objectState.zOrder = z.max; + } + + // Decrease ZOrder within frame + down(frame, objectState) { + const z = this._getZ(frame); + z.min--; + objectState.zOrder = z.min; + } + } + + class Shape extends Drawn { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.points = data.points; + this.occluded = data.occluded; + this.zOrder = data.z_order; + + const z = this._getZ(this.frame); + z.max = Math.max(z.max, this.zOrder || 0); + z.min = Math.min(z.min, this.zOrder || 0); + } + + // Method is used to export data to the server + toJSON() { + return { + type: this.shapeType, + clientID: this.clientID, + occluded: this.occluded, + z_order: this.zOrder, + points: [...this.points], + attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => { + attributeAccumulator.push({ + spec_id: attrId, + value: this.attributes[attrId], + }); + + return attributeAccumulator; + }, []), + id: this.serverID, + frame: this.frame, + label_id: this.label.id, + group: this.group, + }; + } + + // Method is used to construct ObjectState objects + get(frame) { + if (frame !== this.frame) { + throw new ScriptingError( + 'Got frame is not equal to the frame of the shape', + ); + } + + return { + objectType: ObjectType.SHAPE, + shapeType: this.shapeType, + clientID: this.clientID, + serverID: this.serverID, + occluded: this.occluded, + lock: this.lock, + zOrder: this.zOrder, + points: [...this.points], + attributes: Object.assign({}, this.attributes), + label: this.label, + group: this.group, + color: this.color, + visibility: this.visibility, + }; + } + + save(frame, data) { + if (frame !== this.frame) { + throw new ScriptingError( + 'Got frame is not equal to the frame of the shape', + ); + } + + if (this.lock && data.lock) { + return objectStateFactory.call(this, frame, this.get(frame)); + } + + // All changes are done in this temporary object + const copy = this.get(frame); + const updated = data.updateFlags; + + if (updated.label) { + checkObjectType('label', data.label, null, Label); + copy.label = data.label; + copy.attributes = {}; + this.appendDefaultAttributes.call(copy, copy.label); + } + + if (updated.attributes) { + const labelAttributes = copy.label.attributes + .reduce((accumulator, value) => { + accumulator[value.id] = value; + return accumulator; + }, {}); + + for (const attrID of Object.keys(data.attributes)) { + const value = data.attributes[attrID]; + if (attrID in labelAttributes + && validateAttributeValue(value, labelAttributes[attrID])) { + copy.attributes[attrID] = value; + } else { + throw new ArgumentError( + `Trying to save unknown attribute with id ${attrID} and value ${value}`, + ); + } + } + } + + if (updated.points) { + checkObjectType('points', data.points, null, Array); + checkNumberOfPoints(this.shapeType, data.points); + + // cut points + const { width, height } = this.frameMeta[frame]; + const cutPoints = []; + for (let i = 0; i < data.points.length - 1; i += 2) { + const x = data.points[i]; + const y = data.points[i + 1]; + + checkObjectType('coordinate', x, 'number', null); + checkObjectType('coordinate', y, 'number', null); + + cutPoints.push( + Math.clamp(x, 0, width), + Math.clamp(y, 0, height), + ); + } + + if (checkShapeArea(this.shapeType, cutPoints)) { + copy.points = cutPoints; + } + } + + if (updated.occluded) { + checkObjectType('occluded', data.occluded, 'boolean', null); + copy.occluded = data.occluded; + } + + if (updated.group) { + checkObjectType('group', data.group, 'integer', null); + copy.group = data.group; + } + + if (updated.zOrder) { + checkObjectType('zOrder', data.zOrder, 'integer', null); + copy.zOrder = data.zOrder; + } + + if (updated.lock) { + checkObjectType('lock', data.lock, 'boolean', null); + copy.lock = data.lock; + } + + if (updated.color) { + checkObjectType('color', data.color, 'string', null); + if (/^#[0-9A-F]{6}$/i.test(data.color)) { + throw new ArgumentError( + `Got invalid color value: "${data.color}"`, + ); + } + + copy.color = data.color; + } + + if (updated.visibility) { + if (!isEnum.call(VisibleState, data.visibility)) { + throw new ArgumentError( + `Got invalid visibility value: "${data.visibility}"`, + ); + } + + copy.visibility = data.visibility; + } + + // Reset flags and commit all changes + updated.reset(); + for (const prop of Object.keys(copy)) { + if (prop in this) { + this[prop] = copy[prop]; + } + } + + return objectStateFactory.call(this, frame, this.get(frame)); + } + } + + class Track extends Drawn { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapes = data.shapes.reduce((shapeAccumulator, value) => { + shapeAccumulator[value.frame] = { + serverID: value.id, + occluded: value.occluded, + zOrder: value.z_order, + points: value.points, + outside: value.outside, + attributes: value.attributes.reduce((attributeAccumulator, attr) => { + attributeAccumulator[attr.spec_id] = attr.value; + return attributeAccumulator; + }, {}), + }; + + const z = this._getZ(value.frame); + z.max = Math.max(z.max, value.z_order); + z.min = Math.min(z.min, value.z_order); + + return shapeAccumulator; + }, {}); + + this.cache = {}; + } + + // Method is used to export data to the server + toJSON() { + const labelAttributes = this.label.attributes.reduce((accumulator, attribute) => { + accumulator[attribute.id] = attribute; + return accumulator; + }, {}); + + return { + clientID: this.clientID, + id: this.serverID, + frame: this.frame, + label_id: this.label.id, + group: this.group, + attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => { + if (!labelAttributes[attrId].mutable) { + attributeAccumulator.push({ + spec_id: attrId, + value: this.attributes[attrId], + }); + } + + return attributeAccumulator; + }, []), + shapes: Object.keys(this.shapes).reduce((shapesAccumulator, frame) => { + shapesAccumulator.push({ + type: this.shapeType, + occluded: this.shapes[frame].occluded, + z_order: this.shapes[frame].zOrder, + points: [...this.shapes[frame].points], + outside: this.shapes[frame].outside, + attributes: Object.keys(this.shapes[frame].attributes) + .reduce((attributeAccumulator, attrId) => { + if (labelAttributes[attrId].mutable) { + attributeAccumulator.push({ + spec_id: attrId, + value: this.shapes[frame].attributes[attrId], + }); + } + + return attributeAccumulator; + }, []), + id: this.shapes[frame].serverID, + frame: +frame, + }); + + return shapesAccumulator; + }, []), + }; + } + + // Method is used to construct ObjectState objects + get(frame) { + if (!(frame in this.cache)) { + const interpolation = Object.assign( + {}, this.getPosition(frame), + { + attributes: this.getAttributes(frame), + group: this.group, + objectType: ObjectType.TRACK, + shapeType: this.shapeType, + clientID: this.clientID, + serverID: this.serverID, + lock: this.lock, + color: this.color, + visibility: this.visibility, + }, + ); + + this.cache[frame] = interpolation; + } + + const result = JSON.parse(JSON.stringify(this.cache[frame])); + result.label = this.label; + return result; + } + + neighborsFrames(targetFrame) { + const frames = Object.keys(this.shapes).map(frame => +frame); + let lDiff = Number.MAX_SAFE_INTEGER; + let rDiff = Number.MAX_SAFE_INTEGER; + + for (const frame of frames) { + const diff = Math.abs(targetFrame - frame); + if (frame <= targetFrame && diff < lDiff) { + lDiff = diff; + } else if (diff < rDiff) { + rDiff = diff; + } + } + + const leftFrame = lDiff === Number.MAX_SAFE_INTEGER ? null : targetFrame - lDiff; + const rightFrame = rDiff === Number.MAX_SAFE_INTEGER ? null : targetFrame + rDiff; + + return { + leftFrame, + rightFrame, + }; + } + + getAttributes(targetFrame) { + const result = {}; + + // First of all copy all unmutable attributes + for (const attrID in this.attributes) { + if (Object.prototype.hasOwnProperty.call(this.attributes, attrID)) { + result[attrID] = this.attributes[attrID]; + } + } + + // Secondly get latest mutable attributes up to target frame + const frames = Object.keys(this.shapes).sort((a, b) => +a - +b); + for (const frame of frames) { + if (frame <= targetFrame) { + const { attributes } = this.shapes[frame]; + + for (const attrID in attributes) { + if (Object.prototype.hasOwnProperty.call(attributes, attrID)) { + result[attrID] = attributes[attrID]; + } + } + } + } + + return result; + } + + save(frame, data) { + if (this.lock && data.lock) { + return objectStateFactory.call(this, frame, this.get(frame)); + } + + // All changes are done in this temporary object + const copy = Object.assign(this.get(frame)); + copy.attributes = Object.assign(copy.attributes); + copy.points = [...copy.points]; + + const updated = data.updateFlags; + let positionUpdated = false; + + if (updated.label) { + checkObjectType('label', data.label, null, Label); + copy.label = data.label; + copy.attributes = {}; + + // Shape attributes will be removed later after all checks + this.appendDefaultAttributes.call(copy, copy.label); + } + + const labelAttributes = copy.label.attributes + .reduce((accumulator, value) => { + accumulator[value.id] = value; + return accumulator; + }, {}); + + if (updated.attributes) { + for (const attrID of Object.keys(data.attributes)) { + const value = data.attributes[attrID]; + if (attrID in labelAttributes + && validateAttributeValue(value, labelAttributes[attrID])) { + copy.attributes[attrID] = value; + } else { + throw new ArgumentError( + `Trying to save unknown attribute with id ${attrID} and value ${value}`, + ); + } + } + } + + if (updated.points) { + checkObjectType('points', data.points, null, Array); + checkNumberOfPoints(this.shapeType, data.points); + + // cut points + const { width, height } = this.frameMeta[frame]; + const cutPoints = []; + for (let i = 0; i < data.points.length - 1; i += 2) { + const x = data.points[i]; + const y = data.points[i + 1]; + + checkObjectType('coordinate', x, 'number', null); + checkObjectType('coordinate', y, 'number', null); + + cutPoints.push( + Math.clamp(x, 0, width), + Math.clamp(y, 0, height), + ); + } + + if (checkShapeArea(this.shapeType, cutPoints)) { + copy.points = cutPoints; + positionUpdated = true; + } + } + + if (updated.occluded) { + checkObjectType('occluded', data.occluded, 'boolean', null); + copy.occluded = data.occluded; + positionUpdated = true; + } + + if (updated.outside) { + checkObjectType('outside', data.outside, 'boolean', null); + copy.outside = data.outside; + positionUpdated = true; + } + + if (updated.group) { + checkObjectType('group', data.group, 'integer', null); + copy.group = data.group; + } + + if (updated.zOrder) { + checkObjectType('zOrder', data.zOrder, 'integer', null); + copy.zOrder = data.zOrder; + positionUpdated = true; + } + + if (updated.lock) { + checkObjectType('lock', data.lock, 'boolean', null); + copy.lock = data.lock; + } + + if (updated.color) { + checkObjectType('color', data.color, 'string', null); + if (/^#[0-9A-F]{6}$/i.test(data.color)) { + throw new ArgumentError( + `Got invalid color value: "${data.color}"`, + ); + } + + copy.color = data.color; + } + + if (updated.visibility) { + if (!isEnum.call(VisibleState, data.visibility)) { + throw new ArgumentError( + `Got invalid visibility value: "${data.visibility}"`, + ); + } + + copy.visibility = data.visibility; + } + + if (updated.keyframe) { + // Just check here + checkObjectType('keyframe', data.keyframe, 'boolean', null); + } + + // Commit all changes + for (const prop of Object.keys(copy)) { + if (prop in this) { + this[prop] = copy[prop]; + } + + this.cache[frame][prop] = copy[prop]; + } + + if (updated.attributes) { + // Mutable attributes will be updated below + for (const attrID of Object.keys(copy.attributes)) { + if (!labelAttributes[attrID].mutable) { + this.shapes[frame].attributes[attrID] = data.attributes[attrID]; + this.shapes[frame].attributes[attrID] = data.attributes[attrID]; + } + } + } + + if (updated.label) { + for (const shape of Object.values(this.shapes)) { + shape.attributes = {}; + } + } + + // Remove keyframe + if (updated.keyframe && !data.keyframe) { + // Remove all cache after this keyframe because it have just become outdated + for (const cacheFrame in this.cache) { + if (+cacheFrame > frame) { + delete this.cache[cacheFrame]; + } + } + + this.cache[frame].keyframe = false; + delete this.shapes[frame]; + updated.reset(); + + return objectStateFactory.call(this, frame, this.get(frame)); + } + + // Add/update keyframe + if (positionUpdated || (updated.keyframe && data.keyframe)) { + // Remove all cache after this keyframe because it have just become outdated + for (const cacheFrame in this.cache) { + if (+cacheFrame > frame) { + delete this.cache[cacheFrame]; + } + } + + this.cache[frame].keyframe = true; + data.keyframe = true; + + this.shapes[frame] = { + frame, + zOrder: copy.zOrder, + points: copy.points, + outside: copy.outside, + occluded: copy.occluded, + attributes: {}, + }; + + if (updated.attributes) { + // Unmutable attributes were updated above + for (const attrID of Object.keys(copy.attributes)) { + if (labelAttributes[attrID].mutable) { + this.shapes[frame].attributes[attrID] = data.attributes[attrID]; + this.shapes[frame].attributes[attrID] = data.attributes[attrID]; + } + } + } + } + + updated.reset(); + + return objectStateFactory.call(this, frame, this.get(frame)); + } + + getPosition(targetFrame) { + const { + leftFrame, + rightFrame, + } = this.neighborsFrames(targetFrame); + + const rightPosition = Number.isInteger(rightFrame) ? this.shapes[rightFrame] : null; + const leftPosition = Number.isInteger(leftFrame) ? this.shapes[leftFrame] : null; + + if (leftPosition && leftFrame === targetFrame) { + return { + points: [...leftPosition.points], + occluded: leftPosition.occluded, + outside: leftPosition.outside, + zOrder: leftPosition.zOrder, + keyframe: true, + }; + } + + if (rightPosition && leftPosition) { + return Object.assign({}, this.interpolatePosition( + leftPosition, + rightPosition, + (targetFrame - leftFrame) / (rightFrame - leftFrame), + ), { + keyframe: false, + }); + } + + if (rightPosition) { + return { + points: [...rightPosition.points], + occluded: rightPosition.occluded, + outside: true, + zOrder: 0, + keyframe: false, + }; + } + + if (leftPosition) { + return { + points: [...leftPosition.points], + occluded: leftPosition.occluded, + outside: leftPosition.outside, + zOrder: 0, + keyframe: false, + }; + } + + throw new ScriptingError( + `No one neightbour frame found for the track with client ID: "${this.id}"`, + ); + } + + delete(force) { + if (!this.lock || force) { + this.removed = true; + this.resetCache(); + } + + return true; + } + + resetCache() { + this.cache = {}; + } + } + + class Tag extends Annotation { + constructor(data, clientID, injection) { + super(data, clientID, injection); + } + + // Method is used to export data to the server + toJSON() { + return { + clientID: this.clientID, + id: this.serverID, + frame: this.frame, + label_id: this.label.id, + group: this.group, + attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => { + attributeAccumulator.push({ + spec_id: attrId, + value: this.attributes[attrId], + }); + + return attributeAccumulator; + }, []), + }; + } + + // Method is used to construct ObjectState objects + get(frame) { + if (frame !== this.frame) { + throw new ScriptingError( + 'Got frame is not equal to the frame of the shape', + ); + } + + return { + objectType: ObjectType.TAG, + clientID: this.clientID, + serverID: this.serverID, + lock: this.lock, + attributes: Object.assign({}, this.attributes), + label: this.label, + group: this.group, + }; + } + + save(frame, data) { + if (frame !== this.frame) { + throw new ScriptingError( + 'Got frame is not equal to the frame of the shape', + ); + } + + if (this.lock && data.lock) { + return objectStateFactory.call(this, frame, this.get(frame)); + } + + // All changes are done in this temporary object + const copy = this.get(frame); + const updated = data.updateFlags; + + if (updated.label) { + checkObjectType('label', data.label, null, Label); + copy.label = data.label; + copy.attributes = {}; + this.appendDefaultAttributes.call(copy, copy.label); + } + + if (updated.attributes) { + const labelAttributes = copy.label + .attributes.map(attr => `${attr.id}`); + + for (const attrID of Object.keys(data.attributes)) { + if (labelAttributes.includes(attrID)) { + copy.attributes[attrID] = data.attributes[attrID]; + } + } + } + + if (updated.group) { + checkObjectType('group', data.group, 'integer', null); + copy.group = data.group; + } + + if (updated.lock) { + checkObjectType('lock', data.lock, 'boolean', null); + copy.lock = data.lock; + } + + // Reset flags and commit all changes + updated.reset(); + for (const prop of Object.keys(copy)) { + if (prop in this) { + this[prop] = copy[prop]; + } + } + + return objectStateFactory.call(this, frame, this.get(frame)); + } + } + + class RectangleShape extends Shape { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.RECTANGLE; + checkNumberOfPoints(this.shapeType, this.points); + } + + static distance(points, x, y) { + const [xtl, ytl, xbr, ybr] = points; + + if (!(x >= xtl && x <= xbr && y >= ytl && y <= ybr)) { + // Cursor is outside of a box + return null; + } + + // The shortest distance from point to an edge + return Math.min.apply(null, [x - xtl, y - ytl, xbr - x, ybr - y]); + } + } + + class PolyShape extends Shape { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + } + } + + class PolygonShape extends PolyShape { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.POLYGON; + checkNumberOfPoints(this.shapeType, this.points); + } + + static distance(points, x, y) { + function position(x1, y1, x2, y2) { + return ((x2 - x1) * (y - y1) - (x - x1) * (y2 - y1)); + } + + let wn = 0; + const distances = []; + + for (let i = 0, j = points.length - 2; i < points.length - 1; j = i, i += 2) { + // Current point + const x1 = points[j]; + const y1 = points[j + 1]; + + // Next point + const x2 = points[i]; + const y2 = points[i + 1]; + + // Check if a point is inside a polygon + // with a winding numbers algorithm + // https://en.wikipedia.org/wiki/Point_in_polygon#Winding_number_algorithm + if (y1 <= y) { + if (y2 > y) { + if (position(x1, y1, x2, y2) > 0) { + wn++; + } + } + } else if (y2 <= y) { + if (position(x1, y1, x2, y2) < 0) { + wn--; + } + } + + // Find the shortest distance from point to an edge + // Get an equation of a line in general + const aCoef = (y1 - y2); + const bCoef = (x2 - x1); + + // Vector (aCoef, bCoef) is a perpendicular to line + // Now find the point where two lines + // (edge and its perpendicular through the point (x,y)) are cross + const xCross = x - aCoef; + const yCross = y - bCoef; + + if (((xCross - x1) * (x2 - xCross)) >= 0 + && ((yCross - y1) * (y2 - yCross)) >= 0) { + // Cross point is on segment between p1(x1,y1) and p2(x2,y2) + distances.push(Math.sqrt( + Math.pow(x - xCross, 2) + + Math.pow(y - yCross, 2), + )); + } else { + distances.push( + Math.min( + Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)), + Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2)), + ), + ); + } + } + + if (wn !== 0) { + return Math.min.apply(null, distances); + } + + return null; + } + } + + class PolylineShape extends PolyShape { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.POLYLINE; + checkNumberOfPoints(this.shapeType, this.points); + } + + static distance(points, x, y) { + const distances = []; + for (let i = 0; i < points.length - 2; i += 2) { + // Current point + const x1 = points[i]; + const y1 = points[i + 1]; + + // Next point + const x2 = points[i + 2]; + const y2 = points[i + 3]; + + // Find the shortest distance from point to an edge + if (((x - x1) * (x2 - x)) >= 0 && ((y - y1) * (y2 - y)) >= 0) { + // Find the length of a perpendicular + // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line + distances.push( + Math.abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) / Math + .sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)), + ); + } else { + // The link below works for lines (which have infinit length) + // There is a case when perpendicular doesn't cross the edge + // In this case we don't use the computed distance + // Instead we use just distance to the nearest point + distances.push( + Math.min( + Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)), + Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2)), + ), + ); + } + } + + return Math.min.apply(null, distances); + } + } + + class PointsShape extends PolyShape { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.POINTS; + checkNumberOfPoints(this.shapeType, this.points); + } + + static distance(points, x, y) { + const distances = []; + for (let i = 0; i < points.length; i += 2) { + const x1 = points[i]; + const y1 = points[i + 1]; + + distances.push( + Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)), + ); + } + + return Math.min.apply(null, distances); + } + } + + class RectangleTrack extends Track { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.RECTANGLE; + for (const shape of Object.values(this.shapes)) { + checkNumberOfPoints(this.shapeType, shape.points); + } + } + + interpolatePosition(leftPosition, rightPosition, offset) { + const positionOffset = [ + rightPosition.points[0] - leftPosition.points[0], + rightPosition.points[1] - leftPosition.points[1], + rightPosition.points[2] - leftPosition.points[2], + rightPosition.points[3] - leftPosition.points[3], + ]; + + return { // xtl, ytl, xbr, ybr + points: [ + leftPosition.points[0] + positionOffset[0] * offset, + leftPosition.points[1] + positionOffset[1] * offset, + leftPosition.points[2] + positionOffset[2] * offset, + leftPosition.points[3] + positionOffset[3] * offset, + ], + occluded: leftPosition.occluded, + outside: leftPosition.outside, + zOrder: leftPosition.zOrder, + }; + } + } + + class PolyTrack extends Track { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + } + + interpolatePosition(leftPosition, rightPosition, offset) { + function findBox(points) { + let xmin = Number.MAX_SAFE_INTEGER; + let ymin = Number.MAX_SAFE_INTEGER; + let xmax = Number.MIN_SAFE_INTEGER; + let ymax = Number.MIN_SAFE_INTEGER; + + for (let i = 0; i < points.length; i += 2) { + if (points[i] < xmin) xmin = points[i]; + if (points[i + 1] < ymin) ymin = points[i + 1]; + if (points[i] > xmax) xmax = points[i]; + if (points[i + 1] > ymax) ymax = points[i + 1]; + } + + return { + xmin, + ymin, + xmax, + ymax, + }; + } + + function normalize(points, box) { + const normalized = []; + const width = box.xmax - box.xmin; + const height = box.ymax - box.ymin; + + for (let i = 0; i < points.length; i += 2) { + normalized.push( + (points[i] - box.xmin) / width, + (points[i + 1] - box.ymin) / height, + ); + } + + return normalized; + } + + function denormalize(points, box) { + const denormalized = []; + const width = box.xmax - box.xmin; + const height = box.ymax - box.ymin; + + for (let i = 0; i < points.length; i += 2) { + denormalized.push( + points[i] * width + box.xmin, + points[i + 1] * height + box.ymin, + ); + } + + return denormalized; + } + + function toPoints(array) { + const points = []; + for (let i = 0; i < array.length; i += 2) { + points.push({ + x: array[i], + y: array[i + 1], + }); + } + + return points; + } + + function toArray(points) { + const array = []; + for (const point of points) { + array.push(point.x, point.y); + } + + return array; + } + + function computeDistances(source, target) { + const distances = {}; + for (let i = 0; i < source.length; i++) { + distances[i] = distances[i] || {}; + for (let j = 0; j < target.length; j++) { + const dx = source[i].x - target[j].x; + const dy = source[i].y - target[j].y; + + distances[i][j] = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); + } + } + + return distances; + } + + function truncateByThreshold(mapping, threshold) { + for (const key of Object.keys(mapping)) { + if (mapping[key].distance > threshold) { + delete mapping[key]; + } + } + } + + // https://en.wikipedia.org/wiki/Stable_marriage_problem + // TODO: One of important part of the algorithm is to correctly match + // "corner" points. Thus it is possible for each of such point calculate + // a descriptor (d) and use (x, y, d) to calculate the distance. One more + // idea is to be sure that order or matched points is preserved. For example, + // if p1 matches q1 and p2 matches q2 and between p1 and p2 we don't have any + // points thus we should not have points between q1 and q2 as well. + function stableMarriageProblem(men, women, distances) { + const menPreferences = {}; + for (const man of men) { + menPreferences[man] = women.concat() + .sort((w1, w2) => distances[man][w1] - distances[man][w2]); + } + + // Start alghoritm with max N^2 complexity + const womenMaybe = {}; // id woman:id man,distance + const menBusy = {}; // id man:boolean + let prefIndex = 0; + + // While there is at least one free man + while (Object.values(menBusy).length !== men.length) { + // Every man makes offer to the best woman + for (const man of men) { + // The man have already found a woman + if (menBusy[man]) { + continue; + } + + const woman = menPreferences[man][prefIndex]; + const distance = distances[man][woman]; + + // A women chooses the best offer and says "maybe" + if (woman in womenMaybe && womenMaybe[woman].distance > distance) { + // A woman got better offer + const prevChoice = womenMaybe[woman].value; + delete womenMaybe[woman]; + delete menBusy[prevChoice]; + } + + if (!(woman in womenMaybe)) { + womenMaybe[woman] = { + value: man, + distance, + }; + + menBusy[man] = true; + } + } + + prefIndex++; + } + + const result = {}; + for (const woman of Object.keys(womenMaybe)) { + result[womenMaybe[woman].value] = { + value: woman, + distance: womenMaybe[woman].distance, + }; + } + + return result; + } + + function getMapping(source, target) { + function sumEdges(points) { + let result = 0; + for (let i = 1; i < points.length; i += 2) { + const distance = Math.sqrt(Math.pow(points[i].x - points[i - 1].x, 2) + + Math.pow(points[i].y - points[i - 1].y, 2)); + result += distance; + } + + // Corner case when work with one point + // Mapping in this case can't be wrong + if (!result) { + return Number.MAX_SAFE_INTEGER; + } + + return result; + } + + function computeDeviation(points, average) { + let result = 0; + for (let i = 1; i < points.length; i += 2) { + const distance = Math.sqrt(Math.pow(points[i].x - points[i - 1].x, 2) + + Math.pow(points[i].y - points[i - 1].y, 2)); + result += Math.pow(distance - average, 2); + } + + return result; + } + + const processedSource = []; + const processedTarget = []; + + const distances = computeDistances(source, target); + const mapping = stableMarriageProblem(Array.from(source.keys()), + Array.from(target.keys()), distances); + + const average = (sumEdges(target) + + sumEdges(source)) / (target.length + source.length); + const meanSquareDeviation = Math.sqrt((computeDeviation(source, average) + + computeDeviation(target, average)) / (source.length + target.length)); + const threshold = average + 3 * meanSquareDeviation; // 3 sigma rule + truncateByThreshold(mapping, threshold); + for (const key of Object.keys(mapping)) { + mapping[key] = mapping[key].value; + } + + // const receivingOrder = Object.keys(mapping).map(x => +x).sort((a,b) => a - b); + const receivingOrder = this.appendMapping(mapping, source, target); + + for (const pointIdx of receivingOrder) { + processedSource.push(source[pointIdx]); + processedTarget.push(target[mapping[pointIdx]]); + } + + return [processedSource, processedTarget]; + } + + let leftBox = findBox(leftPosition.points); + let rightBox = findBox(rightPosition.points); + + // Sometimes (if shape has one point or shape is line), + // We can get box with zero area + // Next computation will be with NaN in this case + // We have to prevent it + const delta = 1; + if (leftBox.xmax - leftBox.xmin < delta || rightBox.ymax - rightBox.ymin < delta) { + leftBox = { + xmin: 0, + xmax: 1024, // TODO: Get actual image size + ymin: 0, + ymax: 768, + }; + + rightBox = leftBox; + } + + const leftPoints = toPoints(normalize(leftPosition.points, leftBox)); + const rightPoints = toPoints(normalize(rightPosition.points, rightBox)); + + let newLeftPoints = []; + let newRightPoints = []; + if (leftPoints.length > rightPoints.length) { + const [ + processedRight, + processedLeft, + ] = getMapping.call(this, rightPoints, leftPoints); + newLeftPoints = processedLeft; + newRightPoints = processedRight; + } else { + const [ + processedLeft, + processedRight, + ] = getMapping.call(this, leftPoints, rightPoints); + newLeftPoints = processedLeft; + newRightPoints = processedRight; + } + + const absoluteLeftPoints = denormalize(toArray(newLeftPoints), leftBox); + const absoluteRightPoints = denormalize(toArray(newRightPoints), rightBox); + + const interpolation = []; + for (let i = 0; i < absoluteLeftPoints.length; i++) { + interpolation.push(absoluteLeftPoints[i] + ( + absoluteRightPoints[i] - absoluteLeftPoints[i]) * offset); + } + + return { + points: interpolation, + occluded: leftPosition.occluded, + outside: leftPosition.outside, + zOrder: leftPosition.zOrder, + }; + } + + // mapping is predicted order of points sourse_idx:target_idx + // some points from source and target can absent in mapping + // source, target - arrays of points. Target array size >= sourse array size + appendMapping(mapping, source, target) { + const targetMatched = Object.values(mapping).map(x => +x); + const sourceMatched = Object.keys(mapping).map(x => +x); + const orderForReceive = []; + + function findNeighbors(point) { + let prev = point; + let next = point; + + if (!targetMatched.length) { + // Prevent infinity loop + throw new ScriptingError('Interpolation mapping is empty'); + } + + while (!targetMatched.includes(prev)) { + prev--; + if (prev < 0) { + prev = target.length - 1; + } + } + + while (!targetMatched.includes(next)) { + next++; + if (next >= target.length) { + next = 0; + } + } + + return [prev, next]; + } + + function computeOffset(point, prev, next) { + const pathPoints = []; + + while (prev !== next) { + pathPoints.push(target[prev]); + prev++; + if (prev >= target.length) { + prev = 0; + } + } + pathPoints.push(target[next]); + + let curveLength = 0; + let offset = 0; + let iCrossed = false; + for (let k = 1; k < pathPoints.length; k++) { + const p1 = pathPoints[k]; + const p2 = pathPoints[k - 1]; + const distance = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + + if (!iCrossed) { + offset += distance; + } + curveLength += distance; + if (target[point] === pathPoints[k]) { + iCrossed = true; + } + } + + if (!curveLength) { + return 0; + } + + return offset / curveLength; + } + + for (let i = 0; i < target.length; i++) { + const index = targetMatched.indexOf(i); + if (index === -1) { + // We have to find a neighbours which have been mapped + const [prev, next] = findNeighbors(i); + + // Now compute edge offset + const offset = computeOffset(i, prev, next); + + // Get point between two neighbors points + const prevPoint = target[prev]; + const nextPoint = target[next]; + const autoPoint = { + x: prevPoint.x + (nextPoint.x - prevPoint.x) * offset, + y: prevPoint.y + (nextPoint.y - prevPoint.y) * offset, + }; + + // Put it into matched + source.push(autoPoint); + mapping[source.length - 1] = i; + orderForReceive.push(source.length - 1); + } else { + orderForReceive.push(sourceMatched[index]); + } + } + + return orderForReceive; + } + } + + class PolygonTrack extends PolyTrack { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.POLYGON; + for (const shape of Object.values(this.shapes)) { + checkNumberOfPoints(this.shapeType, shape.points); + } + } + } + + class PolylineTrack extends PolyTrack { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.POLYLINE; + for (const shape of Object.values(this.shapes)) { + checkNumberOfPoints(this.shapeType, shape.points); + } + } + } + + class PointsTrack extends PolyTrack { + constructor(data, clientID, color, injection) { + super(data, clientID, color, injection); + this.shapeType = ObjectShape.POINTS; + for (const shape of Object.values(this.shapes)) { + checkNumberOfPoints(this.shapeType, shape.points); + } + } + } + + RectangleTrack.distance = RectangleShape.distance; + PolygonTrack.distance = PolygonShape.distance; + PolylineTrack.distance = PolylineShape.distance; + PointsTrack.distance = PointsShape.distance; + + module.exports = { + RectangleShape, + PolygonShape, + PolylineShape, + PointsShape, + RectangleTrack, + PolygonTrack, + PolylineTrack, + PointsTrack, + Track, + Shape, + Tag, + objectStateFactory, + }; +})(); diff --git a/cvat-core/src/annotations-saver.js b/cvat-core/src/annotations-saver.js new file mode 100644 index 00000000..2a27e2d5 --- /dev/null +++ b/cvat-core/src/annotations-saver.js @@ -0,0 +1,279 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const serverProxy = require('./server-proxy'); + const { Task } = require('./session'); + const { ScriptingError } = ('./exceptions'); + + class AnnotationsSaver { + constructor(version, collection, session) { + this.sessionType = session instanceof Task ? 'task' : 'job'; + this.id = session.id; + this.version = version; + this.collection = collection; + this.initialObjects = {}; + this.hash = this._getHash(); + + // We need use data from export instead of initialData + // Otherwise we have differ keys order and JSON comparison code incorrect + const exported = this.collection.export(); + + this._resetState(); + for (const shape of exported.shapes) { + this.initialObjects.shapes[shape.id] = shape; + } + + for (const track of exported.tracks) { + this.initialObjects.tracks[track.id] = track; + } + + for (const tag of exported.tags) { + this.initialObjects.tags[tag.id] = tag; + } + } + + _resetState() { + this.initialObjects = { + shapes: {}, + tracks: {}, + tags: {}, + }; + } + + _getHash() { + const exported = this.collection.export(); + return JSON.stringify(exported); + } + + async _request(data, action) { + const result = await serverProxy.annotations.updateAnnotations( + this.sessionType, + this.id, + data, + action, + ); + + return result; + } + + async _put(data) { + const result = await this._request(data, 'put'); + return result; + } + + async _create(created) { + const result = await this._request(created, 'create'); + return result; + } + + async _update(updated) { + const result = await this._request(updated, 'update'); + return result; + } + + async _delete(deleted) { + const result = await this._request(deleted, 'delete'); + return result; + } + + _split(exported) { + const splitted = { + created: { + shapes: [], + tracks: [], + tags: [], + }, + updated: { + shapes: [], + tracks: [], + tags: [], + }, + deleted: { + shapes: [], + tracks: [], + tags: [], + }, + }; + + // Find created and updated objects + for (const type of Object.keys(exported)) { + for (const object of exported[type]) { + if (object.id in this.initialObjects[type]) { + const exportedHash = JSON.stringify(object); + const initialHash = JSON.stringify(this.initialObjects[type][object.id]); + if (exportedHash !== initialHash) { + splitted.updated[type].push(object); + } + } else if (typeof (object.id) === 'undefined') { + splitted.created[type].push(object); + } else { + throw new ScriptingError( + `Id of object is defined "${object.id}"` + + 'but it absents in initial state', + ); + } + } + } + + // Now find deleted objects + const indexes = { + shapes: exported.shapes.map((object) => +object.id), + tracks: exported.tracks.map((object) => +object.id), + tags: exported.tags.map((object) => +object.id), + }; + + for (const type of Object.keys(this.initialObjects)) { + for (const id of Object.keys(this.initialObjects[type])) { + if (!indexes[type].includes(+id)) { + const object = this.initialObjects[type][id]; + splitted.deleted[type].push(object); + } + } + } + + + return splitted; + } + + _updateCreatedObjects(saved, indexes) { + const savedLength = saved.tracks.length + + saved.shapes.length + saved.tags.length; + + const indexesLength = indexes.tracks.length + + indexes.shapes.length + indexes.tags.length; + + if (indexesLength !== savedLength) { + throw new ScriptingError( + 'Number of indexes is differed by number of saved objects' + + `${indexesLength} vs ${savedLength}`, + ); + } + + // Updated IDs of created objects + for (const type of Object.keys(indexes)) { + for (let i = 0; i < indexes[type].length; i++) { + const clientID = indexes[type][i]; + this.collection.objects[clientID].serverID = saved[type][i].id; + if (type === 'tracks') { + // We have to reset cache because of old value of serverID was saved there + this.collection.objects[clientID].resetCache(); + } + } + } + } + + _receiveIndexes(exported) { + // Receive client indexes before saving + const indexes = { + tracks: exported.tracks.map((track) => track.clientID), + shapes: exported.shapes.map((shape) => shape.clientID), + tags: exported.tags.map((tag) => tag.clientID), + }; + + // Remove them from the request body + exported.tracks.concat(exported.shapes).concat(exported.tags) + .map((value) => { + delete value.clientID; + return value; + }); + + return indexes; + } + + async save(onUpdate) { + if (typeof onUpdate !== 'function') { + onUpdate = (message) => { + console.log(message); + }; + } + + try { + const exported = this.collection.export(); + const { flush } = this.collection; + if (flush) { + onUpdate('New objects are being saved..'); + const indexes = this._receiveIndexes(exported); + const savedData = await this._put({ ...exported, version: this.version }); + this.version = savedData.version; + this.collection.flush = false; + + onUpdate('Saved objects are being updated in the client'); + this._updateCreatedObjects(savedData, indexes); + + onUpdate('Initial state is being updated'); + + this._resetState(); + for (const type of Object.keys(this.initialObjects)) { + for (const object of savedData[type]) { + this.initialObjects[type][object.id] = object; + } + } + } else { + const { + created, + updated, + deleted, + } = this._split(exported); + + onUpdate('New objects are being saved..'); + const indexes = this._receiveIndexes(created); + const createdData = await this._create({ ...created, version: this.version }); + this.version = createdData.version; + + onUpdate('Saved objects are being updated in the client'); + this._updateCreatedObjects(createdData, indexes); + + onUpdate('Initial state is being updated'); + for (const type of Object.keys(this.initialObjects)) { + for (const object of createdData[type]) { + this.initialObjects[type][object.id] = object; + } + } + + onUpdate('Changed objects are being saved..'); + this._receiveIndexes(updated); + const updatedData = await this._update({ ...updated, version: this.version }); + this.version = updatedData.version; + + onUpdate('Initial state is being updated'); + for (const type of Object.keys(this.initialObjects)) { + for (const object of updatedData[type]) { + this.initialObjects[type][object.id] = object; + } + } + + onUpdate('Changed objects are being saved..'); + this._receiveIndexes(deleted); + const deletedData = await this._delete({ ...deleted, version: this.version }); + this._version = deletedData.version; + + onUpdate('Initial state is being updated'); + for (const type of Object.keys(this.initialObjects)) { + for (const object of deletedData[type]) { + delete this.initialObjects[type][object.id]; + } + } + } + + this.hash = this._getHash(); + onUpdate('Saving is done'); + } catch (error) { + onUpdate(`Can not save annotations: ${error.message}`); + throw error; + } + } + + hasUnsavedChanges() { + return this._getHash() !== this.hash; + } + } + + module.exports = AnnotationsSaver; +})(); diff --git a/cvat-core/src/annotations.js b/cvat-core/src/annotations.js new file mode 100644 index 00000000..8c9014ca --- /dev/null +++ b/cvat-core/src/annotations.js @@ -0,0 +1,242 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const serverProxy = require('./server-proxy'); + const Collection = require('./annotations-collection'); + const AnnotationsSaver = require('./annotations-saver'); + const { checkObjectType } = require('./common'); + const { Task } = require('./session'); + const { + Loader, + Dumper, + } = require('./annotation-format.js'); + const { + ScriptingError, + DataError, + ArgumentError, + } = require('./exceptions'); + + const jobCache = new WeakMap(); + const taskCache = new WeakMap(); + + function getCache(sessionType) { + if (sessionType === 'task') { + return taskCache; + } + + if (sessionType === 'job') { + return jobCache; + } + + throw new ScriptingError( + `Unknown session type was received ${sessionType}`, + ); + } + + async function getAnnotationsFromServer(session) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (!cache.has(session)) { + const rawAnnotations = await serverProxy.annotations + .getAnnotations(sessionType, session.id); + + // Get meta information about frames + const startFrame = sessionType === 'job' ? session.startFrame : 0; + const stopFrame = sessionType === 'job' ? session.stopFrame : session.size - 1; + const frameMeta = {}; + for (let i = startFrame; i <= stopFrame; i++) { + frameMeta[i] = await session.frames.get(i); + } + + const collection = new Collection({ + labels: session.labels || session.task.labels, + startFrame, + stopFrame, + frameMeta, + }).import(rawAnnotations); + + const saver = new AnnotationsSaver(rawAnnotations.version, collection, session); + + cache.set(session, { + collection, + saver, + + }); + } + } + + async function getAnnotations(session, frame, filter) { + await getAnnotationsFromServer(session); + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + return cache.get(session).collection.get(frame, filter); + } + + async function saveAnnotations(session, onUpdate) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + await cache.get(session).saver.save(onUpdate); + } + + // If a collection wasn't uploaded, than it wasn't changed, finally we shouldn't save it + } + + function mergeAnnotations(session, objectStates) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).collection.merge(objectStates); + } + + throw new DataError( + 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', + ); + } + + function splitAnnotations(session, objectState, frame) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).collection.split(objectState, frame); + } + + throw new DataError( + 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', + ); + } + + function groupAnnotations(session, objectStates, reset) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).collection.group(objectStates, reset); + } + + throw new DataError( + 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', + ); + } + + function hasUnsavedChanges(session) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).saver.hasUnsavedChanges(); + } + + return false; + } + + async function clearAnnotations(session, reload) { + checkObjectType('reload', reload, 'boolean', null); + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + cache.get(session).collection.clear(); + } + + if (reload) { + cache.delete(session); + await getAnnotationsFromServer(session); + } + } + + function annotationsStatistics(session) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).collection.statistics(); + } + + throw new DataError( + 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', + ); + } + + function putAnnotations(session, objectStates) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).collection.put(objectStates); + } + + throw new DataError( + 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', + ); + } + + function selectObject(session, objectStates, x, y) { + const sessionType = session instanceof Task ? 'task' : 'job'; + const cache = getCache(sessionType); + + if (cache.has(session)) { + return cache.get(session).collection.select(objectStates, x, y); + } + + throw new DataError( + 'Collection has not been initialized yet. Call annotations.get() or annotations.clear(true) before', + ); + } + + async function uploadAnnotations(session, file, loader) { + const sessionType = session instanceof Task ? 'task' : 'job'; + if (!(loader instanceof Loader)) { + throw new ArgumentError( + 'A loader must be instance of Loader class', + ); + } + await serverProxy.annotations.uploadAnnotations(sessionType, session.id, file, loader.name); + } + + async function dumpAnnotations(session, name, dumper) { + if (!(dumper instanceof Dumper)) { + throw new ArgumentError( + 'A dumper must be instance of Dumper class', + ); + } + + let result = null; + const sessionType = session instanceof Task ? 'task' : 'job'; + if (sessionType === 'job') { + result = await serverProxy.annotations + .dumpAnnotations(session.task.id, name, dumper.name); + } else { + result = await serverProxy.annotations + .dumpAnnotations(session.id, name, dumper.name); + } + + return result; + } + + module.exports = { + getAnnotations, + putAnnotations, + saveAnnotations, + hasUnsavedChanges, + mergeAnnotations, + splitAnnotations, + groupAnnotations, + clearAnnotations, + annotationsStatistics, + selectObject, + uploadAnnotations, + dumpAnnotations, + }; +})(); diff --git a/cvat-core/src/api-implementation.js b/cvat-core/src/api-implementation.js new file mode 100644 index 00000000..8f4b8841 --- /dev/null +++ b/cvat-core/src/api-implementation.js @@ -0,0 +1,172 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */ + +/* global + require:false +*/ + + +(() => { + const PluginRegistry = require('./plugins'); + const serverProxy = require('./server-proxy'); + const { + isBoolean, + isInteger, + isEnum, + isString, + checkFilter, + } = require('./common'); + + const { + TaskStatus, + TaskMode, + } = require('./enums'); + + const User = require('./user'); + const { AnnotationFormat } = require('./annotation-format.js'); + const { ArgumentError } = require('./exceptions'); + const { Task } = require('./session'); + + function implementAPI(cvat) { + cvat.plugins.list.implementation = PluginRegistry.list; + cvat.plugins.register.implementation = PluginRegistry.register.bind(cvat); + + cvat.server.about.implementation = async () => { + const result = await serverProxy.server.about(); + return result; + }; + + cvat.server.share.implementation = async (directory) => { + const result = await serverProxy.server.share(directory); + return result; + }; + + cvat.server.formats.implementation = async () => { + const result = await serverProxy.server.formats(); + return result.map(el => new AnnotationFormat(el)); + }; + + cvat.server.register.implementation = async (username, firstName, lastName, + email, password1, password2) => { + await serverProxy.server.register(username, firstName, lastName, email, + password1, password2); + }; + + cvat.server.login.implementation = async (username, password) => { + await serverProxy.server.login(username, password); + }; + + cvat.server.logout.implementation = async () => { + await serverProxy.server.logout(); + }; + + cvat.server.authorized.implementation = async () => { + const result = await serverProxy.server.authorized(); + return result; + }; + + cvat.users.get.implementation = async (filter) => { + checkFilter(filter, { + self: isBoolean, + }); + + let users = null; + if ('self' in filter && filter.self) { + users = await serverProxy.users.getSelf(); + users = [users]; + } else { + users = await serverProxy.users.getUsers(); + } + + users = users.map(user => new User(user)); + return users; + }; + + cvat.jobs.get.implementation = async (filter) => { + checkFilter(filter, { + taskID: isInteger, + jobID: isInteger, + }); + + if (('taskID' in filter) && ('jobID' in filter)) { + throw new ArgumentError( + 'Only one of fields "taskID" and "jobID" allowed simultaneously', + ); + } + + if (!Object.keys(filter).length) { + throw new ArgumentError( + 'Job filter must not be empty', + ); + } + + let tasks = null; + if ('taskID' in filter) { + tasks = await serverProxy.tasks.getTasks(`id=${filter.taskID}`); + } else { + const job = await serverProxy.jobs.getJob(filter.jobID); + if (typeof (job.task_id) !== 'undefined') { + tasks = await serverProxy.tasks.getTasks(`id=${job.task_id}`); + } + } + + // If task was found by its id, then create task instance and get Job instance from it + if (tasks !== null && tasks.length) { + const task = new Task(tasks[0]); + return filter.jobID ? task.jobs.filter(job => job.id === filter.jobID) : task.jobs; + } + + return []; + }; + + cvat.tasks.get.implementation = async (filter) => { + checkFilter(filter, { + page: isInteger, + name: isString, + id: isInteger, + owner: isString, + assignee: isString, + search: isString, + status: isEnum.bind(TaskStatus), + mode: isEnum.bind(TaskMode), + }); + + if ('search' in filter && Object.keys(filter).length > 1) { + if (!('page' in filter && Object.keys(filter).length === 2)) { + throw new ArgumentError( + 'Do not use the filter field "search" with others', + ); + } + } + + if ('id' in filter && Object.keys(filter).length > 1) { + if (!('page' in filter && Object.keys(filter).length === 2)) { + throw new ArgumentError( + 'Do not use the filter field "id" with others', + ); + } + } + + const searchParams = new URLSearchParams(); + for (const field of ['name', 'owner', 'assignee', 'search', 'status', 'mode', 'id', 'page']) { + if (Object.prototype.hasOwnProperty.call(filter, field)) { + searchParams.set(field, filter[field]); + } + } + + const tasksData = await serverProxy.tasks.getTasks(searchParams.toString()); + const tasks = tasksData.map(task => new Task(task)); + tasks.count = tasksData.count; + + return tasks; + }; + + return cvat; + } + + module.exports = implementAPI; +})(); diff --git a/cvat-core/src/api.js b/cvat-core/src/api.js new file mode 100644 index 00000000..21c2299a --- /dev/null +++ b/cvat-core/src/api.js @@ -0,0 +1,520 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +/** + * External API which should be used by for development + * @module API +*/ + +function build() { + const PluginRegistry = require('./plugins'); + const User = require('./user'); + const ObjectState = require('./object-state'); + const Statistics = require('./statistics'); + const { Job, Task } = require('./session'); + const { Attribute, Label } = require('./labels'); + + const { + ShareFileType, + TaskStatus, + TaskMode, + AttributeType, + ObjectType, + ObjectShape, + VisibleState, + LogType, + } = require('./enums'); + + const { + Exception, + ArgumentError, + DataError, + ScriptingError, + PluginError, + ServerError, + } = require('./exceptions'); + + const pjson = require('../package.json'); + const config = require('./config'); + + /** + * API entrypoint + * @namespace cvat + * @memberof module:API + */ + const cvat = { + /** + * Namespace is used for an interaction with a server + * @namespace server + * @package + * @memberof module:API.cvat + */ + server: { + /** + * @typedef {Object} ServerInfo + * @property {string} name A name of the tool + * @property {string} description A description of the tool + * @property {string} version A version of the tool + * @global + */ + + /** + * Method returns some information about the annotation tool + * @method about + * @async + * @memberof module:API.cvat.server + * @return {ServerInfo} + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async about() { + const result = await PluginRegistry + .apiWrapper(cvat.server.about); + return result; + }, + /** + * @typedef {Object} FileInfo + * @property {string} name A name of a file + * @property {module:API.cvat.enums.ShareFileType} type + * A type of a file + * @global + */ + + /** + * Method returns a list of files in a specified directory on a share + * @method share + * @async + * @memberof module:API.cvat.server + * @param {string} [directory=/] - Share directory path + * @returns {FileInfo[]} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async share(directory = '/') { + const result = await PluginRegistry + .apiWrapper(cvat.server.share, directory); + return result; + }, + /** + * Method returns available annotation formats + * @method formats + * @async + * @memberof module:API.cvat.server + * @returns {module:API.cvat.classes.AnnotationFormat[]} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async formats() { + const result = await PluginRegistry + .apiWrapper(cvat.server.formats); + return result; + }, + /** + * Method allows to register on a server + * @method register + * @async + * @memberof module:API.cvat.server + * @param {string} username An username for the new account + * @param {string} firstName A first name for the new account + * @param {string} lastName A last name for the new account + * @param {string} email A email address for the new account + * @param {string} password1 A password for the new account + * @param {string} password2 The confirmation password for the new account + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async register(username, firstName, lastName, email, password1, password2) { + const result = await PluginRegistry + .apiWrapper(cvat.server.register, username, firstName, + lastName, email, password1, password2); + return result; + }, + /** + * Method allows to login on a server + * @method login + * @async + * @memberof module:API.cvat.server + * @param {string} username An username of an account + * @param {string} password A password of an account + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async login(username, password) { + const result = await PluginRegistry + .apiWrapper(cvat.server.login, username, password); + return result; + }, + /** + * Method allows to logout from the server + * @method logout + * @async + * @memberof module:API.cvat.server + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async logout() { + const result = await PluginRegistry + .apiWrapper(cvat.server.logout); + return result; + }, + /** + * Method allows to know whether you are authorized on the server + * @method authorized + * @async + * @memberof module:API.cvat.server + * @returns {boolean} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async authorized() { + const result = await PluginRegistry + .apiWrapper(cvat.server.authorized); + return result; + }, + }, + /** + * Namespace is used for getting tasks + * @namespace tasks + * @memberof module:API.cvat + */ + tasks: { + /** + * @typedef {Object} TaskFilter + * @property {string} name Check if name contains this value + * @property {module:API.cvat.enums.TaskStatus} status + * Check if status contains this value + * @property {module:API.cvat.enums.TaskMode} mode + * Check if mode contains this value + * @property {integer} id Check if id equals this value + * @property {integer} page Get specific page + * (default REST API returns 20 tasks per request. + * In order to get more, it is need to specify next page) + * @property {string} owner Check if owner user contains this value + * @property {string} assignee Check if assigneed contains this value + * @property {string} search Combined search of contains among all fields + * @global + */ + + /** + * Method returns list of tasks corresponding to a filter + * @method get + * @async + * @memberof module:API.cvat.tasks + * @param {TaskFilter} [filter={}] task filter + * @returns {module:API.cvat.classes.Task[]} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async get(filter = {}) { + const result = await PluginRegistry + .apiWrapper(cvat.tasks.get, filter); + return result; + }, + }, + /** + * Namespace is used for getting jobs + * @namespace jobs + * @memberof module:API.cvat + */ + jobs: { + /** + * @typedef {Object} JobFilter + * Only one of fields is allowed simultaneously + * @property {integer} taskID filter all jobs of specific task + * @property {integer} jobID filter job with a specific id + * @global + */ + + /** + * Method returns list of jobs corresponding to a filter + * @method get + * @async + * @memberof module:API.cvat.jobs + * @param {JobFilter} filter job filter + * @returns {module:API.cvat.classes.Job[]} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async get(filter = {}) { + const result = await PluginRegistry + .apiWrapper(cvat.jobs.get, filter); + return result; + }, + }, + /** + * Namespace is used for getting users + * @namespace users + * @memberof module:API.cvat + */ + users: { + /** + * @typedef {Object} UserFilter + * @property {boolean} self get only self + * @global + */ + + /** + * Method returns list of users corresponding to a filter + * @method get + * @async + * @memberof module:API.cvat.users + * @param {UserFilter} [filter={}] user filter + * @returns {module:API.cvat.classes.User[]} + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + */ + async get(filter = {}) { + const result = await PluginRegistry + .apiWrapper(cvat.users.get, filter); + return result; + }, + }, + /** + * Namespace is used for plugin management + * @namespace plugins + * @memberof module:API.cvat + */ + plugins: { + /** + * @typedef {Object} Plugin + * A plugin is a Javascript object. It must have properties are listed below.
+ * It also mustn't have property 'functions' which is used internally.
+ * You can expand any API method including class methods.
+ * In order to expand class method just use a class name + * in a cvat space (example is listed below). + * + * @property {string} name A name of a plugin + * @property {string} description A description of a plugin + * Example plugin implementation listed below: + * @example + * plugin = { + * name: 'Example Plugin', + * description: 'This example plugin demonstrates how plugin system in CVAT works', + * cvat: { + * server: { + * about: { + * // Plugin adds some actions after executing the cvat.server.about() + * // For example it adds a field with installed plugins to a result + * // An argument "self" is a plugin itself + * // An argument "result" is a return value of cvat.server.about() + * // All next arguments are arguments of a wrapped function + * // (in this case the wrapped function doesn't have any arguments) + * async leave(self, result) { + * result.plugins = await self.internal.getPlugins(); + * // Note that a method leave must return "result" (changed or not) + * // Otherwise API won't work as expected + * return result; + * }, + * }, + * }, + * // In this example plugin also wraps a class method + * classes: { + * Job: { + * prototype: { + * annotations: { + * put: { + * // The first argument "self" is a plugin, like in a case above + * // The second argument is an argument of the + * // Job.annotations.put() + * // It contains an array of objects to put + * // In this sample we round objects coordinates and save them + * enter(self, objects) { + * for (const obj of objects) { + * if (obj.type != 'tag') { + * const points = obj.position.map((point) => { + * const roundPoint = { + * x: Math.round(point.x), + * y: Math.round(point.y), + * }; + * return roundPoint; + * }); + * } + * } + * }, + * }, + * }, + * }, + * }, + * }, + * }, + * // In general you can add any others members to your plugin + * // Members below are only examples + * internal: { + * async getPlugins() { + * // Collect information about installed plugins + * const plugins = await cvat.plugins.list(); + * return plugins.map((el) => { + * return { + * name: el.name, + * description: el.description, + * }; + * }); + * }, + * }, + * }; + * @global + */ + + /** + * Method returns list of installed plugins + * @method list + * @async + * @memberof module:API.cvat.plugins + * @returns {Plugin[]} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async list() { + const result = await PluginRegistry + .apiWrapper(cvat.plugins.list); + return result; + }, + /** + * Install plugin to CVAT + * @method register + * @async + * @memberof module:API.cvat.plugins + * @param {Plugin} [plugin] plugin for registration + * @throws {module:API.cvat.exceptions.PluginError} + */ + async register(plugin) { + const result = await PluginRegistry + .apiWrapper(cvat.plugins.register, plugin); + return result; + }, + }, + /** + * Namespace contains some changeable configurations + * @namespace config + * @memberof module:API.cvat + */ + config: { + /** + * @memberof module:API.cvat.config + * @property {string} backendAPI host with a backend api + * @memberof module:API.cvat.config + * @property {string} proxy Axios proxy settings. + * For more details please read here + * @memberof module:API.cvat.config + * @property {integer} taskID this value is displayed in a logs if available + * @memberof module:API.cvat.config + * @property {integer} jobID this value is displayed in a logs if available + * @memberof module:API.cvat.config + * @property {integer} clientID read only auto-generated + * value which is displayed in a logs + * @memberof module:API.cvat.config + */ + get backendAPI() { + return config.backendAPI; + }, + set backendAPI(value) { + config.backendAPI = value; + }, + get proxy() { + return config.proxy; + }, + set proxy(value) { + config.proxy = value; + }, + get taskID() { + return config.taskID; + }, + set taskID(value) { + config.taskID = value; + }, + get jobID() { + return config.jobID; + }, + set jobID(value) { + config.jobID = value; + }, + get clientID() { + return config.clientID; + }, + }, + /** + * Namespace contains some library information e.g. api version + * @namespace client + * @memberof module:API.cvat + */ + client: { + /** + * @property {string} version Client version. + * Format: {major}.{minor}.{patch} + *
  • A major number is changed after an API becomes + * incompatible with a previous version + *
  • A minor number is changed after an API expands + *
  • A patch number is changed after an each build + * @memberof module:API.cvat.client + * @readonly + */ + version: `${pjson.version}`, + }, + /** + * Namespace is used for access to enums + * @namespace enums + * @memberof module:API.cvat + */ + enums: { + ShareFileType, + TaskStatus, + TaskMode, + AttributeType, + ObjectType, + ObjectShape, + VisibleState, + LogType, + }, + /** + * Namespace is used for access to exceptions + * @namespace exceptions + * @memberof module:API.cvat + */ + exceptions: { + Exception, + ArgumentError, + DataError, + ScriptingError, + PluginError, + ServerError, + }, + /** + * Namespace is used for access to classes + * @namespace classes + * @memberof module:API.cvat + */ + classes: { + Task, + User, + Job, + Attribute, + Label, + Statistics, + ObjectState, + }, + }; + + cvat.server = Object.freeze(cvat.server); + cvat.tasks = Object.freeze(cvat.tasks); + cvat.jobs = Object.freeze(cvat.jobs); + cvat.users = Object.freeze(cvat.users); + cvat.plugins = Object.freeze(cvat.plugins); + cvat.client = Object.freeze(cvat.client); + cvat.enums = Object.freeze(cvat.enums); + + const implementAPI = require('./api-implementation'); + + Math.clamp = function (value, min, max) { + return Math.min(Math.max(value, min), max); + }; + + const implemented = Object.freeze(implementAPI(cvat)); + return implemented; +} + +module.exports = build(); diff --git a/cvat-core/src/common.js b/cvat-core/src/common.js new file mode 100644 index 00000000..51819bff --- /dev/null +++ b/cvat-core/src/common.js @@ -0,0 +1,90 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const { ArgumentError } = require('./exceptions'); + + function isBoolean(value) { + return typeof (value) === 'boolean'; + } + + function isInteger(value) { + return typeof (value) === 'number' && Number.isInteger(value); + } + + // Called with specific Enum context + function isEnum(value) { + for (const key in this) { + if (Object.prototype.hasOwnProperty.call(this, key)) { + if (this[key] === value) { + return true; + } + } + } + + return false; + } + + function isString(value) { + return typeof (value) === 'string'; + } + + function checkFilter(filter, fields) { + for (const prop in filter) { + if (Object.prototype.hasOwnProperty.call(filter, prop)) { + if (!(prop in fields)) { + throw new ArgumentError( + `Unsupported filter property has been recieved: "${prop}"`, + ); + } else if (!fields[prop](filter[prop])) { + throw new ArgumentError( + `Received filter property "${prop}" is not satisfied for checker`, + ); + } + } + } + } + + function checkObjectType(name, value, type, instance) { + if (type) { + if (typeof (value) !== type) { + // specific case for integers which aren't native type in JS + if (type === 'integer' && Number.isInteger(value)) { + return; + } + + throw new ArgumentError( + `"${name}" is expected to be "${type}", but "${typeof (value)}" has been got.`, + ); + } + } else if (instance) { + if (!(value instanceof instance)) { + if (value !== undefined) { + throw new ArgumentError( + `"${name}" is expected to be ${instance.name}, but ` + + `"${value.constructor.name}" has been got`, + ); + } + + throw new ArgumentError( + `"${name}" is expected to be ${instance.name}, but "undefined" has been got.`, + ); + } + } + } + + module.exports = { + isBoolean, + isInteger, + isEnum, + isString, + checkFilter, + checkObjectType, + }; +})(); diff --git a/cvat-core/src/config.js b/cvat-core/src/config.js new file mode 100644 index 00000000..e940a214 --- /dev/null +++ b/cvat-core/src/config.js @@ -0,0 +1,12 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +module.exports = { + backendAPI: 'http://localhost:7000/api/v1', + proxy: false, + taskID: undefined, + jobID: undefined, + clientID: +Date.now().toString().substr(-6), +}; diff --git a/cvat-core/src/enums.js b/cvat-core/src/enums.js new file mode 100644 index 00000000..241cca71 --- /dev/null +++ b/cvat-core/src/enums.js @@ -0,0 +1,195 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +(() => { + /** + * Share files types + * @enum {string} + * @name ShareFileType + * @memberof module:API.cvat.enums + * @property {string} DIR 'DIR' + * @property {string} REG 'REG' + * @readonly + */ + const ShareFileType = Object.freeze({ + DIR: 'DIR', + REG: 'REG', + }); + + /** + * Task statuses + * @enum {string} + * @name TaskStatus + * @memberof module:API.cvat.enums + * @property {string} ANNOTATION 'annotation' + * @property {string} VALIDATION 'validation' + * @property {string} COMPLETED 'completed' + * @readonly + */ + const TaskStatus = Object.freeze({ + ANNOTATION: 'annotation', + VALIDATION: 'validation', + COMPLETED: 'completed', + }); + + /** + * Task modes + * @enum {string} + * @name TaskMode + * @memberof module:API.cvat.enums + * @property {string} ANNOTATION 'annotation' + * @property {string} INTERPOLATION 'interpolation' + * @readonly + */ + const TaskMode = Object.freeze({ + ANNOTATION: 'annotation', + INTERPOLATION: 'interpolation', + }); + + /** + * Attribute types + * @enum {string} + * @name AttributeType + * @memberof module:API.cvat.enums + * @property {string} CHECKBOX 'checkbox' + * @property {string} SELECT 'select' + * @property {string} RADIO 'radio' + * @property {string} NUMBER 'number' + * @property {string} TEXT 'text' + * @readonly + */ + const AttributeType = Object.freeze({ + CHECKBOX: 'checkbox', + RADIO: 'radio', + SELECT: 'select', + NUMBER: 'number', + TEXT: 'text', + }); + + /** + * Object types + * @enum {string} + * @name ObjectType + * @memberof module:API.cvat.enums + * @property {string} TAG 'tag' + * @property {string} SHAPE 'shape' + * @property {string} TRACK 'track' + * @readonly + */ + const ObjectType = Object.freeze({ + TAG: 'tag', + SHAPE: 'shape', + TRACK: 'track', + }); + + /** + * Object shapes + * @enum {string} + * @name ObjectShape + * @memberof module:API.cvat.enums + * @property {string} RECTANGLE 'rectangle' + * @property {string} POLYGON 'polygon' + * @property {string} POLYLINE 'polyline' + * @property {string} POINTS 'points' + * @readonly + */ + const ObjectShape = Object.freeze({ + RECTANGLE: 'rectangle', + POLYGON: 'polygon', + POLYLINE: 'polyline', + POINTS: 'points', + }); + + /** + * Object visibility states + * @enum {string} + * @name ObjectShape + * @memberof module:API.cvat.enums + * @property {string} ALL 'all' + * @property {string} SHAPE 'shape' + * @property {string} NONE 'none' + * @readonly + */ + const VisibleState = Object.freeze({ + ALL: 'all', + SHAPE: 'shape', + NONE: 'none', + }); + + /** + * Event types + * @enum {number} + * @name LogType + * @memberof module:API.cvat.enums + * @property {number} pasteObject 0 + * @property {number} changeAttribute 1 + * @property {number} dragObject 2 + * @property {number} deleteObject 3 + * @property {number} pressShortcut 4 + * @property {number} resizeObject 5 + * @property {number} sendLogs 6 + * @property {number} saveJob 7 + * @property {number} jumpFrame 8 + * @property {number} drawObject 9 + * @property {number} changeLabel 10 + * @property {number} sendTaskInfo 11 + * @property {number} loadJob 12 + * @property {number} moveImage 13 + * @property {number} zoomImage 14 + * @property {number} lockObject 15 + * @property {number} mergeObjects 16 + * @property {number} copyObject 17 + * @property {number} propagateObject 18 + * @property {number} undoAction 19 + * @property {number} redoAction 20 + * @property {number} sendUserActivity 21 + * @property {number} sendException 22 + * @property {number} changeFrame 23 + * @property {number} debugInfo 24 + * @property {number} fitImage 25 + * @property {number} rotateImage 26 + * @readonly + */ + const LogType = { + pasteObject: 0, + changeAttribute: 1, + dragObject: 2, + deleteObject: 3, + pressShortcut: 4, + resizeObject: 5, + sendLogs: 6, + saveJob: 7, + jumpFrame: 8, + drawObject: 9, + changeLabel: 10, + sendTaskInfo: 11, + loadJob: 12, + moveImage: 13, + zoomImage: 14, + lockObject: 15, + mergeObjects: 16, + copyObject: 17, + propagateObject: 18, + undoAction: 19, + redoAction: 20, + sendUserActivity: 21, + sendException: 22, + changeFrame: 23, + debugInfo: 24, + fitImage: 25, + rotateImage: 26, + }; + + module.exports = { + ShareFileType, + TaskStatus, + TaskMode, + AttributeType, + ObjectType, + ObjectShape, + VisibleState, + LogType, + }; +})(); diff --git a/cvat-core/src/exceptions.js b/cvat-core/src/exceptions.js new file mode 100644 index 00000000..97788cf6 --- /dev/null +++ b/cvat-core/src/exceptions.js @@ -0,0 +1,272 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const Platform = require('platform'); + const ErrorStackParser = require('error-stack-parser'); + const config = require('./config'); + + /** + * Base exception class + * @memberof module:API.cvat.exceptions + * @extends Error + * @ignore + */ + class Exception extends Error { + /** + * @param {string} message - Exception message + */ + constructor(message) { + super(message); + + const time = new Date().toISOString(); + const system = Platform.os.toString(); + const client = `${Platform.name} ${Platform.version}`; + const info = ErrorStackParser.parse(this)[0]; + const filename = `${info.fileName}`; + const line = info.lineNumber; + const column = info.columnNumber; + const { + jobID, + taskID, + clientID, + } = config; + + const projID = undefined; // wasn't implemented + + Object.defineProperties(this, Object.freeze({ + system: { + /** + * @name system + * @type {string} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => system, + }, + client: { + /** + * @name client + * @type {string} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => client, + }, + time: { + /** + * @name time + * @type {string} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => time, + }, + jobID: { + /** + * @name jobID + * @type {integer} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => jobID, + }, + taskID: { + /** + * @name taskID + * @type {integer} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => taskID, + }, + projID: { + /** + * @name projID + * @type {integer} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => projID, + }, + clientID: { + /** + * @name clientID + * @type {integer} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => clientID, + }, + filename: { + /** + * @name filename + * @type {string} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => filename, + }, + line: { + /** + * @name line + * @type {integer} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => line, + }, + column: { + /** + * @name column + * @type {integer} + * @memberof module:API.cvat.exceptions.Exception + * @readonly + * @instance + */ + get: () => column, + }, + })); + } + + /** + * Save an exception on a server + * @name save + * @method + * @memberof Exception + * @instance + * @async + */ + async save() { + const exceptionObject = { + system: this.system, + client: this.client, + time: this.time, + job_id: this.jobID, + task_id: this.taskID, + proj_id: this.projID, + client_id: this.clientID, + message: this.message, + filename: this.filename, + line: this.line, + column: this.column, + stack: this.stack, + }; + + try { + const serverProxy = require('./server-proxy'); + await serverProxy.server.exception(exceptionObject); + } catch (exception) { + // add event + } + } + } + + /** + * Exceptions are referred with arguments data + * @memberof module:API.cvat.exceptions + * @extends module:API.cvat.exceptions.Exception + */ + class ArgumentError extends Exception { + /** + * @param {string} message - Exception message + */ + constructor(message) { + super(message); + } + } + + /** + * Unexpected problems with data which are not connected with a user input + * @memberof module:API.cvat.exceptions + * @extends module:API.cvat.exceptions.Exception + */ + class DataError extends Exception { + /** + * @param {string} message - Exception message + */ + constructor(message) { + super(message); + } + } + + /** + * Unexpected situations in code + * @memberof module:API.cvat.exceptions + * @extends module:API.cvat.exceptions.Exception + */ + class ScriptingError extends Exception { + /** + * @param {string} message - Exception message + */ + constructor(message) { + super(message); + } + } + + /** + * Plugin-referred exceptions + * @memberof module:API.cvat.exceptions + * @extends module:API.cvat.exceptions.Exception + */ + class PluginError extends Exception { + /** + * @param {string} message - Exception message + */ + constructor(message) { + super(message); + } + } + + /** + * Exceptions in interaction with a server + * @memberof module:API.cvat.exceptions + * @extends module:API.cvat.exceptions.Exception + */ + class ServerError extends Exception { + /** + * @param {string} message - Exception message + * @param {(string|integer)} code - Response code + */ + constructor(message, code) { + super(message); + + Object.defineProperties(this, Object.freeze({ + /** + * @name code + * @type {(string|integer)} + * @memberof module:API.cvat.exceptions.ServerError + * @readonly + * @instance + */ + code: { + get: () => code, + }, + })); + } + } + + module.exports = { + Exception, + ArgumentError, + DataError, + ScriptingError, + PluginError, + ServerError, + }; +})(); diff --git a/cvat-core/src/frames.js b/cvat-core/src/frames.js new file mode 100644 index 00000000..06342bd0 --- /dev/null +++ b/cvat-core/src/frames.js @@ -0,0 +1,144 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false + global:false +*/ + +(() => { + const PluginRegistry = require('./plugins'); + const serverProxy = require('./server-proxy'); + const { ArgumentError } = require('./exceptions'); + const { isBrowser, isNode } = require('browser-or-node'); + + // This is the frames storage + const frameDataCache = {}; + const frameCache = {}; + + /** + * Class provides meta information about specific frame and frame itself + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class FrameData { + constructor(width, height, tid, number) { + Object.defineProperties(this, Object.freeze({ + /** + * @name width + * @type {integer} + * @memberof module:API.cvat.classes.FrameData + * @readonly + * @instance + */ + width: { + value: width, + writable: false, + }, + /** + * @name height + * @type {integer} + * @memberof module:API.cvat.classes.FrameData + * @readonly + * @instance + */ + height: { + value: height, + writable: false, + }, + tid: { + value: tid, + writable: false, + }, + number: { + value: number, + writable: false, + }, + })); + } + + /** + * Method returns URL encoded image which can be placed in the img tag + * @method data + * @returns {string} + * @memberof module:API.cvat.classes.FrameData + * @instance + * @async + * @param {function} [onServerRequest = () => {}] + * callback which will be called if data absences local + * @throws {module:API.cvat.exception.ServerError} + * @throws {module:API.cvat.exception.PluginError} + */ + async data(onServerRequest = () => {}) { + const result = await PluginRegistry + .apiWrapper.call(this, FrameData.prototype.data, onServerRequest); + return result; + } + } + + FrameData.prototype.data.implementation = async function (onServerRequest) { + return new Promise(async (resolve, reject) => { + try { + if (this.number in frameCache[this.tid]) { + resolve(frameCache[this.tid][this.number]); + } else { + onServerRequest(); + const frame = await serverProxy.frames.getData(this.tid, this.number); + + if (isNode) { + frameCache[this.tid][this.number] = global.Buffer.from(frame, 'binary').toString('base64'); + resolve(frameCache[this.tid][this.number]); + } else if (isBrowser) { + const reader = new FileReader(); + reader.onload = () => { + frameCache[this.tid][this.number] = reader.result; + resolve(frameCache[this.tid][this.number]); + }; + reader.readAsDataURL(frame); + } + } + } catch (exception) { + reject(exception); + } + }); + }; + + async function getFrame(taskID, mode, frame) { + if (!(taskID in frameDataCache)) { + frameDataCache[taskID] = {}; + frameDataCache[taskID].meta = await serverProxy.frames.getMeta(taskID); + + frameCache[taskID] = {}; + } + + if (!(frame in frameDataCache[taskID])) { + let size = null; + if (mode === 'interpolation') { + [size] = frameDataCache[taskID].meta; + } else if (mode === 'annotation') { + if (frame >= frameDataCache[taskID].meta.length) { + throw new ArgumentError( + `Meta information about frame ${frame} can't be received from the server`, + ); + } else { + size = frameDataCache[taskID].meta[frame]; + } + } else { + throw new ArgumentError( + `Invalid mode is specified ${mode}`, + ); + } + + frameDataCache[taskID][frame] = new FrameData(size.width, size.height, taskID, frame); + } + + return frameDataCache[taskID][frame]; + } + + module.exports = { + FrameData, + getFrame, + }; +})(); diff --git a/cvat-core/src/labels.js b/cvat-core/src/labels.js new file mode 100644 index 00000000..dc0235ff --- /dev/null +++ b/cvat-core/src/labels.js @@ -0,0 +1,210 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const { AttributeType } = require('./enums'); + const { ArgumentError } = require('./exceptions'); + + /** + * Class representing an attribute + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class Attribute { + constructor(initialData) { + const data = { + id: undefined, + default_value: undefined, + input_type: undefined, + mutable: undefined, + name: undefined, + values: undefined, + }; + + for (const key in data) { + if (Object.prototype.hasOwnProperty.call(data, key)) { + if (Object.prototype.hasOwnProperty.call(initialData, key)) { + if (Array.isArray(initialData[key])) { + data[key] = [...initialData[key]]; + } else { + data[key] = initialData[key]; + } + } + } + } + + if (!Object.values(AttributeType).includes(data.input_type)) { + throw new ArgumentError( + `Got invalid attribute type ${data.input_type}`, + ); + } + + Object.defineProperties(this, Object.freeze({ + /** + * @name id + * @type {integer} + * @memberof module:API.cvat.classes.Attribute + * @readonly + * @instance + */ + id: { + get: () => data.id, + }, + /** + * @name defaultValue + * @type {(string|integer|boolean)} + * @memberof module:API.cvat.classes.Attribute + * @readonly + * @instance + */ + defaultValue: { + get: () => data.default_value, + }, + /** + * @name inputType + * @type {module:API.cvat.enums.AttributeType} + * @memberof module:API.cvat.classes.Attribute + * @readonly + * @instance + */ + inputType: { + get: () => data.input_type, + }, + /** + * @name mutable + * @type {boolean} + * @memberof module:API.cvat.classes.Attribute + * @readonly + * @instance + */ + mutable: { + get: () => data.mutable, + }, + /** + * @name name + * @type {string} + * @memberof module:API.cvat.classes.Attribute + * @readonly + * @instance + */ + name: { + get: () => data.name, + }, + /** + * @name values + * @type {(string[]|integer[]|boolean[])} + * @memberof module:API.cvat.classes.Attribute + * @readonly + * @instance + */ + values: { + get: () => [...data.values], + }, + })); + } + + toJSON() { + const object = { + name: this.name, + mutable: this.mutable, + input_type: this.inputType, + default_value: this.defaultValue, + values: this.values, + }; + + if (typeof (this.id) !== 'undefined') { + object.id = this.id; + } + + return object; + } + } + + /** + * Class representing a label + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class Label { + constructor(initialData) { + const data = { + id: undefined, + name: undefined, + }; + + for (const key in data) { + if (Object.prototype.hasOwnProperty.call(data, key)) { + if (Object.prototype.hasOwnProperty.call(initialData, key)) { + data[key] = initialData[key]; + } + } + } + + data.attributes = []; + + if (Object.prototype.hasOwnProperty.call(initialData, 'attributes') + && Array.isArray(initialData.attributes)) { + for (const attrData of initialData.attributes) { + data.attributes.push(new Attribute(attrData)); + } + } + + Object.defineProperties(this, Object.freeze({ + /** + * @name id + * @type {integer} + * @memberof module:API.cvat.classes.Label + * @readonly + * @instance + */ + id: { + get: () => data.id, + }, + /** + * @name name + * @type {string} + * @memberof module:API.cvat.classes.Label + * @readonly + * @instance + */ + name: { + get: () => data.name, + }, + /** + * @name attributes + * @type {module:API.cvat.classes.Attribute[]} + * @memberof module:API.cvat.classes.Label + * @readonly + * @instance + */ + attributes: { + get: () => [...data.attributes], + }, + })); + } + + toJSON() { + const object = { + name: this.name, + attributes: [...this.attributes.map(el => el.toJSON())], + }; + + if (typeof (this.id) !== 'undefined') { + object.id = this.id; + } + + return object; + } + } + + module.exports = { + Attribute, + Label, + }; +})(); diff --git a/cvat-core/src/logging.js b/cvat-core/src/logging.js new file mode 100644 index 00000000..f6b52c4e --- /dev/null +++ b/cvat-core/src/logging.js @@ -0,0 +1,42 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const PluginRegistry = require('./plugins'); + + /** + * Class describe scheme of a log object + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class Log { + constructor(logType, continuous, details) { + this.type = logType; + this.continuous = continuous; + this.details = details; + } + + /** + * Method closes a continue log + * @method close + * @memberof module:API.cvat.classes.Log + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + */ + async close() { + const result = await PluginRegistry + .apiWrapper.call(this, Log.prototype.close); + return result; + } + } + + module.exports = Log; +})(); diff --git a/cvat-core/src/object-state.js b/cvat-core/src/object-state.js new file mode 100644 index 00000000..a3ad0952 --- /dev/null +++ b/cvat-core/src/object-state.js @@ -0,0 +1,419 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const PluginRegistry = require('./plugins'); + const { ArgumentError } = require('./exceptions'); + + /** + * Class representing a state of an object on a specific frame + * @memberof module:API.cvat.classes + */ + class ObjectState { + /** + * @param {Object} serialized - is an dictionary which contains + * initial information about an ObjectState; + * Necessary fields: objectType, shapeType + * (don't have setters) + * Necessary fields for objects which haven't been added to collection yet: frame + * (doesn't have setters) + * Optional fields: points, group, zOrder, outside, occluded, + * attributes, lock, label, mode, color, keyframe, clientID, serverID + * These fields can be set later via setters + */ + constructor(serialized) { + const data = { + label: null, + attributes: {}, + + points: null, + outside: null, + occluded: null, + keyframe: null, + + group: null, + zOrder: null, + lock: null, + color: null, + visibility: null, + + clientID: serialized.clientID, + serverID: serialized.serverID, + + frame: serialized.frame, + objectType: serialized.objectType, + shapeType: serialized.shapeType, + updateFlags: {}, + }; + + // Shows whether any properties updated since last reset() or interpolation + Object.defineProperty(data.updateFlags, 'reset', { + value: function reset() { + this.label = false; + this.attributes = false; + + this.points = false; + this.outside = false; + this.occluded = false; + this.keyframe = false; + + this.group = false; + this.zOrder = false; + this.lock = false; + this.color = false; + this.visibility = false; + }, + writable: false, + }); + + Object.defineProperties(this, Object.freeze({ + // Internal property. We don't need document it. + updateFlags: { + get: () => data.updateFlags, + }, + frame: { + /** + * @name frame + * @type {integer} + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + */ + get: () => data.frame, + }, + objectType: { + /** + * @name objectType + * @type {module:API.cvat.enums.ObjectType} + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + */ + get: () => data.objectType, + }, + shapeType: { + /** + * @name shapeType + * @type {module:API.cvat.enums.ObjectShape} + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + */ + get: () => data.shapeType, + }, + clientID: { + /** + * @name clientID + * @type {integer} + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + */ + get: () => data.clientID, + }, + serverID: { + /** + * @name serverID + * @type {integer} + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + */ + get: () => data.serverID, + }, + label: { + /** + * @name shape + * @type {module:API.cvat.classes.Label} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.label, + set: (labelInstance) => { + data.updateFlags.label = true; + data.label = labelInstance; + }, + }, + color: { + /** + * @name color + * @type {string} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.color, + set: (color) => { + data.updateFlags.color = true; + data.color = color; + }, + }, + visibility: { + /** + * @name visibility + * @type {module:API.cvat.enums.VisibleState} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.visibility, + set: (visibility) => { + data.updateFlags.visibility = true; + data.visibility = visibility; + }, + }, + points: { + /** + * @name points + * @type {number[]} + * @memberof module:API.cvat.classes.ObjectState + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + */ + get: () => data.points, + set: (points) => { + if (Array.isArray(points)) { + data.updateFlags.points = true; + data.points = [...points]; + } else { + throw new ArgumentError( + 'Points are expected to be an array ' + + `but got ${typeof (points) === 'object' + ? points.constructor.name : typeof (points)}`, + ); + } + }, + }, + group: { + /** + * @name group + * @type {integer} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.group, + set: (group) => { + data.updateFlags.group = true; + data.group = group; + }, + }, + zOrder: { + /** + * @name zOrder + * @type {integer} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.zOrder, + set: (zOrder) => { + data.updateFlags.zOrder = true; + data.zOrder = zOrder; + }, + }, + outside: { + /** + * @name outside + * @type {boolean} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.outside, + set: (outside) => { + data.updateFlags.outside = true; + data.outside = outside; + }, + }, + keyframe: { + /** + * @name keyframe + * @type {boolean} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.keyframe, + set: (keyframe) => { + data.updateFlags.keyframe = true; + data.keyframe = keyframe; + }, + }, + occluded: { + /** + * @name occluded + * @type {boolean} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.occluded, + set: (occluded) => { + data.updateFlags.occluded = true; + data.occluded = occluded; + }, + }, + lock: { + /** + * @name lock + * @type {boolean} + * @memberof module:API.cvat.classes.ObjectState + * @instance + */ + get: () => data.lock, + set: (lock) => { + data.updateFlags.lock = true; + data.lock = lock; + }, + }, + attributes: { + /** + * Object is id:value pairs where "id" is an integer + * attribute identifier and "value" is an attribute value + * @name attributes + * @type {Object} + * @memberof module:API.cvat.classes.ObjectState + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + */ + get: () => data.attributes, + set: (attributes) => { + if (typeof (attributes) !== 'object') { + throw new ArgumentError( + 'Attributes are expected to be an object ' + + `but got ${typeof (attributes) === 'object' + ? attributes.constructor.name : typeof (attributes)}`, + ); + } + + for (const attrID of Object.keys(attributes)) { + data.updateFlags.attributes = true; + data.attributes[attrID] = attributes[attrID]; + } + }, + }, + })); + + this.label = serialized.label; + this.group = serialized.group; + this.zOrder = serialized.zOrder; + this.outside = serialized.outside; + this.keyframe = serialized.keyframe; + this.occluded = serialized.occluded; + this.color = serialized.color; + this.lock = serialized.lock; + this.visibility = serialized.visibility; + + // It can be undefined in a constructor and it can be defined later + if (typeof (serialized.points) !== 'undefined') { + this.points = serialized.points; + } + if (typeof (serialized.attributes) !== 'undefined') { + this.attributes = serialized.attributes; + } + + data.updateFlags.reset(); + } + + /** + * Method saves/updates an object state in a collection + * @method save + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @returns {module:API.cvat.classes.ObjectState} updated state of an object + */ + async save() { + const result = await PluginRegistry + .apiWrapper.call(this, ObjectState.prototype.save); + return result; + } + + /** + * Method deletes an object from a collection + * @method delete + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + * @param {boolean} [force=false] delete object even if it is locked + * @async + * @returns {boolean} true if object has been deleted + * @throws {module:API.cvat.exceptions.PluginError} + */ + async delete(force = false) { + const result = await PluginRegistry + .apiWrapper.call(this, ObjectState.prototype.delete, force); + return result; + } + + /** + * Set the highest ZOrder within a frame + * @method up + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + */ + async up() { + const result = await PluginRegistry + .apiWrapper.call(this, ObjectState.prototype.up); + return result; + } + + /** + * Set the lowest ZOrder within a frame + * @method down + * @memberof module:API.cvat.classes.ObjectState + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + */ + async down() { + const result = await PluginRegistry + .apiWrapper.call(this, ObjectState.prototype.down); + return result; + } + } + + // Updates element in collection which contains it + ObjectState.prototype.save.implementation = async function () { + if (this.hidden && this.hidden.save) { + return this.hidden.save(); + } + + return this; + }; + + // Delete element from a collection which contains it + ObjectState.prototype.delete.implementation = async function (force) { + if (this.hidden && this.hidden.delete) { + return this.hidden.delete(force); + } + + return false; + }; + + ObjectState.prototype.up.implementation = async function () { + if (this.hidden && this.hidden.up) { + return this.hidden.up(); + } + + return false; + }; + + ObjectState.prototype.down.implementation = async function () { + if (this.hidden && this.hidden.down) { + return this.hidden.down(); + } + + return false; + }; + + + module.exports = ObjectState; +})(); diff --git a/cvat-core/src/plugins.js b/cvat-core/src/plugins.js new file mode 100644 index 00000000..595e6cbc --- /dev/null +++ b/cvat-core/src/plugins.js @@ -0,0 +1,113 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const { PluginError } = require('./exceptions'); + + const plugins = []; + class PluginRegistry { + static async apiWrapper(wrappedFunc, ...args) { + // I have to optimize the wrapper + const pluginList = await PluginRegistry.list(); + for (const plugin of pluginList) { + const pluginDecorators = plugin.functions + .filter(obj => obj.callback === wrappedFunc)[0]; + if (pluginDecorators && pluginDecorators.enter) { + try { + await pluginDecorators.enter.call(this, plugin, ...args); + } catch (exception) { + if (exception instanceof PluginError) { + throw exception; + } else { + throw new PluginError(`Exception in plugin ${plugin.name}: ${exception.toString()}`); + } + } + } + } + + let result = await wrappedFunc.implementation.call(this, ...args); + + for (const plugin of pluginList) { + const pluginDecorators = plugin.functions + .filter(obj => obj.callback === wrappedFunc)[0]; + if (pluginDecorators && pluginDecorators.leave) { + try { + result = await pluginDecorators.leave.call(this, plugin, result, ...args); + } catch (exception) { + if (exception instanceof PluginError) { + throw exception; + } else { + throw new PluginError(`Exception in plugin ${plugin.name}: ${exception.toString()}`); + } + } + } + } + + return result; + } + + // Called with cvat context + static async register(plug) { + const functions = []; + + if (typeof (plug) !== 'object') { + throw new PluginError(`Plugin should be an object, but got "${typeof (plug)}"`); + } + + if (!('name' in plug) || typeof (plug.name) !== 'string') { + throw new PluginError('Plugin must contain a "name" field and it must be a string'); + } + + if (!('description' in plug) || typeof (plug.description) !== 'string') { + throw new PluginError('Plugin must contain a "description" field and it must be a string'); + } + + if ('functions' in plug) { + throw new PluginError('Plugin must not contain a "functions" field'); + } + + (function traverse(plugin, api) { + const decorator = {}; + for (const key in plugin) { + if (Object.prototype.hasOwnProperty.call(plugin, key)) { + if (typeof (plugin[key]) === 'object') { + if (Object.prototype.hasOwnProperty.call(api, key)) { + traverse(plugin[key], api[key]); + } + } else if (['enter', 'leave'].includes(key) + && typeof (api) === 'function' + && typeof (plugin[key] === 'function')) { + decorator.callback = api; + decorator[key] = plugin[key]; + } + } + } + + if (Object.keys(decorator).length) { + functions.push(decorator); + } + }(plug, { + cvat: this, + })); + + Object.defineProperty(plug, 'functions', { + value: functions, + writable: false, + }); + + plugins.push(plug); + } + + static async list() { + return plugins; + } + } + + module.exports = PluginRegistry; +})(); diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js new file mode 100644 index 00000000..9b5eef3f --- /dev/null +++ b/cvat-core/src/server-proxy.js @@ -0,0 +1,589 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const FormData = require('form-data'); + const { + ServerError, + } = require('./exceptions'); + const store = require('store'); + const config = require('./config'); + + function generateError(errorData, baseMessage) { + if (errorData.response) { + const message = `${baseMessage}. ` + + `${errorData.message}. ${JSON.stringify(errorData.response.data) || ''}.`; + return new ServerError(message, errorData.response.status); + } + + // Server is unavailable (no any response) + const message = `${baseMessage}. ` + + `${errorData.message}.`; // usually is "Error Network" + return new ServerError(message, 0); + } + + class ServerProxy { + constructor() { + const Axios = require('axios'); + Axios.defaults.withCredentials = true; + Axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN'; + Axios.defaults.xsrfCookieName = 'csrftoken'; + + let token = store.get('token'); + if (token) { + Axios.defaults.headers.common.Authorization = `Token ${token}`; + } + + async function about() { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/server/about`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get "about" information from the server'); + } + + return response.data; + } + + async function share(directory) { + const { backendAPI } = config; + directory = encodeURIComponent(directory); + + let response = null; + try { + response = await Axios.get(`${backendAPI}/server/share?directory=${directory}`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get "share" information from the server'); + } + + return response.data; + } + + async function exception(exceptionObject) { + const { backendAPI } = config; + + try { + await Axios.post(`${backendAPI}/server/exception`, JSON.stringify(exceptionObject), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not send an exception to the server'); + } + } + + async function formats() { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/server/annotation/formats`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get annotation formats from the server'); + } + + return response.data; + } + + async function register(username, firstName, lastName, email, password1, password2) { + let response = null; + try { + const data = JSON.stringify({ + username, + first_name: firstName, + last_name: lastName, + email, + password1, + password2, + }); + response = await Axios.post(`${config.backendAPI}/auth/register`, data, { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData, `Could not register '${username}' user on the server`); + } + + return response.data; + } + + async function login(username, password) { + const authenticationData = ([ + `${encodeURIComponent('username')}=${encodeURIComponent(username)}`, + `${encodeURIComponent('password')}=${encodeURIComponent(password)}`, + ]).join('&').replace(/%20/g, '+'); + + let authenticationResponse = null; + try { + authenticationResponse = await Axios.post( + `${config.backendAPI}/auth/login`, + authenticationData, { + proxy: config.proxy, + }, + ); + } catch (errorData) { + throw generateError(errorData, 'Could not login on a server'); + } + + if (authenticationResponse.headers['set-cookie']) { + // Browser itself setup cookie and header is none + // In NodeJS we need do it manually + const cookies = authenticationResponse.headers['set-cookie'].join(';'); + Axios.defaults.headers.common.Cookie = cookies; + } + + token = authenticationResponse.data.key; + store.set('token', token); + Axios.defaults.headers.common.Authorization = `Token ${token}`; + } + + async function logout() { + try { + await Axios.post(`${config.backendAPI}/auth/logout`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not logout from the server'); + } + + store.remove('token'); + Axios.defaults.headers.common.Authorization = ''; + } + + async function authorized() { + try { + await module.exports.users.getSelf(); + } catch (serverError) { + if (serverError.code === 401) { + return false; + } + + throw serverError; + } + + return true; + } + + async function getTasks(filter = '') { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/tasks?${filter}`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get tasks from a server'); + } + + response.data.results.count = response.data.count; + return response.data.results; + } + + async function saveTask(id, taskData) { + const { backendAPI } = config; + + try { + await Axios.patch(`${backendAPI}/tasks/${id}`, JSON.stringify(taskData), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not save the task on the server'); + } + } + + async function deleteTask(id) { + const { backendAPI } = config; + + try { + await Axios.delete(`${backendAPI}/tasks/${id}`); + } catch (errorData) { + throw generateError(errorData, 'Could not delete the task from the server'); + } + } + + async function createTask(taskData, files, onUpdate) { + const { backendAPI } = config; + + async function wait(id) { + return new Promise((resolve, reject) => { + async function checkStatus() { + try { + const response = await Axios.get(`${backendAPI}/tasks/${id}/status`); + if (['Queued', 'Started'].includes(response.data.state)) { + if (response.data.message !== '') { + onUpdate(response.data.message); + } + setTimeout(checkStatus, 1000); + } else if (response.data.state === 'Finished') { + resolve(); + } else if (response.data.state === 'Failed') { + // If request has been successful, but task hasn't been created + // Then passed data is wrong and we can pass code 400 + const message = 'Could not create the task on the server. ' + + `${response.data.message}.`; + reject(new ServerError(message, 400)); + } else { + // If server has another status, it is unexpected + // Therefore it is server error and we can pass code 500 + reject(new ServerError( + `Unknown task state has been received: ${response.data.state}`, + 500, + )); + } + } catch (errorData) { + reject( + generateError(errorData, 'Could not put task to the server'), + ); + } + } + + setTimeout(checkStatus, 1000); + }); + } + + const batchOfFiles = new FormData(); + for (const key in files) { + if (Object.prototype.hasOwnProperty.call(files, key)) { + for (let i = 0; i < files[key].length; i++) { + batchOfFiles.append(`${key}[${i}]`, files[key][i]); + } + } + } + + let response = null; + + onUpdate('The task is being created on the server..'); + try { + response = await Axios.post(`${backendAPI}/tasks`, JSON.stringify(taskData), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not put task to the server'); + } + + onUpdate('The data is being uploaded to the server..'); + try { + await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`, batchOfFiles, { + proxy: config.proxy, + }); + } catch (errorData) { + try { + await deleteTask(response.data.id); + } catch (_) { + // ignore + } + + throw generateError(errorData, 'Could not put data to the server'); + } + + try { + await wait(response.data.id); + } catch (createException) { + await deleteTask(response.data.id); + throw createException; + } + + const createdTask = await getTasks(`?id=${response.id}`); + return createdTask[0]; + } + + async function getJob(jobID) { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/jobs/${jobID}`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get jobs from a server'); + } + + return response.data; + } + + async function saveJob(id, jobData) { + const { backendAPI } = config; + + try { + await Axios.patch(`${backendAPI}/jobs/${id}`, JSON.stringify(jobData), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not save the job on the server'); + } + } + + async function getUsers() { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/users`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get users from the server'); + } + + return response.data.results; + } + + async function getSelf() { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/users/self`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError(errorData, 'Could not get user data from the server'); + } + + return response.data; + } + + async function getData(tid, frame) { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/${frame}`, { + proxy: config.proxy, + responseType: 'blob', + }); + } catch (errorData) { + throw generateError( + errorData, + `Could not get frame ${frame} for the task ${tid} from the server`, + ); + } + + return response.data; + } + + async function getMeta(tid) { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/tasks/${tid}/frames/meta`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError( + errorData, + `Could not get frame meta info for the task ${tid} from the server`, + ); + } + + return response.data; + } + + // Session is 'task' or 'job' + async function getAnnotations(session, id) { + const { backendAPI } = config; + + let response = null; + try { + response = await Axios.get(`${backendAPI}/${session}s/${id}/annotations`, { + proxy: config.proxy, + }); + } catch (errorData) { + throw generateError( + errorData, + `Could not get annotations for the ${session} ${id} from the server`, + ); + } + + return response.data; + } + + // Session is 'task' or 'job' + async function updateAnnotations(session, id, data, action) { + const { backendAPI } = config; + let requestFunc = null; + let url = null; + if (action.toUpperCase() === 'PUT') { + requestFunc = Axios.put.bind(Axios); + url = `${backendAPI}/${session}s/${id}/annotations`; + } else { + requestFunc = Axios.patch.bind(Axios); + url = `${backendAPI}/${session}s/${id}/annotations?action=${action}`; + } + + let response = null; + try { + response = await requestFunc(url, JSON.stringify(data), { + proxy: config.proxy, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (errorData) { + throw generateError( + errorData, + `Could not ${action} annotations for the ${session} ${id} on the server`, + ); + } + + return response.data; + } + + // Session is 'task' or 'job' + async function uploadAnnotations(session, id, file, format) { + const { backendAPI } = config; + + let annotationData = new FormData(); + annotationData.append('annotation_file', file); + + return new Promise((resolve, reject) => { + async function request() { + try { + const response = await Axios + .put(`${backendAPI}/${session}s/${id}/annotations?format=${format}`, annotationData, { + proxy: config.proxy, + }); + if (response.status === 202) { + annotationData = new FormData(); + setTimeout(request, 3000); + } else { + resolve(); + } + } catch (errorData) { + reject(generateError( + errorData, + `Could not upload annotations for the ${session} ${id}`, + )); + } + } + + setTimeout(request); + }); + } + + // Session is 'task' or 'job' + async function dumpAnnotations(id, name, format) { + const { backendAPI } = config; + const filename = name.replace(/\//g, '_'); + let url = `${backendAPI}/tasks/${id}/annotations/${filename}?format=${format}`; + + return new Promise((resolve, reject) => { + async function request() { + try { + const response = await Axios + .get(`${url}`, { + proxy: config.proxy, + }); + if (response.status === 202) { + setTimeout(request, 3000); + } else { + url = `${url}&action=download`; + resolve(url); + } + } catch (errorData) { + reject(generateError( + errorData, + `Could not dump annotations for the task ${id} from the server`, + )); + } + } + + setTimeout(request); + }); + } + + Object.defineProperties(this, Object.freeze({ + server: { + value: Object.freeze({ + about, + share, + formats, + exception, + login, + logout, + authorized, + register, + }), + writable: false, + }, + + tasks: { + value: Object.freeze({ + getTasks, + saveTask, + createTask, + deleteTask, + }), + writable: false, + }, + + jobs: { + value: Object.freeze({ + getJob, + saveJob, + }), + writable: false, + }, + + users: { + value: Object.freeze({ + getUsers, + getSelf, + }), + writable: false, + }, + + frames: { + value: Object.freeze({ + getData, + getMeta, + }), + writable: false, + }, + + annotations: { + value: Object.freeze({ + updateAnnotations, + getAnnotations, + dumpAnnotations, + uploadAnnotations, + }), + writable: false, + }, + })); + } + } + + const serverProxy = new ServerProxy(); + module.exports = serverProxy; +})(); diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js new file mode 100644 index 00000000..e985b5e4 --- /dev/null +++ b/cvat-core/src/session.js @@ -0,0 +1,1433 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +/* global + require:false +*/ + +(() => { + const PluginRegistry = require('./plugins'); + const serverProxy = require('./server-proxy'); + const { getFrame } = require('./frames'); + const { ArgumentError } = require('./exceptions'); + const { TaskStatus } = require('./enums'); + const { Label } = require('./labels'); + + function buildDublicatedAPI(prototype) { + Object.defineProperties(prototype, { + annotations: Object.freeze({ + value: { + async upload(file, loader) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.upload, file, loader); + return result; + }, + + async save() { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.save); + return result; + }, + + async clear(reload = false) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.clear, reload); + return result; + }, + + async dump(name, dumper) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.dump, name, dumper); + return result; + }, + + async statistics() { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.statistics); + return result; + }, + + async put(arrayOfObjects = []) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.put, arrayOfObjects); + return result; + }, + + async get(frame, filter = {}) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.get, frame, filter); + return result; + }, + + async search(filter, frameFrom, frameTo) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.search, + filter, frameFrom, frameTo); + return result; + }, + + async select(objectStates, x, y) { + const result = await PluginRegistry + .apiWrapper.call(this, + prototype.annotations.select, objectStates, x, y); + return result; + }, + + async hasUnsavedChanges() { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.hasUnsavedChanges); + return result; + }, + + async merge(objectStates) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.merge, objectStates); + return result; + }, + + async split(objectState, frame) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.split, objectState, frame); + return result; + }, + + async group(objectStates, reset = false) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.annotations.group, + objectStates, reset); + return result; + }, + }, + writable: true, + }), + frames: Object.freeze({ + value: { + async get(frame) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.frames.get, frame); + return result; + }, + }, + writable: true, + }), + logs: Object.freeze({ + value: { + async put(logType, details) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.logs.put, logType, details); + return result; + }, + async save(onUpdate) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.logs.save, onUpdate); + return result; + }, + }, + writable: true, + }), + actions: Object.freeze({ + value: { + async undo(count) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.actions.undo, count); + return result; + }, + async redo(count) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.actions.redo, count); + return result; + }, + async clear() { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.actions.clear); + return result; + }, + }, + writable: true, + }), + events: Object.freeze({ + value: { + async subscribe(evType, callback) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.events.subscribe, evType, callback); + return result; + }, + async unsubscribe(evType, callback = null) { + const result = await PluginRegistry + .apiWrapper.call(this, prototype.events.unsubscribe, evType, callback); + return result; + }, + }, + writable: true, + }), + }); + } + + /** + * Base abstract class for Task and Job. It contains common members. + * @hideconstructor + * @virtual + */ + class Session { + constructor() { + /** + * An interaction with annotations + * @namespace annotations + * @memberof Session + */ + /** + * Upload annotations from a dump file + * You need upload annotations from a server again after successful executing + * @method upload + * @memberof Session.annotations + * @param {File} annotations - a text file with annotations + * @param {module:API.cvat.classes.Loader} loader - a loader + * which will be used to upload + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + /** + * Save all changes in annotations on a server + * Objects which hadn't been saved on a server before, + * get a serverID after saving. But received object states aren't updated. + * So, after successful saving it's recommended to update them manually + * (call the annotations.get() again) + * @method save + * @memberof Session.annotations + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + * @instance + * @async + * @param {function} [onUpdate] saving can be long. + * This callback can be used to notify a user about current progress + * Its argument is a text string + */ + /** + * Remove all annotations and optionally reinitialize it + * @method clear + * @memberof Session.annotations + * @param {boolean} [reload = false] reset all changes and + * reinitialize annotations by data from a server + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @throws {module:API.cvat.exceptions.ServerError} + * @instance + * @async + */ + /** + * Dump of annotations to a file. + * Method always dumps annotations for a whole task. + * @method dump + * @memberof Session.annotations + * @param {string} name - a name of a file with annotations + * @param {module:API.cvat.classes.Dumper} dumper - a dumper + * which will be used to dump + * @returns {string} URL which can be used in order to get a dump file + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * Collect short statistics about a task or a job. + * @method statistics + * @memberof Session.annotations + * @returns {module:API.cvat.classes.Statistics} statistics object + * @throws {module:API.cvat.exceptions.PluginError} + * @instance + * @async + */ + /** + * Create new objects from one-frame states + * After successful adding you need to update object states on a frame + * @method put + * @memberof Session.annotations + * @param {module:API.cvat.classes.ObjectState[]} data + * array of objects on the specific frame + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.DataError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * @typedef {Object} ObjectFilter + * @property {string} [label] a name of a label + * @property {module:API.cvat.enums.ObjectType} [type] + * @property {module:API.cvat.enums.ObjectShape} [shape] + * @property {boolean} [occluded] a value of occluded property + * @property {boolean} [lock] a value of lock property + * @property {number} [width] a width of a shape + * @property {number} [height] a height of a shape + * @property {Object[]} [attributes] dictionary with "name: value" pairs + * @global + */ + /** + * Get annotations for a specific frame + * @method get + * @param {integer} frame get objects from the frame + * @param {ObjectFilter[]} [filter = []] + * get only objects are satisfied to specific filter + * @returns {module:API.cvat.classes.ObjectState[]} + * @memberof Session.annotations + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * Find frame which contains at least one object satisfied to a filter + * @method search + * @memberof Session.annotations + * @param {ObjectFilter} [filter = []] filter + * @param {integer} from lower bound of a search + * @param {integer} to upper bound of a search + * @returns {integer} the nearest frame which contains filtered objects + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * Select shape under a cursor by using minimal distance + * between a cursor and a shape edge or a shape point + * For closed shapes a cursor is placed inside a shape + * @method select + * @memberof Session.annotations + * @param {module:API.cvat.classes.ObjectState[]} objectStates + * objects which can be selected + * @param {float} x horizontal coordinate + * @param {float} y vertical coordinate + * @returns {Object} + * a pair of {state: ObjectState, distance: number} for selected object. + * Pair values can be null if there aren't any sutisfied objects + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * Method unites several shapes and tracks into the one + * All shapes must be the same (rectangle, polygon, etc) + * All labels must be the same + * After successful merge you need to update object states on a frame + * @method merge + * @memberof Session.annotations + * @param {module:API.cvat.classes.ObjectState[]} objectStates + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * Method splits a track into two parts + * (start frame: previous frame), (frame, last frame) + * After successful split you need to update object states on a frame + * @method split + * @memberof Session.annotations + * @param {module:API.cvat.classes.ObjectState} objectState + * @param {integer} frame + * @throws {module:API.cvat.exceptions.ArgumentError} + * @throws {module:API.cvat.exceptions.PluginError} + * @instance + * @async + */ + /** + * Method creates a new group and put all passed objects into it + * After successful split you need to update object states on a frame + * @method group + * @memberof Session.annotations + * @param {module:API.cvat.classes.ObjectState[]} objectStates + * @param {boolean} reset pass "true" to reset group value (set it to 0) + * @returns {integer} an ID of created group + * @throws {module:API.cvat.exceptions.ArgumentError} + * @throws {module:API.cvat.exceptions.PluginError} + * @instance + * @async + */ + /** + * Indicate if there are any changes in + * annotations which haven't been saved on a server + * @method hasUnsavedChanges + * @memberof Session.annotations + * @returns {boolean} + * @throws {module:API.cvat.exceptions.PluginError} + * @instance + * @async + */ + + + /** + * Namespace is used for an interaction with frames + * @namespace frames + * @memberof Session + */ + /** + * Get frame by its number + * @method get + * @memberof Session.frames + * @param {integer} frame number of frame which you want to get + * @returns {module:API.cvat.classes.FrameData} + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + + /** + * Namespace is used for an interaction with logs + * @namespace logs + * @memberof Session + */ + + /** + * Append log to a log collection. + * Continue logs will have been added after "close" method is called + * @method put + * @memberof Session.logs + * @param {module:API.cvat.enums.LogType} type a type of a log + * @param {boolean} continuous log is a continuous log + * @param {Object} details any others data which will be append to log data + * @returns {module:API.cvat.classes.Log} + * @instance + * @async + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + /** + * Save accumulated logs on a server + * @method save + * @memberof Session.logs + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ServerError} + * @instance + * @async + */ + + /** + * Namespace is used for an interaction with actions + * @namespace actions + * @memberof Session + */ + + /** + * Is a dictionary of pairs "id:action" where "id" is an identifier of an object + * which has been affected by undo/redo and "action" is what exactly has been + * done with the object. Action can be: "created", "deleted", "updated". + * Size of an output array equal the param "count". + * @typedef {Object} HistoryAction + * @global + */ + /** + * Undo actions + * @method undo + * @memberof Session.actions + * @returns {HistoryAction} + * @throws {module:API.cvat.exceptions.PluginError} + * @instance + * @async + */ + /** + * Redo actions + * @method redo + * @memberof Session.actions + * @returns {HistoryAction} + * @throws {module:API.cvat.exceptions.PluginError} + * @instance + * @async + */ + + + /** + * Namespace is used for an interaction with events + * @namespace events + * @memberof Session + */ + /** + * Subscribe on an event + * @method subscribe + * @memberof Session.events + * @param {module:API.cvat.enums.EventType} type - event type + * @param {functions} callback - function which will be called on event + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + /** + * Unsubscribe from an event. If callback is not provided, + * all callbacks will be removed from subscribers for the event + * @method unsubscribe + * @memberof Session.events + * @param {module:API.cvat.enums.EventType} type - event type + * @param {functions} [callback = null] - function which is called on event + * @throws {module:API.cvat.exceptions.PluginError} + * @throws {module:API.cvat.exceptions.ArgumentError} + * @instance + * @async + */ + } + } + + /** + * Class representing a job. + * @memberof module:API.cvat.classes + * @hideconstructor + * @extends Session + */ + class Job extends Session { + constructor(initialData) { + super(); + const data = { + id: undefined, + assignee: undefined, + status: undefined, + start_frame: undefined, + stop_frame: undefined, + task: undefined, + }; + + for (const property in data) { + if (Object.prototype.hasOwnProperty.call(data, property)) { + if (property in initialData) { + data[property] = initialData[property]; + } + + if (data[property] === undefined) { + throw new ArgumentError( + `Job field "${property}" was not initialized`, + ); + } + } + } + + Object.defineProperties(this, Object.freeze({ + /** + * @name id + * @type {integer} + * @memberof module:API.cvat.classes.Job + * @readonly + * @instance + */ + id: { + get: () => data.id, + }, + /** + * Identifier of a user who is responsible for the job + * @name assignee + * @type {integer} + * @memberof module:API.cvat.classes.Job + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + assignee: { + get: () => data.assignee, + set: () => (assignee) => { + if (!Number.isInteger(assignee) || assignee < 0) { + throw new ArgumentError( + 'Value must be a non negative integer', + ); + } + data.assignee = assignee; + }, + }, + /** + * @name status + * @type {module:API.cvat.enums.TaskStatus} + * @memberof module:API.cvat.classes.Job + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + status: { + get: () => data.status, + set: (status) => { + const type = TaskStatus; + let valueInEnum = false; + for (const value in type) { + if (type[value] === status) { + valueInEnum = true; + break; + } + } + + if (!valueInEnum) { + throw new ArgumentError( + 'Value must be a value from the enumeration cvat.enums.TaskStatus', + ); + } + + data.status = status; + }, + }, + /** + * @name startFrame + * @type {integer} + * @memberof module:API.cvat.classes.Job + * @readonly + * @instance + */ + startFrame: { + get: () => data.start_frame, + }, + /** + * @name stopFrame + * @type {integer} + * @memberof module:API.cvat.classes.Job + * @readonly + * @instance + */ + stopFrame: { + get: () => data.stop_frame, + }, + /** + * @name task + * @type {module:API.cvat.classes.Task} + * @memberof module:API.cvat.classes.Job + * @readonly + * @instance + */ + task: { + get: () => data.task, + }, + })); + + // When we call a function, for example: task.annotations.get() + // In the method get we lose the task context + // So, we need return it + this.annotations = { + get: Object.getPrototypeOf(this).annotations.get.bind(this), + put: Object.getPrototypeOf(this).annotations.put.bind(this), + save: Object.getPrototypeOf(this).annotations.save.bind(this), + dump: Object.getPrototypeOf(this).annotations.dump.bind(this), + merge: Object.getPrototypeOf(this).annotations.merge.bind(this), + split: Object.getPrototypeOf(this).annotations.split.bind(this), + group: Object.getPrototypeOf(this).annotations.group.bind(this), + clear: Object.getPrototypeOf(this).annotations.clear.bind(this), + upload: Object.getPrototypeOf(this).annotations.upload.bind(this), + select: Object.getPrototypeOf(this).annotations.select.bind(this), + statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this), + hasUnsavedChanges: Object.getPrototypeOf(this) + .annotations.hasUnsavedChanges.bind(this), + }; + + this.frames = { + get: Object.getPrototypeOf(this).frames.get.bind(this), + }; + } + + /** + * Method updates job data like status or assignee + * @method save + * @memberof module:API.cvat.classes.Job + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async save() { + const result = await PluginRegistry + .apiWrapper.call(this, Job.prototype.save); + return result; + } + } + + /** + * Class representing a task + * @memberof module:API.cvat.classes + * @extends Session + */ + class Task extends Session { + /** + * In a fact you need use the constructor only if you want to create a task + * @param {object} initialData - Object which is used for initalization + *
    It can contain keys: + *
  • name + *
  • assignee + *
  • bug_tracker + *
  • z_order + *
  • labels + *
  • segment_size + *
  • overlap + */ + constructor(initialData) { + super(); + const data = { + id: undefined, + name: undefined, + status: undefined, + size: undefined, + mode: undefined, + owner: undefined, + assignee: undefined, + created_date: undefined, + updated_date: undefined, + bug_tracker: undefined, + overlap: undefined, + segment_size: undefined, + z_order: undefined, + image_quality: undefined, + start_frame: undefined, + stop_frame: undefined, + frame_filter: undefined, + }; + + for (const property in data) { + if (Object.prototype.hasOwnProperty.call(data, property) + && property in initialData) { + data[property] = initialData[property]; + } + } + + data.labels = []; + data.jobs = []; + data.files = Object.freeze({ + server_files: [], + client_files: [], + remote_files: [], + }); + + if (Array.isArray(initialData.segments)) { + for (const segment of initialData.segments) { + if (Array.isArray(segment.jobs)) { + for (const job of segment.jobs) { + const jobInstance = new Job({ + url: job.url, + id: job.id, + assignee: job.assignee, + status: job.status, + start_frame: segment.start_frame, + stop_frame: segment.stop_frame, + task: this, + }); + data.jobs.push(jobInstance); + } + } + } + } + + if (Array.isArray(initialData.labels)) { + for (const label of initialData.labels) { + const classInstance = new Label(label); + data.labels.push(classInstance); + } + } + + Object.defineProperties(this, Object.freeze({ + /** + * @name id + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + id: { + get: () => data.id, + }, + /** + * @name name + * @type {string} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + name: { + get: () => data.name, + set: (value) => { + if (!value.trim().length) { + throw new ArgumentError( + 'Value must not be empty', + ); + } + data.name = value; + }, + }, + /** + * @name status + * @type {module:API.cvat.enums.TaskStatus} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + status: { + get: () => data.status, + }, + /** + * @name size + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + size: { + get: () => data.size, + }, + /** + * @name mode + * @type {TaskMode} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + mode: { + get: () => data.mode, + }, + /** + * Identificator of a user who has created the task + * @name owner + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + owner: { + get: () => data.owner, + }, + /** + * Identificator of a user who is responsible for the task + * @name assignee + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + assignee: { + get: () => data.assignee, + set: () => (assignee) => { + if (!Number.isInteger(assignee) || assignee < 0) { + throw new ArgumentError( + 'Value must be a non negative integer', + ); + } + data.assignee = assignee; + }, + }, + /** + * @name createdDate + * @type {string} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + createdDate: { + get: () => data.created_date, + }, + /** + * @name updatedDate + * @type {string} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + updatedDate: { + get: () => data.updated_date, + }, + /** + * @name bugTracker + * @type {string} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + bugTracker: { + get: () => data.bug_tracker, + set: (tracker) => { + data.bug_tracker = tracker; + }, + }, + /** + * @name overlap + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + overlap: { + get: () => data.overlap, + set: (overlap) => { + if (!Number.isInteger(overlap) || overlap < 0) { + throw new ArgumentError( + 'Value must be a non negative integer', + ); + } + data.overlap = overlap; + }, + }, + /** + * @name segmentSize + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + segmentSize: { + get: () => data.segment_size, + set: (segment) => { + if (!Number.isInteger(segment) || segment < 0) { + throw new ArgumentError( + 'Value must be a positive integer', + ); + } + data.segment_size = segment; + }, + }, + /** + * @name zOrder + * @type {boolean} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + zOrder: { + get: () => data.z_order, + set: (zOrder) => { + if (typeof (zOrder) !== 'boolean') { + throw new ArgumentError( + 'Value must be a boolean', + ); + } + data.z_order = zOrder; + }, + }, + /** + * @name imageQuality + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + imageQuality: { + get: () => data.image_quality, + set: (quality) => { + if (!Number.isInteger(quality) || quality < 0) { + throw new ArgumentError( + 'Value must be a positive integer', + ); + } + data.image_quality = quality; + }, + }, + /** + * After task has been created value can be appended only. + * @name labels + * @type {module:API.cvat.classes.Label[]} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + labels: { + get: () => [...data.labels], + set: (labels) => { + if (!Array.isArray(labels)) { + throw new ArgumentError( + 'Value must be an array of Labels', + ); + } + + for (const label of labels) { + if (!(label instanceof Label)) { + throw new ArgumentError( + 'Each array value must be an instance of Label. ' + + `${typeof (label)} was found`, + ); + } + } + + if (typeof (data.id) === 'undefined') { + data.labels = [...labels]; + } else { + data.labels = data.labels.concat([...labels]); + } + }, + }, + /** + * @name jobs + * @type {module:API.cvat.classes.Job[]} + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + */ + jobs: { + get: () => [...data.jobs], + }, + /** + * List of files from shared resource + * @name serverFiles + * @type {string[]} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + serverFiles: { + get: () => [...data.files.server_files], + set: (serverFiles) => { + if (!Array.isArray(serverFiles)) { + throw new ArgumentError( + `Value must be an array. But ${typeof (serverFiles)} has been got.`, + ); + } + + for (const value of serverFiles) { + if (typeof (value) !== 'string') { + throw new ArgumentError( + `Array values must be a string. But ${typeof (value)} has been got.`, + ); + } + } + + Array.prototype.push.apply(data.files.server_files, serverFiles); + }, + }, + /** + * List of files from client host + * @name clientFiles + * @type {File[]} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + clientFiles: { + get: () => [...data.files.client_files], + set: (clientFiles) => { + if (!Array.isArray(clientFiles)) { + throw new ArgumentError( + `Value must be an array. But ${typeof (clientFiles)} has been got.`, + ); + } + + for (const value of clientFiles) { + if (!(value instanceof File)) { + throw new ArgumentError( + `Array values must be a File. But ${value.constructor.name} has been got.`, + ); + } + } + + Array.prototype.push.apply(data.files.client_files, clientFiles); + }, + }, + /** + * List of files from remote host + * @name remoteFiles + * @type {File[]} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + remoteFiles: { + get: () => [...data.files.remote_files], + set: (remoteFiles) => { + if (!Array.isArray(remoteFiles)) { + throw new ArgumentError( + `Value must be an array. But ${typeof (remoteFiles)} has been got.`, + ); + } + + for (const value of remoteFiles) { + if (typeof (value) !== 'string') { + throw new ArgumentError( + `Array values must be a string. But ${typeof (value)} has been got.`, + ); + } + } + + Array.prototype.push.apply(data.files.remote_files, remoteFiles); + }, + }, + /** + * The first frame of a video to annotation + * @name startFrame + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + startFrame: { + get: () => data.start_frame, + set: (frame) => { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + 'Value must be a not negative integer', + ); + } + data.start_frame = frame; + }, + }, + /** + * The last frame of a video to annotation + * @name stopFrame + * @type {integer} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + stopFrame: { + get: () => data.stop_frame, + set: (frame) => { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + 'Value must be a not negative integer', + ); + } + data.stop_frame = frame; + }, + }, + /** + * Filter to ignore some frames during task creation + * @name frameFilter + * @type {string} + * @memberof module:API.cvat.classes.Task + * @instance + * @throws {module:API.cvat.exceptions.ArgumentError} + */ + frameFilter: { + get: () => data.frame_filter, + set: (filter) => { + if (typeof (filter) !== 'string') { + throw new ArgumentError( + `Filter value must be a string. But ${typeof (filter)} has been got.`, + ); + } + + data.frame_filter = filter; + }, + }, + })); + + // When we call a function, for example: task.annotations.get() + // In the method get we lose the task context + // So, we need return it + this.annotations = { + get: Object.getPrototypeOf(this).annotations.get.bind(this), + put: Object.getPrototypeOf(this).annotations.put.bind(this), + save: Object.getPrototypeOf(this).annotations.save.bind(this), + dump: Object.getPrototypeOf(this).annotations.dump.bind(this), + merge: Object.getPrototypeOf(this).annotations.merge.bind(this), + split: Object.getPrototypeOf(this).annotations.split.bind(this), + group: Object.getPrototypeOf(this).annotations.group.bind(this), + clear: Object.getPrototypeOf(this).annotations.clear.bind(this), + upload: Object.getPrototypeOf(this).annotations.upload.bind(this), + select: Object.getPrototypeOf(this).annotations.select.bind(this), + statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this), + hasUnsavedChanges: Object.getPrototypeOf(this) + .annotations.hasUnsavedChanges.bind(this), + }; + + this.frames = { + get: Object.getPrototypeOf(this).frames.get.bind(this), + }; + } + + /** + * Method updates data of a created task or creates new task from scratch + * @method save + * @returns {module:API.cvat.classes.Task} + * @memberof module:API.cvat.classes.Task + * @param {function} [onUpdate] - the function which is used only if task hasn't + * been created yet. It called in order to notify about creation status. + * It receives the string parameter which is a status message + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async save(onUpdate = () => {}) { + const result = await PluginRegistry + .apiWrapper.call(this, Task.prototype.save, onUpdate); + return result; + } + + /** + * Method deletes a task from a server + * @method delete + * @memberof module:API.cvat.classes.Task + * @readonly + * @instance + * @async + * @throws {module:API.cvat.exceptions.ServerError} + * @throws {module:API.cvat.exceptions.PluginError} + */ + async delete() { + const result = await PluginRegistry + .apiWrapper.call(this, Task.prototype.delete); + return result; + } + } + + module.exports = { + Job, + Task, + }; + + const { + getAnnotations, + putAnnotations, + saveAnnotations, + hasUnsavedChanges, + mergeAnnotations, + splitAnnotations, + groupAnnotations, + clearAnnotations, + selectObject, + annotationsStatistics, + uploadAnnotations, + dumpAnnotations, + } = require('./annotations'); + + buildDublicatedAPI(Job.prototype); + buildDublicatedAPI(Task.prototype); + + Job.prototype.save.implementation = async function () { + // TODO: Add ability to change an assignee + if (this.id) { + const jobData = { + status: this.status, + }; + + await serverProxy.jobs.saveJob(this.id, jobData); + return this; + } + + throw new ArgumentError( + 'Can not save job without and id', + ); + }; + + Job.prototype.frames.get.implementation = async function (frame) { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + `Frame must be a positive integer. Got: "${frame}"`, + ); + } + + if (frame < this.startFrame || frame > this.stopFrame) { + throw new ArgumentError( + `The frame with number ${frame} is out of the job`, + ); + } + + const frameData = await getFrame(this.task.id, this.task.mode, frame); + return frameData; + }; + + // TODO: Check filter for annotations + Job.prototype.annotations.get.implementation = async function (frame, filter) { + if (frame < this.startFrame || frame > this.stopFrame) { + throw new ArgumentError( + `Frame ${frame} does not exist in the job`, + ); + } + + const annotationsData = await getAnnotations(this, frame, filter); + return annotationsData; + }; + + Job.prototype.annotations.save.implementation = async function (onUpdate) { + const result = await saveAnnotations(this, onUpdate); + return result; + }; + + Job.prototype.annotations.merge.implementation = async function (objectStates) { + const result = await mergeAnnotations(this, objectStates); + return result; + }; + + Job.prototype.annotations.split.implementation = async function (objectState, frame) { + const result = await splitAnnotations(this, objectState, frame); + return result; + }; + + Job.prototype.annotations.group.implementation = async function (objectStates, reset) { + const result = await groupAnnotations(this, objectStates, reset); + return result; + }; + + Job.prototype.annotations.hasUnsavedChanges.implementation = function () { + const result = hasUnsavedChanges(this); + return result; + }; + + Job.prototype.annotations.clear.implementation = async function (reload) { + const result = await clearAnnotations(this, reload); + return result; + }; + + Job.prototype.annotations.select.implementation = function (frame, x, y) { + const result = selectObject(this, frame, x, y); + return result; + }; + + Job.prototype.annotations.statistics.implementation = function () { + const result = annotationsStatistics(this); + return result; + }; + + Job.prototype.annotations.put.implementation = function (objectStates) { + const result = putAnnotations(this, objectStates); + return result; + }; + + Job.prototype.annotations.upload.implementation = async function (file, loader) { + const result = await uploadAnnotations(this, file, loader); + return result; + }; + + Job.prototype.annotations.dump.implementation = async function (name, dumper) { + const result = await dumpAnnotations(this, name, dumper); + return result; + }; + + Task.prototype.save.implementation = async function saveTaskImplementation(onUpdate) { + // TODO: Add ability to change an owner and an assignee + if (typeof (this.id) !== 'undefined') { + // If the task has been already created, we update it + const taskData = { + name: this.name, + bug_tracker: this.bugTracker, + z_order: this.zOrder, + labels: [...this.labels.map(el => el.toJSON())], + }; + + await serverProxy.tasks.saveTask(this.id, taskData); + return this; + } + + const taskData = { + name: this.name, + labels: this.labels.map(el => el.toJSON()), + image_quality: this.imageQuality, + z_order: Boolean(this.zOrder), + }; + + if (typeof (this.bugTracker) !== 'undefined') { + taskData.bug_tracker = this.bugTracker; + } + if (typeof (this.segmentSize) !== 'undefined') { + taskData.segment_size = this.segmentSize; + } + if (typeof (this.overlap) !== 'undefined') { + taskData.overlap = this.overlap; + } + if (typeof (this.startFrame) !== 'undefined') { + taskData.start_frame = this.startFrame; + } + if (typeof (this.stopFrame) !== 'undefined') { + taskData.stop_frame = this.stopFrame; + } + if (typeof (this.frameFilter) !== 'undefined') { + taskData.frame_filter = this.frameFilter; + } + + const taskFiles = { + client_files: this.clientFiles, + server_files: this.serverFiles, + remote_files: this.remoteFiles, + }; + + const task = await serverProxy.tasks.createTask(taskData, taskFiles, onUpdate); + return new Task(task); + }; + + Task.prototype.delete.implementation = async function () { + const result = await serverProxy.tasks.deleteTask(this.id); + return result; + }; + + Task.prototype.frames.get.implementation = async function (frame) { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + `Frame must be a positive integer. Got: "${frame}"`, + ); + } + + if (frame >= this.size) { + throw new ArgumentError( + `The frame with number ${frame} is out of the task`, + ); + } + + const result = await getFrame(this.id, this.mode, frame); + return result; + }; + + // TODO: Check filter for annotations + Task.prototype.annotations.get.implementation = async function (frame, filter) { + if (!Number.isInteger(frame) || frame < 0) { + throw new ArgumentError( + `Frame must be a positive integer. Got: "${frame}"`, + ); + } + + if (frame >= this.size) { + throw new ArgumentError( + `Frame ${frame} does not exist in the task`, + ); + } + + const result = await getAnnotations(this, frame, filter); + return result; + }; + + Task.prototype.annotations.save.implementation = async function (onUpdate) { + const result = await saveAnnotations(this, onUpdate); + return result; + }; + + Task.prototype.annotations.merge.implementation = async function (objectStates) { + const result = await mergeAnnotations(this, objectStates); + return result; + }; + + Task.prototype.annotations.split.implementation = async function (objectState, frame) { + const result = await splitAnnotations(this, objectState, frame); + return result; + }; + + Task.prototype.annotations.group.implementation = async function (objectStates, reset) { + const result = await groupAnnotations(this, objectStates, reset); + return result; + }; + + Task.prototype.annotations.hasUnsavedChanges.implementation = function () { + const result = hasUnsavedChanges(this); + return result; + }; + + Task.prototype.annotations.clear.implementation = async function (reload) { + const result = await clearAnnotations(this, reload); + return result; + }; + + Task.prototype.annotations.select.implementation = function (frame, x, y) { + const result = selectObject(this, frame, x, y); + return result; + }; + + Task.prototype.annotations.statistics.implementation = function () { + const result = annotationsStatistics(this); + return result; + }; + + Task.prototype.annotations.put.implementation = function (objectStates) { + const result = putAnnotations(this, objectStates); + return result; + }; + + Task.prototype.annotations.upload.implementation = async function (file, loader) { + const result = await uploadAnnotations(this, file, loader); + return result; + }; + + Task.prototype.annotations.dump.implementation = async function (name, dumper) { + const result = await dumpAnnotations(this, name, dumper); + return result; + }; +})(); diff --git a/cvat-core/src/statistics.js b/cvat-core/src/statistics.js new file mode 100644 index 00000000..36910eb7 --- /dev/null +++ b/cvat-core/src/statistics.js @@ -0,0 +1,91 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + + +(() => { + /** + * Class representing collection statistics + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class Statistics { + constructor(label, total) { + Object.defineProperties(this, Object.freeze({ + /** + * Statistics by labels with a structure: + * @example + * { + * label: { + * boxes: { + * tracks: 10, + * shapes: 11, + * }, + * polygons: { + * tracks: 13, + * shapes: 14, + * }, + * polylines: { + * tracks: 16, + * shapes: 17, + * }, + * points: { + * tracks: 19, + * shapes: 20, + * }, + * tags: 66, + * manually: 186, + * interpolated: 500, + * total: 608, + * } + * } + * @name label + * @type {Object} + * @memberof module:API.cvat.classes.Statistics + * @readonly + * @instance + */ + label: { + get: () => JSON.parse(JSON.stringify(label)), + }, + /** + * Total statistics (covers all labels) with a structure: + * @example + * { + * boxes: { + * tracks: 10, + * shapes: 11, + * }, + * polygons: { + * tracks: 13, + * shapes: 14, + * }, + * polylines: { + * tracks: 16, + * shapes: 17, + * }, + * points: { + * tracks: 19, + * shapes: 20, + * }, + * tags: 66, + * manually: 186, + * interpolated: 500, + * total: 608, + * } + * @name total + * @type {Object} + * @memberof module:API.cvat.classes.Statistics + * @readonly + * @instance + */ + total: { + get: () => JSON.parse(JSON.stringify(total)), + }, + })); + } + } + + module.exports = Statistics; +})(); diff --git a/cvat-core/src/user.js b/cvat-core/src/user.js new file mode 100644 index 00000000..62221c67 --- /dev/null +++ b/cvat-core/src/user.js @@ -0,0 +1,151 @@ +/* +* Copyright (C) 2019 Intel Corporation +* SPDX-License-Identifier: MIT +*/ + +(() => { + /** + * Class representing a user + * @memberof module:API.cvat.classes + * @hideconstructor + */ + class User { + constructor(initialData) { + const data = { + id: null, + username: null, + email: null, + first_name: null, + last_name: null, + groups: null, + last_login: null, + date_joined: null, + is_staff: null, + is_superuser: null, + is_active: null, + }; + + for (const property in data) { + if (Object.prototype.hasOwnProperty.call(data, property) + && property in initialData) { + data[property] = initialData[property]; + } + } + + Object.defineProperties(this, Object.freeze({ + id: { + /** + * @name id + * @type {integer} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.id, + }, + username: { + /** + * @name username + * @type {string} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.username, + }, + email: { + /** + * @name email + * @type {string} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.email, + }, + firstName: { + /** + * @name firstName + * @type {string} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.first_name, + }, + lastName: { + /** + * @name lastName + * @type {string} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.last_name, + }, + groups: { + /** + * @name groups + * @type {string[]} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => JSON.parse(JSON.stringify(data.groups)), + }, + lastLogin: { + /** + * @name lastLogin + * @type {string} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.last_login, + }, + dateJoined: { + /** + * @name dateJoined + * @type {string} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.date_joined, + }, + isStaff: { + /** + * @name isStaff + * @type {boolean} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.is_staff, + }, + isSuperuser: { + /** + * @name isSuperuser + * @type {boolean} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.is_superuser, + }, + isActive: { + /** + * @name isActive + * @type {boolean} + * @memberof module:API.cvat.classes.User + * @readonly + * @instance + */ + get: () => data.is_active, + }, + })); + } + } + + module.exports = User; +})(); diff --git a/cvat-core/tests/api/annotations.js b/cvat-core/tests/api/annotations.js new file mode 100644 index 00000000..71a70f46 --- /dev/null +++ b/cvat-core/tests/api/annotations.js @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); +const serverProxy = require('../../src/server-proxy'); + +// Test cases +describe('Feature: get annotations', () => { + test('get annotations from a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + expect(Array.isArray(annotations)).toBeTruthy(); + expect(annotations).toHaveLength(11); + for (const state of annotations) { + expect(state).toBeInstanceOf(window.cvat.classes.ObjectState); + } + }); + + test('get annotations from a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 101 }))[0]; + const annotations0 = await job.annotations.get(0); + const annotations10 = await job.annotations.get(10); + expect(Array.isArray(annotations0)).toBeTruthy(); + expect(Array.isArray(annotations10)).toBeTruthy(); + expect(annotations0).toHaveLength(1); + expect(annotations10).toHaveLength(2); + for (const state of annotations0.concat(annotations10)) { + expect(state).toBeInstanceOf(window.cvat.classes.ObjectState); + } + }); + + test('get annotations for frame out of task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + + // Out of task + expect(task.annotations.get(500)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + // Out of task + expect(task.annotations.get(-1)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get annotations for frame out of job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 101 }))[0]; + + // Out of segment + expect(job.annotations.get(500)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + // Out of segment + expect(job.annotations.get(-1)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + // TODO: Test filter (hasn't been implemented yet) +}); + + +describe('Feature: put annotations', () => { + test('put a shape to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + let annotations = await task.annotations.get(1); + const { length } = annotations; + + const state = new window.cvat.classes.ObjectState({ + frame: 1, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + await task.annotations.put([state]); + annotations = await task.annotations.get(1); + expect(annotations).toHaveLength(length + 1); + }); + + test('put a shape to a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + let annotations = await job.annotations.get(5); + const { length } = annotations; + + const state = new window.cvat.classes.ObjectState({ + frame: 5, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.RECTANGLE, + points: [0, 0, 100, 100], + occluded: false, + label: job.task.labels[0], + }); + + await job.annotations.put([state]); + annotations = await job.annotations.get(5); + expect(annotations).toHaveLength(length + 1); + }); + + test('put a track to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + let annotations = await task.annotations.get(1); + const { length } = annotations; + + const state = new window.cvat.classes.ObjectState({ + frame: 1, + objectType: window.cvat.enums.ObjectType.TRACK, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + await task.annotations.put([state]); + annotations = await task.annotations.get(1); + expect(annotations).toHaveLength(length + 1); + }); + + test('put a track to a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + let annotations = await job.annotations.get(5); + const { length } = annotations; + + const state = new window.cvat.classes.ObjectState({ + frame: 5, + objectType: window.cvat.enums.ObjectType.TRACK, + shapeType: window.cvat.enums.ObjectShape.RECTANGLE, + points: [0, 0, 100, 100], + occluded: false, + label: job.task.labels[0], + }); + + await job.annotations.put([state]); + annotations = await job.annotations.get(5); + expect(annotations).toHaveLength(length + 1); + }); + + test('put object without objectType to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + await task.annotations.clear(true); + const state = new window.cvat.classes.ObjectState({ + frame: 1, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('put shape with bad attributes to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + await task.annotations.clear(true); + const state = new window.cvat.classes.ObjectState({ + frame: 1, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + attributes: { 'bad key': 55 }, + occluded: true, + label: task.labels[0], + }); + + expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('put shape without points and with invalud points to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + await task.annotations.clear(true); + const state = new window.cvat.classes.ObjectState({ + frame: 1, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + occluded: true, + points: [], + label: task.labels[0], + }); + + await expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.DataError); + + delete state.points; + await expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.DataError); + + state.points = ['150,50 250,30']; + expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('put shape without type to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + await task.annotations.clear(true); + const state = new window.cvat.classes.ObjectState({ + frame: 1, + objectType: window.cvat.enums.ObjectType.SHAPE, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('put shape without label and with bad label to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + await task.annotations.clear(true); + const state = new window.cvat.classes.ObjectState({ + frame: 1, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + }); + + await expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.label = 'bad label'; + await expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.label = {}; + await expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('put shape with bad frame to a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + await task.annotations.clear(true); + const state = new window.cvat.classes.ObjectState({ + frame: '5', + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + expect(task.annotations.put([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: check unsaved changes', () => { + test('check unsaved changes in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + const annotations = await task.annotations.get(0); + + annotations[0].keyframe = true; + await annotations[0].save(); + + expect(await task.annotations.hasUnsavedChanges()).toBe(true); + }); + + test('check unsaved changes in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + const annotations = await job.annotations.get(0); + + annotations[0].occluded = true; + await annotations[0].save(); + + expect(await job.annotations.hasUnsavedChanges()).toBe(true); + }); +}); + +describe('Feature: save annotations', () => { + test('create & save annotations for a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + let annotations = await task.annotations.get(0); + const { length } = annotations; + const state = new window.cvat.classes.ObjectState({ + frame: 0, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + await task.annotations.put([state]); + expect(await task.annotations.hasUnsavedChanges()).toBe(true); + await task.annotations.save(); + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + annotations = await task.annotations.get(0); + expect(annotations).toHaveLength(length + 1); + }); + + test('update & save annotations for a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + annotations[0].occluded = true; + await annotations[0].save(); + expect(await task.annotations.hasUnsavedChanges()).toBe(true); + await task.annotations.save(); + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + }); + + test('delete & save annotations for a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + await annotations[0].delete(); + expect(await task.annotations.hasUnsavedChanges()).toBe(true); + await task.annotations.save(); + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + }); + + test('create & save annotations for a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + let annotations = await job.annotations.get(0); + const { length } = annotations; + const state = new window.cvat.classes.ObjectState({ + frame: 0, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: job.task.labels[0], + }); + + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + await job.annotations.put([state]); + expect(await job.annotations.hasUnsavedChanges()).toBe(true); + await job.annotations.save(); + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + annotations = await job.annotations.get(0); + expect(annotations).toHaveLength(length + 1); + }); + + test('update & save annotations for a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + const annotations = await job.annotations.get(0); + + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + annotations[0].points = [0, 100, 200, 300]; + await annotations[0].save(); + expect(await job.annotations.hasUnsavedChanges()).toBe(true); + await job.annotations.save(); + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + }); + + test('delete & save annotations for a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + const annotations = await job.annotations.get(0); + + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + await annotations[0].delete(); + expect(await job.annotations.hasUnsavedChanges()).toBe(true); + await job.annotations.save(); + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + }); + + test('delete & save annotations for a job when there are a track and a shape with the same id', async () => { + const job = (await window.cvat.jobs.get({ jobID: 112 }))[0]; + const annotations = await job.annotations.get(0); + let okay = false; + + // Temporary override this method because we need to know what data + // have been sent to a server + const oldImplementation = serverProxy.annotations.updateAnnotations; + serverProxy.annotations.updateAnnotations = async (session, id, data, action) => { + const result = await oldImplementation + .call(serverProxy.annotations, session, id, data, action); + if (action === 'delete') { + okay = okay || (action === 'delete' && !!(data.shapes.length || data.tracks.length)); + } + return result; + }; + + await annotations[0].delete(); + await job.annotations.save(); + + serverProxy.annotations.updateAnnotations = oldImplementation; + expect(okay).toBe(true); + }); +}); + +describe('Feature: merge annotations', () => { + test('merge annotations in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations0 = await task.annotations.get(0); + const annotations1 = await task.annotations.get(1); + const states = [annotations0[0], annotations1[0]]; + await task.annotations.merge(states); + const merged0 = (await task.annotations.get(0)) + .filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK); + const merged1 = (await task.annotations.get(1)) + .filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK); + expect(merged0).toHaveLength(1); + expect(merged1).toHaveLength(1); + + expect(merged0[0].points).toEqual(states[0].points); + expect(merged1[0].points).toEqual(states[1].points); + }); + + test('merge annotations in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + const annotations0 = await job.annotations.get(0); + const annotations1 = await job.annotations.get(1); + const states = [annotations0[0], annotations1[0]]; + await job.annotations.merge(states); + const merged0 = (await job.annotations.get(0)) + .filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK); + const merged1 = (await job.annotations.get(1)) + .filter((state) => state.objectType === window.cvat.enums.ObjectType.TRACK); + expect(merged0).toHaveLength(1); + expect(merged1).toHaveLength(1); + + expect(merged0[0].points).toEqual(states[0].points); + expect(merged1[0].points).toEqual(states[1].points); + }); + + test('trying to merge not object state', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations0 = await task.annotations.get(0); + const states = [annotations0[0], {}]; + + expect(task.annotations.merge(states)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to merge object state which is not saved in a collection', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations0 = await task.annotations.get(0); + + const state = new window.cvat.classes.ObjectState({ + frame: 0, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + const states = [annotations0[0], state]; + + expect(task.annotations.merge(states)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to merge with bad label', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations0 = await task.annotations.get(0); + const annotations1 = await task.annotations.get(1); + const states = [annotations0[0], annotations1[0]]; + states[0].label = new window.cvat.classes.Label({ + id: 500, + name: 'new_label', + attributes: [], + }); + + expect(task.annotations.merge(states)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to merge with different shape types', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations0 = await task.annotations.get(0); + const annotations1 = (await task.annotations.get(1)) + .filter((state) => state.shapeType === window.cvat.enums.ObjectShape.POLYGON); + const states = [annotations0[0], annotations1[0]]; + + expect(task.annotations.merge(states)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to merge with different labels', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations0 = await task.annotations.get(0); + const annotations1 = await task.annotations.get(1); + const states = [annotations0[0], annotations1[0]]; + states[1].label = new window.cvat.classes.Label({ + id: 500, + name: 'new_label', + attributes: [], + }); + + expect(task.annotations.merge(states)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: split annotations', () => { + test('split annotations in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations4 = await task.annotations.get(4); + const annotations5 = await task.annotations.get(5); + + expect(annotations4[0].clientID).toBe(annotations5[0].clientID); + await task.annotations.split(annotations5[0], 5); + const splitted4 = await task.annotations.get(4); + const splitted5 = (await task.annotations.get(5)).filter((state) => !state.outside); + expect(splitted4[0].clientID).not.toBe(splitted5[0].clientID); + }); + + test('split annotations in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 101 }))[0]; + const annotations4 = await job.annotations.get(4); + const annotations5 = await job.annotations.get(5); + + expect(annotations4[0].clientID).toBe(annotations5[0].clientID); + await job.annotations.split(annotations5[0], 5); + const splitted4 = await job.annotations.get(4); + const splitted5 = (await job.annotations.get(5)).filter((state) => !state.outside); + expect(splitted4[0].clientID).not.toBe(splitted5[0].clientID); + }); + + test('split on a bad frame', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations4 = await task.annotations.get(4); + const annotations5 = await task.annotations.get(5); + + expect(annotations4[0].clientID).toBe(annotations5[0].clientID); + expect(task.annotations.split(annotations5[0], 'bad frame')) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: group annotations', () => { + test('group annotations in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + let annotations = await task.annotations.get(0); + const groupID = await task.annotations.group(annotations); + expect(typeof (groupID)).toBe('number'); + annotations = await task.annotations.get(0); + for (const state of annotations) { + expect(state.group).toBe(groupID); + } + }); + + test('group annotations in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + let annotations = await job.annotations.get(0); + const groupID = await job.annotations.group(annotations); + expect(typeof (groupID)).toBe('number'); + annotations = await job.annotations.get(0); + for (const state of annotations) { + expect(state.group).toBe(groupID); + } + }); + + test('trying to group object state which has not been saved in a collection', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + await task.annotations.clear(true); + + const state = new window.cvat.classes.ObjectState({ + frame: 0, + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.POLYGON, + points: [0, 0, 100, 0, 100, 50], + occluded: true, + label: task.labels[0], + }); + + expect(task.annotations.group([state])) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to group not object state', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + expect(task.annotations.group(annotations.concat({}))) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: clear annotations', () => { + test('clear annotations in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + let annotations = await task.annotations.get(0); + expect(annotations.length).not.toBe(0); + await task.annotations.clear(); + annotations = await task.annotations.get(0); + expect(annotations.length).toBe(0); + }); + + test('clear annotations in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + let annotations = await job.annotations.get(0); + expect(annotations.length).not.toBe(0); + await job.annotations.clear(); + annotations = await job.annotations.get(0); + expect(annotations.length).toBe(0); + }); + + test('clear annotations with reload in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + let annotations = await task.annotations.get(0); + expect(annotations.length).not.toBe(0); + annotations[0].occluded = true; + await annotations[0].save(); + expect(await task.annotations.hasUnsavedChanges()).toBe(true); + await task.annotations.clear(true); + annotations = await task.annotations.get(0); + expect(annotations.length).not.toBe(0); + expect(await task.annotations.hasUnsavedChanges()).toBe(false); + }); + + test('clear annotations with reload in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + let annotations = await job.annotations.get(0); + expect(annotations.length).not.toBe(0); + annotations[0].occluded = true; + await annotations[0].save(); + expect(await job.annotations.hasUnsavedChanges()).toBe(true); + await job.annotations.clear(true); + annotations = await job.annotations.get(0); + expect(annotations.length).not.toBe(0); + expect(await job.annotations.hasUnsavedChanges()).toBe(false); + }); + + test('clear annotations with bad reload parameter', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + await task.annotations.clear(true); + expect(task.annotations.clear('reload')) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: get statistics', () => { + test('get statistics from a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + await task.annotations.clear(true); + const statistics = await task.annotations.statistics(); + expect(statistics).toBeInstanceOf(window.cvat.classes.Statistics); + expect(statistics.total.total).toBe(29); + }); + + test('get statistics from a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 101 }))[0]; + await job.annotations.clear(true); + const statistics = await job.annotations.statistics(); + expect(statistics).toBeInstanceOf(window.cvat.classes.Statistics); + expect(statistics.total.total).toBe(512); + }); +}); + +describe('Feature: select object', () => { + test('select object in a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + let result = await task.annotations.select(annotations, 1430, 765); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE); + result = await task.annotations.select(annotations, 1415, 765); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.POLYGON); + expect(result.state.points.length).toBe(10); + result = await task.annotations.select(annotations, 1083, 543); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.POINTS); + expect(result.state.points.length).toBe(16); + result = await task.annotations.select(annotations, 613, 811); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.POLYGON); + expect(result.state.points.length).toBe(94); + }); + + test('select object in a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + const annotations = await job.annotations.get(0); + let result = await job.annotations.select(annotations, 490, 540); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE); + result = await job.annotations.select(annotations, 430, 260); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.POLYLINE); + result = await job.annotations.select(annotations, 1473, 250); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE); + result = await job.annotations.select(annotations, 1490, 237); + expect(result.state.shapeType).toBe(window.cvat.enums.ObjectShape.POLYGON); + expect(result.state.points.length).toBe(94); + }); + + test('trying to select from not object states', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + expect(task.annotations.select(annotations.concat({}), 500, 500)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to select with invalid coordinates', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + expect(task.annotations.select(annotations, null, null)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + expect(task.annotations.select(annotations, null, null)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + expect(task.annotations.select(annotations, '5', '10')) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); diff --git a/cvat-core/tests/api/frames.js b/cvat-core/tests/api/frames.js new file mode 100644 index 00000000..2ecb51c9 --- /dev/null +++ b/cvat-core/tests/api/frames.js @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); + +const { FrameData } = require('../../src/frames'); + +describe('Feature: get frame meta', () => { + test('get meta for a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const frame = await task.frames.get(0); + expect(frame).toBeInstanceOf(FrameData); + }); + + test('get meta for a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + const frame = await job.frames.get(0); + expect(frame).toBeInstanceOf(FrameData); + }); + + test('pass frame number out of a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + expect(task.frames.get(100)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + expect(task.frames.get(-1)) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('pass bad frame number', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + expect(task.frames.get('5')) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('do not pass any frame number', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + expect(task.frames.get()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: get frame data', () => { + test('get frame data for a task', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const frame = await task.frames.get(0); + const frameData = await frame.data(); + expect(typeof (frameData)).toBe('string'); + }); + + test('get frame data for a job', async () => { + const job = (await window.cvat.jobs.get({ jobID: 100 }))[0]; + const frame = await job.frames.get(0); + const frameData = await frame.data(); + expect(typeof (frameData)).toBe('string'); + }); +}); diff --git a/cvat-core/tests/api/jobs.js b/cvat-core/tests/api/jobs.js new file mode 100644 index 00000000..2a68c415 --- /dev/null +++ b/cvat-core/tests/api/jobs.js @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); + +const { Job } = require('../../src/session'); + + +// Test cases +describe('Feature: get a list of jobs', () => { + test('get jobs by a task id', async () => { + const result = await window.cvat.jobs.get({ + taskID: 3, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(2); + for (const el of result) { + expect(el).toBeInstanceOf(Job); + } + + expect(result[0].task.id).toBe(3); + expect(result[0].task).toBe(result[1].task); + }); + + test('get jobs by an unknown task id', async () => { + const result = await window.cvat.jobs.get({ + taskID: 50, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(0); + }); + + + test('get jobs by a job id', async () => { + const result = await window.cvat.jobs.get({ + jobID: 1, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(1); + expect(result[0].id).toBe(1); + }); + + test('get jobs by an unknown job id', async () => { + const result = await window.cvat.jobs.get({ + taskID: 50, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(0); + }); + + test('get jobs by invalid filter with both taskID and jobID', async () => { + expect(window.cvat.jobs.get({ + taskID: 1, + jobID: 1, + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get jobs by invalid job id', async () => { + expect(window.cvat.jobs.get({ + jobID: '1', + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get jobs by invalid task id', async () => { + expect(window.cvat.jobs.get({ + taskID: '1', + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get jobs by unknown filter', async () => { + expect(window.cvat.jobs.get({ + unknown: 50, + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + + +describe('Feature: save job', () => { + test('save status of a job', async () => { + let result = await window.cvat.jobs.get({ + jobID: 1, + }); + + result[0].status = 'validation'; + await result[0].save(); + + result = await window.cvat.jobs.get({ + jobID: 1, + }); + expect(result[0].status).toBe('validation'); + }); + + test('save invalid status of a job', async () => { + const result = await window.cvat.jobs.get({ + jobID: 1, + }); + + await result[0].save(); + expect(() => { + result[0].status = 'invalid'; + }).toThrow(window.cvat.exceptions.ArgumentError); + }); +}); diff --git a/cvat-core/tests/api/object-state.js b/cvat-core/tests/api/object-state.js new file mode 100644 index 00000000..96988b3e --- /dev/null +++ b/cvat-core/tests/api/object-state.js @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); + +describe('Feature: set attributes for an object state', () => { + test('set a valid value', () => { + const state = new window.cvat.classes.ObjectState({ + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.RECTANGLE, + frame: 5, + }); + + const attributes = { + 5: 'man', + 6: 'glasses', + }; + + state.attributes = attributes; + expect(state.attributes).toEqual(attributes); + }); + + test('trying to set a bad value', () => { + const state = new window.cvat.classes.ObjectState({ + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.RECTANGLE, + frame: 5, + }); + + let attributes = 'bad attribute'; + expect(() => { + state.attributes = attributes; + }).toThrow(window.cvat.exceptions.ArgumentError); + + attributes = 5; + expect(() => { + state.attributes = attributes; + }).toThrow(window.cvat.exceptions.ArgumentError); + + attributes = false; + expect(() => { + state.attributes = attributes; + }).toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: set points for an object state', () => { + test('set a valid value', () => { + const state = new window.cvat.classes.ObjectState({ + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.RECTANGLE, + frame: 5, + }); + + const points = [1, 2, 3, 4]; + state.points = points; + expect(state.points).toEqual(points); + }); + + test('trying to set a bad value', () => { + const state = new window.cvat.classes.ObjectState({ + objectType: window.cvat.enums.ObjectType.SHAPE, + shapeType: window.cvat.enums.ObjectShape.RECTANGLE, + frame: 5, + }); + + let points = 'bad points'; + expect(() => { + state.points = points; + }).toThrow(window.cvat.exceptions.ArgumentError); + + points = 5; + expect(() => { + state.points = points; + }).toThrow(window.cvat.exceptions.ArgumentError); + + points = false; + expect(() => { + state.points = points; + }).toThrow(window.cvat.exceptions.ArgumentError); + + points = {}; + expect(() => { + state.points = points; + }).toThrow(window.cvat.exceptions.ArgumentError); + }); +}); + +describe('Feature: save object from its state', () => { + test('save valid values for a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + let state = annotations[0]; + expect(state.objectType).toBe(window.cvat.enums.ObjectType.SHAPE); + expect(state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE); + state.points = [0, 0, 100, 100]; + state.occluded = true; + [, state.label] = task.labels; + state.lock = true; + state = await state.save(); + expect(state).toBeInstanceOf(window.cvat.classes.ObjectState); + expect(state.label.id).toBe(task.labels[1].id); + expect(state.lock).toBe(true); + expect(state.occluded).toBe(true); + expect(state.points).toEqual([0, 0, 100, 100]); + }); + + test('save valid values for a track', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(10); + let state = annotations[1]; + expect(state.objectType).toBe(window.cvat.enums.ObjectType.TRACK); + expect(state.shapeType).toBe(window.cvat.enums.ObjectShape.RECTANGLE); + + state.occluded = true; + state.lock = true; + state.points = [100, 200, 200, 400]; + state.attributes = { + 1: 'sitting', + 3: 'female', + 2: '10', + 4: 'true', + }; + + state = await state.save(); + expect(state).toBeInstanceOf(window.cvat.classes.ObjectState); + expect(state.lock).toBe(true); + expect(state.occluded).toBe(true); + expect(state.points).toEqual([100, 200, 200, 400]); + expect(state.attributes[1]).toBe('sitting'); + expect(state.attributes[2]).toBe('10'); + expect(state.attributes[3]).toBe('female'); + expect(state.attributes[4]).toBe('true'); + + state.lock = false; + [state.label] = task.labels; + state = await state.save(); + expect(state.label.id).toBe(task.labels[0].id); + + state.outside = true; + state = await state.save(); + expect(state.lock).toBe(false); + expect(state.outside).toBe(true); + + state.keyframe = false; + state = await state.save(); + expect(state.keyframe).toBe(false); + }); + + test('save bad values for a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + const state = annotations[0]; + + state.occluded = 'false'; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + const oldPoints = state.points; + state.occluded = false; + state.points = ['100', '50', '100', {}]; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.points = oldPoints; + state.lock = 'true'; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + const oldLabel = state.label; + state.lock = false; + state.label = 1; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.label = oldLabel; + state.attributes = { 1: {}, 2: false, 3: () => {} }; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('save bad values for a track', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + const state = annotations[0]; + + state.occluded = 'false'; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + const oldPoints = state.points; + state.occluded = false; + state.points = ['100', '50', '100', {}]; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.points = oldPoints; + state.lock = 'true'; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + const oldLabel = state.label; + state.lock = false; + state.label = 1; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.label = oldLabel; + state.outside = 5; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.outside = false; + state.keyframe = '10'; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + + state.keyframe = true; + state.attributes = { 1: {}, 2: false, 3: () => {} }; + await expect(state.save()) + .rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('trying to change locked shape', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + let state = annotations[0]; + + state.lock = true; + state = await state.save(); + + const { points } = state; + state.points = [0, 0, 500, 500]; + state = await state.save(); + expect(state.points).toEqual(points); + }); + + test('trying to set too small area of a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + let state = annotations[0]; + + const { points } = state; + state.points = [0, 0, 2, 2]; // area is 4 + state = await state.save(); + expect(state.points).toEqual(points); + }); + + test('trying to set too small area of a track', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + let state = annotations[0]; + + const { points } = state; + state.points = [0, 0, 2, 2]; // area is 4 + state = await state.save(); + expect(state.points).toEqual(points); + }); + + test('trying to set too small length of a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + let state = annotations[8]; + + const { points } = state; + state.points = [0, 0, 2, 2]; // length is 2 + state = await state.save(); + expect(state.points).toEqual(points); + }); +}); + +describe('Feature: delete object', () => { + test('delete a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotationsBefore = await task.annotations.get(0); + const { length } = annotationsBefore; + await annotationsBefore[0].delete(); + const annotationsAfter = await task.annotations.get(0); + expect(annotationsAfter).toHaveLength(length - 1); + }); + + test('delete a track', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotationsBefore = await task.annotations.get(0); + const { length } = annotationsBefore; + await annotationsBefore[0].delete(); + const annotationsAfter = await task.annotations.get(0); + expect(annotationsAfter).toHaveLength(length - 1); + }); +}); + +describe('Feature: change z order of an object', () => { + test('up z order for a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + const state = annotations[0]; + + const { zOrder } = state; + await state.up(); + expect(state.zOrder).toBeGreaterThan(zOrder); + }); + + test('up z order for a track', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + const state = annotations[0]; + + const { zOrder } = state; + await state.up(); + expect(state.zOrder).toBeGreaterThan(zOrder); + }); + + test('down z order for a shape', async () => { + const task = (await window.cvat.tasks.get({ id: 100 }))[0]; + const annotations = await task.annotations.get(0); + const state = annotations[0]; + + const { zOrder } = state; + await state.down(); + expect(state.zOrder).toBeLessThan(zOrder); + }); + + test('down z order for a track', async () => { + const task = (await window.cvat.tasks.get({ id: 101 }))[0]; + const annotations = await task.annotations.get(0); + const state = annotations[0]; + + const { zOrder } = state; + await state.down(); + expect(state.zOrder).toBeLessThan(zOrder); + }); +}); diff --git a/cvat-core/tests/api/plugins.js b/cvat-core/tests/api/plugins.js new file mode 100644 index 00000000..2db3cdd5 --- /dev/null +++ b/cvat-core/tests/api/plugins.js @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); + +describe('Feature: dummy feature', () => { + test('dummy test', async () => { + // TODO: Write test after design of plugin system + }); +}); + +/* +const plugin = { + name: 'Example Plugin', + description: 'This example plugin demonstrates how plugin system in CVAT works', + cvat: { + server: { + about: { + async leave(self, result) { + result.plugins = await self.internal.getPlugins(); + return result; + }, + }, + }, + classes: { + Job: { + prototype: { + annotations: { + put: { + enter(self, objects) { + for (const obj of objects) { + if (obj.type !== 'tag') { + const points = obj.position.map((point) => { + const roundPoint = { + x: Math.round(point.x), + y: Math.round(point.y), + }; + return roundPoint; + }); + obj.points = points; + } + } + }, + }, + }, + }, + }, + }, + }, + internal: { + async getPlugins() { + const plugins = await window.cvat.plugins.list(); + return plugins.map((el) => { + const obj = { + name: el.name, + description: el.description, + }; + return obj; + }); + }, + }, +}; + + +async function test() { + await window.cvat.plugins.register(plugin); + await window.cvat.server.login('admin', 'nimda760'); + + try { + console.log(JSON.stringify(await window.cvat.server.about())); + console.log(await window.cvat.users.get({ self: false })); + console.log(await window.cvat.users.get({ self: true })); + console.log(JSON.stringify(await window.cvat.jobs.get({ taskID: 8 }))); + console.log(JSON.stringify(await window.cvat.jobs.get({ jobID: 10 }))); + console.log(await window.cvat.tasks.get()); + console.log(await window.cvat.tasks.get({ id: 8 })); + console.log('Done.'); + } catch (exception) { + console.log(exception.constructor.name); + console.log(exception.message); + } +} +*/ diff --git a/cvat-core/tests/api/server.js b/cvat-core/tests/api/server.js new file mode 100644 index 00000000..6220e158 --- /dev/null +++ b/cvat-core/tests/api/server.js @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); +const { + AnnotationFormat, + Loader, + Dumper, +} = require('../../src/annotation-format'); + +// Test cases +describe('Feature: get info about cvat', () => { + test('get info about server', async () => { + const result = await window.cvat.server.about(); + expect(result).toBeInstanceOf(Object); + expect('name' in result).toBeTruthy(); + expect('description' in result).toBeTruthy(); + expect('version' in result).toBeTruthy(); + }); +}); + + +describe('Feature: get share storage info', () => { + test('get files in a root of a share storage', async () => { + const result = await window.cvat.server.share(); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(5); + }); + + test('get files in a some dir of a share storage', async () => { + const result = await window.cvat.server.share('images'); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(8); + }); + + test('get files in a some unknown dir of a share storage', async () => { + expect(window.cvat.server.share( + 'Unknown Directory', + )).rejects.toThrow(window.cvat.exceptions.ServerError); + }); +}); + +describe('Feature: get annotation formats', () => { + test('get annotation formats from a server', async () => { + const result = await window.cvat.server.formats(); + expect(Array.isArray(result)).toBeTruthy(); + for (const format of result) { + expect(format).toBeInstanceOf(AnnotationFormat); + } + }); +}); + +describe('Feature: get annotation loaders', () => { + test('get annotation formats from a server', async () => { + const result = await window.cvat.server.formats(); + expect(Array.isArray(result)).toBeTruthy(); + for (const format of result) { + expect(format).toBeInstanceOf(AnnotationFormat); + const { loaders } = format; + expect(Array.isArray(loaders)).toBeTruthy(); + for (const loader of loaders) { + expect(loader).toBeInstanceOf(Loader); + } + } + }); +}); + +describe('Feature: get annotation dumpers', () => { + test('get annotation formats from a server', async () => { + const result = await window.cvat.server.formats(); + expect(Array.isArray(result)).toBeTruthy(); + for (const format of result) { + expect(format).toBeInstanceOf(AnnotationFormat); + const { dumpers } = format; + expect(Array.isArray(dumpers)).toBeTruthy(); + for (const dumper of dumpers) { + expect(dumper).toBeInstanceOf(Dumper); + } + } + }); +}); \ No newline at end of file diff --git a/cvat-core/tests/api/tasks.js b/cvat-core/tests/api/tasks.js new file mode 100644 index 00000000..232bec63 --- /dev/null +++ b/cvat-core/tests/api/tasks.js @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); + +const { Task } = require('../../src/session'); + + +// Test cases +describe('Feature: get a list of tasks', () => { + test('get all tasks', async () => { + const result = await window.cvat.tasks.get(); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(6); + for (const el of result) { + expect(el).toBeInstanceOf(Task); + } + }); + + test('get a task by an id', async () => { + const result = await window.cvat.tasks.get({ + id: 3, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(Task); + expect(result[0].id).toBe(3); + }); + + test('get a task by an unknown id', async () => { + const result = await window.cvat.tasks.get({ + id: 50, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(0); + }); + + test('get a task by an invalid id', async () => { + expect(window.cvat.tasks.get({ + id: '50', + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get tasks by filters', async () => { + const result = await window.cvat.tasks.get({ + mode: 'interpolation', + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(3); + for (const el of result) { + expect(el).toBeInstanceOf(Task); + expect(el.mode).toBe('interpolation'); + } + }); + + test('get tasks by invalid filters', async () => { + expect(window.cvat.tasks.get({ + unknown: '5', + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get task by name, status and mode', async () => { + const result = await window.cvat.tasks.get({ + mode: 'interpolation', + status: 'annotation', + name: 'Test Task', + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(1); + for (const el of result) { + expect(el).toBeInstanceOf(Task); + expect(el.mode).toBe('interpolation'); + expect(el.status).toBe('annotation'); + expect(el.name).toBe('Test Task'); + } + }); +}); + +describe('Feature: save a task', () => { + test('save some changed fields in a task', async () => { + let result = await window.cvat.tasks.get({ + id: 2, + }); + + result[0].bugTracker = 'newBugTracker'; + result[0].zOrder = true; + result[0].name = 'New Task Name'; + + result[0].save(); + + result = await window.cvat.tasks.get({ + id: 2, + }); + + expect(result[0].bugTracker).toBe('newBugTracker'); + expect(result[0].zOrder).toBe(true); + expect(result[0].name).toBe('New Task Name'); + }); + + test('save some new labels in a task', async () => { + let result = await window.cvat.tasks.get({ + id: 2, + }); + + const labelsLength = result[0].labels.length; + const newLabel = new window.cvat.classes.Label({ + name: 'My boss\'s car', + attributes: [{ + default_value: 'false', + input_type: 'checkbox', + mutable: true, + name: 'parked', + values: ['false'], + }], + }); + + result[0].labels = [newLabel]; + result[0].save(); + + result = await window.cvat.tasks.get({ + id: 2, + }); + + expect(result[0].labels).toHaveLength(labelsLength + 1); + const appendedLabel = result[0].labels.filter((el) => el.name === 'My boss\'s car'); + expect(appendedLabel).toHaveLength(1); + expect(appendedLabel[0].attributes).toHaveLength(1); + expect(appendedLabel[0].attributes[0].name).toBe('parked'); + expect(appendedLabel[0].attributes[0].defaultValue).toBe('false'); + expect(appendedLabel[0].attributes[0].mutable).toBe(true); + expect(appendedLabel[0].attributes[0].inputType).toBe('checkbox'); + }); + + test('save new task without an id', async () => { + const task = new window.cvat.classes.Task({ + name: 'New Task', + labels: [{ + name: 'My boss\'s car', + attributes: [{ + default_value: 'false', + input_type: 'checkbox', + mutable: true, + name: 'parked', + values: ['false'], + }], + }], + bug_tracker: 'bug tracker value', + image_quality: 50, + z_order: true, + }); + + const result = await task.save(); + expect(typeof (result.id)).toBe('number'); + }); +}); + +describe('Feature: delete a task', () => { + test('delete a task', async () => { + let result = await window.cvat.tasks.get({ + id: 3, + }); + + await result[0].delete(); + result = await window.cvat.tasks.get({ + id: 3, + }); + + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(0); + }); +}); diff --git a/cvat-core/tests/api/user.js b/cvat-core/tests/api/user.js new file mode 100644 index 00000000..9d6765dc --- /dev/null +++ b/cvat-core/tests/api/user.js @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* global + require:false + jest:false + describe:false +*/ + +// Setup mock for a server +jest.mock('../../src/server-proxy', () => { + const mock = require('../mocks/server-proxy.mock'); + return mock; +}); + +// Initialize api +window.cvat = require('../../src/api'); + +const User = require('../../src/user'); + +// Test cases +describe('Feature: get a list of users', () => { + test('get all users', async () => { + const result = await window.cvat.users.get(); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(2); + for (const el of result) { + expect(el).toBeInstanceOf(User); + } + }); + + test('get only self', async () => { + const result = await window.cvat.users.get({ + self: true, + }); + expect(Array.isArray(result)).toBeTruthy(); + expect(result).toHaveLength(1); + expect(result[0]).toBeInstanceOf(User); + }); + + test('get users with unknown filter key', async () => { + expect(window.cvat.users.get({ + unknown: '50', + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); + + test('get users with invalid filter key', async () => { + expect(window.cvat.users.get({ + self: 1, + })).rejects.toThrow(window.cvat.exceptions.ArgumentError); + }); +}); diff --git a/cvat-core/tests/mocks/dummy-data.mock.js b/cvat-core/tests/mocks/dummy-data.mock.js new file mode 100644 index 00000000..e8449c06 --- /dev/null +++ b/cvat-core/tests/mocks/dummy-data.mock.js @@ -0,0 +1,2608 @@ +/* eslint-disable */ + +const aboutDummyData = { + "name": "Computer Vision Annotation Tool", + "description": "CVAT is completely re-designed and re-implemented version of Video Annotation Tool from Irvine, California tool. It is free, online, interactive video and image annotation tool for computer vision. It is being used by our team to annotate million of objects with different properties. Many UI and UX decisions are based on feedbacks from professional data annotation team.", + "version": "0.5.dev20190516142240" +} + +const formatsDummyData = [{ + "id": 1, + "dumpers": [ + { + "display_name": "CVAT XML 1.1 for videos", + "format": "XML", + "version": "1.1", + "handler": "dump_as_cvat_interpolation" + }, + { + "display_name": "CVAT XML 1.1 for images", + "format": "XML", + "version": "1.1", + "handler": "dump_as_cvat_annotation" + } + ], + "loaders": [ + { + "display_name": "CVAT XML 1.1", + "format": "XML", + "version": "1.1", + "handler": "load" + } + ], + "name": "CVAT", + "created_date": "2019-08-08T12:18:56.571488+03:00", + "updated_date": "2019-08-08T12:18:56.571533+03:00", + "handler_file": "cvat/apps/annotation/cvat.py", + "owner": null +}, +{ + "id": 2, + "dumpers": [ + { + "display_name": "PASCAL VOC ZIP 1.0", + "format": "ZIP", + "version": "1.0", + "handler": "dump" + } + ], + "loaders": [ + { + "display_name": "PASCAL VOC ZIP 1.0", + "format": "ZIP", + "version": "1.0", + "handler": "load" + } + ], + "name": "PASCAL VOC", + "created_date": "2019-08-08T12:18:56.625025+03:00", + "updated_date": "2019-08-08T12:18:56.625071+03:00", + "handler_file": "cvat/apps/annotation/pascal_voc.py", + "owner": null +}, +{ + "id": 3, + "dumpers": [ + { + "display_name": "YOLO ZIP 1.0", + "format": "ZIP", + "version": "1.0", + "handler": "dump" + } + ], + "loaders": [ + { + "display_name": "YOLO ZIP 1.0", + "format": "ZIP", + "version": "1.0", + "handler": "load" + } + ], + "name": "YOLO", + "created_date": "2019-08-08T12:18:56.667534+03:00", + "updated_date": "2019-08-08T12:18:56.667578+03:00", + "handler_file": "cvat/apps/annotation/yolo.py", + "owner": null +}]; + +const usersDummyData = { + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "url": "http://localhost:7000/api/v1/users/1", + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "", + "email": "admin@dummy.com", + "groups": [ + "admin" + ], + "is_staff": true, + "is_superuser": true, + "is_active": true, + "last_login": "2019-05-17T11:53:05.961434+03:00", + "date_joined": "2019-05-13T15:33:17.833200+03:00" + }, + { + "url": "http://localhost:7000/api/v1/users/2", + "id": 2, + "username": "bsekache", + "first_name": "", + "last_name": "", + "email": "", + "groups": [ + "user", + "observer" + ], + "is_staff": false, + "is_superuser": false, + "is_active": true, + "last_login": "2019-05-16T13:07:19.564241+03:00", + "date_joined": "2019-05-16T13:05:57+03:00" + } + ] +} + +const shareDummyData = [ + { + "name": "images", + "type": "DIR", + "children": [ + { + "name": "image000001.jpg", + "type": "REG" + }, + { + "name": "nowy-jork-time-sqare.jpg", + "type": "REG" + }, + { + "name": "123123.jpg", + "type": "REG" + }, + { + "name": "ws_Oasis-night_1920x1200.jpg", + "type": "REG" + }, + { + "name": "image000002.jpg", + "type": "REG" + }, + { + "name": "fdgdfgfd.jpg", + "type": "REG" + }, + { + "name": "bbbbb.jpg", + "type": "REG" + }, + { + "name": "gdfgdfgdf.jpg", + "type": "REG" + } + ] + }, + { + "name": "2.avi", + "type": "REG" + }, + { + "name": "data", + "type": "DIR", + "children": [], + }, + { + "name": "out.MOV", + "type": "REG" + }, + { + "name": "bbbbb.jpg", + "type": "REG" + } +] + +const tasksDummyData = { + "count": 5, + "next": null, + "previous": null, + "results": [ + { + "url": "http://localhost:7000/api/v1/tasks/102", + "id": 102, + "name": "Test", + "size": 1, + "mode": "annotation", + "owner": 1, + "assignee": null, + "bug_tracker": "", + "created_date": "2019-09-05T11:59:22.987942Z", + "updated_date": "2019-09-05T14:04:07.569344Z", + "overlap": 0, + "segment_size": 0, + "z_order": false, + "status": "annotation", + "labels": [{ + "id": 5, + "name": "car", + "attributes": [] + }], + "segments": [{ + "start_frame": 0, + "stop_frame": 0, + "jobs": [{ + "url":"http://localhost:7000/api/v1/jobs/112", + "id": 112, + "assignee":null, + "status":"annotation" + }] + }], + "image_quality": 50, + "start_frame": 0, + "stop_frame": 0, + "frame_filter": "" + }, { + "url": "http://localhost:7000/api/v1/tasks/100", + "id": 100, + "name": "Image Task", + "size": 9, + "mode": "annotation", + "owner": 1, + "assignee": null, + "bug_tracker": "", + "created_date": "2019-06-18T13:05:08.941304+03:00", + "updated_date": "2019-07-16T15:51:29.142871+03:00", + "overlap": 0, + "segment_size": 0, + "z_order": false, + "status": "annotation", + "labels": [ + { + "id": 1, + "name": "car,", + "attributes": [ + + ] + }, + { + "id": 2, + "name": "person", + "attributes": [ + + ] + } + ], + "segments": [ + { + "start_frame": 0, + "stop_frame": 8, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/100", + "id": 100, + "assignee": null, + "status": "annotation" + } + ] + } + ], + "image_quality": 50, + "start_frame": 0, + "stop_frame": 0, + "frame_filter": "" + }, + { + "url": "http://localhost:7000/api/v1/tasks/10", + "id": 101, + "name": "Video Task", + "size": 5002, + "mode": "interpolation", + "owner": 1, + "assignee": null, + "bug_tracker": "", + "created_date": "2019-06-21T16:34:49.199691+03:00", + "updated_date": "2019-07-12T16:43:58.904892+03:00", + "overlap": 5, + "segment_size": 500, + "z_order": false, + "status": "annotation", + "labels": [ + { + "id": 22, + "name": "bicycle", + "attributes":[ + { + "id": 13, + "name": "driver", + "mutable": false, + "input_type": "radio", + "default_value": "man", + "values": [ + "man", + "woman" + ] + }, + { + "id": 14, + "name": "sport", + "mutable": true, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + } + ] + }, + { + "id": 21, + "name": "car", + "attributes": [ + { + "id": 10, + "name": "model", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "bmw", + "mazda", + "suzuki", + "kia" + ] + }, + { + "id": 11, + "name": "driver", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "man", + "woman" + ] + }, + { + "id": 12, + "name": "parked", + "mutable": true, + "input_type": "checkbox", + "default_value": "true", + "values": [ + "true" + ] + } + ] + }, + { + "id": 20, + "name": "face", + "attributes": [ + { + "id": 6, + "name": "age", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "baby (0-5)", + "child (6-12)", + "adolescent (13-19)", + "adult (20-45)", + "middle-age (46-64)", + "old (65-)" + ] + }, + { + "id": 7, + "name": "glass", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "sunglass", + "transparent", + "other" + ] + }, + { + "id": 8, + "name": "beard", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "yes" + ] + }, + { + "id": 9, + "name": "race", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "asian", + "black", + "caucasian", + "other" + ] + } + ] + }, + { + "id": 23, + "name": "motorcycle", + "attributes": [ + { + "id": 15, + "name": "model", + "mutable": false, + "input_type": "text", + "default_value": "unknown", + "values": [ + "unknown" + ] + } + ] + }, + { + "id": 19, + "name": "person, pedestrian", + "attributes": [ + { + "id": 1, + "name": "action", + "mutable": true, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "sitting", + "raising_hand", + "standing" + ] + }, + { + "id": 2, + "name": "age", + "mutable": false, + "input_type": "number", + "default_value": "1", + "values": [ + "1", + "100", + "1" + ] + }, + { + "id": 3, + "name": "gender", + "mutable" :false, + "input_type": "select", + "default_value": "male", + "values": [ + "male", + "female" + ] + }, + { + "id": 4, + "name": "false positive", + "mutable": false, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + }, + { + "id": 5, + "name": "clother", + "mutable": true, + "input_type": "text", + "default_value": "non, initialized", + "values": [ + "non, initialized" + ] + } + ] + }, + { + "id": 24, + "name": "road", + "attributes": [ + + ] + } + ], + "segments": [ + { + "start_frame": 0, + "stop_frame": 499, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/10", + "id": 101, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 495, + "stop_frame": 994, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/11", + "id": 102, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 990, + "stop_frame": 1489, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/12", + "id": 103, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 1485, + "stop_frame": 1984, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/13", + "id": 104, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 1980, + "stop_frame": 2479, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/14", + "id": 105, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 2475, + "stop_frame": 2974, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/15", + "id": 106, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 2970, + "stop_frame": 3469, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/16", + "id": 107, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 3465, + "stop_frame": 3964, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/17", + "id": 108, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 3960, + "stop_frame": 4459, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/18", + "id": 109, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 4455, + "stop_frame": 4954, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/19", + "id": 110, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 4950, + "stop_frame": 5001, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/20", + "id": 111, + "assignee": null, + "status": "annotation" + } + ] + } + ], + "image_quality": 50, + "start_frame": 0, + "stop_frame": 5001, + "frame_filter": "" + }, + { + "url": "http://localhost:7000/api/v1/tasks/3", + "id": 3, + "name": "Test Task", + "size": 5002, + "mode": "interpolation", + "owner": 2, + "assignee": null, + "bug_tracker": "", + "created_date": "2019-05-16T13:08:00.621747+03:00", + "updated_date": "2019-05-16T13:08:00.621797+03:00", + "overlap": 5, + "segment_size": 5000, + "z_order": true, + "flipped": false, + "status": "annotation", + "labels": [ + { + "id": 16, + "name": "bicycle", + "attributes": [ + { + "id": 43, + "name": "driver", + "mutable": false, + "input_type": "radio", + "default_value": "man", + "values": [ + "man", + "woman" + ] + }, + { + "id": 44, + "name": "sport", + "mutable": true, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + } + ] + }, + { + "id": 15, + "name": "car", + "attributes": [ + { + "id": 40, + "name": "model", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "bmw", + "mazda", + "suzuki", + "kia" + ] + }, + { + "id": 41, + "name": "driver", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "man", + "woman" + ] + }, + { + "id": 42, + "name": "parked", + "mutable": true, + "input_type": "checkbox", + "default_value": "true", + "values": [ + "true" + ] + } + ] + }, + { + "id": 14, + "name": "face", + "attributes": [ + { + "id": 36, + "name": "age", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "baby (0-5)", + "child (6-12)", + "adolescent (13-19)", + "adult (20-45)", + "middle-age (46-64)", + "old (65-)" + ] + }, + { + "id": 37, + "name": "glass", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "sunglass", + "transparent", + "other" + ] + }, + { + "id": 38, + "name": "beard", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "yes" + ] + }, + { + "id": 39, + "name": "race", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "asian", + "black", + "caucasian", + "other" + ] + } + ] + }, + { + "id": 17, + "name": "motorcycle", + "attributes": [ + { + "id": 45, + "name": "model", + "mutable": false, + "input_type": "text", + "default_value": "unknown", + "values": [ + "unknown" + ] + } + ] + }, + { + "id": 13, + "name": "person, pedestrian", + "attributes": [ + { + "id": 31, + "name": "action", + "mutable": true, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "sitting", + "raising_hand", + "standing" + ] + }, + { + "id": 32, + "name": "age", + "mutable": false, + "input_type": "number", + "default_value": "1", + "values": [ + "1", + "100", + "1" + ] + }, + { + "id": 33, + "name": "gender", + "mutable": false, + "input_type": "select", + "default_value": "male", + "values": [ + "male", + "female" + ] + }, + { + "id": 34, + "name": "false positive", + "mutable": false, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + }, + { + "id": 35, + "name": "clother", + "mutable": true, + "input_type": "text", + "default_value": "non, initialized", + "values": [ + "non, initialized" + ] + } + ] + }, + { + "id": 18, + "name": "road", + "attributes": [] + } + ], + "segments": [ + { + "start_frame": 0, + "stop_frame": 4999, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/3", + "id": 3, + "assignee": null, + "status": "annotation" + } + ] + }, + { + "start_frame": 4995, + "stop_frame": 5001, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/4", + "id": 4, + "assignee": null, + "status": "annotation" + } + ] + } + ], + "image_quality": 50 + }, + { + "url": "http://localhost:7000/api/v1/tasks/2", + "id": 2, + "name": "Video", + "size": 75, + "mode": "interpolation", + "owner": 1, + "assignee": null, + "bug_tracker": "", + "created_date": "2019-05-15T11:40:19.487999+03:00", + "updated_date": "2019-05-15T16:58:27.992785+03:00", + "overlap": 5, + "segment_size": 0, + "z_order": false, + "flipped": false, + "status": "annotation", + "labels": [ + { + "id": 10, + "name": "bicycle", + "attributes": [ + { + "id": 28, + "name": "driver", + "mutable": false, + "input_type": "radio", + "default_value": "man", + "values": [ + "man", + "woman" + ] + }, + { + "id": 29, + "name": "sport", + "mutable": true, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + } + ] + }, + { + "id": 9, + "name": "car", + "attributes": [ + { + "id": 25, + "name": "model", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "bmw", + "mazda", + "suzuki", + "kia" + ] + }, + { + "id": 26, + "name": "driver", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "man", + "woman" + ] + }, + { + "id": 27, + "name": "parked", + "mutable": true, + "input_type": "checkbox", + "default_value": "true", + "values": [ + "true" + ] + } + ] + }, + { + "id": 8, + "name": "face", + "attributes": [ + { + "id": 21, + "name": "age", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "baby (0-5)", + "child (6-12)", + "adolescent (13-19)", + "adult (20-45)", + "middle-age (46-64)", + "old (65-)" + ] + }, + { + "id": 22, + "name": "glass", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "sunglass", + "transparent", + "other" + ] + }, + { + "id": 23, + "name": "beard", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "yes" + ] + }, + { + "id": 24, + "name": "race", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "asian", + "black", + "caucasian", + "other" + ] + } + ] + }, + { + "id": 11, + "name": "motorcycle", + "attributes": [ + { + "id": 30, + "name": "model", + "mutable": false, + "input_type": "text", + "default_value": "unknown", + "values": [ + "unknown" + ] + } + ] + }, + { + "id": 7, + "name": "person, pedestrian", + "attributes": [ + { + "id": 16, + "name": "action", + "mutable": true, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "sitting", + "raising_hand", + "standing" + ] + }, + { + "id": 17, + "name": "age", + "mutable": false, + "input_type": "number", + "default_value": "1", + "values": [ + "1", + "100", + "1" + ] + }, + { + "id": 18, + "name": "gender", + "mutable": false, + "input_type": "select", + "default_value": "male", + "values": [ + "male", + "female" + ] + }, + { + "id": 19, + "name": "false positive", + "mutable": false, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + }, + { + "id": 20, + "name": "clother", + "mutable": true, + "input_type": "text", + "default_value": "non, initialized", + "values": [ + "non, initialized" + ] + } + ] + }, + { + "id": 12, + "name": "road", + "attributes": [] + } + ], + "segments": [ + { + "start_frame": 0, + "stop_frame": 74, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/2", + "id": 2, + "assignee": null, + "status": "annotation" + } + ] + } + ], + "image_quality": 50 + }, + { + "url": "http://localhost:7000/api/v1/tasks/1", + "id": 1, + "name": "Labels Set", + "size": 9, + "mode": "annotation", + "owner": 1, + "assignee": null, + "bug_tracker": "http://bugtracker.com/issue12345", + "created_date": "2019-05-13T15:35:29.871003+03:00", + "updated_date": "2019-05-15T11:20:55.770587+03:00", + "overlap": 0, + "segment_size": 0, + "z_order": true, + "flipped": false, + "status": "annotation", + "labels": [ + { + "id": 4, + "name": "bicycle", + "attributes": [ + { + "id": 13, + "name": "driver", + "mutable": false, + "input_type": "radio", + "default_value": "man", + "values": [ + "man", + "woman" + ] + }, + { + "id": 14, + "name": "sport", + "mutable": true, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + } + ] + }, + { + "id": 3, + "name": "car", + "attributes": [ + { + "id": 10, + "name": "model", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "bmw", + "mazda", + "suzuki", + "kia" + ] + }, + { + "id": 11, + "name": "driver", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "man", + "woman" + ] + }, + { + "id": 12, + "name": "parked", + "mutable": true, + "input_type": "checkbox", + "default_value": "true", + "values": [ + "true" + ] + } + ] + }, + { + "id": 2, + "name": "face", + "attributes": [ + { + "id": 6, + "name": "age", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "baby (0-5)", + "child (6-12)", + "adolescent (13-19)", + "adult (20-45)", + "middle-age (46-64)", + "old (65-)" + ] + }, + { + "id": 7, + "name": "glass", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "sunglass", + "transparent", + "other" + ] + }, + { + "id": 8, + "name": "beard", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "no", + "yes" + ] + }, + { + "id": 9, + "name": "race", + "mutable": false, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "skip", + "asian", + "black", + "caucasian", + "other" + ] + } + ] + }, + { + "id": 5, + "name": "motorcycle", + "attributes": [ + { + "id": 15, + "name": "model", + "mutable": false, + "input_type": "text", + "default_value": "unknown", + "values": [ + "unknown" + ] + } + ] + }, + { + "id": 1, + "name": "person, pedestrian", + "attributes": [ + { + "id": 1, + "name": "action", + "mutable": true, + "input_type": "select", + "default_value": "__undefined__", + "values": [ + "__undefined__", + "sitting", + "raising_hand", + "standing" + ] + }, + { + "id": 2, + "name": "age", + "mutable": false, + "input_type": "number", + "default_value": "1", + "values": [ + "1", + "100", + "1" + ] + }, + { + "id": 3, + "name": "gender", + "mutable": false, + "input_type": "select", + "default_value": "male", + "values": [ + "male", + "female" + ] + }, + { + "id": 4, + "name": "false positive", + "mutable": false, + "input_type": "checkbox", + "default_value": "false", + "values": [ + "false" + ] + }, + { + "id": 5, + "name": "clother", + "mutable": true, + "input_type": "text", + "default_value": "non, initialized", + "values": [ + "non, initialized" + ] + } + ] + }, + { + "id": 6, + "name": "road", + "attributes": [] + } + ], + "segments": [ + { + "start_frame": 0, + "stop_frame": 8, + "jobs": [ + { + "url": "http://localhost:7000/api/v1/jobs/1", + "id": 1, + "assignee": null, + "status": "annotation" + } + ] + } + ], + "image_quality": 95 + } + ] +} + +const taskAnnotationsDummyData = { + '112': { + "version":21, + "tags": [], + "shapes": [{ + "type": "rectangle", + "occluded": false, + "z_order": 1, + "points": [ + 557.7890625, + 276.2216796875, + 907.1888732910156, + 695.5014038085938 + ], + "id": 15, + "frame": 0, + "label_id": 5, + "group": 0, + "attributes": [] + }], + "tracks": [{ + "id": 15, + "frame": 0, + "label_id": 5, + "group": 0, + "shapes": [{ + "type": "rectangle", + "occluded": false, + "z_order": 13, + "points": [ + 792.787109375, + 16.5234375, + 1171.1027526855469, + 521.3458862304688 + ], + "id": 22, + "frame": 0, + "outside": false, + "attributes": [] + }], + "attributes": [] + }] + }, + '101': { + "version":21, + "tags":[], + "shapes":[], + "tracks":[ + { + "id": 25, // interpolation + "frame": 10, + "label_id": 19, + "group": 0, + "shapes": [{ + "type": "polygon", + "occluded": false, + "z_order": 2, + "points": [ + 377.64912280702083, + 458.5473684210556, + 383.82456140351314, + 458.5473684210556, + 406.98245614035477, + 455.45964912281124, + 431.6842105263204, + 455.45964912281124, + 457.92982456140817, + 455.45964912281124, + 482.6315789473738, + 455.45964912281124, + 508.87719298246157, + 455.45964912281124, + 535.1228070175493, + 455.45964912281124, + 559.8245614035113, + 455.45964912281124, + 587.6140350877249, + 455.45964912281124, + 620.0350877193014, + 455.45964912281124, + 640.1052631578968, + 455.45964912281124, + 664.8070175438625, + 453.9157894736891, + 692.5964912280724, + 450.8280701754411, + 721.9298245614082, + 450.8280701754411, + 743.5438596491258, + 447.74035087719676, + 769.7894736842136, + 446.1964912280746, + 796.0350877193014, + 446.1964912280746, + 823.8245614035113, + 446.1964912280746, + 846.9824561403548, + 446.1964912280746, + 876.3157894736869, + 446.1964912280746, + 905.6491228070226, + 446.1964912280746, + 931.8947368421104, + 446.1964912280746, + 959.6842105263204, + 446.1964912280746, + 987.4736842105303, + 446.1964912280746, + 1015.2631578947403, + 446.1964912280746, + 1039.964912280706, + 446.1964912280746, + 1066.2105263157937, + 446.1964912280746, + 1090.9122807017593, + 446.1964912280746, + 1115.614035087725, + 446.1964912280746, + 1138.7719298245647, + 449.28421052631893, + 1231.4000000000015, + 413.8000000000011, + 1180.4561403508815, + 467.81052631579223, + 1180.4561403508815, + 494.05614035088, + 1180.4561403508815, + 520.3017543859678, + 1180.4561403508815, + 545.0035087719334, + 1180.4561403508815, + 571.2491228070212, + 1180.4561403508815, + 597.494736842109, + 1180.4561403508815, + 620.6526315789524, + 1180.4561403508815, + 649.9859649122845, + 1180.4561403508815, + 676.2315789473723, + 1180.4561403508815, + 699.3894736842158, + 1180.4561403508815, + 727.1789473684257, + 1180.4561403508815, + 747.2491228070212, + 1180.4561403508815, + 771.9508771929868, + 1180.4561403508815, + 802.8280701754411, + 1180.4561403508815, + 830.6175438596547, + 1180.4561403508815, + 853.7754385964945, + 1180.4561403508815, + 880.0210526315823, + 1183.5438596491258, + 901.6350877193036, + 1183.5438596491258, + 929.4245614035135, + 1186.6315789473738, + 952.5824561403533, + 1188.175438596496, + 975.7403508771968, + 1188.175438596496, + 1001.9859649122845, + 1188.175438596496, + 1023.6000000000022, + 1188.175438596496, + 1057.5649122807044, + 1186.6315789473738, + 1082.26666666667, + 1186.6315789473738, + 1108.5122807017578, + 1186.6315789473738, + 1133.2140350877235, + 1175.82421875, + 1154.828125, + 1155.7543859649159, + 1156.371929824567, + 1132.5964912280724, + 1154.828070175441, + 1106.3508771929846, + 1154.828070175441, + 1078.5614035087747, + 1154.828070175441, + 1053.8596491228127, + 1150.1964912280746, + 1030.7017543859693, + 1148.6526315789524, + 1002.9122807017593, + 1148.6526315789524, + 982.8421052631602, + 1148.6526315789524, + 953.5087719298281, + 1147.1087719298303, + 922.6315789473738, + 1147.1087719298303, + 891.7543859649159, + 1147.1087719298303, + 868.5964912280724, + 1147.1087719298303, + 839.2631578947403, + 1147.1087719298303, + 816.1052631578968, + 1147.1087719298303, + 786.7719298245647, + 1147.1087719298303, + 760.5263157894769, + 1147.1087719298303, + 735.8245614035113, + 1147.1087719298303, + 708.0350877193014, + 1142.47719298246, + 684.8771929824616, + 1140.933333333338, + 658.6315789473738, + 1140.933333333338, + 633.9298245614082, + 1140.933333333338, + 607.6842105263204, + 1139.3894736842158, + 581.4385964912326, + 1134.7578947368456, + 559.8245614035113, + 1133.2140350877235, + 535.1228070175493, + 1131.6701754386013, + 505.7894736842136, + 1131.6701754386013, + 482.6315789473738, + 1131.6701754386013, + 454.8421052631602, + 1130.1263157894791, + 430.1403508771964, + 1130.1263157894791, + 405.4385964912326, + 1130.1263157894791, + 383.82421875, + 1130.126953125, + 382.28070175438916, + 1113.143859649128, + 380.736842105267, + 1088.4421052631624, + 380.736842105267, + 1056.0210526315823, + 380.736842105267, + 1026.6877192982502, + 379.1929824561448, + 1005.0736842105289, + 374.5614035087765, + 978.8280701754411, + 371.47368421053034, + 949.494736842109, + 371.47368421053034, + 921.705263157899, + 371.47368421053034, + 897.0035087719334, + 371.47368421053034, + 866.1263157894791, + 371.47368421053034, + 842.9684210526357, + 371.47368421053034, + 810.5473684210556, + 371.47368421053034, + 778.1263157894791, + 377.64912280702083, + 751.8807017543913, + 380.736842105267, + 722.5473684210556, + 385.3684210526353, + 693.2140350877235, + 385.3684210526353, + 668.5122807017578, + 386.9122807017575, + 643.8105263157922, + 388.45614035088147, + 619.1087719298266, + 388.45614035088147, + 591.3192982456167, + 388.45614035088147, + 563.5298245614067, + 388.45614035088147, + 535.7403508771968, + 388.45614035088147, + 511.03859649123115, + 386.9122807017575, + 487.88070175439134 + ], + "id":382, + "frame":10, + "outside":false, + "attributes": [{ + "spec_id":1, + "value":"__undefined__" + }, { + "spec_id":5, + "value":"non, initialized" + }] + }, { + "type": "polygon", + "occluded": false, + "z_order": 2, + "points": [ + 502.701171875, + 1093.07421875, + 860.8771929824616, + 443.10877192982844, + 1462.9824561403548, + 1120.8631578947425 + ], + "id": 383, + "frame": 20, + "outside": false, + "attributes": [] + }, { + "type": "polygon", + "occluded": false, + "z_order": 2, + "points": [ + 502.701171875, + 1093.07421875, + 860.8771929824616, + 443.10877192982844, + 1462.9824561403548, + 1120.8631578947425 + ], + "id": 384, + "frame": 22, + "outside": true, + "attributes": [] + }], + "attributes": [{ + "spec_id": 2, + "value": "1" + }, { + "spec_id": 3, + "value": "male" + }, { + "spec_id": 4, + "value": "false" + }] + }, + { + "id": 60, + "frame": 0, + "label_id": 19, + "group": 0, + "shapes": [{ + "type": "rectangle", + "occluded": false, + "z_order": 1, + "points": [ + 425.58984375, + 540.298828125, + 755.9765625, + 745.6328125 + ], + "id": 379, + "frame": 0, + "outside": false, + "attributes": [ + { + "spec_id":5, + "value":"non, initialized" + }, + { + "spec_id":1, + "value":"__undefined__" + } + ] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 1, + "points": [ + 238.8000000000011, + 498.6000000000022, + 546.01171875, + 660.720703125 + ], + "id": 380, + "frame": 10, + "outside": false, + "attributes": [] + }, { + "type":"rectangle", + "occluded":false, + "z_order":1, + "points":[ + 13.3955078125, + 447.650390625, + 320.6072265624989, + 609.7710937499978 + ], + "id":381, + "frame":20, + "outside":false, + "attributes":[ + + ] + }], + "attributes":[ + { + "spec_id":2, + "value":"1" + }, + { + "spec_id":3, + "value":"male" + }, + { + "spec_id":4, + "value":"false" + }] + } + ] + }, + '100': { + "version": 16, + "tags": [], + "shapes": [{ + "type": "rectangle", + "occluded": false, + "z_order": 1, + "points": [ + 387.91, + 403.81, + 595.14, + 712.25 + ], + "id": 108, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 2, + "points": [ + 783.12, + 368.91, + 990.35, + 677.34 + ], + "id": 109, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 3, + "points": [ + 1277.1, + 239.99, + 1484.33, + 548.43 + ], + "id": 110, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 4, + "points": [ + 1420.48, + 713.49, + 1627.71, + 1021.92 + ], + "id": 111, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes":[ + + ] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 5, + "points": [ + 896.38, + 659.27, + 1103.61, + 967.71 + ], + "id": 112, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "polygon", + "occluded": false, + "z_order": 6, + "points": [ + 449.36, + 892.97, + 449.36, + 892.97, + 468.63, + 913.46, + 495.14, + 933.94, + 527.67, + 955.62, + 562.61, + 973.7, + 589.12, + 983.34, + 613.21, + 988.15, + 632.49, + 991.77, + 656.59, + 994.18, + 686.71, + 994.18, + 733.69, + 980.93, + 772.25, + 959.24, + 809.6, + 927.91, + 837.31, + 896.59, + 851.77, + 867.67, + 861.41, + 841.17, + 862.61, + 805.02, + 840.92, + 759.24, + 802.37, + 720.68, + 777.07, + 703.82, + 750.56, + 690.56, + 726.47, + 684.54, + 698.75, + 680.92, + 681.89, + 680.92, + 656.59, + 680.92, + 633.69, + 683.33, + 608.39, + 690.56, + 578.27, + 706.22, + 548.15, + 718.27, + 518.03, + 730.32, + 486.71, + 743.57, + 458.99, + 756.83, + 434.9, + 766.47, + 408.39, + 777.31, + 381.89, + 786.95, + 354.17, + 794.18, + 331.28, + 800.2, + 295.14, + 803.82, + 283.09, + 800.2, + 267.43, + 783.33, + 255.38, + 766.47, + 232.49, + 733.94, + 220.44, + 713.45, + 212.0, + 688.15, + 208.39, + 666.47, + 210.8, + 647.19 + ], + "id": 113, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes":[] + }, { + "type": "polygon", + "occluded": false, + "z_order": 7, + "points": [ + 1260.84, + 344.81, + 1260.84, + 344.81, + 1280.11, + 365.29, + 1306.62, + 385.78, + 1339.15, + 407.46, + 1374.09, + 425.53, + 1400.6, + 435.17, + 1424.69, + 439.99, + 1443.97, + 443.61, + 1468.07, + 446.02, + 1498.19, + 446.02, + 1545.18, + 432.76, + 1583.73, + 411.08, + 1621.08, + 379.75, + 1648.79, + 348.43, + 1663.25, + 319.51, + 1672.89, + 293.0, + 1674.09, + 256.86, + 1652.41, + 211.08, + 1613.85, + 172.52, + 1588.55, + 155.65, + 1562.04, + 142.4, + 1537.95, + 136.38, + 1510.24, + 132.76, + 1493.37, + 132.76, + 1468.07, + 132.76, + 1445.18, + 135.17, + 1419.87, + 142.4, + 1389.75, + 158.06, + 1359.63, + 170.11, + 1329.51, + 182.16, + 1298.19, + 195.41, + 1270.48, + 208.67, + 1246.38, + 218.3, + 1219.87, + 229.15, + 1193.37, + 238.79, + 1165.66, + 246.02, + 1142.76, + 252.04, + 1106.62, + 255.65, + 1094.57, + 252.04, + 1078.91, + 235.17, + 1066.86, + 218.3, + 1043.97, + 185.77, + 1031.92, + 165.29, + 1023.49, + 139.99, + 1019.87, + 118.3, + 1022.28, + 99.03 + ], + "id": 114, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "polygon", + "occluded": false, + "z_order": 8, + "points": [ + 1113.21, + 723.09, + 1322.86, + 1018.28, + 1562.62, + 873.7, + 1587.92, + 641.16, + 1267.43, + 530.32 + ], + "id": 115, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "polyline", + "occluded": false, + "z_order": 9, + "points": [ + 268.63, + 359.23, + 277.07, + 344.78, + 292.73, + 325.5, + 312.01, + 311.04, + 331.28, + 300.2, + 349.36, + 295.38, + 375.86, + 290.56, + 387.91, + 290.56, + 418.03, + 290.56, + 439.72, + 292.97, + 457.79, + 295.38, + 492.73, + 301.4, + 525.26, + 306.22, + 534.9, + 306.22, + 571.04, + 296.58, + 591.53, + 284.54, + 610.8, + 272.49, + 640.92, + 253.21, + 655.38, + 238.75 + ], + "id": 116, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "points", + "occluded": false, + "z_order": 10, + "points": [ + 1089.12, + 505.02, + 1178.28, + 543.57, + 1074.66, + 602.61, + 1109.6, + 680.92, + 1172.25, + 631.53, + 1036.11, + 576.1, + 1057.79, + 445.98, + 1185.51, + 400.2 + ], + "id": 117, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [ + + ] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1565.03, + 555.62, + 1787.92, + 765.26 + ], + "id": 118, + "frame": 0, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 100.0, + 100.0 + ], + "id": 119, + "frame": 1, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "polygon", + "occluded": false, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 0.0, + 100.0, + 200.0 + ], + "id": 120, + "frame": 1, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 121, + "frame": 1, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 122, + "frame": 1, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 123, + "frame": 2, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 124, + "frame": 2, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 125, + "frame": 3, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 126, + "frame": 3, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 127, + "frame": 4, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 128, + "frame": 4, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 129, + "frame": 5, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 130, + "frame": 5, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 131, + "frame": 6, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 132, + "frame": 6, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 133, + "frame": 7, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 134, + "frame": 7, + "label_id": 2, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": false, + "z_order": 11, + "points": [ + 1211.6, + 500.48, + 1434.49, + 710.12 + ], + "id": 135, + "frame": 8, + "label_id": 1, + "group": 0, + "attributes": [] + }, { + "type": "rectangle", + "occluded": true, + "z_order": 0, + "points": [ + 0.0, + 0.0, + 200.0, + 200.0 + ], + "id": 136, + "frame": 8, + "label_id": 2, + "group": 0, + "attributes": [] + }], + "tracks":[] + } +}; + +const jobAnnotationsDummyData = JSON.parse(JSON.stringify(taskAnnotationsDummyData)); + +const frameMetaDummyData = { + 1: [{ + "width": 1920, + "height": 1080 + }, { + "width": 1600, + "height": 1143 + }, { + "width": 1600, + "height": 859 + }, { + "width": 3840, + "height": 2160 + }, { + "width": 2560, + "height": 1920 + }, { + "width": 1920, + "height": 1080 + }, { + "width": 1920, + "height": 1080 + }, { + "width": 700, + "height": 453 + }, { + "width": 1920, + "height": 1200 + }], + 2: [{ + "width": 1920, + "height": 1080 + }], + 3: [{ + "width": 1888, + "height": 1408 + }], + 100: [{ + "width": 1920, + "height": 1080 + }, { + "width": 1600, + "height": 1143 + }, { + "width": 1600, + "height": 859 + }, { + "width": 3840, + "height": 2160 + }, { + "width": 2560, + "height": 1920 + }, { + "width": 1920, + "height": 1080 + }, { + "width": 1920, + "height": 1080 + }, { + "width": 700, + "height": 453 + }, { + "width": 1920, + "height": 1200 + }], + 101: [{ + "width": 1888, + "height": 1408 + }], + 102: [{ + "width":1920, + "height":1080 + }], +} + +module.exports = { + tasksDummyData, + aboutDummyData, + shareDummyData, + usersDummyData, + taskAnnotationsDummyData, + jobAnnotationsDummyData, + frameMetaDummyData, + formatsDummyData, +} diff --git a/cvat-core/tests/mocks/server-proxy.mock.js b/cvat-core/tests/mocks/server-proxy.mock.js new file mode 100644 index 00000000..2f7ed2f5 --- /dev/null +++ b/cvat-core/tests/mocks/server-proxy.mock.js @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: MIT +*/ + +/* eslint import/no-extraneous-dependencies: 0 */ + +/* global + require:false +*/ + +const { + tasksDummyData, + aboutDummyData, + formatsDummyData, + shareDummyData, + usersDummyData, + taskAnnotationsDummyData, + jobAnnotationsDummyData, + frameMetaDummyData, +} = require('./dummy-data.mock'); + +class ServerProxy { + constructor() { + async function about() { + return JSON.parse(JSON.stringify(aboutDummyData)); + } + + async function share(directory) { + let position = shareDummyData; + + // Emulation of internal directories + if (directory.length > 1) { + const components = directory.split('/'); + + for (const component of components) { + const idx = position.map(x => x.name).indexOf(component); + if (idx !== -1 && 'children' in position[idx]) { + position = position[idx].children; + } else { + throw new window.cvat.exceptions.ServerError( + `${component} is not a valid directory`, + 400, + ); + } + } + } + + return JSON.parse(JSON.stringify(position)); + } + + async function formats() { + return JSON.parse(JSON.stringify(formatsDummyData)); + } + + async function exception() { + return null; + } + + async function login() { + return null; + } + + async function logout() { + return null; + } + + async function getTasks(filter = '') { + function QueryStringToJSON(query) { + const pairs = [...new URLSearchParams(query).entries()]; + + const result = {}; + for (const pair of pairs) { + const [key, value] = pair; + if (['id'].includes(key)) { + result[key] = +value; + } else { + result[key] = value; + } + } + + return JSON.parse(JSON.stringify(result)); + } + + // Emulation of a query filter + const queries = QueryStringToJSON(filter); + const result = tasksDummyData.results.filter((x) => { + for (const key in queries) { + if (Object.prototype.hasOwnProperty.call(queries, key)) { + // TODO: Particular match for some fields is not checked + if (queries[key] !== x[key]) { + return false; + } + } + } + + return true; + }); + + return result; + } + + async function saveTask(id, taskData) { + const object = tasksDummyData.results.filter(task => task.id === id)[0]; + for (const prop in taskData) { + if (Object.prototype.hasOwnProperty.call(taskData, prop) + && Object.prototype.hasOwnProperty.call(object, prop)) { + object[prop] = taskData[prop]; + } + } + } + + async function createTask(taskData) { + const id = Math.max(...tasksDummyData.results.map(el => el.id)) + 1; + tasksDummyData.results.push({ + id, + url: `http://localhost:7000/api/v1/tasks/${id}`, + name: taskData.name, + size: 5000, + mode: 'interpolation', + owner: 2, + assignee: null, + bug_tracker: taskData.bug_tracker, + created_date: '2019-05-16T13:08:00.621747+03:00', + updated_date: '2019-05-16T13:08:00.621797+03:00', + overlap: taskData.overlap ? taskData.overlap : 5, + segment_size: taskData.segment_size ? taskData.segment_size : 5000, + z_order: taskData.z_order, + flipped: false, + status: 'annotation', + image_quality: taskData.image_quality, + labels: JSON.parse(JSON.stringify(taskData.labels)), + }); + + const createdTask = await getTasks(`?id=${id}`); + return createdTask[0]; + } + + async function deleteTask(id) { + const tasks = tasksDummyData.results; + const task = tasks.filter(el => el.id === id)[0]; + if (task) { + tasks.splice(tasks.indexOf(task), 1); + } + } + + async function getJob(jobID) { + const jobs = tasksDummyData.results.reduce((acc, task) => { + for (const segment of task.segments) { + for (const job of segment.jobs) { + const copy = JSON.parse(JSON.stringify(job)); + copy.start_frame = segment.start_frame; + copy.stop_frame = segment.stop_frame; + copy.task_id = task.id; + + acc.push(copy); + } + } + + return acc; + }, []).filter(job => job.id === jobID); + + return jobs[0] || { + detail: 'Not found.', + }; + } + + async function saveJob(id, jobData) { + const object = tasksDummyData.results.reduce((acc, task) => { + for (const segment of task.segments) { + for (const job of segment.jobs) { + acc.push(job); + } + } + + return acc; + }, []).filter(job => job.id === id)[0]; + + for (const prop in jobData) { + if (Object.prototype.hasOwnProperty.call(jobData, prop) + && Object.prototype.hasOwnProperty.call(object, prop)) { + object[prop] = jobData[prop]; + } + } + } + + async function getUsers() { + return JSON.parse(JSON.stringify(usersDummyData)).results; + } + + async function getSelf() { + return JSON.parse(JSON.stringify(usersDummyData)).results[0]; + } + + async function getData() { + return 'DUMMY_IMAGE'; + } + + async function getMeta(tid) { + return JSON.parse(JSON.stringify(frameMetaDummyData[tid])); + } + + async function getAnnotations(session, id) { + if (session === 'task') { + return JSON.parse(JSON.stringify(taskAnnotationsDummyData[id])); + } + + if (session === 'job') { + return JSON.parse(JSON.stringify(jobAnnotationsDummyData[id])); + } + + return null; + } + + async function updateAnnotations(session, id, data, action) { + // Actually we do not change our dummy data + // We just update the argument in some way and return it + + data.version += 1; + + if (action === 'create') { + let idGenerator = 1000; + data.tracks.concat(data.tags).concat(data.shapes).map((el) => { + el.id = ++idGenerator; + return el; + }); + + return data; + } + + if (action === 'update') { + return data; + } + + if (action === 'delete') { + return data; + } + + return null; + } + + Object.defineProperties(this, Object.freeze({ + server: { + value: Object.freeze({ + about, + share, + formats, + exception, + login, + logout, + }), + writable: false, + }, + + tasks: { + value: Object.freeze({ + getTasks, + saveTask, + createTask, + deleteTask, + }), + writable: false, + }, + + jobs: { + value: Object.freeze({ + getJob, + saveJob, + }), + writable: false, + }, + + users: { + value: Object.freeze({ + getUsers, + getSelf, + }), + writable: false, + }, + + frames: { + value: Object.freeze({ + getData, + getMeta, + }), + writable: false, + }, + + annotations: { + value: { + updateAnnotations, + getAnnotations, + }, + // To implement on of important tests + writable: true, + }, + })); + } +} + +const serverProxy = new ServerProxy(); +module.exports = serverProxy; diff --git a/cvat-core/webpack.config.js b/cvat-core/webpack.config.js new file mode 100644 index 00000000..9b55ccde --- /dev/null +++ b/cvat-core/webpack.config.js @@ -0,0 +1,72 @@ +/* global + require:true, + __dirname:true, +*/ + +const path = require('path'); + +const nodeConfig = { + target: 'node', + mode: 'production', + devtool: 'source-map', + entry: './src/api.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'cvat-core.node.js', + library: 'cvat', + libraryTarget: 'commonjs', + }, + module: { + rules: [{ + test: /.js?$/, + exclude: /node_modules/, + }], + }, + externals: { + canvas: 'commonjs canvas', + }, + stats: { + warnings: false, + }, +}; + +const webConfig = { + target: 'web', + mode: 'production', + devtool: 'source-map', + entry: './src/api.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'cvat-core.min.js', + library: 'cvat', + libraryTarget: 'window', + }, + module: { + rules: [{ + test: /.js?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: [ + ['@babel/preset-env', { + targets: { + chrome: 58, + }, + useBuiltIns: 'usage', + corejs: 3, + loose: false, + spec: false, + debug: false, + include: [], + exclude: [], + }], + ], + sourceType: 'unambiguous', + }, + }, + }], + }, +}; + +module.exports = [nodeConfig, webConfig]; diff --git a/cvat-ui/.dockerignore b/cvat-ui/.dockerignore new file mode 100644 index 00000000..e3fbd983 --- /dev/null +++ b/cvat-ui/.dockerignore @@ -0,0 +1,2 @@ +build +node_modules diff --git a/cvat-ui/.env b/cvat-ui/.env new file mode 100644 index 00000000..ac71a091 --- /dev/null +++ b/cvat-ui/.env @@ -0,0 +1,9 @@ +REACT_APP_VERSION=${npm_package_version} + +REACT_APP_API_PROTOCOL=http +REACT_APP_API_HOST=localhost +REACT_APP_API_PORT=7000 +REACT_APP_API_HOST_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT} +REACT_APP_API_FULL_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT}/api/v1 + +SKIP_PREFLIGHT_CHECK=true diff --git a/cvat-ui/.env.production b/cvat-ui/.env.production new file mode 100644 index 00000000..d5af5e40 --- /dev/null +++ b/cvat-ui/.env.production @@ -0,0 +1,9 @@ +REACT_APP_VERSION=${npm_package_version} + +REACT_APP_API_PROTOCOL=http +REACT_APP_API_HOST=localhost +REACT_APP_API_PORT=8080 +REACT_APP_API_HOST_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT} +REACT_APP_API_FULL_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT}/api/v1 + +SKIP_PREFLIGHT_CHECK=true diff --git a/cvat-ui/.gitignore b/cvat-ui/.gitignore new file mode 100644 index 00000000..4d29575d --- /dev/null +++ b/cvat-ui/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/cvat-ui/Dockerfile b/cvat-ui/Dockerfile new file mode 100644 index 00000000..1b5ce8eb --- /dev/null +++ b/cvat-ui/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:18.04 AS cvat-ui + +ARG http_proxy +ARG https_proxy +ARG no_proxy +ARG socks_proxy + +ENV TERM=xterm \ + http_proxy=${http_proxy} \ + https_proxy=${https_proxy} \ + no_proxy=${no_proxy} \ + socks_proxy=${socks_proxy} + +ENV LANG='C.UTF-8' \ + LC_ALL='C.UTF-8' + +# Install necessary apt packages +RUN apt update && apt install -yq nodejs npm curl && \ + npm install -g n && n 10.16.3 + +# Create output directory +RUN mkdir /tmp/cvat-ui +WORKDIR /tmp/cvat-ui/ + +# Install dependencies +COPY package*.json /tmp/cvat-ui/ +RUN npm install + +# Build source code +COPY . /tmp/cvat-ui/ +RUN mv .env.production .env && npm run build + +FROM nginx +# Replace default.conf configuration to remove unnecessary rules +COPY react_nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=cvat-ui /tmp/cvat-ui/build /usr/share/nginx/html/ diff --git a/cvat-ui/README.md b/cvat-ui/README.md new file mode 100644 index 00000000..897dc836 --- /dev/null +++ b/cvat-ui/README.md @@ -0,0 +1,44 @@ +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `npm start` + +Runs the app in the development mode.
    +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
    +You will also see any lint errors in the console. + +### `npm test` + +Launches the test runner in the interactive watch mode.
    +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `npm run build` + +Builds the app for production to the `build` folder.
    +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
    +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `npm run eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/cvat-ui/config-overrides.js b/cvat-ui/config-overrides.js new file mode 100644 index 00000000..55538a98 --- /dev/null +++ b/cvat-ui/config-overrides.js @@ -0,0 +1,14 @@ +const { override, fixBabelImports, addLessLoader } = require('customize-cra'); + +module.exports = override( + fixBabelImports('import', { + libraryName: 'antd', + libraryDirectory: 'es', + style: true, + }), + + addLessLoader({ + javascriptEnabled: true, + // modifyVars: { '@primary-color': '#1DA57A' }, + }), +); diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json new file mode 100644 index 00000000..488fa301 --- /dev/null +++ b/cvat-ui/package-lock.json @@ -0,0 +1,14745 @@ +{ + "name": "cvat-ui", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ant-design/colors": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.1.0.tgz", + "integrity": "sha512-Td7g1P53sNFyT4Gya6836e70TrhoVZ+HjZs6mpWIHrxl4/VqsjjOyzj/8ktOuw0lCx+BfYu9UO1CiJ0MoYYfhg==", + "requires": { + "tinycolor2": "^1.4.1" + } + }, + "@ant-design/create-react-context": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ant-design/create-react-context/-/create-react-context-0.2.4.tgz", + "integrity": "sha512-8sw+/w6r+aEbd+OJ62ojoSE4zDt/3yfQydmbWFznoftjr8v/opOswGjM+/MU0rSaREbluqzOmZ6xdecHpSaS2w==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "@ant-design/icons": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.0.1.tgz", + "integrity": "sha512-SqiNhgoivKczEqIJc/9hntgtvmq4R3Ef73ehibqDPAT059IjsXXM7nze0S5P8F4HP76jgPiv5od+2JUhQl/nig==" + }, + "@ant-design/icons-react": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-react/-/icons-react-2.0.1.tgz", + "integrity": "sha512-r1QfoltMuruJZqdiKcbPim3d8LNsVPB733U0gZEUSxBLuqilwsW28K2rCTWSMTjmFX7Mfpf+v/wdiFe/XCqThw==", + "requires": { + "@ant-design/colors": "^3.1.0", + "babel-runtime": "^6.26.0" + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz", + "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helpers": "^7.4.3", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "@babel/generator": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "requires": { + "@babel/types": "^7.4.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", + "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz", + "integrity": "sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw==", + "requires": { + "@babel/types": "^7.3.0", + "esutils": "^2.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", + "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz", + "integrity": "sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==", + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.4.4", + "@babel/helper-split-export-declaration": "^7.4.4" + } + }, + "@babel/helper-define-map": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz", + "integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==", + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/types": "^7.4.4", + "lodash": "^4.17.11" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", + "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", + "requires": { + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", + "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", + "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz", + "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.4.4", + "lodash": "^4.17.11" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" + }, + "@babel/helper-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.4.4.tgz", + "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", + "requires": { + "lodash": "^4.17.11" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz", + "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", + "requires": { + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.2.0" + } + }, + "@babel/helpers": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", + "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==" + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0", + "@babel/plugin-syntax-async-generators": "^7.2.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.0.tgz", + "integrity": "sha512-t2ECPNOXsIeK1JxJNKmgbzQtoG27KIlVE61vTqX0DKR9E9sZlVVxWUtEW9D5FlZ8b8j7SBNCHY47GgPKCKlpPg==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.4.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.0.tgz", + "integrity": "sha512-d08TLmXeK/XbgCo7ZeZ+JaeZDtDai/2ctapTRsWWkkmy7G/cqz8DQN/HlWG7RR4YmfXxmExsbU3SuCjlM7AtUg==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.4.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-decorators": "^7.2.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-json-strings": "^7.2.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", + "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", + "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", + "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", + "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz", + "integrity": "sha512-r6YMuZDWLtLlu0kqIim5o/3TNRAlWb073HwT3e2nKf9I8IIvOggPrnILYPsrrKilmn/mYEMCf/Z07w3yQJF6dg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz", + "integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz", + "integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", + "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz", + "integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.11" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz", + "integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-define-map": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.4.4", + "@babel/helper-split-export-declaration": "^7.4.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", + "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", + "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", + "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.0.tgz", + "integrity": "sha512-C4ZVNejHnfB22vI2TYN4RUp2oCmq6cSEAg4RygSvYZUECRqUu9O4PMEMNJ4wsemaRGg27BbgYctG4BZh+AgIHw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-flow": "^7.2.0" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", + "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", + "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", + "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", + "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", + "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", + "requires": { + "@babel/helper-module-transforms": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", + "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", + "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz", + "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==", + "requires": { + "regexp-tree": "^0.1.6" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", + "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", + "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "requires": { + "@babel/helper-call-delegate": "^7.4.4", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", + "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.2.0.tgz", + "integrity": "sha512-YYQFg6giRFMsZPKUM9v+VcHOdfSQdz9jHCx3akAi3UYgyjndmdYGSXylQ/V+HswQt4fL8IklchD9HTsaOCrWQQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz", + "integrity": "sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz", + "integrity": "sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg==", + "requires": { + "@babel/helper-builder-react-jsx": "^7.3.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz", + "integrity": "sha512-v6S5L/myicZEy+jr6ielB0OR8h+EH/1QFx/YJ7c7Ua+7lqsjj/vW6fD5FR9hB/6y7mGbfT4vAURn3xqBxsUcdg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz", + "integrity": "sha512-A32OkKTp4i5U6aE88GwwcuV4HAprUgHcTq0sSafLxjr6AW0QahrCRCjxogkbbcdtpbXkuTOlgpjophCxb6sh5g==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", + "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", + "requires": { + "regenerator-transform": "^0.14.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", + "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.3.tgz", + "integrity": "sha512-7Q61bU+uEI7bCUFReT1NKn7/X6sDQsZ7wL1sJ9IYMAO7cI+eg6x9re1cEw2fCRMbbTVyoeUKWSV1M6azEfKCfg==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", + "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.4.5.tgz", + "integrity": "sha512-RPB/YeGr4ZrFKNwfuQRlMf2lxoCUaU01MTw39/OFE/RiL8HDjtn68BwEPft1P7JN4akyEmjGWAMNldOV7o9V2g==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-typescript": "^7.2.0" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz", + "integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/preset-env": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.5.tgz", + "integrity": "sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.4.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.4.4", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.4.4", + "@babel/plugin-transform-classes": "^7.4.4", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.4.4", + "@babel/plugin-transform-function-name": "^7.4.4", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-member-expression-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-modules-systemjs": "^7.4.4", + "@babel/plugin-transform-modules-umd": "^7.2.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", + "@babel/plugin-transform-new-target": "^7.4.4", + "@babel/plugin-transform-object-super": "^7.2.0", + "@babel/plugin-transform-parameters": "^7.4.4", + "@babel/plugin-transform-property-literals": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.4.5", + "@babel/plugin-transform-reserved-words": "^7.2.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.2.0", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.4.4", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + } + }, + "@babel/preset-react": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", + "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-transform-react-display-name": "^7.0.0", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "@babel/plugin-transform-react-jsx-self": "^7.0.0", + "@babel/plugin-transform-react-jsx-source": "^7.0.0" + } + }, + "@babel/preset-typescript": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz", + "integrity": "sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-transform-typescript": "^7.3.2" + } + }, + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/types": "^7.4.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + } + }, + "@babel/types": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==" + }, + "@csstools/normalize.css": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-9.0.1.tgz", + "integrity": "sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA==" + }, + "@hapi/address": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.0.0.tgz", + "integrity": "sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw==" + }, + "@hapi/hoek": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-6.2.4.tgz", + "integrity": "sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A==" + }, + "@hapi/joi": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.0.tgz", + "integrity": "sha512-n6kaRQO8S+kepUTbXL9O/UOL788Odqs38/VOfoCrATDtTvyfiO3fgjlSRaNkHabpTLgM7qru9ifqXlXbXk8SeQ==", + "requires": { + "@hapi/address": "2.x.x", + "@hapi/hoek": "6.x.x", + "@hapi/marker": "1.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/marker": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@hapi/marker/-/marker-1.0.0.tgz", + "integrity": "sha512-JOfdekTXnJexfE8PyhZFyHvHjt81rBFSAbTIRAhF2vv/2Y1JzoKsGqxH/GpZJoF7aEfYok8JVcAHmSz1gkBieA==" + }, + "@hapi/topo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.0.tgz", + "integrity": "sha512-gZDI/eXOIk8kP2PkUKjWu9RW8GGVd2Hkgjxyr/S7Z+JF+0mr7bAlbw+DkTRxnD580o8Kqxlnba9wvqp5aOHBww==", + "requires": { + "@hapi/hoek": "6.x.x" + } + }, + "@jest/console": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", + "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "requires": { + "@jest/source-map": "^24.3.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", + "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.8.0", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve-dependencies": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "jest-watcher": "^24.8.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "strip-ansi": "^5.0.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==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "@jest/environment": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", + "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", + "requires": { + "@jest/fake-timers": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0" + } + }, + "@jest/fake-timers": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", + "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", + "requires": { + "@jest/types": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0" + } + }, + "@jest/reporters": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", + "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", + "requires": { + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.1.1", + "jest-haste-map": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.2.1", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + }, + "dependencies": { + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + } + } + }, + "@jest/source-map": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", + "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + } + } + }, + "@jest/test-result": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", + "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/types": "^24.8.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", + "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", + "requires": { + "@jest/test-result": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0" + } + }, + "@jest/transform": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", + "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.8.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-util": "^24.8.0", + "micromatch": "^3.1.10", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", + "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^12.0.9" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz", + "integrity": "sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz", + "integrity": "sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz", + "integrity": "sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.0.tgz", + "integrity": "sha512-3eI17Pb3jlg3oqV4Tie069n1SelYKBUpI90txDcnBWk4EGFW+YQGyQjy6iuJAReH0RnpUJ9jUExrt/xniGvhqw==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz", + "integrity": "sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz", + "integrity": "sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz", + "integrity": "sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw==" + }, + "@svgr/babel-preset": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.0.tgz", + "integrity": "sha512-Lgy1RJiZumGtv6yJroOxzFuL64kG/eIcivJQ7y9ljVWL+0QXvFz4ix1xMrmjMD+rpJWwj50ayCIcFelevG/XXg==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^4.2.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^4.2.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^4.2.0", + "@svgr/babel-plugin-svg-dynamic-title": "^4.3.0", + "@svgr/babel-plugin-svg-em-dimensions": "^4.2.0", + "@svgr/babel-plugin-transform-react-native-svg": "^4.2.0", + "@svgr/babel-plugin-transform-svg-component": "^4.2.0" + } + }, + "@svgr/core": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.0.tgz", + "integrity": "sha512-Ycu1qrF5opBgKXI0eQg3ROzupalCZnSDETKCK/3MKN4/9IEmt3jPX/bbBjftklnRW+qqsCEpO0y/X9BTRw2WBg==", + "requires": { + "@svgr/plugin-jsx": "^4.3.0", + "camelcase": "^5.3.1", + "cosmiconfig": "^5.2.0" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.2.0.tgz", + "integrity": "sha512-IvAeb7gqrGB5TH9EGyBsPrMRH/QCzIuAkLySKvH2TLfLb2uqk98qtJamordRQTpHH3e6TORfBXoTo7L7Opo/Ow==", + "requires": { + "@babel/types": "^7.4.0" + } + }, + "@svgr/plugin-jsx": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.0.tgz", + "integrity": "sha512-0ab8zJdSOTqPfjZtl89cjq2IOmXXUYV3Fs7grLT9ur1Al3+x3DSp2+/obrYKUGbQUnLq96RMjSZ7Icd+13vwlQ==", + "requires": { + "@babel/core": "^7.4.3", + "@svgr/babel-preset": "^4.3.0", + "@svgr/hast-util-to-babel-ast": "^4.2.0", + "rehype-parse": "^6.0.0", + "unified": "^7.1.0", + "vfile": "^4.0.0" + } + }, + "@svgr/plugin-svgo": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.2.0.tgz", + "integrity": "sha512-zUEKgkT172YzHh3mb2B2q92xCnOAMVjRx+o0waZ1U50XqKLrVQ/8dDqTAtnmapdLsGurv8PSwenjLCUpj6hcvw==", + "requires": { + "cosmiconfig": "^5.2.0", + "merge-deep": "^3.0.2", + "svgo": "^1.2.1" + } + }, + "@svgr/webpack": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-4.1.0.tgz", + "integrity": "sha512-d09ehQWqLMywP/PT/5JvXwPskPK9QCXUjiSkAHehreB381qExXf5JFCBWhfEyNonRbkIneCeYM99w+Ud48YIQQ==", + "requires": { + "@babel/core": "^7.1.6", + "@babel/plugin-transform-react-constant-elements": "^7.0.0", + "@babel/preset-env": "^7.1.6", + "@babel/preset-react": "^7.0.0", + "@svgr/core": "^4.1.0", + "@svgr/plugin-jsx": "^4.1.0", + "@svgr/plugin-svgo": "^4.0.3", + "loader-utils": "^1.1.0" + } + }, + "@types/babel__core": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", + "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", + "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/history": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.2.tgz", + "integrity": "sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==" + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==" + }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "24.0.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.13.tgz", + "integrity": "sha512-3m6RPnO35r7Dg+uMLj1+xfZaOgIHHHut61djNjzwExXN4/Pm9has9C6I1KMYSfz7mahDhWUOVg4HW/nZdv5Pww==", + "requires": { + "@types/jest-diff": "*" + } + }, + "@types/jest-diff": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", + "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==" + }, + "@types/node": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.3.tgz", + "integrity": "sha512-zkOxCS/fA+3SsdA+9Yun0iANxzhQRiNwTvJSr6N95JhuJ/x27z9G2URx1Jpt3zYFfCGUXZGL5UDxt5eyLE7wgw==" + }, + "@types/prop-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", + "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==" + }, + "@types/q": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", + "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" + }, + "@types/react": { + "version": "16.8.19", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.19.tgz", + "integrity": "sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-dom": { + "version": "16.8.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.8.4.tgz", + "integrity": "sha512-eIRpEW73DCzPIMaNBDP5pPIpK1KXyZwNgfxiVagb5iGiz6da+9A5hslSX6GAQKdO7SayVCS/Fr2kjqprgAvkfA==", + "requires": { + "@types/react": "*" + } + }, + "@types/react-redux": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.1.tgz", + "integrity": "sha512-owqNahzE8en/jR4NtrUJDJya3tKru7CIEGSRL/pVS84LtSCdSoT7qZTkrbBd3S4Lp11sAp+7LsvxIeONJVKMnw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "@types/react-router": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.0.2.tgz", + "integrity": "sha512-sdMN284GEOcqDEMS/hE/XD06Abw2fws30+xkZf3C9cSRcWopiv/HDTmunYI7DKLYKVRaWFkq1lkuJ6qeYu0E7A==", + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.4.tgz", + "integrity": "sha512-xrwaWHpnxKk/TTRe7pmoGy3E4SyF/ojFqNfFJacw7OLdfLXRvGfk4r/XePVaZNVfeJzL8fcnNilPN7xOdJ/vGw==", + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/react-slick": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.4.tgz", + "integrity": "sha512-vXoIy4GUfB7/YgqubR4H7RALo+pRdMYCeLgWwV3MPwl5pggTlEkFBTF19R7u+LJc85uMqC7RfsbkqPLMQ4ab+A==", + "requires": { + "@types/react": "*" + } + }, + "@types/redux-logger": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/redux-logger/-/redux-logger-3.0.7.tgz", + "integrity": "sha512-oV9qiCuowhVR/ehqUobWWkXJjohontbDGLV88Be/7T4bqMQ3kjXwkFNL7doIIqlbg3X2PC5WPziZ8/j/QHNQ4A==", + "requires": { + "redux": "^3.6.0" + }, + "dependencies": { + "redux": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", + "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", + "requires": { + "lodash": "^4.2.1", + "lodash-es": "^4.2.1", + "loose-envify": "^1.1.0", + "symbol-observable": "^1.0.3" + } + } + } + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" + }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" + }, + "@types/vfile": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/vfile/-/vfile-3.0.2.tgz", + "integrity": "sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw==", + "requires": { + "@types/node": "*", + "@types/unist": "*", + "@types/vfile-message": "*" + } + }, + "@types/vfile-message": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/vfile-message/-/vfile-message-1.0.1.tgz", + "integrity": "sha512-mlGER3Aqmq7bqR1tTTIVHq8KSAFFRyGbrxuM8C/H82g6k7r2fS+IMEkIu3D7JHzG10NvPdR8DNx0jr0pwpp4dA==", + "requires": { + "@types/node": "*", + "@types/unist": "*" + } + }, + "@types/yargs": { + "version": "12.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", + "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", + "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "requires": { + "@typescript-eslint/parser": "1.6.0", + "@typescript-eslint/typescript-estree": "1.6.0", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", + "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "requires": { + "@typescript-eslint/typescript-estree": "1.6.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", + "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + } + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + }, + "acorn-globals": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", + "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + }, + "add-dom-event-listener": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", + "integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==", + "requires": { + "object-assign": "4.x" + } + }, + "address": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz", + "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==" + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==" + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "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==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "antd": { + "version": "3.19.8", + "resolved": "https://registry.npmjs.org/antd/-/antd-3.19.8.tgz", + "integrity": "sha512-sGBJHuzr/7Vm3VJpzt5vjOWjtth8Ld38FneIQeX5OKHudWYH8j+Xt0j4q2sYwOiV9lYFuh4tlZv7/7NFyCO2vw==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "@ant-design/icons": "~2.0.0", + "@ant-design/icons-react": "~2.0.1", + "@types/react-slick": "^0.23.4", + "array-tree-filter": "^2.1.0", + "babel-runtime": "6.x", + "classnames": "~2.2.6", + "copy-to-clipboard": "^3.2.0", + "css-animation": "^1.5.0", + "dom-closest": "^0.2.0", + "enquire.js": "^2.1.6", + "lodash": "^4.17.11", + "moment": "^2.24.0", + "omit.js": "^1.0.2", + "prop-types": "^15.7.2", + "raf": "^3.4.1", + "rc-animate": "^2.8.3", + "rc-calendar": "~9.15.0", + "rc-cascader": "~0.17.4", + "rc-checkbox": "~2.1.6", + "rc-collapse": "~1.11.3", + "rc-dialog": "~7.4.0", + "rc-drawer": "~1.9.8", + "rc-dropdown": "~2.4.1", + "rc-editor-mention": "^1.1.13", + "rc-form": "^2.4.5", + "rc-input-number": "~4.4.5", + "rc-mentions": "~0.3.1", + "rc-menu": "~7.4.23", + "rc-notification": "~3.3.1", + "rc-pagination": "~1.20.1", + "rc-progress": "~2.3.0", + "rc-rate": "~2.5.0", + "rc-select": "~9.1.4", + "rc-slider": "~8.6.11", + "rc-steps": "~3.4.1", + "rc-switch": "~1.9.0", + "rc-table": "~6.6.0", + "rc-tabs": "~9.6.4", + "rc-time-picker": "~3.6.6", + "rc-tooltip": "~3.7.3", + "rc-tree": "~2.1.0", + "rc-tree-select": "~2.9.1", + "rc-trigger": "^2.6.2", + "rc-upload": "~2.6.7", + "rc-util": "^4.6.0", + "react-lazy-load": "^3.0.13", + "react-lifecycles-compat": "^3.0.4", + "react-slick": "~0.24.0", + "resize-observer-polyfill": "^1.5.1", + "shallowequal": "^1.1.0", + "warning": "~4.0.3" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=" + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=" + }, + "array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "requires": { + "babel-runtime": "6.x" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "autoprefixer": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.0.tgz", + "integrity": "sha512-kuip9YilBqhirhHEGHaBTZKXL//xxGnzvsD0FtBQa6z+A69qZD6s/BAX9VzDF1i9VKDquTJDQaPLSEhOnL6FvQ==", + "requires": { + "browserslist": "^4.6.1", + "caniuse-lite": "^1.0.30000971", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.16", + "postcss-value-parser": "^3.3.1" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "axobject-query": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", + "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "babel-eslint": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", + "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "babel-extract-comments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", + "integrity": "sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ==", + "requires": { + "babylon": "^6.18.0" + } + }, + "babel-jest": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", + "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", + "requires": { + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.6.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-loader": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz", + "integrity": "sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==", + "requires": { + "find-cache-dir": "^2.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1", + "util.promisify": "^1.0.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.2.0.tgz", + "integrity": "sha512-fP899ELUnTaBcIzmrW7nniyqqdYWrWuJUyPWHxFa/c7r7hS6KC8FscNfLlBNIoPSc55kYMGEEKjPjJGCLbE1qA==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-import": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/babel-plugin-import/-/babel-plugin-import-1.12.0.tgz", + "integrity": "sha512-3Fo7sJ2Hm71y1VJS7eMA/E7J5+roKJmzwia5BxzUQREBs6CRylwtvQq8m39W8nplG4Y7rZwOCndh5MzRTSmHpA==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/runtime": "^7.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", + "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", + "requires": { + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", + "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-macros": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz", + "integrity": "sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q==", + "requires": { + "@babel/runtime": "^7.4.2", + "cosmiconfig": "^5.2.0", + "resolve": "^1.10.0" + } + }, + "babel-plugin-named-asset-import": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.2.tgz", + "integrity": "sha512-CxwvxrZ9OirpXQ201Ec57OmGhmI8/ui/GwTDy0hSp6CmRvgRC0pSair6Z04Ck+JStA0sMPZzSJ3uE4n17EXpPQ==" + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=" + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-jest": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", + "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.6.0" + } + }, + "babel-preset-react-app": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-9.0.0.tgz", + "integrity": "sha512-YVsDA8HpAKklhFLJtl9+AgaxrDaor8gGvDFlsg1ByOS0IPGUovumdv4/gJiAnLcDmZmKlH6+9sVOz4NVW7emAg==", + "requires": { + "@babel/core": "7.4.3", + "@babel/plugin-proposal-class-properties": "7.4.0", + "@babel/plugin-proposal-decorators": "7.4.0", + "@babel/plugin-proposal-object-rest-spread": "7.4.3", + "@babel/plugin-syntax-dynamic-import": "7.2.0", + "@babel/plugin-transform-classes": "7.4.3", + "@babel/plugin-transform-destructuring": "7.4.3", + "@babel/plugin-transform-flow-strip-types": "7.4.0", + "@babel/plugin-transform-react-constant-elements": "7.2.0", + "@babel/plugin-transform-react-display-name": "7.2.0", + "@babel/plugin-transform-runtime": "7.4.3", + "@babel/preset-env": "7.4.3", + "@babel/preset-react": "7.0.0", + "@babel/preset-typescript": "7.3.3", + "@babel/runtime": "7.4.3", + "babel-plugin-dynamic-import-node": "2.2.0", + "babel-plugin-macros": "2.5.1", + "babel-plugin-transform-react-remove-prop-types": "0.4.24" + }, + "dependencies": { + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.3.tgz", + "integrity": "sha512-xC//6DNSSHVjq8O2ge0dyYlhshsH4T7XdCVoxbi5HzLYWfsC5ooFlJjrXk8RcAT+hjHAK9UjBXdylzSoDK3t4g==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.3.tgz", + "integrity": "sha512-PUaIKyFUDtG6jF5DUJOfkBdwAS/kFFV3XFk7Nn0a6vR7ZT8jYw5cGtIlat77wcnd0C6ViGqo/wyNf4ZHytF/nQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-define-map": "^7.4.0", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.4.0", + "@babel/helper-split-export-declaration": "^7.4.0", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.3.tgz", + "integrity": "sha512-rVTLLZpydDFDyN4qnXdzwoVpk1oaXHIvPEOkOLyr88o7oHxVc/LyrnDx+amuBWGOwUb7D1s/uLsKBNTx08htZg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/preset-env": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.3.tgz", + "integrity": "sha512-FYbZdV12yHdJU5Z70cEg0f6lvtpZ8jFSDakTm7WXeJbLXh4R0ztGEu/SW7G1nJ2ZvKwDhz8YrbA84eYyprmGqw==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.4.3", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.0", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.4.0", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.4.0", + "@babel/plugin-transform-classes": "^7.4.3", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.4.3", + "@babel/plugin-transform-dotall-regex": "^7.4.3", + "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.4.3", + "@babel/plugin-transform-function-name": "^7.4.3", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-member-expression-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.4.3", + "@babel/plugin-transform-modules-systemjs": "^7.4.0", + "@babel/plugin-transform-modules-umd": "^7.2.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.2", + "@babel/plugin-transform-new-target": "^7.4.0", + "@babel/plugin-transform-object-super": "^7.2.0", + "@babel/plugin-transform-parameters": "^7.4.3", + "@babel/plugin-transform-property-literals": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.4.3", + "@babel/plugin-transform-reserved-words": "^7.2.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.2.0", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.2.0", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.4.3", + "@babel/types": "^7.4.0", + "browserslist": "^4.5.2", + "core-js-compat": "^3.0.0", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + } + }, + "@babel/runtime": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz", + "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "bail": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.4.tgz", + "integrity": "sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.3.tgz", + "integrity": "sha512-CNBqTCq22RKM8wKJNowcqihHJ4SkI8CGeK7KOR9tPboXUuS5Zk5lQgzzTbs4oxD8x+6HUshZUa2OyNI9lR93bQ==", + "requires": { + "caniuse-lite": "^1.0.30000975", + "electron-to-chromium": "^1.3.164", + "node-releases": "^1.1.23" + } + }, + "bser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", + "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz", + "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + } + } + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30000978", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000978.tgz", + "integrity": "sha512-H6gK6kxUzG6oAwg/Jal279z8pHw0BzrpZfwo/CA9FFm/vA0l8IhDfkZtepyJNE2Y4V6Dp3P3ubz6czby1/Mgsw==" + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "requires": { + "rsvp": "^4.8.4" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz", + "integrity": "sha512-u5ElzokS8A1pm9vM3/iDgTcI3xqHxuCao94Oz8etI3cf0Tio0p8izkDYbTIn09uP3yUUr6+veaE6IkjnTYS46g==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "ccount": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.4.tgz", + "integrity": "sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w==" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chokidar": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "optional": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "requires": { + "source-map": "~0.6.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", + "requires": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "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==", + "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=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "comma-separated-tokens": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.7.tgz", + "integrity": "sha512-Jrx3xsP4pPv4AwJUDWY9wOXGtwPXARej6Xd99h4TUGotmf8APuquKMpK+dnD3UgyxK7OEWaisjZz+3b5jtL6xQ==" + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-classes": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz", + "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=", + "requires": { + "component-indexof": "0.0.3" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "component-indexof": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz", + "integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ=" + }, + "compressible": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", + "requires": { + "mime-db": ">= 1.40.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "confusing-browser-globals": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.7.tgz", + "integrity": "sha512-cgHI1azax5ATrZ8rJ+ODDML9Fvu67PimB6aNxBrc/QwSaDaM9eTfIEUHx3bBLJJ82ioSb+/5zfsMCCEJax3ByQ==" + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "^0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "copy-to-clipboard": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", + "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" + }, + "core-js-compat": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.4.tgz", + "integrity": "sha512-Z5zbO9f1d0YrJdoaQhphVAnKPimX92D6z8lCGphH89MNRxlL1prI9ExJPqVwP0/kgkQCv8c4GJGT8X16yUncOg==", + "requires": { + "browserslist": "^4.6.2", + "core-js-pure": "3.1.4", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", + "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==" + } + } + }, + "core-js-pure": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.4.tgz", + "integrity": "sha512-uJ4Z7iPNwiu1foygbcZYJsJs1jiXrTTCvxfLDXNhI/I+NHbSIEyr548y4fcsCEyWY0XgfAG/qqaunJ1SThHenA==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "create-react-context": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.2.tgz", + "integrity": "sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==", + "requires": { + "fbjs": "^0.8.0", + "gud": "^1.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-animation": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/css-animation/-/css-animation-1.5.0.tgz", + "integrity": "sha512-hWYoWiOZ7Vr20etzLh3kpWgtC454tW5vn4I6rLANDgpzNSkO7UfOqyCEeaoBSG9CYWQpRkFWTWbWW8o3uZrNLw==", + "requires": { + "babel-runtime": "6.x", + "component-classes": "^1.2.5" + } + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + } + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "css-loader": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", + "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", + "requires": { + "camelcase": "^5.2.0", + "icss-utils": "^4.1.0", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.14", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^2.0.6", + "postcss-modules-scope": "^2.1.0", + "postcss-modules-values": "^2.0.0", + "postcss-value-parser": "^3.3.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-select": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", + "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^2.1.2", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.0.0-alpha.28", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", + "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", + "requires": { + "mdn-data": "~1.1.0", + "source-map": "^0.5.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "css-unit-converter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", + "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=" + }, + "css-url-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", + "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=" + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=" + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=" + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "requires": { + "postcss": "^7.0.0" + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" + }, + "csso": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", + "requires": { + "css-tree": "1.0.0-alpha.29" + }, + "dependencies": { + "css-tree": { + "version": "1.0.0-alpha.29", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", + "requires": { + "mdn-data": "~1.1.0", + "source-map": "^0.5.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "cssom": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" + }, + "cssstyle": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", + "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "requires": { + "cssom": "0.3.x" + } + }, + "csstype": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.5.tgz", + "integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "customize-cra": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/customize-cra/-/customize-cra-0.2.14.tgz", + "integrity": "sha512-LtEMXNzkhnnqGPc1dP5fnPlF1ic1dj34hDbRVJIzfMQgOaGByHhx51fTR7fv7sTPEbCPrOBP777MkCo0GPV57g==", + "requires": { + "lodash.flow": "^3.5.0" + } + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" + }, + "damerau-levenshtein": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz", + "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" + }, + "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=" + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "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==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "diff-sequences": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", + "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "docopt": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/docopt/-/docopt-0.6.2.tgz", + "integrity": "sha1-so6eIiDaXsSffqW7JKR3h0Be6xE=" + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-align": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.8.3.tgz", + "integrity": "sha512-thE1qB8mvtRZgwN4+IGFz1rv7zVsr08c2/IEYtOJIeTzW4YDadIOd5nQ4BpiiAvUWg55xTeGq7zLTDxDYWDrnw==" + }, + "dom-closest": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz", + "integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=", + "requires": { + "dom-matches": ">=1.0.1" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "dom-matches": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz", + "integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw=" + }, + "dom-scroll-into-view": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz", + "integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4=" + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", + "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" + }, + "dotenv-expand": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-4.2.0.tgz", + "integrity": "sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU=" + }, + "draft-js": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz", + "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==", + "requires": { + "fbjs": "^0.8.15", + "immutable": "~3.7.4", + "object-assign": "^4.1.0" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "ejs": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz", + "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==" + }, + "electron-to-chromium": { + "version": "1.3.174", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.174.tgz", + "integrity": "sha512-OEh3EARo2B07ZRtxB0u9GqWyWmTeNS+diMp5bjw4kqMjgpzqM0w1zUOyErDsyWxTdArbvZ79T/w5n3WsBVHLfA==" + }, + "elliptic": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", + "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ=" + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.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==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "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=" + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + } + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "import-fresh": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "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==" + } + } + }, + "eslint-config-react-app": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-4.0.1.tgz", + "integrity": "sha512-ZsaoXUIGsK8FCi/x4lT2bZR5mMkL/Kgj+Lnw690rbvvUr/uiwgFiD8FcfAhkCycm7Xte6O5lYz4EqMx2vX7jgw==", + "requires": { + "confusing-browser-globals": "^1.0.7" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "eslint-loader": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.2.tgz", + "integrity": "sha512-rA9XiXEOilLYPOIInvVH5S/hYfyTPyxag6DZhoQOduM+3TkghAEQ3VcFO8VnX4J4qg/UIBzp72aOf/xvYmpmsg==", + "requires": { + "loader-fs-cache": "^1.0.0", + "loader-utils": "^1.0.2", + "object-assign": "^4.0.1", + "object-hash": "^1.1.4", + "rimraf": "^2.6.1" + } + }, + "eslint-module-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz", + "integrity": "sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==", + "requires": { + "debug": "^2.6.8", + "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==", + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "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=", + "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=" + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "2.50.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.50.1.tgz", + "integrity": "sha512-9kRxF9hfM/O6WGZcZPszOVPd2W0TLHBtceulLTsGfwMPtiCCLnCW0ssRiOOiXyqrCA20pm1iXdXm7gQeN306zQ==", + "requires": { + "lodash": "^4.17.10" + } + }, + "eslint-plugin-import": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz", + "integrity": "sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A==", + "requires": { + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.3.0", + "has": "^1.0.3", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "read-pkg-up": "^2.0.0", + "resolve": "^1.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "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=", + "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=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "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=", + "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=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "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=", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz", + "integrity": "sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w==", + "requires": { + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.2", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^7.0.2", + "has": "^1.0.3", + "jsx-ast-utils": "^2.0.1" + } + }, + "eslint-plugin-react": { + "version": "7.12.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz", + "integrity": "sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ==", + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.0.1", + "object.fromentries": "^2.0.0", + "prop-types": "^15.6.2", + "resolve": "^1.9.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.1.tgz", + "integrity": "sha512-wHhmGJyVuijnYIJXZJHDUF2WM+rJYTjulUTqF9k61d3BTk8etydz+M4dXUVH7M76ZRS85rqBTCx0Es/lLsrjnA==" + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==" + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "eventlistener": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz", + "integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg=" + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==" + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "expect": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", + "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", + "requires": { + "@jest/types": "^24.8.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-regex-util": "^24.3.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "requires": { + "bser": "^2.0.0" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + } + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==" + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=" + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "requires": { + "debug": "^3.2.6" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "fork-ts-checker-webpack-plugin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.1.1.tgz", + "integrity": "sha512-gqWAEMLlae/oeVnN6RWCAhesOJMswAN1MaKNqhhjXHV5O0/rTUjWI4UbgQHdlrVbCnb+xLotXmJbBlC66QmpFw==", + "requires": { + "babel-code-frame": "^6.22.0", + "chalk": "^2.4.1", + "chokidar": "^2.0.4", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.6.tgz", + "integrity": "sha512-vfmKZp3XPM36DNF0qhW+Cdxk7xm7gTEHY1clv1Xq1arwRQuKZgAhw+NZNWbJBtuaNxzNXwhfdPYRrvIbjfS33A==", + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "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=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", + "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "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": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "requires": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "dependencies": { + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + } + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, + "gzip-size": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz", + "integrity": "sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==", + "requires": { + "duplexer": "^0.1.1", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==" + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "harmony-reflect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz", + "integrity": "sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hast-util-from-parse5": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.1.tgz", + "integrity": "sha512-UfPzdl6fbxGAxqGYNThRUhRlDYY7sXu6XU9nQeX4fFZtV+IHbyEJtd+DUuwOqNV4z3K05E/1rIkoVr/JHmeWWA==", + "requires": { + "ccount": "^1.0.3", + "hastscript": "^5.0.0", + "property-information": "^5.0.0", + "web-namespaces": "^1.1.2", + "xtend": "^4.0.1" + } + }, + "hast-util-parse-selector": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.2.tgz", + "integrity": "sha512-jIMtnzrLTjzqgVEQqPEmwEZV+ea4zHRFTP8Z2Utw0I5HuBOXHzUPPQWr6ouJdJqDKLbFU/OEiYwZ79LalZkmmw==" + }, + "hastscript": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.0.tgz", + "integrity": "sha512-7mOQX5VfVs/gmrOGlN8/EDfp1GqV6P3gTNVt+KnX4gbYhpASTM8bklFdFQCbFRAadURXAmw0R1QQdBdqp7jswQ==", + "requires": { + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.2.0", + "property-information": "^5.0.1", + "space-separated-tokens": "^1.0.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" + }, + "history": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz", + "integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^2.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^0.4.0" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + } + } + }, + "html-webpack-plugin": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz", + "integrity": "sha512-y5l4lGxOW3pz3xBTFdfB9rnnrWRPVxlAhX6nrBYIcW+2k2zC3mSp/3DxlWVCMBfnO6UAnoF8OcFn0IMy6kaKAQ==", + "requires": { + "html-minifier": "^3.5.20", + "loader-utils": "^1.1.0", + "lodash": "^4.17.11", + "pretty-error": "^2.1.1", + "tapable": "^1.1.0", + "util.promisify": "1.0.0" + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-parser-js": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "requires": { + "postcss": "^7.0.14" + } + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true + }, + "immer": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", + "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==" + }, + "immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks=" + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.4.1.tgz", + "integrity": "sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "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==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "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=" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "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==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "is-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.0.0.tgz", + "integrity": "sha512-F/pJIk8QD6OX5DNhRB7hWamLsUilmkDGho48KbgZ6xg/lmAZXHxzXQ91jzB3yRSw5kdQGGGc4yz8HYhTYIMWPg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "ismobilejs": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-0.5.2.tgz", + "integrity": "sha512-ta9UdV60xVZk/ZafFtSFslQaE76SvNkcs1r73d2PVR21zVzx9xuYv9tNe4MxA1NN7WoeCc2RjGot3Bz1eHDx3Q==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", + "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==" + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "requires": { + "handlebars": "^4.1.2" + } + }, + "jest": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.7.1.tgz", + "integrity": "sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA==", + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.7.1" + }, + "dependencies": { + "jest-cli": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", + "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "requires": { + "@jest/core": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + } + } + } + }, + "jest-changed-files": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", + "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", + "requires": { + "@jest/types": "^24.8.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", + "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.8.0", + "@jest/types": "^24.8.0", + "babel-jest": "^24.8.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.8.0", + "jest-environment-node": "^24.8.0", + "jest-get-type": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.8.0", + "realpath-native": "^1.1.0" + }, + "dependencies": { + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + } + } + }, + "jest-diff": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", + "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.3.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-docblock": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", + "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", + "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", + "requires": { + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-environment-jsdom": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", + "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", + "requires": { + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-jsdom-fourteen": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-0.1.0.tgz", + "integrity": "sha512-4vtoRMg7jAstitRzL4nbw83VmGH8Rs13wrND3Ud2o1fczDhMUF32iIrNKwYGgeOPUdfvZU4oy8Bbv+ni1fgVCA==", + "requires": { + "jest-mock": "^24.5.0", + "jest-util": "^24.5.0", + "jsdom": "^14.0.0" + }, + "dependencies": { + "jsdom": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz", + "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==", + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.3", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "jest-environment-node": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", + "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", + "requires": { + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0" + } + }, + "jest-get-type": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", + "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==" + }, + "jest-haste-map": { + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.1.tgz", + "integrity": "sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==", + "requires": { + "@jest/types": "^24.8.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "optional": true + } + } + } + } + }, + "jest-jasmine2": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", + "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.8.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", + "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", + "requires": { + "pretty-format": "^24.8.0" + } + }, + "jest-matcher-utils": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", + "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.8.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" + } + }, + "jest-message-util": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", + "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", + "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", + "requires": { + "@jest/types": "^24.8.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==" + }, + "jest-regex-util": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", + "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==" + }, + "jest-resolve": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", + "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", + "requires": { + "@jest/types": "^24.7.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", + "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", + "requires": { + "@jest/types": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.8.0" + } + }, + "jest-runner": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", + "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.8.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-leak-detector": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + }, + "dependencies": { + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + } + } + }, + "jest-runtime": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", + "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/yargs": "^12.0.2", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^12.0.2" + }, + "dependencies": { + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + } + } + }, + "jest-serializer": { + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", + "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==" + }, + "jest-snapshot": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", + "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "expect": "^24.8.0", + "jest-diff": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.8.0", + "semver": "^5.5.0" + }, + "dependencies": { + "jest-resolve": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "requires": { + "@jest/types": "^24.8.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + } + } + }, + "jest-util": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", + "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", + "requires": { + "@jest/console": "^24.7.1", + "@jest/fake-timers": "^24.8.0", + "@jest/source-map": "^24.3.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + } + } + }, + "jest-validate": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", + "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", + "requires": { + "@jest/types": "^24.8.0", + "camelcase": "^5.0.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.8.0", + "leven": "^2.1.0", + "pretty-format": "^24.8.0" + } + }, + "jest-watch-typeahead": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.3.0.tgz", + "integrity": "sha512-+uOtlppt9ysST6k6ZTqsPI0WNz2HLa8bowiZylZoQCQaAVn7XsVmHhZREkz73FhKelrFrpne4hQQjdq42nFEmA==", + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.4.1", + "jest-watcher": "^24.3.0", + "slash": "^2.0.0", + "string-length": "^2.0.0", + "strip-ansi": "^5.0.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==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "jest-watcher": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", + "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", + "requires": { + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "@types/yargs": "^12.0.9", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.8.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.6.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", + "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "requires": { + "merge-stream": "^1.0.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "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==" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "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=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", + "requires": { + "string-convert": "^0.2.0" + } + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsx-ast-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.0.tgz", + "integrity": "sha512-yAmhGSzR7TsD0OQpu1AGLz8Bx84cxMqtgoJrufomY6BlveEDlREhvu1rea21936xbe5tlUh7IPda82m5ae0H8Q==", + "requires": { + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + } + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "requires": { + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + }, + "less": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", + "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + } + }, + "less-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", + "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^4.0.1" + } + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "loader-fs-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz", + "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==", + "requires": { + "find-cache-dir": "^0.1.1", + "mkdirp": "0.5.1" + }, + "dependencies": { + "find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "requires": { + "find-up": "^1.0.0" + } + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash-es": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", + "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=" + }, + "lodash.template": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "requires": { + "lodash._reinterpolate": "~3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "requires": { + "lodash._reinterpolate": "~3.0.0" + } + }, + "lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "loglevel": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz", + "integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "requires": { + "tmpl": "1.0.x" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + } + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "merge-deep": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz", + "integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==", + "requires": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "merge2": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mini-create-react-context": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz", + "integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==", + "requires": { + "@babel/runtime": "^7.4.0", + "gud": "^1.0.0", + "tiny-warning": "^1.0.2" + } + }, + "mini-css-extract-plugin": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", + "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==", + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + } + }, + "mini-store": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mini-store/-/mini-store-2.0.0.tgz", + "integrity": "sha512-EG0CuwpQmX+XL4QVS0kxNwHW5ftSbhygu1qxQH0pipugjnPkbvkalCdQbEihMwtQY6d3MTN+MS0q+aurs+RfLQ==", + "requires": { + "hoist-non-react-statics": "^2.3.1", + "prop-types": "^15.6.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.0.2" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=" + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "mutationobserver-shim": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", + "integrity": "sha512-gciOLNN8Vsf7YzcqRjKzlAJ6y7e+B86u7i3KXes0xfxx/nfLmozlW1Vn+Sc9x3tPIePFgc1AeIFhtRgkqTjzDQ==" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" + }, + "node-notifier": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", + "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node-releases": { + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.23.tgz", + "integrity": "sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w==", + "requires": { + "semver": "^5.3.0" + } + }, + "node-sass": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.11", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "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==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.fromentries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", + "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.11.0", + "function-bind": "^1.1.1", + "has": "^1.0.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "omit.js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.2.tgz", + "integrity": "sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ==", + "requires": { + "babel-runtime": "^6.23.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", + "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + } + } + }, + "optimize-css-assets-webpack-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-Rqm6sSjWtx9FchdP0uzTQDc7GXDKnwVEGoSxjezPkzMewx7gEWE9IMUYKmigTRC4U3RaNSwYVnUDLuIdtTpm0A==", + "requires": { + "cssnano": "^4.1.0", + "last-call-webpack-plugin": "^3.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==" + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=" + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + } + } + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "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=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "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==" + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "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=", + "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=" + } + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "pnp-webpack-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.2.1.tgz", + "integrity": "sha512-W6GctK7K2qQiVR+gYSv/Gyt6jwwIH4vwdviFqx+Y2jAtVf5eZyYIDf5Ac2NCDMBiX5yWscBLZElPTsyA1UtVVA==", + "requires": { + "ts-pnp": "^1.0.0" + } + }, + "portfinder": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", + "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.1.tgz", + "integrity": "sha512-L2YKB3vF4PetdTIthQVeT+7YiSzMoNMLLYxPXXppOOP7NoazEAy45sh2LvJ8leCQjfBcfkYQs8TtCcQjeZTp8A==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-browser-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-2.0.0.tgz", + "integrity": "sha512-xGG0UvoxwBc4Yx4JX3gc0RuDl1kc4bVihCzzk6UC72YPfq5fu3c717Nu8Un3nvnq1BJ31gBnFXIG/OaUTnpHgA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-calc": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", + "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss": "^7.0.5", + "postcss-selector-parser": "^5.0.0-rc.4", + "postcss-value-parser": "^3.3.1" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "requires": { + "postcss": "^7.0.14" + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz", + "integrity": "sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-font-variant": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz", + "integrity": "sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-initial": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.0.tgz", + "integrity": "sha512-WzrqZ5nG9R9fUtrA+we92R4jhVvEB32IIRTzfIG/PLL8UV4CvbF1ugTEHEFX6vWxl41Xt5RTCJPEZkuWzrOM+Q==", + "requires": { + "lodash.template": "^4.2.4", + "postcss": "^7.0.2" + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", + "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^3.3.1" + } + }, + "postcss-modules-scope": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", + "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", + "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^7.0.6" + } + }, + "postcss-nesting": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.0.tgz", + "integrity": "sha512-WSsbVd5Ampi3Y0nk/SKr5+K34n52PqMqEfswu6RtU4r7wA8vSD+gM8/D9qq4aJkHImwn1+9iEFTbjoWsQeqtaQ==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-normalize": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-7.0.1.tgz", + "integrity": "sha512-NOp1fwrG+6kVXWo7P9SizCHX6QvioxFD/hZcI2MLxPmVnFJFC0j0DDpIuNw2tUDeCFMni59gCVgeJ1/hYhj2OQ==", + "requires": { + "@csstools/normalize.css": "^9.0.1", + "browserslist": "^4.1.1", + "postcss": "^7.0.2", + "postcss-browser-comments": "^2.0.0" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + } + }, + "postcss-preset-env": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.6.0.tgz", + "integrity": "sha512-I3zAiycfqXpPIFD6HXhLfWXIewAWO8emOKz+QSsxaUZb9Dp8HbF5kUf+4Wy/AxR33o+LRoO8blEWCHth0ZsCLA==", + "requires": { + "autoprefixer": "^9.4.9", + "browserslist": "^4.4.2", + "caniuse-lite": "^1.0.30000939", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.3.0", + "postcss": "^7.0.14", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.2", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.7", + "postcss-custom-properties": "^8.0.9", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-safe-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", + "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", + "requires": { + "postcss": "^7.0.0" + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-not": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz", + "integrity": "sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", + "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "requires": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "pretty-bytes": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.2.0.tgz", + "integrity": "sha512-ujANBhiUsl9AhREUDUEY1GPOharMGm8x8juS7qOHybcLi7XsKfrYQ88hSly1l2i0klXHTDYrlL8ihMCG55Dc3w==" + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "pretty-format": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", + "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", + "requires": { + "@jest/types": "^24.8.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + }, + "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==" + } + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "prompts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", + "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", + "requires": { + "kleur": "^3.0.2", + "sisteransi": "^1.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "property-information": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.1.0.tgz", + "integrity": "sha512-tODH6R3+SwTkAQckSp2S9xyYX8dEKYkeXw+4TmJzTxnNzd6mQPu1OD4f9zPrvw/Rm4wpPgI+Zp63mNSGNzUgHg==", + "requires": { + "xtend": "^4.0.1" + } + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.33.tgz", + "integrity": "sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "query-string": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.1.tgz", + "integrity": "sha512-g6y0Lbq10a5pPQpjlFuojfMfV1Pd2Jw9h75ypiYPPia3Gcq2rgkKiIwbkS6JxH7c5f5u/B/sB+d13PU+g1eu4Q==", + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + } + } + }, + "rc-align": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-2.4.5.tgz", + "integrity": "sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==", + "requires": { + "babel-runtime": "^6.26.0", + "dom-align": "^1.7.0", + "prop-types": "^15.5.8", + "rc-util": "^4.0.4" + } + }, + "rc-animate": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.8.3.tgz", + "integrity": "sha512-VPSHJF/PW9zrPVCdQ94/YOI2lFfJVlaiAeQveJN2nlPVMivgvXkuFJyfe42GbZqm+qlnRjH9B4WbY9rCZz9miw==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "css-animation": "^1.3.2", + "prop-types": "15.x", + "raf": "^3.4.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-calendar": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/rc-calendar/-/rc-calendar-9.15.1.tgz", + "integrity": "sha512-cJcq3DnNYrYvDi0CvoguRf2eBGN/E+svs1Dm3cPEsi4yVhxMjNGdq/5Iyxa84dCOWOJHV5NcecUIasfwvaTuoQ==", + "requires": { + "babel-runtime": "6.x", + "classnames": "2.x", + "moment": "2.x", + "prop-types": "^15.5.8", + "rc-trigger": "^2.2.0", + "rc-util": "^4.1.1", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-cascader": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-0.17.4.tgz", + "integrity": "sha512-CeFQJIMzY7x++uPqlx4Xl/cH8iTs8nRoW522+DLb21kdL5kWqKlK+3iHXExoxcAymjwo5ScIiXi+NY4m8Pgq9w==", + "requires": { + "array-tree-filter": "^2.1.0", + "prop-types": "^15.5.8", + "rc-trigger": "^2.2.0", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.4", + "shallow-equal": "^1.0.0", + "warning": "^4.0.1" + } + }, + "rc-checkbox": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.1.7.tgz", + "integrity": "sha512-8L+0XuucUOMUM6F/7qH+hnQpEHPZfW1Um02lUHEVdpZNor5mC0Fj4x8GvTtwcM1pAl5tD3I6lHYD8cE1W8RZJw==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "prop-types": "15.x", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-collapse": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-1.11.3.tgz", + "integrity": "sha512-yECQX2iDPWnKcVi3Wz5bomZuJ2u+wv+kGxuKo2GIRz7Brh9jkGQz5ElghCV1jqDGnzy8GIRxxHHSwlSgdxdUog==", + "requires": { + "classnames": "2.x", + "css-animation": "1.x", + "prop-types": "^15.5.6", + "rc-animate": "2.x", + "react-is": "^16.7.0", + "shallowequal": "^1.1.0" + } + }, + "rc-dialog": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-7.4.1.tgz", + "integrity": "sha512-vvVVP7AUjxs2AEGL5GUr6BjfVzaiBV5RoiPYchCDqHmf8n7xTrfsACAhZ2Vezj6mtl2446zhxoGvhxNpyCyX7A==", + "requires": { + "babel-runtime": "6.x", + "rc-animate": "2.x", + "rc-util": "^4.4.0" + } + }, + "rc-drawer": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-1.9.9.tgz", + "integrity": "sha512-4oG0okZ7JhOTnGHRkxhOO1yb1U13v5ocns+40xmfogdD+oVNTKHIamCU1cKVVcMQYWpUCn8aYbawY2JuuGN/pA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "prop-types": "^15.5.0", + "rc-util": "^4.5.1" + } + }, + "rc-dropdown": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-2.4.1.tgz", + "integrity": "sha512-p0XYn0wrOpAZ2fUGE6YJ6U8JBNc5ASijznZ6dkojdaEfQJAeZtV9KMEewhxkVlxGSbbdXe10ptjBlTEW9vEwEg==", + "requires": { + "babel-runtime": "^6.26.0", + "classnames": "^2.2.6", + "prop-types": "^15.5.8", + "rc-trigger": "^2.5.1", + "react-lifecycles-compat": "^3.0.2" + } + }, + "rc-editor-core": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/rc-editor-core/-/rc-editor-core-0.8.10.tgz", + "integrity": "sha512-T3aHpeMCIYA1sdAI7ynHHjXy5fqp83uPlD68ovZ0oClTSc3tbHmyCxXlA+Ti4YgmcpCYv7avF6a+TIbAka53kw==", + "requires": { + "babel-runtime": "^6.26.0", + "classnames": "^2.2.5", + "draft-js": "^0.10.0", + "immutable": "^3.7.4", + "lodash": "^4.16.5", + "prop-types": "^15.5.8", + "setimmediate": "^1.0.5" + } + }, + "rc-editor-mention": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/rc-editor-mention/-/rc-editor-mention-1.1.13.tgz", + "integrity": "sha512-3AOmGir91Fi2ogfRRaXLtqlNuIwQpvla7oUnGHS1+3eo7b+fUp5IlKcagqtwUBB5oDNofoySXkLBxzWvSYNp/Q==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "^2.2.5", + "dom-scroll-into-view": "^1.2.0", + "draft-js": "~0.10.0", + "immutable": "~3.7.4", + "prop-types": "^15.5.8", + "rc-animate": "^2.3.0", + "rc-editor-core": "~0.8.3" + } + }, + "rc-form": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/rc-form/-/rc-form-2.4.6.tgz", + "integrity": "sha512-yv+G1X0lJODswtrbrsa5bkicwk8NoIQmsOADYTzGlzOtvlwsdSLS02YEBONSi9tWTACgwFLY0ifERN6CJFF9dg==", + "requires": { + "async-validator": "~1.8.5", + "babel-runtime": "6.x", + "create-react-class": "^15.5.3", + "dom-scroll-into-view": "1.x", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.4", + "warning": "^4.0.3" + } + }, + "rc-hammerjs": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/rc-hammerjs/-/rc-hammerjs-0.6.9.tgz", + "integrity": "sha512-4llgWO3RgLyVbEqUdGsDfzUDqklRlQW5VEhE3x35IvhV+w//VPRG34SBavK3D2mD/UaLKaohgU41V4agiftC8g==", + "requires": { + "babel-runtime": "6.x", + "hammerjs": "^2.0.8", + "prop-types": "^15.5.9" + } + }, + "rc-input-number": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-4.4.5.tgz", + "integrity": "sha512-Dt20e8Ylc/N/6oXiPUlwDVdx3fz7W5umUOa4z5pBuWFG7NPlBVXRWkq7+nbnTyaK24UxN67PVpmD3+Omo+QRZQ==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.0", + "prop-types": "^15.5.7", + "rc-util": "^4.5.1", + "rmc-feedback": "^2.0.0" + } + }, + "rc-mentions": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-0.3.1.tgz", + "integrity": "sha512-fa5dN3IMTahJfAga1nmma9OymK/ZBV/MZfV11h4kjDmCAVETv5EbAlV0mn6Y+JajvXS6n/XFoPUSF+nwK/AeWw==", + "requires": { + "@ant-design/create-react-context": "^0.2.4", + "babel-runtime": "^6.23.0", + "classnames": "^2.2.6", + "rc-menu": "^7.4.22", + "rc-trigger": "^2.6.2", + "rc-util": "^4.6.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-menu": { + "version": "7.4.23", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-7.4.23.tgz", + "integrity": "sha512-d0pUMN0Zr3GCFxNpas8p7AUTeX8viItUOQXku4AsyX82ZzUz79HgGul2Nk17BIFTtLzqdB7/NT6WVb5PAOOILw==", + "requires": { + "babel-runtime": "6.x", + "classnames": "2.x", + "dom-scroll-into-view": "1.x", + "ismobilejs": "^0.5.1", + "mini-store": "^2.0.0", + "mutationobserver-shim": "^0.3.2", + "prop-types": "^15.5.6", + "rc-animate": "2.x", + "rc-trigger": "^2.3.0", + "rc-util": "^4.1.0", + "resize-observer-polyfill": "^1.5.0" + } + }, + "rc-notification": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-3.3.1.tgz", + "integrity": "sha512-U5+f4BmBVfMSf3OHSLyRagsJ74yKwlrQAtbbL5ijoA0F2C60BufwnOcHG18tVprd7iaIjzZt1TKMmQSYSvgrig==", + "requires": { + "babel-runtime": "6.x", + "classnames": "2.x", + "prop-types": "^15.5.8", + "rc-animate": "2.x", + "rc-util": "^4.0.4" + } + }, + "rc-pagination": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-1.20.1.tgz", + "integrity": "sha512-EC2sxfKo1+R34fDN8EQgFiJn0Z6SKef4O0FizoqV3IomPczSikoarxL1RrHzqeGNsfg7JbUYaux5fQdmUAlPnA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "^15.5.7", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-progress": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.3.0.tgz", + "integrity": "sha512-hYBKFSsNgD7jsF8j+ZC1J8y5UIC2X/ktCYI/OQhQNSX6mGV1IXnUCjAd9gbLmzmpChPvKyymRNfckScUNiTpFQ==", + "requires": { + "babel-runtime": "6.x", + "prop-types": "^15.5.8" + } + }, + "rc-rate": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.5.0.tgz", + "integrity": "sha512-aXX5klRqbVZxvLghcKnLqqo7LvLVCHswEDteWsm5Gb7NBIPa1YKTcAbvb5SZ4Z4i4EeRoZaPwygRAWsQgGtbKw==", + "requires": { + "classnames": "^2.2.5", + "prop-types": "^15.5.8", + "rc-util": "^4.3.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-select": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-9.1.5.tgz", + "integrity": "sha512-P2QDl5xSdrYuvODnwZIKxhBv2AzfsuFNfaoXjRsPTlQvOjLMCGYgyRzZ4xdUy1IAc1yER6LV+g7e4N9Qc+3DDQ==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "component-classes": "1.x", + "dom-scroll-into-view": "1.x", + "prop-types": "^15.5.8", + "raf": "^3.4.0", + "rc-animate": "2.x", + "rc-menu": "^7.3.0", + "rc-trigger": "^2.5.4", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.2", + "warning": "^4.0.2" + } + }, + "rc-slider": { + "version": "8.6.13", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-8.6.13.tgz", + "integrity": "sha512-fCUe8pPn8n9pq1ARX44nN2nzJoATtna4x/PdskUrxIvZXN8ja7HuceN/hq6kokZjo3FBD2B1yMZvZh6oi68l6Q==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "prop-types": "^15.5.4", + "rc-tooltip": "^3.7.0", + "rc-util": "^4.0.4", + "shallowequal": "^1.0.1", + "warning": "^4.0.3" + } + }, + "rc-steps": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-3.4.1.tgz", + "integrity": "sha512-zdeOFmFqiXlXCQyHet1qrDDbGKZ7OQTrlzn8DP5N6M/WqN7HaYoUDy1fZ+NY2htL5WzzVFQpDRKzjiOiHaSqgw==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "^2.2.3", + "lodash": "^4.17.5", + "prop-types": "^15.5.7" + } + }, + "rc-switch": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.0.tgz", + "integrity": "sha512-Isas+egaK6qSk64jaEw4GgPStY4umYDbT7ZY93bZF1Af+b/JEsKsJdNOU2qG3WI0Z6tXo2DDq0kJCv8Yhu0zww==", + "requires": { + "classnames": "^2.2.1", + "prop-types": "^15.5.6", + "react-lifecycles-compat": "^3.0.4" + } + }, + "rc-table": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-6.6.7.tgz", + "integrity": "sha512-NC6kaHl6q/Eed0THGVSFLfc0xRcctiVEv63oDCrpYRTJPCTfG9q5APDLzwh44dKDAfXHt2JCJ5QtRJH8zaM2IQ==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "component-classes": "^1.2.6", + "lodash": "^4.17.5", + "mini-store": "^2.0.0", + "prop-types": "^15.5.8", + "rc-util": "^4.0.4", + "react-lifecycles-compat": "^3.0.2", + "shallowequal": "^1.0.2", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "rc-tabs": { + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-9.6.4.tgz", + "integrity": "sha512-l4PoDSShNJ6pWGuR1UcUgvee48b3Qu1jgMEaD1hH3Rc+mqysoO7hA9AQ1YywkIy34afGTTejAWDSIFZ0lmg08g==", + "requires": { + "babel-runtime": "6.x", + "classnames": "2.x", + "create-react-context": "0.2.2", + "lodash": "^4.17.5", + "prop-types": "15.x", + "raf": "^3.4.1", + "rc-hammerjs": "~0.6.0", + "rc-util": "^4.0.4", + "resize-observer-polyfill": "^1.5.1", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "rc-time-picker": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.6.6.tgz", + "integrity": "sha512-NVeJuxWjg9eJ0+jcCCT2dxVY2OBYxOrjsgu8ly0lk9IUJ8lwjS6JU+OibHRPJPew3Smfz88dz7GQRdBE7BcnRA==", + "requires": { + "classnames": "2.x", + "moment": "2.x", + "prop-types": "^15.5.8", + "raf": "^3.4.1", + "rc-trigger": "^2.2.0" + } + }, + "rc-tooltip": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-3.7.3.tgz", + "integrity": "sha512-dE2ibukxxkrde7wH9W8ozHKUO4aQnPZ6qBHtrTH9LoO836PjDdiaWO73fgPB05VfJs9FbZdmGPVEbXCeOP99Ww==", + "requires": { + "babel-runtime": "6.x", + "prop-types": "^15.5.8", + "rc-trigger": "^2.2.2" + } + }, + "rc-tree": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-2.1.0.tgz", + "integrity": "sha512-DyHG/W9rW8cYfBrqVrZUep5yt30scyBuYvFnGrU32bh1DUj8GKqOcdoRBaIiOBYurmIiJ02rq6BeBbvVtVp0mw==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "prop-types": "^15.5.8", + "rc-animate": "^2.6.0", + "rc-util": "^4.5.1", + "react-lifecycles-compat": "^3.0.4", + "warning": "^4.0.3" + } + }, + "rc-tree-select": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-2.9.1.tgz", + "integrity": "sha512-AfJQC1ZzaeH+Onmx84TtVLUL2guBZe7exA8XSfj1RRB1doDbYGTtybzpP3CEw/tuSftSRnz+iPt+iaxRTrgXRw==", + "requires": { + "classnames": "^2.2.1", + "dom-scroll-into-view": "^1.2.1", + "prop-types": "^15.5.8", + "raf": "^3.4.0", + "rc-animate": "^2.8.2", + "rc-tree": "~2.0.0", + "rc-trigger": "^3.0.0-rc.2", + "rc-util": "^4.5.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.0.2", + "warning": "^4.0.1" + }, + "dependencies": { + "rc-tree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-2.0.0.tgz", + "integrity": "sha512-DAT/jsbnFbHqG9Df9OaVG93CAVtTsJVnJiwKX+wqsG8TChpty3s6QX3zJZ+gBgjkq4ikLbu1kuFJtX63EKhSAA==", + "requires": { + "babel-runtime": "^6.23.0", + "classnames": "2.x", + "prop-types": "^15.5.8", + "rc-animate": "^2.6.0", + "rc-util": "^4.5.1", + "react-lifecycles-compat": "^3.0.4", + "warning": "^3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "rc-trigger": { + "version": "3.0.0-rc.3", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-3.0.0-rc.3.tgz", + "integrity": "sha512-4vB6cpxcUdm2qO5VtB9q1TZz0MoWm9BzFLvGknulphGrl1qI6uxUsPDCvqnmujdpDdAKGGfjxntFpA7RtAwkFQ==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-align": "^2.4.1", + "rc-animate": "^3.0.0-rc.1", + "rc-util": "^4.4.0" + }, + "dependencies": { + "rc-animate": { + "version": "3.0.0-rc.6", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.0.0-rc.6.tgz", + "integrity": "sha512-oBLPpiT6Q4t6YvD/pkLcmofBP1p01TX0Otse8Q4+Mxt8J+VSDflLZGIgf62EwkvRwsQUkLPjZVFBsldnPKLzjg==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "component-classes": "^1.2.6", + "fbjs": "^0.8.16", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-util": "^4.5.0", + "react-lifecycles-compat": "^3.0.4" + } + } + } + } + } + }, + "rc-trigger": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.4.tgz", + "integrity": "sha512-dN/Iia5E+xLh33OyvIol1Gg1WfHVfL9fUdq34/ejeYENghUNkF0965PDe+GYIgypYplhUM967rjbr/UEm6ctjA==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "15.x", + "rc-align": "^2.4.0", + "rc-animate": "2.x", + "rc-util": "^4.4.0" + } + }, + "rc-upload": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-2.6.7.tgz", + "integrity": "sha512-i6roYvM31ue50r0w/MbxOdbbkZHqpJLT29JyjQC2W5i/7w0/lZJkWEmj/DG5WRRJCnVfIiKmXp2437oXnUFNuw==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5", + "prop-types": "^15.5.7", + "warning": "4.x" + } + }, + "rc-util": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.6.0.tgz", + "integrity": "sha512-rbgrzm1/i8mgfwOI4t1CwWK7wGe+OwX+dNa7PVMgxZYPBADGh86eD4OcJO1UKGeajIMDUUKMluaZxvgraQIOmw==", + "requires": { + "add-dom-event-listener": "^1.1.0", + "babel-runtime": "6.x", + "prop-types": "^15.5.10", + "shallowequal": "^0.2.2" + }, + "dependencies": { + "shallowequal": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz", + "integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=", + "requires": { + "lodash.keys": "^3.1.2" + } + } + } + }, + "react": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", + "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-app-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.1.tgz", + "integrity": "sha512-LbVpT1NdzTdDDs7xEZdebjDrqsvKi5UyVKUQqtTYYNyC1JJYVAwNQWe4ybWvoT2V2WW9PGVO2u5Y6aVj4ER/Ow==", + "requires": { + "core-js": "3.0.1", + "object-assign": "4.1.1", + "promise": "8.0.2", + "raf": "3.4.1", + "regenerator-runtime": "0.13.2", + "whatwg-fetch": "3.0.0" + }, + "dependencies": { + "core-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", + "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==" + }, + "promise": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.0.2.tgz", + "integrity": "sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw==", + "requires": { + "asap": "~2.0.6" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } + } + }, + "react-app-rewired": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/react-app-rewired/-/react-app-rewired-2.1.3.tgz", + "integrity": "sha512-NXC2EsQrnEMV7xD70rHcBq0B4PSEzjY/K2m/e+GRgit2jZO/uZApnpCZSKvIX2leLRN69Sqf2id0VXZ1F62CDw==", + "requires": { + "cross-spawn": "^6.0.5", + "dotenv": "^6.2.0", + "semver": "^5.6.0" + } + }, + "react-dev-utils": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.0.1.tgz", + "integrity": "sha512-pnaeMo/Pxel8aZpxk1WwxT3uXxM3tEwYvsjCYn5R7gNxjhN1auowdcLDzFB8kr7rafAj2rxmvfic/fbac5CzwQ==", + "requires": { + "@babel/code-frame": "7.0.0", + "address": "1.0.3", + "browserslist": "4.5.4", + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "1.0.5", + "filesize": "3.6.1", + "find-up": "3.0.0", + "fork-ts-checker-webpack-plugin": "1.1.1", + "global-modules": "2.0.0", + "globby": "8.0.2", + "gzip-size": "5.0.0", + "immer": "1.10.0", + "inquirer": "6.2.2", + "is-root": "2.0.0", + "loader-utils": "1.2.3", + "opn": "5.4.0", + "pkg-up": "2.0.0", + "react-error-overlay": "^5.1.6", + "recursive-readdir": "2.2.2", + "shell-quote": "1.6.1", + "sockjs-client": "1.3.0", + "strip-ansi": "5.2.0", + "text-table": "0.2.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==" + }, + "browserslist": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz", + "integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==", + "requires": { + "caniuse-lite": "^1.0.30000955", + "electron-to-chromium": "^1.3.122", + "node-releases": "^1.1.13" + } + }, + "inquirer": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.0.0", + "through": "^2.3.6" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "react-dom": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", + "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-error-overlay": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.6.tgz", + "integrity": "sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==" + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "react-lazy-load": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/react-lazy-load/-/react-lazy-load-3.0.13.tgz", + "integrity": "sha1-OwqS0zbUPT8Nc8vm81sXBQsIuCQ=", + "requires": { + "eventlistener": "0.0.1", + "lodash.debounce": "^4.0.0", + "lodash.throttle": "^4.0.0", + "prop-types": "^15.5.8" + } + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-redux": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.0.tgz", + "integrity": "sha512-hyu/PoFK3vZgdLTg9ozbt7WF3GgX5+Yn3pZm5/96/o4UueXA+zj08aiSC9Mfj2WtD1bvpIb3C5yvskzZySzzaw==", + "requires": { + "@babel/runtime": "^7.4.5", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.8.6" + } + }, + "react-router": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz", + "integrity": "sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.3.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-router-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.1.tgz", + "integrity": "sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.0.1", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, + "react-scripts": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.0.1.tgz", + "integrity": "sha512-LKEjBhVpEB+c312NeJhzF+NATxF7JkHNr5GhtwMeRS1cMeLElMeIu8Ye7WGHtDP7iz7ra4ryy48Zpo6G/cwWUw==", + "requires": { + "@babel/core": "7.4.3", + "@svgr/webpack": "4.1.0", + "@typescript-eslint/eslint-plugin": "1.6.0", + "@typescript-eslint/parser": "1.6.0", + "babel-eslint": "10.0.1", + "babel-jest": "^24.8.0", + "babel-loader": "8.0.5", + "babel-plugin-named-asset-import": "^0.3.2", + "babel-preset-react-app": "^9.0.0", + "camelcase": "^5.2.0", + "case-sensitive-paths-webpack-plugin": "2.2.0", + "css-loader": "2.1.1", + "dotenv": "6.2.0", + "dotenv-expand": "4.2.0", + "eslint": "^5.16.0", + "eslint-config-react-app": "^4.0.1", + "eslint-loader": "2.1.2", + "eslint-plugin-flowtype": "2.50.1", + "eslint-plugin-import": "2.16.0", + "eslint-plugin-jsx-a11y": "6.2.1", + "eslint-plugin-react": "7.12.4", + "eslint-plugin-react-hooks": "^1.5.0", + "file-loader": "3.0.1", + "fs-extra": "7.0.1", + "fsevents": "2.0.6", + "html-webpack-plugin": "4.0.0-beta.5", + "identity-obj-proxy": "3.0.0", + "is-wsl": "^1.1.0", + "jest": "24.7.1", + "jest-environment-jsdom-fourteen": "0.1.0", + "jest-resolve": "24.7.1", + "jest-watch-typeahead": "0.3.0", + "mini-css-extract-plugin": "0.5.0", + "optimize-css-assets-webpack-plugin": "5.0.1", + "pnp-webpack-plugin": "1.2.1", + "postcss-flexbugs-fixes": "4.1.0", + "postcss-loader": "3.0.0", + "postcss-normalize": "7.0.1", + "postcss-preset-env": "6.6.0", + "postcss-safe-parser": "4.0.1", + "react-app-polyfill": "^1.0.1", + "react-dev-utils": "^9.0.1", + "resolve": "1.10.0", + "sass-loader": "7.1.0", + "semver": "6.0.0", + "style-loader": "0.23.1", + "terser-webpack-plugin": "1.2.3", + "ts-pnp": "1.1.2", + "url-loader": "1.1.2", + "webpack": "4.29.6", + "webpack-dev-server": "3.2.1", + "webpack-manifest-plugin": "2.0.4", + "workbox-webpack-plugin": "4.2.0" + }, + "dependencies": { + "semver": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==" + } + } + }, + "react-slick": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.24.0.tgz", + "integrity": "sha512-Pvo0B74ohumQdYOf0qP+pdQpj9iUbAav7+2qiF3uTc5XeQp/Y/cnIeDBM2tB3txthfSe05jKIqLMJTS6qVvt5g==", + "requires": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "requires": { + "util.promisify": "^1.0.0" + } + }, + "recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "requires": { + "minimatch": "3.0.4" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "redux": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.3.tgz", + "integrity": "sha512-v/Iaw67Pe+na+cZvcKvPxAKT1ww5kM+M09fmaCndCQC4Lo434AYb5975HJgJlp0D7dJxfYaLxMD4VwfpLOZ1Rw==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-logger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", + "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", + "requires": { + "deep-diff": "^0.3.5" + } + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.0.tgz", + "integrity": "sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==", + "requires": { + "private": "^0.1.6" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp-tree": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.10.tgz", + "integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==" + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + }, + "regexpu-core": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz", + "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.0.2", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "regjsgen": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", + "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==" + }, + "regjsparser": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } + } + }, + "rehype-parse": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.0.tgz", + "integrity": "sha512-V2OjMD0xcSt39G4uRdMTqDXXm6HwkUbLMDayYKA/d037j8/OtVSQ+tqKwYWOuyBeoCs/3clXRe30VUjeMDTBSA==", + "requires": { + "hast-util-from-parse5": "^5.0.0", + "parse5": "^5.0.0", + "xtend": "^4.0.1" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + }, + "resolve-pathname": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rmc-feedback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rmc-feedback/-/rmc-feedback-2.0.0.tgz", + "integrity": "sha512-5PWOGOW7VXks/l3JzlOU9NIxRpuaSS8d9zA3UULUCuTKnpwBHNvv1jSJzxgbbCQeYzROWUpgKI4za3X4C/mKmQ==", + "requires": { + "babel-runtime": "6.x", + "classnames": "^2.2.5" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==" + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, + "sass-loader": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "requires": { + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" + } + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "requires": { + "xmlchars": "^2.1.1" + } + }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz", + "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==" + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "requires": { + "is-buffer": "^1.0.2" + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=" + } + } + }, + "shallow-equal": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.0.tgz", + "integrity": "sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA==" + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, + "sisteransi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", + "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==" + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + }, + "dependencies": { + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-explorer": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/source-map-explorer/-/source-map-explorer-1.8.0.tgz", + "integrity": "sha512-1Q0lNSw5J7pChKmjqniOCLbvLFi4KJfrtixk99CzvRcqFiGBJvRHMrw0PjLwKOvbuAo8rNOukJhEPA0Nj85xDw==", + "requires": { + "btoa": "^1.2.1", + "convert-source-map": "^1.6.0", + "docopt": "^0.6.2", + "ejs": "^2.6.1", + "fs-extra": "^7.0.1", + "glob": "^7.1.3", + "opn": "^5.5.0", + "source-map": "^0.5.1", + "temp": "^0.9.0" + }, + "dependencies": { + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "space-separated-tokens": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.4.tgz", + "integrity": "sha512-UyhMSmeIqZrQn2UdjYpxEkwY9JUrn8pP+7L4f91zRzOQuI8MF1FGLfYU9DKCYeLdo7LXMxwrX5zKFy7eeeVHuA==" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + }, + "string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-comments": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz", + "integrity": "sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw==", + "requires": { + "babel-extract-comments": "^1.0.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "svgo": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.2.tgz", + "integrity": "sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.28", + "css-url-regex": "^1.1.0", + "csso": "^3.5.1", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "table": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz", + "integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==", + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.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==" + }, + "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==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "temp": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.0.tgz", + "integrity": "sha512-YfUhPQCJoNQE5N+FJQcdPz63O3x3sdT4Xju69Gj4iZe0lBKOtnAMi0SLj9xKhGkcGhsxThvTJ/usxtFPo438zQ==", + "requires": { + "rimraf": "~2.6.2" + } + }, + "terser": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", + "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "requires": { + "commander": "^2.19.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.10" + } + }, + "terser-webpack-plugin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", + "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.16.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.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=" + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==" + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, + "tiny-invariant": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz", + "integrity": "sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==" + }, + "tiny-warning": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz", + "integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==" + }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "trough": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.4.tgz", + "integrity": "sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q==" + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "requires": { + "glob": "^7.1.2" + } + }, + "ts-pnp": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.2.tgz", + "integrity": "sha512-f5Knjh7XCyRIzoC/z1Su1yLLRrPrFCgtUAh/9fCSP6NKbATwpOL1+idQVXQokK9GRFURn/jYPGPfegIctwunoA==" + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tsutils": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.0.tgz", + "integrity": "sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw==", + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz", + "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==" + }, + "ua-parser-js": { + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz", + "integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==" + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==" + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==" + }, + "unified": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-7.1.0.tgz", + "integrity": "sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw==", + "requires": { + "@types/unist": "^2.0.0", + "@types/vfile": "^3.0.0", + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^3.0.0", + "x-is-string": "^0.1.0" + }, + "dependencies": { + "vfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-3.0.1.tgz", + "integrity": "sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ==", + "requires": { + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + } + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "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==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-equal": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "vendors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz", + "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vfile": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.0.1.tgz", + "integrity": "sha512-lRHFCuC4SQBFr7Uq91oJDJxlnftoTLQ7eKIpMdubhYcVMho4781a8MWXLy3qZrZ0/STD1kRiKc0cQOHm4OkPeA==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "dependencies": { + "unist-util-stringify-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.1.tgz", + "integrity": "sha512-Zqlf6+FRI39Bah8Q6ZnNGrEHUhwJOkHde2MHVk96lLyftfJJckaPslKgzhVcviXj8KcE9UJM9F+a4JEiBUTYgA==", + "requires": { + "@types/unist": "^2.0.2" + } + }, + "vfile-message": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.1.tgz", + "integrity": "sha512-KtasSV+uVU7RWhUn4Lw+wW1Zl/nW8JWx7JCPps10Y9JRRIDeDXf8wfBLoOSsJLyo27DqMyAi54C6Jf/d6Kr2Bw==", + "requires": { + "@types/unist": "^2.0.2", + "unist-util-stringify-position": "^2.0.0" + } + } + } + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + }, + "vm-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==" + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "requires": { + "makeerror": "1.0.x" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "web-namespaces": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.3.tgz", + "integrity": "sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA==" + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "webpack": { + "version": "4.29.6", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.6.tgz", + "integrity": "sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.0.5", + "acorn-dynamic-import": "^4.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^1.0.0", + "tapable": "^1.1.0", + "terser-webpack-plugin": "^1.1.0", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + } + }, + "webpack-dev-middleware": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz", + "integrity": "sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.2", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + } + } + }, + "webpack-dev-server": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz", + "integrity": "sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^4.1.1", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "^0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.2.0", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "schema-utils": "^1.0.0", + "selfsigned": "^1.9.1", + "semver": "^5.6.0", + "serve-index": "^1.7.2", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.5.1", + "webpack-log": "^2.0.0", + "yargs": "12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "requires": { + "xregexp": "4.0.0" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-manifest-plugin": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.0.4.tgz", + "integrity": "sha512-nejhOHexXDBKQOj/5v5IZSfCeTO3x1Dt1RZEcGfBSul891X/eLIcIVH31gwxPDdsi2Z8LKKFGpM4w9+oTBOSCg==", + "requires": { + "fs-extra": "^7.0.0", + "lodash": ">=3.5 <5", + "tapable": "^1.0.0" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "websocket-driver": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "requires": { + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "workbox-background-sync": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz", + "integrity": "sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-broadcast-update": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz", + "integrity": "sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-build": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-4.3.1.tgz", + "integrity": "sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw==", + "requires": { + "@babel/runtime": "^7.3.4", + "@hapi/joi": "^15.0.0", + "common-tags": "^1.8.0", + "fs-extra": "^4.0.2", + "glob": "^7.1.3", + "lodash.template": "^4.4.0", + "pretty-bytes": "^5.1.0", + "stringify-object": "^3.3.0", + "strip-comments": "^1.0.2", + "workbox-background-sync": "^4.3.1", + "workbox-broadcast-update": "^4.3.1", + "workbox-cacheable-response": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-expiration": "^4.3.1", + "workbox-google-analytics": "^4.3.1", + "workbox-navigation-preload": "^4.3.1", + "workbox-precaching": "^4.3.1", + "workbox-range-requests": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1", + "workbox-streams": "^4.3.1", + "workbox-sw": "^4.3.1", + "workbox-window": "^4.3.1" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "workbox-cacheable-response": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz", + "integrity": "sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-4.3.1.tgz", + "integrity": "sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg==" + }, + "workbox-expiration": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-4.3.1.tgz", + "integrity": "sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-google-analytics": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz", + "integrity": "sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg==", + "requires": { + "workbox-background-sync": "^4.3.1", + "workbox-core": "^4.3.1", + "workbox-routing": "^4.3.1", + "workbox-strategies": "^4.3.1" + } + }, + "workbox-navigation-preload": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz", + "integrity": "sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-precaching": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-4.3.1.tgz", + "integrity": "sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-range-requests": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz", + "integrity": "sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-routing": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-4.3.1.tgz", + "integrity": "sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-strategies": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-4.3.1.tgz", + "integrity": "sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-streams": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-4.3.1.tgz", + "integrity": "sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "workbox-sw": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-4.3.1.tgz", + "integrity": "sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w==" + }, + "workbox-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-YZsiA+y/ns/GdWRaBsfYv8dln1ebWtGnJcTOg1ppO0pO1tScAHX0yGtHIjndxz3L/UUhE8b0NQE9KeLNwJwA5A==", + "requires": { + "@babel/runtime": "^7.0.0", + "json-stable-stringify": "^1.0.1", + "workbox-build": "^4.2.0" + } + }, + "workbox-window": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-4.3.1.tgz", + "integrity": "sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg==", + "requires": { + "workbox-core": "^4.3.1" + } + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, + "worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "requires": { + "microevent.ts": "~0.1.1" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xmlchars": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.1.1.tgz", + "integrity": "sha512-7hew1RPJ1iIuje/Y01bGD/mXokXxegAgVS+e+E0wSi2ILHQkYAH1+JXARwTjZSM4Z4Z+c73aKspEcqj+zPPL/w==" + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + }, + "dependencies": { + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + } + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/cvat-ui/package.json b/cvat-ui/package.json new file mode 100644 index 00000000..cc5aaff0 --- /dev/null +++ b/cvat-ui/package.json @@ -0,0 +1,56 @@ +{ + "name": "cvat-ui", + "version": "0.1.0", + "license": "MIT", + "private": true, + "dependencies": { + "@types/jest": "24.0.13", + "@types/node": "^12.0.3", + "@types/react": "16.8.19", + "@types/react-dom": "16.8.4", + "@types/react-redux": "^7.1.1", + "@types/react-router-dom": "^4.3.4", + "@types/redux-logger": "^3.0.7", + "antd": "^3.19.1", + "babel-plugin-import": "^1.11.2", + "customize-cra": "^0.2.12", + "less": "^3.9.0", + "less-loader": "^5.0.0", + "node-sass": "^4.12.0", + "query-string": "^6.8.1", + "react": "^16.8.6", + "react-app-rewired": "^2.1.3", + "react-dom": "^16.8.6", + "react-redux": "^7.1.0", + "react-router-dom": "^5.0.1", + "react-scripts": "3.0.1", + "react-scripts-ts": "^3.1.0", + "redux": "^4.0.4", + "redux-logger": "^3.0.6", + "redux-thunk": "^2.3.0", + "source-map-explorer": "^1.8.0", + "typescript": "3.4.5" + }, + "scripts": { + "analyze": "source-map-explorer 'build/static/js/*.js'", + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "test": "react-app-rewired test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/cvat-ui/public/cvat-core.min.js b/cvat-ui/public/cvat-core.min.js new file mode 100644 index 00000000..79057d9a --- /dev/null +++ b/cvat-ui/public/cvat-core.min.js @@ -0,0 +1,15 @@ +window.cvat=function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=90)}([function(t,e,n){(function(e){var n="object",r=function(t){return t&&t.Math==Math&&t};t.exports=r(typeof globalThis==n&&globalThis)||r(typeof window==n&&window)||r(typeof self==n&&self)||r(typeof e==n&&e)||Function("return this")()}).call(this,n(28))},function(t,e,n){var r=n(0),o=n(31),i=n(57),s=n(97),a=r.Symbol,c=o("wks");t.exports=function(t){return c[t]||(c[t]=s&&a[t]||(s?a:i)("Symbol."+t))}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){var r=n(11);t.exports=function(t){if(!r(t))throw TypeError(String(t)+" is not an object");return t}},function(t,e,n){"use strict";var r=n(30),o=n(108),i=n(25),s=n(17),a=n(73),c=s.set,u=s.getterFor("Array Iterator");t.exports=a(Array,"Array",function(t,e){c(this,{type:"Array Iterator",target:r(t),index:0,kind:e})},function(){var t=u(this),e=t.target,n=t.kind,r=t.index++;return!e||r>=e.length?(t.target=void 0,{value:void 0,done:!0}):"keys"==n?{value:r,done:!1}:"values"==n?{value:e[r],done:!1}:{value:[r,e[r]],done:!1}},"values"),i.Arguments=i.Array,o("keys"),o("values"),o("entries")},function(t,e,n){n(9),(()=>{const e=n(113),r=n(115),o=n(48);class i extends Error{constructor(t){super(t);const n=(new Date).toISOString(),i=e.os.toString(),s=`${e.name} ${e.version}`,a=r.parse(this)[0],c=`${a.fileName}`,u=a.lineNumber,l=a.columnNumber,{jobID:f,taskID:p,clientID:h}=o;Object.defineProperties(this,Object.freeze({system:{get:()=>i},client:{get:()=>s},time:{get:()=>n},jobID:{get:()=>f},taskID:{get:()=>p},projID:{get:()=>void 0},clientID:{get:()=>h},filename:{get:()=>c},line:{get:()=>u},column:{get:()=>l}}))}async save(){const t={system:this.system,client:this.client,time:this.time,job_id:this.jobID,task_id:this.taskID,proj_id:this.projID,client_id:this.clientID,message:this.message,filename:this.filename,line:this.line,column:this.column,stack:this.stack};try{const e=n(18);await e.server.exception(t)}catch(t){}}}t.exports={Exception:i,ArgumentError:class extends i{constructor(t){super(t)}},DataError:class extends i{constructor(t){super(t)}},ScriptingError:class extends i{constructor(t){super(t)}},PluginError:class extends i{constructor(t){super(t)}},ServerError:class extends i{constructor(t,e){super(t),Object.defineProperties(this,Object.freeze({code:{get:()=>e}}))}}}})()},function(t,e,n){"use strict";var r=n(78),o=n(136),i=Object.prototype.toString;function s(t){return"[object Array]"===i.call(t)}function a(t){return null!==t&&"object"==typeof t}function c(t){return"[object Function]"===i.call(t)}function u(t,e){if(null!=t)if("object"!=typeof t&&(t=[t]),s(t))for(var n=0,r=t.length;ns;){var a,c,u,l=r[s++],f=i?l.ok:l.fail,p=l.resolve,h=l.reject,d=l.domain;try{f?(i||(2===e.rejection&&et(t,e),e.rejection=1),!0===f?a=o:(d&&d.enter(),a=f(o),d&&(d.exit(),u=!0)),a===l.promise?h(D("Promise-chain cycle")):(c=K(a))?c.call(a,p,h):p(a)):h(o)}catch(t){d&&!u&&d.exit(),h(t)}}e.reactions=[],e.notified=!1,n&&!e.rejection&&Q(t,e)})}},Y=function(t,e,n){var r,o;V?((r=L.createEvent("Event")).promise=e,r.reason=n,r.initEvent(t,!1,!0),u.dispatchEvent(r)):r={promise:e,reason:n},(o=u["on"+t])?o(r):"unhandledrejection"===t&&A("Unhandled promise rejection",n)},Q=function(t,e){j.call(u,function(){var n,r=e.value;if(tt(e)&&(n=T(function(){q?z.emit("unhandledRejection",r,t):Y("unhandledrejection",t,r)}),e.rejection=q||tt(e)?2:1,n.error))throw n.value})},tt=function(t){return 1!==t.rejection&&!t.parent},et=function(t,e){j.call(u,function(){q?z.emit("rejectionHandled",t):Y("rejectionhandled",t,e.value)})},nt=function(t,e,n,r){return function(o){t(e,n,o,r)}},rt=function(t,e,n,r){e.done||(e.done=!0,r&&(e=r),e.value=n,e.state=2,Z(t,e,!0))},ot=function(t,e,n,r){if(!e.done){e.done=!0,r&&(e=r);try{if(t===n)throw D("Promise can't be resolved itself");var o=K(n);o?k(function(){var r={done:!1};try{o.call(n,nt(ot,t,r,e),nt(rt,t,r,e))}catch(n){rt(t,r,n,e)}}):(e.value=n,e.state=1,Z(t,e,!1))}catch(n){rt(t,{done:!1},n,e)}}};X&&(R=function(t){y(this,R,C),m(t),r.call(this);var e=N(this);try{t(nt(ot,this,e),nt(rt,this,e))}catch(t){rt(this,e,t)}},(r=function(t){$(this,{type:C,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=h(R.prototype,{then:function(t,e){var n=M(this),r=W(x(this,R));return r.ok="function"!=typeof t||t,r.fail="function"==typeof e&&e,r.domain=q?z.domain:void 0,n.parent=!0,n.reactions.push(r),0!=n.state&&Z(this,n,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new r,e=N(t);this.promise=t,this.resolve=nt(ot,t,e),this.reject=nt(rt,t,e)},E.f=W=function(t){return t===R||t===i?new o(t):J(t)},c||"function"!=typeof f||(s=f.prototype.then,p(f.prototype,"then",function(t,e){var n=this;return new R(function(t,e){s.call(n,t,e)}).then(t,e)}),"function"==typeof B&&a({global:!0,enumerable:!0,forced:!0},{fetch:function(t){return S(R,B.apply(u,arguments))}}))),a({global:!0,wrap:!0,forced:X},{Promise:R}),d(R,C,!1,!0),b(C),i=l.Promise,a({target:C,stat:!0,forced:X},{reject:function(t){var e=W(this);return e.reject.call(void 0,t),e.promise}}),a({target:C,stat:!0,forced:c||X},{resolve:function(t){return S(c&&this===i?R:this,t)}}),a({target:C,stat:!0,forced:H},{all:function(t){var e=this,n=W(e),r=n.resolve,o=n.reject,i=T(function(){var n=m(e.resolve),i=[],s=0,a=1;w(t,function(t){var c=s++,u=!1;i.push(void 0),a++,n.call(e,t).then(function(t){u||(u=!0,i[c]=t,--a||r(i))},o)}),--a||r(i)});return i.error&&o(i.value),n.promise},race:function(t){var e=this,n=W(e),r=n.reject,o=T(function(){var o=m(e.resolve);w(t,function(t){o.call(e,t).then(n.resolve,r)})});return o.error&&r(o.value),n.promise}})},function(t,e,n){var r=n(2);t.exports=!r(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){var r=n(10),o=n(14),i=n(29);t.exports=r?function(t,e,n){return o.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(0),o=n(40).f,i=n(12),s=n(15),a=n(43),c=n(58),u=n(62);t.exports=function(t,e){var n,l,f,p,h,d=t.target,b=t.global,g=t.stat;if(n=b?r:g?r[d]||a(d,{}):(r[d]||{}).prototype)for(l in e){if(p=e[l],f=t.noTargetGet?(h=o(n,l))&&h.value:n[l],!u(b?l:d+(g?".":"#")+l,t.forced)&&void 0!==f){if(typeof p==typeof f)continue;c(p,f)}(t.sham||f&&f.sham)&&i(p,"sham",!0),s(n,l,p,t)}}},function(t,e,n){var r=n(10),o=n(55),i=n(3),s=n(41),a=Object.defineProperty;e.f=r?a:function(t,e,n){if(i(t),e=s(e,!0),i(n),o)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(0),o=n(31),i=n(12),s=n(7),a=n(43),c=n(56),u=n(17),l=u.get,f=u.enforce,p=String(c).split("toString");o("inspectSource",function(t){return c.call(t)}),(t.exports=function(t,e,n,o){var c=!!o&&!!o.unsafe,u=!!o&&!!o.enumerable,l=!!o&&!!o.noTargetGet;"function"==typeof n&&("string"!=typeof e||s(n,"name")||i(n,"name",e),f(n).source=p.join("string"==typeof e?e:"")),t!==r?(c?!l&&t[e]&&(u=!0):delete t[e],u?t[e]=n:i(t,e,n)):u?t[e]=n:a(e,n)})(Function.prototype,"toString",function(){return"function"==typeof this&&l(this).source||c.call(this)})},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r,o,i,s=n(91),a=n(0),c=n(11),u=n(12),l=n(7),f=n(44),p=n(45),h=a.WeakMap;if(s){var d=new h,b=d.get,g=d.has,m=d.set;r=function(t,e){return m.call(d,t,e),e},o=function(t){return b.call(d,t)||{}},i=function(t){return g.call(d,t)}}else{var y=f("state");p[y]=!0,r=function(t,e){return u(t,y,e),e},o=function(t){return l(t,y)?t[y]:{}},i=function(t){return l(t,y)}}t.exports={set:r,get:o,has:i,enforce:function(t){return i(t)?o(t):r(t,{})},getterFor:function(t){return function(e){var n;if(!c(e)||(n=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}}}},function(t,e,n){n(9),n(117),(()=>{const e=n(122),{ServerError:r}=n(5),o=n(123),i=n(48);function s(t,e){if(t.response){const n=`${e}. `+`${t.message}. ${JSON.stringify(t.response.data)||""}.`;return new r(n,t.response.status)}const n=`${e}. `+`${t.message}.`;return new r(n,0)}const a=new class{constructor(){const a=n(134);a.defaults.withCredentials=!0,a.defaults.xsrfHeaderName="X-CSRFTOKEN",a.defaults.xsrfCookieName="csrftoken";let c=o.get("token");async function u(t=""){const{backendAPI:e}=i;let n=null;try{n=await a.get(`${e}/tasks?${t}`,{proxy:i.proxy})}catch(t){throw s(t,"Could not get tasks from a server")}return n.data.results.count=n.data.count,n.data.results}async function l(t){const{backendAPI:e}=i;try{await a.delete(`${e}/tasks/${t}`)}catch(t){throw s(t,"Could not delete the task from the server")}}c&&(a.defaults.headers.common.Authorization=`Token ${c}`),Object.defineProperties(this,Object.freeze({server:{value:Object.freeze({about:async function(){const{backendAPI:t}=i;let e=null;try{e=await a.get(`${t}/server/about`,{proxy:i.proxy})}catch(t){throw s(t,'Could not get "about" information from the server')}return e.data},share:async function(t){const{backendAPI:e}=i;t=encodeURIComponent(t);let n=null;try{n=await a.get(`${e}/server/share?directory=${t}`,{proxy:i.proxy})}catch(t){throw s(t,'Could not get "share" information from the server')}return n.data},formats:async function(){const{backendAPI:t}=i;let e=null;try{e=await a.get(`${t}/server/annotation/formats`,{proxy:i.proxy})}catch(t){throw s(t,"Could not get annotation formats from the server")}return e.data},exception:async function(t){const{backendAPI:e}=i;try{await a.post(`${e}/server/exception`,JSON.stringify(t),{proxy:i.proxy,headers:{"Content-Type":"application/json"}})}catch(t){throw s(t,"Could not send an exception to the server")}},login:async function(t,e){const n=[`${encodeURIComponent("username")}=${encodeURIComponent(t)}`,`${encodeURIComponent("password")}=${encodeURIComponent(e)}`].join("&").replace(/%20/g,"+");let r=null;try{r=await a.post(`${i.backendAPI}/auth/login`,n,{proxy:i.proxy})}catch(t){throw s(t,"Could not login on a server")}if(r.headers["set-cookie"]){const t=r.headers["set-cookie"].join(";");a.defaults.headers.common.Cookie=t}c=r.data.key,o.set("token",c),a.defaults.headers.common.Authorization=`Token ${c}`},logout:async function(){try{await a.post(`${i.backendAPI}/auth/logout`,{proxy:i.proxy})}catch(t){throw s(t,"Could not logout from the server")}o.remove("token"),a.defaults.headers.common.Authorization=""},authorized:async function(){try{await t.exports.users.getSelf()}catch(t){if(401===t.code)return!1;throw t}return!0},register:async function(t,e,n,r,o,c){let u=null;try{const l=JSON.stringify({username:t,first_name:e,last_name:n,email:r,password1:o,password2:c});u=await a.post(`${i.backendAPI}/auth/register`,l,{proxy:i.proxy,headers:{"Content-Type":"application/json"}})}catch(e){throw s(e,`Could not register '${t}' user on the server`)}return u.data}}),writable:!1},tasks:{value:Object.freeze({getTasks:u,saveTask:async function(t,e){const{backendAPI:n}=i;try{await a.patch(`${n}/tasks/${t}`,JSON.stringify(e),{proxy:i.proxy,headers:{"Content-Type":"application/json"}})}catch(t){throw s(t,"Could not save the task on the server")}},createTask:async function(t,n,o){const{backendAPI:c}=i,f=new e;for(const t in n)if(Object.prototype.hasOwnProperty.call(n,t))for(let e=0;e{setTimeout(async function i(){try{const u=await a.get(`${c}/tasks/${t}/status`);if(["Queued","Started"].includes(u.data.state))""!==u.data.message&&o(u.data.message),setTimeout(i,1e3);else if("Finished"===u.data.state)e();else if("Failed"===u.data.state){const t="Could not create the task on the server. "+`${u.data.message}.`;n(new r(t,400))}else n(new r(`Unknown task state has been received: ${u.data.state}`,500))}catch(t){n(s(t,"Could not put task to the server"))}},1e3)})}(p.data.id)}catch(t){throw await l(p.data.id),t}return(await u(`?id=${p.id}`))[0]},deleteTask:l}),writable:!1},jobs:{value:Object.freeze({getJob:async function(t){const{backendAPI:e}=i;let n=null;try{n=await a.get(`${e}/jobs/${t}`,{proxy:i.proxy})}catch(t){throw s(t,"Could not get jobs from a server")}return n.data},saveJob:async function(t,e){const{backendAPI:n}=i;try{await a.patch(`${n}/jobs/${t}`,JSON.stringify(e),{proxy:i.proxy,headers:{"Content-Type":"application/json"}})}catch(t){throw s(t,"Could not save the job on the server")}}}),writable:!1},users:{value:Object.freeze({getUsers:async function(){const{backendAPI:t}=i;let e=null;try{e=await a.get(`${t}/users`,{proxy:i.proxy})}catch(t){throw s(t,"Could not get users from the server")}return e.data.results},getSelf:async function(){const{backendAPI:t}=i;let e=null;try{e=await a.get(`${t}/users/self`,{proxy:i.proxy})}catch(t){throw s(t,"Could not get user data from the server")}return e.data}}),writable:!1},frames:{value:Object.freeze({getData:async function(t,e){const{backendAPI:n}=i;let r=null;try{r=await a.get(`${n}/tasks/${t}/frames/${e}`,{proxy:i.proxy,responseType:"blob"})}catch(n){throw s(n,`Could not get frame ${e} for the task ${t} from the server`)}return r.data},getMeta:async function(t){const{backendAPI:e}=i;let n=null;try{n=await a.get(`${e}/tasks/${t}/frames/meta`,{proxy:i.proxy})}catch(e){throw s(e,`Could not get frame meta info for the task ${t} from the server`)}return n.data}}),writable:!1},annotations:{value:Object.freeze({updateAnnotations:async function(t,e,n,r){const{backendAPI:o}=i;let c=null,u=null;"PUT"===r.toUpperCase()?(c=a.put.bind(a),u=`${o}/${t}s/${e}/annotations`):(c=a.patch.bind(a),u=`${o}/${t}s/${e}/annotations?action=${r}`);let l=null;try{l=await c(u,JSON.stringify(n),{proxy:i.proxy,headers:{"Content-Type":"application/json"}})}catch(n){throw s(n,`Could not ${r} annotations for the ${t} ${e} on the server`)}return l.data},getAnnotations:async function(t,e){const{backendAPI:n}=i;let r=null;try{r=await a.get(`${n}/${t}s/${e}/annotations`,{proxy:i.proxy})}catch(n){throw s(n,`Could not get annotations for the ${t} ${e} from the server`)}return r.data},dumpAnnotations:async function(t,e,n){const{backendAPI:r}=i,o=e.replace(/\//g,"_");let c=`${r}/tasks/${t}/annotations/${o}?format=${n}`;return new Promise((e,n)=>{setTimeout(async function r(){try{202===(await a.get(`${c}`,{proxy:i.proxy})).status?setTimeout(r,3e3):e(c=`${c}&action=download`)}catch(e){n(s(e,`Could not dump annotations for the task ${t} from the server`))}})})},uploadAnnotations:async function(t,n,r,o){const{backendAPI:c}=i;let u=new e;return u.append("annotation_file",r),new Promise((r,l)=>{setTimeout(async function f(){try{202===(await a.put(`${c}/${t}s/${n}/annotations?format=${o}`,u,{proxy:i.proxy})).status?(u=new e,setTimeout(f,3e3)):r()}catch(e){l(s(e,`Could not upload annotations for the ${t} ${n}`))}})})}}),writable:!1}}))}};t.exports=a})()},function(t,e,n){(function(e){var n=Object.assign?Object.assign:function(t,e,n,r){for(var o=1;o{const e=Object.freeze({DIR:"DIR",REG:"REG"}),n=Object.freeze({ANNOTATION:"annotation",VALIDATION:"validation",COMPLETED:"completed"}),r=Object.freeze({ANNOTATION:"annotation",INTERPOLATION:"interpolation"}),o=Object.freeze({CHECKBOX:"checkbox",RADIO:"radio",SELECT:"select",NUMBER:"number",TEXT:"text"}),i=Object.freeze({TAG:"tag",SHAPE:"shape",TRACK:"track"}),s=Object.freeze({RECTANGLE:"rectangle",POLYGON:"polygon",POLYLINE:"polyline",POINTS:"points"}),a=Object.freeze({ALL:"all",SHAPE:"shape",NONE:"none"});t.exports={ShareFileType:e,TaskStatus:n,TaskMode:r,AttributeType:o,ObjectType:i,ObjectShape:s,VisibleState:a,LogType:{pasteObject:0,changeAttribute:1,dragObject:2,deleteObject:3,pressShortcut:4,resizeObject:5,sendLogs:6,saveJob:7,jumpFrame:8,drawObject:9,changeLabel:10,sendTaskInfo:11,loadJob:12,moveImage:13,zoomImage:14,lockObject:15,mergeObjects:16,copyObject:17,propagateObject:18,undoAction:19,redoAction:20,sendUserActivity:21,sendException:22,changeFrame:23,debugInfo:24,fitImage:25,rotateImage:26}}})()},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e){t.exports=!1},function(t,e,n){var r=n(14).f,o=n(7),i=n(1)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},function(t,e){t.exports={}},function(t,e,n){n(107),n(4),n(9),n(8),(()=>{const{PluginError:e}=n(5),r=[];class o{static async apiWrapper(t,...n){const r=await o.list();for(const o of r){const r=o.functions.filter(e=>e.callback===t)[0];if(r&&r.enter)try{await r.enter.call(this,o,...n)}catch(t){throw t instanceof e?t:new e(`Exception in plugin ${o.name}: ${t.toString()}`)}}let i=await t.implementation.call(this,...n);for(const o of r){const r=o.functions.filter(e=>e.callback===t)[0];if(r&&r.leave)try{i=await r.leave.call(this,o,i,...n)}catch(t){throw t instanceof e?t:new e(`Exception in plugin ${o.name}: ${t.toString()}`)}}return i}static async register(t){const n=[];if("object"!=typeof t)throw new e(`Plugin should be an object, but got "${typeof t}"`);if(!("name"in t)||"string"!=typeof t.name)throw new e('Plugin must contain a "name" field and it must be a string');if(!("description"in t)||"string"!=typeof t.description)throw new e('Plugin must contain a "description" field and it must be a string');if("functions"in t)throw new e('Plugin must not contain a "functions" field');!function t(e,r){const o={};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&("object"==typeof e[n]?Object.prototype.hasOwnProperty.call(r,n)&&t(e[n],r[n]):["enter","leave"].includes(n)&&"function"==typeof r&&(e[n],1)&&(o.callback=r,o[n]=e[n]));Object.keys(o).length&&n.push(o)}(t,{cvat:this}),Object.defineProperty(t,"functions",{value:n,writable:!1}),r.push(t)}static async list(){return r}}t.exports=o})()},function(t,e,n){var r=n(21);t.exports=function(t){return Object(r(t))}},function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(54),o=n(21);t.exports=function(t){return r(o(t))}},function(t,e,n){var r=n(0),o=n(43),i=n(22),s=r["__core-js_shared__"]||o("__core-js_shared__",{});(t.exports=function(t,e){return s[t]||(s[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.2.1",mode:i?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e,n){var r=n(59),o=n(0),i=function(t){return"function"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?i(r[t])||i(o[t]):r[t]&&r[t][e]||o[t]&&o[t][e]}},function(t,e,n){var r=n(34),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(24);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){var r=n(100),o=n(25),i=n(1)("iterator");t.exports=function(t){if(null!=t)return t[i]||t["@@iterator"]||o[r(t)]}},function(t,e,n){n(4),n(9),n(152),n(8),n(52),(()=>{const e=n(26),r=n(18),{getFrame:o}=n(155),{ArgumentError:i}=n(5),{TaskStatus:s}=n(20),{Label:a}=n(38);function c(t){Object.defineProperties(t,{annotations:Object.freeze({value:{async upload(n,r){return await e.apiWrapper.call(this,t.annotations.upload,n,r)},async save(){return await e.apiWrapper.call(this,t.annotations.save)},async clear(n=!1){return await e.apiWrapper.call(this,t.annotations.clear,n)},async dump(n,r){return await e.apiWrapper.call(this,t.annotations.dump,n,r)},async statistics(){return await e.apiWrapper.call(this,t.annotations.statistics)},async put(n=[]){return await e.apiWrapper.call(this,t.annotations.put,n)},async get(n,r={}){return await e.apiWrapper.call(this,t.annotations.get,n,r)},async search(n,r,o){return await e.apiWrapper.call(this,t.annotations.search,n,r,o)},async select(n,r,o){return await e.apiWrapper.call(this,t.annotations.select,n,r,o)},async hasUnsavedChanges(){return await e.apiWrapper.call(this,t.annotations.hasUnsavedChanges)},async merge(n){return await e.apiWrapper.call(this,t.annotations.merge,n)},async split(n,r){return await e.apiWrapper.call(this,t.annotations.split,n,r)},async group(n,r=!1){return await e.apiWrapper.call(this,t.annotations.group,n,r)}},writable:!0}),frames:Object.freeze({value:{async get(n){return await e.apiWrapper.call(this,t.frames.get,n)}},writable:!0}),logs:Object.freeze({value:{async put(n,r){return await e.apiWrapper.call(this,t.logs.put,n,r)},async save(n){return await e.apiWrapper.call(this,t.logs.save,n)}},writable:!0}),actions:Object.freeze({value:{async undo(n){return await e.apiWrapper.call(this,t.actions.undo,n)},async redo(n){return await e.apiWrapper.call(this,t.actions.redo,n)},async clear(){return await e.apiWrapper.call(this,t.actions.clear)}},writable:!0}),events:Object.freeze({value:{async subscribe(n,r){return await e.apiWrapper.call(this,t.events.subscribe,n,r)},async unsubscribe(n,r=null){return await e.apiWrapper.call(this,t.events.unsubscribe,n,r)}},writable:!0})})}class u{constructor(){}}class l extends u{constructor(t){super();const e={id:void 0,assignee:void 0,status:void 0,start_frame:void 0,stop_frame:void 0,task:void 0};for(const n in e)if(Object.prototype.hasOwnProperty.call(e,n)&&(n in t&&(e[n]=t[n]),void 0===e[n]))throw new i(`Job field "${n}" was not initialized`);Object.defineProperties(this,Object.freeze({id:{get:()=>e.id},assignee:{get:()=>e.assignee,set:()=>t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a non negative integer");e.assignee=t}},status:{get:()=>e.status,set:t=>{const n=s;let r=!1;for(const e in n)if(n[e]===t){r=!0;break}if(!r)throw new i("Value must be a value from the enumeration cvat.enums.TaskStatus");e.status=t}},startFrame:{get:()=>e.start_frame},stopFrame:{get:()=>e.stop_frame},task:{get:()=>e.task}})),this.annotations={get:Object.getPrototypeOf(this).annotations.get.bind(this),put:Object.getPrototypeOf(this).annotations.put.bind(this),save:Object.getPrototypeOf(this).annotations.save.bind(this),dump:Object.getPrototypeOf(this).annotations.dump.bind(this),merge:Object.getPrototypeOf(this).annotations.merge.bind(this),split:Object.getPrototypeOf(this).annotations.split.bind(this),group:Object.getPrototypeOf(this).annotations.group.bind(this),clear:Object.getPrototypeOf(this).annotations.clear.bind(this),upload:Object.getPrototypeOf(this).annotations.upload.bind(this),select:Object.getPrototypeOf(this).annotations.select.bind(this),statistics:Object.getPrototypeOf(this).annotations.statistics.bind(this),hasUnsavedChanges:Object.getPrototypeOf(this).annotations.hasUnsavedChanges.bind(this)},this.frames={get:Object.getPrototypeOf(this).frames.get.bind(this)}}async save(){return await e.apiWrapper.call(this,l.prototype.save)}}class f extends u{constructor(t){super();const e={id:void 0,name:void 0,status:void 0,size:void 0,mode:void 0,owner:void 0,assignee:void 0,created_date:void 0,updated_date:void 0,bug_tracker:void 0,overlap:void 0,segment_size:void 0,z_order:void 0,image_quality:void 0,start_frame:void 0,stop_frame:void 0,frame_filter:void 0};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&n in t&&(e[n]=t[n]);if(e.labels=[],e.jobs=[],e.files=Object.freeze({server_files:[],client_files:[],remote_files:[]}),Array.isArray(t.segments))for(const n of t.segments)if(Array.isArray(n.jobs))for(const t of n.jobs){const r=new l({url:t.url,id:t.id,assignee:t.assignee,status:t.status,start_frame:n.start_frame,stop_frame:n.stop_frame,task:this});e.jobs.push(r)}if(Array.isArray(t.labels))for(const n of t.labels){const t=new a(n);e.labels.push(t)}Object.defineProperties(this,Object.freeze({id:{get:()=>e.id},name:{get:()=>e.name,set:t=>{if(!t.trim().length)throw new i("Value must not be empty");e.name=t}},status:{get:()=>e.status},size:{get:()=>e.size},mode:{get:()=>e.mode},owner:{get:()=>e.owner},assignee:{get:()=>e.assignee,set:()=>t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a non negative integer");e.assignee=t}},createdDate:{get:()=>e.created_date},updatedDate:{get:()=>e.updated_date},bugTracker:{get:()=>e.bug_tracker,set:t=>{e.bug_tracker=t}},overlap:{get:()=>e.overlap,set:t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a non negative integer");e.overlap=t}},segmentSize:{get:()=>e.segment_size,set:t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a positive integer");e.segment_size=t}},zOrder:{get:()=>e.z_order,set:t=>{if("boolean"!=typeof t)throw new i("Value must be a boolean");e.z_order=t}},imageQuality:{get:()=>e.image_quality,set:t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a positive integer");e.image_quality=t}},labels:{get:()=>[...e.labels],set:t=>{if(!Array.isArray(t))throw new i("Value must be an array of Labels");for(const e of t)if(!(e instanceof a))throw new i("Each array value must be an instance of Label. "+`${typeof e} was found`);void 0===e.id?e.labels=[...t]:e.labels=e.labels.concat([...t])}},jobs:{get:()=>[...e.jobs]},serverFiles:{get:()=>[...e.files.server_files],set:t=>{if(!Array.isArray(t))throw new i(`Value must be an array. But ${typeof t} has been got.`);for(const e of t)if("string"!=typeof e)throw new i(`Array values must be a string. But ${typeof e} has been got.`);Array.prototype.push.apply(e.files.server_files,t)}},clientFiles:{get:()=>[...e.files.client_files],set:t=>{if(!Array.isArray(t))throw new i(`Value must be an array. But ${typeof t} has been got.`);for(const e of t)if(!(e instanceof File))throw new i(`Array values must be a File. But ${e.constructor.name} has been got.`);Array.prototype.push.apply(e.files.client_files,t)}},remoteFiles:{get:()=>[...e.files.remote_files],set:t=>{if(!Array.isArray(t))throw new i(`Value must be an array. But ${typeof t} has been got.`);for(const e of t)if("string"!=typeof e)throw new i(`Array values must be a string. But ${typeof e} has been got.`);Array.prototype.push.apply(e.files.remote_files,t)}},startFrame:{get:()=>e.start_frame,set:t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a not negative integer");e.start_frame=t}},stopFrame:{get:()=>e.stop_frame,set:t=>{if(!Number.isInteger(t)||t<0)throw new i("Value must be a not negative integer");e.stop_frame=t}},frameFilter:{get:()=>e.frame_filter,set:t=>{if("string"!=typeof t)throw new i(`Filter value must be a string. But ${typeof t} has been got.`);e.frame_filter=t}}})),this.annotations={get:Object.getPrototypeOf(this).annotations.get.bind(this),put:Object.getPrototypeOf(this).annotations.put.bind(this),save:Object.getPrototypeOf(this).annotations.save.bind(this),dump:Object.getPrototypeOf(this).annotations.dump.bind(this),merge:Object.getPrototypeOf(this).annotations.merge.bind(this),split:Object.getPrototypeOf(this).annotations.split.bind(this),group:Object.getPrototypeOf(this).annotations.group.bind(this),clear:Object.getPrototypeOf(this).annotations.clear.bind(this),upload:Object.getPrototypeOf(this).annotations.upload.bind(this),select:Object.getPrototypeOf(this).annotations.select.bind(this),statistics:Object.getPrototypeOf(this).annotations.statistics.bind(this),hasUnsavedChanges:Object.getPrototypeOf(this).annotations.hasUnsavedChanges.bind(this)},this.frames={get:Object.getPrototypeOf(this).frames.get.bind(this)}}async save(t=(()=>{})){return await e.apiWrapper.call(this,f.prototype.save,t)}async delete(){return await e.apiWrapper.call(this,f.prototype.delete)}}t.exports={Job:l,Task:f};const{getAnnotations:p,putAnnotations:h,saveAnnotations:d,hasUnsavedChanges:b,mergeAnnotations:g,splitAnnotations:m,groupAnnotations:y,clearAnnotations:v,selectObject:w,annotationsStatistics:O,uploadAnnotations:x,dumpAnnotations:j}=n(157);c(l.prototype),c(f.prototype),l.prototype.save.implementation=async function(){if(this.id){const t={status:this.status};return await r.jobs.saveJob(this.id,t),this}throw new i("Can not save job without and id")},l.prototype.frames.get.implementation=async function(t){if(!Number.isInteger(t)||t<0)throw new i(`Frame must be a positive integer. Got: "${t}"`);if(tthis.stopFrame)throw new i(`The frame with number ${t} is out of the job`);return await o(this.task.id,this.task.mode,t)},l.prototype.annotations.get.implementation=async function(t,e){if(tthis.stopFrame)throw new i(`Frame ${t} does not exist in the job`);return await p(this,t,e)},l.prototype.annotations.save.implementation=async function(t){return await d(this,t)},l.prototype.annotations.merge.implementation=async function(t){return await g(this,t)},l.prototype.annotations.split.implementation=async function(t,e){return await m(this,t,e)},l.prototype.annotations.group.implementation=async function(t,e){return await y(this,t,e)},l.prototype.annotations.hasUnsavedChanges.implementation=function(){return b(this)},l.prototype.annotations.clear.implementation=async function(t){return await v(this,t)},l.prototype.annotations.select.implementation=function(t,e,n){return w(this,t,e,n)},l.prototype.annotations.statistics.implementation=function(){return O(this)},l.prototype.annotations.put.implementation=function(t){return h(this,t)},l.prototype.annotations.upload.implementation=async function(t,e){return await x(this,t,e)},l.prototype.annotations.dump.implementation=async function(t,e){return await j(this,t,e)},f.prototype.save.implementation=async function(t){if(void 0!==this.id){const t={name:this.name,bug_tracker:this.bugTracker,z_order:this.zOrder,labels:[...this.labels.map(t=>t.toJSON())]};return await r.tasks.saveTask(this.id,t),this}const e={name:this.name,labels:this.labels.map(t=>t.toJSON()),image_quality:this.imageQuality,z_order:Boolean(this.zOrder)};void 0!==this.bugTracker&&(e.bug_tracker=this.bugTracker),void 0!==this.segmentSize&&(e.segment_size=this.segmentSize),void 0!==this.overlap&&(e.overlap=this.overlap),void 0!==this.startFrame&&(e.start_frame=this.startFrame),void 0!==this.stopFrame&&(e.stop_frame=this.stopFrame),void 0!==this.frameFilter&&(e.frame_filter=this.frameFilter);const n={client_files:this.clientFiles,server_files:this.serverFiles,remote_files:this.remoteFiles},o=await r.tasks.createTask(e,n,t);return new f(o)},f.prototype.delete.implementation=async function(){return await r.tasks.deleteTask(this.id)},f.prototype.frames.get.implementation=async function(t){if(!Number.isInteger(t)||t<0)throw new i(`Frame must be a positive integer. Got: "${t}"`);if(t>=this.size)throw new i(`The frame with number ${t} is out of the task`);return await o(this.id,this.mode,t)},f.prototype.annotations.get.implementation=async function(t,e){if(!Number.isInteger(t)||t<0)throw new i(`Frame must be a positive integer. Got: "${t}"`);if(t>=this.size)throw new i(`Frame ${t} does not exist in the task`);return await p(this,t,e)},f.prototype.annotations.save.implementation=async function(t){return await d(this,t)},f.prototype.annotations.merge.implementation=async function(t){return await g(this,t)},f.prototype.annotations.split.implementation=async function(t,e){return await m(this,t,e)},f.prototype.annotations.group.implementation=async function(t,e){return await y(this,t,e)},f.prototype.annotations.hasUnsavedChanges.implementation=function(){return b(this)},f.prototype.annotations.clear.implementation=async function(t){return await v(this,t)},f.prototype.annotations.select.implementation=function(t,e,n){return w(this,t,e,n)},f.prototype.annotations.statistics.implementation=function(){return O(this)},f.prototype.annotations.put.implementation=function(t){return h(this,t)},f.prototype.annotations.upload.implementation=async function(t,e){return await x(this,t,e)},f.prototype.annotations.dump.implementation=async function(t,e){return await j(this,t,e)}})()},function(t,e,n){n(4),n(8),n(52),(()=>{const{AttributeType:e}=n(20),{ArgumentError:r}=n(5);class o{constructor(t){const n={id:void 0,default_value:void 0,input_type:void 0,mutable:void 0,name:void 0,values:void 0};for(const e in n)Object.prototype.hasOwnProperty.call(n,e)&&Object.prototype.hasOwnProperty.call(t,e)&&(Array.isArray(t[e])?n[e]=[...t[e]]:n[e]=t[e]);if(!Object.values(e).includes(n.input_type))throw new r(`Got invalid attribute type ${n.input_type}`);Object.defineProperties(this,Object.freeze({id:{get:()=>n.id},defaultValue:{get:()=>n.default_value},inputType:{get:()=>n.input_type},mutable:{get:()=>n.mutable},name:{get:()=>n.name},values:{get:()=>[...n.values]}}))}toJSON(){const t={name:this.name,mutable:this.mutable,input_type:this.inputType,default_value:this.defaultValue,values:this.values};return void 0!==this.id&&(t.id=this.id),t}}t.exports={Attribute:o,Label:class{constructor(t){const e={id:void 0,name:void 0};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);if(e.attributes=[],Object.prototype.hasOwnProperty.call(t,"attributes")&&Array.isArray(t.attributes))for(const n of t.attributes)e.attributes.push(new o(n));Object.defineProperties(this,Object.freeze({id:{get:()=>e.id},name:{get:()=>e.name},attributes:{get:()=>[...e.attributes]}}))}toJSON(){const t={name:this.name,attributes:[...this.attributes.map(t=>t.toJSON())]};return void 0!==this.id&&(t.id=this.id),t}}}})()},function(t,e,n){(()=>{const{ArgumentError:e}=n(5);t.exports={isBoolean:function(t){return"boolean"==typeof t},isInteger:function(t){return"number"==typeof t&&Number.isInteger(t)},isEnum:function(t){for(const e in this)if(Object.prototype.hasOwnProperty.call(this,e)&&this[e]===t)return!0;return!1},isString:function(t){return"string"==typeof t},checkFilter:function(t,n){for(const r in t)if(Object.prototype.hasOwnProperty.call(t,r)){if(!(r in n))throw new e(`Unsupported filter property has been recieved: "${r}"`);if(!n[r](t[r]))throw new e(`Received filter property "${r}" is not satisfied for checker`)}},checkObjectType:function(t,n,r,o){if(r){if(typeof n!==r){if("integer"===r&&Number.isInteger(n))return;throw new e(`"${t}" is expected to be "${r}", but "${typeof n}" has been got.`)}}else if(o&&!(n instanceof o)){if(void 0!==n)throw new e(`"${t}" is expected to be ${o.name}, but `+`"${n.constructor.name}" has been got`);throw new e(`"${t}" is expected to be ${o.name}, but "undefined" has been got.`)}}}})()},function(t,e,n){var r=n(10),o=n(53),i=n(29),s=n(30),a=n(41),c=n(7),u=n(55),l=Object.getOwnPropertyDescriptor;e.f=r?l:function(t,e){if(t=s(t),e=a(e,!0),u)try{return l(t,e)}catch(t){}if(c(t,e))return i(!o.f.call(t,e),t[e])}},function(t,e,n){var r=n(11);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(0),o=n(11),i=r.document,s=o(i)&&o(i.createElement);t.exports=function(t){return s?i.createElement(t):{}}},function(t,e,n){var r=n(0),o=n(12);t.exports=function(t,e){try{o(r,t,e)}catch(n){r[t]=e}return e}},function(t,e,n){var r=n(31),o=n(57),i=r("keys");t.exports=function(t){return i[t]||(i[t]=o(t))}},function(t,e){t.exports={}},function(t,e){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation");return t}},function(t,e){t.exports={backendAPI:"http://localhost:7000/api/v1",proxy:!1,taskID:void 0,jobID:void 0,clientID:+Date.now().toString().substr(-6)}},function(t,e,n){var r=n(34),o=n(21),i=function(t){return function(e,n){var i,s,a=String(o(e)),c=r(n),u=a.length;return c<0||c>=u?t?"":void 0:(i=a.charCodeAt(c))<55296||i>56319||c+1===u||(s=a.charCodeAt(c+1))<56320||s>57343?t?a.charAt(c):i:t?a.slice(c,c+2):s-56320+(i-55296<<10)+65536}};t.exports={codeAt:i(!1),charAt:i(!0)}},function(t,e,n){"use strict";(function(e){var r=n(6),o=n(138),i={"Content-Type":"application/x-www-form-urlencoded"};function s(t,e){!r.isUndefined(t)&&r.isUndefined(t["Content-Type"])&&(t["Content-Type"]=e)}var a,c={adapter:("undefined"!=typeof XMLHttpRequest?a=n(80):void 0!==e&&(a=n(80)),a),transformRequest:[function(t,e){return o(e,"Content-Type"),r.isFormData(t)||r.isArrayBuffer(t)||r.isBuffer(t)||r.isStream(t)||r.isFile(t)||r.isBlob(t)?t:r.isArrayBufferView(t)?t.buffer:r.isURLSearchParams(t)?(s(e,"application/x-www-form-urlencoded;charset=utf-8"),t.toString()):r.isObject(t)?(s(e,"application/json;charset=utf-8"),JSON.stringify(t)):t}],transformResponse:[function(t){if("string"==typeof t)try{t=JSON.parse(t)}catch(t){}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(t){return t>=200&&t<300}};c.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],function(t){c.headers[t]={}}),r.forEach(["post","put","patch"],function(t){c.headers[t]=r.merge(i)}),t.exports=c}).call(this,n(79))},function(t,e,n){n(4),n(9),n(8),(()=>{const e=n(26),{ArgumentError:r}=n(5);class o{constructor(t){const e={label:null,attributes:{},points:null,outside:null,occluded:null,keyframe:null,group:null,zOrder:null,lock:null,color:null,visibility:null,clientID:t.clientID,serverID:t.serverID,frame:t.frame,objectType:t.objectType,shapeType:t.shapeType,updateFlags:{}};Object.defineProperty(e.updateFlags,"reset",{value:function(){this.label=!1,this.attributes=!1,this.points=!1,this.outside=!1,this.occluded=!1,this.keyframe=!1,this.group=!1,this.zOrder=!1,this.lock=!1,this.color=!1,this.visibility=!1},writable:!1}),Object.defineProperties(this,Object.freeze({updateFlags:{get:()=>e.updateFlags},frame:{get:()=>e.frame},objectType:{get:()=>e.objectType},shapeType:{get:()=>e.shapeType},clientID:{get:()=>e.clientID},serverID:{get:()=>e.serverID},label:{get:()=>e.label,set:t=>{e.updateFlags.label=!0,e.label=t}},color:{get:()=>e.color,set:t=>{e.updateFlags.color=!0,e.color=t}},visibility:{get:()=>e.visibility,set:t=>{e.updateFlags.visibility=!0,e.visibility=t}},points:{get:()=>e.points,set:t=>{if(!Array.isArray(t))throw new r("Points are expected to be an array "+`but got ${"object"==typeof t?t.constructor.name:typeof t}`);e.updateFlags.points=!0,e.points=[...t]}},group:{get:()=>e.group,set:t=>{e.updateFlags.group=!0,e.group=t}},zOrder:{get:()=>e.zOrder,set:t=>{e.updateFlags.zOrder=!0,e.zOrder=t}},outside:{get:()=>e.outside,set:t=>{e.updateFlags.outside=!0,e.outside=t}},keyframe:{get:()=>e.keyframe,set:t=>{e.updateFlags.keyframe=!0,e.keyframe=t}},occluded:{get:()=>e.occluded,set:t=>{e.updateFlags.occluded=!0,e.occluded=t}},lock:{get:()=>e.lock,set:t=>{e.updateFlags.lock=!0,e.lock=t}},attributes:{get:()=>e.attributes,set:t=>{if("object"!=typeof t)throw new r("Attributes are expected to be an object "+`but got ${"object"==typeof t?t.constructor.name:typeof t}`);for(const n of Object.keys(t))e.updateFlags.attributes=!0,e.attributes[n]=t[n]}}})),this.label=t.label,this.group=t.group,this.zOrder=t.zOrder,this.outside=t.outside,this.keyframe=t.keyframe,this.occluded=t.occluded,this.color=t.color,this.lock=t.lock,this.visibility=t.visibility,void 0!==t.points&&(this.points=t.points),void 0!==t.attributes&&(this.attributes=t.attributes),e.updateFlags.reset()}async save(){return await e.apiWrapper.call(this,o.prototype.save)}async delete(t=!1){return await e.apiWrapper.call(this,o.prototype.delete,t)}async up(){return await e.apiWrapper.call(this,o.prototype.up)}async down(){return await e.apiWrapper.call(this,o.prototype.down)}}o.prototype.save.implementation=async function(){return this.hidden&&this.hidden.save?this.hidden.save():this},o.prototype.delete.implementation=async function(t){return!(!this.hidden||!this.hidden.delete)&&this.hidden.delete(t)},o.prototype.up.implementation=async function(){return!(!this.hidden||!this.hidden.up)&&this.hidden.up()},o.prototype.down.implementation=async function(){return!(!this.hidden||!this.hidden.down)&&this.hidden.down()},t.exports=o})()},function(t,e,n){"use strict";n(13)({target:"URL",proto:!0,enumerable:!0},{toJSON:function(){return URL.prototype.toString.call(this)}})},function(t,e,n){"use strict";var r={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!r.call({1:2},1);e.f=i?function(t){var e=o(this,t);return!!e&&e.enumerable}:r},function(t,e,n){var r=n(2),o=n(16),i="".split;t.exports=r(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==o(t)?i.call(t,""):Object(t)}:Object},function(t,e,n){var r=n(10),o=n(2),i=n(42);t.exports=!r&&!o(function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(31);t.exports=r("native-function-to-string",Function.toString)},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++n+r).toString(36)}},function(t,e,n){var r=n(7),o=n(92),i=n(40),s=n(14);t.exports=function(t,e){for(var n=o(e),a=s.f,c=i.f,u=0;uc;)r(a,n=e[c++])&&(~i(u,n)||u.push(n));return u}},function(t,e){e.f=Object.getOwnPropertySymbols},function(t,e,n){var r=n(2),o=/#|\.prototype\./,i=function(t,e){var n=a[s(t)];return n==u||n!=c&&("function"==typeof e?r(e):!!e)},s=i.normalize=function(t){return String(t).replace(o,".").toLowerCase()},a=i.data={},c=i.NATIVE="N",u=i.POLYFILL="P";t.exports=i},function(t,e,n){var r=n(15);t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},function(t,e,n){var r=n(1),o=n(25),i=r("iterator"),s=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||s[i]===t)}},function(t,e,n){var r=n(3);t.exports=function(t,e,n,o){try{return o?e(r(n)[0],n[1]):e(n)}catch(e){var i=t.return;throw void 0!==i&&r(i.call(t)),e}}},function(t,e,n){var r,o,i,s=n(0),a=n(2),c=n(16),u=n(35),l=n(67),f=n(42),p=s.location,h=s.setImmediate,d=s.clearImmediate,b=s.process,g=s.MessageChannel,m=s.Dispatch,y=0,v={},w=function(t){if(v.hasOwnProperty(t)){var e=v[t];delete v[t],e()}},O=function(t){return function(){w(t)}},x=function(t){w(t.data)},j=function(t){s.postMessage(t+"",p.protocol+"//"+p.host)};h&&d||(h=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return v[++y]=function(){("function"==typeof t?t:Function(t)).apply(void 0,e)},r(y),y},d=function(t){delete v[t]},"process"==c(b)?r=function(t){b.nextTick(O(t))}:m&&m.now?r=function(t){m.now(O(t))}:g?(i=(o=new g).port2,o.port1.onmessage=x,r=u(i.postMessage,i,1)):!s.addEventListener||"function"!=typeof postMessage||s.importScripts||a(j)?r="onreadystatechange"in f("script")?function(t){l.appendChild(f("script")).onreadystatechange=function(){l.removeChild(this),w(t)}}:function(t){setTimeout(O(t),0)}:(r=j,s.addEventListener("message",x,!1))),t.exports={set:h,clear:d}},function(t,e,n){var r=n(32);t.exports=r("document","documentElement")},function(t,e,n){var r=n(32);t.exports=r("navigator","userAgent")||""},function(t,e,n){"use strict";var r=n(24),o=function(t){var e,n;this.promise=new t(function(t,r){if(void 0!==e||void 0!==n)throw TypeError("Bad Promise constructor");e=t,n=r}),this.resolve=r(e),this.reject=r(n)};t.exports.f=function(t){return new o(t)}},function(t,e,n){var r=n(3),o=n(71),i=n(46),s=n(45),a=n(67),c=n(42),u=n(44)("IE_PROTO"),l=function(){},f=function(){var t,e=c("iframe"),n=i.length;for(e.style.display="none",a.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(" + + + +
    + + + diff --git a/cvat-ui/public/manifest.json b/cvat-ui/public/manifest.json new file mode 100644 index 00000000..43611204 --- /dev/null +++ b/cvat-ui/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "CVAT", + "name": "Computer Vision Annotation Tool", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/cvat-ui/react_nginx.conf b/cvat-ui/react_nginx.conf new file mode 100644 index 00000000..9a8bbc92 --- /dev/null +++ b/cvat-ui/react_nginx.conf @@ -0,0 +1,7 @@ +server { + root /usr/share/nginx/html; + # Any route that doesn't have a file extension (e.g. /devices) + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/cvat-ui/src/actions/annotations.actions.ts b/cvat-ui/src/actions/annotations.actions.ts new file mode 100644 index 00000000..85b417ad --- /dev/null +++ b/cvat-ui/src/actions/annotations.actions.ts @@ -0,0 +1,75 @@ +import { Dispatch, ActionCreator } from 'redux'; + + +export const dumpAnnotation = () => (dispatch: Dispatch) => { + dispatch({ + type: 'DUMP_ANNOTATION', + }); +} + +export const dumpAnnotationSuccess = (downloadLink: string) => (dispatch: Dispatch) => { + dispatch({ + type: 'DUMP_ANNOTATION_SUCCESS', + payload: downloadLink, + }); +} + +export const dumpAnnotationError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'DUMP_ANNOTATION_ERROR', + payload: error, + }); +} + +export const uploadAnnotation = () => (dispatch: Dispatch) => { + dispatch({ + type: 'UPLOAD_ANNOTATION', + }); +} + +export const uploadAnnotationSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'UPLOAD_ANNOTATION_SUCCESS', + }); +} + +export const uploadAnnotationError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'UPLOAD_ANNOTATION_ERROR', + payload: error, + }); +} + +export const dumpAnnotationAsync = (task: any, dumper: any) => { + return (dispatch: ActionCreator) => { + dispatch(dumpAnnotation()); + + return task.annotations.dump(task.name, dumper).then( + (downloadLink: string) => { + dispatch(dumpAnnotationSuccess(downloadLink)); + }, + (error: any) => { + dispatch(dumpAnnotationError(error)); + + throw error; + }, + ); + }; +} + +export const uploadAnnotationAsync = (task: any, file: File, loader: any) => { + return (dispatch: ActionCreator) => { + dispatch(uploadAnnotation()); + + return task.annotations.upload(file, loader).then( + (response: any) => { + dispatch(uploadAnnotationSuccess()); + }, + (error: any) => { + dispatch(uploadAnnotationError(error)); + + throw error; + }, + ); + }; +} diff --git a/cvat-ui/src/actions/auth.actions.ts b/cvat-ui/src/actions/auth.actions.ts new file mode 100644 index 00000000..9447403e --- /dev/null +++ b/cvat-ui/src/actions/auth.actions.ts @@ -0,0 +1,172 @@ +import { History } from 'history'; +import { Dispatch, ActionCreator } from 'redux'; + + +export const login = () => (dispatch: Dispatch) => { + dispatch({ + type: 'LOGIN', + }); +} + +export const loginSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'LOGIN_SUCCESS', + }); +} + +export const loginError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'LOGIN_ERROR', + payload: error, + }); +} + +export const logout = () => (dispatch: Dispatch) => { + dispatch({ + type: 'LOGOUT', + }); +} + +export const logoutSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'LOGOUT_SUCCESS', + }); +} + +export const logoutError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'LOGOUT_ERROR', + payload: error, + }); +} + +export const isAuthenticated = () => (dispatch: Dispatch) => { + dispatch({ + type: 'IS_AUTHENTICATED', + }); +} + +export const isAuthenticatedSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'IS_AUTHENTICATED_SUCCESS', + }); +} + +export const isAuthenticatedFail = () => (dispatch: Dispatch) => { + dispatch({ + type: 'IS_AUTHENTICATED_FAIL', + }); +} + +export const isAuthenticatedError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'IS_AUTHENTICATED_ERROR', + payload: error, + }); +} + +export const register = () => (dispatch: Dispatch) => { + dispatch({ + type: 'REGISTER', + }); +} + +export const registerSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'REGISTER_SUCCESS', + }); +} + +export const registerError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'REGISTER_ERROR', + payload: error, + }); +} + +export const loginAsync = (username: string, password: string, history: History) => { + return (dispatch: ActionCreator) => { + dispatch(login()); + + return (window as any).cvat.server.login(username, password).then( + (loggedIn: any) => { + dispatch(loginSuccess()); + + history.push(history.location.state ? history.location.state.from : '/tasks'); + }, + (error: any) => { + dispatch(loginError(error)); + + throw error; + }, + ); + }; +} + +export const logoutAsync = () => { + return (dispatch: ActionCreator) => { + dispatch(logout()); + + return (window as any).cvat.server.logout().then( + (loggedOut: any) => { + dispatch(logoutSuccess()); + }, + (error: any) => { + dispatch(logoutError(error)); + + throw error; + }, + ); + }; +} + +export const isAuthenticatedAsync = () => { + return (dispatch: ActionCreator) => { + dispatch(isAuthenticated()); + + return (window as any).cvat.server.authorized().then( + (isAuthenticated: boolean) => { + isAuthenticated ? dispatch(isAuthenticatedSuccess()) : dispatch(isAuthenticatedFail()); + }, + (error: any) => { + dispatch(isAuthenticatedError(error)); + + throw error; + }, + ); + }; +} + +export const registerAsync = ( + username: string, + firstName: string, + lastName: string, + email: string, + password: string, + passwordConfirmation: string, + history: History, +) => { + return (dispatch: ActionCreator) => { + dispatch(register()); + + return (window as any).cvat.server.register( + username, + firstName, + lastName, + email, + password, + passwordConfirmation, + ).then( + (registered: any) => { + dispatch(registerSuccess()); + + history.replace('/login'); + }, + (error: any) => { + dispatch(registerError(error)); + + throw error; + }, + ); + }; +} diff --git a/cvat-ui/src/actions/server.actions.ts b/cvat-ui/src/actions/server.actions.ts new file mode 100644 index 00000000..3c5e9af4 --- /dev/null +++ b/cvat-ui/src/actions/server.actions.ts @@ -0,0 +1,109 @@ +import { Dispatch, ActionCreator } from 'redux'; + + +export const getServerInfo = () => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_SERVER_INFO', + }); +} + +export const getServerInfoSuccess = (information: null) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_SERVER_INFO_SUCCESS', + payload: information, + }); +} + +export const getServerInfoError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_SERVER_INFO_ERROR', + payload: error, + }); +} + +export const getShareFiles = () => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_SHARE_FILES', + }); +} + +export const getShareFilesSuccess = (files: []) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_SHARE_FILES_SUCCESS', + payload: files, + }); +} + +export const getShareFilesError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_SHARE_FILES_ERROR', + payload: error, + }); +} + +export const getAnnotationFormats = () => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_ANNOTATION_FORMATS', + }); +} + +export const getAnnotationFormatsSuccess = (annotationFormats: []) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_ANNOTATION_FORMATS_SUCCESS', + payload: annotationFormats, + }); +} + +export const getAnnotationFormatsError = (error = {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_ANNOTATION_FORMATS_ERROR', + payload: error, + }); +} + +export const getServerInfoAsync = () => { + return (dispatch: ActionCreator) => { + dispatch(getServerInfo()); + + return (window as any).cvat.server.about().then( + (information: any) => { + dispatch(getServerInfoSuccess(information)); + }, + (error: any) => { + dispatch(getServerInfoError(error)); + }, + ); + }; +} + +export const getShareFilesAsync = (directory: string) => { + return (dispatch: ActionCreator) => { + dispatch(getShareFiles()); + + return (window as any).cvat.server.share(directory).then( + (files: any) => { + dispatch(getShareFilesSuccess(files)); + }, + (error: any) => { + dispatch(getShareFilesError(error)); + }, + ); + }; +} + +export const getAnnotationFormatsAsync = () => { + return (dispatch: ActionCreator) => { + dispatch(getAnnotationFormats()); + + return (window as any).cvat.server.formats().then( + (formats: any) => { + dispatch(getAnnotationFormatsSuccess(formats)); + }, + (error: any) => { + dispatch(getAnnotationFormatsError(error)); + + throw error; + }, + ); + }; +} diff --git a/cvat-ui/src/actions/tasks-filter.actions.ts b/cvat-ui/src/actions/tasks-filter.actions.ts new file mode 100644 index 00000000..6a333b35 --- /dev/null +++ b/cvat-ui/src/actions/tasks-filter.actions.ts @@ -0,0 +1,9 @@ +import { Dispatch } from 'redux'; + + +export const filterTasks = (queryParams: { search?: string, page?: number }) => (dispatch: Dispatch) => { + dispatch({ + type: 'FILTER_TASKS', + payload: queryParams, + }); +} diff --git a/cvat-ui/src/actions/tasks.actions.ts b/cvat-ui/src/actions/tasks.actions.ts new file mode 100644 index 00000000..cedddd8b --- /dev/null +++ b/cvat-ui/src/actions/tasks.actions.ts @@ -0,0 +1,175 @@ +import { History } from 'history'; +import { Dispatch, ActionCreator } from 'redux'; + +import queryString from 'query-string'; + +import setQueryObject from '../utils/tasks-filter' + + +export const getTasks = () => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_TASKS', + }); +} + +export const getTasksSuccess = (tasks: []) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_TASKS_SUCCESS', + payload: tasks, + }); +} + +export const getTasksError = (error: {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_TASKS_ERROR', + payload: error, + }); +} + +export const createTask = () => (dispatch: Dispatch) => { + dispatch({ + type: 'CREATE_TASK', + }); +} + +export const createTaskSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'CREATE_TASK_SUCCESS', + }); +} + +export const createTaskError = (error: {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'CREATE_TASK_ERROR', + payload: error, + }); +} + +export const updateTask = () => (dispatch: Dispatch) => { + dispatch({ + type: 'UPDATE_TASK', + }); +} + +export const updateTaskSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'UPDATE_TASK_SUCCESS', + }); +} + +export const updateTaskError = (error: {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'UPDATE_TASK_ERROR', + payload: error, + }); +} + +export const deleteTask = () => (dispatch: Dispatch) => { + dispatch({ + type: 'DELETE_TASK', + }); +} + +export const deleteTaskSuccess = () => (dispatch: Dispatch) => { + dispatch({ + type: 'DELETE_TASK_SUCCESS', + }); +} + +export const deleteTaskError = (error: {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'DELETE_TASK_ERROR', + payload: error, + }); +} + +export const getTasksAsync = (queryObject = {}) => { + return (dispatch: ActionCreator) => { + dispatch(getTasks()); + + return (window as any).cvat.tasks.get(queryObject).then( + (tasks: any) => { + dispatch(getTasksSuccess(tasks)); + }, + (error: any) => { + dispatch(getTasksError(error)); + + throw error; + }, + ); + }; +} + +export const createTaskAsync = (task: any) => { + return (dispatch: ActionCreator) => { + dispatch(createTask()); + + return task.save().then( + (created: any) => { + dispatch(createTaskSuccess()); + + return dispatch(getTasksAsync()); + }, + (error: any) => { + dispatch(createTaskError(error)); + + throw error; + }, + ); + }; +} + +export const updateTaskAsync = (task: any) => { + return (dispatch: ActionCreator) => { + dispatch(updateTask()); + + return task.save().then( + (updated: any) => { + dispatch(updateTaskSuccess()); + + return dispatch(getTasksAsync()); + }, + (error: any) => { + dispatch(updateTaskError(error)); + + throw error; + }, + ); + }; +} + +export const deleteTaskAsync = (task: any, history: History) => { + return (dispatch: ActionCreator, getState: any) => { + dispatch(deleteTask()); + + return task.delete().then( + (deleted: any) => { + dispatch(deleteTaskSuccess()); + + const state = getState(); + + const queryObject = { + page: state.tasksFilter.currentPage, + search: state.tasksFilter.searchQuery, + } + + if (state.tasks.tasks.length === 1 && state.tasks.tasksCount !== 1) { + queryObject.page = queryObject.page - 1; + + history.push({ search: queryString.stringify(queryObject) }); + } else if (state.tasks.tasksCount === 1) { + return dispatch(getTasksAsync()); + } else { + const query = setQueryObject(queryObject); + + return dispatch(getTasksAsync(query)); + } + }, + (error: any) => { + dispatch(deleteTaskError(error)); + + throw error; + }, + ); + }; +} diff --git a/cvat-ui/src/actions/users.actions.ts b/cvat-ui/src/actions/users.actions.ts new file mode 100644 index 00000000..93e50ac1 --- /dev/null +++ b/cvat-ui/src/actions/users.actions.ts @@ -0,0 +1,40 @@ +import { Dispatch, ActionCreator } from 'redux'; + + +export const getUsers = () => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_USERS', + }); +} + +export const getUsersSuccess = (users: [], isCurrentUser: boolean) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_USERS_SUCCESS', + payload: users, + currentUser: isCurrentUser ? (users as any)[0] : isCurrentUser, + }); +} + +export const getUsersError = (error: {}) => (dispatch: Dispatch) => { + dispatch({ + type: 'GET_USERS_ERROR', + payload: error, + }); +} + +export const getUsersAsync = (filter = {}) => { + return (dispatch: ActionCreator) => { + dispatch(getUsers()); + + return (window as any).cvat.users.get(filter).then( + (users: any) => { + dispatch(getUsersSuccess(users, (filter as any).self)); + }, + (error: any) => { + dispatch(getUsersError(error)); + + throw error; + }, + ); + }; +} diff --git a/cvat-ui/src/components/app/app.scss b/cvat-ui/src/components/app/app.scss new file mode 100644 index 00000000..34b8ee09 --- /dev/null +++ b/cvat-ui/src/components/app/app.scss @@ -0,0 +1,3 @@ +.App { + +} diff --git a/cvat-ui/src/components/app/app.test.tsx b/cvat-ui/src/components/app/app.test.tsx new file mode 100644 index 00000000..21ab2b40 --- /dev/null +++ b/cvat-ui/src/components/app/app.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './app'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/app/app.tsx b/cvat-ui/src/components/app/app.tsx new file mode 100644 index 00000000..8df1edb6 --- /dev/null +++ b/cvat-ui/src/components/app/app.tsx @@ -0,0 +1,65 @@ +import React, { PureComponent } from 'react'; +import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; + +import { connect } from 'react-redux'; + +import HeaderLayout from '../header-layout/header-layout'; + +import TasksPage from '../tasks-page/tasks-page'; +import LoginPage from '../login-page/login-page'; +import RegisterPage from '../register-page/register-page'; +import PageNotFound from '../page-not-found/page-not-found'; + +import './app.scss'; + + +const ProtectedRoute = ({ component: Component, ...rest }: any) => { + return ( + { + return rest.isAuthenticated ? ( + <> + + + + ) : ( + + ); + } } + /> + ); +}; + +class App extends PureComponent { + componentDidMount() { + (window as any).cvat.config.backendAPI = process.env.REACT_APP_API_FULL_URL; + } + + render() { + return( + + + + + + + + + + ); + } +} + +const mapStateToProps = (state: any) => { + return state.authContext; +}; + +export default connect(mapStateToProps)(App); diff --git a/cvat-ui/src/components/header-layout/header-layout.scss b/cvat-ui/src/components/header-layout/header-layout.scss new file mode 100644 index 00000000..5476e4c8 --- /dev/null +++ b/cvat-ui/src/components/header-layout/header-layout.scss @@ -0,0 +1,66 @@ +.header-layout { + min-width: 1024px; + height: 100%; + padding: 0 16px; + line-height: initial; + background: #d8d8d8; + + &__logo { + display: flex; + align-items: center; + justify-content: center; + + img { + height: 18px; + } + } + + &__menu { + .ant-menu { + font-size: 16px; + color: black; + background-color: #d8d8d8; + line-height: 44px; + border-bottom: none; + + .ant-menu-item { + border-bottom: 3px solid transparent; + } + + .last-menu-item { + float: right; + margin-right: 28px; + } + + .ant-menu-item-selected, .ant-menu-item-active { + color: black !important; + border-bottom: 3px solid black !important; + background-color: #c3c3c3 !important; + } + + a, a:hover { + color: black; + } + } + } + + &__dropdown { + border-left: 1px solid #c3c3c3; + cursor: pointer; + + display: flex; + align-items: center; + font-size: 16px; + color: black; + + i:first-child { + margin-right: 12px; + font-size: 18px; + } + + i:last-child { + margin-left: auto; + font-size: 18px; + } + } +} diff --git a/cvat-ui/src/components/header-layout/header-layout.test.tsx b/cvat-ui/src/components/header-layout/header-layout.test.tsx new file mode 100644 index 00000000..6153a99a --- /dev/null +++ b/cvat-ui/src/components/header-layout/header-layout.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import HeaderLayout from './header-layout'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/header-layout/header-layout.tsx b/cvat-ui/src/components/header-layout/header-layout.tsx new file mode 100644 index 00000000..b3edefd1 --- /dev/null +++ b/cvat-ui/src/components/header-layout/header-layout.tsx @@ -0,0 +1,87 @@ +import React, { PureComponent } from 'react'; + +import { Link, withRouter } from 'react-router-dom'; + +import { connect } from 'react-redux'; + +import { logoutAsync } from '../../actions/auth.actions'; + +import { Layout, Row, Col, Menu, Dropdown, Icon } from 'antd'; +import { ClickParam } from 'antd/lib/menu'; + +import './header-layout.scss'; + + +const { Header } = Layout; + +class HeaderLayout extends PureComponent { + hostUrl: string | undefined; + + constructor(props: any) { + super(props); + + this.state = { + selectedMenuItem: null, + }; + + this.hostUrl = process.env.REACT_APP_API_HOST_URL; + } + + componentDidMount() { + this.setState({ selectedMenuItem: this.props.location.pathname.split('/')[1] }); + } + + render() { + const dropdownMenu = ( + + Logout + + ); + + return ( +
    + + + CVAT logo + + + + + Tasks + + + Models + + + Analitics + + + Help + + + + + + { this.props.currentUser ? { this.props.currentUser.username } : null } + + + + +
    + ); + } + + private selectMenuItem = (event: ClickParam) => { + this.setState({ selectedMenuItem: event.key }); + } + + private logout = () => { + this.props.dispatch(logoutAsync()); + } +} + +const mapStateToProps = (state: any) => { + return { ...state.authContext, ...state.users }; +}; + +export default withRouter(connect(mapStateToProps)(HeaderLayout) as any); diff --git a/cvat-ui/src/components/login-page/login-page.scss b/cvat-ui/src/components/login-page/login-page.scss new file mode 100644 index 00000000..18122d20 --- /dev/null +++ b/cvat-ui/src/components/login-page/login-page.scss @@ -0,0 +1,10 @@ +.login-form { + display: flex; + flex-direction: column; + justify-content: center; + height: 100vh; + + &__title { + + } +} diff --git a/cvat-ui/src/components/login-page/login-page.test.tsx b/cvat-ui/src/components/login-page/login-page.test.tsx new file mode 100644 index 00000000..6bec1fa7 --- /dev/null +++ b/cvat-ui/src/components/login-page/login-page.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import Login from './login-page'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx new file mode 100644 index 00000000..47153be2 --- /dev/null +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -0,0 +1,105 @@ +import React, { PureComponent } from 'react'; +import { Link } from 'react-router-dom'; + +import { connect } from 'react-redux'; +import { loginAsync, isAuthenticatedAsync } from '../../actions/auth.actions'; +import { getUsersAsync } from '../../actions/users.actions'; + +import { Button, Icon, Input, Form, Col, Row, Spin } from 'antd'; +import Title from 'antd/lib/typography/Title'; + +import './login-page.scss'; + + +class LoginForm extends PureComponent { + constructor(props: any) { + super(props); + + this.state = { loading: false }; + } + + componentDidMount() { + this.setState({ loading: true }); + + this.props.dispatch(isAuthenticatedAsync()).then( + (isAuthenticated: boolean) => { + this.setState({ loading: false }); + + if (this.props.isAuthenticated) { + this.props.dispatch(getUsersAsync({ self: true })); + this.props.history.replace(this.props.location.state ? this.props.location.state.from : '/tasks'); + } + } + ); + } + + render() { + const { getFieldDecorator } = this.props.form; + + return ( + + + +
    + Login + + + {getFieldDecorator('username', { + rules: [{ required: true, message: 'Please enter your username!' }], + })( + } + type="text" + name="username" + placeholder="Username" + />, + )} + + + + {getFieldDecorator('password', { + rules: [{ required: true, message: 'Please enter your password!' }], + })( + } + type="password" + name="password" + placeholder="Password" + />, + )} + + + + + + + Have not registered yet? Register here. +
    + +
    +
    + ); + } + + private onSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + this.props.form.validateFields((error: any, values: any) => { + if (!error) { + this.props.dispatch(loginAsync(values.username, values.password, this.props.history)).then( + (loggedIn: any) => { + this.props.dispatch(getUsersAsync({ self: true })); + }, + ); + } + }); + } +} + +const mapStateToProps = (state: any) => { + return state.authContext; +}; + +export default Form.create()(connect(mapStateToProps)(LoginForm)); diff --git a/cvat-ui/src/components/modals/task-create/task-create.scss b/cvat-ui/src/components/modals/task-create/task-create.scss new file mode 100644 index 00000000..5f68722a --- /dev/null +++ b/cvat-ui/src/components/modals/task-create/task-create.scss @@ -0,0 +1,8 @@ +.ant-badge { + width: 100%; +} + +.ant-tree.ant-tree-directory { + height: 108px; + overflow: auto; +} diff --git a/cvat-ui/src/components/modals/task-create/task-create.test.tsx b/cvat-ui/src/components/modals/task-create/task-create.test.tsx new file mode 100644 index 00000000..e69de29b diff --git a/cvat-ui/src/components/modals/task-create/task-create.tsx b/cvat-ui/src/components/modals/task-create/task-create.tsx new file mode 100644 index 00000000..34d8addf --- /dev/null +++ b/cvat-ui/src/components/modals/task-create/task-create.tsx @@ -0,0 +1,352 @@ +import React, { PureComponent } from 'react'; + +import { Form, Input, Icon, Checkbox, Radio, Upload, Badge, Tree, TreeSelect, InputNumber } from 'antd'; +import { UploadFile, UploadChangeParam } from 'antd/lib/upload/interface'; + +import configureStore from '../../../store'; +import { getShareFilesAsync } from '../../../actions/server.actions'; + +import { validateLabels, FileSource, fileModel } from '../../../utils/tasks-dto'; + +import './task-create.scss'; + + +const { TreeNode } = Tree; +const { SHOW_PARENT } = TreeSelect; +const { Dragger } = Upload; + +const formItemLayout = { + labelCol: { + xs: { span: 24 }, + sm: { span: 8 }, + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 }, + }, +}; + +const formItemTailLayout = { + labelCol: { + xs: { span: 24 }, + }, + wrapperCol: { + xs: { span: 24 }, + }, +}; + +class TaskCreateForm extends PureComponent { + store: any; + + constructor(props: any) { + super(props); + + this.store = configureStore(); + + this.state = { + confirmDirty: false, + selectedFileList: [], + filesCounter: 0, + treeData: [], + }; + } + + componentDidMount() { + this.getSharedFiles('').then( + (data: any) => { + this.setState({ treeData: fileModel('', this.store.getState().server.files) }); + }, + ); + } + + private renderTreeNodes = (data: any) => { + return data.map((item: any) => { + if (!item.isLeaf) { + return ( + + { item.children ? this.renderTreeNodes(item.children) : '' } + + ); + } + + return ; + }); + } + + private renderUploader = () => { + const { getFieldDecorator } = this.props.form; + + switch (this.props.form.getFieldValue('source')) { + case FileSource.Local: + return ( + + +
    + {getFieldDecorator('localUpload', { + rules: [{ required: true, message: 'Please, add some files!' }], + })( + +

    + +

    +

    Click or drag file to this area to upload

    +
    + )} +
    +
    +
    + ); + case FileSource.Remote: + return ( + + {getFieldDecorator('remoteURL', { + rules: [], + })( + + )} + + ); + case FileSource.Share: + return ( + + {getFieldDecorator('sharedFiles', { + rules: [{ required: true, message: 'Please, add some files!' }], + })( + + { this.renderTreeNodes(this.state.treeData) } + + )} + + ); + default: + break; + } + } + + render() { + const { getFieldDecorator } = this.props.form; + + return ( +
    + + {getFieldDecorator('name', { + rules: [ + { + required: true, + pattern: new RegExp('[a-zA-Z0-9_]+'), + message: 'Bad task name!', + }, + ], + })( + } + type="text" + name="name" + /> + )} + + + + {getFieldDecorator('labels', { + rules: [ + { required: true, message: 'Please add some labels!' }, + { validator: validateLabels, message: 'Bad labels format!' }, + ], + })( + } + type="text" + name="labels" + /> + )} + + + + {getFieldDecorator('bugTracker', { + rules: [{ type: 'url', message: 'Bad bug tracker link!' }], + })( + } + type="text" + name="bug-tracker" + /> + )} + + + + {getFieldDecorator('source', { + rules: [], + initialValue: 1, + })( + + Local + Remote + Share + + )} + + + + {getFieldDecorator('zOrder', { + rules: [], + initialValue: false, + })( + + )} + + + + {getFieldDecorator('segmentSize', { + rules: [], + })( + + )} + + + + {getFieldDecorator('overlapSize', { + rules: [], + initialValue: 0, + })( + + )} + + + + {getFieldDecorator('imageQuality', { + rules: [{ required: true }], + initialValue: 50, + })( + + )} + + + + {getFieldDecorator('startFrame', { + rules: [], + initialValue: 0, + })( + + )} + + + + {getFieldDecorator('stopFrame', { + rules: [], + })( + + )} + + + + {getFieldDecorator('frameFilter', { + rules: [], + })( + } + type="text" + name="frame-filter" + /> + )} + + + { this.renderUploader() } +
    + ); + } + + private onLoadData = (treeNode: any) => { + return new Promise(resolve => { + if (treeNode.props.children) { + resolve(); + + return; + } + + this.getSharedFiles(treeNode.props.dataRef.id).then( + (data: any) => { + treeNode.props.dataRef.children = fileModel(treeNode, this.store.getState().server.files); + + this.setState({ + treeData: [...this.state.treeData], + }); + + resolve(); + }, + ); + }); + } + + private getSharedFiles = (directory: string) => { + return this.store.dispatch(getShareFilesAsync(directory)); + } + + private onUploaderChange = (info: UploadChangeParam) => { + const nextState: { selectedFileList: UploadFile[], filesCounter: number } = { + selectedFileList: this.state.selectedFileList, + filesCounter: this.state.filesCounter, + }; + + switch (info.file.status) { + case 'uploading': + nextState.selectedFileList.push(info.file); + nextState.filesCounter += 1; + break; + case 'done': + break; + default: + // INFO: error or removed + nextState.selectedFileList = info.fileList; + } + + this.setState(() => nextState); + } + + private resetUploader = () => { + this.setState({ selectedFileList: [], filesCounter: 0 }); + } + + private simulateRequest = ({ file, onSuccess }: any) => { + setTimeout(() => { + onSuccess(file); + }, 0); + } +} + +export default Form.create()(TaskCreateForm); diff --git a/cvat-ui/src/components/modals/task-update/task-update.scss b/cvat-ui/src/components/modals/task-update/task-update.scss new file mode 100644 index 00000000..e69de29b diff --git a/cvat-ui/src/components/modals/task-update/task-update.test.tsx b/cvat-ui/src/components/modals/task-update/task-update.test.tsx new file mode 100644 index 00000000..e69de29b diff --git a/cvat-ui/src/components/modals/task-update/task-update.tsx b/cvat-ui/src/components/modals/task-update/task-update.tsx new file mode 100644 index 00000000..c3f4d2a2 --- /dev/null +++ b/cvat-ui/src/components/modals/task-update/task-update.tsx @@ -0,0 +1,51 @@ +import React, { PureComponent } from 'react'; + +import { Form, Input, Icon } from 'antd'; + +import { serializeLabels, validateLabels } from '../../../utils/tasks-dto' + +import './task-update.scss'; + + +class TaskUpdateForm extends PureComponent { + render() { + const { getFieldDecorator } = this.props.form; + + return ( +
    + + {getFieldDecorator('oldLabels', { + rules: [], + initialValue: serializeLabels(this.props.task), + })( + } + type="text" + name="oldLabels" + placeholder="Old labels" + />, + )} + + + + {getFieldDecorator('newLabels', { + rules: [ + { required: true, message: 'Please input new labels!' }, + { validator: validateLabels, message: 'Bad labels format!' }, + ], + })( + } + type="text" + name="new-labels" + placeholder="Expand the specification here" + />, + )} + +
    + ); + } +} + +export default Form.create()(TaskUpdateForm) as any; diff --git a/cvat-ui/src/components/page-not-found/page-not-found.scss b/cvat-ui/src/components/page-not-found/page-not-found.scss new file mode 100644 index 00000000..40c0b5d3 --- /dev/null +++ b/cvat-ui/src/components/page-not-found/page-not-found.scss @@ -0,0 +1,3 @@ +.empty.not-found { + height: 100vh; +} diff --git a/cvat-ui/src/components/page-not-found/page-not-found.test.tsx b/cvat-ui/src/components/page-not-found/page-not-found.test.tsx new file mode 100644 index 00000000..101c2cc0 --- /dev/null +++ b/cvat-ui/src/components/page-not-found/page-not-found.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import NotFound from './page-not-found'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/page-not-found/page-not-found.tsx b/cvat-ui/src/components/page-not-found/page-not-found.tsx new file mode 100644 index 00000000..04588969 --- /dev/null +++ b/cvat-ui/src/components/page-not-found/page-not-found.tsx @@ -0,0 +1,22 @@ +import React, { PureComponent } from 'react'; +import { Link } from 'react-router-dom'; + +import { Empty } from 'antd'; + +import './page-not-found.scss'; + + +class PageNotFound extends PureComponent { + render() { + return( + + Go back to tasks + + ); + } +} + +export default PageNotFound; diff --git a/cvat-ui/src/components/register-page/register-page.scss b/cvat-ui/src/components/register-page/register-page.scss new file mode 100644 index 00000000..0e46e1a8 --- /dev/null +++ b/cvat-ui/src/components/register-page/register-page.scss @@ -0,0 +1,10 @@ +.register-form { + display: flex; + flex-direction: column; + justify-content: center; + height: 100vh; + + &__title { + + } +} diff --git a/cvat-ui/src/components/register-page/register-page.test.tsx b/cvat-ui/src/components/register-page/register-page.test.tsx new file mode 100644 index 00000000..2b96c1a7 --- /dev/null +++ b/cvat-ui/src/components/register-page/register-page.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import RegisterPage from './register-page'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/register-page/register-page.tsx b/cvat-ui/src/components/register-page/register-page.tsx new file mode 100644 index 00000000..0f035893 --- /dev/null +++ b/cvat-ui/src/components/register-page/register-page.tsx @@ -0,0 +1,211 @@ +import React, { PureComponent } from 'react'; +import { Link } from 'react-router-dom'; + +import { connect } from 'react-redux'; +import { registerAsync, isAuthenticatedAsync } from '../../actions/auth.actions'; + +import { Button, Icon, Input, Form, Col, Row, Spin } from 'antd'; +import Title from 'antd/lib/typography/Title'; + +import './register-page.scss'; + + +class RegisterForm extends PureComponent { + constructor(props: any) { + super(props); + + this.state = { confirmDirty: false, loading: false }; + } + + componentDidMount() { + this.setState({ loading: true }); + + this.props.dispatch(isAuthenticatedAsync()).then( + (isAuthenticated: boolean) => { + this.setState({ loading: false }); + + if (this.props.isAuthenticated) { + this.props.history.replace('/tasks'); + } + } + ); + } + + render() { + const { getFieldDecorator } = this.props.form; + + return ( + + + +
    + Register + + + {getFieldDecorator('username', { + rules: [{ required: true, message: 'Please enter your username!' }], + })( + } + type="text" + name="username" + placeholder="Username" + />, + )} + + + + {getFieldDecorator('firstName', { + rules: [], + })( + } + type="text" + name="first-name" + placeholder="First name" + />, + )} + + + + {getFieldDecorator('lastName', { + rules: [], + })( + } + type="text" + name="last-name" + placeholder="Last name" + />, + )} + + + + {getFieldDecorator('email', { + rules: [ + { + type: 'email', + message: 'The input is not valid email!', + }, + { + required: true, + message: 'Please input your email!', + }, + ], + })( + } + type="text" + name="email" + placeholder="Email" + />, + )} + + + + {getFieldDecorator('password', { + rules: [ + { + required: true, + message: 'Please input your password!', + }, + { + validator: this.validateToNextPassword, + }, + ], + })( + } + name="password" + placeholder="Password" + />, + )} + + + + {getFieldDecorator('passwordConfirmation', { + rules: [ + { + required: true, + message: 'Please confirm your password!', + }, + { + validator: this.compareToFirstPassword, + }, + ], + })( + } + name="password-confirmation" + placeholder="Password confirmation" + />, + )} + + + + + + + Already have an account? Login here. +
    + +
    +
    + ); + } + + private handleConfirmBlur = (event: any) => { + const { value } = event.target; + + this.setState({ confirmDirty: this.state.confirmDirty || !!value }); + }; + + private compareToFirstPassword = (rule: any, value: string, callback: Function) => { + const { form } = this.props; + + if (value && value !== form.getFieldValue('password')) { + callback('Two passwords that you enter are inconsistent!'); + } else { + callback(); + } + }; + + private validateToNextPassword = (rule: any, value: string, callback: Function) => { + const { form } = this.props; + + if (value && this.state.confirmDirty) { + form.validateFields(['passwordConfirmation'], { force: true }); + } + + callback(); + }; + + private onSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + this.props.form.validateFields((error: any, values: any) => { + if (!error) { + this.props.dispatch( + registerAsync( + values.username, + values.firstName, + values.lastName, + values.email, + values.password, + values.passwordConfirmation, + this.props.history, + ), + ); + } + }); + } +} + +const mapStateToProps = (state: any) => { + return state.authContext; +}; + +export default Form.create()(connect(mapStateToProps)(RegisterForm)); diff --git a/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.scss b/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.scss new file mode 100644 index 00000000..4c0578ed --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.scss @@ -0,0 +1,45 @@ +.tasks-content { + width: 1024px; + min-height: calc(100vh - 90px - 64px - 46px); + margin: 0 auto; + + .tasks-content-сard { + margin-bottom: 20px; + padding: 20px; + border: 1px solid #001529; + border-radius: 3px; + background: white; + + &__header { + text-align: center; + + h2 { + margin-bottom: 20px; + } + } + + &__content { + + .card-cover { + + img { + max-width: 300px; + } + } + + .card-actions { + display: flex; + flex-flow: column wrap; + + button { + min-width: 200px; + margin-bottom: 1px; + } + } + + .card-jobs { + + } + } + } +} diff --git a/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.test.tsx b/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.test.tsx new file mode 100644 index 00000000..987e20eb --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import TasksContent from './tasks-content'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.tsx b/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.tsx new file mode 100644 index 00000000..d8895c1e --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-content/tasks-content.tsx @@ -0,0 +1,361 @@ +import React, { Component } from 'react'; + +import { withRouter } from 'react-router-dom'; + +import { connect } from 'react-redux'; +import { createTaskAsync, updateTaskAsync, deleteTaskAsync } from '../../../actions/tasks.actions'; +import { getAnnotationFormatsAsync } from '../../../actions/server.actions'; +import { dumpAnnotationAsync, uploadAnnotationAsync } from '../../../actions/annotations.actions'; + +import { Layout, Empty, Button, Modal, Col, Row, Menu, Dropdown, Icon, Upload } from 'antd'; +import Title from 'antd/lib/typography/Title'; + +import { ClickParam } from 'antd/lib/menu'; +import { UploadChangeParam } from 'antd/lib/upload'; + +import TaskUpdateForm from '../../modals/task-update/task-update'; +import TaskCreateForm from '../../modals/task-create/task-create'; + +import { deserializeLabels, taskDTO } from '../../../utils/tasks-dto'; + +import './tasks-content.scss'; + + +const { Content } = Layout; + +class TasksContent extends Component { + hostUrl: string | undefined; + apiUrl: string | undefined; + + createFormRef: any; + updateFormRef: any; + + constructor(props: any) { + super(props); + + this.state = { + dumpers: [], + loaders: [], + selectedLoader: null, + activeTaskId: null, + }; + + this.hostUrl = process.env.REACT_APP_API_HOST_URL; + this.apiUrl = process.env.REACT_APP_API_FULL_URL; + } + + componentDidMount() { + this.props.dispatch(getAnnotationFormatsAsync()).then( + (formats: any) => { + const dumpers = []; + const loaders = []; + + for (const format of this.props.annotationFormats) { + for (const dumper of format.dumpers) { + dumpers.push(dumper); + } + + for (const loader of format.loaders) { + loaders.push(loader); + } + } + + this.setState({ dumpers, loaders }); + } + ); + } + + render() { + return( + <> + { this.props.tasks.length ? this.renderTasks() : this.renderEmpty() } + + ); + } + + private renderEmpty() { + return ( + + To get started with your annotation project + + {/* // TODO: uncomment when modals -> pages */} + {/* create a new task */} + + ) + } + + private renderTasks() { + return ( + + { + this.props.tasks.map( + (task: any) => ( +
    + + + { `${task.name}: ${task.mode}` } + + + + + + Task cover + + + + + + + + + + + + + + + + + + + + + + + Jobs + { + task.jobs.map( + (job: any) => ( + + {`${this.hostUrl}?id=${job.id}`} + + ) + ) + } + + +
    + ) + ) + } +
    + ); + } + + private dumpAnnotationMenu = (task: any) => { + return ( + this.onDumpAnnotation(task, params, this) }> + { + this.state.dumpers.map( + (dumper: any) => ( + + { dumper.name } + + ) + ) + } + + ); + } + + private uploadAnnotationMenu = (task: any) => { + return ( + this.setState({ selectedLoader: params.key, loaderTask: task }) }> + { + this.state.loaders.map( + (loader: any) => ( + + + + + + ), + ) + } + + ); + } + + private setTaskCreateFormRef = (ref: any) => { + this.createFormRef = ref; + } + + private setTaskUpdateFormRef = (ref: any) => { + this.updateFormRef = ref; + } + + private onCreateTask = () => { + Modal.confirm({ + title: 'Create new task', + content: , + centered: true, + className: 'crud-modal', + okText: 'Create', + okType: 'primary', + onOk: () => { + return new Promise((resolve, reject) => { + this.createFormRef.validateFields((error: any, values: any) => { + if (!error) { + const newTask = taskDTO(values); + + this.props.dispatch(createTaskAsync(newTask)).then( + (data: any) => { + resolve(data); + }, + (error: any) => { + reject(error); + Modal.error({ title: error.message, centered: true, okType: 'danger' }); + }, + ); + } else { + reject(error); + } + }); + }); + }, + onCancel: () => { + return; + }, + }); + } + + private onUpdateTask = (task: any) => { + Modal.confirm({ + title: 'Update task', + content: , + centered: true, + className: 'crud-modal', + okText: 'Update', + okType: 'primary', + onOk: () => { + return new Promise((resolve, reject) => { + this.updateFormRef.validateFields((error: any, values: any) => { + if (!error) { + const deserializedLabels = deserializeLabels(values.newLabels); + const newLabels = deserializedLabels.map(label => new (window as any).cvat.classes.Label(label)); + task.labels = newLabels; + this.props.dispatch(updateTaskAsync(task)).then( + (data: any) => { + resolve(data); + }, + (error: any) => { + reject(error); + Modal.error({ title: error.message, centered: true, okType: 'danger' }); + }, + ); + } else { + reject(error); + } + }); + }); + }, + onCancel: () => { + return; + }, + }); + } + + private onDeleteTask = (task: any) => { + Modal.confirm({ + title: 'Do you want to delete this task?', + okText: 'Yes', + okType: 'danger', + centered: true, + autoFocusButton: 'cancel', + onOk: () => { + return new Promise((resolve, reject) => { + this.props.dispatch(deleteTaskAsync(task, this.props.history)).then( + (deleted: any) => { + resolve(deleted); + }, + (error: any) => { + reject(error); + }, + ); + }); + }, + cancelText: 'No', + onCancel: () => { + return; + }, + }); + } + + private onDumpAnnotation = (task: any, event: any, component: TasksContent) => { + const dumper = component.state.dumpers.find((dumper: any) => dumper.name === event.key); + + component.setState({ activeTaskId: task.id }); + this.props.dispatch(dumpAnnotationAsync(task, dumper)).then( + (data: any) => { + const a = document.createElement('a'); + a.href = component.props.downloadLink; + document.body.appendChild(a); + a.click(); + a.remove(); + }, + (error: any) => { + Modal.error({ title: error.message, centered: true, okType: 'danger' }); + }, + ); + } + + private onUploadAnnotation = (task: any, file: File) => { + const loader = this.state.loaders.find((loader: any) => loader.name === this.state.selectedLoader); + + this.setState({ activeTaskId: task.id }); + this.props.dispatch(uploadAnnotationAsync(task, file, loader)).then( + (data: any) => { + + }, + (error: any) => { + Modal.error({ title: error.message, centered: true, okType: 'danger' }); + }, + ); + + return true; + } + + private onUploaderChange = (info: UploadChangeParam) => { + if (info.file.status === 'uploading') { + this.onUploadAnnotation(this.state.loaderTask, (info.file.originFileObj as File)); + } + } + + private simulateRequest = ({ file, onSuccess }: any) => { + setTimeout(() => { + onSuccess(file); + }, 0); + } +} + +const mapStateToProps = (state: any) => { + return { ...state.tasks, ...state.server, ...state.annotations }; +}; + +export default withRouter(connect(mapStateToProps)(TasksContent) as any); diff --git a/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.scss b/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.scss new file mode 100644 index 00000000..ff33fe58 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.scss @@ -0,0 +1,9 @@ +.tasks-footer { + display: flex; + align-items: center; + justify-content: center; + min-width: 1024px; + padding: 0 50px; + height: 64px; + background: #001529; +} diff --git a/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.test.tsx b/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.test.tsx new file mode 100644 index 00000000..c62ff433 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import TasksFooter from './tasks-footer'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.tsx b/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.tsx new file mode 100644 index 00000000..464dcc3c --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-footer/tasks-footer.tsx @@ -0,0 +1,46 @@ +import React, { PureComponent } from 'react'; + +import * as queryString from 'query-string'; + +import { withRouter } from 'react-router-dom' + +import { connect } from 'react-redux'; + +import { Layout, Pagination, Row, Col } from 'antd'; + +import './tasks-footer.scss'; + + +const { Footer } = Layout; + +class TasksFooter extends PureComponent { + render() { + return( +
    + + + + + + +
    + ); + } + + private onPageChange = (page: number, pageSize?: number) => { + const params = { search: this.props.searchQuery, page } + + this.props.history.push({ search: queryString.stringify(params) }); + } +} + +const mapStateToProps = (state: any) => { + return { ...state.tasks, ...state.tasksFilter }; +}; + +export default withRouter(connect(mapStateToProps)(TasksFooter) as any); diff --git a/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.scss b/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.scss new file mode 100644 index 00000000..b09ba38e --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.scss @@ -0,0 +1,41 @@ +.tasks-header { + display: flex; + align-items: center; + width: 1024px; + height: 90px; + line-height: initial; + margin: 0 auto; + padding: 0; + background-color: #f0f2f5; + + &__logo { + font-size: 20px; + + .logo { + margin: 0; + color: black; + } + } + + &__search { + text-align: center; + + .search { + max-width: 300px; + } + } + + &__actions { + text-align: right; + + .action:not(:nth-child(1)) { + margin-left: 8px; + } + + .action { + width: 180px; + font-size: 16px; + line-height: 19px; + } + } +} diff --git a/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.test.tsx b/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.test.tsx new file mode 100644 index 00000000..eb1cfc73 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import TasksHeader from './tasks-header'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.tsx b/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.tsx new file mode 100644 index 00000000..2ac44da8 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-header/tasks-header.tsx @@ -0,0 +1,120 @@ +import React, { Component } from 'react'; + +import { withRouter } from 'react-router-dom'; + +import { connect } from 'react-redux'; +import { createTaskAsync } from '../../../actions/tasks.actions'; + +import { Modal, Layout, Col, Button, Input } from 'antd'; +import Title from 'antd/lib/typography/Title'; + +import TaskCreateForm from '../../modals/task-create/task-create'; + +import { taskDTO } from '../../../utils/tasks-dto'; + +import './tasks-header.scss'; + + +const { Header } = Layout; +const { Search } = Input; + +class TasksHeader extends Component { + createFormRef: any; + + constructor(props: any) { + super(props); + + this.state = { searchQuery: this.props.searchQuery }; + } + + componentDidUpdate(prevProps: any) { + if (this.props.searchQuery !== prevProps.searchQuery) { + this.setState({ searchQuery: this.props.searchQuery }); + } + } + + render() { + return( +
    + + Tasks + + + this.onSearch(query) }> + + + + + +
    + ); + } + + private setTaskCreateFormRef = (ref: any) => { + this.createFormRef = ref; + } + + private onCreateTask = () => { + Modal.confirm({ + title: 'Create new task', + content: , + centered: true, + className: 'crud-modal', + okText: 'Create', + okType: 'primary', + onOk: (closeFunction: Function) => { + return new Promise((resolve, reject) => { + this.createFormRef.validateFields((error: any, values: any) => { + if (!error) { + const newTask = taskDTO(values); + + this.props.dispatch(createTaskAsync(newTask)).then( + (data: any) => { + resolve(data); + closeFunction(); + }, + (error: any) => { + reject(error); + Modal.error({ title: error.message, centered: true, okType: 'danger' }) + } + ); + } else { + reject(error); + } + }); + }); + }, + onCancel: () => { + return; + }, + }); + } + + private onValueChange = (event: any) => { + this.setState({ searchQuery: event.target.value }); + } + + private onSearch = (query: string) => { + if (query !== this.props.searchQuery) { + query ? this.props.history.push(`?search=${query}`) : this.props.history.push(this.props.location.pathname); + } + } +} + +const mapStateToProps = (state: any) => { + return { ...state.tasks, ...state.tasksFilter }; +}; + +export default withRouter(connect(mapStateToProps)(TasksHeader) as any); diff --git a/cvat-ui/src/components/tasks-page/tasks-page.scss b/cvat-ui/src/components/tasks-page/tasks-page.scss new file mode 100644 index 00000000..07b1cd83 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-page.scss @@ -0,0 +1,3 @@ +.layout { + +} diff --git a/cvat-ui/src/components/tasks-page/tasks-page.test.tsx b/cvat-ui/src/components/tasks-page/tasks-page.test.tsx new file mode 100644 index 00000000..bd48512f --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-page.test.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import TasksPage from './tasks-page'; + + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/cvat-ui/src/components/tasks-page/tasks-page.tsx b/cvat-ui/src/components/tasks-page/tasks-page.tsx new file mode 100644 index 00000000..7bf31885 --- /dev/null +++ b/cvat-ui/src/components/tasks-page/tasks-page.tsx @@ -0,0 +1,59 @@ +import React, { PureComponent } from 'react'; +import { Location, Action } from 'history'; + +import * as queryString from 'query-string'; + +import setQueryObject from '../../utils/tasks-filter' + +import { connect } from 'react-redux'; +import { getTasksAsync } from '../../actions/tasks.actions'; +import { filterTasks } from '../../actions/tasks-filter.actions'; + +import { Layout, Spin } from 'antd'; + +import TasksHeader from './tasks-header/tasks-header'; +import TasksContent from './tasks-content/tasks-content'; +import TasksFooter from './tasks-footer/tasks-footer'; + +import './tasks-page.scss'; + + +class TasksPage extends PureComponent { + componentDidMount() { + this.loadTasks(this.props.location.search); + + this.props.history.listen( + (location: Location, action: Action) => { + if (location.pathname.includes('tasks')) { + this.loadTasks(location.search); + } + } + ); + } + + render() { + return ( + + + + + + + + ); + } + + private loadTasks = (params: any) => { + const query = queryString.parse(params); + const queryObject = setQueryObject(query); + + this.props.dispatch(filterTasks(queryObject)); + this.props.dispatch(getTasksAsync(queryObject)); + } +} + +const mapStateToProps = (state: any) => { + return { ...state.authContext, ...state.tasks, ...state.tasksFilter }; +}; + +export default connect(mapStateToProps)(TasksPage); diff --git a/cvat-ui/src/index.scss b/cvat-ui/src/index.scss new file mode 100644 index 00000000..234c7a81 --- /dev/null +++ b/cvat-ui/src/index.scss @@ -0,0 +1,57 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; +} + +// TODO: remove when modals -> pages +.crud-modal { + width: 90% !important; +} + +.spinner { + .ant-spin-spinning { + max-height: 100% !important; + background: white !important; + } +} + +.empty { + display: flex; + flex-direction: column; + justify-content: center; + height: calc(100vh - 90px - 64px - 46px); + + &.ant-empty { + font-size: 16px; + line-height: 19px; + + .ant-empty-image { + margin-bottom: 30px; + } + + .ant-empty-description { + font-size: 20px; + font-weight: bold; + } + + .ant-empty-footer { + margin-top: 10px; + + .ant-btn { + display: block; + margin: auto; + height: auto; + font-size: 16px; + line-height: 19px; + } + } + } +} diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx new file mode 100644 index 00000000..d6d9cc2f --- /dev/null +++ b/cvat-ui/src/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Provider } from 'react-redux' +import configureStore from './store'; + +import App from './components/app/app'; + +import * as serviceWorker from './serviceWorker'; + +import './index.scss'; + + +ReactDOM.render( + + + , + document.getElementById('root') +); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://bit.ly/CRA-PWA +serviceWorker.unregister(); diff --git a/cvat-ui/src/react-app-env.d.ts b/cvat-ui/src/react-app-env.d.ts new file mode 100644 index 00000000..6431bc5f --- /dev/null +++ b/cvat-ui/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/cvat-ui/src/reducers/annotations.reducer.ts b/cvat-ui/src/reducers/annotations.reducer.ts new file mode 100644 index 00000000..1fcf7d2b --- /dev/null +++ b/cvat-ui/src/reducers/annotations.reducer.ts @@ -0,0 +1,29 @@ +import { AnyAction } from 'redux'; + + +export default ( + state = { + downloadLink: null, + isFetching: false, + error: null, + }, + action: AnyAction, +) => { + switch (action.type) { + case 'DUMP_ANNOTATION': + return { ...state, isFetching: true }; + case 'DUMP_ANNOTATION_SUCCESS': + return { ...state, isFetching: false, downloadLink: action.payload }; + case 'DUMP_ANNOTATION_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'UPLOAD_ANNOTATION': + return { ...state, isFetching: true }; + case 'UPLOAD_ANNOTATION_SUCCESS': + return { ...state, isFetching: false }; + case 'UPLOAD_ANNOTATION_ERROR': + return { ...state, isFetching: false, error: action.payload }; + default: + return state; + } +} diff --git a/cvat-ui/src/reducers/auth.reducer.ts b/cvat-ui/src/reducers/auth.reducer.ts new file mode 100644 index 00000000..ed44a45e --- /dev/null +++ b/cvat-ui/src/reducers/auth.reducer.ts @@ -0,0 +1,45 @@ +import { AnyAction } from 'redux'; + + +export default ( + state = { + isAuthenticated: false, + isFetching: false, + error: null, + }, + action: AnyAction, +) => { + switch (action.type) { + case 'LOGIN': + return { ...state, isFetching: true }; + case 'LOGIN_SUCCESS': + return { ...state, isFetching: false, isAuthenticated: true }; + case 'LOGIN_ERROR': + return { ...state, isFetching: false, isAuthenticated: false, error: action.payload }; + + case 'LOGOUT': + return { ...state, isFetching: true }; + case 'LOGOUT_SUCCESS': + return { ...state, isFetching: false, isAuthenticated: false }; + case 'LOGOUT_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'IS_AUTHENTICATED': + return { ...state, isFetching: true }; + case 'IS_AUTHENTICATED_SUCCESS': + return { ...state, isFetching: false, isAuthenticated: true }; + case 'IS_AUTHENTICATED_FAIL': + return { ...state, isFetching: false, isAuthenticated: false }; + + case 'IS_AUTHENTICATED_ERROR': + return { ...state, isFetching: false, error: action.payload }; + case 'REGISTER': + return { ...state, isFetching: true }; + case 'REGISTER_SUCCESS': + return { ...state, isFetching: false }; + case 'REGISTER_ERROR': + return { ...state, isFetching: false, error: action.payload }; + default: + return state; + } +} diff --git a/cvat-ui/src/reducers/root.reducer.ts b/cvat-ui/src/reducers/root.reducer.ts new file mode 100644 index 00000000..46d0ccf1 --- /dev/null +++ b/cvat-ui/src/reducers/root.reducer.ts @@ -0,0 +1,33 @@ +import { combineReducers, AnyAction } from 'redux'; + +import authContext from './auth.reducer'; +import tasks from './tasks.reducer'; +import users from './users.reducer'; +import tasksFilter from './tasks-filter.reducer'; +import server from './server.reducer'; +import annotations from './annotations.reducer'; + + +// TODO: investigate a better way to handle +// INFO: global errors handler reducer +const errorMessage = (state = null, action: AnyAction) => { + const { type, payload } = action; + + if (type === 'RESET_ERROR_MESSAGE') { + return null; + } else if (type.endsWith('ERROR')) { + return payload; + } + + return state; +} + +export default combineReducers({ + authContext, + tasks, + users, + tasksFilter, + server, + annotations, + errorMessage, +}); diff --git a/cvat-ui/src/reducers/server.reducer.ts b/cvat-ui/src/reducers/server.reducer.ts new file mode 100644 index 00000000..ad241596 --- /dev/null +++ b/cvat-ui/src/reducers/server.reducer.ts @@ -0,0 +1,38 @@ +import { AnyAction } from 'redux'; + + +export default ( + state = { + info: null, + files: [], + annotationFormats: [], + isFetching: false, + error: null, + }, + action: AnyAction, +) => { + switch (action.type) { + case 'GET_SERVER_INFO': + return { ...state, isFetching: true }; + case 'GET_SERVER_INFO_SUCCESS': + return { ...state, isFetching: false, info: action.payload }; + case 'GET_SERVER_INFO_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'GET_SHARE_FILES': + return { ...state, isFetching: true }; + case 'GET_SHARE_FILES_SUCCESS': + return { ...state, isFetching: false, files: action.payload }; + case 'GET_SHARE_FILES_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'GET_ANNOTATION_FORMATS': + return { ...state, isFetching: true }; + case 'GET_ANNOTATION_FORMATS_SUCCESS': + return { ...state, isFetching: false, annotationFormats: action.payload }; + case 'GET_ANNOTATION_FORMATS_ERROR': + return { ...state, isFetching: false, error: action.payload }; + default: + return state; + } +} diff --git a/cvat-ui/src/reducers/tasks-filter.reducer.ts b/cvat-ui/src/reducers/tasks-filter.reducer.ts new file mode 100644 index 00000000..a5b2f52f --- /dev/null +++ b/cvat-ui/src/reducers/tasks-filter.reducer.ts @@ -0,0 +1,17 @@ +import { AnyAction } from 'redux'; + + +export default ( + state = { + searchQuery: '', + currentPage: 1 + }, + action: AnyAction, +) => { + switch (action.type) { + case 'FILTER_TASKS': + return { ...state, searchQuery: action.payload.search, currentPage: action.payload.page }; + default: + return state; + } +} diff --git a/cvat-ui/src/reducers/tasks.reducer.ts b/cvat-ui/src/reducers/tasks.reducer.ts new file mode 100644 index 00000000..e0d411c4 --- /dev/null +++ b/cvat-ui/src/reducers/tasks.reducer.ts @@ -0,0 +1,44 @@ +import { AnyAction } from 'redux'; + + +export default ( + state: any = { + tasks: [], + tasksCount: 0, + isFetching: false, + error: null, + }, + action: AnyAction, +) => { + switch (action.type) { + case 'GET_TASKS': + return { ...state, isFetching: true }; + case 'GET_TASKS_SUCCESS': + return { ...state, isFetching: false, tasks: Array.from(action.payload.values()), tasksCount: action.payload.count }; + case 'GET_TASKS_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'CREATE_TASK': + return { ...state, isFetching: true }; + case 'CREATE_TASK_SUCCESS': + return { ...state, isFetching: false }; + case 'CREATE_TASK_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'UPDATE_TASK': + return { ...state, isFetching: true }; + case 'UPDATE_TASK_SUCCESS': + return { ...state, isFetching: false }; + case 'UPDATE_TASK_ERROR': + return { ...state, isFetching: false, error: action.payload }; + + case 'DELETE_TASK': + return { ...state, isFetching: true }; + case 'DELETE_TASK_SUCCESS': + return { ...state, isFetching: false }; + case 'DELETE_TASK_ERROR': + return { ...state, isFetching: false, error: action.payload }; + default: + return state; + } +} diff --git a/cvat-ui/src/reducers/users.reducer.ts b/cvat-ui/src/reducers/users.reducer.ts new file mode 100644 index 00000000..f7a6a18c --- /dev/null +++ b/cvat-ui/src/reducers/users.reducer.ts @@ -0,0 +1,23 @@ +import { AnyAction } from 'redux'; + + +export default ( + state: any = { + users: [], + currentUser: null, + isFetching: false, + error: null, + }, + action: AnyAction, +) => { + switch (action.type) { + case 'GET_USERS': + return { ...state, isFetching: true }; + case 'GET_USERS_SUCCESS': + return { ...state, isFetching: false, users: Array.from(action.payload.values()), currentUser: action.currentUser }; + case 'GET_USERS_ERROR': + return { ...state, isFetching: false, error: action.payload }; + default: + return state; + } +} diff --git a/cvat-ui/src/serviceWorker.ts b/cvat-ui/src/serviceWorker.ts new file mode 100644 index 00000000..15d90cb8 --- /dev/null +++ b/cvat-ui/src/serviceWorker.ts @@ -0,0 +1,143 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +type Config = { + onSuccess?: (registration: ServiceWorkerRegistration) => void; + onUpdate?: (registration: ServiceWorkerRegistration) => void; +}; + +export function register(config?: Config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL( + (process as { env: { [key: string]: string } }).env.PUBLIC_URL, + window.location.href + ); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl: string, config?: Config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl: string, config?: Config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/cvat-ui/src/store.ts b/cvat-ui/src/store.ts new file mode 100644 index 00000000..1a183b73 --- /dev/null +++ b/cvat-ui/src/store.ts @@ -0,0 +1,32 @@ +import { createStore, applyMiddleware, compose } from 'redux'; +import thunk from 'redux-thunk'; +import { createLogger } from 'redux-logger' + +import rootReducer from './reducers/root.reducer'; + +export default function configureStore(initialState = {}) { + const logger = createLogger({ + collapsed: true, + }); + + const middlewares = []; + + middlewares.push(thunk); + + if (process.env.NODE_ENV === `development`) { + middlewares.push(logger); + } + + return createStore( + rootReducer, + initialState, + compose( + applyMiddleware(...middlewares), + (window as any).__REDUX_DEVTOOLS_EXTENSION__ + ? + (window as any).__REDUX_DEVTOOLS_EXTENSION__({ trace: true }) + : + (f: any) => f + ) + ); +} diff --git a/cvat-ui/src/utils/tasks-dto.ts b/cvat-ui/src/utils/tasks-dto.ts new file mode 100644 index 00000000..04280e43 --- /dev/null +++ b/cvat-ui/src/utils/tasks-dto.ts @@ -0,0 +1,152 @@ +export enum FileSource { + Local = 1, + Remote = 2, + Share = 3, +} + +export function fileModel(parentNode: any, files: any) { + return files.map( + (file: any) => { + return { + id: parentNode.props ? `${parentNode.props.value}/${file.name}` : file.name, + isLeaf: file.type !== 'DIR', + name: file.name, + }; + } + ); +} + +export function taskDTO(values: any) { + const newTaskDTO = { + name: values.name, + labels: deserializeLabels(values.labels), + image_quality: values.imageQuality, + z_order: values.zOrder, + bug_tracker: values.bugTracker, + segment_size: values.segmentSize, + overlap: values.overlapSize, + frame_filter: values.frameFilter, + start_frame: values.startFrame, + stop_frame: values.stopFrame, + }; + + const newTask = new (window as any).cvat.classes.Task(newTaskDTO); + + if (values.source === FileSource.Local) { + newTask.clientFiles = values.localUpload.fileList.map((file: any) => file.response); + } else if (values.source === FileSource.Remote) { + newTask.remoteFiles = values.remoteURL + .split(/\r?\n/) + .map((url: string) => url.trim()) + .filter((url: string) => url.length > 0); + } else if (values.source === FileSource.Share) { + newTask.serverFiles = values.sharedFiles; + } + + return newTask; +} + +export function validateLabels(rule: any, value: string, callback: Function) { + if (value) { + try { + deserializeLabels(value); + } catch (error) { + callback(error.message); + } + } + + callback(); +} + +export function serializeLabels(task: any) { + const labels = task.labels.map((label: any) => label.toJSON()); + + let serialized = ''; + + for (const label of labels) { + serialized += ` ${label.name}`; + + for (const attr of label.attributes) { + serialized += ` ${attr.mutable ? '~' : '@'}`; + serialized += `${attr.input_type}=${attr.name}:`; + serialized += attr.values.join(','); + } + } + + return serialized.trim(); +} + +export function deserializeLabels(serialized: string) { + const normalized = serialized.replace(/'+/g, '\'').replace(/\s+/g, ' ').trim(); + const fragments = customSplit(normalized, ' '); + const deserialized = []; + + let latest: any = null; + + for (let fragment of fragments) { + fragment = fragment.trim(); + + if ((fragment.startsWith('~')) || (fragment.startsWith('@'))) { + const regex = /(@|~)(checkbox|select|number|text|radio)=([-,?!_0-9a-zA-Z()\s"]+):([-,?!_0-9a-zA-Z()"\s]+)/g; + const result = regex.exec(fragment); + + if (result === null || latest === null) { + throw Error('Bad labels format'); + } + + const values = customSplit(result[4], ','); + + latest.attributes.push({ + name: result[3].replace(/^"/, '').replace(/"$/, ''), + mutable: result[1] === '~', + input_type: result[2], + default_value: values[0].replace(/^"/, '').replace(/"$/, ''), + values: values.map(val => val.replace(/^"/, '').replace(/"$/, '')), + }); + } else { + latest = { + name: fragment.replace(/^"/, '').replace(/"$/, ''), + attributes: [], + }; + + deserialized.push(latest); + } + } + + return deserialized; +} + +export function customSplit(string: any, separator: any) { + const regex = /"/gi; + const occurences = []; + + let occurence = regex.exec(string); + + while (occurence) { + occurences.push(occurence.index); + occurence = regex.exec(string); + } + + if (occurences.length % 2) { + occurences.pop(); + } + + let copy = ''; + + if (occurences.length) { + let start = 0; + + for (let idx = 0; idx < occurences.length; idx += 2) { + copy += string.substr(start, occurences[idx] - start); + copy += string.substr(occurences[idx], occurences[idx + 1] - occurences[idx] + 1) + .replace(new RegExp(separator, 'g'), '\0'); + start = occurences[idx + 1] + 1; + } + + copy += string.substr(occurences[occurences.length - 1] + 1); + } else { + copy = string; + } + + return copy.split(new RegExp(separator, 'g')).map(x => x.replace(/\0/g, separator)); +} diff --git a/cvat-ui/src/utils/tasks-filter.ts b/cvat-ui/src/utils/tasks-filter.ts new file mode 100644 index 00000000..c57045dc --- /dev/null +++ b/cvat-ui/src/utils/tasks-filter.ts @@ -0,0 +1,13 @@ +export default (params: { search?: string, page?: string }): { search?: string, page?: number } => { + const queryObject: { search?: string, page?: number } = {}; + + if (params['search']) { + queryObject.search = params.search.toString(); + } + + if (params['page']) { + queryObject.page = parseInt(params.page); + } + + return queryObject; +} diff --git a/cvat-ui/tsconfig.json b/cvat-ui/tsconfig.json new file mode 100644 index 00000000..0980b23f --- /dev/null +++ b/cvat-ui/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve" + }, + "include": [ + "src" + ] +} diff --git a/cvat/__init__.py b/cvat/__init__.py index 94a6cc8c..b962ba74 100644 --- a/cvat/__init__.py +++ b/cvat/__init__.py @@ -5,6 +5,6 @@ from cvat.utils.version import get_version -VERSION = (0, 4, 0, 'final') +VERSION = (0, 5, 0, 'final', 0) __version__ = get_version(VERSION) diff --git a/cvat/apps/annotation/README.md b/cvat/apps/annotation/README.md new file mode 100644 index 00000000..c1a317b4 --- /dev/null +++ b/cvat/apps/annotation/README.md @@ -0,0 +1,152 @@ +## Description + +The purpose of this application is to add support for multiple annotation formats for CVAT. +It allows to download and upload annotations in different formats and easily add support for new. + +## How to add a new annotation format support + +1. Write a python script that will be executed via exec() function. Following items must be defined inside at code: + - **format_spec** - a dictionary with the following structure: + ```python + format_spec = { + "name": "CVAT", + "dumpers": [ + { + "display_name": "{name} {format} {version} for videos", + "format": "XML", + "version": "1.1", + "handler": "dump_as_cvat_interpolation" + }, + { + "display_name": "{name} {format} {version} for images", + "format": "XML", + "version": "1.1", + "handler": "dump_as_cvat_annotation" + } + ], + "loaders": [ + { + "display_name": "{name} {format} {version}", + "format": "XML", + "version": "1.1", + "handler": "load", + } + ], + } + ``` + - **name** - unique name for each format + - **dumpers and loaders** - lists of objects that describes exposed dumpers and loaders and must + have following keys: + 1. display_name - **unique** string used as ID for dumpers and loaders. + Also this string is displayed in CVAT UI. + Possible to use a named placeholders like the python format function + (supports only name, format and version variables). + 1. format - a string, used as extension for a dumped annotation. + 1. version - just string with version. + 1. handler - function that will be called and should be defined at top scope. + - dumper/loader handler functions. Each function should have the following signature: + ```python + def dump_handler(file_object, annotations): + ``` + + Inside of the script environment 2 variables are available: + - **file_object** - python's standard file object returned by open() function and exposing a file-oriented API + (with methods such as read() or write()) to an underlying resource. + - **annotations** - instance of [Annotation](annotation.py#L106) class. + + Annotation class expose API and some additional pre-defined types that allow to get/add shapes inside + a loader/dumper code. + + Short description of the public methods: + - **Annotation.shapes** - property, returns a generator of Annotation.LabeledShape objects + - **Annotation.tracks** - property, returns a generator of Annotation.Track objects + - **Annotation.tags** - property, returns a generator of Annotation.Tag objects + - **Annotation.group_by_frame()** - method, returns an iterator on Annotation.Frame object, + which groups annotation objects by frame. Note that TrackedShapes will be represented as Annotation.LabeledShape. + - **Annotation.meta** - property, returns dictionary which represent a task meta information, + for example - video source name, number of frames, number of jobs, etc + - **Annotation.add_tag(tag)** - tag should be a instance of the Annotation.Tag class + - **Annotation.add_shape(shape)** - shape should be a instance of the Annotation.Shape class + - **Annotation.add_track(track)** - track should be a instance of the Annotation.Track class + - **Annotation.Attribute** = namedtuple('Attribute', 'name, value') + - name - String, name of the attribute + - value - String, value of the attribute + - **Annotation.LabeledShape** = namedtuple('LabeledShape', 'type, frame, label, points, occluded, attributes, + group, z_order') + LabeledShape.\__new\__.\__defaults\__ = (0, None) + - **TrackedShape** = namedtuple('TrackedShape', 'type, points, occluded, frame, attributes, outside, + keyframe, z_order') + TrackedShape.\__new\__.\__defaults\__ = (None, ) + - **Track** = namedtuple('Track', 'label, group, shapes') + - **Tag** = namedtuple('Tag', 'frame, label, attributes, group') + Tag.\__new\__.\__defaults\__ = (0, ) + - **Frame** = namedtuple('Frame', 'frame, name, width, height, labeled_shapes, tags') + + Pseudocode for a dumper script + ```python + ... + # dump meta info if necessary + ... + + # iterate over all frames + for frame_annotation in annotations.group_by_frame(): + # get frame info + image_name = frame_annotation.name + image_width = frame_annotation.width + image_height = frame_annotation.height + + # iterate over all shapes on the frame + for shape in frame_annotation.labeled_shapes: + label = shape.label + xtl = shape.points[0] + ytl = shape.points[1] + xbr = shape.points[2] + ybr = shape.points[3] + + # iterate over shape attributes + for attr in shape.attributes: + attr_name = attr.name + attr_value = attr.value + ... + # dump annotation code + file_object.write(...) + ... + ``` + Pseudocode for a loader code + ```python + ... + #read file_object + ... + + for parsed_shape in parsed_shapes: + shape = annotations.LabeledShape( + type="rectangle", + points=[0, 0, 100, 100], + occluded=False, + attributes=[], + label="car", + outside=False, + frame=99, + ) + + annotations.add_shape(shape) + ``` + Full examples can be found in [builtin](builtin) folder. +1. Add path to a new python script to the annotation app settings: + + ```python + BUILTIN_FORMATS = ( + os.path.join(path_prefix, 'cvat.py'), + os.path.join(path_prefix,'pascal_voc.py'), + ) + ``` + +## Ideas for improvements + +- Annotation format manager like DL Model manager with which the user can add custom format support by + writing dumper/loader scripts. +- Often a custom loader/dumper requires additional python packages and it would be useful if CVAT provided some API + that allows the user to install a python dependencies from their own code without changing the source code. + Possible solutions: install additional modules via pip call to a separate directory for each Annotation Format + to reduce version conflicts, etc. Thus, custom code can be run in an extended environment, and core CVAT modules + should not be affected. As well, this functionality can be useful for Auto Annotation module. diff --git a/cvat/apps/annotation/__init__.py b/cvat/apps/annotation/__init__.py new file mode 100644 index 00000000..a6b8e925 --- /dev/null +++ b/cvat/apps/annotation/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT +default_app_config = 'cvat.apps.annotation.apps.AnnotationConfig' diff --git a/cvat/apps/annotation/admin.py b/cvat/apps/annotation/admin.py new file mode 100644 index 00000000..b66dde17 --- /dev/null +++ b/cvat/apps/annotation/admin.py @@ -0,0 +1,3 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT diff --git a/cvat/apps/annotation/annotation.py b/cvat/apps/annotation/annotation.py new file mode 100644 index 00000000..c6a39513 --- /dev/null +++ b/cvat/apps/annotation/annotation.py @@ -0,0 +1,426 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +import os +import copy +from collections import OrderedDict, namedtuple + +from django.utils import timezone + +from cvat.apps.engine.data_manager import DataManager, TrackManager +from cvat.apps.engine.serializers import LabeledDataSerializer + +class AnnotationIR: + def __init__(self, data=None): + self.reset() + if data: + self._tags = getattr(data, 'tags', []) or data['tags'] + self._shapes = getattr(data, 'shapes', []) or data['shapes'] + self._tracks = getattr(data, 'tracks', []) or data['tracks'] + + def add_tag(self, tag): + self._tags.append(tag) + + def add_shape(self, shape): + self._shapes.append(shape) + + def add_track(self, track): + self._tracks.append(track) + + @property + def tags(self): + return self._tags + + @property + def shapes(self): + return self._shapes + + @property + def tracks(self): + return self._tracks + + @property + def version(self): + return self._version + + @tags.setter + def tags(self, tags): + self._tags = tags + + @shapes.setter + def shapes(self, shapes): + self._shapes = shapes + + @tracks.setter + def tracks(self, tracks): + self._tracks = tracks + + @version.setter + def version(self, version): + self._version = version + + def __getitem__(self, key): + return getattr(self, key) + + @property + def data(self): + return { + 'version': self.version, + 'tags': self.tags, + 'shapes': self.shapes, + 'tracks': self.tracks, + } + + def serialize(self): + serializer = LabeledDataSerializer(data=self.data) + if serializer.is_valid(raise_exception=True): + return serializer.data + + #makes a data copy from specified frame interval + def slice(self, start, stop): + is_frame_inside = lambda x: (start <= int(x['frame']) <= stop) + splitted_data = AnnotationIR() + splitted_data.tags = copy.deepcopy(list(filter(is_frame_inside, self.tags))) + splitted_data.shapes = copy.deepcopy(list(filter(is_frame_inside, self.shapes))) + splitted_data.tracks = copy.deepcopy(list(filter(lambda y: len(list(filter(is_frame_inside, y['shapes']))), self.tracks))) + + return splitted_data + + @data.setter + def data(self, data): + self.version = data['version'] + self.tags = data['tags'] + self.shapes = data['shapes'] + self.tracks = data['tracks'] + + def reset(self): + self._version = 0 + self._tags = [] + self._shapes = [] + self._tracks = [] + +class Annotation: + Attribute = namedtuple('Attribute', 'name, value') + LabeledShape = namedtuple('LabeledShape', 'type, frame, label, points, occluded, attributes, group, z_order') + LabeledShape.__new__.__defaults__ = (0, 0) + TrackedShape = namedtuple('TrackedShape', 'type, points, occluded, frame, attributes, outside, keyframe, z_order') + TrackedShape.__new__.__defaults__ = (0, ) + Track = namedtuple('Track', 'label, group, shapes') + Tag = namedtuple('Tag', 'frame, label, attributes, group') + Tag.__new__.__defaults__ = (0, ) + Frame = namedtuple('Frame', 'frame, name, width, height, labeled_shapes, tags') + + def __init__(self, annotation_ir, db_task, scheme='', host='', create_callback=None): + self._annotation_ir = annotation_ir + self._db_task = db_task + self._scheme = scheme + self._host = host + self._create_callback=create_callback + self._MAX_ANNO_SIZE=30000 + + db_labels = self._db_task.label_set.all().prefetch_related('attributespec_set').order_by('pk') + + self._label_mapping = OrderedDict((db_label.id, db_label) for db_label in db_labels) + + self._attribute_mapping = {db_label.id: {'mutable': {}, 'immutable': {}} for db_label in db_labels} + + for db_label in db_labels: + for db_attribute in db_label.attributespec_set.all(): + if db_attribute.mutable: + self._attribute_mapping[db_label.id]['mutable'][db_attribute.id] = db_attribute.name + else: + self._attribute_mapping[db_label.id]['immutable'][db_attribute.id] = db_attribute.name + + self._attribute_mapping_merged = {} + for label_id, attr_mapping in self._attribute_mapping.items(): + self._attribute_mapping_merged[label_id] = { + **attr_mapping['mutable'], + **attr_mapping['immutable'], + } + + self._init_frame_info() + self._init_meta() + + def _get_label_id(self, label_name): + for db_label in self._label_mapping.values(): + if label_name == db_label.name: + return db_label.id + return None + + def _get_label_name(self, label_id): + return self._label_mapping[label_id].name + + def _get_attribute_name(self, attribute_id): + for attribute_mapping in self._attribute_mapping_merged.values(): + if attribute_id in attribute_mapping: + return attribute_mapping[attribute_id] + + def _get_attribute_id(self, label_id, attribute_name, attribute_type=None): + if attribute_type: + container = self._attribute_mapping[label_id][attribute_type] + else: + container = self._attribute_mapping_merged[label_id] + + for attr_id, attr_name in container.items(): + if attribute_name == attr_name: + return attr_id + return None + + def _get_mutable_attribute_id(self, label_id, attribute_name): + return self._get_attribute_id(label_id, attribute_name, 'mutable') + + def _get_immutable_attribute_id(self, label_id, attribute_name): + return self._get_attribute_id(label_id, attribute_name, 'immutable') + + def _init_frame_info(self): + if self._db_task.mode == "interpolation": + self._frame_info = { + frame: { + "path": "frame_{:06d}".format(frame), + "width": self._db_task.video.width, + "height": self._db_task.video.height, + } for frame in range(self._db_task.size) + } + else: + self._frame_info = {db_image.frame: { + "path": db_image.path, + "width": db_image.width, + "height": db_image.height, + } for db_image in self._db_task.image_set.all()} + + def _init_meta(self): + db_segments = self._db_task.segment_set.all().prefetch_related('job_set') + self._meta = OrderedDict([ + ("task", OrderedDict([ + ("id", str(self._db_task.id)), + ("name", self._db_task.name), + ("size", str(self._db_task.size)), + ("mode", self._db_task.mode), + ("overlap", str(self._db_task.overlap)), + ("bugtracker", self._db_task.bug_tracker), + ("created", str(timezone.localtime(self._db_task.created_date))), + ("updated", str(timezone.localtime(self._db_task.updated_date))), + ("start_frame", str(self._db_task.start_frame)), + ("stop_frame", str(self._db_task.stop_frame)), + ("frame_filter", self._db_task.frame_filter), + ("z_order", str(self._db_task.z_order)), + + ("labels", [ + ("label", OrderedDict([ + ("name", db_label.name), + ("attributes", [ + ("attribute", OrderedDict([ + ("name", db_attr.name), + ("mutable", str(db_attr.mutable)), + ("input_type", db_attr.input_type), + ("default_value", db_attr.default_value), + ("values", db_attr.values)])) + for db_attr in db_label.attributespec_set.all()]) + ])) for db_label in self._label_mapping.values() + ]), + + ("segments", [ + ("segment", OrderedDict([ + ("id", str(db_segment.id)), + ("start", str(db_segment.start_frame)), + ("stop", str(db_segment.stop_frame)), + ("url", "{0}://{1}/?id={2}".format( + self._scheme, self._host, db_segment.job_set.all()[0].id))] + )) for db_segment in db_segments + ]), + + ("owner", OrderedDict([ + ("username", self._db_task.owner.username), + ("email", self._db_task.owner.email) + ]) if self._db_task.owner else ""), + + ("assignee", OrderedDict([ + ("username", self._db_task.assignee.username), + ("email", self._db_task.assignee.email) + ]) if self._db_task.assignee else ""), + ])), + ("dumped", str(timezone.localtime(timezone.now()))) + ]) + + if self._db_task.mode == "interpolation": + self._meta["task"]["original_size"] = OrderedDict([ + ("width", str(self._db_task.video.width)), + ("height", str(self._db_task.video.height)) + ]) + # Add source to dumped file + self._meta["source"] = str(os.path.basename(self._db_task.video.path)) + + def _export_attributes(self, attributes): + exported_attributes = [] + for attr in attributes: + attribute_name = self._get_attribute_name(attr["spec_id"]) + exported_attributes.append(Annotation.Attribute( + name=attribute_name, + value=attr["value"], + )) + return exported_attributes + + def _export_tracked_shape(self, shape): + return Annotation.TrackedShape( + type=shape["type"], + frame=self._db_task.start_frame + shape["frame"] * self._db_task.get_frame_step(), + points=shape["points"], + occluded=shape["occluded"], + outside=shape.get("outside", False), + keyframe=shape.get("keyframe", True), + z_order=shape["z_order"], + attributes=self._export_attributes(shape["attributes"]), + ) + + def _export_labeled_shape(self, shape): + return Annotation.LabeledShape( + type=shape["type"], + label=self._get_label_name(shape["label_id"]), + frame=self._db_task.start_frame + shape["frame"] * self._db_task.get_frame_step(), + points=shape["points"], + occluded=shape["occluded"], + z_order=shape.get("z_order", 0), + group=shape.get("group", 0), + attributes=self._export_attributes(shape["attributes"]), + ) + + def _export_tag(self, tag): + return Annotation.Tag( + frame=self._db_task.start_frame + tag["frame"] * self._db_task.get_frame_step(), + label=self._get_label_name(tag["label_id"]), + group=tag.get("group", 0), + attributes=self._export_attributes(tag["attributes"]), + ) + + def group_by_frame(self): + def _get_frame(annotations, shape): + db_image = self._frame_info[shape["frame"]] + frame = self._db_task.start_frame + shape["frame"] * self._db_task.get_frame_step() + rpath = db_image['path'].split(os.path.sep) + if len(rpath) != 1: + rpath = os.path.sep.join(rpath[rpath.index(".upload")+1:]) + else: + rpath = rpath[0] + if frame not in annotations: + annotations[frame] = Annotation.Frame( + frame=frame, + name=rpath, + height=db_image["height"], + width=db_image["width"], + labeled_shapes=[], + tags=[], + ) + return annotations[frame] + + annotations = {} + data_manager = DataManager(self._annotation_ir) + for shape in data_manager.to_shapes(self._db_task.size): + _get_frame(annotations, shape).labeled_shapes.append(self._export_labeled_shape(shape)) + + for tag in self._annotation_ir.tags: + _get_frame(annotations, tag).tags.append(self._export_tag(tag)) + + return iter(annotations.values()) + + @property + def shapes(self): + for shape in self._annotation_ir.shapes: + yield self._export_labeled_shape(shape) + + @property + def tracks(self): + for track in self._annotation_ir.tracks: + tracked_shapes = TrackManager.get_interpolated_shapes(track, 0, self._db_task.size) + for tracked_shape in tracked_shapes: + tracked_shape["attributes"] += track["attributes"] + + yield Annotation.Track( + label=self._get_label_name(track["label_id"]), + group=track['group'], + shapes=[self._export_tracked_shape(shape) for shape in tracked_shapes], + ) + + @property + def tags(self): + for tag in self._annotation_ir.tags: + yield self._export_tag(tag) + + @property + def meta(self): + return self._meta + + def _import_tag(self, tag): + _tag = tag._asdict() + label_id = self._get_label_id(_tag.pop('label')) + _tag['label_id'] = label_id + _tag['attributes'] = [self._import_attribute(label_id, attrib) for attrib in _tag['attributes'] + if self._get_attribute_id(label_id, attrib.name)] + return _tag + + def _import_attribute(self, label_id, attribute): + return { + 'spec_id': self._get_attribute_id(label_id, attribute.name), + 'value': attribute.value, + } + + def _import_shape(self, shape): + _shape = shape._asdict() + label_id = self._get_label_id(_shape.pop('label')) + _shape['label_id'] = label_id + _shape['attributes'] = [self._import_attribute(label_id, attrib) for attrib in _shape['attributes'] + if self._get_attribute_id(label_id, attrib.name)] + return _shape + + def _import_track(self, track): + _track = track._asdict() + label_id = self._get_label_id(_track.pop('label')) + _track['frame'] = min(shape.frame for shape in _track['shapes']) + _track['label_id'] = label_id + _track['attributes'] = [] + _track['shapes'] = [shape._asdict() for shape in _track['shapes']] + for shape in _track['shapes']: + _track['attributes'] = [self._import_attribute(label_id, attrib) for attrib in shape['attributes'] + if self._get_immutable_attribute_id(label_id, attrib.name)] + shape['attributes'] = [self._import_attribute(label_id, attrib) for attrib in shape['attributes'] + if self._get_mutable_attribute_id(label_id, attrib.name)] + + return _track + + def _call_callback(self): + if self._len() > self._MAX_ANNO_SIZE: + self._create_callback(self._annotation_ir.serialize()) + self._annotation_ir.reset() + + def add_tag(self, tag): + imported_tag = self._import_tag(tag) + if imported_tag['label_id']: + self._annotation_ir.add_tag(imported_tag) + self._call_callback() + + def add_shape(self, shape): + imported_shape = self._import_shape(shape) + if imported_shape['label_id']: + self._annotation_ir.add_shape(imported_shape) + self._call_callback() + + def add_track(self, track): + imported_track = self._import_track(track) + if imported_track['label_id']: + self._annotation_ir.add_track(imported_track) + self._call_callback() + + @property + def data(self): + return self._annotation_ir + + def _len(self): + track_len = 0 + for track in self._annotation_ir.tracks: + track_len += len(track['shapes']) + + return len(self._annotation_ir.tags) + len(self._annotation_ir.shapes) + track_len + + @property + def frame_info(self): + return self._frame_info diff --git a/cvat/apps/annotation/apps.py b/cvat/apps/annotation/apps.py new file mode 100644 index 00000000..6a14bfef --- /dev/null +++ b/cvat/apps/annotation/apps.py @@ -0,0 +1,18 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from django.apps import AppConfig +from django.db.models.signals import post_migrate +from cvat.apps.annotation.settings import BUILTIN_FORMATS + +def register_builtins_callback(sender, **kwargs): + from .format import register_format + for builtin_format in BUILTIN_FORMATS: + register_format(builtin_format) + +class AnnotationConfig(AppConfig): + name = 'cvat.apps.annotation' + + def ready(self): + post_migrate.connect(register_builtins_callback, sender=self) diff --git a/cvat/apps/annotation/coco.py b/cvat/apps/annotation/coco.py new file mode 100644 index 00000000..616639f9 --- /dev/null +++ b/cvat/apps/annotation/coco.py @@ -0,0 +1,393 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +format_spec = { + "name": "COCO", + "dumpers": [ + { + "display_name": "{name} {format} {version}", + "format": "JSON", + "version": "1.0", + "handler": "dump" + }, + ], + "loaders": [ + { + "display_name": "{name} {format} {version}", + "format": "JSON", + "version": "1.0", + "handler": "load" + }, + ], +} + +def mask_to_polygon(mask, tolerance=1.0, area_threshold=1): + """Convert object's mask to polygon [[x1,y1, x2,y2 ...], [...]] + Args: + mask: object's mask presented as 2D array of 0 and 1 + tolerance: maximum distance from original points of polygon to approximated + area_threshold: if area of a polygon is less than this value, remove this small object + """ + from skimage import measure + from pycocotools import mask as mask_util + import numpy as np + + polygons = [] + # pad mask with 0 around borders + padded_mask = np.pad(mask, pad_width=1, mode='constant', constant_values=0) + contours = measure.find_contours(padded_mask, 0.5) + # Fix coordinates after padding + contours = np.subtract(contours, 1) + for contour in contours: + if not np.array_equal(contour[0], contour[-1]): + contour = np.vstack((contour, contour[0])) + contour = measure.approximate_polygon(contour, tolerance) + if len(contour) > 2: + contour = np.flip(contour, axis=1) + reshaped_contour = [] + for xy in contour: + reshaped_contour.append(xy[0]) + reshaped_contour.append(xy[1]) + reshaped_contour = [point if point > 0 else 0 for point in reshaped_contour] + + # Check if area of a polygon is enough + rle = mask_util.frPyObjects([reshaped_contour], mask.shape[0], mask.shape[1]) + area = mask_util.area(rle) + if sum(area) > area_threshold: + polygons.append(reshaped_contour) + return polygons + +def dump(file_object, annotations): + import numpy as np + import json + from collections import OrderedDict + from pycocotools import mask as mask_util + from pycocotools import coco as coco_loader + + + def fix_segments_intersections(polygons, height, width, img_name, + threshold=0.0, ratio_tolerance=0.001, area_threshold=1): + """Find all intersected regions and crop contour for back object by objects which + are in front of the first one. It is related to a specialty of segmentation + in CVAT annotation. Intersection is calculated via function 'iou' from cocoapi + Args: + polygons: all objects on image represented as 2D array of objects' contours + height: height of image + width: width of image + img_name: name of image file + threshold: threshold of intersection over union of two objects. + By default is set to 0 and processes any two intersected objects + ratio_tolerance: used for situation when one object is fully or almost fully + inside another one and we don't want make "hole" in one of objects + """ + empty_polygon = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + for i, _ in enumerate(polygons): + rle_bottom = mask_util.frPyObjects([polygons[i]['points']], height, width) + segment_overlapped = False + for j in range(i + 1, len(polygons)): + rle_top = mask_util.frPyObjects([polygons[j]['points']], height, width) + iou = mask_util.iou(rle_bottom, rle_top, [0, 0]) + area_top = sum(mask_util.area(rle_top)) + area_bottom = sum(mask_util.area(rle_bottom)) + if area_bottom == 0: + continue + area_ratio = area_top / area_bottom + sum_iou = sum(iou) + + # If segment is fully inside another one, save this segment as is + if area_ratio - ratio_tolerance < sum_iou[0] < area_ratio + ratio_tolerance: + continue + # Check situation when bottom segment is fully inside top. + # It means that in annotation is mistake. Save this segment as is + if 1 / area_ratio - ratio_tolerance < sum_iou[0] < 1 / area_ratio + ratio_tolerance: + continue + + if sum_iou[0] > threshold: + segment_overlapped = True + bottom_mask = np.array(mask_util.decode(rle_bottom), dtype=np.uint8) + top_mask = np.array(mask_util.decode(rle_top), dtype=np.uint8) + + bottom_mask = np.subtract(bottom_mask, top_mask) + bottom_mask[bottom_mask > 1] = 0 + + bottom_mask = np.sum(bottom_mask, axis=2) + bottom_mask = np.array(bottom_mask > 0, dtype=np.uint8) + polygons[i]['points'] = mask_to_polygon(bottom_mask, area_threshold=area_threshold) + # If some segment is empty, do small fix to avoid error in cocoapi function + if len(polygons[i]['points']) == 0: + polygons[i]['points'] = [empty_polygon] + rle_bottom = mask_util.frPyObjects(polygons[i]['points'], height, width) + if not segment_overlapped: + polygons[i]['points'] = [polygons[i]['points']] + + output_polygons = [] + for polygon in polygons: + poly_len = len(polygon['points']) + if poly_len != 0 and polygon['points'] != [empty_polygon]: + output_polygons.append(polygon) + + return output_polygons + + + def polygon_area_and_bbox(polygon, height, width): + """Calculate area of object's polygon and bounding box around it + Args: + polygon: objects contour represented as 2D array + height: height of object's region (use full image) + width: width of object's region (use full image) + """ + rle = mask_util.frPyObjects(polygon, height, width) + area = mask_util.area(rle) + bbox = mask_util.toBbox(rle) + bbox = [min(bbox[:, 0]), + min(bbox[:, 1]), + max(bbox[:, 0] + bbox[:, 2]) - min(bbox[:, 0]), + max(bbox[:, 1] + bbox[:, 3]) - min(bbox[:, 1])] + return area, bbox + + + def insert_license_data(result_annotation): + """Fill license fields in annotation by blank data + Args: + result_annotation: output annotation in COCO representation + """ + result_annotation['licenses'].append(OrderedDict([ + ('name', ''), + ('id', 0), + ('url', ''), + ])) + + + def insert_info_data(annotations, result_annotation): + """Fill available information of annotation + Args: + xml_root: root for xml parser + result_annotation: output annotation in COCO representation + """ + version = annotations.data.version + description = annotations.meta['task']['name'] + date = annotations.meta['dumped'] + date = date.split(' ')[0] + year = date.split('-')[0] + result_annotation['info'] = OrderedDict([ + ('contributor', ''), + ('date_created', date), + ('description', description), + ('url', ''), + ('version', version), + ('year', year), + ]) + + + def insert_categories_data(annotations, result_annotation): + """Get labels from input annotation and fill categories field in output annotation + Args: + xml_root: root for xml parser + result_annotation: output annotation in COCO representation + labels_file: path to file with labels names. + If not defined, parse annotation to get labels names + """ + def get_categories(names, sort=False): + category_map = {} + categories = [] + # Sort labels by its names to make the same order of ids for different annotations + if sort: + names.sort() + cat_id = 1 + for name in names: + category_map[name] = cat_id + categories.append(OrderedDict([ + ('id', cat_id), + ('name', name), + ('supercategory', ''), + ])) + cat_id += 1 + return category_map, categories + + label_names = [label[1]["name"] for label in annotations.meta['task']['labels']] + + category_map, categories = get_categories(label_names, sort=True) + + result_annotation['categories'] = categories + return category_map + + + def insert_image_data(image, result_annotation): + """Get data from input annotation for image and fill fields for this image in output annotation + Args: + image: dictionary with data for image from original annotation + result_annotation: output annotation in COCO representation + """ + new_img = OrderedDict() + new_img['coco_url'] = '' + new_img['date_captured'] = '' + new_img['flickr_url'] = '' + new_img['license'] = 0 + new_img['id'] = image.frame + new_img['file_name'] = image.name + new_img['height'] = image.height + new_img['width'] = image.width + result_annotation['images'].append(new_img) + + + def insert_annotation_data(image, category_map, segm_id, obj, result_annotation): + """Get data from input annotation for object and fill fields for this object in output annotation + Args: + image: dictionary with data for image from input CVAT annotation + category_map: map for categories represented in the annotation {name: id} + segm_id: identificator of current object + obj: includes data for the object [label, polygon] + result_annotation: output annotation in COCO representation + """ + new_anno = OrderedDict() + new_anno['category_id'] = category_map[obj['label']] + new_anno['id'] = segm_id + new_anno['image_id'] = image.frame + new_anno['iscrowd'] = 0 + new_anno['segmentation'] = obj['points'] + area, bbox = polygon_area_and_bbox(obj['points'], image.height, image.width) + new_anno['area'] = float(np.sum(area)) + new_anno['bbox'] = bbox + result_annotation['annotations'].append(new_anno) + + result_annotation = OrderedDict([ + ('licenses', []), + ('info', {}), + ('categories', []), + ('images', []), + ('annotations', []), + ]) + + insert_license_data(result_annotation) + insert_info_data(annotations, result_annotation) + category_map = insert_categories_data(annotations, result_annotation) + + segm_id = 0 + for img in annotations.group_by_frame(): + polygons = [] + + for shape in img.labeled_shapes: + if shape.type == 'polygon' or shape.type == 'rectangle': + polygon = { + 'label': shape.label, + 'points': shape.points, + 'z_order': shape.z_order, + 'group': shape.group, + } + + if shape.type == 'rectangle': + xtl = polygon['points'][0] + ytl = polygon['points'][1] + xbr = polygon['points'][2] + ybr = polygon['points'][3] + polygon['points'] = [xtl, ytl, xbr, ytl, xbr, ybr, xtl, ybr] + + polygons.append(polygon) + + polygons.sort(key=lambda x: int(x['z_order'])) + + # Create new image + insert_image_data(img, result_annotation) + polygons = fix_segments_intersections(polygons, img.height, img.width, img.name) + + # combine grouped polygons with the same label + grouped_poligons = OrderedDict() + ungrouped_poligons = [] + for polygon in polygons: + group_id = polygon['group'] + label = polygon['label'] + if group_id != 0: + if group_id not in grouped_poligons: + grouped_poligons[group_id] = OrderedDict() + + if label not in grouped_poligons[group_id]: + grouped_poligons[group_id][label] = polygon + else: + grouped_poligons[group_id][label]['points'].extend(polygon['points']) + else: + ungrouped_poligons.append(polygon) + polygons = ungrouped_poligons + [poly for group in grouped_poligons.values() for poly in group.values()] + + # Create new annotation for this image + for poly in polygons: + insert_annotation_data(img, category_map, segm_id, poly, result_annotation) + segm_id += 1 + + file_object.write(json.dumps(result_annotation, indent=2).encode()) + file_object.flush() + + # Try to load created annotation via cocoapi + try: + coco_loader.COCO(file_object.name) + except: + raise + +def load(file_object, annotations): + from pycocotools import coco as coco_loader + from pycocotools import mask as mask_utils + import numpy as np + + def get_filename(path): + import os + return os.path.splitext(os.path.basename(path))[0] + + def match_frame(frame_info, filename): + import re + # try to match by filename + yolo_filename = get_filename(filename) + for frame_number, info in frame_info.items(): + cvat_filename = get_filename(info["path"]) + if cvat_filename == yolo_filename: + return frame_number + + # try to extract frame number from filename + numbers = re.findall(r"\d+", filename) + if numbers and len(numbers) == 1: + return int(numbers[0]) + + raise Exception("Cannot match filename or determinate framenumber for {} filename".format(filename)) + + coco = coco_loader.COCO(file_object.name) + labels={cat['id']: cat['name'] for cat in coco.loadCats(coco.getCatIds())} + + group_idx = 0 + for img_id in coco.getImgIds(): + anns = coco.loadAnns(coco.getAnnIds(imgIds=img_id)) + img = coco.loadImgs(ids=img_id)[0] + frame_number = match_frame(annotations.frame_info, img['file_name']) + for ann in anns: + group = 0 + label_name = labels[ann['category_id']] + if 'segmentation' in ann: + polygons = [] + # polygon + if ann['iscrowd'] == 0: + polygons = ann['segmentation'] + # mask + else: + if isinstance(ann['segmentation']['counts'], list): + rle = mask_utils.frPyObjects([ann['segmentation']], img['height'], img['width']) + else: + rle = [ann['segmentation']] + + mask = np.array(mask_utils.decode(rle), dtype=np.uint8) + mask = np.sum(mask, axis=2) + mask = np.array(mask > 0, dtype=np.uint8) + polygons = mask_to_polygon(mask) + + if len(polygons) > 1: + group_idx += 1 + group = group_idx + + for polygon in polygons: + annotations.add_shape(annotations.LabeledShape( + type='polygon', + frame=frame_number, + label=label_name, + points=polygon, + occluded=False, + attributes=[], + group=group, + )) diff --git a/cvat/apps/annotation/cvat.py b/cvat/apps/annotation/cvat.py new file mode 100644 index 00000000..ab249304 --- /dev/null +++ b/cvat/apps/annotation/cvat.py @@ -0,0 +1,411 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +format_spec = { + "name": "CVAT", + "dumpers": [ + { + "display_name": "{name} {format} {version} for videos", + "format": "XML", + "version": "1.1", + "handler": "dump_as_cvat_interpolation" + }, + { + "display_name": "{name} {format} {version} for images", + "format": "XML", + "version": "1.1", + "handler": "dump_as_cvat_annotation" + } + ], + "loaders": [ + { + "display_name": "{name} {format} {version}", + "format": "XML", + "version": "1.1", + "handler": "load", + } + ], +} + +def pairwise(iterable): + a = iter(iterable) + return zip(a, a) + +def create_xml_dumper(file_object): + from xml.sax.saxutils import XMLGenerator + from collections import OrderedDict + class XmlAnnotationWriter: + def __init__(self, file): + self.version = "1.1" + self.file = file + self.xmlgen = XMLGenerator(self.file, 'utf-8') + self._level = 0 + + def _indent(self, newline = True): + if newline: + self.xmlgen.ignorableWhitespace("\n") + self.xmlgen.ignorableWhitespace(" " * self._level) + + def _add_version(self): + self._indent() + self.xmlgen.startElement("version", {}) + self.xmlgen.characters(self.version) + self.xmlgen.endElement("version") + + def open_root(self): + self.xmlgen.startDocument() + self.xmlgen.startElement("annotations", {}) + self._level += 1 + self._add_version() + + def _add_meta(self, meta): + self._level += 1 + for k, v in meta.items(): + if isinstance(v, OrderedDict): + self._indent() + self.xmlgen.startElement(k, {}) + self._add_meta(v) + self._indent() + self.xmlgen.endElement(k) + elif isinstance(v, list): + self._indent() + self.xmlgen.startElement(k, {}) + for tup in v: + self._add_meta(OrderedDict([tup])) + self._indent() + self.xmlgen.endElement(k) + else: + self._indent() + self.xmlgen.startElement(k, {}) + self.xmlgen.characters(v) + self.xmlgen.endElement(k) + self._level -= 1 + + def add_meta(self, meta): + self._indent() + self.xmlgen.startElement("meta", {}) + self._add_meta(meta) + self._indent() + self.xmlgen.endElement("meta") + + def open_track(self, track): + self._indent() + self.xmlgen.startElement("track", track) + self._level += 1 + + def open_image(self, image): + self._indent() + self.xmlgen.startElement("image", image) + self._level += 1 + + def open_box(self, box): + self._indent() + self.xmlgen.startElement("box", box) + self._level += 1 + + def open_polygon(self, polygon): + self._indent() + self.xmlgen.startElement("polygon", polygon) + self._level += 1 + + def open_polyline(self, polyline): + self._indent() + self.xmlgen.startElement("polyline", polyline) + self._level += 1 + + def open_points(self, points): + self._indent() + self.xmlgen.startElement("points", points) + self._level += 1 + + def add_attribute(self, attribute): + self._indent() + self.xmlgen.startElement("attribute", {"name": attribute["name"]}) + self.xmlgen.characters(attribute["value"]) + self.xmlgen.endElement("attribute") + + def close_box(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("box") + + def close_polygon(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("polygon") + + def close_polyline(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("polyline") + + def close_points(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("points") + + def close_image(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("image") + + def close_track(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("track") + + def close_root(self): + self._level -= 1 + self._indent() + self.xmlgen.endElement("annotations") + self.xmlgen.endDocument() + + return XmlAnnotationWriter(file_object) + +def dump_as_cvat_annotation(file_object, annotations): + from collections import OrderedDict + dumper = create_xml_dumper(file_object) + dumper.open_root() + dumper.add_meta(annotations.meta) + + for frame_annotation in annotations.group_by_frame(): + frame_id = frame_annotation.frame + dumper.open_image(OrderedDict([ + ("id", str(frame_id)), + ("name", frame_annotation.name), + ("width", str(frame_annotation.width)), + ("height", str(frame_annotation.height)) + ])) + + for shape in frame_annotation.labeled_shapes: + dump_data = OrderedDict([ + ("label", shape.label), + ("occluded", str(int(shape.occluded))), + ]) + + if shape.type == "rectangle": + dump_data.update(OrderedDict([ + ("xtl", "{:.2f}".format(shape.points[0])), + ("ytl", "{:.2f}".format(shape.points[1])), + ("xbr", "{:.2f}".format(shape.points[2])), + ("ybr", "{:.2f}".format(shape.points[3])) + ])) + else: + dump_data.update(OrderedDict([ + ("points", ';'.join(( + ','.join(( + "{:.2f}".format(x), + "{:.2f}".format(y) + )) for x, y in pairwise(shape.points)) + )), + ])) + + if annotations.meta["task"]["z_order"] != "False": + dump_data['z_order'] = str(shape.z_order) + if shape.group: + dump_data['group_id'] = str(shape.group) + + if shape.type == "rectangle": + dumper.open_box(dump_data) + elif shape.type == "polygon": + dumper.open_polygon(dump_data) + elif shape.type == "polyline": + dumper.open_polyline(dump_data) + elif shape.type == "points": + dumper.open_points(dump_data) + else: + raise NotImplementedError("unknown shape type") + + for attr in shape.attributes: + dumper.add_attribute(OrderedDict([ + ("name", attr.name), + ("value", attr.value) + ])) + + if shape.type == "rectangle": + dumper.close_box() + elif shape.type == "polygon": + dumper.close_polygon() + elif shape.type == "polyline": + dumper.close_polyline() + elif shape.type == "points": + dumper.close_points() + else: + raise NotImplementedError("unknown shape type") + + dumper.close_image() + dumper.close_root() + +def dump_as_cvat_interpolation(file_object, annotations): + from collections import OrderedDict + dumper = create_xml_dumper(file_object) + dumper.open_root() + dumper.add_meta(annotations.meta) + def dump_track(idx, track): + track_id = idx + dump_data = OrderedDict([ + ("id", str(track_id)), + ("label", track.label), + ]) + + if track.group: + dump_data['group_id'] = str(track.group) + dumper.open_track(dump_data) + + for shape in track.shapes: + dump_data = OrderedDict([ + ("frame", str(shape.frame)), + ("outside", str(int(shape.outside))), + ("occluded", str(int(shape.occluded))), + ("keyframe", str(int(shape.keyframe))), + ]) + + if shape.type == "rectangle": + dump_data.update(OrderedDict([ + ("xtl", "{:.2f}".format(shape.points[0])), + ("ytl", "{:.2f}".format(shape.points[1])), + ("xbr", "{:.2f}".format(shape.points[2])), + ("ybr", "{:.2f}".format(shape.points[3])), + ])) + else: + dump_data.update(OrderedDict([ + ("points", ';'.join(['{:.2f},{:.2f}'.format(x, y) + for x,y in pairwise(shape.points)])) + ])) + + if annotations.meta["task"]["z_order"] != "False": + dump_data["z_order"] = str(shape.z_order) + + if shape.type == "rectangle": + dumper.open_box(dump_data) + elif shape.type == "polygon": + dumper.open_polygon(dump_data) + elif shape.type == "polyline": + dumper.open_polyline(dump_data) + elif shape.type == "points": + dumper.open_points(dump_data) + else: + raise NotImplementedError("unknown shape type") + + for attr in shape.attributes: + dumper.add_attribute(OrderedDict([ + ("name", attr.name), + ("value", attr.value) + ])) + + if shape.type == "rectangle": + dumper.close_box() + elif shape.type == "polygon": + dumper.close_polygon() + elif shape.type == "polyline": + dumper.close_polyline() + elif shape.type == "points": + dumper.close_points() + else: + raise NotImplementedError("unknown shape type") + dumper.close_track() + + counter = 0 + for track in annotations.tracks: + dump_track(counter, track) + counter += 1 + + for shape in annotations.shapes: + dump_track(counter, annotations.Track( + label=shape.label, + group=shape.group, + shapes=[annotations.TrackedShape( + type=shape.type, + points=shape.points, + occluded=shape.occluded, + outside=False, + keyframe=True, + z_order=shape.z_order, + frame=shape.frame, + attributes=shape.attributes, + ), + annotations.TrackedShape( + type=shape.type, + points=shape.points, + occluded=shape.occluded, + outside=True, + keyframe=True, + z_order=shape.z_order, + frame=shape.frame + 1, + attributes=shape.attributes, + ), + ], + )) + counter += 1 + + dumper.close_root() + +def load(file_object, annotations): + import xml.etree.ElementTree as et + context = et.iterparse(file_object, events=("start", "end")) + context = iter(context) + ev, _ = next(context) + + supported_shapes = ('box', 'polygon', 'polyline', 'points') + + track = None + shape = None + image_is_opened = False + for ev, el in context: + if ev == 'start': + if el.tag == 'track': + track = annotations.Track( + label=el.attrib['label'], + group=int(el.attrib.get('group_id', 0)), + shapes=[], + ) + elif el.tag == 'image': + image_is_opened = True + frame_id = int(el.attrib['id']) + elif el.tag in supported_shapes and (track is not None or image_is_opened): + shape = { + 'attributes': [], + 'points': [], + } + elif ev == 'end': + if el.tag == 'attribute' and shape is not None: + shape['attributes'].append(annotations.Attribute( + name=el.attrib['name'], + value=el.text, + )) + if el.tag in supported_shapes: + if track is not None: + shape['frame'] = el.attrib['frame'] + shape['outside'] = el.attrib['outside'] == "1" + shape['keyframe'] = el.attrib['keyframe'] == "1" + else: + shape['frame'] = frame_id + shape['label'] = el.attrib['label'] + shape['group'] = int(el.attrib.get('group_id', 0)) + + shape['type'] = 'rectangle' if el.tag == 'box' else el.tag + shape['occluded'] = el.attrib['occluded'] == '1' + shape['z_order'] = int(el.attrib.get('z_order', 0)) + + if el.tag == 'box': + shape['points'].append(el.attrib['xtl']) + shape['points'].append(el.attrib['ytl']) + shape['points'].append(el.attrib['xbr']) + shape['points'].append(el.attrib['ybr']) + else: + for pair in el.attrib['points'].split(';'): + shape['points'].extend(map(float, pair.split(','))) + + if track is not None: + track.shapes.append(annotations.TrackedShape(**shape)) + else: + annotations.add_shape(annotations.LabeledShape(**shape)) + shape = None + + elif el.tag == 'track': + annotations.add_track(track) + track = None + elif el.tag == 'image': + image_is_opened = False + el.clear() diff --git a/cvat/apps/annotation/format.py b/cvat/apps/annotation/format.py new file mode 100644 index 00000000..497c3812 --- /dev/null +++ b/cvat/apps/annotation/format.py @@ -0,0 +1,45 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from cvat.apps.annotation import models +from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist +from cvat.apps.annotation.serializers import AnnotationFormatSerializer +from django.core.files import File + +import os +from copy import deepcopy + +def register_format(format_file): + source_code = open(format_file, 'r').read() + global_vars = { + "__builtins__": {}, + } + exec(source_code, global_vars) + if "format_spec" not in global_vars or not isinstance(global_vars["format_spec"], dict): + raise Exception("Could not find \'format_spec\' definition in format file specification") + + format_spec = deepcopy(global_vars["format_spec"]) + format_spec["handler_file"] = File(open(format_file)) + for spec in format_spec["loaders"] + format_spec["dumpers"]: + spec["display_name"] = spec["display_name"].format( + name=format_spec["name"], + format=spec["format"], + version=spec["version"], + ) + + try: + annotation_format = models.AnnotationFormat.objects.get(name=format_spec["name"]) + serializer = AnnotationFormatSerializer(annotation_format, data=format_spec) + if serializer.is_valid(raise_exception=True): + serializer.save() + except ObjectDoesNotExist: + serializer = AnnotationFormatSerializer(data=format_spec) + if serializer.is_valid(raise_exception=True): + serializer.save() + +def get_annotation_formats(): + return AnnotationFormatSerializer( + models.AnnotationFormat.objects.all(), + many=True).data diff --git a/cvat/apps/annotation/mask.py b/cvat/apps/annotation/mask.py new file mode 100644 index 00000000..b1713fd0 --- /dev/null +++ b/cvat/apps/annotation/mask.py @@ -0,0 +1,80 @@ +# Copyright (C) 2019 Intel Corporation +# +# SPDX-License-Identifier: MIT + +format_spec = { + "name": "MASK", + "dumpers": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "dump" + }, + ], + "loaders": [ + ], +} + +def dump(file_object, annotations): + from zipfile import ZipFile + import numpy as np + import os + from pycocotools import mask as maskUtils + import matplotlib.image + import io + from collections import OrderedDict + + # RGB format, (0, 0, 0) used for background + def genearte_pascal_colormap(size=256): + colormap = np.zeros((size, 3), dtype=int) + ind = np.arange(size, dtype=int) + + for shift in reversed(range(8)): + for channel in range(3): + colormap[:, channel] |= ((ind >> channel) & 1) << shift + ind >>= 3 + + return colormap + + def convert_box_to_polygon(points): + xtl = shape.points[0] + ytl = shape.points[1] + xbr = shape.points[2] + ybr = shape.points[3] + + return [xtl, ytl, xbr, ytl, xbr, ybr, xtl, ybr] + + colormap = genearte_pascal_colormap() + labels = [label[1]["name"] for label in annotations.meta["task"]["labels"] if label[1]["name"] != 'background'] + labels.insert(0, 'background') + label_colors = OrderedDict((label, colormap[idx]) for idx, label in enumerate(labels)) + + with ZipFile(file_object, "w") as output_zip: + for frame_annotation in annotations.group_by_frame(): + image_name = frame_annotation.name + annotation_name = "{}.png".format(os.path.splitext(os.path.basename(image_name))[0]) + width = frame_annotation.width + height = frame_annotation.height + + shapes = frame_annotation.labeled_shapes + # convert to mask only rectangles and polygons + shapes = [shape for shape in shapes if shape.type == 'rectangle' or shape.type == 'polygon'] + if not shapes: + continue + shapes = sorted(shapes, key=lambda x: int(x.z_order)) + img = np.zeros((height, width, 3)) + buf = io.BytesIO() + for shape in shapes: + points = shape.points if shape.type != 'rectangle' else convert_box_to_polygon(shape.points) + rles = maskUtils.frPyObjects([points], height, width) + rle = maskUtils.merge(rles) + mask = maskUtils.decode(rle) + color = label_colors[shape.label] / 255 + idx = (mask > 0) + img[idx] = color + + matplotlib.image.imsave(buf, img, format='png') + output_zip.writestr(annotation_name, buf.getvalue()) + labels = '\n'.join('{}:{}'.format(label, ','.join(str(i) for i in color)) for label, color in label_colors.items()) + output_zip.writestr('colormap.txt', labels) diff --git a/cvat/apps/annotation/migrations/0001_initial.py b/cvat/apps/annotation/migrations/0001_initial.py new file mode 100644 index 00000000..9c331fee --- /dev/null +++ b/cvat/apps/annotation/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# Generated by Django 2.1.9 on 2019-07-31 15:20 + +import cvat.apps.annotation.models +import cvat.apps.engine.models +from django.conf import settings +import django.core.files.storage +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='AnnotationFormat', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', cvat.apps.engine.models.SafeCharField(max_length=256)), + ('created_date', models.DateTimeField(auto_now_add=True)), + ('updated_date', models.DateTimeField(auto_now_add=True)), + ('handler_file', models.FileField(storage=django.core.files.storage.FileSystemStorage(location=settings.BASE_DIR), upload_to=cvat.apps.annotation.models.upload_file_handler)), + ('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'default_permissions': (), + }, + ), + migrations.CreateModel( + name='AnnotationHandler', + fields=[ + ('type', models.CharField(choices=[('dumper', 'DUMPER'), ('loader', 'LOADER')], max_length=16)), + ('display_name', cvat.apps.engine.models.SafeCharField(max_length=256, primary_key=True, serialize=False)), + ('format', models.CharField(max_length=16)), + ('version', models.CharField(max_length=16)), + ('handler', models.CharField(max_length=256)), + ('annotation_format', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='annotation.AnnotationFormat')), + ], + options={ + 'default_permissions': (), + }, + ), + ] diff --git a/cvat/apps/annotation/migrations/0002_auto_20190805_0927.py b/cvat/apps/annotation/migrations/0002_auto_20190805_0927.py new file mode 100644 index 00000000..6b289864 --- /dev/null +++ b/cvat/apps/annotation/migrations/0002_auto_20190805_0927.py @@ -0,0 +1,74 @@ +# Generated by Django 2.1.9 on 2019-08-05 06:27 + +import cvat.apps.engine.models +from django.db import migrations, models +import django.db.models.deletion + +def split_handlers(apps, schema_editor): + db_alias = schema_editor.connection.alias + handler_model = apps.get_model('annotation', 'AnnotationHandler') + dumper_model = apps.get_model('annotation', "AnnotationDumper") + loader_model = apps.get_model('annotation', 'AnnotationLoader') + + + for db_handler in handler_model.objects.all(): + if db_handler.type == "dumper": + new_handler = dumper_model() + else: + new_handler = loader_model() + + new_handler.display_name = db_handler.display_name + new_handler.format = db_handler.format + new_handler.version = db_handler.version + new_handler.handler = db_handler.handler + new_handler.annotation_format = db_handler.annotation_format + + new_handler.save() + db_handler.delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('annotation', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='AnnotationDumper', + fields=[ + ('display_name', cvat.apps.engine.models.SafeCharField(max_length=256, primary_key=True, serialize=False)), + ('format', models.CharField(max_length=16)), + ('version', models.CharField(max_length=16)), + ('handler', models.CharField(max_length=256)), + ('annotation_format', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='annotation.AnnotationFormat')), + ], + options={ + 'abstract': False, + 'default_permissions': (), + }, + ), + migrations.CreateModel( + name='AnnotationLoader', + fields=[ + ('display_name', cvat.apps.engine.models.SafeCharField(max_length=256, primary_key=True, serialize=False)), + ('format', models.CharField(max_length=16)), + ('version', models.CharField(max_length=16)), + ('handler', models.CharField(max_length=256)), + ('annotation_format', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='annotation.AnnotationFormat')), + ], + options={ + 'abstract': False, + 'default_permissions': (), + }, + ), + migrations.RunPython( + code=split_handlers, + ), + migrations.RemoveField( + model_name='annotationhandler', + name='annotation_format', + ), + migrations.DeleteModel( + name='AnnotationHandler', + ), + ] diff --git a/cvat/apps/annotation/migrations/__init__.py b/cvat/apps/annotation/migrations/__init__.py new file mode 100644 index 00000000..b66dde17 --- /dev/null +++ b/cvat/apps/annotation/migrations/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT diff --git a/cvat/apps/annotation/models.py b/cvat/apps/annotation/models.py new file mode 100644 index 00000000..3595327d --- /dev/null +++ b/cvat/apps/annotation/models.py @@ -0,0 +1,46 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +import os + +from django.db import models +from django.conf import settings +from django.core.files.storage import FileSystemStorage +from django.contrib.auth.models import User + +from cvat.apps.engine.models import SafeCharField + +def upload_file_handler(instance, filename): + return os.path.join('formats', str(instance.id), filename) + +class AnnotationFormat(models.Model): + name = SafeCharField(max_length=256) + owner = models.ForeignKey(User, null=True, blank=True, + on_delete=models.SET_NULL) + created_date = models.DateTimeField(auto_now_add=True) + updated_date = models.DateTimeField(auto_now_add=True) + handler_file = models.FileField( + upload_to=upload_file_handler, + storage=FileSystemStorage(location=os.path.join(settings.BASE_DIR)), + ) + + class Meta: + default_permissions = () + +class AnnotationHandler(models.Model): + display_name = SafeCharField(max_length=256, primary_key=True) + format = models.CharField(max_length=16) + version = models.CharField(max_length=16) + handler = models.CharField(max_length=256) + annotation_format = models.ForeignKey(AnnotationFormat, on_delete=models.CASCADE) + + class Meta: + default_permissions = () + abstract = True + +class AnnotationDumper(AnnotationHandler): + pass + +class AnnotationLoader(AnnotationHandler): + pass diff --git a/cvat/apps/annotation/pascal_voc.py b/cvat/apps/annotation/pascal_voc.py new file mode 100644 index 00000000..1a9c6806 --- /dev/null +++ b/cvat/apps/annotation/pascal_voc.py @@ -0,0 +1,110 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +format_spec = { + "name": "PASCAL VOC", + "dumpers": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "dump" + }, + ], + "loaders": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "load" + }, + ], +} + +def load(file_object, annotations): + from pyunpack import Archive + import os + import re + from tempfile import TemporaryDirectory + + def match_frame(frame_info, filename): + def get_filename(path): + return os.path.splitext(os.path.basename(path))[0] + + # try to match by filename + pascal_filename = get_filename(filename) + for frame_number, info in frame_info.items(): + cvat_filename = get_filename(info['path']) + if cvat_filename == pascal_filename: + return frame_number + + # try to extract framenumber from filename + numbers = re.findall(r'\d+', filename) + if numbers and len(numbers) == 1: + return int(numbers[0]) + + raise Exception('Cannot match filename or determinate framenumber for {} filename'.format(filename)) + + def parse_xml_file(annotation_file): + import xml.etree.ElementTree as ET + root = ET.parse(annotation_file).getroot() + frame_number = match_frame(annotations.frame_info, root.find('filename').text) + + for obj_tag in root.iter('object'): + bbox_tag = obj_tag.find("bndbox") + label = obj_tag.find('name').text + xmin = float(bbox_tag.find('xmin').text) + ymin = float(bbox_tag.find('ymin').text) + xmax = float(bbox_tag.find('xmax').text) + ymax = float(bbox_tag.find('ymax').text) + + annotations.add_shape(annotations.LabeledShape( + type='rectangle', + frame=frame_number, + label=label, + points=[xmin, ymin, xmax, ymax], + occluded=False, + attributes=[], + )) + + archive_file = getattr(file_object, 'name') + with TemporaryDirectory() as tmp_dir: + Archive(archive_file).extractall(tmp_dir) + + for dirpath, _, filenames in os.walk(tmp_dir): + for _file in filenames: + if '.xml' == os.path.splitext(_file)[1]: + parse_xml_file(os.path.join(dirpath, _file)) + +def dump(file_object, annotations): + from pascal_voc_writer import Writer + import os + from zipfile import ZipFile + from tempfile import TemporaryDirectory + + with TemporaryDirectory() as out_dir: + with ZipFile(file_object, 'w') as output_zip: + for frame_annotation in annotations.group_by_frame(): + image_name = frame_annotation.name + width = frame_annotation.width + height = frame_annotation.height + + writer = Writer(image_name, width, height) + writer.template_parameters['path'] = '' + writer.template_parameters['folder'] = '' + + for shape in frame_annotation.labeled_shapes: + if shape.type != "rectangle": + continue + label = shape.label + xtl = shape.points[0] + ytl = shape.points[1] + xbr = shape.points[2] + ybr = shape.points[3] + writer.addObject(label, xtl, ytl, xbr, ybr) + + anno_name = os.path.basename('{}.{}'.format(os.path.splitext(image_name)[0], 'xml')) + anno_file = os.path.join(out_dir, anno_name) + writer.save(anno_file) + output_zip.write(filename=anno_file, arcname=anno_name) diff --git a/cvat/apps/annotation/serializers.py b/cvat/apps/annotation/serializers.py new file mode 100644 index 00000000..8fa8b345 --- /dev/null +++ b/cvat/apps/annotation/serializers.py @@ -0,0 +1,76 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from rest_framework import serializers +from cvat.apps.annotation import models + +class AnnotationDumperSerializer(serializers.ModelSerializer): + class Meta: + model = models.AnnotationDumper + exclude = ('annotation_format',) + # https://www.django-rest-framework.org/api-guide/validators/#updating-nested-serializers + extra_kwargs = { + 'display_name': { + 'validators': [], + }, + } + +class AnnotationLoaderSerializer(serializers.ModelSerializer): + class Meta: + model = models.AnnotationLoader + exclude = ('annotation_format',) + # https://www.django-rest-framework.org/api-guide/validators/#updating-nested-serializers + extra_kwargs = { + 'display_name': { + 'validators': [], + }, + } + +class AnnotationFormatSerializer(serializers.ModelSerializer): + dumpers = AnnotationDumperSerializer(many=True, source="annotationdumper_set") + loaders = AnnotationLoaderSerializer(many=True, source="annotationloader_set") + + class Meta: + model = models.AnnotationFormat + fields = "__all__" + + # pylint: disable=no-self-use + def create(self, validated_data): + dumpers = validated_data.pop("annotationdumper_set") + loaders = validated_data.pop("annotationloader_set") + + annotation_format = models.AnnotationFormat() + annotation_format.name = validated_data["name"] + annotation_format.handler_file = validated_data["handler_file"].name + annotation_format.save() + + for dumper in dumpers: + models.AnnotationDumper(annotation_format=annotation_format, **dumper).save() + + for loader in loaders: + models.AnnotationLoader(annotation_format=annotation_format, **loader).save() + + return annotation_format + + # pylint: disable=no-self-use + def update(self, instance, validated_data): + dumper_names = [handler["display_name"] for handler in validated_data["annotationdumper_set"]] + loader_names = [handler["display_name"] for handler in validated_data["annotationloader_set"]] + + handlers_to_delete = [d for d in instance.annotationdumper_set.all() if d.display_name not in dumper_names] + \ + [l for l in instance.annotationloader_set.all() if l.display_name not in loader_names] + + for db_handler in handlers_to_delete: + db_handler.delete() + + for dumper in validated_data["annotationdumper_set"]: + models.AnnotationDumper(annotation_format=instance, **dumper).save() + for loader in validated_data["annotationloader_set"]: + models.AnnotationLoader(annotation_format=instance, **loader).save() + + instance.save() + return instance + +class AnnotationFileSerializer(serializers.Serializer): + annotation_file = serializers.FileField() diff --git a/cvat/apps/annotation/settings.py b/cvat/apps/annotation/settings.py new file mode 100644 index 00000000..0ac2a38c --- /dev/null +++ b/cvat/apps/annotation/settings.py @@ -0,0 +1,15 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +import os + +path_prefix = os.path.join('cvat', 'apps', 'annotation') +BUILTIN_FORMATS = ( + os.path.join(path_prefix, 'cvat.py'), + os.path.join(path_prefix, 'pascal_voc.py'), + os.path.join(path_prefix, 'yolo.py'), + os.path.join(path_prefix, 'coco.py'), + os.path.join(path_prefix, 'mask.py'), + os.path.join(path_prefix, 'tfrecord.py'), +) diff --git a/cvat/apps/annotation/tests.py b/cvat/apps/annotation/tests.py new file mode 100644 index 00000000..b66dde17 --- /dev/null +++ b/cvat/apps/annotation/tests.py @@ -0,0 +1,3 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT diff --git a/cvat/apps/annotation/tfrecord.py b/cvat/apps/annotation/tfrecord.py new file mode 100644 index 00000000..a911625e --- /dev/null +++ b/cvat/apps/annotation/tfrecord.py @@ -0,0 +1,182 @@ +# Copyright (C) 2019 Intel Corporation +# +# SPDX-License-Identifier: MIT + +format_spec = { + "name": "TFRecord", + "dumpers": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "dump" + }, + ], + "loaders": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "load" + }, + ], +} + +def dump(file_object, annotations): + import tensorflow as tf + import os + import string + from zipfile import ZipFile + import codecs + from tempfile import TemporaryDirectory + from collections import OrderedDict + + # we need it to filter out non-ASCII characters otherwise + # trainning will crash + printable = set(string.printable) + + def int64_feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) + + def int64_list_feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) + + def bytes_feature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) + + def bytes_list_feature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) + + def float_list_feature(value): + return tf.train.Feature(float_list=tf.train.FloatList(value=value)) + + # Defining the main conversion function + def create_tf_example(img_id, img_size, image_name, boxes, label_ids): + # Process one image data per run + height = img_size[0] + width = img_size[1] + + xmins = [] # List of normalized left x coordinates in bounding box (1 per box) + xmaxs = [] # List of normalized right x coordinates in bounding box + # (1 per box) + ymins = [] # List of normalized top y coordinates in bounding box (1 per box) + ymaxs = [] # List of normalized bottom y coordinates in bounding box + # (1 per box) + classes_text = [] # List of string class name of bounding box (1 per box) + classes = [] # List of integer class id of bounding box (1 per box) + + # Loop oer the boxes and fill the above fields + for box in boxes: + # filter out non-ASCII characters + box_name = ''.join(filter(lambda x: x in printable, box.label)) + + xmins.append(box.points[0] / width) + xmaxs.append(box.points[2] / width) + ymins.append(box.points[1] / height) + ymaxs.append(box.points[3] / height) + classes_text.append(box_name.encode('utf8')) + classes.append(label_ids[box.label]) + + tf_example = tf.train.Example(features=tf.train.Features(feature={ + 'image/height': int64_feature(height), + 'image/width': int64_feature(width), + 'image/filename': bytes_feature(image_name.encode('utf8')), + 'image/source_id': int64_feature(img_id), + 'image/object/bbox/xmin': float_list_feature(xmins), + 'image/object/bbox/xmax': float_list_feature(xmaxs), + 'image/object/bbox/ymin': float_list_feature(ymins), + 'image/object/bbox/ymax': float_list_feature(ymaxs), + 'image/object/class/text': bytes_list_feature(classes_text), + 'image/object/class/label': int64_list_feature(classes), + })) + return tf_example + + # Create the label map file + label_ids = OrderedDict((label[1]["name"], idx) for idx, label in enumerate(annotations.meta["task"]["labels"])) + with TemporaryDirectory() as out_dir: + labelmap_file = 'label_map.pbtxt' + with codecs.open(os.path.join(out_dir, labelmap_file), 'w', encoding='utf8') as f: + for label, idx in label_ids.items(): + f.write(u'item {\n') + f.write(u'\tid: {}\n'.format(idx)) + f.write(u"\tname: '{}'\n".format(label)) + f.write(u'}\n\n') + + annotation_file = '{}.tfrecord'.format(annotations.meta['task']['name']) + with tf.io.TFRecordWriter(os.path.join(out_dir, annotation_file)) as writer: + for frame_annotation in annotations.group_by_frame(): + boxes = [shape for shape in frame_annotation.labeled_shapes if shape.type == 'rectangle'] + if not boxes: + continue + tf_example = create_tf_example( + img_id=frame_annotation.frame, + img_size=(frame_annotation.height, frame_annotation.width), + image_name=frame_annotation.name, + boxes=boxes, + label_ids=label_ids, + ) + writer.write(tf_example.SerializeToString()) + + with ZipFile(file_object, 'w') as output_zip: + output_zip.write(filename=os.path.join(out_dir, labelmap_file), arcname=labelmap_file) + output_zip.write(filename=os.path.join(out_dir, annotation_file), arcname=annotation_file) + +def load(file_object, annotations): + from pyunpack import Archive + from tempfile import TemporaryDirectory + import os + import tensorflow as tf + from glob import glob + import numpy as np + + tf.enable_eager_execution() + + def parse_tfrecord_file(filenames): + def clamp(value, _min, _max): + return max(min(_max, value), _min) + + dataset = tf.data.TFRecordDataset(filenames) + image_feature_description = { + 'image/filename': tf.io.FixedLenFeature([], tf.string), + 'image/source_id': tf.io.FixedLenFeature([], tf.int64), + 'image/height': tf.io.FixedLenFeature([], tf.int64), + 'image/width': tf.io.FixedLenFeature([], tf.int64), + # Object boxes and classes. + 'image/object/bbox/xmin': tf.io.VarLenFeature(tf.float32), + 'image/object/bbox/xmax': tf.io.VarLenFeature(tf.float32), + 'image/object/bbox/ymin': tf.io.VarLenFeature(tf.float32), + 'image/object/bbox/ymax': tf.io.VarLenFeature(tf.float32), + 'image/object/class/label': tf.io.VarLenFeature(tf.int64), + 'image/object/class/text': tf.io.VarLenFeature(tf.string), + } + + for record in dataset: + parsed_record = tf.io.parse_single_example(record, image_feature_description) + frame_number = tf.cast(parsed_record['image/source_id'], tf.int64).numpy().item() + frame_height = tf.cast(parsed_record['image/height'], tf.int64).numpy().item() + frame_width = tf.cast(parsed_record['image/width'], tf.int64).numpy().item() + xmins = tf.sparse.to_dense(parsed_record['image/object/bbox/xmin']).numpy() + ymins = tf.sparse.to_dense(parsed_record['image/object/bbox/ymin']).numpy() + xmaxs = tf.sparse.to_dense(parsed_record['image/object/bbox/xmax']).numpy() + ymaxs = tf.sparse.to_dense(parsed_record['image/object/bbox/ymax']).numpy() + labels = tf.sparse.to_dense(parsed_record['image/object/class/text'], default_value='').numpy() + for shape in np.dstack((labels, xmins, ymins, xmaxs, ymaxs))[0]: + annotations.add_shape(annotations.LabeledShape( + type='rectangle', + frame=frame_number, + label=shape[0].decode("utf-8"), + points=[ + clamp(shape[1] * frame_width, 0, frame_width), + clamp(shape[2] * frame_height, 0, frame_height), + clamp(shape[3] * frame_width, 0, frame_width), + clamp(shape[4] * frame_height, 0, frame_height), + ], + occluded=False, + attributes=[], + )) + + archive_file = getattr(file_object, 'name') + with TemporaryDirectory() as tmp_dir: + Archive(archive_file).extractall(tmp_dir) + filenames = glob(os.path.join(tmp_dir, '*.tfrecord')) + parse_tfrecord_file(filenames) diff --git a/cvat/apps/annotation/views.py b/cvat/apps/annotation/views.py new file mode 100644 index 00000000..b66dde17 --- /dev/null +++ b/cvat/apps/annotation/views.py @@ -0,0 +1,3 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT diff --git a/cvat/apps/annotation/yolo.py b/cvat/apps/annotation/yolo.py new file mode 100644 index 00000000..51da4983 --- /dev/null +++ b/cvat/apps/annotation/yolo.py @@ -0,0 +1,140 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +format_spec = { + "name": "YOLO", + "dumpers": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "dump" + }, + ], + "loaders": [ + { + "display_name": "{name} {format} {version}", + "format": "ZIP", + "version": "1.0", + "handler": "load" + }, + ], +} + +def get_filename(path): + import os + return os.path.splitext(os.path.basename(path))[0] + +def load(file_object, annotations): + from pyunpack import Archive + import os + from tempfile import TemporaryDirectory + from glob import glob + + def convert_from_yolo(img_size, box): + # convertation formulas are based on https://github.com/pjreddie/darknet/blob/master/scripts/voc_label.py + # - float values relative to width and height of image + # - are center of rectangle + def clamp(value, _min, _max): + return max(min(_max, value), _min) + xtl = clamp(img_size[0] * (box[0] - box[2] / 2), 0, img_size[0]) + ytl = clamp(img_size[1] * (box[1] - box[3] / 2), 0, img_size[1]) + xbr = clamp(img_size[0] * (box[0] + box[2] / 2), 0, img_size[0]) + ybr = clamp(img_size[1] * (box[1] + box[3] / 2), 0, img_size[1]) + + return [xtl, ytl, xbr, ybr] + + def parse_yolo_obj(img_size, obj): + label_id, x, y, w, h = obj.split(" ") + return int(label_id), convert_from_yolo(img_size, (float(x), float(y), float(w), float(h))) + + def match_frame(frame_info, filename): + import re + # try to match by filename + yolo_filename = get_filename(filename) + for frame_number, info in frame_info.items(): + cvat_filename = get_filename(info["path"]) + if cvat_filename == yolo_filename: + return frame_number + + # try to extract frame number from filename + numbers = re.findall(r"\d+", filename) + if numbers and len(numbers) == 1: + return int(numbers[0]) + + raise Exception("Cannot match filename or determinate framenumber for {} filename".format(filename)) + + def parse_yolo_file(annotation_file, labels_mapping): + frame_number = match_frame(annotations.frame_info, annotation_file) + with open(annotation_file, "r") as fp: + line = fp.readline() + while line: + frame_info = annotations.frame_info[frame_number] + label_id, points = parse_yolo_obj((frame_info["width"], frame_info["height"]), line) + annotations.add_shape(annotations.LabeledShape( + type="rectangle", + frame=frame_number, + label=labels_mapping[label_id], + points=points, + occluded=False, + attributes=[], + )) + line = fp.readline() + + def load_labels(labels_file): + with open(labels_file, "r") as f: + return {idx: label.strip() for idx, label in enumerate(f.readlines()) if label.strip()} + + archive_file = file_object if isinstance(file_object, str) else getattr(file_object, "name") + with TemporaryDirectory() as tmp_dir: + Archive(archive_file).extractall(tmp_dir) + + labels_file = glob(os.path.join(tmp_dir, "*.names")) + if not labels_file: + raise Exception("Could not find '*.names' file with labels in uploaded archive") + elif len(labels_file) == 1: + labels_mapping = load_labels(labels_file[0]) + else: + raise Exception("Too many '*.names' files in uploaded archive: {}".format(labels_file)) + + for dirpath, _, filenames in os.walk(tmp_dir): + for file in filenames: + if ".txt" == os.path.splitext(file)[1]: + parse_yolo_file(os.path.join(dirpath, file), labels_mapping) + +def dump(file_object, annotations): + from zipfile import ZipFile + + # convertation formulas are based on https://github.com/pjreddie/darknet/blob/master/scripts/voc_label.py + # - float values relative to width and height of image + # - are center of rectangle + def convert_to_yolo(img_size, box): + x = (box[0] + box[2]) / 2 / img_size[0] + y = (box[1] + box[3]) / 2 / img_size[1] + w = (box[2] - box[0]) / img_size[0] + h = (box[3] - box[1]) / img_size[1] + + return x, y, w, h + + labels_ids = {label[1]["name"]: idx for idx, label in enumerate(annotations.meta["task"]["labels"])} + + with ZipFile(file_object, "w") as output_zip: + for frame_annotation in annotations.group_by_frame(): + image_name = frame_annotation.name + annotation_name = "{}.txt".format(get_filename(image_name)) + width = frame_annotation.width + height = frame_annotation.height + + yolo_annotation = "" + for shape in frame_annotation.labeled_shapes: + if shape.type != "rectangle": + continue + + label = shape.label + yolo_bb = convert_to_yolo((width, height), shape.points) + yolo_bb = " ".join("{:.6f}".format(p) for p in yolo_bb) + yolo_annotation += "{} {}\n".format(labels_ids[label], yolo_bb) + + output_zip.writestr(annotation_name, yolo_annotation) + output_zip.writestr("obj.names", "\n".join(l[0] for l in sorted(labels_ids.items(), key=lambda x:x[1]))) diff --git a/cvat/apps/authentication/api_urls.py b/cvat/apps/authentication/api_urls.py new file mode 100644 index 00000000..a3752ad5 --- /dev/null +++ b/cvat/apps/authentication/api_urls.py @@ -0,0 +1,28 @@ +# Copyright (C) 2018 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from django.urls import path +from django.conf import settings +from rest_auth.views import ( + LoginView, LogoutView, PasswordChangeView, + PasswordResetView, PasswordResetConfirmView) +from rest_auth.registration.views import RegisterView +from .views import SigningView + +urlpatterns = [ + path('login', LoginView.as_view(), name='rest_login'), + path('logout', LogoutView.as_view(), name='rest_logout'), + path('signing', SigningView.as_view(), name='signing') +] + +if settings.DJANGO_AUTH_TYPE == 'BASIC': + urlpatterns += [ + path('register', RegisterView.as_view(), name='rest_register'), + path('password/reset', PasswordResetView.as_view(), + name='rest_password_reset'), + path('password/reset/confirm', PasswordResetConfirmView.as_view(), + name='rest_password_reset_confirm'), + path('password/change', PasswordChangeView.as_view(), + name='rest_password_change'), + ] diff --git a/cvat/apps/authentication/auth.py b/cvat/apps/authentication/auth.py index 0bb3296e..da4613bf 100644 --- a/cvat/apps/authentication/auth.py +++ b/cvat/apps/authentication/auth.py @@ -7,7 +7,10 @@ from django.conf import settings from django.db.models import Q import rules from . import AUTH_ROLE +from . import signature from rest_framework.permissions import BasePermission +from django.core import signing +from rest_framework import authentication, exceptions def register_signals(): from django.db.models.signals import post_migrate, post_save @@ -30,6 +33,30 @@ def register_signals(): django_auth_ldap.backend.populate_user.connect(create_user) +class SignatureAuthentication(authentication.BaseAuthentication): + """ + Authentication backend for signed URLs. + """ + def authenticate(self, request): + """ + Returns authenticated user if URL signature is valid. + """ + signer = signature.Signer() + sign = request.query_params.get(signature.QUERY_PARAM) + if not sign: + return + + try: + user = signer.unsign(sign, request.build_absolute_uri()) + except signing.SignatureExpired: + raise exceptions.AuthenticationFailed('This URL has expired.') + except signing.BadSignature: + raise exceptions.AuthenticationFailed('Invalid signature.') + if not user.is_active: + raise exceptions.AuthenticationFailed('User inactive or deleted.') + + return (user, None) + # AUTH PREDICATES has_admin_role = rules.is_group_member(str(AUTH_ROLE.ADMIN)) has_user_role = rules.is_group_member(str(AUTH_ROLE.USER)) diff --git a/cvat/apps/authentication/serializers.py b/cvat/apps/authentication/serializers.py new file mode 100644 index 00000000..1269b8fc --- /dev/null +++ b/cvat/apps/authentication/serializers.py @@ -0,0 +1,16 @@ +from rest_auth.registration.serializers import RegisterSerializer +from rest_framework import serializers + + +class RegisterSerializerEx(RegisterSerializer): + first_name = serializers.CharField(required=False) + last_name = serializers.CharField(required=False) + + def get_cleaned_data(self): + data = super().get_cleaned_data() + data.update({ + 'first_name': self.validated_data.get('first_name', ''), + 'last_name': self.validated_data.get('last_name', ''), + }) + + return data diff --git a/cvat/apps/authentication/signature.py b/cvat/apps/authentication/signature.py new file mode 100644 index 00000000..fdcb0403 --- /dev/null +++ b/cvat/apps/authentication/signature.py @@ -0,0 +1,44 @@ +from django.contrib.auth import get_user_model +from django.core import signing +from furl import furl +import hashlib + +QUERY_PARAM = 'sign' +MAX_AGE = 30 + +# Got implementation ideas in https://github.com/marcgibbons/drf_signed_auth +class Signer: + @classmethod + def get_salt(cls, url): + normalized_url = furl(url).remove(QUERY_PARAM).url.encode('utf-8') + salt = hashlib.sha256(normalized_url).hexdigest() + return salt + + def sign(self, user, url): + """ + Create a signature for a user object. + """ + data = { + 'user_id': user.pk, + 'username': user.get_username() + } + + return signing.dumps(data, salt=self.get_salt(url)) + + def unsign(self, signature, url): + """ + Return a user object for a valid signature. + """ + User = get_user_model() + data = signing.loads(signature, salt=self.get_salt(url), max_age=MAX_AGE) + + if not isinstance(data, dict): + raise signing.BadSignature() + + try: + return User.objects.get(**{ + 'pk': data.get('user_id'), + User.USERNAME_FIELD: data.get('username') + }) + except User.DoesNotExist: + raise signing.BadSignature() diff --git a/cvat/apps/authentication/views.py b/cvat/apps/authentication/views.py index c8effb07..1e1bbb10 100644 --- a/cvat/apps/authentication/views.py +++ b/cvat/apps/authentication/views.py @@ -1,4 +1,3 @@ - # Copyright (C) 2018 Intel Corporation # # SPDX-License-Identifier: MIT @@ -6,9 +5,13 @@ from django.shortcuts import render, redirect from django.conf import settings from django.contrib.auth import login, authenticate +from rest_framework import views +from rest_framework.exceptions import ValidationError +from rest_framework.response import Response +from furl import furl from . import forms - +from . import signature def register_user(request): if request.method == 'POST': @@ -23,3 +26,16 @@ def register_user(request): else: form = forms.NewUserForm() return render(request, 'register.html', {'form': form}) + +class SigningView(views.APIView): + def post(self, request): + url = request.data.get('url') + if not url: + raise ValidationError('Please provide `url` parameter') + + signer = signature.Signer() + url = self.request.build_absolute_uri(url) + sign = signer.sign(self.request.user, url) + + url = furl(url).add({signature.QUERY_PARAM: sign}).url + return Response(url) diff --git a/cvat/apps/auto_annotation/admin.py b/cvat/apps/auto_annotation/admin.py index a59acdef..da1eabb4 100644 --- a/cvat/apps/auto_annotation/admin.py +++ b/cvat/apps/auto_annotation/admin.py @@ -2,3 +2,14 @@ # Copyright (C) 2018 Intel Corporation # # SPDX-License-Identifier: MIT + +from django.contrib import admin +from .models import AnnotationModel + +@admin.register(AnnotationModel) +class AnnotationModelAdmin(admin.ModelAdmin): + list_display = ('name', 'owner', 'created_date', 'updated_date', + 'shared', 'primary', 'framework') + + def has_add_permission(self, request): + return False diff --git a/cvat/apps/auto_annotation/inference.py b/cvat/apps/auto_annotation/inference.py new file mode 100644 index 00000000..8d3442e6 --- /dev/null +++ b/cvat/apps/auto_annotation/inference.py @@ -0,0 +1,141 @@ +from .model_loader import ModelLoader +from cvat.apps.engine.utils import import_modules, execute_python_code +import itertools + +def _process_detections(detections, path_to_conv_script, restricted=True): + results = Results() + local_vars = { + "detections": detections, + "results": results, + } + source_code = open(path_to_conv_script).read() + + if restricted: + global_vars = { + "__builtins__": { + "str": str, + "int": int, + "float": float, + "max": max, + "min": min, + "range": range, + }, + } + else: + global_vars = globals() + imports = import_modules(source_code) + global_vars.update(imports) + + + execute_python_code(source_code, global_vars, local_vars) + + return results + +class Results(): + def __init__(self): + self._results = { + "shapes": [], + "tracks": [] + } + + def add_box(self, xtl, ytl, xbr, ybr, label, frame_number, attributes=None): + self.get_shapes().append({ + "label": label, + "frame": frame_number, + "points": [xtl, ytl, xbr, ybr], + "type": "rectangle", + "attributes": attributes or {}, + }) + + def add_points(self, points, label, frame_number, attributes=None): + points = self._create_polyshape(points, label, frame_number, attributes) + points["type"] = "points" + self.get_shapes().append(points) + + def add_polygon(self, points, label, frame_number, attributes=None): + polygon = self._create_polyshape(points, label, frame_number, attributes) + polygon["type"] = "polygon" + self.get_shapes().append(polygon) + + def add_polyline(self, points, label, frame_number, attributes=None): + polyline = self._create_polyshape(points, label, frame_number, attributes) + polyline["type"] = "polyline" + self.get_shapes().append(polyline) + + def get_shapes(self): + return self._results["shapes"] + + def get_tracks(self): + return self._results["tracks"] + + @staticmethod + def _create_polyshape(points, label, frame_number, attributes=None): + return { + "label": label, + "frame": frame_number, + "points": list(itertools.chain.from_iterable(points)), + "attributes": attributes or {}, + } + +def run_inference_engine_annotation(data, model_file, weights_file, + labels_mapping, attribute_spec, convertation_file, job=None, update_progress=None, restricted=True): + def process_attributes(shape_attributes, label_attr_spec): + attributes = [] + for attr_text, attr_value in shape_attributes.items(): + if attr_text in label_attr_spec: + attributes.append({ + "spec_id": label_attr_spec[attr_text], + "value": attr_value, + }) + + return attributes + + def add_shapes(shapes, target_container): + for shape in shapes: + if shape["label"] not in labels_mapping: + continue + db_label = labels_mapping[shape["label"]] + label_attr_spec = attribute_spec.get(db_label) + target_container.append({ + "label_id": db_label, + "frame": shape["frame"], + "points": shape["points"], + "type": shape["type"], + "z_order": 0, + "group": None, + "occluded": False, + "attributes": process_attributes(shape["attributes"], label_attr_spec), + }) + + result = { + "shapes": [], + "tracks": [], + "tags": [], + "version": 0 + } + + data_len = len(data) + model = ModelLoader(model=model_file, weights=weights_file) + + frame_counter = 0 + + detections = [] + for frame in data: + orig_rows, orig_cols = frame.shape[:2] + + detections.append({ + "frame_id": frame_counter, + "frame_height": orig_rows, + "frame_width": orig_cols, + "detections": model.infer(frame), + }) + + frame_counter += 1 + if job and update_progress and not update_progress(job, frame_counter * 100 / data_len): + return None + + processed_detections = _process_detections(detections, convertation_file, restricted=restricted) + + add_shapes(processed_detections.get_shapes(), result["shapes"]) + + return result diff --git a/cvat/apps/auto_annotation/inference_engine.py b/cvat/apps/auto_annotation/inference_engine.py index 6961511b..94c977fa 100644 --- a/cvat/apps/auto_annotation/inference_engine.py +++ b/cvat/apps/auto_annotation/inference_engine.py @@ -6,6 +6,7 @@ from openvino.inference_engine import IENetwork, IEPlugin import subprocess import os +import platform _IE_PLUGINS_PATH = os.getenv("IE_PLUGINS_PATH", None) @@ -27,6 +28,8 @@ def make_plugin(): plugin.add_cpu_extension(os.path.join(_IE_PLUGINS_PATH, 'libcpu_extension_avx2.so')) elif (_check_instruction('sse4')): plugin.add_cpu_extension(os.path.join(_IE_PLUGINS_PATH, 'libcpu_extension_sse4.so')) + elif platform.system() == 'Darwin': + plugin.add_cpu_extension(os.path.join(_IE_PLUGINS_PATH, 'libcpu_extension.dylib')) else: raise Exception('Inference engine requires a support of avx2 or sse4.') diff --git a/cvat/apps/auto_annotation/model_loader.py b/cvat/apps/auto_annotation/model_loader.py index ee83714f..73d33d81 100644 --- a/cvat/apps/auto_annotation/model_loader.py +++ b/cvat/apps/auto_annotation/model_loader.py @@ -7,6 +7,7 @@ import json import cv2 import os import subprocess +import numpy as np from cvat.apps.auto_annotation.inference_engine import make_plugin, make_network @@ -28,9 +29,18 @@ class ModelLoader(): raise Exception("Following layers are not supported by the plugin for specified device {}:\n {}". format(plugin.device, ", ".join(not_supported_layers))) - self._input_blob_name = next(iter(network.inputs)) + iter_inputs = iter(network.inputs) + self._input_blob_name = next(iter_inputs) self._output_blob_name = next(iter(network.outputs)) + self._require_image_info = False + + # NOTE: handeling for the inclusion of `image_info` in OpenVino2019 + if 'image_info' in network.inputs: + self._require_image_info = True + if self._input_blob_name == 'image_info': + self._input_blob_name = next(iter_inputs) + self._net = plugin.load(network=network, num_requests=2) input_type = network.inputs[self._input_blob_name] self._input_layout = input_type if isinstance(input_type, list) else input_type.shape @@ -39,9 +49,22 @@ class ModelLoader(): _, _, h, w = self._input_layout in_frame = image if image.shape[:-1] == (h, w) else cv2.resize(image, (w, h)) in_frame = in_frame.transpose((2, 0, 1)) # Change data layout from HWC to CHW - return self._net.infer(inputs={self._input_blob_name: in_frame})[self._output_blob_name].copy() + inputs = {self._input_blob_name: in_frame} + if self._require_image_info: + info = np.zeros([1, 3]) + info[0, 0] = h + info[0, 1] = w + # frame number + info[0, 2] = 1 + inputs['image_info'] = info + + results = self._net.infer(inputs) + if len(results) == 1: + return results[self._output_blob_name].copy() + else: + return results.copy() -def load_label_map(labels_path): - with open(labels_path, "r") as f: - return json.load(f)["label_map"] +def load_labelmap(labels_path): + with open(labels_path, "r") as f: + return json.load(f)["label_map"] diff --git a/cvat/apps/auto_annotation/model_manager.py b/cvat/apps/auto_annotation/model_manager.py index a72909b6..1569f690 100644 --- a/cvat/apps/auto_annotation/model_manager.py +++ b/cvat/apps/auto_annotation/model_manager.py @@ -9,6 +9,7 @@ import os import rq import shutil import tempfile +import itertools from django.db import transaction from django.utils import timezone @@ -16,19 +17,23 @@ from django.conf import settings from cvat.apps.engine.log import slogger from cvat.apps.engine.models import Task as TaskModel +from cvat.apps.authentication.auth import has_admin_role from cvat.apps.engine.serializers import LabeledDataSerializer from cvat.apps.engine.annotation import put_task_data, patch_task_data from .models import AnnotationModel, FrameworkChoice -from .model_loader import ModelLoader +from .model_loader import ModelLoader, load_labelmap from .image_loader import ImageLoader +from .inference import run_inference_engine_annotation + + def _remove_old_file(model_file_field): if model_file_field and os.path.exists(model_file_field.name): os.remove(model_file_field.name) def _update_dl_model_thread(dl_model_id, name, is_shared, model_file, weights_file, labelmap_file, - interpretation_file, run_tests, is_local_storage, delete_if_test_fails): + interpretation_file, run_tests, is_local_storage, delete_if_test_fails, restricted=True): def _get_file_content(filename): return os.path.basename(filename), open(filename, "rb") @@ -40,13 +45,15 @@ def _update_dl_model_thread(dl_model_id, name, is_shared, model_file, weights_fi def _run_test(model_file, weights_file, labelmap_file, interpretation_file): test_image = np.ones((1024, 1980, 3), np.uint8) * 255 try: - _run_inference_engine_annotation( + dummy_labelmap = {key: key for key in load_labelmap(labelmap_file).keys()} + run_inference_engine_annotation( data=[test_image,], model_file=model_file, weights_file=weights_file, - labels_mapping=labelmap_file, + labels_mapping=dummy_labelmap, attribute_spec={}, convertation_file=interpretation_file, + restricted=restricted ) except Exception as e: return False, str(e) @@ -150,6 +157,11 @@ def create_or_update(dl_model_id, name, model_file, weights_file, labelmap_file, labelmap_file = save_file_as_tmp(labelmap_file) interpretation_file = save_file_as_tmp(interpretation_file) + if owner: + restricted = not has_admin_role(owner) + else: + restricted = not has_admin_role(AnnotationModel.objects.get(pk=dl_model_id).owner) + rq_id = "auto_annotation.create.{}".format(dl_model_id) queue = django_rq.get_queue("default") queue.enqueue_call( @@ -165,6 +177,7 @@ def create_or_update(dl_model_id, name, model_file, weights_file, labelmap_file, run_tests, storage == "local", is_create_request, + restricted ), job_id=rq_id ) @@ -209,136 +222,8 @@ def get_image_data(path_to_data): image_list.sort(key=get_image_key) return ImageLoader(image_list) -class Results(): - def __init__(self): - self._results = { - "shapes": [], - "tracks": [] - } - - def add_box(self, xtl, ytl, xbr, ybr, label, frame_number, attributes=None): - self.get_shapes().append({ - "label": label, - "frame": frame_number, - "points": [xtl, ytl, xbr, ybr], - "type": "rectangle", - "attributes": attributes or {}, - }) - - def add_points(self, points, label, frame_number, attributes=None): - points = self._create_polyshape(points, label, frame_number, attributes) - points["type"] = "points" - self.get_shapes().append(points) - - def add_polygon(self, points, label, frame_number, attributes=None): - polygon = self._create_polyshape(points, label, frame_number, attributes) - polygon["type"] = "polygon" - self.get_shapes().append(polygon) - - def add_polyline(self, points, label, frame_number, attributes=None): - polyline = self._create_polyshape(points, label, frame_number, attributes) - polyline["type"] = "polyline" - self.get_shapes().append(polyline) - - def get_shapes(self): - return self._results["shapes"] - - def get_tracks(self): - return self._results["tracks"] - - @staticmethod - def _create_polyshape(self, points, label, frame_number, attributes=None): - return { - "label": label, - "frame": frame_number, - "points": " ".join("{},{}".format(pair[0], pair[1]) for pair in points), - "attributes": attributes or {}, - } - -def _process_detections(detections, path_to_conv_script): - results = Results() - global_vars = { - "__builtins__": { - "str": str, - "int": int, - "float": float, - "max": max, - "min": min, - "range": range, - }, - } - local_vars = { - "detections": detections, - "results": results, - } - exec (open(path_to_conv_script).read(), global_vars, local_vars) - return results - -def _run_inference_engine_annotation(data, model_file, weights_file, - labels_mapping, attribute_spec, convertation_file, job=None, update_progress=None): - def process_attributes(shape_attributes, label_attr_spec): - attributes = [] - for attr_text, attr_value in shape_attributes.items(): - if attr_text in label_attr_spec: - attributes.append({ - "id": label_attr_spec[attr_text], - "value": attr_value, - }) - - return attributes - - def add_shapes(shapes, target_container): - for shape in shapes: - if shape["label"] not in labels_mapping: - continue - db_label = labels_mapping[shape["label"]] - - target_container.append({ - "label_id": db_label, - "frame": shape["frame"], - "points": shape["points"], - "type": shape["type"], - "z_order": 0, - "group": None, - "occluded": False, - "attributes": process_attributes(shape["attributes"], attribute_spec[db_label]), - }) - - result = { - "shapes": [], - "tracks": [], - "tags": [], - "version": 0 - } - - data_len = len(data) - model = ModelLoader(model=model_file, weights=weights_file) - - frame_counter = 0 - - detections = [] - for frame in data: - orig_rows, orig_cols = frame.shape[:2] - - detections.append({ - "frame_id": frame_counter, - "frame_height": orig_rows, - "frame_width": orig_cols, - "detections": model.infer(frame), - }) - - frame_counter += 1 - if job and update_progress and not update_progress(job, frame_counter * 100 / data_len): - return None - - processed_detections = _process_detections(detections, convertation_file) - - add_shapes(processed_detections.get_shapes(), result["shapes"]) - - - return result -def run_inference_thread(tid, model_file, weights_file, labels_mapping, attributes, convertation_file, reset, user): +def run_inference_thread(tid, model_file, weights_file, labels_mapping, attributes, convertation_file, reset, user, restricted=True): def update_progress(job, progress): job.refresh() if "cancel" in job.meta: @@ -357,7 +242,7 @@ def run_inference_thread(tid, model_file, weights_file, labels_mapping, attribut result = None slogger.glob.info("auto annotation with openvino toolkit for task {}".format(tid)) - result = _run_inference_engine_annotation( + result = run_inference_engine_annotation( data=get_image_data(db_task.get_data_dirname()), model_file=model_file, weights_file=weights_file, @@ -366,6 +251,7 @@ def run_inference_thread(tid, model_file, weights_file, labels_mapping, attribut convertation_file= convertation_file, job=job, update_progress=update_progress, + restricted=restricted ) if result is None: diff --git a/cvat/apps/auto_annotation/models.py b/cvat/apps/auto_annotation/models.py index 72513b10..467997e0 100644 --- a/cvat/apps/auto_annotation/models.py +++ b/cvat/apps/auto_annotation/models.py @@ -3,6 +3,7 @@ # # SPDX-License-Identifier: MIT +import os from enum import Enum from django.db import models @@ -13,7 +14,7 @@ from django.core.files.storage import FileSystemStorage fs = FileSystemStorage() def upload_path_handler(instance, filename): - return "{models_root}/{id}/{file}".format(models_root=settings.MODELS_ROOT, id=instance.id, file=filename) + return os.path.join(settings.MODELS_ROOT, str(instance.id), filename) class FrameworkChoice(Enum): OPENVINO = 'openvino' diff --git a/cvat/apps/auto_annotation/static/auto_annotation/js/dashboardPlugin.js b/cvat/apps/auto_annotation/static/auto_annotation/js/dashboardPlugin.js index dd0e7b57..f04018f1 100644 --- a/cvat/apps/auto_annotation/static/auto_annotation/js/dashboardPlugin.js +++ b/cvat/apps/auto_annotation/static/auto_annotation/js/dashboardPlugin.js @@ -10,7 +10,7 @@ userConfirm */ -window.cvat = window.cvat || {}; +window.cvatUI = window.cvatUI || {}; const AutoAnnotationServer = { start(modelId, taskId, data, success, error, progress, check) { @@ -136,8 +136,8 @@ const AutoAnnotationServer = { class AutoAnnotationModelManagerView { constructor() { - const html = `
  • '), + $itemContent = $(''), + itemText = this.options[type] ? this.makeText(this.options[type], page) : page; + + $itemContainer.addClass(this.options[type + 'Class']); + $itemContainer.data('page', page); + $itemContainer.data('page-type', type); + $itemContainer.append($itemContent.attr('href', this.makeHref(page)).addClass(this.options.anchorClass).html(itemText)); + + return $itemContainer; + }, + + getPages: function (currentPage) { + var pages = []; + + var half = Math.floor(this.options.visiblePages / 2); + var start = currentPage - half + 1 - this.options.visiblePages % 2; + var end = currentPage + half; + + var visiblePages = this.options.visiblePages; + if (visiblePages > this.options.totalPages) { + visiblePages = this.options.totalPages; + } + + // handle boundary case + if (start <= 0) { + start = 1; + end = visiblePages; + } + if (end > this.options.totalPages) { + start = this.options.totalPages - visiblePages + 1; + end = this.options.totalPages; + } + + var itPage = start; + while (itPage <= end) { + pages.push(itPage); + itPage++; + } + + return {"currentPage": currentPage, "numeric": pages}; + }, + + render: function (pages) { + var _this = this; + this.$listContainer.children().remove(); + var items = this.buildListItems(pages); + $.each(items, function(key, item){ + _this.$listContainer.append(item); + }); + + this.$listContainer.children().each(function () { + var $this = $(this), + pageType = $this.data('page-type'); + + switch (pageType) { + case 'page': + if ($this.data('page') === pages.currentPage) { + $this.addClass(_this.options.activeClass); + } + break; + case 'first': + $this.toggleClass(_this.options.disabledClass, pages.currentPage === 1); + break; + case 'last': + $this.toggleClass(_this.options.disabledClass, pages.currentPage === _this.options.totalPages); + break; + case 'prev': + $this.toggleClass(_this.options.disabledClass, !_this.options.loop && pages.currentPage === 1); + break; + case 'next': + $this.toggleClass(_this.options.disabledClass, + !_this.options.loop && pages.currentPage === _this.options.totalPages); + break; + default: + break; + } + + }); + }, + + setupEvents: function () { + var _this = this; + this.$listContainer.off('click').on('click', 'li', function (evt) { + var $this = $(this); + if ($this.hasClass(_this.options.disabledClass) || $this.hasClass(_this.options.activeClass)) { + return false; + } + // Prevent click event if href is not set. + !_this.options.href && evt.preventDefault(); + _this.show(parseInt($this.data('page'))); + }); + }, + + changeTotalPages: function(totalPages, currentPage) { + this.options.totalPages = totalPages; + return this.show(currentPage); + }, + + makeHref: function (page) { + return this.options.href ? this.generateQueryString(page) : "#"; + }, + + makeText: function (text, page) { + return text.replace(this.options.pageVariable, page) + .replace(this.options.totalPagesVariable, this.options.totalPages) + }, + + getPageFromQueryString: function (searchStr) { + var search = this.getSearchString(searchStr), + regex = new RegExp(this.options.pageVariable + '(=([^&#]*)|&|#|$)'), + page = regex.exec(search); + if (!page || !page[2]) { + return null; + } + page = decodeURIComponent(page[2]); + page = parseInt(page); + if (isNaN(page)) { + return null; + } + return page; + }, + + generateQueryString: function (pageNumber, searchStr) { + var search = this.getSearchString(searchStr), + regex = new RegExp(this.options.pageVariable + '=*[^&#]*'); + if (!search) return ''; + return '?' + search.replace(regex, this.options.pageVariable + '=' + pageNumber); + }, + + getSearchString: function (searchStr) { + var search = searchStr || window.location.search; + if (search === '') { + return null; + } + if (search.indexOf('?') === 0) search = search.substr(1); + return search; + }, + + getCurrentPage: function () { + return this.currentPage; + }, + + getTotalPages: function () { + return this.options.totalPages; + } + }; + + // PLUGIN DEFINITION + + $.fn.twbsPagination = function (option) { + var args = Array.prototype.slice.call(arguments, 1); + var methodReturn; + + var $this = $(this); + var data = $this.data('twbs-pagination'); + var options = typeof option === 'object' ? option : {}; + + if (!data) $this.data('twbs-pagination', (data = new TwbsPagination(this, options) )); + if (typeof option === 'string') methodReturn = data[ option ].apply(data, args); + + return ( methodReturn === undefined ) ? $this : methodReturn; + }; + + $.fn.twbsPagination.defaults = { + totalPages: 1, + startPage: 1, + visiblePages: 5, + initiateStartPageClick: true, + hideOnlyOnePage: false, + href: false, + pageVariable: '{{page}}', + totalPagesVariable: '{{total_pages}}', + page: null, + first: 'First', + prev: 'Previous', + next: 'Next', + last: 'Last', + loop: false, + beforePageClick: null, + onPageClick: null, + paginationClass: 'pagination', + nextClass: 'page-item next', + prevClass: 'page-item prev', + lastClass: 'page-item last', + firstClass: 'page-item first', + pageClass: 'page-item', + activeClass: 'active', + disabledClass: 'disabled', + anchorClass: 'page-link' + }; + + $.fn.twbsPagination.Constructor = TwbsPagination; + + $.fn.twbsPagination.noConflict = function () { + $.fn.twbsPagination = old; + return this; + }; + + $.fn.twbsPagination.version = "1.4.2"; + +})(window.jQuery, window, document); diff --git a/cvat/apps/dashboard/static/dashboard/js/3rdparty/pagination/pagination.css b/cvat/apps/dashboard/static/dashboard/js/3rdparty/pagination/pagination.css deleted file mode 100644 index 117bf0ba..00000000 --- a/cvat/apps/dashboard/static/dashboard/js/3rdparty/pagination/pagination.css +++ /dev/null @@ -1 +0,0 @@ -.paginationjs{line-height:1.6;font-family:Marmelad,"Lucida Grande",Arial,"Hiragino Sans GB",Georgia,sans-serif;font-size:14px;box-sizing:initial}.paginationjs:after{display:table;content:" ";clear:both}.paginationjs .paginationjs-pages{float:left}.paginationjs .paginationjs-pages ul{float:left;margin:0;padding:0}.paginationjs .paginationjs-go-button,.paginationjs .paginationjs-go-input,.paginationjs .paginationjs-nav{float:left;margin-left:10px;font-size:14px}.paginationjs .paginationjs-pages li{float:left;border:1px solid #aaa;border-right:none;list-style:none}.paginationjs .paginationjs-pages li>a{min-width:30px;height:28px;line-height:28px;display:block;background:#fff;font-size:14px;color:#333;text-decoration:none;text-align:center}.paginationjs .paginationjs-pages li>a:hover{background:#eee}.paginationjs .paginationjs-pages li.active{border:none}.paginationjs .paginationjs-pages li.active>a{height:30px;line-height:30px;background:#aaa;color:#fff}.paginationjs .paginationjs-pages li.disabled>a{opacity:.3}.paginationjs .paginationjs-pages li.disabled>a:hover{background:0 0}.paginationjs .paginationjs-pages li:first-child,.paginationjs .paginationjs-pages li:first-child>a{border-radius:3px 0 0 3px}.paginationjs .paginationjs-pages li:last-child{border-right:1px solid #aaa;border-radius:0 3px 3px 0}.paginationjs .paginationjs-pages li:last-child>a{border-radius:0 3px 3px 0}.paginationjs .paginationjs-go-input>input[type=text]{width:30px;height:28px;background:#fff;border-radius:3px;border:1px solid #aaa;padding:0;font-size:14px;text-align:center;vertical-align:baseline;outline:0;box-shadow:none;box-sizing:initial}.paginationjs .paginationjs-go-button>input[type=button]{min-width:40px;height:30px;line-height:28px;background:#fff;border-radius:3px;border:1px solid #aaa;text-align:center;padding:0 8px;font-size:14px;vertical-align:baseline;outline:0;box-shadow:none;color:#333;cursor:pointer;vertical-align:middle\9}.paginationjs.paginationjs-theme-blue .paginationjs-go-input>input[type=text],.paginationjs.paginationjs-theme-blue .paginationjs-pages li{border-color:#289de9}.paginationjs .paginationjs-go-button>input[type=button]:hover{background-color:#f8f8f8}.paginationjs .paginationjs-nav{height:30px;line-height:30px}.paginationjs .paginationjs-go-button,.paginationjs .paginationjs-go-input{margin-left:5px\9}.paginationjs.paginationjs-small{font-size:12px}.paginationjs.paginationjs-small .paginationjs-pages li>a{min-width:26px;height:24px;line-height:24px;font-size:12px}.paginationjs.paginationjs-small .paginationjs-pages li.active>a{height:26px;line-height:26px}.paginationjs.paginationjs-small .paginationjs-go-input{font-size:12px}.paginationjs.paginationjs-small .paginationjs-go-input>input[type=text]{width:26px;height:24px;font-size:12px}.paginationjs.paginationjs-small .paginationjs-go-button{font-size:12px}.paginationjs.paginationjs-small .paginationjs-go-button>input[type=button]{min-width:30px;height:26px;line-height:24px;padding:0 6px;font-size:12px}.paginationjs.paginationjs-small .paginationjs-nav{height:26px;line-height:26px;font-size:12px}.paginationjs.paginationjs-big{font-size:16px}.paginationjs.paginationjs-big .paginationjs-pages li>a{min-width:36px;height:34px;line-height:34px;font-size:16px}.paginationjs.paginationjs-big .paginationjs-pages li.active>a{height:36px;line-height:36px}.paginationjs.paginationjs-big .paginationjs-go-input{font-size:16px}.paginationjs.paginationjs-big .paginationjs-go-input>input[type=text]{width:36px;height:34px;font-size:16px}.paginationjs.paginationjs-big .paginationjs-go-button{font-size:16px}.paginationjs.paginationjs-big .paginationjs-go-button>input[type=button]{min-width:50px;height:36px;line-height:34px;padding:0 12px;font-size:16px}.paginationjs.paginationjs-big .paginationjs-nav{height:36px;line-height:36px;font-size:16px}.paginationjs.paginationjs-theme-blue .paginationjs-pages li>a{color:#289de9}.paginationjs.paginationjs-theme-blue .paginationjs-pages li>a:hover{background:#e9f4fc}.paginationjs.paginationjs-theme-blue .paginationjs-pages li.active>a{background:#289de9;color:#fff}.paginationjs.paginationjs-theme-blue .paginationjs-pages li.disabled>a:hover{background:0 0}.paginationjs.paginationjs-theme-blue .paginationjs-go-button>input[type=button]{background:#289de9;border-color:#289de9;color:#fff}.paginationjs.paginationjs-theme-green .paginationjs-go-input>input[type=text],.paginationjs.paginationjs-theme-green .paginationjs-pages li{border-color:#449d44}.paginationjs.paginationjs-theme-blue .paginationjs-go-button>input[type=button]:hover{background-color:#3ca5ea}.paginationjs.paginationjs-theme-green .paginationjs-pages li>a{color:#449d44}.paginationjs.paginationjs-theme-green .paginationjs-pages li>a:hover{background:#ebf4eb}.paginationjs.paginationjs-theme-green .paginationjs-pages li.active>a{background:#449d44;color:#fff}.paginationjs.paginationjs-theme-green .paginationjs-pages li.disabled>a:hover{background:0 0}.paginationjs.paginationjs-theme-green .paginationjs-go-button>input[type=button]{background:#449d44;border-color:#449d44;color:#fff}.paginationjs.paginationjs-theme-yellow .paginationjs-go-input>input[type=text],.paginationjs.paginationjs-theme-yellow .paginationjs-pages li{border-color:#ec971f}.paginationjs.paginationjs-theme-green .paginationjs-go-button>input[type=button]:hover{background-color:#55a555}.paginationjs.paginationjs-theme-yellow .paginationjs-pages li>a{color:#ec971f}.paginationjs.paginationjs-theme-yellow .paginationjs-pages li>a:hover{background:#fdf5e9}.paginationjs.paginationjs-theme-yellow .paginationjs-pages li.active>a{background:#ec971f;color:#fff}.paginationjs.paginationjs-theme-yellow .paginationjs-pages li.disabled>a:hover{background:0 0}.paginationjs.paginationjs-theme-yellow .paginationjs-go-button>input[type=button]{background:#ec971f;border-color:#ec971f;color:#fff}.paginationjs.paginationjs-theme-red .paginationjs-go-input>input[type=text],.paginationjs.paginationjs-theme-red .paginationjs-pages li{border-color:#c9302c}.paginationjs.paginationjs-theme-yellow .paginationjs-go-button>input[type=button]:hover{background-color:#eea135}.paginationjs.paginationjs-theme-red .paginationjs-pages li>a{color:#c9302c}.paginationjs.paginationjs-theme-red .paginationjs-pages li>a:hover{background:#faeaea}.paginationjs.paginationjs-theme-red .paginationjs-pages li.active>a{background:#c9302c;color:#fff}.paginationjs.paginationjs-theme-red .paginationjs-pages li.disabled>a:hover{background:0 0}.paginationjs.paginationjs-theme-red .paginationjs-go-button>input[type=button]{background:#c9302c;border-color:#c9302c;color:#fff}.paginationjs.paginationjs-theme-red .paginationjs-go-button>input[type=button]:hover{background-color:#ce4541}.paginationjs .paginationjs-pages li.paginationjs-next{border-right:1px solid #aaa\9}.paginationjs .paginationjs-go-input>input[type=text]{line-height:28px\9;vertical-align:middle\9}.paginationjs.paginationjs-big .paginationjs-pages li>a{line-height:36px\9}.paginationjs.paginationjs-big .paginationjs-go-input>input[type=text]{height:36px\9;line-height:36px\9} \ No newline at end of file diff --git a/cvat/apps/dashboard/static/dashboard/js/3rdparty/pagination/pagination.min.js b/cvat/apps/dashboard/static/dashboard/js/3rdparty/pagination/pagination.min.js deleted file mode 100644 index a8472b59..00000000 --- a/cvat/apps/dashboard/static/dashboard/js/3rdparty/pagination/pagination.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * pagination.js 2.1.4 - * A jQuery plugin to provide simple yet fully customisable pagination - * https://github.com/superRaytin/paginationjs - - * Homepage: http://pagination.js.org - * - * Copyright 2014-2100, superRaytin - * Released under the MIT license. -*/ -!function(a,b){function c(a){throw new Error("Pagination: "+a)}function d(a){a.dataSource||c('"dataSource" is required.'),"string"==typeof a.dataSource?void 0===a.totalNumberLocator?void 0===a.totalNumber?c('"totalNumber" is required.'):b.isNumeric(a.totalNumber)||c('"totalNumber" is incorrect. (Number)'):b.isFunction(a.totalNumberLocator)||c('"totalNumberLocator" should be a Function.'):i.isObject(a.dataSource)&&(void 0===a.locator?c('"dataSource" is an Object, please specify "locator".'):"string"==typeof a.locator||b.isFunction(a.locator)||c(a.locator+" is incorrect. (String | Function)")),void 0===a.formatResult||b.isFunction(a.formatResult)||c('"formatResult" should be a Function.')}function e(a){var c=["go","previous","next","disable","enable","refresh","show","hide","destroy"];b.each(c,function(b,c){a.off(h+c)}),a.data("pagination",{}),b(".paginationjs",a).remove()}function f(a,b){return("object"==(b=typeof a)?null==a&&"null"||Object.prototype.toString.call(a).slice(8,-1):b).toLowerCase()}void 0===b&&c("Pagination requires jQuery.");var g="pagination",h="__pagination-";b.fn.pagination&&(g="pagination2"),b.fn[g]=function(f){if(void 0===f)return this;var j=b(this),k=b.extend({},b.fn[g].defaults,f),l={initialize:function(){var a=this;if(j.data("pagination")||j.data("pagination",{}),!1!==a.callHook("beforeInit")){j.data("pagination").initialized&&b(".paginationjs",j).remove(),a.disabled=!!k.disabled;var c=a.model={pageRange:k.pageRange,pageSize:k.pageSize};a.parseDataSource(k.dataSource,function(b){if(a.isAsync=i.isString(b),i.isArray(b)&&(c.totalNumber=k.totalNumber=b.length),a.isDynamicTotalNumber=a.isAsync&&k.totalNumberLocator,!(k.hideWhenLessThanOnePage&&a.getTotalPage()<=1)){var d=a.render(!0);k.className&&d.addClass(k.className),c.el=d,j["bottom"===k.position?"append":"prepend"](d),a.observer(),j.data("pagination").initialized=!0,a.callHook("afterInit",d)}})}},render:function(a){var c=this,d=c.model,e=d.el||b('
    '),f=!0!==a;c.callHook("beforeRender",f);var g=d.pageNumber||k.pageNumber,h=k.pageRange,i=c.getTotalPage(),j=g-h,l=g+h;return l>i&&(l=i,j=i-2*h,j=j<1?1:j),j<=1&&(j=1,l=Math.min(2*h+1,i)),e.html(c.generateHTML({currentPage:g,pageRange:h,rangeStart:j,rangeEnd:l})),c.callHook("afterRender",f),e},generateHTML:function(a){var c,d,e=this,f=a.currentPage,g=e.getTotalPage(),h=a.rangeStart,i=a.rangeEnd,j=e.getTotalNumber(),l=k.showPrevious,m=k.showNext,n=k.showPageNumbers,o=k.showNavigator,p=k.showGoInput,q=k.showGoButton,r=k.pageLink,s=k.prevText,t=k.nextText,u=k.ellipsisText,v=k.goButtonText,w=k.classPrefix,x=k.activeClassName,y=k.disableClassName,z=k.ulClassName,A="",B='',C='',D=b.isFunction(k.formatNavigator)?k.formatNavigator(f,g,j):k.formatNavigator,E=b.isFunction(k.formatGoInput)?k.formatGoInput(B,f,g,j):k.formatGoInput,F=b.isFunction(k.formatGoButton)?k.formatGoButton(C,f,g,j):k.formatGoButton,G=b.isFunction(k.autoHidePrevious)?k.autoHidePrevious():k.autoHidePrevious,H=b.isFunction(k.autoHideNext)?k.autoHideNext():k.autoHideNext,I=b.isFunction(k.header)?k.header(f,g,j):k.header,J=b.isFunction(k.footer)?k.footer(f,g,j):k.footer;if(I&&(c=e.replaceVariables(I,{currentPage:f,totalPage:g,totalNumber:j}),A+=c),l||n||m){if(A+='
    ',A+=z?'
      ':"
    "}return o&&D&&(c=e.replaceVariables(D,{currentPage:f,totalPage:g,totalNumber:j}),A+='
    '+c+"
    "),p&&E&&(c=e.replaceVariables(E,{currentPage:f,totalPage:g,totalNumber:j,input:B}),A+='
    '+c+"
    "),q&&F&&(c=e.replaceVariables(F,{currentPage:f,totalPage:g,totalNumber:j,button:C}),A+='
    '+c+"
    "),J&&(c=e.replaceVariables(J,{currentPage:f,totalPage:g,totalNumber:j}),A+=c),A},findTotalNumberFromRemoteResponse:function(a){this.model.totalNumber=k.totalNumberLocator(a)},go:function(a,c){function d(a){if(!1===e.callHook("beforePaging",g))return!1;if(f.direction=void 0===f.pageNumber?0:g>f.pageNumber?1:-1,f.pageNumber=g,e.render(),e.disabled&&e.isAsync&&e.enable(),j.data("pagination").model=f,k.formatResult){var d=b.extend(!0,[],a);i.isArray(a=k.formatResult(d))||(a=d)}j.data("pagination").currentPageData=a,e.doCallback(a,c),e.callHook("afterPaging",g),1==g&&e.callHook("afterIsFirstPage"),g==e.getTotalPage()&&e.callHook("afterIsLastPage")}var e=this,f=e.model;if(!e.disabled){var g=a;if((g=parseInt(g))&&!(g<1)){var h=k.pageSize,l=e.getTotalNumber(),m=e.getTotalPage();if(!(l>0&&g>m)){if(!e.isAsync)return void d(e.getDataFragment(g));var n={},o=k.alias||{};n[o.pageSize?o.pageSize:"pageSize"]=h,n[o.pageNumber?o.pageNumber:"pageNumber"]=g;var p=b.isFunction(k.ajax)?k.ajax():k.ajax,q={type:"get",cache:!1,data:{},contentType:"application/x-www-form-urlencoded; charset=UTF-8",dataType:"json",async:!0};b.extend(!0,q,p),b.extend(q.data,n),q.url=k.dataSource,q.success=function(a){e.isDynamicTotalNumber?e.findTotalNumberFromRemoteResponse(a):e.model.totalNumber=k.totalNumber,d(e.filterDataByLocator(a))},q.error=function(a,b,c){k.formatAjaxError&&k.formatAjaxError(a,b,c),e.enable()},e.disable(),b.ajax(q)}}}},doCallback:function(a,c){var d=this,e=d.model;b.isFunction(c)?c(a,e):b.isFunction(k.callback)&&k.callback(a,e)},destroy:function(){!1!==this.callHook("beforeDestroy")&&(this.model.el.remove(),j.off(),b("#paginationjs-style").remove(),this.callHook("afterDestroy"))},previous:function(a){this.go(this.model.pageNumber-1,a)},next:function(a){this.go(this.model.pageNumber+1,a)},disable:function(){var a=this,b=a.isAsync?"async":"sync";!1!==a.callHook("beforeDisable",b)&&(a.disabled=!0,a.model.disabled=!0,a.callHook("afterDisable",b))},enable:function(){var a=this,b=a.isAsync?"async":"sync";!1!==a.callHook("beforeEnable",b)&&(a.disabled=!1,a.model.disabled=!1,a.callHook("afterEnable",b))},refresh:function(a){this.go(this.model.pageNumber,a)},show:function(){var a=this;a.model.el.is(":visible")||a.model.el.show()},hide:function(){var a=this;a.model.el.is(":visible")&&a.model.el.hide()},replaceVariables:function(a,b){var c;for(var d in b){var e=b[d],f=new RegExp("<%=\\s*"+d+"\\s*%>","img");c=(c||a).replace(f,e)}return c},getDataFragment:function(a){var b=k.pageSize,c=k.dataSource,d=this.getTotalNumber(),e=b*(a-1)+1,f=Math.min(a*b,d);return c.slice(e-1,f)},getTotalNumber:function(){return this.model.totalNumber||k.totalNumber||0},getTotalPage:function(){return Math.ceil(this.getTotalNumber()/k.pageSize)},getLocator:function(a){var d;return"string"==typeof a?d=a:b.isFunction(a)?d=a():c('"locator" is incorrect. (String | Function)'),d},filterDataByLocator:function(a){var d,e=this.getLocator(k.locator);if(i.isObject(a)){try{b.each(e.split("."),function(b,c){d=(d||a)[c]})}catch(a){}d?i.isArray(d)||c("dataSource."+e+" must be an Array."):c("dataSource."+e+" is undefined.")}return d||a},parseDataSource:function(a,d){var e=this;i.isObject(a)?d(k.dataSource=e.filterDataByLocator(a)):i.isArray(a)?d(k.dataSource=a):b.isFunction(a)?k.dataSource(function(a){i.isArray(a)||c('The parameter of "done" Function should be an Array.'),e.parseDataSource.call(e,a,d)}):"string"==typeof a?(/^https?|file:/.test(a)&&(k.ajaxDataType="jsonp"),d(a)):c('Unexpected type of "dataSource".')},callHook:function(c){var d,e=j.data("pagination"),f=Array.prototype.slice.apply(arguments);return f.shift(),k[c]&&b.isFunction(k[c])&&!1===k[c].apply(a,f)&&(d=!1),e.hooks&&e.hooks[c]&&b.each(e.hooks[c],function(b,c){!1===c.apply(a,f)&&(d=!1)}),!1!==d},observer:function(){var a=this,d=a.model.el;j.on(h+"go",function(d,e,f){(e=parseInt(b.trim(e)))&&(b.isNumeric(e)||c('"pageNumber" is incorrect. (Number)'),a.go(e,f))}),d.delegate(".J-paginationjs-page","click",function(c){var d=b(c.currentTarget),e=b.trim(d.attr("data-num"));if(e&&!d.hasClass(k.disableClassName)&&!d.hasClass(k.activeClassName))return!1!==a.callHook("beforePageOnClick",c,e)&&(a.go(e),a.callHook("afterPageOnClick",c,e),!!k.pageLink&&void 0)}),d.delegate(".J-paginationjs-previous","click",function(c){var d=b(c.currentTarget),e=b.trim(d.attr("data-num"));if(e&&!d.hasClass(k.disableClassName))return!1!==a.callHook("beforePreviousOnClick",c,e)&&(a.go(e),a.callHook("afterPreviousOnClick",c,e),!!k.pageLink&&void 0)}),d.delegate(".J-paginationjs-next","click",function(c){var d=b(c.currentTarget),e=b.trim(d.attr("data-num"));if(e&&!d.hasClass(k.disableClassName))return!1!==a.callHook("beforeNextOnClick",c,e)&&(a.go(e),a.callHook("afterNextOnClick",c,e),!!k.pageLink&&void 0)}),d.delegate(".J-paginationjs-go-button","click",function(c){var e=b(".J-paginationjs-go-pagenumber",d).val();if(!1===a.callHook("beforeGoButtonOnClick",c,e))return!1;j.trigger(h+"go",e),a.callHook("afterGoButtonOnClick",c,e)}),d.delegate(".J-paginationjs-go-pagenumber","keyup",function(c){if(13===c.which){var e=b(c.currentTarget).val();if(!1===a.callHook("beforeGoInputOnEnter",c,e))return!1;j.trigger(h+"go",e),b(".J-paginationjs-go-pagenumber",d).focus(),a.callHook("afterGoInputOnEnter",c,e)}}),j.on(h+"previous",function(b,c){a.previous(c)}),j.on(h+"next",function(b,c){a.next(c)}),j.on(h+"disable",function(){a.disable()}),j.on(h+"enable",function(){a.enable()}),j.on(h+"refresh",function(b,c){a.refresh(c)}),j.on(h+"show",function(){a.show()}),j.on(h+"hide",function(){a.hide()}),j.on(h+"destroy",function(){a.destroy()});var e=Math.max(a.getTotalPage(),1),f=k.pageNumber;a.isDynamicTotalNumber&&(f=1),k.triggerPagingOnInit&&j.trigger(h+"go",Math.min(f,e))}};if(j.data("pagination")&&!0===j.data("pagination").initialized){if(b.isNumeric(f))return j.trigger.call(this,h+"go",f,arguments[1]),this;if("string"==typeof f){var m=Array.prototype.slice.apply(arguments);switch(m[0]=h+m[0],f){case"previous":case"next":case"go":case"disable":case"enable":case"refresh":case"show":case"hide":case"destroy":j.trigger.apply(this,m);break;case"getSelectedPageNum":return j.data("pagination").model?j.data("pagination").model.pageNumber:j.data("pagination").attributes.pageNumber;case"getTotalPage":return Math.ceil(j.data("pagination").model.totalNumber/j.data("pagination").model.pageSize);case"getSelectedPageData":return j.data("pagination").currentPageData;case"isDisabled":return!0===j.data("pagination").model.disabled;default:c("Unknown action: "+f)}return this}e(j)}else i.isObject(f)||c("Illegal options");return d(k),l.initialize(),this},b.fn[g].defaults={totalNumber:0,pageNumber:1,pageSize:10,pageRange:2,showPrevious:!0,showNext:!0,showPageNumbers:!0,showNavigator:!1,showGoInput:!1,showGoButton:!1,pageLink:"",prevText:"«",nextText:"»",ellipsisText:"...",goButtonText:"Go",classPrefix:"paginationjs",activeClassName:"active",disableClassName:"disabled",inlineStyle:!0,formatNavigator:"<%= currentPage %> / <%= totalPage %>",formatGoInput:"<%= input %>",formatGoButton:"<%= button %>",position:"bottom",autoHidePrevious:!1,autoHideNext:!1,triggerPagingOnInit:!0,hideWhenLessThanOnePage:!1,showFirstOnEllipsisShow:!0,showLastOnEllipsisShow:!0,callback:function(){}},b.fn.addHook=function(a,d){arguments.length<2&&c("Missing argument."),b.isFunction(d)||c("callback must be a function.");var e=b(this),f=e.data("pagination");f||(e.data("pagination",{}),f=e.data("pagination")),!f.hooks&&(f.hooks={}),f.hooks[a]=f.hooks[a]||[],f.hooks[a].push(d)},b[g]=function(a,d){arguments.length<2&&c("Requires two parameters.");var e;if(e="string"!=typeof a&&a instanceof jQuery?a:b(a),e.length)return e.pagination(d),e};var i={};b.each(["Object","Array","String"],function(a,b){i["is"+b]=function(a){return f(a)===b.toLowerCase()}}),"function"==typeof define&&define.amd&&define(function(){return b})}(this,window.jQuery); \ No newline at end of file diff --git a/cvat/apps/dashboard/static/dashboard/js/dashboard.js b/cvat/apps/dashboard/static/dashboard/js/dashboard.js index 8bc302b6..8ebf05ea 100644 --- a/cvat/apps/dashboard/static/dashboard/js/dashboard.js +++ b/cvat/apps/dashboard/static/dashboard/js/dashboard.js @@ -5,24 +5,18 @@ */ /* global - AnnotationParser:false userConfirm:false - createExportContainer:false - dumpAnnotationRequest: false - dumpAnnotation:false LabelsInfo:false showMessage:false showOverlay:false + isDefaultFormat:false */ -'use strict'; - class TaskView { - constructor(details, ondelete, onupdate) { - this.init(details); + constructor(task, annotationFormats) { + this.init(task); + this._annotationFormats = annotationFormats; - this._ondelete = ondelete; - this._onupdate = onupdate; this._UI = null; } @@ -31,193 +25,83 @@ class TaskView { this._UI.find('.dashboardJobList').empty(); } - _remove() { - $.ajax ({ - url: `/api/v1/tasks/${this._id}`, - type: 'DELETE', - success: () => { - this._ondelete(this._id); - this._disable(); - }, - error: (errorData) => { - const message = `Can not build CVAT dashboard. Code: ${errorData.status}. ` + - `Message: ${errorData.responseText || errorData.statusText}`; - showMessage(message); + async _remove() { + try { + await this._task.delete(); + this._disable(); + } catch (exception) { + let { message } = exception; + if (exception instanceof window.cvat.exceptions.ServerError) { + message += ` Code: ${exception.code}`; } - }); + showMessage(message); + } } _update() { $('#dashboardUpdateModal').remove(); + const dashboardUpdateModal = $($('#dashboardUpdateTemplate').html()).appendTo('body'); - $('#dashboardOldLabels').prop('value', LabelsInfo.serialize(this._labels)); + // TODO: Use JSON labels format instead of custom + $('#dashboardOldLabels').prop('value', LabelsInfo.serialize(this._task.labels.map(el => el.toJSON()))); $('#dashboardCancelUpdate').on('click', () => { dashboardUpdateModal.remove(); }); - - $('#dashboardSubmitUpdate').on('click', () => { + $('#dashboardSubmitUpdate').on('click', async () => { + let jsonLabels = null; try { - const body = { - labels: LabelsInfo.deserialize($('#dashboardNewLabels').prop('value')) - }; - $.ajax({ - url: `/api/v1/tasks/${this._id}`, - type: 'PATCH', - contentType: 'application/json', - data: JSON.stringify(body) - }).done(() => { - this._onupdate(); - dashboardUpdateModal.remove(); - showMessage('Task has been successfully updated'); - }).fail((errorData) => { - const message = `Can not build CVAT dashboard. Code: ${errorData.status}. ` + - `Message: ${errorData.responseText || errorData.statusText}`; - showMessage(message); - }); + jsonLabels = LabelsInfo.deserialize($('#dashboardNewLabels').prop('value')); } catch (exception) { showMessage(exception); + return; } - }); - } - _upload() { - async function saveChunk(parsed) { - const CHUNK_SIZE = 30000; - - class Chunk { - constructor() { - this.shapes = []; - this.tracks = []; - this.tags = []; - this.capasity = CHUNK_SIZE; - this.version = 0; - } - - length() { - return this.tags.length + - this.shapes.length + - this.tracks.reduce((sum, track) => sum + track.shapes.length, 0); - } - - isFull() { - return this.length() >= this.capasity; + try { + const labels = jsonLabels.map(label => new window.cvat.classes.Label(label)); + this._task.labels = labels; + await this._task.save(); + showMessage('Task has been successfully updated'); + } catch (exception) { + let { message } = exception; + if (exception instanceof window.cvat.exceptions.ServerError) { + message += ` Code: ${exception.code}`; } + showMessage(message); + } - isEmpty() { - return this.length() === 0; - } + dashboardUpdateModal.remove(); + }); + } - clear() { - this.shapes = []; - this.tracks = []; - this.tags = []; - } - export() { - return { - shapes: this.shapes, - tracks: this.tracks, - tags: this.tags, - version: this.version, - }; - } - async save(taskID) { + _upload(uploadAnnotationButton, format) { + const button = $(uploadAnnotationButton); + $(``) + .on('change', async (onChangeEvent) => { + const file = onChangeEvent.target.files[0]; + $(onChangeEvent.target).remove(); + if (file) { + button.prop('disabled', true); try { - const response = await $.ajax({ - url: `/api/v1/tasks/${taskID}/annotations?action=create`, - type: 'PATCH', - data: JSON.stringify(chunk.export()), - contentType: 'application/json', - }); - this.version = response.version; - this.clear(); + await this._task.annotations.upload(file, format); } catch (error) { - throw error; - } - } - } - - const splitAndSave = async (chunk, prop, splitStep) => { - for(let start = 0; start < parsed[prop].length; start += splitStep) { - Array.prototype.push.apply(chunk[prop], parsed[prop].slice(start, start + splitStep)); - if (chunk.isFull()) { - await chunk.save(this._id); + showMessage(error.message); + } finally { + button.prop('disabled', false); } } - // save tail - if (!chunk.isEmpty()) { - await chunk.save(this._id); - } - }; - - let chunk = new Chunk(); - // FIXME tags aren't supported by parser - // await split(chunk, "tags", CHUNK_SIZE); - await splitAndSave(chunk, "shapes", CHUNK_SIZE); - await splitAndSave(chunk, "tracks", 1); - } - - async function save(parsed) { - await $.ajax({ - url: `/api/v1/tasks/${this._id}/annotations`, - type: 'DELETE', - }); - - await saveChunk.call(this, parsed); - } - - async function onload(overlay, text) { - try { - overlay.setMessage('Required data are being downloaded from the server..'); - const imageCache = await $.get(`/api/v1/tasks/${this._id}/frames/meta`); - const labelsCopy = JSON.parse(JSON.stringify(this._labels)); - const parser = new AnnotationParser({ - start: 0, - stop: this._size, - flipped: this._flipped, - image_meta_data: imageCache, - }, new LabelsInfo(labelsCopy)); - - overlay.setMessage('The annotation file is being parsed..'); - const parsed = parser.parse(text); - - overlay.setMessage('The annotation is being saved..'); - await save.call(this, parsed); - - const message = 'Annotation have been successfully uploaded'; - showMessage(message); - } catch(errorData) { - let message = null; - if (typeof(errorData) === 'string') { - message = `Can not upload annotations. ${errorData}`; - } else { - message = `Can not upload annotations. Code: ${errorData.status}. ` + - `Message: ${errorData.responseText || errorData.statusText}`; - } - showMessage(message); - } finally { - overlay.remove(); - } - } - - $('').on('change', (e) => { - const file = e.target.files[0]; - $(e.target).remove(); - if (file) { - const overlay = showOverlay('File is being parsed..'); - const fileReader = new FileReader(); - fileReader.onload = (e) => { - onload.call(this, overlay, e.target.result); - } - fileReader.readAsText(file); - } - }).click(); + }).click(); } - async _dump(button) { + async _dump(button, format) { button.disabled = true; try { - await dumpAnnotationRequest(this._id, this._name); + const url = await this._task.annotations.dump(this._task.name, format); + const a = document.createElement('a'); + a.href = `${url}`; + document.body.appendChild(a); + a.click(); + a.remove(); } catch (error) { showMessage(error.message); } finally { @@ -225,59 +109,84 @@ class TaskView { } } - init(details) { - for (let prop in details) { - this[`_${prop}`] = details[prop]; - } + init(task) { + this._task = task; } render(baseURL) { - const self = this; - this._UI = $(`
    `).append( + this._UI = $(`
    `).append( $(`
    - -
    `) + + `), ).append( $(`
    - -
    `) + + `), ).append( $('
    ').css({ - 'background-image': `url("/api/v1/tasks/${this._id}/frames/0")` - }) + 'background-image': `url("/api/v1/tasks/${this._task.id}/frames/0")`, + }), ); + const buttonsContainer = $('
    ').appendTo(this._UI); + const downloadButton = $(''); + $('').appendTo(downloadButton); - const buttonsContainer = $(`
    `).appendTo(this._UI); - $('').on('click', (e) => { - self._dump(e.target); - }).appendTo(buttonsContainer); + const uploadButton = $(''); + $('').appendTo(uploadButton); - $('').on('click', () => { - userConfirm("The current annotation will be lost. Are you sure?", () => self._upload()); - }).appendTo(buttonsContainer); + const dumpers = {}; + const loaders = {}; + + for (const format of this._annotationFormats) { + for (const dumper of format.dumpers) { + dumpers[dumper.name] = dumper; + const item = $(`