diff --git a/.eslintignore b/.eslintignore index bd9ce3b02c..acdf1c19c2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -433,6 +433,9 @@ packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js.map packages/app-desktop/gui/MainScreen/commands/toggleEditors.d.ts packages/app-desktop/gui/MainScreen/commands/toggleEditors.js packages/app-desktop/gui/MainScreen/commands/toggleEditors.js.map +packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.d.ts +packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js +packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map @@ -583,6 +586,9 @@ packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map packages/app-desktop/gui/NoteListItem.d.ts packages/app-desktop/gui/NoteListItem.js packages/app-desktop/gui/NoteListItem.js.map +packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.d.ts +packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js +packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js.map packages/app-desktop/gui/NoteTextViewer.d.ts packages/app-desktop/gui/NoteTextViewer.js packages/app-desktop/gui/NoteTextViewer.js.map @@ -592,15 +598,60 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map packages/app-desktop/gui/OneDriveLoginScreen.d.ts packages/app-desktop/gui/OneDriveLoginScreen.js packages/app-desktop/gui/OneDriveLoginScreen.js.map +packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts +packages/app-desktop/gui/ResizableLayout/MoveButtons.js +packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map packages/app-desktop/gui/ResizableLayout/ResizableLayout.d.ts packages/app-desktop/gui/ResizableLayout/ResizableLayout.js packages/app-desktop/gui/ResizableLayout/ResizableLayout.js.map -packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.d.ts -packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js -packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js.map -packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.d.ts -packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js -packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js.map +packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.d.ts +packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js +packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js.map +packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.d.ts +packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js +packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js.map +packages/app-desktop/gui/ResizableLayout/utils/iterateItems.d.ts +packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js +packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js.map +packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.d.ts +packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js +packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js.map +packages/app-desktop/gui/ResizableLayout/utils/movements.d.ts +packages/app-desktop/gui/ResizableLayout/utils/movements.js +packages/app-desktop/gui/ResizableLayout/utils/movements.js.map +packages/app-desktop/gui/ResizableLayout/utils/movements.test.d.ts +packages/app-desktop/gui/ResizableLayout/utils/movements.test.js +packages/app-desktop/gui/ResizableLayout/utils/movements.test.js.map +packages/app-desktop/gui/ResizableLayout/utils/persist.d.ts +packages/app-desktop/gui/ResizableLayout/utils/persist.js +packages/app-desktop/gui/ResizableLayout/utils/persist.js.map +packages/app-desktop/gui/ResizableLayout/utils/persist.test.d.ts +packages/app-desktop/gui/ResizableLayout/utils/persist.test.js +packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map +packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts +packages/app-desktop/gui/ResizableLayout/utils/removeItem.js +packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map +packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts +packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js +packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map +packages/app-desktop/gui/ResizableLayout/utils/style.d.ts +packages/app-desktop/gui/ResizableLayout/utils/style.js +packages/app-desktop/gui/ResizableLayout/utils/style.js.map +packages/app-desktop/gui/ResizableLayout/utils/types.d.ts +packages/app-desktop/gui/ResizableLayout/utils/types.js +packages/app-desktop/gui/ResizableLayout/utils/types.js.map +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.d.ts +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js.map +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.d.ts +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js.map +packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.d.ts +packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js +packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js.map +packages/app-desktop/gui/ResizableLayout/utils/validateLayout.d.ts +packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js +packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js.map packages/app-desktop/gui/ResourceScreen.d.ts packages/app-desktop/gui/ResourceScreen.js packages/app-desktop/gui/ResourceScreen.js.map @@ -679,6 +730,9 @@ packages/app-desktop/plugins/GotoAnything.js.map packages/app-desktop/services/bridge.d.ts packages/app-desktop/services/bridge.js packages/app-desktop/services/bridge.js.map +packages/app-desktop/services/commands/stateToWhenClauseContext.d.ts +packages/app-desktop/services/commands/stateToWhenClauseContext.js +packages/app-desktop/services/commands/stateToWhenClauseContext.js.map packages/app-desktop/services/commands/types.d.ts packages/app-desktop/services/commands/types.js packages/app-desktop/services/commands/types.js.map diff --git a/.eslintrc.js b/.eslintrc.js index 71b84d8da5..3094918b24 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,6 +25,9 @@ module.exports = { 'afterEach': 'readonly', 'jasmine': 'readonly', + // Jest variables + 'test': 'readonly', + // React Native variables '__DEV__': 'readonly', diff --git a/.gitignore b/.gitignore index 9f2756a45b..cba7af660c 100644 --- a/.gitignore +++ b/.gitignore @@ -425,6 +425,9 @@ packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js.map packages/app-desktop/gui/MainScreen/commands/toggleEditors.d.ts packages/app-desktop/gui/MainScreen/commands/toggleEditors.js packages/app-desktop/gui/MainScreen/commands/toggleEditors.js.map +packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.d.ts +packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js +packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map @@ -575,6 +578,9 @@ packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map packages/app-desktop/gui/NoteListItem.d.ts packages/app-desktop/gui/NoteListItem.js packages/app-desktop/gui/NoteListItem.js.map +packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.d.ts +packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js +packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js.map packages/app-desktop/gui/NoteTextViewer.d.ts packages/app-desktop/gui/NoteTextViewer.js packages/app-desktop/gui/NoteTextViewer.js.map @@ -584,15 +590,60 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map packages/app-desktop/gui/OneDriveLoginScreen.d.ts packages/app-desktop/gui/OneDriveLoginScreen.js packages/app-desktop/gui/OneDriveLoginScreen.js.map +packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts +packages/app-desktop/gui/ResizableLayout/MoveButtons.js +packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map packages/app-desktop/gui/ResizableLayout/ResizableLayout.d.ts packages/app-desktop/gui/ResizableLayout/ResizableLayout.js packages/app-desktop/gui/ResizableLayout/ResizableLayout.js.map -packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.d.ts -packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js -packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js.map -packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.d.ts -packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js -packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js.map +packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.d.ts +packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js +packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js.map +packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.d.ts +packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js +packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js.map +packages/app-desktop/gui/ResizableLayout/utils/iterateItems.d.ts +packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js +packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js.map +packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.d.ts +packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js +packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js.map +packages/app-desktop/gui/ResizableLayout/utils/movements.d.ts +packages/app-desktop/gui/ResizableLayout/utils/movements.js +packages/app-desktop/gui/ResizableLayout/utils/movements.js.map +packages/app-desktop/gui/ResizableLayout/utils/movements.test.d.ts +packages/app-desktop/gui/ResizableLayout/utils/movements.test.js +packages/app-desktop/gui/ResizableLayout/utils/movements.test.js.map +packages/app-desktop/gui/ResizableLayout/utils/persist.d.ts +packages/app-desktop/gui/ResizableLayout/utils/persist.js +packages/app-desktop/gui/ResizableLayout/utils/persist.js.map +packages/app-desktop/gui/ResizableLayout/utils/persist.test.d.ts +packages/app-desktop/gui/ResizableLayout/utils/persist.test.js +packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map +packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts +packages/app-desktop/gui/ResizableLayout/utils/removeItem.js +packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map +packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts +packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js +packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map +packages/app-desktop/gui/ResizableLayout/utils/style.d.ts +packages/app-desktop/gui/ResizableLayout/utils/style.js +packages/app-desktop/gui/ResizableLayout/utils/style.js.map +packages/app-desktop/gui/ResizableLayout/utils/types.d.ts +packages/app-desktop/gui/ResizableLayout/utils/types.js +packages/app-desktop/gui/ResizableLayout/utils/types.js.map +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.d.ts +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js.map +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.d.ts +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js +packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js.map +packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.d.ts +packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js +packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js.map +packages/app-desktop/gui/ResizableLayout/utils/validateLayout.d.ts +packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js +packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js.map packages/app-desktop/gui/ResourceScreen.d.ts packages/app-desktop/gui/ResourceScreen.js packages/app-desktop/gui/ResourceScreen.js.map @@ -671,6 +722,9 @@ packages/app-desktop/plugins/GotoAnything.js.map packages/app-desktop/services/bridge.d.ts packages/app-desktop/services/bridge.js packages/app-desktop/services/bridge.js.map +packages/app-desktop/services/commands/stateToWhenClauseContext.d.ts +packages/app-desktop/services/commands/stateToWhenClauseContext.js +packages/app-desktop/services/commands/stateToWhenClauseContext.js.map packages/app-desktop/services/commands/types.d.ts packages/app-desktop/services/commands/types.js packages/app-desktop/services/commands/types.js.map diff --git a/joplin.code-workspace b/joplin.code-workspace index bf09fcce86..a1724691dc 100644 --- a/joplin.code-workspace +++ b/joplin.code-workspace @@ -8,6 +8,7 @@ "files.exclude": { "lerna-debug.log": true, "_mydocs/mdtest/": true, + "./packages/lib/plugin_types": true, "_releases/": true, "_vieux/": true, ".gitignore": true, diff --git a/packages/app-cli/tests/services_CommandService.ts b/packages/app-cli/tests/services_CommandService.ts index 0453fea14f..b723c38e89 100644 --- a/packages/app-cli/tests/services_CommandService.ts +++ b/packages/app-cli/tests/services_CommandService.ts @@ -1,6 +1,7 @@ import MenuUtils from '@joplin/lib/services/commands/MenuUtils'; import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils'; import CommandService, { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService'; +import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext'; import KeymapService from '@joplin/lib/services/KeymapService'; const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js'); @@ -17,7 +18,7 @@ function newService(): CommandService { return {}; }, }; - service.initialize(mockStore, true); + service.initialize(mockStore, true, stateToWhenClauseContext); return service; } diff --git a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/content_script/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/dialog/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/dialog/src/index.ts b/packages/app-cli/tests/support/plugins/dialog/src/index.ts index b1e6ed4a2f..488803d1a6 100644 --- a/packages/app-cli/tests/support/plugins/dialog/src/index.ts +++ b/packages/app-cli/tests/support/plugins/dialog/src/index.ts @@ -4,12 +4,12 @@ joplin.plugins.register({ onStart: async function() { const dialogs = joplin.views.dialogs; - const handle = await dialogs.create(); + const handle = await dialogs.create('myDialog1'); await dialogs.setHtml(handle, '

Testing dialog with default buttons

Second line

Third line

'); const result = await dialogs.open(handle); alert('Got result: ' + JSON.stringify(result)); - const handle2 = await dialogs.create(); + const handle2 = await dialogs.create('myDialog2'); await dialogs.setHtml(handle2, '

Testing dialog with custom buttons

Second line

Third line

'); await dialogs.setButtons(handle2, [ { diff --git a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/events/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/events/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/json_export/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/menu/api/Global.d.ts b/packages/app-cli/tests/support/plugins/menu/api/Global.d.ts index 011fe0dafd..f6a0078fa2 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/Global.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/Global.d.ts @@ -1,6 +1,6 @@ import Plugin from '../Plugin'; import Joplin from './Joplin'; -import Logger from 'lib/Logger'; +import Logger from '../../../Logger'; /** * @ignore */ diff --git a/packages/app-cli/tests/support/plugins/menu/api/Joplin.d.ts b/packages/app-cli/tests/support/plugins/menu/api/Joplin.d.ts index e12babda23..b6ed5824ce 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/Joplin.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/Joplin.d.ts @@ -7,7 +7,7 @@ import JoplinCommands from './JoplinCommands'; import JoplinViews from './JoplinViews'; import JoplinInterop from './JoplinInterop'; import JoplinSettings from './JoplinSettings'; -import Logger from 'lib/Logger'; +import Logger from '../../../Logger'; /** * This is the main entry point to the Joplin API. You can access various services using the provided accessors. */ diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinCommands.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinCommands.d.ts index 677e6d5b0f..8a9f30451c 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinCommands.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinCommands.d.ts @@ -1,25 +1,35 @@ import { Command } from './types'; /** - * This class allows executing or registering new Joplin commands. Commands can be executed or associated with - * {@link JoplinViewsToolbarButtons | toolbar buttons} or {@link JoplinViewsMenuItems | menu items}. + * This class allows executing or registering new Joplin commands. Commands + * can be executed or associated with + * {@link JoplinViewsToolbarButtons | toolbar buttons} or + * {@link JoplinViewsMenuItems | menu items}. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command) * * ## Executing Joplin's internal commands * - * It is also possible to execute internal Joplin's commands which, as of now, are not well documented. - * You can find the list directly on GitHub though at the following locations: + * It is also possible to execute internal Joplin's commands which, as of + * now, are not well documented. You can find the list directly on GitHub + * though at the following locations: * - * https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/MainScreen/commands - * https://github.com/laurent22/joplin/tree/dev/ElectronClient/commands - * https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.ts + * * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands) + * * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands) + * * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts) * - * To view what arguments are supported, you can open any of these files and look at the `execute()` command. + * To view what arguments are supported, you can open any of these files + * and look at the `execute()` command. */ export default class JoplinCommands { /** - * desktop Executes the given command. - * The `props` are the arguments passed to the command, and they vary based on the command + * desktop Executes the given + * command. + * + * The command can take any number of arguments, and the supported + * arguments will vary based on the command. For custom commands, this + * is the `args` passed to the `execute()` function. For built-in + * commands, you can find the supported arguments by checking the links + * above. * * ```typescript * // Create a new note in the current notebook: @@ -27,10 +37,10 @@ export default class JoplinCommands { * * // Create a new sub-notebook under the provided notebook * // Note: internally, notebooks are called "folders". - * await joplin.commands.execute('newFolder', { parent_id: "SOME_FOLDER_ID" }); + * await joplin.commands.execute('newFolder', "SOME_FOLDER_ID"); * ``` */ - execute(commandName: string, props?: any): Promise; + execute(commandName: string, ...args: any[]): Promise; /** * desktop Registers a new command. * diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinData.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinData.d.ts index 213d576ed8..fbde6ff9a0 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinData.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinData.d.ts @@ -6,7 +6,7 @@ import { Path } from './types'; * * This is also what you would use to search notes, via the `search` endpoint. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/simple) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple) * * In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls. * And each method takes these parameters: diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinInterop.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinInterop.d.ts index 142c01ddfc..8de655c83a 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinInterop.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinInterop.d.ts @@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types'; /** * Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) * * To implement an import or export module, you would simply define an object with various event handlers that are called * by the application during the import/export process. diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinPlugins.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinPlugins.d.ts index aa561f1dac..fc9fda6a36 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinPlugins.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinPlugins.d.ts @@ -1,6 +1,6 @@ import Plugin from '../Plugin'; -import Logger from 'lib/Logger'; -import { Script } from './types'; +import Logger from '../../../Logger'; +import { ContentScriptType, Script } from './types'; /** * This class provides access to plugin-related features. */ @@ -21,4 +21,18 @@ export default class JoplinPlugins { * ``` */ register(script: Script): Promise; + /** + * Registers a new content script. Unlike regular plugin code, which runs in a separate process, content scripts run within the main process code + * and thus allow improved performances and more customisations in specific cases. It can be used for example to load a Markdown or editor plugin. + * + * Note that registering a content script in itself will do nothing - it will only be loaded in specific cases by the relevant app modules + * (eg. the Markdown renderer or the code editor). So it is not a way to inject and run arbitrary code in the app, which for safety and performance reasons is not supported. + * + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script) + * + * @param type Defines how the script will be used. See the type definition for more information about each supported type. + * @param id A unique ID for the content script. + * @param scriptPath Must be a path relative to the plugin main script. For example, if your file content_script.js is next to your index.ts file, you would set `scriptPath` to `"./content_script.js`. + */ + registerContentScript(type: ContentScriptType, id: string, scriptPath: string): Promise; } diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinSettings.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinSettings.d.ts index 4deaae636c..0fa39d9640 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinSettings.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinSettings.d.ts @@ -7,7 +7,7 @@ import { SettingItem, SettingSection } from './types'; * * Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/settings) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings) */ export default class JoplinSettings { private plugin_; @@ -37,7 +37,7 @@ export default class JoplinSettings { * * The list of available settings is not documented yet, but can be found by looking at the source code: * - * https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/ReactNativeClient/lib/models/Setting.ts#L142 + * https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142 */ globalValue(key: string): Promise; } diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsDialogs.d.ts index e76f8a41bc..7a9f8e941f 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsDialogs.d.ts @@ -5,7 +5,7 @@ import { ButtonSpec, ViewHandle, ButtonId } from './types'; * Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return and provide the button ID that was * clicked on. There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/dialog) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog) */ export default class JoplinViewsDialogs { private store; @@ -16,7 +16,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenuItems.d.ts index d2fb8d9949..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenuItems.d.ts @@ -3,7 +3,7 @@ import Plugin from '../Plugin'; /** * Allows creating and managing menu items. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command) */ export default class JoplinViewsMenuItems { private store; @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenus.d.ts index 866486a080..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsMenus.d.ts @@ -3,7 +3,7 @@ import Plugin from '../Plugin'; /** * Allows creating menus. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/menu) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu) */ export default class JoplinViewsMenus { private store; @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsPanels.d.ts index 98458e401d..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsPanels.d.ts @@ -1,10 +1,13 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/toc) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ export default class JoplinViewsPanels { private store; @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsToolbarButtons.d.ts index d937b50b43..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinViewsToolbarButtons.d.ts @@ -3,7 +3,7 @@ import Plugin from '../Plugin'; /** * Allows creating and managing toolbar buttons. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command) */ export default class JoplinViewsToolbarButtons { private store; @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/menu/api/JoplinWorkspace.d.ts b/packages/app-cli/tests/support/plugins/menu/api/JoplinWorkspace.d.ts index 361eb3a675..0686d32a9c 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/JoplinWorkspace.d.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/JoplinWorkspace.d.ts @@ -2,7 +2,7 @@ * The workspace service provides access to all the parts of Joplin that are being worked on - i.e. the currently selected notes or notebooks as well * as various related events, such as when a new note is selected, or when the note content changes. * - * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins) + * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins) */ export default class JoplinWorkspace { private store; diff --git a/packages/app-cli/tests/support/plugins/menu/api/types.ts b/packages/app-cli/tests/support/plugins/menu/api/types.ts index ef16847f68..e4da323694 100644 --- a/packages/app-cli/tests/support/plugins/menu/api/types.ts +++ b/packages/app-cli/tests/support/plugins/menu/api/types.ts @@ -3,12 +3,48 @@ // ================================================================= export interface Command { - name: string - label: string - iconName?: string, - execute(props:any):Promise - isEnabled?(props:any):boolean - mapStateToProps?(state:any):any + /** + * Name of command - must be globally unique + */ + name: string; + + /** + * Label to be displayed on menu items or keyboard shortcut editor for example. + * If it is missing, it's assumed it's a private command, to be called programmatically only. + * In that case the command will not appear in the shortcut editor or command panel, and logically + * should not be used as a menu item. + */ + label?: string; + + /** + * Icon to be used on toolbar buttons for example + */ + iconName?: string; + + /** + * Code to be ran when the command is executed. It may return a result. + */ + execute(...args: any[]): Promise; + + /** + * Defines whether the command should be enabled or disabled, which in turns affects + * the enabled state of any associated button or menu item. + * + * The condition should be expressed as a "when-clause" (as in Visual Studio Code). It's a simple boolean expression that evaluates to + * `true` or `false`. It supports the following operators: + * + * Operator | Symbol | Example + * -- | -- | -- + * Equality | == | "editorType == markdown" + * Inequality | != | "currentScreen != config" + * Or | \|\| | "noteIsTodo \|\| noteTodoCompleted" + * And | && | "oneNoteSelected && !inConflictFolder" + * + * Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/services/commands/stateToWhenClauseContext.ts). + * + * Note: Commands are enabled by default unless you use this property. + */ + enabledCondition?: string; } // ================================================================= @@ -26,7 +62,7 @@ export enum ImportModuleOutputFormat { } /** - * Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export) for an example. + * Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example. * * In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported. * @@ -36,113 +72,113 @@ export interface ExportModule { /** * The format to be exported, eg "enex", "jex", "json", etc. */ - format: string, + format: string; /** * The description that will appear in the UI, for example in the menu item. */ - description: string, + description: string; /** * Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter. */ - target: FileSystemItem, + target: FileSystemItem; /** * Only applies to single file exporters or importers * It tells whether the format can package multiple notes into one file. * For example JEX or ENEX can, but HTML cannot. */ - isNoteArchive: boolean, + isNoteArchive: boolean; /** * The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module. */ - fileExtensions?: string[], + fileExtensions?: string[]; /** * Called when the export process starts. */ - onInit(context:ExportContext): Promise; + onInit(context: ExportContext): Promise; /** * Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc. */ - onProcessItem(context:ExportContext, itemType:number, item:any):Promise; + onProcessItem(context: ExportContext, itemType: number, item: any): Promise; /** * Called when a resource file needs to be exported. */ - onProcessResource(context:ExportContext, resource:any, filePath:string):Promise; + onProcessResource(context: ExportContext, resource: any, filePath: string): Promise; /** * Called when the export process is done. */ - onClose(context:ExportContext):Promise; + onClose(context: ExportContext): Promise; } export interface ImportModule { /** * The format to be exported, eg "enex", "jex", "json", etc. */ - format: string, + format: string; /** * The description that will appear in the UI, for example in the menu item. */ - description: string, + description: string; /** * Only applies to single file exporters or importers * It tells whether the format can package multiple notes into one file. * For example JEX or ENEX can, but HTML cannot. */ - isNoteArchive: boolean, + isNoteArchive: boolean; /** * The type of sources that are supported by the module. Tells whether the module can import files or directories or both. */ - sources: FileSystemItem[], + sources: FileSystemItem[]; /** * Tells the file extensions of the exported files. */ - fileExtensions?: string[], + fileExtensions?: string[]; /** * Tells the type of notes that will be generated, either HTML or Markdown (default). */ - outputFormat?: ImportModuleOutputFormat, + outputFormat?: ImportModuleOutputFormat; /** * Called when the import process starts. There is only one event handler within which you should import the complete data. */ - onExec(context:ImportContext): Promise; + onExec(context: ImportContext): Promise; } export interface ExportOptions { - format?: string, - path?:string, - sourceFolderIds?: string[], - sourceNoteIds?: string[], - modulePath?:string, - target?:FileSystemItem, + format?: string; + path?: string; + sourceFolderIds?: string[]; + sourceNoteIds?: string[]; + modulePath?: string; + target?: FileSystemItem; } export interface ExportContext { - destPath: string, - options: ExportOptions, + destPath: string; + options: ExportOptions; /** * You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next. */ - userData?: any, + userData?: any; } export interface ImportContext { - sourcePath: string, - options: any, - warnings: string[], + sourcePath: string; + options: any; + warnings: string[]; } // ================================================================= @@ -150,7 +186,7 @@ export interface ImportContext { // ================================================================= export interface Script { - onStart?(event:any):Promise, + onStart?(event: any): Promise; } // ================================================================= @@ -158,7 +194,7 @@ export interface Script { // ================================================================= export interface CreateMenuItemOptions { - accelerator: string, + accelerator: string; } export enum MenuItemLocation { @@ -172,10 +208,26 @@ export enum MenuItemLocation { } export interface MenuItem { - commandName?: string, - accelerator?: string, - submenu?: MenuItem[], - label?: string, + /** + * Command that should be associated with the menu item. All menu item should + * have a command associated with them unless they are a sub-menu. + */ + commandName?: string; + + /** + * Accelerator associated with the menu item + */ + accelerator?: string; + + /** + * Menu items that should appear below this menu item. Allows creating a menu tree. + */ + submenu?: MenuItem[]; + + /** + * Menu item label. If not specified, the command label will be used instead. + */ + label?: string; } // ================================================================= @@ -183,9 +235,9 @@ export interface MenuItem { // ================================================================= export interface ButtonSpec { - id: ButtonId, - title?: string, - onClick?():void, + id: ButtonId; + title?: string; + onClick?(): void; } export type ButtonId = string; @@ -225,28 +277,28 @@ export enum SettingItemType { // Redefine a simplified interface to mask internal details // and to remove function calls as they would have to be async. export interface SettingItem { - value: any, - type: SettingItemType, - public: boolean, - label:string, + value: any; + type: SettingItemType; + public: boolean; + label: string; - description?:string, - isEnum?: boolean, - section?: string, - options?:any, - appTypes?:string[], - secure?: boolean, - advanced?: boolean, - minimum?: number, - maximum?: number, - step?: number, + description?: string; + isEnum?: boolean; + section?: string; + options?: any; + appTypes?: string[]; + secure?: boolean; + advanced?: boolean; + minimum?: number; + maximum?: number; + step?: number; } export interface SettingSection { - label: string, - iconName?: string, - description?: string, - name?: string, + label: string; + iconName?: string; + description?: string; + name?: string; } // ================================================================= @@ -261,3 +313,48 @@ export interface SettingSection { * [2]: (Optional) Resource link. */ export type Path = string[]; + +// ================================================================= +// Plugins type +// ================================================================= + +export enum ContentScriptType { + /** + * Registers a new Markdown-It plugin, which should follow the template below. + * + * ```javascript + * module.exports = { + * default: function(context) { + * return { + * plugin: function(markdownIt, options) { + * // ... + * }, + * assets: { + * // ... + * }, + * } + * } + * } + * ``` + * + * - The `context` argument is currently unused but could be used later on to provide access to your own plugin so that the content script and plugin can communicate. + * + * - The **required** `plugin` key is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information. The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml.ts), which contains a number of options, mostly useful for Joplin's internal code. + * + * - Using the **optional** `assets` key you may specify assets such as JS or CSS that should be loaded in the rendered HTML document. Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to see how the data should be structured. + * + * To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific features, you would simply create a file such as this: + * + * ```javascript + * module.exports = { + * default: function(context) { + * return { + * plugin: require('markdown-it-toc-done-right'); + * } + * } + * } + * ``` + */ + MarkdownItPlugin = 'markdownItPlugin', + CodeMirrorPlugin = 'codeMirrorPlugin', +} diff --git a/packages/app-cli/tests/support/plugins/menu/src/index.ts b/packages/app-cli/tests/support/plugins/menu/src/index.ts index f59055909f..4d2e976299 100644 --- a/packages/app-cli/tests/support/plugins/menu/src/index.ts +++ b/packages/app-cli/tests/support/plugins/menu/src/index.ts @@ -2,7 +2,7 @@ import joplin from 'api'; joplin.plugins.register({ onStart: async function() { - await joplin.views.menus.create('My Menu', [ + await joplin.views.menus.create('myMenu', 'My Menu', [ { commandName: "newNote", }, diff --git a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/multi_selection/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/register_command/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/register_command/src/index.ts b/packages/app-cli/tests/support/plugins/register_command/src/index.ts index 37ed5613a3..276081ca5f 100644 --- a/packages/app-cli/tests/support/plugins/register_command/src/index.ts +++ b/packages/app-cli/tests/support/plugins/register_command/src/index.ts @@ -31,14 +31,14 @@ joplin.plugins.register({ }); // Add the first command to the note toolbar - await joplin.views.toolbarButtons.create('testCommand1', ToolbarButtonLocation.NoteToolbar); + await joplin.views.toolbarButtons.create('myButton1', 'testCommand1', ToolbarButtonLocation.NoteToolbar); // Add the second command to the editor toolbar - await joplin.views.toolbarButtons.create('testCommand2', ToolbarButtonLocation.EditorToolbar); + await joplin.views.toolbarButtons.create('myButton2', 'testCommand2', ToolbarButtonLocation.EditorToolbar); // Also add the commands to the menu - await joplin.views.menuItems.create('testCommand1', MenuItemLocation.Tools, { accelerator: 'CmdOrCtrl+Alt+Shift+B' }); - await joplin.views.menuItems.create('testCommand2', MenuItemLocation.Tools); + await joplin.views.menuItems.create('myMenuItem1', 'testCommand1', MenuItemLocation.Tools, { accelerator: 'CmdOrCtrl+Alt+Shift+B' }); + await joplin.views.menuItems.create('myMenuItem2', 'testCommand2', MenuItemLocation.Tools); console.info('Running command with arguments...'); const result = await joplin.commands.execute('commandWithResult', 'abcd', 123); diff --git a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/selected_text/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/settings/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/toc/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/toc/src/index.ts b/packages/app-cli/tests/support/plugins/toc/src/index.ts index ece86933dd..3fd104c962 100644 --- a/packages/app-cli/tests/support/plugins/toc/src/index.ts +++ b/packages/app-cli/tests/support/plugins/toc/src/index.ts @@ -41,7 +41,7 @@ joplin.plugins.register({ onStart: async function() { const panels = joplin.views.panels; - const view = await panels.create(); + const view = await (panels as any).create(); await panels.setHtml(view, 'Loading...'); await panels.addScript(view, './webview.js'); diff --git a/packages/app-cli/tests/support/plugins/updatePlugins.sh b/packages/app-cli/tests/support/plugins/updatePlugins.sh index d6d0239e62..ae315cc19d 100755 --- a/packages/app-cli/tests/support/plugins/updatePlugins.sh +++ b/packages/app-cli/tests/support/plugins/updatePlugins.sh @@ -11,6 +11,7 @@ yo joplin rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/dialog/" rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/events/" rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/json_export/" +rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/menu/" rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/multi_selection/" rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/register_command/" rsync -a --delete --exclude "src/" --exclude "package.json" --exclude "package-lock.json" --exclude "node_modules/" --exclude "dist/" "$TEMP_DIR/" "$SCRIPT_DIR/selected_text/" diff --git a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsDialogs.d.ts b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsDialogs.d.ts index cc3a833d8e..5956c0eca4 100644 --- a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsDialogs.d.ts +++ b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsDialogs.d.ts @@ -17,7 +17,7 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - create(): Promise; + create(id: string): Promise; /** * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel" */ diff --git a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenuItems.d.ts b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenuItems.d.ts index 5993284021..69e2a8f181 100644 --- a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenuItems.d.ts +++ b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenuItems.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; + create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise; } diff --git a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenus.d.ts b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenus.d.ts index a87c766e73..f5f803cb1b 100644 --- a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenus.d.ts +++ b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsMenus.d.ts @@ -14,5 +14,5 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; + create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise; } diff --git a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsPanels.d.ts b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsPanels.d.ts index 9c4499b915..3646269eeb 100644 --- a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsPanels.d.ts +++ b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsPanels.d.ts @@ -1,8 +1,11 @@ import Plugin from '../Plugin'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -14,7 +17,7 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - create(): Promise; + create(id: string): Promise; /** * Sets the panel webview HTML */ diff --git a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsToolbarButtons.d.ts b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsToolbarButtons.d.ts index fea348b5cd..ba17c83e34 100644 --- a/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsToolbarButtons.d.ts +++ b/packages/app-cli/tests/support/plugins/withExternalModules/api/JoplinViewsToolbarButtons.d.ts @@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - create(commandName: string, location: ToolbarButtonLocation): Promise; + create(id: string, commandName: string, location: ToolbarButtonLocation): Promise; } diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index 000a08014f..3894b66b26 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -18,6 +18,9 @@ import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerS import SpellCheckerServiceDriverNative from './services/spellChecker/SpellCheckerServiceDriverNative'; import bridge from './services/bridge'; import menuCommandNames from './gui/menuCommandNames'; +import { LayoutItem } from './gui/ResizableLayout/utils/types'; +import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext'; +import ResourceService from '@joplin/lib/services/ResourceService'; const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js'); const MasterKey = require('@joplin/lib/models/MasterKey'); @@ -27,7 +30,6 @@ const Tag = require('@joplin/lib/models/Tag.js'); const { reg } = require('@joplin/lib/registry.js'); const packageInfo = require('./packageInfo.js'); const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker'); -const ResourceService = require('@joplin/lib/services/ResourceService').default; const ClipperServer = require('@joplin/lib/ClipperServer'); const ExternalEditWatcher = require('@joplin/lib/services/ExternalEditWatcher'); const { webFrame } = require('electron'); @@ -67,6 +69,7 @@ const commands = [ require('./gui/MainScreen/commands/openNote'), require('./gui/MainScreen/commands/openFolder'), require('./gui/MainScreen/commands/openTag'), + require('./gui/MainScreen/commands/toggleLayoutMoveMode'), require('./gui/NoteEditor/commands/focusElementNoteBody'), require('./gui/NoteEditor/commands/focusElementNoteTitle'), require('./gui/NoteEditor/commands/showLocalSearch'), @@ -105,17 +108,17 @@ export interface AppState extends State { route: AppStateRoute; navHistory: any[]; noteVisiblePanes: string[]; - sidebarVisibility: boolean; - noteListVisibility: boolean; windowContentSize: any; watchedNoteFiles: string[]; lastEditorScrollPercents: any; devToolsVisible: boolean; visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs. focusedField: string; + layoutMoveMode: boolean; // Extra reducer keys go here watchedResources: any; + mainLayout: LayoutItem; } const appDefaultState: AppState = { @@ -127,14 +130,14 @@ const appDefaultState: AppState = { }, navHistory: [], noteVisiblePanes: ['editor', 'viewer'], - sidebarVisibility: true, - noteListVisibility: true, windowContentSize: bridge().windowContentSize(), watchedNoteFiles: [], lastEditorScrollPercents: {}, devToolsVisible: false, visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs. focusedField: null, + layoutMoveMode: false, + mainLayout: null, ...resourceEditWatcherDefaultState, }; @@ -234,25 +237,12 @@ class Application extends BaseApplication { newState.noteVisiblePanes = action.panes; break; - case 'SIDEBAR_VISIBILITY_TOGGLE': + case 'MAIN_LAYOUT_SET': - newState = Object.assign({}, state); - newState.sidebarVisibility = !state.sidebarVisibility; - break; - - case 'SIDEBAR_VISIBILITY_SET': - newState = Object.assign({}, state); - newState.sidebarVisibility = action.visibility; - break; - - case 'NOTELIST_VISIBILITY_TOGGLE': - newState = Object.assign({}, state); - newState.noteListVisibility = !state.noteListVisibility; - break; - - case 'NOTELIST_VISIBILITY_SET': - newState = Object.assign({}, state); - newState.noteListVisibility = action.visibility; + newState = { + ...state, + mainLayout: action.value, + }; break; case 'NOTE_FILE_WATCHER_ADD': @@ -333,6 +323,14 @@ class Application extends BaseApplication { } break; + case 'LAYOUT_MOVE_MODE_SET': + + newState = { + ...state, + layoutMoveMode: action.value, + }; + break; + } } catch (error) { error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`; @@ -384,14 +382,6 @@ class Application extends BaseApplication { Setting.setValue('noteVisiblePanes', newState.noteVisiblePanes); } - if (['SIDEBAR_VISIBILITY_TOGGLE', 'SIDEBAR_VISIBILITY_SET'].indexOf(action.type) >= 0) { - Setting.setValue('sidebarVisibility', newState.sidebarVisibility); - } - - if (['NOTELIST_VISIBILITY_TOGGLE', 'NOTELIST_VISIBILITY_SET'].indexOf(action.type) >= 0) { - Setting.setValue('noteListVisibility', newState.noteListVisibility); - } - if (['NOTE_DEVTOOLS_TOGGLE', 'NOTE_DEVTOOLS_SET'].indexOf(action.type) >= 0) { this.toggleDevTools(newState.devToolsVisible); } @@ -497,6 +487,37 @@ class Application extends BaseApplication { return cssString; } + private async initPluginService() { + const pluginLogger = new Logger(); + pluginLogger.addTarget(TargetType.File, { path: `${Setting.value('profileDir')}/log-plugins.txt` }); + pluginLogger.addTarget(TargetType.Console, { prefix: 'Plugin Service:' }); + pluginLogger.setLevel(Setting.value('env') == 'dev' ? Logger.LEVEL_DEBUG : Logger.LEVEL_INFO); + + const pluginRunner = new PluginRunner(); + PluginService.instance().setLogger(pluginLogger); + PluginService.instance().initialize(PlatformImplementation.instance(), pluginRunner, this.store()); + + try { + if (await shim.fsDriver().exists(Setting.value('pluginDir'))) await PluginService.instance().loadAndRunPlugins(Setting.value('pluginDir')); + } catch (error) { + this.logger().error(`There was an error loading plugins from ${Setting.value('pluginDir')}:`, error); + } + + try { + if (Setting.value('plugins.devPluginPaths')) { + const paths = Setting.value('plugins.devPluginPaths').split(',').map((p: string) => p.trim()); + await PluginService.instance().loadAndRunPlugins(paths); + } + + // Also load dev plugins that have passed via command line arguments + if (Setting.value('startupDevPlugins')) { + await PluginService.instance().loadAndRunPlugins(Setting.value('startupDevPlugins')); + } + } catch (error) { + this.logger().error(`There was an error loading plugins from ${Setting.value('plugins.devPluginPaths')}:`, error); + } + } + async start(argv: string[]): Promise { const electronIsDev = require('electron-is-dev'); @@ -539,7 +560,7 @@ class Application extends BaseApplication { this.initRedux(); - CommandService.instance().initialize(this.store(), Setting.value('env') == 'dev'); + CommandService.instance().initialize(this.store(), Setting.value('env') == 'dev', stateToWhenClauseContext); for (const command of commands) { CommandService.instance().registerDeclaration(command.declaration); @@ -688,34 +709,7 @@ class Application extends BaseApplication { bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated); - const pluginLogger = new Logger(); - pluginLogger.addTarget(TargetType.File, { path: `${Setting.value('profileDir')}/log-plugins.txt` }); - pluginLogger.addTarget(TargetType.Console, { prefix: 'Plugin Service:' }); - pluginLogger.setLevel(Setting.value('env') == 'dev' ? Logger.LEVEL_DEBUG : Logger.LEVEL_INFO); - - const pluginRunner = new PluginRunner(); - PluginService.instance().setLogger(pluginLogger); - PluginService.instance().initialize(PlatformImplementation.instance(), pluginRunner, this.store()); - - try { - if (await shim.fsDriver().exists(Setting.value('pluginDir'))) await PluginService.instance().loadAndRunPlugins(Setting.value('pluginDir')); - } catch (error) { - this.logger().error(`There was an error loading plugins from ${Setting.value('pluginDir')}:`, error); - } - - try { - if (Setting.value('plugins.devPluginPaths')) { - const paths = Setting.value('plugins.devPluginPaths').split(',').map((p: string) => p.trim()); - await PluginService.instance().loadAndRunPlugins(paths); - } - - // Also load dev plugins that have passed via command line arguments - if (Setting.value('startupDevPlugins')) { - await PluginService.instance().loadAndRunPlugins(Setting.value('startupDevPlugins')); - } - } catch (error) { - this.logger().error(`There was an error loading plugins from ${Setting.value('plugins.devPluginPaths')}:`, error); - } + await this.initPluginService(); this.setupContextMenu(); diff --git a/packages/app-desktop/gui/Button/Button.tsx b/packages/app-desktop/gui/Button/Button.tsx index 6f6686baf5..8b7c71abe2 100644 --- a/packages/app-desktop/gui/Button/Button.tsx +++ b/packages/app-desktop/gui/Button/Button.tsx @@ -14,7 +14,7 @@ interface Props { iconName?: string; level?: ButtonLevel; className?: string; - onClick: Function; + onClick?: Function; color?: string; iconAnimation?: string; tooltip?: string; @@ -57,12 +57,14 @@ const StyledButtonPrimary = styled(StyledButtonBase)` border: none; background-color: ${(props: any) => props.theme.backgroundColor5}; - &:hover { - background-color: ${(props: any) => props.theme.backgroundColorHover5}; - } + ${(props: any) => props.disabled} { + &:hover { + background-color: ${(props: any) => props.theme.backgroundColorHover5}; + } - &:active { - background-color: ${(props: any) => props.theme.backgroundColorActive5}; + &:active { + background-color: ${(props: any) => props.theme.backgroundColorActive5}; + } } ${StyledIcon} { @@ -78,12 +80,14 @@ const StyledButtonSecondary = styled(StyledButtonBase)` border: 1px solid ${(props: any) => props.theme.borderColor4}; background-color: ${(props: any) => props.theme.backgroundColor4}; - &:hover { - background-color: ${(props: any) => props.theme.backgroundColorHover4}; - } + ${(props: any) => props.disabled} { + &:hover { + background-color: ${(props: any) => props.theme.backgroundColorHover4}; + } - &:active { - background-color: ${(props: any) => props.theme.backgroundColorActive4}; + &:active { + background-color: ${(props: any) => props.theme.backgroundColorActive4}; + } } ${StyledIcon} { diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index bf3190ddad..37e185c67e 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -1,13 +1,15 @@ import * as React from 'react'; -import ResizableLayout, { findItemByKey, LayoutItem, LayoutItemDirection, allDynamicSizes } from '../ResizableLayout/ResizableLayout'; -import NoteList from '../NoteList/NoteList'; +import ResizableLayout from '../ResizableLayout/ResizableLayout'; +import findItemByKey from '../ResizableLayout/utils/findItemByKey'; +import { MoveButtonClickEvent } from '../ResizableLayout/MoveButtons'; +import { move } from '../ResizableLayout/utils/movements'; +import { LayoutItem } from '../ResizableLayout/utils/types'; import NoteEditor from '../NoteEditor/NoteEditor'; import NoteContentPropertiesDialog from '../NoteContentPropertiesDialog'; import ShareNoteDialog from '../ShareNoteDialog'; -import NoteListControls from '../NoteListControls/NoteListControls'; import CommandService from '@joplin/lib/services/CommandService'; import PluginService from '@joplin/lib/services/plugins/PluginService'; -import { utils as pluginUtils } from '@joplin/lib/services/plugins/reducer'; +import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer'; import SideBar from '../SideBar/SideBar'; import UserWebview from '../../services/plugins/UserWebview'; import UserWebviewDialog from '../../services/plugins/UserWebviewDialog'; @@ -15,20 +17,61 @@ import { ContainerType } from '@joplin/lib/services/plugins/WebviewController'; import { stateUtils } from '@joplin/lib/reducer'; import InteropServiceHelper from '../../InteropServiceHelper'; import { _ } from '@joplin/lib/locale'; +import NoteListWrapper from '../NoteListWrapper/NoteListWrapper'; +import { AppState } from '../../app'; +import { saveLayout, loadLayout } from '../ResizableLayout/utils/persist'; +import Setting from '@joplin/lib/models/Setting'; +import produce from 'immer'; +import shim from '@joplin/lib/shim'; +import bridge from '../../services/bridge'; +import time from '@joplin/lib/time'; +import styled from 'styled-components'; +import { themeStyle } from '@joplin/lib/theme'; +import validateLayout from '../ResizableLayout/utils/validateLayout'; +import iterateItems from '../ResizableLayout/utils/iterateItems'; +import removeItem from '../ResizableLayout/utils/removeItem'; -const produce = require('immer').default; const { connect } = require('react-redux'); const { PromptDialog } = require('../PromptDialog.min.js'); const NotePropertiesDialog = require('../NotePropertiesDialog.min.js'); -const Setting = require('@joplin/lib/models/Setting').default; -const shim = require('@joplin/lib/shim').default; -const { themeStyle } = require('@joplin/lib/theme.js'); -const bridge = require('electron').remote.require('./bridge').default; const PluginManager = require('@joplin/lib/services/PluginManager'); const EncryptionService = require('@joplin/lib/services/EncryptionService'); const ipcRenderer = require('electron').ipcRenderer; -const time = require('@joplin/lib/time').default; -const styled = require('styled-components').default; + +interface LayerModalState { + visible: boolean; + message: string; +} + +interface Props { + plugins: PluginStates; + pluginsLoaded: boolean; + hasNotesBeingSaved: boolean; + dispatch: Function; + mainLayout: LayoutItem; + style: any; + layoutMoveMode: boolean; + editorNoteStatuses: any; + customCss: string; + shouldUpgradeSyncTarget: boolean; + hasDisabledSyncItems: boolean; + hasDisabledEncryptionItems: boolean; + showMissingMasterKeyMessage: boolean; + showNeedUpgradingMasterKeyMessage: boolean; + showShouldReencryptMessage: boolean; + focusedField: string; + themeId: number; + settingEditorCodeView: boolean; + pluginsLegacy: any; +} + +interface State { + promptOptions: any; + modalLayer: LayerModalState; + notePropertiesDialogOptions: any; + noteContentPropertiesDialogOptions: any; + shareNoteDialogOptions: any; +} const StyledUserWebviewDialogContainer = styled.div` display: flex; @@ -41,6 +84,15 @@ const StyledUserWebviewDialogContainer = styled.div` box-sizing: border-box; `; +const defaultLayout: LayoutItem = { + key: 'root', + children: [ + { key: 'sideBar', width: 250 }, + { key: 'noteList', width: 250 }, + { key: 'editor' }, + ], +}; + const commands = [ require('./commands/editAlarm'), require('./commands/exportPdf'), @@ -65,20 +117,21 @@ const commands = [ require('./commands/toggleNoteList'), require('./commands/toggleSideBar'), require('./commands/toggleVisiblePanes'), + require('./commands/toggleLayoutMoveMode'), require('./commands/openNote'), require('./commands/openFolder'), require('./commands/openTag'), ]; -class MainScreenComponent extends React.Component { +class MainScreenComponent extends React.Component { - waitForNotesSavedIID_: any; - isPrinting_: boolean; - styleKey_: string; - styles_: any; - promptOnClose_: Function; + private waitForNotesSavedIID_: any; + private isPrinting_: boolean; + private styleKey_: string; + private styles_: any; + private promptOnClose_: Function; - constructor(props: any) { + constructor(props: Props) { super(props); this.state = { @@ -90,9 +143,10 @@ class MainScreenComponent extends React.Component { notePropertiesDialogOptions: {}, noteContentPropertiesDialogOptions: {}, shareNoteDialogOptions: {}, - layout: this.buildLayout(props.plugins), }; + this.updateMainLayout(this.buildLayout(props.plugins)); + this.registerCommands(); this.setupAppCloseHandling(); @@ -103,117 +157,72 @@ class MainScreenComponent extends React.Component { this.userWebview_message = this.userWebview_message.bind(this); this.resizableLayout_resize = this.resizableLayout_resize.bind(this); this.resizableLayout_renderItem = this.resizableLayout_renderItem.bind(this); + this.resizableLayout_moveButtonClick = this.resizableLayout_moveButtonClick.bind(this); this.window_resize = this.window_resize.bind(this); this.rowHeight = this.rowHeight.bind(this); + this.layoutModeListenerKeyDown = this.layoutModeListenerKeyDown.bind(this); window.addEventListener('resize', this.window_resize); } - buildLayout(plugins: any): LayoutItem { - const rootLayoutSize = this.rootLayoutSize(); - const theme = themeStyle(this.props.themeId); - const sideBarMinWidth = 200; - - const sizes = { - sideBarColumn: { - width: 150, - }, - noteListColumn: { - width: 150, - }, - pluginColumn: { - width: 150, - }, - ...Setting.value('ui.layout'), - }; - - for (const k in sizes) { - if (sizes[k].width < sideBarMinWidth) sizes[k].width = sideBarMinWidth; - } - - const pluginColumnChildren: LayoutItem[] = []; - + private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) { const infos = pluginUtils.viewInfosByType(plugins, 'webview'); - for (const info of infos) { - if (info.view.containerType !== ContainerType.Panel) continue; + let newLayout = produce(layout, (draftLayout: LayoutItem) => { + for (const info of infos) { + if (info.view.containerType !== ContainerType.Panel) continue; - // For now it's assumed all views go in the "pluginColumn" so they are - // resizable vertically. But horizontally they stretch 100% - const viewId = info.view.id; + const viewId = info.view.id; + const existingItem = findItemByKey(draftLayout, viewId); - const size = { - ...(sizes[viewId] ? sizes[viewId] : null), - width: '100%', - }; + if (!existingItem) { + draftLayout.children.push({ + key: viewId, + context: { + pluginId: info.plugin.id, + }, + }); + } + } + }); - pluginColumnChildren.push({ - key: viewId, - resizableBottom: true, - context: { - plugin: info.plugin, - control: info.view, - }, - ...size, - }); + // Remove layout items that belong to plugins that are no longer + // active. + const pluginIds = Object.keys(plugins); + const itemsToRemove: string[] = []; + iterateItems(newLayout, (_itemIndex: number, item: LayoutItem, _parent: LayoutItem) => { + if (item.context && item.context.pluginId && !pluginIds.includes(item.context.pluginId)) { + itemsToRemove.push(item.key); + } + return true; + }); + + for (const itemKey of itemsToRemove) { + newLayout = removeItem(newLayout, itemKey); } - return { - key: 'root', - direction: LayoutItemDirection.Row, - width: rootLayoutSize.width, - height: rootLayoutSize.height, - children: [ - { - key: 'sideBarColumn', - direction: LayoutItemDirection.Column, - resizableRight: true, - width: sizes.sideBarColumn.width, - visible: Setting.value('sidebarVisibility'), - minWidth: sideBarMinWidth, - children: [ - { - key: 'sideBar', - }, - ], - }, - { - key: 'noteListColumn', - direction: LayoutItemDirection.Column, - resizableRight: true, - width: sizes.noteListColumn.width, - visible: Setting.value('noteListVisibility'), - minWidth: sideBarMinWidth, - children: [ - { - height: theme.topRowHeight, - key: 'noteListControls', - }, - { - key: 'noteList', - }, - ], - }, - { - key: 'pluginColumn', - direction: LayoutItemDirection.Column, - resizableRight: true, - width: sizes.pluginColumn.width, - visible: !!pluginColumnChildren.length, - minWidth: sideBarMinWidth, - children: pluginColumnChildren, - }, - { - key: 'editorColumn', - direction: LayoutItemDirection.Column, - children: [ - { - key: 'editor', - }, - ], - }, - ], - }; + return newLayout !== layout ? validateLayout(newLayout) : layout; + } + + private buildLayout(plugins: PluginStates): LayoutItem { + const rootLayoutSize = this.rootLayoutSize(); + + const userLayout = Setting.value('ui.layout'); + let output = null; + + try { + output = loadLayout(userLayout, defaultLayout, rootLayoutSize); + + if (!findItemByKey(output, 'sideBar') || !findItemByKey(output, 'noteList') || !findItemByKey(output, 'editor')) { + throw new Error('"sideBar", "noteList" and "editor" must be present in the layout'); + } + } catch (error) { + console.warn('Could not load layout - restoring default layout:', error); + console.warn('Layout was:', userLayout); + output = loadLayout(null, defaultLayout, rootLayoutSize); + } + + return this.updateLayoutPluginViews(output, plugins); } window_resize() { @@ -263,25 +272,22 @@ class MainScreenComponent extends React.Component { this.setState({ shareNoteDialogOptions: {} }); } + updateMainLayout(layout: LayoutItem) { + this.props.dispatch({ + type: 'MAIN_LAYOUT_SET', + value: layout, + }); + } + updateRootLayoutSize() { - this.setState({ layout: produce(this.state.layout, (draft: any) => { + this.updateMainLayout(produce(this.props.mainLayout, (draft: any) => { const s = this.rootLayoutSize(); draft.width = s.width; draft.height = s.height; - }) }); + })); } - componentDidUpdate(prevProps: any, prevState: any) { - if (this.props.noteListVisibility !== prevProps.noteListVisibility || this.props.sidebarVisibility !== prevProps.sidebarVisibility) { - this.setState({ layout: produce(this.state.layout, (draft: any) => { - const noteListColumn = findItemByKey(draft, 'noteListColumn'); - noteListColumn.visible = this.props.noteListVisibility; - - const sideBarColumn = findItemByKey(draft, 'sideBarColumn'); - sideBarColumn.visible = this.props.sidebarVisibility; - }) }); - } - + componentDidUpdate(prevProps: Props, prevState: State) { if (prevProps.style.width !== this.props.style.width || prevProps.style.height !== this.props.style.height || this.messageBoxVisible(prevProps) !== this.messageBoxVisible(this.props) @@ -290,7 +296,8 @@ class MainScreenComponent extends React.Component { } if (prevProps.plugins !== this.props.plugins) { - this.setState({ layout: this.buildLayout(this.props.plugins) }); + this.updateMainLayout(this.updateLayoutPluginViews(this.props.mainLayout, this.props.plugins)); + // this.setState({ layout: this.buildLayout(this.props.plugins) }); } if (this.state.notePropertiesDialogOptions !== prevState.notePropertiesDialogOptions) { @@ -313,28 +320,28 @@ class MainScreenComponent extends React.Component { name: 'shareNote', }); } + + if (this.props.mainLayout !== prevProps.mainLayout) { + const toSave = saveLayout(this.props.mainLayout); + Setting.setValue('ui.layout', toSave); + } + } + + layoutModeListenerKeyDown(event: any) { + if (event.key !== 'Escape') return; + if (!this.props.layoutMoveMode) return; + CommandService.instance().execute('toggleLayoutMoveMode'); } componentDidMount() { - this.updateRootLayoutSize(); + window.addEventListener('keydown', this.layoutModeListenerKeyDown); } componentWillUnmount() { this.unregisterCommands(); window.removeEventListener('resize', this.window_resize); - } - - toggleSideBar() { - this.props.dispatch({ - type: 'SIDEBAR_VISIBILITY_TOGGLE', - }); - } - - toggleNoteList() { - this.props.dispatch({ - type: 'NOTELIST_VISIBILITY_TOGGLE', - }); + window.removeEventListener('keydown', this.layoutModeListenerKeyDown); } async waitForNoteToSaved(noteId: string) { @@ -399,8 +406,8 @@ class MainScreenComponent extends React.Component { return 50; } - styles(themeId: number, width: number, height: number, messageBoxVisible: boolean, isSidebarVisible: any, isNoteListVisible: any) { - const styleKey = [themeId, width, height, messageBoxVisible, +isSidebarVisible, +isNoteListVisible].join('_'); + styles(themeId: number, width: number, height: number, messageBoxVisible: boolean) { + const styleKey = [themeId, width, height, messageBoxVisible].join('_'); if (styleKey === this.styleKey_) return this.styles_; const theme = themeStyle(themeId); @@ -561,30 +568,57 @@ class MainScreenComponent extends React.Component { } resizableLayout_resize(event: any) { - this.setState({ layout: event.layout }); - Setting.setValue('ui.layout', allDynamicSizes(event.layout)); + this.updateMainLayout(event.layout); + } + + resizableLayout_moveButtonClick(event: MoveButtonClickEvent) { + const newLayout = move(this.props.mainLayout, event.itemKey, event.direction); + this.updateMainLayout(newLayout); } resizableLayout_renderItem(key: string, event: any) { const eventEmitter = event.eventEmitter; - if (key === 'sideBar') { - return ; - } else if (key === 'noteList') { - return ; - } else if (key === 'editor') { - const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE'; - return ; - } else if (key === 'noteListControls') { - return ; - } else if (key.indexOf('plugin-view') === 0) { - const { control, plugin } = event.item.context; + const components: any = { + sideBar: () => { + return ; + }, + + noteList: () => { + return ; + }, + + editor: () => { + const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE'; + return ; + }, + }; + + if (components[key]) return components[key](); + + if (key.indexOf('plugin-view') === 0) { + const viewInfo = pluginUtils.viewInfoByViewId(this.props.plugins, event.item.key); + + if (!viewInfo) { + console.warn(`Could not find plugin associated with view: ${event.item.key}`); + return null; + } + + const { view, plugin } = viewInfo; + return { this.props.style ); const promptOptions = this.state.promptOptions; - const sidebarVisibility = this.props.sidebarVisibility; - const noteListVisibility = this.props.noteListVisibility; - const styles = this.styles(this.props.themeId, style.width, style.height, this.messageBoxVisible(), sidebarVisibility, noteListVisibility); + const styles = this.styles(this.props.themeId, style.width, style.height, this.messageBoxVisible()); if (!this.promptOnClose_) { this.promptOnClose_ = (answer: any, buttonType: any) => { @@ -656,6 +688,18 @@ class MainScreenComponent extends React.Component { const noteContentPropertiesDialogOptions = this.state.noteContentPropertiesDialogOptions; const shareNoteDialogOptions = this.state.shareNoteDialogOptions; + const layoutComp = this.props.mainLayout ? ( + + ) : null; + return (
{this.state.modalLayer.message}
@@ -667,25 +711,17 @@ class MainScreenComponent extends React.Component { {messageComp} - + {layoutComp} {pluginDialog}
); } } -const mapStateToProps = (state: any) => { +const mapStateToProps = (state: AppState) => { return { themeId: state.settings.theme, settingEditorCodeView: state.settings['editor.codeView'], - sidebarVisibility: state.sidebarVisibility, - noteListVisibility: state.noteListVisibility, folders: state.folders, notes: state.notes, hasDisabledSyncItems: state.hasDisabledSyncItems, @@ -703,6 +739,8 @@ const mapStateToProps = (state: any) => { editorNoteStatuses: state.editorNoteStatuses, hasNotesBeingSaved: stateUtils.hasNotesBeingSaved(state), focusedField: state.focusedField, + layoutMoveMode: state.layoutMoveMode, + mainLayout: state.mainLayout, }; }; diff --git a/packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.ts b/packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.ts new file mode 100644 index 0000000000..5609f251f1 --- /dev/null +++ b/packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.ts @@ -0,0 +1,20 @@ +import { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService'; +import { _ } from '@joplin/lib/locale'; +import { AppState } from '../../../app'; + +export const declaration: CommandDeclaration = { + name: 'toggleLayoutMoveMode', + label: () => _('Change application layout'), +}; + +export const runtime = (): CommandRuntime => { + return { + execute: async (context: CommandContext, value: boolean = null) => { + const newValue = value !== null ? value : !(context.state as AppState).layoutMoveMode; + context.dispatch({ + type: 'LAYOUT_MOVE_MODE_SET', + value: newValue, + }); + }, + }; +}; diff --git a/packages/app-desktop/gui/MainScreen/commands/toggleNoteList.ts b/packages/app-desktop/gui/MainScreen/commands/toggleNoteList.ts index 396aa09b6f..3c11c88850 100644 --- a/packages/app-desktop/gui/MainScreen/commands/toggleNoteList.ts +++ b/packages/app-desktop/gui/MainScreen/commands/toggleNoteList.ts @@ -1,5 +1,8 @@ -import { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService'; +import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService'; import { _ } from '@joplin/lib/locale'; +import setLayoutItemProps from '../../ResizableLayout/utils/setLayoutItemProps'; +import layoutItemProp from '../../ResizableLayout/utils/layoutItemProp'; +import { AppState } from '../../../app'; export const declaration: CommandDeclaration = { name: 'toggleNoteList', @@ -7,11 +10,18 @@ export const declaration: CommandDeclaration = { iconName: 'fas fa-align-justify', }; -export const runtime = (comp: any): CommandRuntime => { +export const runtime = (): CommandRuntime => { return { - execute: async () => { - comp.props.dispatch({ - type: 'NOTELIST_VISIBILITY_TOGGLE', + execute: async (context: CommandContext) => { + const layout = (context.state as AppState).mainLayout; + + const newLayout = setLayoutItemProps(layout, 'noteList', { + visible: !layoutItemProp(layout, 'noteList', 'visible'), + }); + + context.dispatch({ + type: 'MAIN_LAYOUT_SET', + value: newLayout, }); }, }; diff --git a/packages/app-desktop/gui/MainScreen/commands/toggleSideBar.ts b/packages/app-desktop/gui/MainScreen/commands/toggleSideBar.ts index 7a6c4e311e..b5bb24e51b 100644 --- a/packages/app-desktop/gui/MainScreen/commands/toggleSideBar.ts +++ b/packages/app-desktop/gui/MainScreen/commands/toggleSideBar.ts @@ -1,5 +1,8 @@ -import { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService'; +import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService'; import { _ } from '@joplin/lib/locale'; +import setLayoutItemProps from '../../ResizableLayout/utils/setLayoutItemProps'; +import layoutItemProp from '../../ResizableLayout/utils/layoutItemProp'; +import { AppState } from '../../../app'; export const declaration: CommandDeclaration = { name: 'toggleSideBar', @@ -7,11 +10,18 @@ export const declaration: CommandDeclaration = { iconName: 'fas fa-bars', }; -export const runtime = (comp: any): CommandRuntime => { +export const runtime = (): CommandRuntime => { return { - execute: async () => { - comp.props.dispatch({ - type: 'SIDEBAR_VISIBILITY_TOGGLE', + execute: async (context: CommandContext) => { + const layout = (context.state as AppState).mainLayout; + + const newLayout = setLayoutItemProps(layout, 'sideBar', { + visible: !layoutItemProp(layout, 'sideBar', 'visible'), + }); + + context.dispatch({ + type: 'MAIN_LAYOUT_SET', + value: newLayout, }); }, }; diff --git a/packages/app-desktop/gui/MenuBar.tsx b/packages/app-desktop/gui/MenuBar.tsx index e648cc8988..fb49e65fbf 100644 --- a/packages/app-desktop/gui/MenuBar.tsx +++ b/packages/app-desktop/gui/MenuBar.tsx @@ -13,9 +13,9 @@ import { Module } from '@joplin/lib/services/interop/types'; import InteropServiceHelper from '../InteropServiceHelper'; import { _ } from '@joplin/lib/locale'; import { MenuItem, MenuItemLocation } from '@joplin/lib/services/plugins/api/types'; -import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext'; import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerService'; import menuCommandNames from './menuCommandNames'; +import stateToWhenClauseContext from '../services/commands/stateToWhenClauseContext'; const { connect } = require('react-redux'); const { reg } = require('@joplin/lib/registry.js'); @@ -519,6 +519,8 @@ function useMenu(props: Props) { view: { label: _('&View'), submenu: [ + menuItemDic.toggleLayoutMoveMode, + separator(), menuItemDic.toggleSideBar, menuItemDic.toggleNoteList, menuItemDic.toggleVisiblePanes, diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.tsx index d26becf2ac..75332ce065 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.tsx @@ -5,7 +5,7 @@ import { utils as pluginUtils } from '@joplin/lib/services/plugins/reducer'; import { connect } from 'react-redux'; import { AppState } from '../../../../app'; import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils'; -import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext'; +import stateToWhenClauseContext from '../../../../services/commands/stateToWhenClauseContext'; const { buildStyle } = require('@joplin/lib/theme'); interface ToolbarProps { diff --git a/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx b/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx index 39e08bf2f2..df08ff9a13 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteEditor.tsx @@ -23,12 +23,12 @@ import eventManager from '@joplin/lib/eventManager'; import { AppState } from '../../app'; import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils'; import { _ } from '@joplin/lib/locale'; -import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext'; import TagList from '../TagList'; import NoteTitleBar from './NoteTitle/NoteTitleBar'; import markupLanguageUtils from '@joplin/lib/markupLanguageUtils'; import usePrevious from '../hooks/usePrevious'; import Setting from '@joplin/lib/models/Setting'; +import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext'; const { themeStyle } = require('@joplin/lib/theme'); const { substrWithEllipsis } = require('@joplin/lib/string-utils'); diff --git a/packages/app-desktop/gui/NoteListControls/NoteListControls.tsx b/packages/app-desktop/gui/NoteListControls/NoteListControls.tsx index bf47c6aebc..561bd39659 100644 --- a/packages/app-desktop/gui/NoteListControls/NoteListControls.tsx +++ b/packages/app-desktop/gui/NoteListControls/NoteListControls.tsx @@ -8,10 +8,11 @@ const styled = require('styled-components').default; interface Props { showNewNoteButtons: boolean; + height: number; } const StyledRoot = styled.div` - width: 100%; + height: ${(props: any) => props.height}px; display: flex; flex-direction: row; padding: ${(props: any) => props.theme.mainPadding}px; @@ -68,7 +69,7 @@ export default function NoteListControls(props: Props) { } return ( - + {renderNewNoteButtons()} diff --git a/packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.tsx b/packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.tsx new file mode 100644 index 0000000000..a513f53701 --- /dev/null +++ b/packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.tsx @@ -0,0 +1,39 @@ +import { themeStyle } from '@joplin/lib/theme'; +import * as React from 'react'; +import { useMemo } from 'react'; +import NoteList from '../NoteList/NoteList'; +import NoteListControls from '../NoteListControls/NoteListControls'; +import { Size } from '../ResizableLayout/utils/types'; +import styled from 'styled-components'; + +interface Props { + resizableLayoutEventEmitter: any; + size: Size; + visible: boolean; + focusedField: string; + themeId: number; +} + +const StyledRoot = styled.div` + display: flex; + flex-direction: column; +`; + +export default function NoteListWrapper(props: Props) { + const theme = themeStyle(props.themeId); + const controlHeight = theme.topRowHeight; + + const noteListSize = useMemo(() => { + return { + width: props.size.width, + height: props.size.height - controlHeight, + }; + }, [props.size, controlHeight]); + + return ( + + + + + ); +} diff --git a/packages/app-desktop/gui/NoteToolbar/NoteToolbar.tsx b/packages/app-desktop/gui/NoteToolbar/NoteToolbar.tsx index 6487bfe6df..333344516c 100644 --- a/packages/app-desktop/gui/NoteToolbar/NoteToolbar.tsx +++ b/packages/app-desktop/gui/NoteToolbar/NoteToolbar.tsx @@ -3,7 +3,7 @@ import CommandService from '@joplin/lib/services/CommandService'; import ToolbarBase from '../ToolbarBase'; import { utils as pluginUtils } from '@joplin/lib/services/plugins/reducer'; import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils'; -import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext'; +import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext'; const { connect } = require('react-redux'); const { buildStyle } = require('@joplin/lib/theme'); diff --git a/packages/app-desktop/gui/ResizableLayout/MoveButtons.tsx b/packages/app-desktop/gui/ResizableLayout/MoveButtons.tsx new file mode 100644 index 0000000000..26905c67fd --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/MoveButtons.tsx @@ -0,0 +1,80 @@ +import * as React from 'react'; +import { useCallback } from 'react'; +import Button, { ButtonLevel } from '../Button/Button'; +import { MoveDirection } from './utils/movements'; +import styled from 'styled-components'; + +const StyledRoot = styled.div` + display: flex; + flex-direction: column; + padding: 5px; + background-color: ${props => props.theme.backgroundColor}; + border-radius: 5px; +`; + +const ButtonRow = styled.div` + display: flex; + flex-direction: row; + justify-content: center; +`; + +const EmptyButton = styled(Button)` + visibility: hidden; +`; + +const ArrowButton = styled(Button)` + opacity: ${props => props.disabled ? 0.2 : 1}; +`; + +export interface MoveButtonClickEvent { + direction: MoveDirection; + itemKey: string; +} + +interface Props { + onClick(event: MoveButtonClickEvent): void; + itemKey: string; + canMoveLeft: boolean; + canMoveRight: boolean; + canMoveUp: boolean; + canMoveDown: boolean; +} + +export default function MoveButtons(props: Props) { + const onButtonClick = useCallback((direction: MoveDirection) => { + props.onClick({ direction, itemKey: props.itemKey }); + }, [props.onClick, props.itemKey]); + + function canMove(dir: MoveDirection) { + if (dir === MoveDirection.Up) return props.canMoveUp; + if (dir === MoveDirection.Down) return props.canMoveDown; + if (dir === MoveDirection.Left) return props.canMoveLeft; + if (dir === MoveDirection.Right) return props.canMoveRight; + throw new Error('Unreachable'); + } + + function renderButton(dir: MoveDirection) { + return onButtonClick(dir)} + />; + } + + return ( + + + {renderButton(MoveDirection.Up)} + + + {renderButton(MoveDirection.Left)} + + {renderButton(MoveDirection.Right)} + + + {renderButton(MoveDirection.Down)} + + + ); +} diff --git a/packages/app-desktop/gui/ResizableLayout/ResizableLayout.tsx b/packages/app-desktop/gui/ResizableLayout/ResizableLayout.tsx index f5591d75d2..a469ba7352 100644 --- a/packages/app-desktop/gui/ResizableLayout/ResizableLayout.tsx +++ b/packages/app-desktop/gui/ResizableLayout/ResizableLayout.tsx @@ -1,36 +1,18 @@ import * as React from 'react'; -import { useRef, useState } from 'react'; -import produce from 'immer'; -import useWindowResizeEvent from './hooks/useWindowResizeEvent'; -import useLayoutItemSizes, { LayoutItemSizes, itemSize } from './hooks/useLayoutItemSizes'; -const { Resizable } = require('re-resizable'); +import { useRef, useState, useEffect } from 'react'; +import useWindowResizeEvent from './utils/useWindowResizeEvent'; +import setLayoutItemProps from './utils/setLayoutItemProps'; +import useLayoutItemSizes, { LayoutItemSizes, itemSize } from './utils/useLayoutItemSizes'; +import validateLayout from './utils/validateLayout'; +import { Size, LayoutItem } from './utils/types'; +import { canMove, MoveDirection } from './utils/movements'; +import MoveButtons, { MoveButtonClickEvent } from './MoveButtons'; +import { StyledWrapperRoot, StyledMoveOverlay, MoveModeRootWrapper, MoveModeRootMessage } from './utils/style'; +import { Resizable } from 're-resizable'; const EventEmitter = require('events'); -export const dragBarThickness = 5; - -export enum LayoutItemDirection { - Row = 'row', - Column = 'column', -} - -export interface Size { - width: number; - height: number; -} - -export interface LayoutItem { - key: string; - width?: number; - height?: number; - minWidth?: number; - minHeight?: number; - children?: LayoutItem[]; - direction?: LayoutItemDirection; - resizableRight?: boolean; - resizableBottom?: boolean; - visible?: boolean; - context?: any; -} +const itemMinWidth = 20; +const itemMinHeight = 20; interface onResizeEvent { layout: LayoutItem; @@ -42,78 +24,24 @@ interface Props { width?: number; height?: number; renderItem: Function; + onMoveButtonClick(event: MoveButtonClickEvent): void; + moveMode: boolean; + moveModeMessage: string; } -export function allDynamicSizes(layout: LayoutItem): any { - const output: any = {}; - - function recurseProcess(item: LayoutItem) { - if (item.resizableBottom || item.resizableRight) { - if ('width' in item || 'height' in item) { - const size: any = {}; - if ('width' in item) size.width = item.width; - if ('height' in item) size.height = item.height; - output[item.key] = size; - } - } - - if (item.children) { - for (const child of item.children) { - recurseProcess(child); - } - } - } - - recurseProcess(layout); - - return output; +function itemVisible(item: LayoutItem, moveMode: boolean) { + if (moveMode) return true; + if (item.children && !item.children.length) return false; + return item.visible !== false; } -export function findItemByKey(layout: LayoutItem, key: string): LayoutItem { - function recurseFind(item: LayoutItem): LayoutItem { - if (item.key === key) return item; - - if (item.children) { - for (const child of item.children) { - const found = recurseFind(child); - if (found) return found; - } - } - return null; - } - - const output = recurseFind(layout); - if (!output) throw new Error(`Invalid item key: ${key}`); - return output; -} - -function updateLayoutItem(layout: LayoutItem, key: string, props: any) { - return produce(layout, (draftState: LayoutItem) => { - function recurseFind(item: LayoutItem) { - if (item.key === key) { - for (const n in props) { - (item as any)[n] = props[n]; - } - } else { - if (item.children) { - for (const child of item.children) { - recurseFind(child); - } - } - } - } - - recurseFind(draftState); - }); -} - -function renderContainer(item: LayoutItem, sizes: LayoutItemSizes, onResizeStart: Function, onResize: Function, onResizeStop: Function, children: any[], isLastChild: boolean): any { +function renderContainer(item: LayoutItem, parent: LayoutItem | null, sizes: LayoutItemSizes, onResizeStart: Function, onResize: Function, onResizeStop: Function, children: any[], isLastChild: boolean, moveMode: boolean): any { const style: any = { - display: item.visible !== false ? 'flex' : 'none', + display: itemVisible(item, moveMode) ? 'flex' : 'none', flexDirection: item.direction, }; - const size: Size = itemSize(item, sizes); + const size: Size = itemSize(item, parent, sizes, true); const className = `resizableLayoutItem rli-${item.key}`; if (item.resizableRight || item.resizableBottom) { @@ -128,21 +56,18 @@ function renderContainer(item: LayoutItem, sizes: LayoutItemSizes, onResizeStart topLeft: false, }; - if (item.resizableRight) style.paddingRight = dragBarThickness; - if (item.resizableBottom) style.paddingBottom = dragBarThickness; - return ( {children} @@ -161,8 +86,29 @@ function ResizableLayout(props: Props) { const [resizedItem, setResizedItem] = useState(null); - function renderLayoutItem(item: LayoutItem, sizes: LayoutItemSizes, isVisible: boolean, isLastChild: boolean): any { + function renderItemWrapper(comp: any, item: LayoutItem, parent: LayoutItem | null, size: Size, moveMode: boolean) { + const moveOverlay = moveMode ? ( + + + + ) : null; + return ( + + {moveOverlay} + {comp} + + ); + } + + function renderLayoutItem(item: LayoutItem, parent: LayoutItem | null, sizes: LayoutItemSizes, isVisible: boolean, isLastChild: boolean): any { function onResizeStart() { setResizedItem({ key: item.key, @@ -171,45 +117,79 @@ function ResizableLayout(props: Props) { }); } - function onResize(_event: any, _direction: any, _refToElement: HTMLDivElement, delta: any) { - const newLayout = updateLayoutItem(props.layout, item.key, { - width: resizedItem.initialWidth + delta.width, - height: resizedItem.initialHeight + delta.height, - }); + function onResize(_event: any, direction: string, _refToElement: any, delta: any) { + const newWidth = Math.max(itemMinWidth, resizedItem.initialWidth + delta.width); + const newHeight = Math.max(itemMinHeight, resizedItem.initialHeight + delta.height); + + const newSize: any = {}; + + if (item.width) newSize.width = item.width; + if (item.height) newSize.height = item.height; + + if (direction === 'bottom') { + newSize.height = newHeight; + } else { + newSize.width = newWidth; + } + + const newLayout = setLayoutItemProps(props.layout, item.key, newSize); props.onResize({ layout: newLayout }); eventEmitter.current.emit('resize'); } - function onResizeStop(_event: any, _direction: any, _refToElement: HTMLDivElement, delta: any) { + function onResizeStop(_event: any, _direction: any, _refToElement: any, delta: any) { onResize(_event, _direction, _refToElement, delta); setResizedItem(null); } if (!item.children) { + const size = itemSize(item, parent, sizes, false); + const comp = props.renderItem(item.key, { item: item, eventEmitter: eventEmitter.current, - size: sizes[item.key], + size: size, visible: isVisible, }); - return renderContainer(item, sizes, onResizeStart, onResize, onResizeStop, [comp], isLastChild); + const wrapper = renderItemWrapper(comp, item, parent, size, props.moveMode); + + return renderContainer(item, parent, sizes, onResizeStart, onResize, onResizeStop, [wrapper], isLastChild, props.moveMode); } else { const childrenComponents = []; for (let i = 0; i < item.children.length; i++) { const child = item.children[i]; - childrenComponents.push(renderLayoutItem(child, sizes, isVisible && child.visible !== false, i === item.children.length - 1)); + childrenComponents.push(renderLayoutItem(child, item, sizes, isVisible && itemVisible(child, props.moveMode), i === item.children.length - 1)); } - return renderContainer(item, sizes, onResizeStart, onResize, onResizeStop, childrenComponents, isLastChild); + return renderContainer(item, parent, sizes, onResizeStart, onResize, onResizeStop, childrenComponents, isLastChild, props.moveMode); } } - useWindowResizeEvent(eventEmitter); - const sizes = useLayoutItemSizes(props.layout); + useEffect(() => { + validateLayout(props.layout); + }, [props.layout]); - return renderLayoutItem(props.layout, sizes, props.layout.visible !== false, true); + useWindowResizeEvent(eventEmitter); + const sizes = useLayoutItemSizes(props.layout, props.moveMode); + + function renderMoveModeBox(rootComp: any) { + return ( + + {props.moveModeMessage} + {rootComp} + + ); + } + + const rootComp = renderLayoutItem(props.layout, null, sizes, itemVisible(props.layout, props.moveMode), true); + + if (props.moveMode) { + return renderMoveModeBox(rootComp); + } else { + return rootComp; + } } export default ResizableLayout; diff --git a/packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.ts b/packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.ts deleted file mode 100644 index 3bcefd71df..0000000000 --- a/packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { useMemo } from 'react'; -import { LayoutItem, Size, dragBarThickness } from '../ResizableLayout'; - -export interface LayoutItemSizes { - [key: string]: Size; -} - -export function itemSize(item: LayoutItem, sizes: LayoutItemSizes): Size { - return { - width: 'width' in item ? item.width : sizes[item.key].width, - height: 'height' in item ? item.height : sizes[item.key].height, - }; -} - -function calculateChildrenSizes(item: LayoutItem, sizes: LayoutItemSizes): LayoutItemSizes { - if (!item.children) return sizes; - - const parentSize = itemSize(item, sizes); - - const remainingSize: Size = { - width: parentSize.width, - height: parentSize.height, - }; - - const noWidthChildren: LayoutItem[] = []; - const noHeightChildren: LayoutItem[] = []; - - for (const child of item.children) { - let w = 'width' in child ? child.width : null; - let h = 'height' in child ? child.height : null; - if (child.visible === false) { - w = 0; - h = 0; - } - - if (item.resizableRight) w -= dragBarThickness; - if (item.resizableBottom) h -= dragBarThickness; - - sizes[child.key] = { width: w, height: h }; - if (w !== null) remainingSize.width -= w; - if (h !== null) remainingSize.height -= h; - if (w === null) noWidthChildren.push(child); - if (h === null) noHeightChildren.push(child); - } - - if (noWidthChildren.length) { - const w = item.direction === 'row' ? remainingSize.width / noWidthChildren.length : parentSize.width; - for (const child of noWidthChildren) { - sizes[child.key].width = w; - } - } - - if (noHeightChildren.length) { - const h = item.direction === 'column' ? remainingSize.height / noHeightChildren.length : parentSize.height; - for (const child of noHeightChildren) { - sizes[child.key].height = h; - } - } - - for (const child of item.children) { - const childrenSizes = calculateChildrenSizes(child, sizes); - sizes = { ...sizes, ...childrenSizes }; - } - - return sizes; -} - -export default function useLayoutItemSizes(layout: LayoutItem) { - return useMemo(() => { - let sizes: LayoutItemSizes = {}; - - if (!('width' in layout) || !('height' in layout)) throw new Error('width and height are required on layout root'); - - sizes[layout.key] = { - width: layout.width, - height: layout.height, - }; - - sizes = calculateChildrenSizes(layout, sizes); - - return sizes; - }, [layout]); -} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.ts b/packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.ts new file mode 100644 index 0000000000..9238cdc2a1 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.ts @@ -0,0 +1,23 @@ +import { LayoutItem } from './types'; + +export default function findItemByKey(layout: LayoutItem, key: string): LayoutItem { + if (!layout) throw new Error('Layout cannot be null'); + + function recurseFind(item: LayoutItem): LayoutItem { + if (item.key === key) return item; + + if (item.children) { + for (const child of item.children) { + const found = recurseFind(child); + if (found) return found; + } + } + return null; + } + + return recurseFind(layout); + + // const output = recurseFind(layout); + // if (!output) throw new Error(`Could not find item "${key}"`); + // return output; +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.ts b/packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.ts new file mode 100644 index 0000000000..d9dca0a63a --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.ts @@ -0,0 +1,5 @@ +import { tempContainerPrefix } from './types'; + +export default function(itemKey: string): boolean { + return itemKey.indexOf(tempContainerPrefix) === 0; +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/iterateItems.ts b/packages/app-desktop/gui/ResizableLayout/utils/iterateItems.ts new file mode 100644 index 0000000000..1a40de9361 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/iterateItems.ts @@ -0,0 +1,21 @@ +import { LayoutItem } from './types'; + +type ItemItemCallback = (itemIndex: number, item: LayoutItem, parent: LayoutItem)=> boolean; + +export default function iterateItems(layout: LayoutItem, callback: ItemItemCallback) { + const result = callback(0, layout, null); + if (result === false) return; + + function recurseFind(item: LayoutItem, callback: Function): boolean { + if (item.children) { + for (let childIndex = 0; childIndex < item.children.length; childIndex++) { + const child = item.children[childIndex]; + if (callback(childIndex, child, item) === false) return false; + if (recurseFind(child, callback) === false) return false; + } + } + return true; + } + + if (recurseFind(layout, callback) === false) return; +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.ts b/packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.ts new file mode 100644 index 0000000000..df64dcdf78 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.ts @@ -0,0 +1,8 @@ +import findItemByKey from './findItemByKey'; +import { LayoutItem } from './types'; + +export default function layoutItemProp(layout: LayoutItem, key: string, propName: string) { + const item = findItemByKey(layout, key); + if (!item) throw new Error(`Could not find layout item: ${key}`); + return (item as any)[propName]; +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/movements.test.ts b/packages/app-desktop/gui/ResizableLayout/utils/movements.test.ts new file mode 100644 index 0000000000..805464982d --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/movements.test.ts @@ -0,0 +1,228 @@ +import { LayoutItem, LayoutItemDirection } from './types'; +import validateLayout from './validateLayout'; +import { canMove, MoveDirection, moveHorizontal, moveVertical } from './movements'; +import findItemByKey from './findItemByKey'; + +describe('movements', () => { + + test('should move items horizontally to the right', () => { + let layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + }, + { + key: 'col2', + }, + { + key: 'col3', + }, + ], + }); + + expect(() => moveHorizontal(layout, 'col1', -1)).toThrow(); + + layout = moveHorizontal(layout, 'col1', 1); + + expect(layout.children[0].children[0].key).toBe('col2'); + expect(layout.children[0].children[1].key).toBe('col1'); + expect(layout.children[1].key).toBe('col3'); + + layout = moveHorizontal(layout, 'col1', 1); + + expect(layout.children[0].key).toBe('col2'); + expect(layout.children[1].key).toBe('col1'); + expect(layout.children[2].key).toBe('col3'); + + layout = moveHorizontal(layout, 'col1', 1); + layout = moveHorizontal(layout, 'col1', 1); + + expect(() => moveHorizontal(layout, 'col1', 1)).toThrow(); + }); + + test('should move items horizontally to the left', () => { + let layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + direction: LayoutItemDirection.Column, + children: [ + { key: 'item1' }, + { key: 'item2' }, + ], + }, + { + key: 'col2', + }, + ], + }); + + layout = moveHorizontal(layout, 'item2', -1); + + expect(layout.children[0].key).toBe('item2'); + expect(layout.children[1].key).toBe('item1'); + expect(layout.children[2].key).toBe('col2'); + }); + + test('should move items vertically', () => { + let layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + }, + { + key: 'col2', + direction: LayoutItemDirection.Column, + children: [ + { key: 'row1' }, + { key: 'row2' }, + { key: 'row3' }, + ], + }, + { + key: 'col3', + }, + ], + }); + + layout = moveVertical(layout, 'row3', -1); + + expect(layout.children[1].children[0].key).toBe('row1'); + expect(layout.children[1].children[1].key).toBe('row3'); + expect(layout.children[1].children[2].key).toBe('row2'); + }); + + test('should tell if item can be moved', () => { + const layout: LayoutItem = validateLayout({ + key: 'root', + width: 200, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + resizableRight: true, + direction: LayoutItemDirection.Column, + children: [ + { key: 'row1' }, + { key: 'row2' }, + ], + }, + { + key: 'col2', + }, + ], + }); + + expect(canMove(MoveDirection.Up, findItemByKey(layout, 'col1'), findItemByKey(layout, 'root'))).toBe(false); + expect(canMove(MoveDirection.Down, findItemByKey(layout, 'col1'), findItemByKey(layout, 'root'))).toBe(false); + expect(canMove(MoveDirection.Left, findItemByKey(layout, 'col1'), findItemByKey(layout, 'root'))).toBe(false); + expect(canMove(MoveDirection.Right, findItemByKey(layout, 'col1'), findItemByKey(layout, 'root'))).toBe(true); + + expect(canMove(MoveDirection.Up, findItemByKey(layout, 'row1'), findItemByKey(layout, 'col1'))).toBe(false); + expect(canMove(MoveDirection.Down, findItemByKey(layout, 'row1'), findItemByKey(layout, 'col1'))).toBe(true); + expect(canMove(MoveDirection.Left, findItemByKey(layout, 'row1'), findItemByKey(layout, 'col1'))).toBe(true); + expect(canMove(MoveDirection.Right, findItemByKey(layout, 'row1'), findItemByKey(layout, 'col1'))).toBe(true); + + expect(canMove(MoveDirection.Up, findItemByKey(layout, 'row2'), findItemByKey(layout, 'col1'))).toBe(true); + expect(canMove(MoveDirection.Down, findItemByKey(layout, 'row2'), findItemByKey(layout, 'col1'))).toBe(false); + expect(canMove(MoveDirection.Left, findItemByKey(layout, 'row2'), findItemByKey(layout, 'col1'))).toBe(true); + expect(canMove(MoveDirection.Right, findItemByKey(layout, 'row2'), findItemByKey(layout, 'col1'))).toBe(true); + + expect(canMove(MoveDirection.Up, findItemByKey(layout, 'col2'), findItemByKey(layout, 'root'))).toBe(false); + expect(canMove(MoveDirection.Down, findItemByKey(layout, 'col2'), findItemByKey(layout, 'root'))).toBe(false); + expect(canMove(MoveDirection.Left, findItemByKey(layout, 'col2'), findItemByKey(layout, 'root'))).toBe(true); + expect(canMove(MoveDirection.Right, findItemByKey(layout, 'col2'), findItemByKey(layout, 'root'))).toBe(false); + }); + + test('Container with only one child should take the width of its parent', () => { + let layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + width: 50, + }, + { + key: 'col2', + }, + ], + }); + + layout = moveHorizontal(layout, 'col2', -1); + + expect(layout.children[0].children[0].key).toBe('col1'); + expect(layout.children[0].children[0].width).toBe(undefined); + }); + + test('Temp container should take the width of the child it replaces', () => { + let layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + width: 20, + }, + { + key: 'col2', + width: 80, + }, + { + key: 'col3', + }, + ], + }); + + layout = moveHorizontal(layout, 'col2', -1); + + expect(layout.children[0].width).toBe(20); + expect(layout.children[0].children[0].width).toBe(undefined); + expect(layout.children[0].children[1].width).toBe(undefined); + }); + + test('Last child should have flexible width if all siblings have fixed width', () => { + let layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + width: 20, + }, + { + key: 'col2', + width: 20, + }, + { + key: 'col3', + }, + ], + }); + + layout = moveHorizontal(layout, 'col3', -1); + + expect(layout.children[0].width).toBe(20); + expect(layout.children[1].width).toBe(undefined); + }); + +}); diff --git a/packages/app-desktop/gui/ResizableLayout/utils/movements.ts b/packages/app-desktop/gui/ResizableLayout/utils/movements.ts new file mode 100644 index 0000000000..eee79ca20d --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/movements.ts @@ -0,0 +1,190 @@ +import iterateItems from './iterateItems'; +import { LayoutItem, LayoutItemDirection, tempContainerPrefix } from './types'; +import produce from 'immer'; +import uuid from '@joplin/lib/uuid'; +import validateLayout from './validateLayout'; + +export enum MoveDirection { + Up = 'up', + Down = 'down', + Left = 'left', + Right = 'right', +} + +enum MovementDirection { + Horizontal = 1, + Vertical = 2, +} + +function array_move(arr: any[], old_index: number, new_index: number) { + arr = arr.slice(); + if (new_index >= arr.length) { + let k = new_index - arr.length + 1; + while (k--) { + arr.push(undefined); + } + } + arr.splice(new_index, 0, arr.splice(old_index, 1)[0]); + return arr; +} + +function findItemIndex(siblings: LayoutItem[], key: string) { + return siblings.findIndex((value: LayoutItem) => { + return value.key === key; + }); +} + +function isHorizontalMove(direction: MoveDirection) { + return direction === MoveDirection.Left || direction === MoveDirection.Right; +} + +function resetItemSizes(items: LayoutItem[]) { + return items.map((item: LayoutItem) => { + const newItem = { ...item }; + delete newItem.width; + delete newItem.height; + return newItem; + }); +} + +export function canMove(direction: MoveDirection, item: LayoutItem, parent: LayoutItem) { + if (!parent) return false; + + if (isHorizontalMove(direction)) { + if (parent.isRoot) { + const idx = direction === MoveDirection.Left ? 0 : parent.children.length - 1; + return parent.children[idx] !== item; + } else if (parent.direction === LayoutItemDirection.Column) { + return true; + } + } else { + if (parent.isRoot) { + return false; + } else if (parent.direction === LayoutItemDirection.Column) { + const idx = direction === MoveDirection.Up ? 0 : parent.children.length - 1; + return parent.children[idx] !== item; + } + } + + throw new Error('Unhandled case'); +} + +// For all movements we make the assumption that there's a root container, +// which is a row of multiple columns. Within each of these columns there +// can be multiple rows (one item per row). Items cannot be more deeply +// nested. +function moveItem(direction: MovementDirection, layout: LayoutItem, key: string, inc: number): LayoutItem { + const itemParents: Record = {}; + + const itemIsRoot = (item: LayoutItem) => { + return !itemParents[item.key]; + }; + + const updatedLayout = produce(layout, (draft: any) => { + iterateItems(draft, (itemIndex: number, item: LayoutItem, parent: LayoutItem) => { + itemParents[item.key] = parent; + + if (item.key !== key || !parent) return true; + + // - "flow" means we are moving an item horizontally within a + // row + // - "contrary" means we are moving an item horizontally within + // a column. Sicen it can't move horizontally, it is moved + // out of its container. And vice-versa for vertical + // movements. + let moveType = null; + + if (direction === MovementDirection.Horizontal && parent.direction === LayoutItemDirection.Row) moveType = 'flow'; + if (direction === MovementDirection.Horizontal && parent.direction === LayoutItemDirection.Column) moveType = 'contrary'; + if (direction === MovementDirection.Vertical && parent.direction === LayoutItemDirection.Column) moveType = 'flow'; + if (direction === MovementDirection.Vertical && parent.direction === LayoutItemDirection.Row) moveType = 'contrary'; + + if (moveType === 'flow') { + const newIndex = itemIndex + inc; + + if (newIndex >= parent.children.length || newIndex < 0) throw new Error(`Cannot move item "${key}" from position ${itemIndex} to ${newIndex}`); + + // If the item next to it is a container (has children), + // move the item inside the container + if (parent.children[newIndex].children) { + const newParent = parent.children[newIndex]; + parent.children.splice(itemIndex, 1); + newParent.children.push(item); + newParent.children = resetItemSizes(newParent.children); + } else { + // If the item is a child of the root container, create + // a new column at `newIndex` and move the item that + // was there, as well as the current item, in this + // container. + if (itemIsRoot(parent)) { + const targetChild = parent.children[newIndex]; + + // The new container takes the size of the item it + // replaces. + const newSize: any = {}; + if (direction === MovementDirection.Horizontal) { + if ('width' in targetChild) newSize.width = targetChild.width; + } else { + if ('height' in targetChild) newSize.height = targetChild.height; + } + + const newParent: LayoutItem = { + key: `${tempContainerPrefix}${uuid.createNano()}`, + direction: LayoutItemDirection.Column, + children: [ + targetChild, + item, + ], + ...newSize, + }; + + parent.children[newIndex] = newParent; + parent.children.splice(itemIndex, 1); + + newParent.children = resetItemSizes(newParent.children); + } else { + // Otherwise the default case is simply to move the + // item left/right + parent.children = array_move(parent.children, itemIndex, newIndex); + } + } + } else { + const parentParent = itemParents[parent.key]; + const parentIndex = findItemIndex(parentParent.children, parent.key); + + parent.children.splice(itemIndex, 1); + + let newInc = inc; + if (parent.children.length <= 1) { + parentParent.children[parentIndex] = parent.children[0]; + newInc = inc < 0 ? inc + 1 : inc; + } + + const newItemIndex = parentIndex + newInc; + + parentParent.children.splice(newItemIndex, 0, item); + parentParent.children = resetItemSizes(parentParent.children); + } + + return false; + }); + }); + + return validateLayout(updatedLayout); +} + +export function moveHorizontal(layout: LayoutItem, key: string, inc: number): LayoutItem { + return moveItem(MovementDirection.Horizontal, layout, key, inc); +} + +export function moveVertical(layout: LayoutItem, key: string, inc: number): LayoutItem { + return moveItem(MovementDirection.Vertical, layout, key, inc); +} + +export function move(layout: LayoutItem, key: string, direction: MoveDirection): LayoutItem { + if (direction === MoveDirection.Up) return moveVertical(layout, key, -1); + if (direction === MoveDirection.Down) return moveVertical(layout, key, +1); + if (direction === MoveDirection.Left) return moveHorizontal(layout, key, -1); + if (direction === MoveDirection.Right) return moveHorizontal(layout, key, +1); + throw new Error('Unreachable'); +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/persist.test.ts b/packages/app-desktop/gui/ResizableLayout/utils/persist.test.ts new file mode 100644 index 0000000000..015e8d854b --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/persist.test.ts @@ -0,0 +1,77 @@ +import { loadLayout, saveLayout } from './persist'; +import { LayoutItem, LayoutItemDirection } from './types'; +import validateLayout from './validateLayout'; + +describe('persist', () => { + + test('should save layout and filter out non-user properties', () => { + const layout: LayoutItem = validateLayout({ + key: 'root', + width: 100, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + width: 50, + }, + { + key: 'col2', + direction: LayoutItemDirection.Column, + children: [ + { key: 'item1', height: 20 }, + { key: 'item2' }, + ], + }, + { + key: 'col3', + }, + ], + }); + + const toSave = saveLayout(layout); + + expect(toSave.key).toBe('root'); + expect(toSave.width).toBeUndefined(); + expect(toSave.height).toBeUndefined(); + expect(toSave.direction).toBeUndefined(); + expect(toSave.children.length).toBe(3); + + expect(toSave.children[1].key).toBe('col2'); + expect(toSave.children[1].direction).toBeUndefined(); + }); + + test('should load a layout', () => { + const layout: any = { + key: 'root', + children: [ + { + key: 'col1', + width: 50, + }, + { + key: 'col2', + children: [ + { key: 'item1', height: 20 }, + { key: 'item2' }, + ], + }, + { + key: 'col3', + }, + ], + }; + + const loaded = loadLayout(layout, null, { width: 100, height: 200 }); + + expect(loaded.key).toBe('root'); + expect(loaded.width).toBe(100); + expect(loaded.height).toBe(200); + expect(loaded.direction).toBe(LayoutItemDirection.Row); + expect(loaded.children.length).toBe(3); + + expect(loaded.children[1].key).toBe('col2'); + expect(loaded.children[1].direction).toBe(LayoutItemDirection.Column); + }); + +}); diff --git a/packages/app-desktop/gui/ResizableLayout/utils/persist.ts b/packages/app-desktop/gui/ResizableLayout/utils/persist.ts new file mode 100644 index 0000000000..98292efc9b --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/persist.ts @@ -0,0 +1,41 @@ +import { LayoutItem, Size } from './types'; +import produce from 'immer'; +import iterateItems from './iterateItems'; +import validateLayout from './validateLayout'; + +export function saveLayout(layout: LayoutItem): any { + const propertyWhiteList = [ + 'visible', + 'width', + 'height', + 'children', + 'key', + 'context', + ]; + + return produce(layout, (draft: any) => { + delete draft.width; + delete draft.height; + iterateItems(draft, (_itemIndex: number, item: LayoutItem, _parent: LayoutItem) => { + for (const k of Object.keys(item)) { + if (!propertyWhiteList.includes(k)) delete (item as any)[k]; + } + return true; + }); + }); +} + +export function loadLayout(layout: any, defaultLayout: LayoutItem, rootSize: Size): LayoutItem { + let output: LayoutItem = null; + + if (layout) { + output = { ...layout }; + } else { + output = { ...defaultLayout }; + } + + output.width = rootSize.width; + output.height = rootSize.height; + + return validateLayout(output); +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/removeItem.ts b/packages/app-desktop/gui/ResizableLayout/utils/removeItem.ts new file mode 100644 index 0000000000..2f067c3356 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/removeItem.ts @@ -0,0 +1,18 @@ +import produce from 'immer'; +import iterateItems from './iterateItems'; +import { LayoutItem } from './types'; +import validateLayout from './validateLayout'; + +export default function(layout: LayoutItem, itemKey: string): LayoutItem { + const output = produce(layout, (layoutDraft: LayoutItem) => { + iterateItems(layoutDraft, (itemIndex: number, item: LayoutItem, parent: LayoutItem) => { + if (item.key === itemKey) { + parent.children.splice(itemIndex, 1); + return false; + } + return true; + }); + }); + + return output !== layout ? validateLayout(output) : layout; +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.ts b/packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.ts new file mode 100644 index 0000000000..8cf7cfa714 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.ts @@ -0,0 +1,23 @@ +import produce from 'immer'; +import { LayoutItem } from './types'; +import validateLayout from './validateLayout'; + +export default function setLayoutItemProps(layout: LayoutItem, key: string, props: any) { + return validateLayout(produce(layout, (draftState: LayoutItem) => { + function recurseFind(item: LayoutItem) { + if (item.key === key) { + for (const n in props) { + (item as any)[n] = props[n]; + } + } else { + if (item.children) { + for (const child of item.children) { + recurseFind(child); + } + } + } + } + + recurseFind(draftState); + })); +} diff --git a/packages/app-desktop/gui/ResizableLayout/utils/style.ts b/packages/app-desktop/gui/ResizableLayout/utils/style.ts new file mode 100644 index 0000000000..023b17153e --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/style.ts @@ -0,0 +1,38 @@ +import { ThemeAppearance } from '@joplin/lib/themes/type'; +import styled from 'styled-components'; + +export const StyledWrapperRoot = styled.div` + position: relative; + display: flex; + width: ${props => props.size.width}px; + height: ${props => props.size.height}px; +`; + +export const StyledMoveOverlay = styled.div` + display: flex; + justify-content: center; + align-items: center; + z-index: 100; + background-color: ${props => props.theme.appearance === ThemeAppearance.Light ? 'rgba(0,0,0,0.5)' : 'rgba(255,255,255,0.5)'}; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +`; + +export const MoveModeRootWrapper = styled.div` + position:relative; + display: flex; + align-items: center; + justify-content: center; +`; + +export const MoveModeRootMessage = styled.div` + position:absolute; + bottom: 10px; + z-index:200; + background-color: ${props => props.theme.backgroundColor}; + padding: 10px; + border-radius: 5; +`; diff --git a/packages/app-desktop/gui/ResizableLayout/utils/types.ts b/packages/app-desktop/gui/ResizableLayout/utils/types.ts new file mode 100644 index 0000000000..e9e3b5a410 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/types.ts @@ -0,0 +1,26 @@ +export enum LayoutItemDirection { + Row = 'row', + Column = 'column', +} + +export interface Size { + width: number; + height: number; +} + +export interface LayoutItem { + key: string; + isRoot?: boolean; + width?: number; + height?: number; + minWidth?: number; + minHeight?: number; + children?: LayoutItem[]; + direction?: LayoutItemDirection; + resizableRight?: boolean; + resizableBottom?: boolean; + visible?: boolean; + context?: any; +} + +export const tempContainerPrefix = 'tempContainer-'; diff --git a/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.ts b/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.ts new file mode 100644 index 0000000000..13e0f4821f --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.ts @@ -0,0 +1,89 @@ +import useLayoutItemSizes, { itemSize } from './useLayoutItemSizes'; +import { LayoutItem, LayoutItemDirection } from './types'; +import { renderHook } from '@testing-library/react-hooks'; +import validateLayout from './validateLayout'; + +describe('useLayoutItemSizes', () => { + + test('should validate the layout', () => { + const layout: LayoutItem = validateLayout({ + key: 'root', + width: 200, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { key: 'col1' }, + { key: 'col2' }, + ], + }); + + expect(layout.isRoot).toBe(true); + }); + + test('should give item sizes', () => { + const layout: LayoutItem = validateLayout({ + key: 'root', + width: 200, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + width: 50, + }, + { + key: 'col2', + }, + ], + }); + + const { result } = renderHook(() => useLayoutItemSizes(layout)); + const sizes = result.current; + + expect(sizes.root).toEqual({ width: 200, height: 100 }); + expect(sizes.col1).toEqual({ width: 50, height: 100 }); + expect(sizes.col2).toEqual({ width: 150, height: 100 }); + }); + + test('should leave room for the resizer controls', () => { + const layout: LayoutItem = validateLayout({ + key: 'root', + width: 200, + height: 100, + direction: LayoutItemDirection.Row, + children: [ + { + key: 'col1', + resizableRight: true, + direction: LayoutItemDirection.Column, + children: [ + { key: 'row1', resizableBottom: true }, + { key: 'row2' }, + ], + }, + { + key: 'col2', + }, + ], + }); + + const { result } = renderHook(() => useLayoutItemSizes(layout)); + + const sizes = result.current; + + expect(sizes).toEqual({ + root: { width: 200, height: 100 }, + col1: { width: 100, height: 100 }, + col2: { width: 100, height: 100 }, + row1: { width: 100, height: 50 }, + row2: { width: 100, height: 50 }, + }); + + expect(itemSize(layout.children[0], layout, sizes, true)).toEqual({ width: 100, height: 100 }); + + const parent = layout.children[0]; + expect(itemSize(parent.children[0], parent, sizes, false)).toEqual({ width: 95, height: 45 }); + expect(itemSize(parent.children[1], parent, sizes, false)).toEqual({ width: 95, height: 50 }); + }); + +}); diff --git a/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.ts b/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.ts new file mode 100644 index 0000000000..b0a6a6dc01 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.ts @@ -0,0 +1,95 @@ +import { useMemo } from 'react'; +import { LayoutItem, Size } from './types'; + +const dragBarThickness = 5; + +export interface LayoutItemSizes { + [key: string]: Size; +} + +// Container always take the full space while the items within it need to +// accomodate for the resize handle. +export function itemSize(item: LayoutItem, parent: LayoutItem | null, sizes: LayoutItemSizes, isContainer: boolean): Size { + const parentResizableRight = !!parent && parent.resizableRight; + const parentResizableBottom = !!parent && parent.resizableBottom; + + const rightGap = !isContainer && (item.resizableRight || parentResizableRight) ? dragBarThickness : 0; + const bottomGap = !isContainer && (item.resizableBottom || parentResizableBottom) ? dragBarThickness : 0; + + return { + width: ('width' in item ? item.width : sizes[item.key].width) - rightGap, + height: ('height' in item ? item.height : sizes[item.key].height) - bottomGap, + }; +} + +// This calculate the size of each item within the layout. However +// the final size, as rendered by the component is determined by +// `itemSize()`, as it takes into account the resizer handle +function calculateChildrenSizes(item: LayoutItem, parent: LayoutItem | null, sizes: LayoutItemSizes, makeAllVisible: boolean): LayoutItemSizes { + if (!item.children) return sizes; + + const parentSize = itemSize(item, parent, sizes, true); + + const remainingSize: Size = { + width: parentSize.width, + height: parentSize.height, + }; + + const noWidthChildren: any[] = []; + const noHeightChildren: any[] = []; + + for (const child of item.children) { + let w = 'width' in child ? child.width : null; + let h = 'height' in child ? child.height : null; + if (!makeAllVisible && child.visible === false) { + w = 0; + h = 0; + } + + sizes[child.key] = { width: w, height: h }; + if (w !== null) remainingSize.width -= w; + if (h !== null) remainingSize.height -= h; + if (w === null) noWidthChildren.push({ item: child, parent: item }); + if (h === null) noHeightChildren.push({ item: child, parent: item }); + } + + if (noWidthChildren.length) { + const w = item.direction === 'row' ? Math.floor(remainingSize.width / noWidthChildren.length) : parentSize.width; + for (const child of noWidthChildren) { + const finalWidth = w; + sizes[child.item.key].width = finalWidth; + } + } + + if (noHeightChildren.length) { + const h = item.direction === 'column' ? Math.floor(remainingSize.height / noHeightChildren.length) : parentSize.height; + for (const child of noHeightChildren) { + const finalHeight = h; + sizes[child.item.key].height = finalHeight; + } + } + + for (const child of item.children) { + const childrenSizes = calculateChildrenSizes(child, parent, sizes, makeAllVisible); + sizes = { ...sizes, ...childrenSizes }; + } + + return sizes; +} + +export default function useLayoutItemSizes(layout: LayoutItem, makeAllVisible: boolean = false) { + return useMemo(() => { + let sizes: LayoutItemSizes = {}; + + if (!('width' in layout) || !('height' in layout)) throw new Error('width and height are required on layout root'); + + sizes[layout.key] = { + width: layout.width, + height: layout.height, + }; + + sizes = calculateChildrenSizes(layout, null, sizes, makeAllVisible); + + return sizes; + }, [layout, makeAllVisible]); +} diff --git a/packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.ts b/packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.ts similarity index 100% rename from packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.ts rename to packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.ts diff --git a/packages/app-desktop/gui/ResizableLayout/utils/validateLayout.ts b/packages/app-desktop/gui/ResizableLayout/utils/validateLayout.ts new file mode 100644 index 0000000000..9a248acac4 --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/validateLayout.ts @@ -0,0 +1,103 @@ +import produce from 'immer'; +import iterateItems from './iterateItems'; +import { LayoutItem, LayoutItemDirection } from './types'; + +function updateItemSize(itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) { + if (!parent) return; + + // If a container has only one child, this child should not + // have a width and height, and simply fill up the container + if (parent.children.length === 1) { + delete itemDraft.width; + delete itemDraft.height; + } + + // If all children of a container have a fixed width, the + // latest child should have a flexible width (i.e. no "width" + // property), so that it fills up the remaining space + if (itemIndex === parent.children.length - 1) { + let allChildrenAreSized = true; + for (const child of parent.children) { + if (parent.direction === LayoutItemDirection.Row) { + if (!child.width) { + allChildrenAreSized = false; + break; + } + } else { + if (!child.height) { + allChildrenAreSized = false; + break; + } + } + } + + if (allChildrenAreSized) { + if (parent.direction === LayoutItemDirection.Row) { + delete itemDraft.width; + } else { + delete itemDraft.height; + } + } + } +} + +// All items should be resizable, except for the root and the latest child +// of a container. +function updateResizeRules(itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) { + if (!parent) return; + const isLastChild = itemIndex === parent.children.length - 1; + itemDraft.resizableRight = parent.direction === LayoutItemDirection.Row && !isLastChild; + itemDraft.resizableBottom = parent.direction === LayoutItemDirection.Column && !isLastChild; +} + +// Container direction should alternate between row (for the root) and +// columns, then rows again. +function updateDirection(_itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) { + if (!parent) { + itemDraft.direction = LayoutItemDirection.Row; + } else { + itemDraft.direction = parent.direction === LayoutItemDirection.Row ? LayoutItemDirection.Column : LayoutItemDirection.Row; + } +} + +function itemShouldBeVisible(item: LayoutItem): boolean { + if (!item.children) return item.visible !== false; + + let oneIsVisible = false; + + for (const child of item.children) { + if (itemShouldBeVisible(child)) { + oneIsVisible = true; + break; + } + } + + return oneIsVisible; +} + +// If all children of a container are hidden, the container should be +// hidden too. A container visiblity cannot be changed by the user. +function updateContainerVisibility(_itemIndex: number, itemDraft: LayoutItem, _parent: LayoutItem) { + if (itemDraft.children) { + itemDraft.visible = itemShouldBeVisible(itemDraft); + } else { + itemDraft.visible = itemDraft.visible !== false; + } +} + +export default function validateLayout(layout: LayoutItem): LayoutItem { + if (!layout) throw new Error('Layout is null'); + if (!layout.children || !layout.children.length) throw new Error('Root does not have children'); + + return produce(layout, (draft: LayoutItem) => { + draft.isRoot = true; + + iterateItems(draft, (itemIndex: number, itemDraft: LayoutItem, parent: LayoutItem) => { + updateItemSize(itemIndex, itemDraft, parent); + updateResizeRules(itemIndex, itemDraft, parent); + updateDirection(itemIndex, itemDraft, parent); + updateContainerVisibility(itemIndex, itemDraft, parent); + return true; + }); + }); +} diff --git a/packages/app-desktop/gui/Root.tsx b/packages/app-desktop/gui/Root.tsx index 72d8f0a023..642ea718e1 100644 --- a/packages/app-desktop/gui/Root.tsx +++ b/packages/app-desktop/gui/Root.tsx @@ -6,7 +6,7 @@ import OneDriveLoginScreen from './OneDriveLoginScreen'; import DropboxLoginScreen from './DropboxLoginScreen'; import ErrorBoundary from './ErrorBoundary'; import { themeStyle } from '@joplin/lib/theme'; -import { Size } from './ResizableLayout/ResizableLayout'; +import { Size } from './ResizableLayout/utils/types'; import MenuBar from './MenuBar'; import { _ } from '@joplin/lib/locale'; const React = require('react'); @@ -67,20 +67,10 @@ async function initialize() { type: 'NOTE_VISIBLE_PANES_SET', panes: Setting.value('noteVisiblePanes'), }); - - store.dispatch({ - type: 'SIDEBAR_VISIBILITY_SET', - visibility: Setting.value('sidebarVisibility'), - }); - - store.dispatch({ - type: 'NOTELIST_VISIBILITY_SET', - visibility: Setting.value('noteListVisibility'), - }); } class RootComponent extends React.Component { - async componentDidMount() { + public async componentDidMount() { if (this.props.appState == 'starting') { this.props.dispatch({ type: 'APP_STATE_SET', @@ -98,7 +88,7 @@ class RootComponent extends React.Component { await WelcomeUtils.install(this.props.dispatch); } - render() { + public render() { const navigatorStyle = { width: this.props.size.width / this.props.zoomFactor, height: this.props.size.height / this.props.zoomFactor, diff --git a/packages/app-desktop/gui/SideBar/SideBar.tsx b/packages/app-desktop/gui/SideBar/SideBar.tsx index e745f800fb..4d5f580507 100644 --- a/packages/app-desktop/gui/SideBar/SideBar.tsx +++ b/packages/app-desktop/gui/SideBar/SideBar.tsx @@ -685,8 +685,6 @@ const mapStateToProps = (state: any) => { collapsedFolderIds: state.collapsedFolderIds, decryptionWorker: state.decryptionWorker, resourceFetcher: state.resourceFetcher, - sidebarVisibility: state.sidebarVisibility, - noteListVisibility: state.noteListVisibility, }; }; diff --git a/packages/app-desktop/gui/SideBar/commands/focusElementSideBar.ts b/packages/app-desktop/gui/SideBar/commands/focusElementSideBar.ts index f9061f7625..986e14aa58 100644 --- a/packages/app-desktop/gui/SideBar/commands/focusElementSideBar.ts +++ b/packages/app-desktop/gui/SideBar/commands/focusElementSideBar.ts @@ -1,5 +1,7 @@ -import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService'; +import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService'; import { _ } from '@joplin/lib/locale'; +import layoutItemProp from '../../ResizableLayout/utils/layoutItemProp'; +import { AppState } from '../../../app'; export const declaration: CommandDeclaration = { name: 'focusElementSideBar', @@ -9,8 +11,8 @@ export const declaration: CommandDeclaration = { export const runtime = (comp: any): CommandRuntime => { return { - execute: async (context: any) => { - const sideBarVisible = !!context.state.sidebarVisibility; + execute: async (context: CommandContext) => { + const sideBarVisible = layoutItemProp((context.state as AppState).mainLayout, 'sideBar', 'visible'); if (sideBarVisible) { const item = comp.selectedItem(); diff --git a/packages/app-desktop/gui/menuCommandNames.ts b/packages/app-desktop/gui/menuCommandNames.ts index af34d9c96b..468cab5f35 100644 --- a/packages/app-desktop/gui/menuCommandNames.ts +++ b/packages/app-desktop/gui/menuCommandNames.ts @@ -30,6 +30,7 @@ export default function() { 'textPaste', 'textSelectAll', 'toggleExternalEditing', + 'toggleLayoutMoveMode', 'toggleNoteList', 'toggleSideBar', 'toggleVisiblePanes', diff --git a/packages/app-desktop/jest.config.js b/packages/app-desktop/jest.config.js new file mode 100644 index 0000000000..e047fa0bbc --- /dev/null +++ b/packages/app-desktop/jest.config.js @@ -0,0 +1,191 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + // clearMocks: false, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + // coverageDirectory: undefined, + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: 'v8', + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: 'node', + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + testMatch: [ + '**/*.test.js', + ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/packages/app-desktop/package-lock.json b/packages/app-desktop/package-lock.json index 66e7e0c231..73212e3431 100644 --- a/packages/app-desktop/package-lock.json +++ b/packages/app-desktop/package-lock.json @@ -36,6 +36,128 @@ "@babel/highlight": "^7.10.4" } }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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=", + "dev": true + } + } + }, "@babel/generator": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", @@ -145,6 +267,34 @@ } } }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + }, + "dependencies": { + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "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=", + "dev": true + } + } + }, "@babel/helper-module-imports": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", @@ -153,6 +303,266 @@ "@babel/types": "^7.0.0" } }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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=", + "dev": true + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "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=", + "dev": true + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + }, + "dependencies": { + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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=", + "dev": true + } + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + }, + "dependencies": { + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "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=", + "dev": true + } + } + }, "@babel/helper-split-export-declaration": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", @@ -183,6 +593,97 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + }, + "dependencies": { + "@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", + "dev": true + }, + "@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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=", + "dev": true + } + } + }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", @@ -231,6 +732,114 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, "@babel/runtime": { "version": "7.4.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", @@ -346,6 +955,22 @@ } } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, "@develar/schema-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.1.0.tgz", @@ -491,12 +1116,706 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz", "integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==" }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "node-notifier": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true, + "optional": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "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==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, "@styled-system/background": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz", @@ -608,6 +1927,74 @@ "defer-to-connect": "^1.0.1" } }, + "@testing-library/react-hooks": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-3.4.2.tgz", + "integrity": "sha512-RfPG0ckOzUIVeIqlOc1YztKgFW+ON8Y5xaSPbiBkfj9nMkkiLhLeBXT5icfPX65oJV/zCZu4z8EVnUc6GY9C5A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.4", + "@types/testing-library__react-hooks": "^3.4.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "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.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz", + "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", + "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -620,11 +2007,19 @@ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", "dev": true }, + "@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@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==", - "dev": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -634,36 +2029,79 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dev": true, "requires": { "react-is": "^16.7.0" } } } }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "@types/jasmine": { "version": "3.5.11", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.11.tgz", "integrity": "sha512-fg1rOd/DehQTIJTifGqGVY6q92lDgnLfs7C6t1ccSwQrMyoTGSoH6wWzhJDZb6ezhsdwAX4EIBLe8w5fXWmEng==", "dev": true }, + "@types/jest": { + "version": "26.0.15", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz", + "integrity": "sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, "@types/node": { "version": "14.14.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", "dev": true }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/prettier": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz", + "integrity": "sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==", + "dev": true + }, "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "dev": true + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/react": { "version": "16.9.55", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.55.tgz", "integrity": "sha512-6KLe6lkILeRwyyy7yG9rULKJ0sXplUsl98MGoCfpteXf9sPWFWWMknDcsvubcpaTdBuxtsLF6HDUwdApZL/xIg==", - "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -672,11 +2110,18 @@ "csstype": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", - "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==", - "dev": true + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" } } }, + "@types/react-native": { + "version": "0.63.34", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.34.tgz", + "integrity": "sha512-6syTIfUt+DY4mJBoO0Y5i4jsDg0I7v31XIPRgsJZAlHeMY9p9GehtGd4VpQKB/NgzLiGzx9ahJPE8w+0lt/WxA==", + "requires": { + "@types/react": "*" + } + }, "@types/react-redux": { "version": "7.1.9", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.9.tgz", @@ -725,11 +2170,96 @@ } } }, + "@types/react-test-renderer": { + "version": "16.9.3", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.3.tgz", + "integrity": "sha512-wJ7IlN5NI82XMLOyHSa+cNN4Z0I+8/YaLl04uDgcZ+W+ExWCmCiVTLT/7fRNqzy4OhStZcUwIqLNF7q+AdW43Q==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/styled-components": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.4.tgz", + "integrity": "sha512-78f5Zuy0v/LTQNOYfpH+CINHpchzMMmAt9amY2YNtSgsk1TmlKm8L2Wijss/mtTrsUAVTm2CdGB8VOM65vA8xg==", + "requires": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "@types/react-native": "*", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz", + "integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==" + } + } + }, + "@types/testing-library__react-hooks": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.1.tgz", + "integrity": "sha512-G4JdzEcq61fUyV6wVW9ebHWEiLK2iQvaBuCHHn9eMSbZzVh4Z4wHnUGIvQOYCCYeu5DnUtFyNYuAAgbSaO/43Q==", + "dev": true, + "requires": { + "@types/react-test-renderer": "*" + } + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", + "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, "ajv": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", @@ -800,6 +2330,23 @@ "ansi-wrap": "^0.1.0" } }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, "ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", @@ -826,6 +2373,24 @@ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", "dev": true }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } + } + }, "app-builder-bin": { "version": "1.9.11", "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-1.9.11.tgz", @@ -1146,6 +2711,12 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, "babel-cli": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", @@ -2004,6 +3575,85 @@ "babel-template": "^6.24.1" } }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", @@ -2032,6 +3682,31 @@ "touch": "^2.0.1" } }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@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", @@ -2139,6 +3814,26 @@ } } }, + "babel-preset-current-node-syntax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz", + "integrity": "sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, "babel-preset-flow": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", @@ -2148,6 +3843,16 @@ "babel-plugin-transform-flow-strip-types": "^6.22.0" } }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, "babel-preset-react": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", @@ -2538,6 +4243,30 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, "buffer": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.0.tgz", @@ -2800,6 +4529,15 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, + "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==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2819,6 +4557,12 @@ "supports-color": "^2.0.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -2841,6 +4585,12 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3055,6 +4805,12 @@ } } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -3065,6 +4821,12 @@ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.56.0.tgz", "integrity": "sha512-MfKVmYgifXjQpLSgpETuih7A7WTTIsxvKfSLGseTY5+qt0E1UD1wblZGM6WLenORo8sgmf+3X+WTe2WF7mufyw==" }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, "collection-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", @@ -3305,6 +5067,19 @@ "object-assign": "^4.1.1" } }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, "crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", @@ -3331,6 +5106,29 @@ "postcss-value-parser": "^4.0.2" } }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, "csstype": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz", @@ -3360,6 +5158,17 @@ "assert-plus": "^1.0.0" } }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, "debounce": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", @@ -3380,6 +5189,12 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -3405,6 +5220,18 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", @@ -3524,6 +5351,12 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, "detect-node": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", @@ -3531,6 +5364,12 @@ "dev": true, "optional": true }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, "dmg-builder": { "version": "22.3.2", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.3.2.tgz", @@ -3582,6 +5421,23 @@ "@babel/runtime": "^7.1.2" } }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, "dot-prop": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", @@ -4035,6 +5891,12 @@ } } }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -4148,16 +6010,71 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "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=", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -4263,6 +6180,46 @@ "homedir-polyfill": "^1.0.1" } }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -4448,11 +6405,26 @@ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fast-memoize": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==" }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, "fbjs": { "version": "0.8.16", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", @@ -4500,6 +6472,15 @@ "dev": true, "optional": true }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -4680,6 +6661,13 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "dev": true, + "optional": true + }, "fullstore": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fullstore/-/fullstore-1.1.0.tgz", @@ -4706,12 +6694,33 @@ "wide-align": "^1.1.0" } }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -5898,6 +7907,51 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -6019,6 +8073,21 @@ "lru-cache": "^5.1.1" } }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "http-cache-semantics": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", @@ -6036,6 +8105,12 @@ "sshpk": "^1.7.0" } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -6066,6 +8141,16 @@ "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", "dev": true }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6112,6 +8197,12 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -6170,6 +8261,15 @@ "ci-info": "^2.0.0" } }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -6214,6 +8314,13 @@ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "optional": true + }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -6260,6 +8367,12 @@ "number-is-nan": "^1.0.0" } }, + "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==", + "dev": true + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -6291,6 +8404,12 @@ "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", "dev": true }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -6324,6 +8443,12 @@ "dev": true, "optional": true }, + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, "is-primitive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", @@ -6426,6 +8551,1602 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "requires": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + } + } + }, + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "requires": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "dev": true, + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "js-sha512": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", @@ -6454,6 +10175,40 @@ "dev": true, "optional": true }, + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + } + }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", @@ -6471,6 +10226,12 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -6551,6 +10312,12 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, "last-run": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", @@ -6603,6 +10370,22 @@ "flush-write-stream": "^1.0.2" } }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "liftoff": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", @@ -6619,6 +10402,12 @@ "resolve": "^1.1.7" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -6666,6 +10455,12 @@ "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", "dev": true }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -6755,6 +10550,15 @@ "kind-of": "^6.0.2" } }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -6851,6 +10655,12 @@ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz", "integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA==" }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -7128,12 +10938,24 @@ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node-abi": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz", @@ -7340,6 +11162,18 @@ } } }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "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=", + "dev": true + }, "node-notifier": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", @@ -7429,6 +11263,15 @@ "pify": "^3.0.0" } }, + "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=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -7445,6 +11288,18 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7610,6 +11465,20 @@ "mimic-fn": "^1.0.0" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "ora": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", @@ -7727,6 +11596,18 @@ "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true }, + "p-each-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -7835,6 +11716,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -7858,6 +11745,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", @@ -7908,6 +11801,12 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -7930,6 +11829,60 @@ "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==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -7963,6 +11916,12 @@ "which-pm-runs": "^1.0.0" } }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -7981,6 +11940,56 @@ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==" }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + } + } + }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -8004,6 +12013,16 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", @@ -8259,6 +12278,30 @@ "react-transition-group": "^2.2.1" } }, + "react-test-renderer": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz", + "integrity": "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.19.1" + }, + "dependencies": { + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } + } + }, "react-tooltip": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-3.10.0.tgz", @@ -8545,6 +12588,105 @@ "remove-trailing-separator": "^1.1.0" } }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "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.3", + "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.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8570,6 +12712,23 @@ "path-parse": "^1.0.6" } }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", @@ -8679,6 +12838,12 @@ "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz", "integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g==" }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, "rxjs": { "version": "6.5.5", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", @@ -8708,6 +12873,35 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "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": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + } + } + }, "sanitize-filename": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", @@ -8723,6 +12917,15 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, "scheduler": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", @@ -8825,6 +13028,21 @@ "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=", + "dev": true, + "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=", + "dev": true + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -8880,6 +13098,12 @@ } } }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -9158,6 +13382,23 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", "dev": true }, + "stack-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", + "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, "stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", @@ -9185,6 +13426,12 @@ } } }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -9197,6 +13444,33 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -9232,6 +13506,18 @@ "is-utf8": "^0.2.0" } }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -9344,6 +13630,33 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "sver-compat": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", @@ -9359,6 +13672,12 @@ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" }, + "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==", + "dev": true + }, "taboverride": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/taboverride/-/taboverride-4.0.3.tgz", @@ -9447,6 +13766,33 @@ "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", "dev": true }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -9510,6 +13856,12 @@ "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.2.0.tgz", "integrity": "sha512-Q7KAu9sLB6TBhKFdb2LHPGy770zkSEjpN1VRqZ6pxNuVQ0mbGWgMocHDvM9XL9yJaOhFrJP6s9XM7zG2gapGpA==" }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -9564,6 +13916,15 @@ "safe-regex": "^1.1.0" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "to-through": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", @@ -9591,6 +13952,26 @@ } } }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -9640,6 +14021,21 @@ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -9911,6 +14307,41 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true, + "optional": true + }, + "v8-to-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz", + "integrity": "sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, "v8flags": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", @@ -10009,6 +14440,33 @@ "vinyl": "^2.0.0" } }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "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=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -10018,11 +14476,54 @@ "defaults": "^1.0.3" } }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, "whatwg-fetch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" }, + "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==", + "dev": true + }, + "whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -10099,6 +14600,12 @@ } } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -10192,12 +14699,30 @@ "typedarray-to-buffer": "^3.1.5" } }, + "ws": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", + "dev": true + }, "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "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==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/packages/app-desktop/package.json b/packages/app-desktop/package.json index c4a5b1c1a2..b9ad0b2532 100644 --- a/packages/app-desktop/package.json +++ b/packages/app-desktop/package.json @@ -10,7 +10,8 @@ "postinstall": "npm run build && gulp electronRebuild", "tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json", "watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json", - "start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools" + "start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools", + "test": "jest" }, "repository": { "type": "git", @@ -86,11 +87,13 @@ }, "homepage": "https://github.com/laurent22/joplin#readme", "devDependencies": { + "@joplin/tools": "^1.0.9", + "@testing-library/react-hooks": "^3.4.2", "@types/jasmine": "^3.5.11", + "@types/jest": "^26.0.15", "@types/node": "^14.14.6", "@types/react": "16.9.55", "@types/react-redux": "^7.1.9", - "@joplin/tools": "^1.0.9", "ajv": "^6.5.0", "app-builder-bin": "^1.9.11", "babel-cli": "^6.26.0", @@ -100,7 +103,9 @@ "electron-rebuild": "^1.10.1", "glob": "^7.1.6", "gulp": "^4.0.2", + "jest": "^26.6.3", "js-sha512": "^0.8.0", + "react-test-renderer": "^16.14.0", "typescript": "^4.0.5" }, "optionalDependencies": { @@ -112,6 +117,7 @@ "@fortawesome/fontawesome-free": "^5.13.0", "@joplin/lib": "^1.0.9", "@joplin/renderer": "^1.0.17", + "@types/styled-components": "^5.1.4", "async-mutex": "^0.1.3", "codemirror": "^5.56.0", "color": "^3.1.2", diff --git a/packages/app-desktop/services/commands/stateToWhenClauseContext.ts b/packages/app-desktop/services/commands/stateToWhenClauseContext.ts new file mode 100644 index 0000000000..4dbe1c26ab --- /dev/null +++ b/packages/app-desktop/services/commands/stateToWhenClauseContext.ts @@ -0,0 +1,23 @@ +// This extends the generic stateToWhenClauseContext (potentially shared by +// all apps) with additional properties specific to the desktop app. So in +// general, any desktop component should import this file, and not the lib +// one. + +import { AppState } from '../../app'; +import libStateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext'; +import layoutItemProp from '../../gui/ResizableLayout/utils/layoutItemProp'; + +export default function stateToWhenClauseContext(state: AppState) { + return { + ...libStateToWhenClauseContext(state), + + // UI elements + markdownEditorVisible: !!state.settings['editor.codeView'], + richTextEditorVisible: !state.settings['editor.codeView'], + markdownEditorPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('editor'), + markdownViewerPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('viewer'), + modalDialogVisible: !!Object.keys(state.visibleDialogs).length, + sideBarVisible: !!state.mainLayout && layoutItemProp(state.mainLayout, 'sideBar', 'visible'), + noteListHasNotes: !!state.notes.length, + }; +} diff --git a/packages/lib/.gitignore b/packages/lib/.gitignore new file mode 100644 index 0000000000..6a64b44e91 --- /dev/null +++ b/packages/lib/.gitignore @@ -0,0 +1 @@ +plugin_types/ diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index 37a5f52f31..44443e9d13 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -661,9 +661,6 @@ class Setting extends BaseModel { 'ui.layout': { value: {}, type: SettingItemType.Object, public: false, appTypes: ['desktop'] }, - 'style.sidebar.width': { value: 150, minimum: 80, maximum: 400, type: SettingItemType.Int, public: false, appTypes: ['desktop'] }, - 'style.noteList.width': { value: 150, minimum: 80, maximum: 400, type: SettingItemType.Int, public: false, appTypes: ['desktop'] }, - // TODO: Is there a better way to do this? The goal here is to simply have // a way to display a link to the customizable stylesheets, not for it to // serve as a customizable Setting. But because the Setting page is auto- @@ -728,8 +725,6 @@ class Setting extends BaseModel { }, }, noteVisiblePanes: { value: ['editor', 'viewer'], type: SettingItemType.Array, public: false, appTypes: ['desktop'] }, - sidebarVisibility: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] }, - noteListVisibility: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] }, tagHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] }, folderHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: ['desktop'] }, editor: { value: '', type: SettingItemType.String, subType: 'file_path_and_args', public: true, appTypes: ['cli', 'desktop'], label: () => _('Text editor command'), description: () => _('The editor command (may include arguments) that will be used to open a note. If none is provided it will try to auto-detect the default editor.') }, diff --git a/packages/lib/services/CommandService.ts b/packages/lib/services/CommandService.ts index e117fad6b2..664a26ebcb 100644 --- a/packages/lib/services/CommandService.ts +++ b/packages/lib/services/CommandService.ts @@ -3,7 +3,6 @@ import eventManager from '../eventManager'; import BaseService from './BaseService'; import shim from '../shim'; import WhenClause from './WhenClause'; -import stateToWhenClauseContext from './commands/stateToWhenClauseContext'; type LabelFunction = ()=> string; type EnabledCondition = string; @@ -103,11 +102,13 @@ export default class CommandService extends BaseService { private commands_: Commands = {}; private store_: any; private devMode_: boolean; + private stateToWhenClauseContext_: Function; - public initialize(store: any, devMode: boolean) { + public initialize(store: any, devMode: boolean, stateToWhenClauseContext: Function) { utils.store = store; this.store_ = store; this.devMode_ = devMode; + this.stateToWhenClauseContext_ = stateToWhenClauseContext; } public on(eventName: string, callback: Function) { @@ -236,7 +237,7 @@ export default class CommandService extends BaseService { } public currentWhenClauseContext() { - return stateToWhenClauseContext(this.store_.getState()); + return this.stateToWhenClauseContext_(this.store_.getState()); } public isPublic(commandName: string) { diff --git a/packages/lib/services/commands/stateToWhenClauseContext.ts b/packages/lib/services/commands/stateToWhenClauseContext.ts index aa281083ef..84d97a25f0 100644 --- a/packages/lib/services/commands/stateToWhenClauseContext.ts +++ b/packages/lib/services/commands/stateToWhenClauseContext.ts @@ -1,23 +1,14 @@ -import { stateUtils } from '../../reducer'; +import { State, stateUtils } from '../../reducer'; const BaseModel = require('../../BaseModel').default; const Folder = require('../../models/Folder'); const MarkupToHtml = require('@joplin/renderer/MarkupToHtml').default; -export default function stateToWhenClauseContext(state: any) { +export default function stateToWhenClauseContext(state: State) { const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null; const note = noteId ? BaseModel.byId(state.notes, noteId) : null; return { - // UI elements - markdownEditorVisible: !!state.settings['editor.codeView'], - richTextEditorVisible: !state.settings['editor.codeView'], - markdownEditorPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('editor'), - markdownViewerPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('viewer'), - modalDialogVisible: !!Object.keys(state.visibleDialogs).length, - sideBarVisible: !!state.sidebarVisibility, - noteListHasNotes: !!state.notes.length, - // Application state notesAreBeingSaved: stateUtils.hasNotesBeingSaved(state), syncStarted: state.syncStarted, diff --git a/packages/lib/services/plugins/Plugin.ts b/packages/lib/services/plugins/Plugin.ts index 028e601871..cf7d732175 100644 --- a/packages/lib/services/plugins/Plugin.ts +++ b/packages/lib/services/plugins/Plugin.ts @@ -62,6 +62,10 @@ export default class Plugin { return this.baseDir_; } + public get viewCount(): number { + return Object.keys(this.viewControllers_).length; + } + on(eventName: string, callback: Function) { return this.eventEmitter_.on(eventName, callback); } @@ -101,7 +105,7 @@ export default class Plugin { } public addViewController(v: ViewController) { - if (this.viewControllers_[v.handle]) throw new Error(`View already added: ${v.handle}`); + if (this.viewControllers_[v.handle]) throw new Error(`View already added or there is already a view with this ID: ${v.handle}`); this.viewControllers_[v.handle] = v; } @@ -110,4 +114,8 @@ export default class Plugin { return this.viewControllers_[handle]; } + public deprecationNotice(goneInVersion: string, message: string) { + this.logger_.warn(`Plugin: ${this.id}: DEPRECATION NOTICE: ${message} The current feature will stop working in version ${goneInVersion}.`); + } + } diff --git a/packages/lib/services/plugins/PluginService.ts b/packages/lib/services/plugins/PluginService.ts index 5a74d7e98d..e4bf77f809 100644 --- a/packages/lib/services/plugins/PluginService.ts +++ b/packages/lib/services/plugins/PluginService.ts @@ -50,6 +50,10 @@ export default class PluginService extends BaseService { return this.plugins_[id]; } + public allPluginIds(): string[] { + return Object.keys(this.plugins_); + } + private async parsePluginJsBundle(jsBundleString: string) { const scriptText = jsBundleString; const lines = scriptText.split('\n'); @@ -170,4 +174,17 @@ export default class PluginService extends BaseService { return this.runner_.run(plugin, pluginApi); } + // public async handleDisabledPlugins() { + // const enabledPlugins = this.allPluginIds(); + // const v = await this.kvStore_.value('pluginService.lastEnabledPlugins'); + // const lastEnabledPlugins = v ? JSON.parse(v) : []; + + // const disabledPlugins = []; + // for (const id of lastEnabledPlugins) { + // if (!enabledPlugins.includes(id)) disabledPlugins.push(id); + // } + + // await this.kvStore_.setValue('pluginService.lastEnabledPlugins', JSON.stringify(enabledPlugins)); + // } + } diff --git a/packages/lib/services/plugins/WebviewController.ts b/packages/lib/services/plugins/WebviewController.ts index ac85676f91..326802c42c 100644 --- a/packages/lib/services/plugins/WebviewController.ts +++ b/packages/lib/services/plugins/WebviewController.ts @@ -23,7 +23,7 @@ export default class WebviewController extends ViewController { private messageListener_: Function = null; private closeResponse_: CloseResponse = null; - constructor(id: string, pluginId: string, store: any, baseDir: string) { + constructor(id: string, pluginId: string, store: any, baseDir: string, containerType: ContainerType) { super(id, pluginId, store); this.baseDir_ = toSystemSlashes(baseDir, 'linux'); @@ -33,7 +33,7 @@ export default class WebviewController extends ViewController { view: { id: this.handle, type: this.type, - containerType: ContainerType.Panel, + containerType: containerType, html: '', scripts: [], opened: false, @@ -68,10 +68,6 @@ export default class WebviewController extends ViewController { return this.storeView.containerType; } - public set containerType(containerType: ContainerType) { - this.setStoreProp('containerType', containerType); - } - public async addScript(path: string) { const fullPath = toSystemSlashes(shim.fsDriver().resolve(`${this.baseDir_}/${path}`), 'linux'); diff --git a/packages/lib/services/plugins/api/JoplinPlugins.ts b/packages/lib/services/plugins/api/JoplinPlugins.ts index ba48522ff1..a177af40fa 100644 --- a/packages/lib/services/plugins/api/JoplinPlugins.ts +++ b/packages/lib/services/plugins/api/JoplinPlugins.ts @@ -42,7 +42,7 @@ export default class JoplinPlugins { // be handled correctly by loggers, etc. const newError: Error = new Error(error.message); newError.stack = error.stack; - this.logger.error(`In plugin ${this.plugin.id}:`, newError); + this.logger.error(`Uncaught exception in plugin "${this.plugin.id}":`, newError); }).then(() => { this.logger.info(`Finished running onStart handler: ${this.plugin.id} (Took ${Date.now() - startTime}ms)`); this.plugin.emit('started'); diff --git a/packages/lib/services/plugins/api/JoplinUtils.js b/packages/lib/services/plugins/api/JoplinUtils.js deleted file mode 100644 index 1961c41c44..0000000000 --- a/packages/lib/services/plugins/api/JoplinUtils.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const Entities = require('html-entities').AllHtmlEntities; -const htmlentities = new Entities().encode; -class JoplinUtils { - escapeHtml(text) { - return htmlentities(text); - } -} -exports.default = JoplinUtils; -// # sourceMappingURL=JoplinUtils.js.map diff --git a/packages/lib/services/plugins/api/JoplinViewsDialogs.ts b/packages/lib/services/plugins/api/JoplinViewsDialogs.ts index 1fbabb9d8b..73958a2f99 100644 --- a/packages/lib/services/plugins/api/JoplinViewsDialogs.ts +++ b/packages/lib/services/plugins/api/JoplinViewsDialogs.ts @@ -30,10 +30,14 @@ export default class JoplinViewsDialogs { /** * Creates a new dialog */ - async create(): Promise { - const handle = createViewHandle(this.plugin); - const controller = new WebviewController(handle, this.plugin.id, this.store, this.plugin.baseDir); - controller.containerType = ContainerType.Dialog; + async create(id: string): Promise { + if (!id) { + this.plugin.deprecationNotice('1.5', 'Creating a view without an ID is deprecated. To fix it, change your call to `joplin.views.dialogs.create("my-unique-id")`'); + id = `${this.plugin.viewCount}`; + } + + const handle = createViewHandle(this.plugin, id); + const controller = new WebviewController(handle, this.plugin.id, this.store, this.plugin.baseDir, ContainerType.Dialog); this.plugin.addViewController(controller); return handle; } diff --git a/packages/lib/services/plugins/api/JoplinViewsMenuItems.ts b/packages/lib/services/plugins/api/JoplinViewsMenuItems.ts index 2674742ecc..5997f157c8 100644 --- a/packages/lib/services/plugins/api/JoplinViewsMenuItems.ts +++ b/packages/lib/services/plugins/api/JoplinViewsMenuItems.ts @@ -22,8 +22,16 @@ export default class JoplinViewsMenuItems { /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ - async create(commandName: string, location: MenuItemLocation = MenuItemLocation.Tools, options: CreateMenuItemOptions = null) { - const handle = createViewHandle(this.plugin); + async create(id: string, commandName: string, location: MenuItemLocation = MenuItemLocation.Tools, options: CreateMenuItemOptions = null) { + if (typeof location !== 'string') { + this.plugin.deprecationNotice('1.5', 'Creating a view without an ID is deprecated. To fix it, change your call to `joplin.views.menuItem.create("my-unique-id", ...)`'); + options = location as any; + location = commandName as any || MenuItemLocation.Tools; + commandName = id as any; + id = `${this.plugin.viewCount}`; + } + + const handle = createViewHandle(this.plugin, id); const controller = new MenuItemController(handle, this.plugin.id, this.store, commandName, location); this.plugin.addViewController(controller); diff --git a/packages/lib/services/plugins/api/JoplinViewsMenus.ts b/packages/lib/services/plugins/api/JoplinViewsMenus.ts index d22a9b4ac9..487b421942 100644 --- a/packages/lib/services/plugins/api/JoplinViewsMenus.ts +++ b/packages/lib/services/plugins/api/JoplinViewsMenus.ts @@ -35,8 +35,16 @@ export default class JoplinViewsMenus { * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the * menu as a sub-menu of the application build-in menus. */ - public async create(label: string, menuItems: MenuItem[], location: MenuItemLocation = MenuItemLocation.Tools) { - const handle = createViewHandle(this.plugin); + public async create(id: string, label: string, menuItems: MenuItem[], location: MenuItemLocation = MenuItemLocation.Tools) { + if (!Array.isArray(menuItems)) { + this.plugin.deprecationNotice('1.5', 'Creating a view without an ID is deprecated. To fix it, change your call to `joplin.views.menus.create("my-unique-id", ...)`'); + location = menuItems as any || MenuItemLocation.Tools; + menuItems = label as any; + label = id as any; + id = `${this.plugin.viewCount}`; + } + + const handle = createViewHandle(this.plugin, id); const controller = new MenuController(handle, this.plugin.id, this.store, label, menuItems, location); this.plugin.addViewController(controller); this.registerCommandAccelerators(menuItems); diff --git a/packages/lib/services/plugins/api/JoplinViewsPanels.ts b/packages/lib/services/plugins/api/JoplinViewsPanels.ts index e159a890d5..60395b4a78 100644 --- a/packages/lib/services/plugins/api/JoplinViewsPanels.ts +++ b/packages/lib/services/plugins/api/JoplinViewsPanels.ts @@ -1,11 +1,14 @@ import Plugin from '../Plugin'; import createViewHandle from '../utils/createViewHandle'; -import WebviewController from '../WebviewController'; +import WebviewController, { ContainerType } from '../WebviewController'; import { ViewHandle } from './types'; /** - * Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example - * it could be used to display a table of content for the active note, or display various metadata or graph. + * Allows creating and managing view panels. View panels currently are + * displayed at the right of the sidebar and allows displaying any HTML + * content (within a webview) and update it in real-time. For example it + * could be used to display a table of content for the active note, or + * display various metadata or graph. * * [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc) */ @@ -26,9 +29,14 @@ export default class JoplinViewsPanels { /** * Creates a new panel */ - async create(): Promise { - const handle = createViewHandle(this.plugin); - const controller = new WebviewController(handle, this.plugin.id, this.store, this.plugin.baseDir); + async create(id: string): Promise { + if (!id) { + this.plugin.deprecationNotice('1.5', 'Creating a view without an ID is deprecated. To fix it, change your call to `joplin.views.panels.create("my-unique-id")`'); + id = `${this.plugin.viewCount}`; + } + + const handle = createViewHandle(this.plugin, id); + const controller = new WebviewController(handle, this.plugin.id, this.store, this.plugin.baseDir, ContainerType.Panel); this.plugin.addViewController(controller); return handle; } diff --git a/packages/lib/services/plugins/api/JoplinViewsToolbarButtons.ts b/packages/lib/services/plugins/api/JoplinViewsToolbarButtons.ts index b1e07130e2..26d387229b 100644 --- a/packages/lib/services/plugins/api/JoplinViewsToolbarButtons.ts +++ b/packages/lib/services/plugins/api/JoplinViewsToolbarButtons.ts @@ -21,8 +21,15 @@ export default class JoplinViewsToolbarButtons { /** * Creates a new toolbar button and associate it with the given command. */ - async create(commandName: string, location: ToolbarButtonLocation) { - const handle = createViewHandle(this.plugin); + async create(id: string, commandName: string, location: ToolbarButtonLocation) { + if (arguments.length < 3) { + this.plugin.deprecationNotice('1.5', 'Creating a view without an ID is deprecated. To fix it, change your call to `joplin.views.toolbarButtons.create("my-unique-id", ...)`'); + location = commandName as any; + commandName = id as any; + id = `${this.plugin.viewCount}`; + } + + const handle = createViewHandle(this.plugin, id); const controller = new ToolbarButtonController(handle, this.plugin.id, this.store, commandName, location); this.plugin.addViewController(controller); } diff --git a/packages/lib/services/plugins/reducer.ts b/packages/lib/services/plugins/reducer.ts index 0eba22ea01..ea9e75aefc 100644 --- a/packages/lib/services/plugins/reducer.ts +++ b/packages/lib/services/plugins/reducer.ts @@ -82,6 +82,30 @@ export const utils = { return output; }, + viewInfoByViewId: function(plugins: PluginStates, viewId: string): ViewInfo { + for (const pluginId in plugins) { + const plugin = plugins[pluginId]; + if (plugin.views[viewId]) { + return { + plugin: plugin, + view: plugin.views[viewId], + }; + } + } + return null; + }, + + allViewIds: function(plugins: PluginStates): string[] { + const output = []; + for (const pluginId in plugins) { + const plugin = plugins[pluginId]; + for (const viewId in plugin.views) { + output.push(viewId); + } + } + return output; + }, + commandNamesFromViews: function(plugins: PluginStates, toolbarType: string): string[] { const infos = utils.viewInfosByType(plugins, 'toolbarButton'); @@ -91,42 +115,44 @@ export const utils = { }, }; -const reducer = (draft: Draft, action: any) => { +const reducer = (draftRoot: Draft, action: any) => { if (action.type.indexOf('PLUGIN_') !== 0) return; // All actions should be scoped to a plugin, except when adding a new plugin if (!action.pluginId && action.type !== 'PLUGIN_ADD') throw new Error(`action.pluginId is required. Action was: ${JSON.stringify(action)}`); + const draft = draftRoot.pluginService as State; + try { switch (action.type) { case 'PLUGIN_ADD': - if (draft.pluginService.plugins[action.plugin.id]) throw new Error(`Plugin is already loaded: ${JSON.stringify(action)}`); - draft.pluginService.plugins[action.plugin.id] = action.plugin; + if (draft.plugins[action.plugin.id]) throw new Error(`Plugin is already loaded: ${JSON.stringify(action)}`); + draft.plugins[action.plugin.id] = action.plugin; break; case 'PLUGIN_VIEW_ADD': - draft.pluginService.plugins[action.pluginId].views[action.view.id] = { ...action.view }; + draft.plugins[action.pluginId].views[action.view.id] = { ...action.view }; break; case 'PLUGIN_VIEW_PROP_SET': - draft.pluginService.plugins[action.pluginId].views[action.id][action.name] = action.value; + (draft.plugins[action.pluginId].views[action.id] as any)[action.name] = action.value; break; case 'PLUGIN_VIEW_PROP_PUSH': - draft.pluginService.plugins[action.pluginId].views[action.id][action.name].push(action.value); + (draft.plugins[action.pluginId].views[action.id] as any)[action.name].push(action.value); break; case 'PLUGIN_CONTENT_SCRIPTS_ADD': { const type = action.contentScript.type; - if (!draft.pluginService.plugins[action.pluginId].contentScripts[type]) draft.pluginService.plugins[action.pluginId].contentScripts[type] = []; + if (!draft.plugins[action.pluginId].contentScripts[type]) draft.plugins[action.pluginId].contentScripts[type] = []; - draft.pluginService.plugins[action.pluginId].contentScripts[type].push({ + draft.plugins[action.pluginId].contentScripts[type].push({ id: action.contentScript.id, path: action.contentScript.path, }); diff --git a/packages/lib/services/plugins/utils/createViewHandle.ts b/packages/lib/services/plugins/utils/createViewHandle.ts index f5476bab27..f58f267c63 100644 --- a/packages/lib/services/plugins/utils/createViewHandle.ts +++ b/packages/lib/services/plugins/utils/createViewHandle.ts @@ -1,8 +1,8 @@ -import uuid from '../../../uuid'; import Plugin from '../Plugin'; export type ViewHandle = string; -export default function createViewHandle(plugin: Plugin): ViewHandle { - return `plugin-view-${plugin.id}-${uuid.createNano()}`; +export default function createViewHandle(plugin: Plugin, id: string): ViewHandle { + if (!id) throw new Error('A view ID must be provided'); + return `plugin-view-${plugin.id}-${id}`; }