Full Code of shayc/cboard for AI

master 5bbe00e8f200 cached
836 files
20.7 MB
5.5M tokens
1508 symbols
1 requests
Copy disabled (too large) Download .txt
Showing preview only (24,061K chars total). Download the full file to get everything.
Repository: shayc/cboard
Branch: master
Commit: 5bbe00e8f200
Files: 836
Total size: 20.7 MB

Directory structure:
gitextract_i5_to3kl/

├── .circleci/
│   └── config.yml
├── .dockerignore
├── .editorconfig
├── .eslintrc
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE.md
│   ├── copilot-instructions.md
│   ├── prompts/
│   │   └── tests.prompt.md
│   └── workflows/
│       └── codeql-analysis.yml
├── .gitignore
├── .grenrc.json
├── .nvmrc
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── CBOARD_SYMBOLS_INTEGRATION.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.txt
├── Makefile
├── README.md
├── __mocks__/
│   ├── browser-image-resizer.js
│   ├── react-redux.js
│   └── undici.js
├── browserstack.yml
├── cboard.njsproj.user
├── craco.config.js
├── env/
│   ├── local-private.gpg
│   ├── local.js
│   ├── prod-private.gpg
│   └── prod.js
├── funding.json
├── package.json
├── playwright.config.ts
├── public/
│   ├── .well-known/
│   │   └── assetlinks.json
│   ├── _redirects
│   ├── index.html
│   ├── manifest.json
│   └── ogv/
│       ├── ogv-decoder-audio-opus-wasm.js
│       ├── ogv-decoder-audio-opus-wasm.wasm
│       ├── ogv-demuxer-ogg-wasm.js
│       ├── ogv-demuxer-ogg-wasm.wasm
│       └── ogv-worker-audio.js
├── roadmap.md
├── rootfs/
│   └── etc/
│       └── nginx/
│           └── conf.d/
│               ├── default.conf
│               └── gzip.conf
├── scripts/
│   ├── arasaac-create-files.js
│   ├── arasaac-download-symbols.js
│   ├── crowdin-fetch-latest.js
│   ├── crowdin-push-changes.js
│   ├── decrypt-private.sh
│   └── encrypt-private.sh
├── src/
│   ├── __mocks__/
│   │   ├── axios.js
│   │   ├── react-intl.js
│   │   ├── store.js
│   │   └── styleMock.js
│   ├── __test__/
│   │   ├── helpers.test.js
│   │   ├── i18n.test.js
│   │   ├── reducers.test.js
│   │   ├── registerServiceWorker.test.js
│   │   └── store.test.js
│   ├── analytics.js
│   ├── api/
│   │   ├── __mocks__/
│   │   │   └── api.js
│   │   ├── api.js
│   │   ├── api.test.js
│   │   ├── boards.json
│   │   ├── cboard-symbols.js
│   │   ├── communicators.json
│   │   ├── corePicSeePal.json
│   │   ├── index.js
│   │   └── mulberry-symbols.json
│   ├── appInsights.js
│   ├── common/
│   │   └── test_utils.js
│   ├── components/
│   │   ├── Account/
│   │   │   ├── Activate/
│   │   │   │   ├── Activate.actions.js
│   │   │   │   ├── Activate.container.js
│   │   │   │   ├── Activate.css
│   │   │   │   ├── Activate.messages.js
│   │   │   │   └── index.js
│   │   │   ├── ChangePassword/
│   │   │   │   ├── ChangePassword.actions.js
│   │   │   │   ├── ChangePassword.component.js
│   │   │   │   ├── ChangePassword.constants.js
│   │   │   │   ├── ChangePassword.css
│   │   │   │   ├── ChangePassword.messages.js
│   │   │   │   ├── index.js
│   │   │   │   └── validationSchema.js
│   │   │   ├── Login/
│   │   │   │   ├── Login.actions.js
│   │   │   │   ├── Login.actions.test.js
│   │   │   │   ├── Login.component.js
│   │   │   │   ├── Login.component.test.js
│   │   │   │   ├── Login.constants.js
│   │   │   │   ├── Login.css
│   │   │   │   ├── Login.messages.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Login.component.test.js.snap
│   │   │   │   ├── index.js
│   │   │   │   └── validationSchema.js
│   │   │   ├── OAuthLogin/
│   │   │   │   ├── OAuthLogin.container.js
│   │   │   │   ├── OAuthLogin.css
│   │   │   │   ├── OAuthLogin.messages.js
│   │   │   │   └── index.js
│   │   │   ├── ResetPassword/
│   │   │   │   ├── ResetPassword.actions.js
│   │   │   │   ├── ResetPassword.component.js
│   │   │   │   ├── ResetPassword.constants.js
│   │   │   │   ├── ResetPassword.css
│   │   │   │   ├── ResetPassword.messages.js
│   │   │   │   ├── index.js
│   │   │   │   └── validationSchema.js
│   │   │   └── SignUp/
│   │   │       ├── SignUp.actions.js
│   │   │       ├── SignUp.component.js
│   │   │       ├── SignUp.css
│   │   │       ├── SignUp.messages.js
│   │   │       ├── Signup.component.test.js
│   │   │       ├── __snapshots__/
│   │   │       │   └── Signup.component.test.js.snap
│   │   │       ├── index.js
│   │   │       └── validationSchema.js
│   │   ├── Analytics/
│   │   │   ├── Analytics.component.js
│   │   │   ├── Analytics.container.js
│   │   │   ├── Analytics.css
│   │   │   ├── Analytics.messages.js
│   │   │   └── index.js
│   │   ├── App/
│   │   │   ├── App.actions.js
│   │   │   ├── App.component.js
│   │   │   ├── App.constants.js
│   │   │   ├── App.container.js
│   │   │   ├── App.css
│   │   │   ├── App.messages.js
│   │   │   ├── App.reducer.js
│   │   │   ├── App.selectors.js
│   │   │   ├── __tests__/
│   │   │   │   ├── App.actions.test.js
│   │   │   │   └── App.reducer.test.js
│   │   │   └── index.js
│   │   ├── AppLoading/
│   │   │   ├── AppLoading.css
│   │   │   ├── AppLoading.js
│   │   │   └── index.js
│   │   ├── AuthScreen/
│   │   │   ├── Auth.helpers.js
│   │   │   ├── AuthScreen.component.js
│   │   │   ├── AuthScreen.component.test.js
│   │   │   ├── AuthScreen.css
│   │   │   ├── AuthScreen.messages.js
│   │   │   ├── __snapshots__/
│   │   │   │   └── AuthScreen.component.test.js.snap
│   │   │   └── index.js
│   │   ├── Board/
│   │   │   ├── Board.actions.js
│   │   │   ├── Board.analytics.js
│   │   │   ├── Board.component.js
│   │   │   ├── Board.constants.js
│   │   │   ├── Board.container.js
│   │   │   ├── Board.css
│   │   │   ├── Board.messages.js
│   │   │   ├── Board.reducer.js
│   │   │   ├── BoardShare/
│   │   │   │   ├── BoardShare.component.js
│   │   │   │   ├── BoardShare.css
│   │   │   │   ├── BoardShare.messages.js
│   │   │   │   └── index.js
│   │   │   ├── BoardTour/
│   │   │   │   └── BoardTour.js
│   │   │   ├── EditToolbar/
│   │   │   │   ├── EditToolbar.component.js
│   │   │   │   ├── EditToolbar.css
│   │   │   │   ├── EditToolbar.messages.js
│   │   │   │   ├── EditToolbar.test.js
│   │   │   │   └── index.js
│   │   │   ├── EmptyBoard/
│   │   │   │   ├── EmptyBoard.component.js
│   │   │   │   ├── EmptyBoard.css
│   │   │   │   ├── EmptyBoard.messages.js
│   │   │   │   ├── EmptyBoard.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── EmptyBoard.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── ImageEditor/
│   │   │   │   ├── ImageEditor.component.js
│   │   │   │   ├── ImageEditor.css
│   │   │   │   ├── ImageEditor.messages.js
│   │   │   │   └── index.js
│   │   │   ├── ImprovePhraseOutput/
│   │   │   │   ├── ImprovePhraseOutput.js
│   │   │   │   ├── ImprovePhraseOutput.module.css
│   │   │   │   └── index.js
│   │   │   ├── Navbar/
│   │   │   │   ├── Navbar.css
│   │   │   │   ├── Navbar.js
│   │   │   │   ├── Navbar.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Navbar.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Output/
│   │   │   │   ├── Output.container.js
│   │   │   │   ├── PhraseShare/
│   │   │   │   │   ├── PhraseShare.component.js
│   │   │   │   │   ├── PhraseShare.css
│   │   │   │   │   ├── PhraseShare.messages.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── SymbolOutput/
│   │   │   │   │   ├── BackspaceButton/
│   │   │   │   │   │   ├── BackspaceButton.js
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── ClearButton/
│   │   │   │   │   │   ├── ClearButton.js
│   │   │   │   │   │   ├── ClearButton.test.js
│   │   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   │   └── ClearButton.test.js.snap
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Scroll/
│   │   │   │   │   │   ├── Scroll.css
│   │   │   │   │   │   ├── Scroll.js
│   │   │   │   │   │   ├── Scroll.test.js
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── ShareButton/
│   │   │   │   │   │   ├── ShareButton.js
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── SymbolOutput.css
│   │   │   │   │   ├── SymbolOutput.js
│   │   │   │   │   ├── SymbolOutput.test.js
│   │   │   │   │   └── index.js
│   │   │   │   └── index.js
│   │   │   ├── Symbol/
│   │   │   │   ├── Symbol.css
│   │   │   │   ├── Symbol.js
│   │   │   │   ├── Symbol.test.js
│   │   │   │   └── index.js
│   │   │   ├── SymbolSearch/
│   │   │   │   ├── SymbolNotFound/
│   │   │   │   │   ├── SymbolNotFound.component.js
│   │   │   │   │   ├── SymbolNotFound.css
│   │   │   │   │   ├── SymbolNotFound.messages.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── SymbolSearch.component.js
│   │   │   │   ├── SymbolSearch.component.test.js
│   │   │   │   ├── SymbolSearch.css
│   │   │   │   ├── SymbolSearch.messages.js
│   │   │   │   ├── SymbolSearchTour.component.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── SymbolSearch.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Tile/
│   │   │   │   ├── Tile.component.js
│   │   │   │   ├── Tile.css
│   │   │   │   ├── Tile.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Tile.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── TileEditor/
│   │   │   │   ├── LoadBoardEditor/
│   │   │   │   │   ├── LoadBoardEditor.js
│   │   │   │   │   ├── LoadBoardEditor.messages.js
│   │   │   │   │   ├── LoadBoardEditor.module.css
│   │   │   │   │   └── useAllBoardsFetcher.js
│   │   │   │   ├── LostedFolderForLoadBoardAlert/
│   │   │   │   │   ├── LostedFolderForLoadBoardAlert.tsx
│   │   │   │   │   └── index.ts
│   │   │   │   ├── TileEditor.component.js
│   │   │   │   ├── TileEditor.css
│   │   │   │   ├── TileEditor.messages.js
│   │   │   │   ├── TileEditor.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── TileEditor.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── __tests__/
│   │   │   │   ├── Board.actions.test.js
│   │   │   │   ├── Board.component.test.js
│   │   │   │   └── Board.reducer.test.js
│   │   │   └── index.js
│   │   ├── Communicator/
│   │   │   ├── Communicator.actions.js
│   │   │   ├── Communicator.constants.js
│   │   │   ├── Communicator.reducer.js
│   │   │   ├── CommunicatorDialog/
│   │   │   │   ├── CommunicatorDialog.component.js
│   │   │   │   ├── CommunicatorDialog.constants.js
│   │   │   │   ├── CommunicatorDialog.container.js
│   │   │   │   ├── CommunicatorDialog.css
│   │   │   │   ├── CommunicatorDialog.messages.js
│   │   │   │   ├── CommunicatorDialog.test.js
│   │   │   │   ├── CommunicatorDialogBoardItem.component.js
│   │   │   │   ├── CommunicatorDialogButtons.component.js
│   │   │   │   ├── CommunicatorDialogButtons.test.js
│   │   │   │   ├── CommunicatorDialogTour.component.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   ├── CommunicatorDialog.test.js.snap
│   │   │   │   │   └── CommunicatorDialogButtons.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── CommunicatorToolbar/
│   │   │   │   ├── CommunicatorToolbar.component.js
│   │   │   │   ├── CommunicatorToolbar.container.js
│   │   │   │   ├── CommunicatorToolbar.css
│   │   │   │   ├── CommunicatorToolbar.messages.js
│   │   │   │   ├── CommunicatorToolbar.test.js
│   │   │   │   ├── DefaultBoardSelector/
│   │   │   │   │   ├── DefaultBoardOption.js
│   │   │   │   │   ├── DefaultBoardSelector.js
│   │   │   │   │   ├── DefaultBoardSelector.module.css
│   │   │   │   │   ├── DefaultBoardsGallery.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── CommunicatorToolbar.test.js.snap
│   │   │   │   └── index.js
│   │   │   └── __tests__/
│   │   │       ├── Communicator.actions.test.js
│   │   │       └── Communicator.reducer.test.js
│   │   ├── EditGridButtons/
│   │   │   ├── EditGridButtons.component.js
│   │   │   ├── EditGridButtons.css
│   │   │   └── index.js
│   │   ├── FixedGrid/
│   │   │   ├── DraggableItem/
│   │   │   │   ├── DraggableItem.js
│   │   │   │   └── DraggableItem.module.css
│   │   │   ├── DroppableCell/
│   │   │   │   ├── DroppableCell.js
│   │   │   │   └── DroppableCell.module.css
│   │   │   ├── Grid.js
│   │   │   ├── Grid.module.css
│   │   │   ├── GridBase.js
│   │   │   ├── GridBase.module.css
│   │   │   ├── Row/
│   │   │   │   ├── Row.js
│   │   │   │   └── Row.module.css
│   │   │   ├── index.js
│   │   │   └── utils.ts
│   │   ├── Grid/
│   │   │   ├── Grid.constants.js
│   │   │   ├── Grid.container.js
│   │   │   ├── Grid.css
│   │   │   └── index.js
│   │   ├── LoggedInFeature/
│   │   │   ├── LoginRequiredModal.js
│   │   │   ├── LoginRequiredModal.messages.js
│   │   │   └── LoginRequiredModal.module.css
│   │   ├── NavigationButtons/
│   │   │   ├── NavigationButtons.component.js
│   │   │   ├── NavigationButtons.css
│   │   │   ├── NavigationButtons.test.js
│   │   │   ├── __snapshots__/
│   │   │   │   └── NavigationButtons.test.js.snap
│   │   │   └── index.js
│   │   ├── NotFound/
│   │   │   ├── NotFound.css
│   │   │   ├── NotFound.js
│   │   │   ├── __tests__/
│   │   │   │   ├── NotFound.test.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── NotFound.test.js.snap
│   │   │   └── index.js
│   │   ├── Notifications/
│   │   │   ├── Notifications.actions.js
│   │   │   ├── Notifications.component.js
│   │   │   ├── Notifications.constants.js
│   │   │   ├── Notifications.container.js
│   │   │   ├── Notifications.messages.js
│   │   │   ├── Notifications.reducer.js
│   │   │   ├── __tests__/
│   │   │   │   ├── Notifications.actions.test.js
│   │   │   │   ├── Notifications.component.test.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── Notifications.component.test.js.snap
│   │   │   └── index.js
│   │   ├── PremiumFeature/
│   │   │   ├── PremiumFeature.js
│   │   │   ├── PremiumFeature.messages.js
│   │   │   ├── PremiumRequiredModal.js
│   │   │   ├── PremiumRequiredModal.module.css
│   │   │   └── index.js
│   │   ├── ScrollButtons/
│   │   │   ├── ScrollButtons.js
│   │   │   └── index.js
│   │   ├── Settings/
│   │   │   ├── About/
│   │   │   │   ├── About.component.js
│   │   │   │   ├── About.css
│   │   │   │   ├── About.messages.js
│   │   │   │   ├── About.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── About.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Display/
│   │   │   │   ├── Display.component.js
│   │   │   │   ├── Display.constants.js
│   │   │   │   ├── Display.container.js
│   │   │   │   ├── Display.css
│   │   │   │   ├── Display.messages.js
│   │   │   │   ├── Display.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Display.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Export/
│   │   │   │   ├── Export.component.js
│   │   │   │   ├── Export.constants.js
│   │   │   │   ├── Export.container.js
│   │   │   │   ├── Export.css
│   │   │   │   ├── Export.helpers.js
│   │   │   │   ├── Export.messages.js
│   │   │   │   ├── Export.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Export.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Help/
│   │   │   │   ├── Help.component.js
│   │   │   │   ├── Help.css
│   │   │   │   └── index.js
│   │   │   ├── Import/
│   │   │   │   ├── Import.component.js
│   │   │   │   ├── Import.constants.js
│   │   │   │   ├── Import.container.js
│   │   │   │   ├── Import.css
│   │   │   │   ├── Import.helpers.js
│   │   │   │   ├── Import.messages.js
│   │   │   │   ├── Import.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Import.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Language/
│   │   │   │   ├── DownloadDialog.js
│   │   │   │   ├── Language.component.js
│   │   │   │   ├── Language.container.js
│   │   │   │   ├── Language.messages.js
│   │   │   │   ├── downloadablesTts.json
│   │   │   │   ├── downloadingLangErrorDialog.js
│   │   │   │   └── index.js
│   │   │   ├── Navigation/
│   │   │   │   ├── Navigation.component.js
│   │   │   │   ├── Navigation.constants.js
│   │   │   │   ├── Navigation.container.js
│   │   │   │   ├── Navigation.css
│   │   │   │   ├── Navigation.messages.js
│   │   │   │   ├── Navigation.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Navigation.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── People/
│   │   │   │   ├── DeleteConfirmationDialog.js
│   │   │   │   ├── People.component.js
│   │   │   │   ├── People.component.test.js
│   │   │   │   ├── People.container.js
│   │   │   │   ├── People.messages.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── People.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Scanning/
│   │   │   │   ├── Scanning.component.js
│   │   │   │   ├── Scanning.constants.js
│   │   │   │   ├── Scanning.container.js
│   │   │   │   ├── Scanning.css
│   │   │   │   ├── Scanning.messages.js
│   │   │   │   ├── Scanning.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Scanning.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Settings.component.js
│   │   │   ├── Settings.container.js
│   │   │   ├── Settings.css
│   │   │   ├── Settings.messages.js
│   │   │   ├── Settings.wrapper.js
│   │   │   ├── SettingsSection.component.js
│   │   │   ├── SettingsTour.component.js
│   │   │   ├── Speech/
│   │   │   │   ├── Speech.component.js
│   │   │   │   ├── Speech.component.test.js
│   │   │   │   ├── Speech.constants.js
│   │   │   │   ├── Speech.container.js
│   │   │   │   ├── Speech.css
│   │   │   │   ├── Speech.messages.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Speech.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Subscribe/
│   │   │   │   ├── Subscribe.component.js
│   │   │   │   ├── Subscribe.component.test.js
│   │   │   │   ├── Subscribe.constants.js
│   │   │   │   ├── Subscribe.container.js
│   │   │   │   ├── Subscribe.css
│   │   │   │   ├── Subscribe.helpers.js
│   │   │   │   ├── Subscribe.messages.js
│   │   │   │   ├── SubscriptionInfo.js
│   │   │   │   ├── SubscriptionPlans.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Subscribe.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Symbols/
│   │   │   │   ├── DeleteArasaacDialog.js
│   │   │   │   ├── DownloadArasaacDialog.js
│   │   │   │   ├── NoConnectionDialog.js
│   │   │   │   ├── Symbols.component.js
│   │   │   │   ├── Symbols.container.js
│   │   │   │   ├── Symbols.css
│   │   │   │   ├── Symbols.messages.js
│   │   │   │   └── index.js
│   │   │   └── index.js
│   │   ├── UI/
│   │   │   ├── AnalyticsButton/
│   │   │   │   ├── AnalyticsButton.js
│   │   │   │   ├── AnalyticsButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── AnalyticsButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── BackButton/
│   │   │   │   ├── BackButton.js
│   │   │   │   ├── BackButton.messages.js
│   │   │   │   ├── BackButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── BackButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Barchart/
│   │   │   │   ├── Barchart.component.js
│   │   │   │   ├── Barchart.css
│   │   │   │   └── index.js
│   │   │   ├── ColorSelect/
│   │   │   │   ├── Circle/
│   │   │   │   │   ├── Circle.js
│   │   │   │   │   ├── Circle.test.js
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   └── Circle.test.js.snap
│   │   │   │   │   └── index.js
│   │   │   │   ├── ColorSelect.js
│   │   │   │   ├── ColorSelect.messages.js
│   │   │   │   ├── ColorSelect.test.js
│   │   │   │   ├── ColorSelectDropdown.css
│   │   │   │   ├── HairColor.messages.js
│   │   │   │   ├── HairColorSelect.js
│   │   │   │   ├── HairColorSelect.test.js
│   │   │   │   ├── SkinTone.messages.js
│   │   │   │   ├── SkinToneSelect.js
│   │   │   │   ├── SkinToneSelect.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   ├── ColorSelect.test.js.snap
│   │   │   │   │   ├── HairColorSelect.test.js.snap
│   │   │   │   │   └── SkinToneSelect.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Doughnut/
│   │   │   │   ├── Doughnut.component.js
│   │   │   │   ├── Doughnut.css
│   │   │   │   └── index.js
│   │   │   ├── Downloader/
│   │   │   │   ├── Downloader.css
│   │   │   │   ├── Downloader.js
│   │   │   │   ├── Downloader.messages.js
│   │   │   │   └── index.js
│   │   │   ├── FilterBar/
│   │   │   │   ├── FilterBar.css
│   │   │   │   ├── FilterBar.js
│   │   │   │   ├── FilterBar.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FilterBar.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FormDialog/
│   │   │   │   ├── FormDialog.css
│   │   │   │   ├── FormDialog.js
│   │   │   │   ├── FormDialog.messages.js
│   │   │   │   ├── FormDialog.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FormDialog.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FormItems/
│   │   │   │   ├── ApiKeyTextField.js
│   │   │   │   ├── FormItems.messages.js
│   │   │   │   ├── PasswordTextField.js
│   │   │   │   ├── TextField.js
│   │   │   │   ├── TextField.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── TextField.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FullScreenButton/
│   │   │   │   ├── FullScreenButton.js
│   │   │   │   ├── FullScreenButton.messages.js
│   │   │   │   ├── FullScreenButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FullScreenButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FullScreenDialog/
│   │   │   │   ├── FullScreenDialog.css
│   │   │   │   ├── FullScreenDialog.js
│   │   │   │   ├── FullScreenDialog.test.js
│   │   │   │   ├── FullScreenDialogContent.js
│   │   │   │   ├── FullScreenDialogContent.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FullScreenDialog.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── HelpButton/
│   │   │   │   ├── HelpButton.js
│   │   │   │   └── index.js
│   │   │   ├── IconButton/
│   │   │   │   ├── IconButton.js
│   │   │   │   ├── IconButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── IconButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── InputImage/
│   │   │   │   ├── InputImage.component.js
│   │   │   │   ├── InputImage.css
│   │   │   │   ├── InputImage.messages.js
│   │   │   │   ├── InputImage.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── InputImage.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── LoadingIcon/
│   │   │   │   ├── LoadingIcon.css
│   │   │   │   ├── LoadingIcon.js
│   │   │   │   ├── LoadingIcon.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── LoadingIcon.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── LockToggle/
│   │   │   │   ├── LockToggle.js
│   │   │   │   ├── LockToggle.messages.js
│   │   │   │   ├── __test__/
│   │   │   │   │   ├── LockToggle.test.js
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   ├── LockToggle.test.js.snap
│   │   │   │   │   │   └── childProof.test.js.snap
│   │   │   │   │   └── childProof.test.js
│   │   │   │   ├── childProof.js
│   │   │   │   └── index.js
│   │   │   ├── ModifiedAreaChart/
│   │   │   │   ├── ModifiedAreaChart.component.js
│   │   │   │   └── index.js
│   │   │   ├── PrintBoardButton/
│   │   │   │   ├── PrintBoardButton.component.js
│   │   │   │   ├── PrintBoardButton.container.js
│   │   │   │   ├── PrintBoardButton.messages.js
│   │   │   │   ├── PrintBoardButton.test.js
│   │   │   │   ├── PrintBoardDialog.component.js
│   │   │   │   ├── PrintBoardDialog.css
│   │   │   │   ├── PrintBoardDialog.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   ├── PrintBoardButton.test.js.snap
│   │   │   │   │   └── PrintBoardDialog.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── ResetToursItem/
│   │   │   │   ├── ResetToursItem.component.js
│   │   │   │   ├── ResetToursItem.container.js
│   │   │   │   ├── ResetToursItem.messages.js
│   │   │   │   └── index.js
│   │   │   ├── SelectedCounter/
│   │   │   │   ├── SelectedCounter.component.js
│   │   │   │   ├── SelectedCounter.css
│   │   │   │   ├── SelectedCounter.messages.js
│   │   │   │   ├── SelectedCounter.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── SelectedCounter.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── SettingsButton/
│   │   │   │   ├── SettingsButton.js
│   │   │   │   ├── SettingsButton.messages.js
│   │   │   │   ├── SettingsButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── SettingsButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── StatCards/
│   │   │   │   ├── StatCards.component.js
│   │   │   │   ├── StatCards.css
│   │   │   │   └── index.js
│   │   │   ├── StatCards2/
│   │   │   │   ├── StatCards2.component.js
│   │   │   │   ├── StatCards2.css
│   │   │   │   └── index.js
│   │   │   ├── StyledTable/
│   │   │   │   ├── StyledTable.component.js
│   │   │   │   ├── StyledTable.css
│   │   │   │   └── index.js
│   │   │   ├── TableCard/
│   │   │   │   ├── TableCard.component.js
│   │   │   │   ├── TableCard.css
│   │   │   │   └── index.js
│   │   │   └── UserIcon/
│   │   │       ├── UserIcon.component.js
│   │   │       ├── UserIcon.container.js
│   │   │       ├── UserIcon.messages.js
│   │   │       ├── UserIcon.test.js
│   │   │       ├── __snapshots__/
│   │   │       │   └── UserIcon.test.js.snap
│   │   │       └── index.js
│   │   ├── VoiceRecorder/
│   │   │   ├── VoiceRecorder.component.js
│   │   │   ├── VoiceRecorder.container.js
│   │   │   ├── VoiceRecorder.css
│   │   │   ├── VoiceRecorder.messages.js
│   │   │   ├── __test__/
│   │   │   │   ├── VoiceRecorder.component.test.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── VoiceRecorder.component.test.js.snap
│   │   │   └── index.js
│   │   └── WelcomeScreen/
│   │       ├── CboardLogo/
│   │       │   ├── CboardLogo.component.js
│   │       │   ├── CboardLogo.css
│   │       │   ├── CboardLogo.test.js
│   │       │   ├── __snapshots__/
│   │       │   │   └── CboardLogo.test.js.snap
│   │       │   └── index.js
│   │       ├── WelcomeScreen.container.js
│   │       ├── WelcomeScreen.css
│   │       ├── WelcomeScreen.messages.js
│   │       ├── WelcomeScreen.test.js
│   │       └── index.js
│   ├── config.js
│   ├── constants.js
│   ├── cordova-util.js
│   ├── env.js
│   ├── helpers.js
│   ├── history.js
│   ├── i18n.js
│   ├── idb/
│   │   └── arasaac/
│   │       ├── arasaacdb.ts
│   │       └── jszip.ts
│   ├── index.css
│   ├── index.js
│   ├── polyfills.js
│   ├── providers/
│   │   ├── LanguageProvider/
│   │   │   ├── LanguageProvider.actions.js
│   │   │   ├── LanguageProvider.constants.js
│   │   │   ├── LanguageProvider.container.js
│   │   │   ├── LanguageProvider.reducer.js
│   │   │   ├── __tests__/
│   │   │   │   ├── LanguageProvider.actions.test.js
│   │   │   │   ├── LanguageProvider.reducer.test.js
│   │   │   │   └── LanguageProvider.test.js
│   │   │   └── index.js
│   │   ├── ScannerProvider/
│   │   │   ├── ScannerProvider.actions.js
│   │   │   ├── ScannerProvider.constants.js
│   │   │   ├── ScannerProvider.reducer.js
│   │   │   └── __tests__/
│   │   │       ├── ScannerProvider.actions.test.js
│   │   │       └── ScannerProvider.reducer.test.js
│   │   ├── SpeechProvider/
│   │   │   ├── SpeechProvider.actions.js
│   │   │   ├── SpeechProvider.analytics.js
│   │   │   ├── SpeechProvider.constants.js
│   │   │   ├── SpeechProvider.container.js
│   │   │   ├── SpeechProvider.messages.js
│   │   │   ├── SpeechProvider.reducer.js
│   │   │   ├── __tests__/
│   │   │   │   ├── SpeechProvider.actions.test.js
│   │   │   │   └── SpeechProvider.reducer.test.js
│   │   │   ├── engine/
│   │   │   │   └── elevenlabs.js
│   │   │   ├── index.js
│   │   │   └── tts.js
│   │   ├── SubscriptionProvider/
│   │   │   ├── SubscriptionProvider.actions.js
│   │   │   ├── SubscriptionProvider.constants.js
│   │   │   ├── SubscriptionProvider.container.js
│   │   │   ├── SubscriptionProvider.reducer.js
│   │   │   ├── SubscriptionProvider.selectors.js
│   │   │   └── index.js
│   │   └── ThemeProvider/
│   │       ├── RTLSupport.js
│   │       ├── ThemeProvider.constants.js
│   │       ├── ThemeProvider.container.js
│   │       └── index.js
│   ├── react-app-env.d.ts
│   ├── reducers.js
│   ├── registerServiceWorker.js
│   ├── setupPolyfills.js
│   ├── setupTests.js
│   ├── store.js
│   ├── translations/
│   │   ├── ar-EG.json
│   │   ├── ar-SA.json
│   │   ├── be-BY.json
│   │   ├── bg-BG.json
│   │   ├── bn-BD.json
│   │   ├── ca-ES.json
│   │   ├── cs-CZ.json
│   │   ├── da-DK.json
│   │   ├── de-DE.json
│   │   ├── el-GR.json
│   │   ├── en-GB.json
│   │   ├── en-US.json
│   │   ├── es-ES.json
│   │   ├── fa-IR.json
│   │   ├── fi-FI.json
│   │   ├── fr-FR.json
│   │   ├── he-IL.json
│   │   ├── help/
│   │   │   ├── ar-SA.md
│   │   │   ├── be-BY.md
│   │   │   ├── bg-BG.md
│   │   │   ├── bn-BD.md
│   │   │   ├── ca-ES.md
│   │   │   ├── cs-CZ.md
│   │   │   ├── da-DK.md
│   │   │   ├── de-DE.md
│   │   │   ├── el-GR.md
│   │   │   ├── en-GB.md
│   │   │   ├── en-US.md
│   │   │   ├── es-ES.md
│   │   │   ├── fa-IR.md
│   │   │   ├── fi-FI.md
│   │   │   ├── fr-FR.md
│   │   │   ├── he-IL.md
│   │   │   ├── hi-IN.md
│   │   │   ├── hr-HR.md
│   │   │   ├── hu-HU.md
│   │   │   ├── id-ID.md
│   │   │   ├── it-IT.md
│   │   │   ├── ja-JP.md
│   │   │   ├── km-KH.md
│   │   │   ├── ko-KR.md
│   │   │   ├── me-ME.md
│   │   │   ├── mk-MK.md
│   │   │   ├── ml-IN.md
│   │   │   ├── nb-NO.md
│   │   │   ├── ne-NP.md
│   │   │   ├── nl-NL.md
│   │   │   ├── no-NO.md
│   │   │   ├── pl-PL.md
│   │   │   ├── pt-BR.md
│   │   │   ├── pt-PT.md
│   │   │   ├── pt-TL.md
│   │   │   ├── ro-RO.md
│   │   │   ├── ru-MD.md
│   │   │   ├── ru-RU.md
│   │   │   ├── sh-HR.md
│   │   │   ├── si-LK.md
│   │   │   ├── sk-SK.md
│   │   │   ├── so-SO.md
│   │   │   ├── sq-AL.md
│   │   │   ├── sq-MK.md
│   │   │   ├── sr-CS.md
│   │   │   ├── sr-ME.md
│   │   │   ├── sr-RS.md
│   │   │   ├── sr-SP.md
│   │   │   ├── sv-SE.md
│   │   │   ├── tg-TJ.md
│   │   │   ├── th-TH.md
│   │   │   ├── tk-TM.md
│   │   │   ├── tr-TR.md
│   │   │   ├── tu-TI.md
│   │   │   ├── uk-UA.md
│   │   │   ├── ur-PK.md
│   │   │   ├── vi-VN.md
│   │   │   ├── zh-CN.md
│   │   │   └── zu-ZA.md
│   │   ├── hi-IN.json
│   │   ├── hr-HR.json
│   │   ├── hu-HU.json
│   │   ├── id-ID.json
│   │   ├── ig-NG.json
│   │   ├── it-IT.json
│   │   ├── ja-JP.json
│   │   ├── ka-GE.json
│   │   ├── km-KH.json
│   │   ├── ko-KR.json
│   │   ├── lo-LA.json
│   │   ├── me-ME.json
│   │   ├── mk-MK.json
│   │   ├── ml-IN.json
│   │   ├── moreLanguages/
│   │   │   ├── ar-SA.md
│   │   │   ├── be-BY.md
│   │   │   ├── bg-BG.md
│   │   │   ├── bn-BD.md
│   │   │   ├── ca-ES.md
│   │   │   ├── cs-CZ.md
│   │   │   ├── da-DK.md
│   │   │   ├── de-DE.md
│   │   │   ├── el-GR.md
│   │   │   ├── en-GB.md
│   │   │   ├── en-US.md
│   │   │   ├── es-ES.md
│   │   │   ├── fa-IR.md
│   │   │   ├── fi-FI.md
│   │   │   ├── fr-FR.md
│   │   │   ├── he-IL.md
│   │   │   ├── hi-IN.md
│   │   │   ├── hr-HR.md
│   │   │   ├── hu-HU.md
│   │   │   ├── id-ID.md
│   │   │   ├── it-IT.md
│   │   │   ├── ja-JP.md
│   │   │   ├── km-KH.md
│   │   │   ├── ko-KR.md
│   │   │   ├── lo-LA.md
│   │   │   ├── me-ME.md
│   │   │   ├── mk-MK.md
│   │   │   ├── ml-IN.md
│   │   │   ├── ne-NP.md
│   │   │   ├── nl-NL.md
│   │   │   ├── no-NO.md
│   │   │   ├── pl-PL.md
│   │   │   ├── pt-BR.md
│   │   │   ├── pt-PT.md
│   │   │   ├── ro-RO.md
│   │   │   ├── ru-MD.md
│   │   │   ├── ru-RU.md
│   │   │   ├── sh-HR.md
│   │   │   ├── si-LK.md
│   │   │   ├── sk-SK.md
│   │   │   ├── so-SO.md
│   │   │   ├── sq-AL.md
│   │   │   ├── sq-MK.md
│   │   │   ├── sr-CS.md
│   │   │   ├── sr-SP.md
│   │   │   ├── sv-SE.md
│   │   │   ├── tg-TJ.md
│   │   │   ├── th-TH.md
│   │   │   ├── tk-TM.md
│   │   │   ├── tr-TR.md
│   │   │   ├── tu-TI.md
│   │   │   ├── uk-UA.md
│   │   │   ├── ur-PK.md
│   │   │   ├── vi-VN.md
│   │   │   ├── zh-CN.md
│   │   │   └── zu-ZA.md
│   │   ├── mr-IN.json
│   │   ├── ms-MY.json
│   │   ├── my-MM.json
│   │   ├── nb-NO.json
│   │   ├── ne-NP.json
│   │   ├── nl-NL.json
│   │   ├── no-NO.json
│   │   ├── pl-PL.json
│   │   ├── pt-BR.json
│   │   ├── pt-PT.json
│   │   ├── pt-TL.json
│   │   ├── ro-RO.json
│   │   ├── ru-MD.json
│   │   ├── ru-RU.json
│   │   ├── sh-HR.json
│   │   ├── si-LK.json
│   │   ├── sk-SK.json
│   │   ├── so-SO.json
│   │   ├── sq-AL.json
│   │   ├── sq-MK.json
│   │   ├── sr-CS.json
│   │   ├── sr-ME.json
│   │   ├── sr-RS.json
│   │   ├── sr-SP.json
│   │   ├── src/
│   │   │   └── cboard.json
│   │   ├── sv-SE.json
│   │   ├── ta-IN.json
│   │   ├── tg-TJ.json
│   │   ├── th-TH.json
│   │   ├── tk-TM.json
│   │   ├── tr-TR.json
│   │   ├── tu-TI.json
│   │   ├── uk-UA.json
│   │   ├── ur-PK.json
│   │   ├── vi-VN.json
│   │   ├── zh-CN.json
│   │   └── zu-ZA.json
│   ├── types.ts
│   └── vfs_fonts.js
├── sw-precache-config.js
├── tests/
│   ├── README.md
│   ├── helpers/
│   │   ├── communication-utils.js
│   │   ├── index.js
│   │   ├── navigation-utils.js
│   │   └── overlay-utils.js
│   ├── logged/
│   │   ├── authentication.spec.js
│   │   └── settings/
│   │       └── speech.spec.js
│   ├── page-objects/
│   │   ├── cboard.js
│   │   └── index.js
│   ├── smoke.spec.js
│   ├── unlogged/
│   │   ├── accessibility.spec.js
│   │   ├── basic-functionality.spec.js
│   │   ├── communication-bar.spec.js
│   │   ├── mobile-responsiveness.spec.js
│   │   ├── navigation.spec.js
│   │   ├── public-board-report-restriction.spec.js
│   │   ├── settings/
│   │   │   ├── display.spec.js
│   │   │   ├── export.spec.js
│   │   │   ├── import.spec.js
│   │   │   ├── language.spec.js
│   │   │   ├── scanning.spec.js
│   │   │   └── symbols.spec.js
│   │   └── unlock.spec.js
│   └── utilities/
│       ├── assertions.js
│       ├── index.js
│       ├── test-setup.js
│       └── test-utils.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .circleci/config.yml
================================================
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2.1
orbs:
  discord: antonioned/discord@0.1.0
jobs:
  build:
    docker:
      # specify the version you desire here
      - image: cimg/node:22.14.0
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
            # when lock file changes, use increasingly general patterns to restore cache
            - yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - yarn-packages-v1-{{ .Branch }}-
            - yarn-packages-v1-
      # Install and save node_modules to cache
      - run: yarn install --cache-folder ~/.cache/yarn
      - save_cache:
          paths:
            - ~/.cache/yarn
          key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
      # run tests
      - run:
          name: Run Unit Tests
          command: yarn test --silent --updateSnapshot --runInBand --no-watchman --testPathPattern="/Board|Communicator|App|Settings|Account|UI/"
  image:
    docker:
      # specify the version you desire here
      - image: cimg/node:22.14.0
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
            # when lock file changes, use increasingly general patterns to restore cache
            - yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - yarn-packages-v1-{{ .Branch }}-
            - yarn-packages-v1-
      # Install and save node_modules to cache
      - run: yarn install --cache-folder ~/.cache/yarn
      - save_cache:
          paths:
            - ~/.cache/yarn
          key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
      # Build Docker Image
      # https://circleci.com/docs/2.0/building-docker-images/#overview
      - setup_remote_docker: # (2)
          version: default
          docker_layer_caching: false # (3)
      # build and push Docker image
      - run:
          name: Build Docker Image
          command: |
            TAG=0.1.$CIRCLE_BUILD_NUM
            echo $TAG > ./public/version
            docker build -t cboard/cboard:$TAG -t cboard/cboard:latest .
            docker login -u $DOCKER_USER -p $DOCKER_PASS
            docker push cboard/cboard:latest
            docker push cboard/cboard:$TAG
  deploy-qa:
    machine:
      image: ubuntu-2204:2024.11.1
    steps:
      - run:
          name: Deploy Over SSH
          command: |
            echo 'cboard-qa06.westus.cloudapp.azure.com \
            AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK9FTOTcgK0KgvdqdUhPLfvnDhzbViGnvtaXWg1XKgq2LEzgUTaHwyDsV5lZ3NnXimwptzF6GvO3u1ySlR2C19s=' >> ~/.ssh/known_hosts
            ssh $SSH_USERNAME@$SSH_SERVER 'bash -ic \
            "docker image prune -f && \
            docker run --rm -v /var/run/docker.sock:/var/run/docker.sock cboard/cboard-bootstrap pull && \
            docker run --rm -v /var/run/docker.sock:/var/run/docker.sock cboard/cboard-bootstrap stop cboard && \
            docker run --rm -v /var/run/docker.sock:/var/run/docker.sock cboard/cboard-bootstrap kill cboard && \
            docker run --rm -v /var/run/docker.sock:/var/run/docker.sock --env-file /home/sharedFolder/env.qa cboard/cboard-bootstrap up -d --no-deps cboard \
            " && exit'
      - discord/status:
          fail_only: false
          failure_message: "**${CIRCLE_USERNAME}**'s Cboard deploy to QA: **${CIRCLE_JOB}** failed."
          webhook: '${DISCORD_STATUS_WEBHOOK}'
          success_message: '**${CIRCLE_USERNAME}** deployed Cboard to QA!!.'
  e2e-qa:
    docker:
      - image: mcr.microsoft.com/playwright:v1.53.0-jammy
    working_directory: ~/repo
    steps:
      - checkout
      # Download and cache dependencies
      - restore_cache:
          keys:
            # when lock file changes, use increasingly general patterns to restore cache
            - yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - yarn-packages-v1-{{ .Branch }}-
            - yarn-packages-v1-
      # Install and save node_modules to cache
      - run:
          name: Install dependencies
          command: yarn install --cache-folder ~/.cache/yarn
      - save_cache:
          paths:
            - ~/.cache/yarn
          key: yarn-packages-v1-{{ .Branch }}-{{ checksum "yarn.lock" }}
      # run tests
      - run:
          name: Run E2E Tests
          command: |
            yarn test:e2e-browserstack
          when: always
      - discord/status:
          fail_only: false
          failure_message: '**${CIRCLE_USERNAME}** Cboard E2E Tests FAILED: **${CIRCLE_JOB}** failed.'
          webhook: '${DISCORD_STATUS_WEBHOOK}'
          success_message: '**${CIRCLE_USERNAME}** Cboard E2E Tests PASSED on QA!!.'
      # Store test results for CircleCI test insights
      - store_test_results:
          path: test-results
      # Store Playwright HTML report
      - store_artifacts:
          path: ./playwright-report
          destination: playwright-report
      # Store test results XML files
      - store_artifacts:
          path: ./test-results
          destination: test-results
workflows:
  build_image_deploy_e2e-qa:
    jobs:
      - build:
          context: azure
      - image:
          context: azure
          requires:
            - build
          filters:
            branches:
              only: master
      - deploy-qa:
          context: azure
          requires:
            - image
          filters:
            branches:
              only: master
      - e2e-qa:
          context: azure
          requires:
            - deploy-qa
          filters:
            branches:
              only: master


================================================
FILE: .dockerignore
================================================
.git
.idea
node_modules
build

================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .eslintrc
================================================
{
  "extends": "react-app"
}

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

open_collective: cboard


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!-- Love cboard? Please consider supporting our collective:
👉  https://opencollective.com/cboard/donate -->

================================================
FILE: .github/copilot-instructions.md
================================================
# Copilot Instructions for Cboard

## Project Overview
Cboard is an augmentative and alternative communication (AAC) web application that helps users with speech and language impairments communicate using symbols and text-to-speech.

## Technological Stack
- React 17
- Material UI v4
- Redux for state management with redux-thunk
- React-Intl for internationalization
- Jest and Enzyme for testing
- Formik and Yup for form handling and validation


================================================
FILE: .github/prompts/tests.prompt.md
================================================
---
mode: 'agent'
description: 'testing the Cboard web app'
---
You are a playwright test generator. Ensure the Cboard web application is fully tested.
- Use Playwrights best practices to generate tests for the site. This includes role 
 locators and Playwrights auto waiting assertions such as expect locator toHaveText, 
 toHaveCount etc. Use the .filter() method to avoid strict mode violations when needed.
- Use the Playwright MCP server to navigate to the Cboard web application site at 
https://app.qa.cboard.io and generate tests based on the current functionality of the app.
 Do not generate tests based on assumptions instead first use the app like a user
  would and manually test the site and then generate tests based on what you have 
  manually tested.
- Use  javaScript as the programming language for the tests.
- Use the Playwright test runner to run the tests.
- Create a page object for locators and then replace the page locators in the test specs.
- Page objects and any helper utilities must be inside descriptive folders.
- include desktop, mobile, and tablet sizes for playwright configuration.

================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]
  schedule:
    - cron: '21 11 * * 0'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'javascript' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://git.io/codeql-language-support

    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2


================================================
FILE: .gitignore
================================================
# See https://help.github.com/ignore-files/ for more about ignoring files.

# Secrets
.private/

# dependencies
/node_modules

# testing
/coverage

# production
/build

# object code
obj/

# vscode
.vscode/chrome

# phpstorm/webstorm/idea
.idea

# netbeans
nbproject/

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

# yarn 
npm-debug.log*
yarn-debug.log*
yarn-error.log*

.env

# visual studio
*.njsproj
*.sln
/.vs

# Playwright
/log/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/tests/test-results/
/tests/playwright-report/
/tests/blob-report/
/tests/playwright/.cache/


================================================
FILE: .grenrc.json
================================================
{
  "dataSource": "milestones",
  "groupBy": {
    "New Features": ["enhancement", "feature", "internal"],
    "Bug Fixes:": ["bug"]
  },
  "changelogFilename": "CHANGELOG.md"
}


================================================
FILE: .nvmrc
================================================
v22.14.0


================================================
FILE: .prettierrc
================================================
{
  "printWidth": 80,
  "singleQuote": true
}


================================================
FILE: .vscode/extensions.json
================================================
{
  // See http://go.microsoft.com/fwlink/?LinkId=827846
  // for the documentation about the extensions.json format
  "recommendations": [
    // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode"
  ]
}


================================================
FILE: .vscode/launch.json
================================================
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "program": "${file}",
      "env": {
        "CBOARD_ENV": "local"
      }
    },
    {
      "name": "Chrome",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceRoot}/src",
      "userDataDir": "${workspaceRoot}/.vscode/chrome",
      "sourceMapPathOverrides": {
        "webpack:///src/*": "${webRoot}/*"
      }
    }
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.formatOnSave": true,
  "chat.tools.terminal.autoApprove": {
    "/^yarn test --watchAll=false --forceExit --testPathPattern=\"/Board\\|Communicator\\|App\\|Settings\\|Account\\|UI/\" 2>&1$/": {
      "approve": true,
      "matchCommandLine": true
    },
    "/^yarn test --watchAll=false --forceExit --updateSnapshot --testPathPattern=\"/Board\\|Communicator\\|App\\|Settings\\|Account\\|UI/\" 2>&1 \\| Select-Object -Last 15$/": {
      "approve": true,
      "matchCommandLine": true
    }
  }
}


================================================
FILE: CBOARD_SYMBOLS_INTEGRATION.md
================================================
# Cboard Symbols Integration Guide

This document explains how to set up and use the Cboard Symbols integration in the main Cboard application.

## Overview

Cboard Symbols is a curated symbol library from the cboard-ai-builder project that has been integrated as a **4th symbol provider** alongside:
- Mulberry (local)
- Global Symbols (API)
- ARASAAC (hybrid with IndexedDB)

## Features

✅ **Search Cboard Symbols** via production API at `https://cbuilder.cboard.io`  
✅ **Skin Tone Support** - Syncs automatically with ARASAAC skin tone selection  
✅ **6 Skin Tone Variants**: emoji, light, medium-light, medium, medium-dark, dark  
✅ **Internationalized Search** - Supports all Cboard languages  
✅ **Enabled by Default** - Available to all users immediately  
✅ **Graceful Degradation** - Falls back silently if API unavailable  

## Setup Instructions

### 1. Generate API Key

Generate a secure API key that will be shared between both applications:

```bash
openssl rand -base64 32
```

Example output:
```
aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890ABCD==
```

### 2. Configure Backend (cboard-ai-builder)

Add the API key to `/cboard-ai-builder/.env`:

```env
# API Key for Cboard main app to access Cboard Symbols
CBOARD_API_KEY=aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890ABCD==
```

### 3. Configure Frontend (cboard)

Add the **same** API key to `/cboard/.env`:

```env
# Cboard Symbols API Key (must match backend)
REACT_APP_CBOARD_SYMBOLS_API_KEY=aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890ABCD==
```

### 4. Restart Both Applications

```bash
# In cboard-ai-builder
npm run dev

# In cboard
npm start
```

## How It Works

### Architecture

```
User types in SymbolSearch
         ↓
  Debounced (300ms)
         ↓
  getSuggestions()
         ↓
  ┌──────┴──────┬──────────┬────────────┐
  ↓             ↓          ↓            ↓
Mulberry    ARASAAC   Global       Cboard
(local)  (IndexedDB) Symbols      Symbols
                        ↓            ↓
                    External     Production
                      API           API
                                    ↓
                         https://cbuilder.cboard.io
                                    ↓
                           [API Key Check]
                                    ↓
                            MongoDB Query
                                    ↓
                         Results with Variants
```

### API Request Flow

1. User types "house" in SymbolSearch
2. After 300ms debounce, `fetchCboardSymbolsSuggestions('house')` is called
3. API client sends request:
   ```
   GET https://cbuilder.cboard.io/api/cboard-symbols/pictograms/en/search/house
   Headers: X-API-Key: <your-api-key>
   ```
4. Backend validates API key
5. MongoDB query searches for "house" in English translations
6. Returns array of pictograms with all skin tone variants
7. Frontend selects appropriate variant based on user's skin tone preference
8. Results displayed in grid layout

### Skin Tone Synchronization

The integration automatically syncs skin tone between ARASAAC and Cboard Symbols:

| ARASAAC Value | Cboard Symbols Value |
|---------------|---------------------|
| `white`       | `skin_light`        |
| `black`       | `skin_dark`         |
| `mulatto`     | `skin_medium`       |
| `asian`       | `skin_medium_light` |
| `aztec`       | `skin_medium_dark`  |
| (default)     | `skin_emoji`        |

When a user changes the skin tone slider, both ARASAAC and Cboard Symbols results update automatically.

## Testing

### Manual Testing Checklist

- [ ] Open Tile Editor and click Search button
- [ ] Verify 4 providers appear in FilterBar (Mulberry, Global Symbols, ARASAAC, Cboard Symbols)
- [ ] Type "house" and verify results appear from Cboard Symbols
- [ ] Toggle Cboard Symbols on/off and verify results update
- [ ] Change skin tone and verify Cboard Symbols update
- [ ] Select a Cboard Symbol and verify it saves to tile
- [ ] Test in multiple languages (en, es, pt, fr)
- [ ] Test with slow network (results should still appear)
- [ ] Test with API key removed (should fail silently, no crash)

### Running Unit Tests

```bash
cd cboard
npm test -- SymbolSearch.component.test.js
```

Expected output:
```
PASS  src/components/Board/SymbolSearch/SymbolSearch.component.test.js
  SymbolSearch tests
    ✓ default renderer
    ✓ includes Cboard Symbols in symbolSets
    ✓ has isFetchingCboardSymbols in state
    ✓ fetchCboardSymbolsSuggestions calls API with correct params
    ✓ fetchCboardSymbolsSuggestions transforms API response correctly
    ✓ showInclusivityOptions is true when ARASAAC or Cboard Symbols enabled
    ✓ skin tone syncs between ARASAAC and Cboard Symbols
    ✓ getSuggestions calls fetchCboardSymbolsSuggestions when enabled
    ✓ getSuggestions does not call fetchCboardSymbolsSuggestions when disabled
```

### Testing API Endpoint

```bash
# Replace with your actual API key
API_KEY="aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890ABCD=="

# Test English search
curl -H "X-API-Key: $API_KEY" \
  https://cbuilder.cboard.io/api/cboard-symbols/pictograms/en/search/house

# Test Spanish search
curl -H "X-API-Key: $API_KEY" \
  https://cbuilder.cboard.io/api/cboard-symbols/pictograms/es/search/casa

# Test without API key (should return 401)
curl https://cbuilder.cboard.io/api/cboard-symbols/pictograms/en/search/house
```

## Troubleshooting

### "No results found" for Cboard Symbols

**Symptoms**: Other providers work, but Cboard Symbols returns no results

**Solutions**:
1. Check API key is set in `.env` (both frontend and backend)
2. Verify keys match exactly (no extra spaces)
3. Restart both applications after changing `.env`
4. Check browser console for API errors
5. Test API endpoint with curl (see Testing section)

### "401 Unauthorized" Error

**Symptoms**: Console shows "Unauthorized - check API key"

**Solutions**:
1. Verify `CBOARD_API_KEY` is set in cboard-ai-builder `.env`
2. Verify `REACT_APP_CBOARD_SYMBOLS_API_KEY` is set in cboard `.env`
3. Ensure both keys are identical
4. Check environment variables are loaded: `console.log(process.env.REACT_APP_CBOARD_SYMBOLS_API_KEY)`

### CORS Errors

**Symptoms**: Browser console shows "blocked by CORS policy"

**Root Causes**:
- Frontend running on different origin than backend expects
- Missing required headers (e.g., `traceparent`, `request-id`)
- Backend not running or not accessible

**Solutions**:
1. **Verify ports**: Backend on `localhost:3000`, frontend on `localhost:3001` (or any port)
2. **Check API key**: Must be set in both `.env` files
3. **Restart both applications** after changing `.env`
4. **Clear browser cache**: Hard refresh with `Ctrl+Shift+R` (or `Cmd+Shift+R` on Mac)
5. **Check backend is running**: Visit `http://localhost:3000` in browser

**How CORS Works** (for reference):
- Backend allows ALL `http://localhost:*` origins (any port) in development
- Backend allows `https://app.cboard.io` in production
- Allowed headers include: `X-API-Key`, `Content-Type`, `request-id`, `x-request-id`, `traceparent`, `tracestate`
- No additional configuration needed - it's automatic!

**Verify CORS Headers** (in browser DevTools → Network tab):
```
Response Headers should include:
  Access-Control-Allow-Origin: http://localhost:3001
  Access-Control-Allow-Headers: X-API-Key, Content-Type, request-id, x-request-id, traceparent, tracestate
  Access-Control-Allow-Methods: GET, OPTIONS
```

### Skin Tone Not Updating

**Symptoms**: Changing skin tone doesn't affect Cboard Symbols

**Solutions**:
1. Verify `mapArasaacToCboardSkinTone()` is imported correctly
2. Check `fetchCboardSymbolsSuggestions` is called in `handleSkinToneChange`
3. Verify variant selection logic in suggestions mapping

### Performance Issues

**Symptoms**: Search is slow or times out

**Solutions**:
1. Check network tab in browser DevTools for slow requests
2. Verify API timeout is set to 10s (see `/cboard/src/api/cboard-symbols.js`)
3. Check MongoDB indexes in backend (should index `translations.{lang}.normalizedConcept`)
4. Consider implementing IndexedDB caching (future enhancement)

## File Changes Summary

### Backend (cboard-ai-builder)

**New Files**:
- `/src/middleware/apiKey.ts` - API key validation middleware
- `/API_KEY_SETUP.md` - API key setup documentation

**Modified Files**:
- `/src/app/api/cboard-symbols/pictograms/[language]/search/[searchtext]/route.ts` - Added API key auth
- `/.env` - Added `CBOARD_API_KEY`

### Frontend (cboard)

**New Files**:
- `/src/api/cboard-symbols.js` - Cboard Symbols API client
- `/CBOARD_SYMBOLS_INTEGRATION.md` - This file

**Modified Files**:
- `/src/components/Board/SymbolSearch/SymbolSearch.component.js` - Added Cboard Symbols provider
- `/src/components/Board/SymbolSearch/SymbolSearch.messages.js` - Added messages
- `/src/components/Board/SymbolSearch/SymbolSearch.component.test.js` - Added tests
- `/.env` - Added `REACT_APP_CBOARD_SYMBOLS_API_KEY`

## API Reference

### Search Endpoint

```
GET https://cbuilder.cboard.io/api/cboard-symbols/pictograms/{language}/search/{searchtext}
```

**Headers**:
- `X-API-Key` (required): Your API key

**Path Parameters**:
- `language` (string): 2-letter ISO language code (e.g., "en", "es", "pt")
- `searchtext` (string): URL-encoded search query

**Response** (200):
```json
[
  {
    "_id": "507f1f77bcf86cd799439011",
    "url": "https://example.com/symbol.png",
    "userId": "user123",
    "originalLanguage": "en",
    "translations": {
      "en": {
        "concept": "house",
        "normalizedConcept": "house",
        "keywords": ["home", "building"]
      },
      "es": {
        "concept": "casa",
        "normalizedConcept": "casa",
        "keywords": ["hogar", "vivienda"]
      }
    },
    "variants": [
      {
        "url": "https://example.com/symbol-emoji.png",
        "skinTone": "skin_emoji"
      },
      {
        "url": "https://example.com/symbol-light.png",
        "skinTone": "skin_light"
      }
    ],
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-01T00:00:00.000Z"
  }
]
```

**Error Responses**:
- `401 Unauthorized`: Invalid or missing API key
- `404 Not Found`: No results for search query
- `500 Internal Server Error`: Server error

## Future Enhancements

### Phase 2 (Future)
- [ ] Add IndexedDB caching for offline support
- [ ] Display user ratings in search results
- [ ] Sort by relevance score and user ratings
- [ ] Add symbol preview on hover
- [ ] Show symbol metadata (author, date)

### Phase 3 (Future)
- [ ] Allow users to contribute symbols
- [ ] Add symbol collections/categories
- [ ] Implement advanced filtering
- [ ] Add symbol usage analytics
- [ ] Create symbol recommendations

## Support

For issues or questions:
1. Check this documentation
2. Review GitHub issues: [cboard-org/cboard](https://github.com/cboard-org/cboard/issues)
3. Check API setup: `/cboard-ai-builder/API_KEY_SETUP.md`
4. Contact: support@cboard.io

## License

This integration maintains the same license as the Cboard project (GPL-3.0).


================================================
FILE: CHANGELOG.md
================================================
# Changelog

## 1.7.1 (23/12/2020)

#### Bug Fixes:

- [**bug**] Shared boards have some titles of symbols translated back to English  [#794](https://github.com/cboard-org/cboard/issues/794)
- [**bug**] Edited positions of tiles are not remembered after logout [#774](https://github.com/cboard-org/cboard/issues/774)

---

## 1.7.0 (04/12/2020)

#### New Features

- [**feature**] New fixed board and grid  [#727](https://github.com/cboard-org/cboard/issues/727)

#### Bug Fixes:

- [**bug**] Importing boards from .json file gives warning "Nothing to import" [#789](https://github.com/cboard-org/cboard/issues/789)
- [**bug**] When font size large and extra large are used, some elements are overlaid with text [#775](https://github.com/cboard-org/cboard/issues/775)
- [**bug**] Imported board with folders (Cboard format) has "missed folders" [#773](https://github.com/cboard-org/cboard/issues/773)

---

## 1.6.0 (10/08/2020)

#### New Features

- [**feature**] Added analytics report on the web version [#766](https://github.com/cboard-org/cboard/issues/766)
- [**feature**] Keep embedded images when importing an OBF board that contains embedded images  [#756](https://github.com/cboard-org/cboard/issues/756)
- [**feature**] For Android app, add the Ability to record audio and save with tile [#726](https://github.com/cboard-org/cboard/issues/726)

#### Bug Fixes:

- [**bug**] Untraslated parts of the interface [#772](https://github.com/cboard-org/cboard/issues/772)
- [**bug**] SVG images won't display after import of an OBZ board  [#765](https://github.com/cboard-org/cboard/issues/765)
- [**bug**] Importing previously exported boards/folders from Cboard show with unknown/strange titles [#734](https://github.com/cboard-org/cboard/issues/734)
- [**bug**] Editing board title doesn't work on Android Cboard [#712](https://github.com/cboard-org/cboard/issues/712)
- [**bug**] Editing board title doesn't work on web Cboard [#710](https://github.com/cboard-org/cboard/issues/710)

---

## 1.5.1 (25/07/2020)

#### Bug Fixes:

- [**bug**] Revert react grid layout as it causes a bug on Android [#758](https://github.com/cboard-org/cboard/issues/758)

---

## 1.5.0 (23/07/2020)

#### New Features

- [**feature**][**good first issue**][**help wanted**] Using help file and returning to Cboard  [#737](https://github.com/cboard-org/cboard/issues/737)
- [**feature**][**help wanted**] Allow exporting just a single board using the obf file format  [#653](https://github.com/cboard-org/cboard/issues/653)

#### Bug Fixes:

- [**bug**] Fix to avoid withe screen on startup for some specific devices [#752](https://github.com/cboard-org/cboard/issues/752)
- [**bug**] Searching symbols from Global Symbols finds the symbols but without the picture [#733](https://github.com/cboard-org/cboard/issues/733)
- [**bug**] Firefox print/export to PDF is missing some symbols [#722](https://github.com/cboard-org/cboard/issues/722)
- [**bug**] Unexpected deletion of board upon folder tile removal [#629](https://github.com/cboard-org/cboard/issues/629)

---

## 1.4.0 (22/06/2020)

#### New Features

- [**feature**] Include Cyrillic script into Cboard using existing Alfanum Serbian voices  [#715](https://github.com/cboard-org/cboard/issues/715)
- [**feature**] Enhancement for the output bar in dark mode [#704](https://github.com/cboard-org/cboard/issues/704)

#### Bug Fixes:

- [**bug**] Fix layout direction for tile editor [#717](https://github.com/cboard-org/cboard/issues/717)
- [**bug**] Fix for UI, tile on hover has underline [#702](https://github.com/cboard-org/cboard/issues/702)
- [**bug**][**linux**] Fix for Firefox Linux Mint blank screen [#700](https://github.com/cboard-org/cboard/issues/700)
- [**bug**][**good first issue**][**help wanted**] When dark theme is on, when the output bar is clicked, it highlights the bar with white color so the text below the symbols can’t be seen because the text is also in white color. [#695](https://github.com/cboard-org/cboard/issues/695)
- [**bug**][**good first issue**][**help wanted**] When dark theme is on, the links that appear in the app are not visible because of the blue color [#694](https://github.com/cboard-org/cboard/issues/694)
- [**bug**] I tried to import the boards I previously exported from the Cboard, and most of them appeared with Unknown labels in the Boards menu [#693](https://github.com/cboard-org/cboard/issues/693)
- [**bug**][**help wanted**] When the option Above for Label position is selected, the exported board will have the label positioned below from the second page of the PDF [#692](https://github.com/cboard-org/cboard/issues/692)
- [**bug**] (Un)locking is not working properly after clicking Build option [#547](https://github.com/cboard-org/cboard/issues/547)

---

## 1.3.1 (28/04/2020)

#### Bug Fixes:

- [**bug**] Hotfix - Fix that Montenegrin is showed under Serbian language [#690](https://github.com/cboard-org/cboard/issues/690)

---

## 1.3.0 (23/04/2020)

#### New Features

- [**feature**] Review Material principles for colors [#686](https://github.com/cboard-org/cboard/issues/686)
- [**feature**][**good first issue**] Feature: Vocalizable folders [#611](https://github.com/cboard-org/cboard/issues/611)
- [**feature**] Dark Theme support [#112](https://github.com/cboard-org/cboard/issues/112)

#### Bug Fixes:

- [**bug**] Support for the new montenegrin TTS from Alfanum  [#688](https://github.com/cboard-org/cboard/issues/688)
- [**bug**] Filtered results are cancelled if click on LOAD MORE buttton  [#670](https://github.com/cboard-org/cboard/issues/670)
- [**bug**] For the boards that are published public with a description for the first time, it is not allowed to change the description [#669](https://github.com/cboard-org/cboard/issues/669)
- [**bug**] When downloading boards with more folders, not all folders are downloaded and visible in All my boards [#668](https://github.com/cboard-org/cboard/issues/668)
- [**bug**] Change filename for exported boards in case of cboard and open board option  [#667](https://github.com/cboard-org/cboard/issues/667)
- [**bug**] Store voiceURI setting on the database and read it at startup  [#666](https://github.com/cboard-org/cboard/issues/666)

---

## 1.2.0 (27/03/2020)

#### New Features

- [**feature**] Allow to export a single board [#663](https://github.com/cboard-org/cboard/issues/663)
- [**feature**][**help wanted**] add  linking the folder in editing mode as well  [#646](https://github.com/cboard-org/cboard/issues/646)
- [**feature**][**good first issue**][**help wanted**] Add a display setting of where the labels can be placed (above or below the symbol or even having no label) [#638](https://github.com/cboard-org/cboard/issues/638)

#### Bug Fixes:

- [**bug**] Fix for the Alfanum TTS Lite CRO engine  [#661](https://github.com/cboard-org/cboard/issues/661)
- [**bug**] Handle case of TTS engine returning no voices  [#658](https://github.com/cboard-org/cboard/issues/658)
- [**bug**] Not possible to edit a folder linking to a board  [#655](https://github.com/cboard-org/cboard/issues/655)
- [**bug**][**good first issue**][**help wanted**] Fix auto-generated user avatar to display one or two letters [#626](https://github.com/cboard-org/cboard/issues/626)

---

## 1.1.7 (11/03/2020)

#### New Features

- [**feature**] Allow to edit the board cover image from communicator builder  [#648](https://github.com/cboard-org/cboard/issues/648)

#### Bug Fixes:

- [**bug**] instead of board ID at the beginning of the PDF write board’s name (or nothing) [#645](https://github.com/cboard-org/cboard/issues/645)
- [**bug**] Some of the symbols are not represented with the same color background in the PDF as they are in the Cboard [#644](https://github.com/cboard-org/cboard/issues/644)
- [**bug**] Exporting two times a board overriddes the first one [#643](https://github.com/cboard-org/cboard/issues/643)
- [**bug**] When add more than one board from public boards, first (or first two) board is visible in Boards and All my boards [#642](https://github.com/cboard-org/cboard/issues/642)
- [**bug**] Board description at the time of publishing were not visible after clicking on information button for the board in the public boards [#641](https://github.com/cboard-org/cboard/issues/641)
- [**bug**][**help wanted**] Export to PDF losing some symbols in the case the symbol source is ARASAAC or Global symbols  X  [#639](https://github.com/cboard-org/cboard/issues/639)

---

## 1.1.6 (20/02/2020)

#### New Features

- [**feature**] Implement symbol sources filtering; Mulberry, Global symbols and ARASAAC  [#636](https://github.com/cboard-org/cboard/issues/636)
- [**feature**][**help wanted**] Ask for board description at the time of publish a board [#620](https://github.com/cboard-org/cboard/issues/620)

#### Bug Fixes:

- [**bug**] Fix bug on public boards become private after board edit [#635](https://github.com/cboard-org/cboard/issues/635)
- [**bug**] Fix Boards search from communicator builder [#634](https://github.com/cboard-org/cboard/issues/634)
- [**bug**] board change of the name cannot be saved [#633](https://github.com/cboard-org/cboard/issues/633)

---

## 1.1.5 (06/02/2020)

#### New Features

- [**feature**] Improve the color select component  [#624](https://github.com/cboard-org/cboard/issues/624)
- [**feature**][**good first issue**][**help wanted**] Add loading Circular progress when click LOAD MORE buttton  [#621](https://github.com/cboard-org/cboard/issues/621)
- [**feature**][**good first issue**][**help wanted**] Option for hiding the sentence bar available through the settings menu. [#608](https://github.com/cboard-org/cboard/issues/608)
- [**feature**][**help wanted**] Linking between existing boards: [#603](https://github.com/cboard-org/cboard/issues/603)

---

## 1.1.4 (31/01/2020)

#### New Features

- [**feature**] Migrate to material ui 4  [#616](https://github.com/cboard-org/cboard/issues/616)
- [**feature**] Added date to the board information display [#615](https://github.com/cboard-org/cboard/issues/615)

#### Bug Fixes:

- [**bug**] Copy boards recursively when get a public board [#618](https://github.com/cboard-org/cboard/issues/618)

---

## 1.1.3 (23/01/2020)

#### New Features

- [**feature**] Full Refactor communicator builder [#610](https://github.com/cboard-org/cboard/issues/610)
- [**feature**] Feature: Support importation of transparent images (alpha channel) [#604](https://github.com/cboard-org/cboard/issues/604)
- [**feature**] Implement Board removal [#602](https://github.com/cboard-org/cboard/issues/602)

#### Bug Fixes:

- [**bug**] Support importation of transparent images [#609](https://github.com/cboard-org/cboard/issues/609)

---

## 1.1.2 (17/12/2019)

#### Bug Fixes:

- [**bug**] restore the original size for the grid [#600](https://github.com/cboard-org/cboard/issues/600)

---

## 1.1.1 (16/12/2019)

#### New Features

- [**feature**] Improve display on tablet devices (better tile size) [#599](https://github.com/cboard-org/cboard/issues/599)

#### Bug Fixes:

- [**bug**] Navigation bar (buttons recent apps, home and back) visible after adding/editing new symbol [#591](https://github.com/cboard-org/cboard/issues/591)

---

## 1.1.0 (14/12/2019)

#### New Features

- [**feature**] Enable public boards to be displayed under public boards list on communicator builder  [#597](https://github.com/cboard-org/cboard/issues/597)
- [**feature**] Hiding voice recorder feature for Android app  [#595](https://github.com/cboard-org/cboard/issues/595)
- [**feature**] The voice recorder function in creating a Symbol is not work [#589](https://github.com/cboard-org/cboard/issues/589)
- [**feature**] Add Image Caching to Cordova application for API symbols [#542](https://github.com/cboard-org/cboard/issues/542)

#### Bug Fixes:

- [**bug**] Cboard version 1.0.12 -The Import and Export functions are not working well [#588](https://github.com/cboard-org/cboard/issues/588)
- [**bug**] Choosing multiple tiles to edit for colour changes etc - have to save individually [#583](https://github.com/cboard-org/cboard/issues/583)
- [**bug**] Fitzgerald colour code white or no background colour needs ring around the colour [#582](https://github.com/cboard-org/cboard/issues/582)

---

## 1.0.12 (07/12/2019)

#### Bug Fixes:

- [**bug**] Obz import from Coughdrop is not working for android  [#585](https://github.com/cboard-org/cboard/issues/585)
- [**bug**] Obz import from cboard is not working for android  [#584](https://github.com/cboard-org/cboard/issues/584)
- [**bug**] After finding the .obf file in the Downloads, the file is greyed and it is not allowed to be clicked and imported into Cboard [#540](https://github.com/cboard-org/cboard/issues/540)

---

## 1.0.11 (29/11/2019)

#### Bug Fixes:

- [**bug**] Editing a tile from default folder causes white screen with loading icon [#573](https://github.com/cboard-org/cboard/issues/573)
- [**bug**] Sharing via Facebook doesn't work and causes white screen [#568](https://github.com/cboard-org/cboard/issues/568)
- [**bug**] Sharing board via e-mail doesn't work [#567](https://github.com/cboard-org/cboard/issues/567)
- [**bug**] Not allowed to go back after editing tile from default folders in home board  [#560](https://github.com/cboard-org/cboard/issues/560)
- [**bug**] White screen situations [#559](https://github.com/cboard-org/cboard/issues/559)
- [**bug**] Navigation bar (buttons recent apps, home and back) and status bar visible after adding new symbol/folder [#541](https://github.com/cboard-org/cboard/issues/541)
- [**bug**] Fix for Exporting in OpenBoard format on Android app  [#538](https://github.com/cboard-org/cboard/issues/538)

---

## 1.0.10 (25/11/2019)

#### Bug Fixes:

- [**bug**] HOTFIX - Fix for  google analytics api update [#571](https://github.com/cboard-org/cboard/issues/571)

---

## 1.0.9 (23/11/2019)

#### New Features

- [**feature**] Update translations [#570](https://github.com/cboard-org/cboard/issues/570)

---

## 1.0.8 (22/11/2019)

#### Bug Fixes:

- [**bug**] All my boards available only when online and missing boards/symbols in offline mode [#565](https://github.com/cboard-org/cboard/issues/565)
- [**bug**] Blank page when try to get a local board from remote  [#562](https://github.com/cboard-org/cboard/issues/562)
- [**bug**] Some time the Audio stream is not working when the Alfanum TTS is selected [#549](https://github.com/cboard-org/cboard/issues/549)
- [**bug**] When user goes to online mode, check if there are new boards/folders/tiles and save them [#539](https://github.com/cboard-org/cboard/issues/539)
- [**bug**] The application often does not remember selected language in the settings [#511](https://github.com/cboard-org/cboard/issues/511)

---

## 1.0.7 (15/11/2019)

#### New Features

- [**feature**] Setup initial version for mobile analytics using Android System to store offline analytics  [#554](https://github.com/cboard-org/cboard/issues/554)

#### Bug Fixes:

- [**bug**] Fix for non standard language code like ME for Montenegrin language [#553](https://github.com/cboard-org/cboard/issues/553)
- [**bug**] Unable to load the image/symbol from the device  [#545](https://github.com/cboard-org/cboard/issues/545)
- [**bug**] Wrong Croatian User Help file in the settings [#544](https://github.com/cboard-org/cboard/issues/544)
- [**bug**] Issues for back navigation [#543](https://github.com/cboard-org/cboard/issues/543)

---

## 1.0.6 (07/11/2019)

#### New Features

- [**feature**] Update Montenegrin translation strings  [#518](https://github.com/cboard-org/cboard/issues/518)
- [**feature**] No option to change password if you forget it and you cannot log in [#513](https://github.com/cboard-org/cboard/issues/513)

#### Bug Fixes:

- [**bug**] After adding the new board, it appears at the bottom of the home board (like new tile) and it shouldn't (screenshot in the right cell - new board is colored grey) [#515](https://github.com/cboard-org/cboard/issues/515)
- [**bug**] Missing board after logging out and in [#514](https://github.com/cboard-org/cboard/issues/514)

---

## 1.0.5 (05/11/2019)

#### New Features

- [**feature**] Update serbian latin Strings [#524](https://github.com/cboard-org/cboard/issues/524)

#### Bug Fixes:

- [**bug**] Fix for Montenegrin translation showing in english [#525](https://github.com/cboard-org/cboard/issues/525)
- [**bug**] Fix for signup on Android app that prevented signup  [#523](https://github.com/cboard-org/cboard/issues/523)
- [**bug**] Support color Scheme [#522](https://github.com/cboard-org/cboard/issues/522)
- [**bug**] Fix for edition of remote boards [#521](https://github.com/cboard-org/cboard/issues/521)
- [**bug**] Fix for unstable empty Board creation [#520](https://github.com/cboard-org/cboard/issues/520)
- [**bug**] Important fix for remote boards synchronism [#519](https://github.com/cboard-org/cboard/issues/519)

---

## 1.0.3 (29/10/2019)

#### Bug Fixes:

- [**bug**] Update the board container to better online synchronism  [#535](https://github.com/cboard-org/cboard/issues/535)

---

## 1.0.2 (27/10/2019)

#### Bug Fixes:

- [**bug**]  fix for board caption on communicator toolbar [#533](https://github.com/cboard-org/cboard/issues/533)
- [**bug**] Fixes for backnavigation issues [#532](https://github.com/cboard-org/cboard/issues/532)

---

## 1.0.1 (27/10/2019)

#### New Features

- [**feature**] Global symbols integration [#531](https://github.com/cboard-org/cboard/issues/531)
- [**feature**] Added translation for Serbian 'sr-SR'.  [#529](https://github.com/cboard-org/cboard/issues/529)
- [**feature**] Added capabilities to create an empty board not linked to the current active board. [#528](https://github.com/cboard-org/cboard/issues/528)
- [**feature**] Support for the alfanum TTS engine. [#527](https://github.com/cboard-org/cboard/issues/527)
- [**feature**] Support for the Samsung TTS engine which is the default tts engine on Samsung devices. [#526](https://github.com/cboard-org/cboard/issues/526)

#### Bug Fixes:

- [**bug**] Remove social login buttons. [#530](https://github.com/cboard-org/cboard/issues/530)

---

## Fix speech provider for Android  (27/09/2019)

---

## fix for package json and index html  (16/09/2019)


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at shayc@outlook.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Cboard

Interested in contributing to Cboard? Thanks! There are plenty of ways you can help.

Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.

Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features.

## Ways of contributing 

- As a developer 
- As a translator 
- As a manual tester
- As an automation tester 

**We're using Discord to collaborate, join us at: https://discord.gg/TEH8uxh**

## As a developer

### What you need to know

In order to contribute as a developer, you will need to have a basic understanding of [React](https://facebook.github.io/react/docs/hello-world.html) and probably [Redux](https://egghead.io/courses/getting-started-with-redux), you will also need to be familiar with [Material-UI](https://material-ui.com/).

### Submitting a Pull Request

Good pull requests, such as patches, improvements, and new features, are a fantastic help. They should remain focused in scope and avoid containing unrelated commits.

Please **ask first** if somebody else is already working on this or the core developers think your feature is in-scope for Cboard. Generally always have a related issue with discussions for whatever you are including.

### Setting Up a Local Copy

1. Clone the repo with `git clone https://github.com/cboard-org/cboard.git`

2. Run `yarn install` in the root `cboard` folder.

Once it is done, you can modify any file locally and run `yarn start`, `yarn test` or `yarn run build`.

## As a translator

To help with translations and proofreading please use our translation management platform: https://crowdin.com/project/cboard

We currently support 40 languages, most of which were machine translated and require proofreading.
Help us make Cboard available in your country!

## As a manual tester

We track all of the project issues using Github:
https://github.com/cboard-org/cboard/issues
 
Our issue tracker is quite active, typically we got two-three bugs/week, then active tester participation is highly appreciated in order to clarify, reproduce, and track the bugs.
**We need help on writing a test plan!**
We have never written a test plan for cboard and it feels really bad. We will appreciate people that is able to design and create a comprehensive test plan covering all of the features and functions of the applications that are available in the Cboard system. 

## As an automation tester 
If you are proficient with automation testing, we will be happy if you can help us!. 
We have developed a little automation framework based on [Webdriver.io](https://webdriver.io/), that runs using the cloud service provided by [Browserstack](https://www.browserstack.com/). Here you can find the repository to start collaborating: 
https://github.com/cboard-org/cboard-webdriverio

## Financial contributions

We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/cboard).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.


## Credits


### Contributors

Thank you to all the people who have already contributed to cboard!
<a href="graphs/contributors"><img src="https://opencollective.com/cboard/contributors.svg?width=890" /></a>


### Backers

Thank you to all our backers! [[Become a backer](https://opencollective.com/cboard#backer)]

<a href="https://opencollective.com/cboard#backers" target="_blank"><img src="https://opencollective.com/cboard/backers.svg?width=890"></a>


### Sponsors

Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/cboard#sponsor))

<a href="https://opencollective.com/cboard/sponsor/0/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/1/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/2/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/3/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/4/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/5/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/6/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/7/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/8/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/9/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/9/avatar.svg"></a>


================================================
FILE: Dockerfile
================================================
# Stage 1 - the build process
FROM node:22.14.0 as build-deps
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install
COPY . ./
RUN NODE_OPTIONS="--max-old-space-size=4192" yarn build

# Stage 2 - the production environment
FROM nginx:stable-alpine
COPY ./rootfs/ /
COPY --from=build-deps /usr/src/app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]


================================================
FILE: LICENSE.txt
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    {one line to give the program's name and a brief idea of what it does.}
    Copyright (C) {year}  {name of author}

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    {project}  Copyright (C) {year}  {fullname}
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.


================================================
FILE: Makefile
================================================
image:
	docker build -t cboard/cboard .

run:
	docker run -p 5000:3000 cboard/cboard:latest

================================================
FILE: README.md
================================================
[![DPG Badge](https://img.shields.io/badge/Verified-DPG-3333AB?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMzEiIGhlaWdodD0iMzMiIHZpZXdCb3g9IjAgMCAzMSAzMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0LjIwMDggMjEuMzY3OEwxMC4xNzM2IDE4LjAxMjRMMTEuNTIxOSAxNi40MDAzTDEzLjk5MjggMTguNDU5TDE5LjYyNjkgMTIuMjExMUwyMS4xOTA5IDEzLjYxNkwxNC4yMDA4IDIxLjM2NzhaTTI0LjYyNDEgOS4zNTEyN0wyNC44MDcxIDMuMDcyOTdMMTguODgxIDUuMTg2NjJMMTUuMzMxNCAtMi4zMzA4MmUtMDVMMTEuNzgyMSA1LjE4NjYyTDUuODU2MDEgMy4wNzI5N0w2LjAzOTA2IDkuMzUxMjdMMCAxMS4xMTc3TDMuODQ1MjEgMTYuMDg5NUwwIDIxLjA2MTJMNi4wMzkwNiAyMi44Mjc3TDUuODU2MDEgMjkuMTA2TDExLjc4MjEgMjYuOTkyM0wxNS4zMzE0IDMyLjE3OUwxOC44ODEgMjYuOTkyM0wyNC44MDcxIDI5LjEwNkwyNC42MjQxIDIyLjgyNzdMMzAuNjYzMSAyMS4wNjEyTDI2LjgxNzYgMTYuMDg5NUwzMC42NjMxIDExLjExNzdMMjQuNjI0MSA5LjM1MTI3WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+Cg==)](https://digitalpublicgoods.net/r/cboard)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/cboard/localized.svg)](https://crowdin.com/project/cboard)
[![Backers on Open Collective](https://opencollective.com/cboard/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/cboard/sponsors/badge.svg)](#sponsors)
[![cboard-org](https://circleci.com/gh/cboard-org/cboard.svg?style=shield)](https://app.circleci.com/pipelines/github/cboard-org/cboard)

# Cboard - AAC Communication Board for browsers

[Cboard](https://app.cboard.io) is an augmentative and alternative communication (AAC) web application, allowing users with speech and language impairments (autism, cerebral palsy) to communicate with symbols and text-to-speech.

![Cboard GIF demo](public/videos/demo.gif)

The app uses the browser's Speech Synthesis API to generate speech when a symbol is clicked. There are thousands of symbols from the most popular AAC symbol libraries to choose from when creating a board. Cboard is available in 40 languages (support varies by platform - Android, iOS, Windows).

**We're using Discord to collaborate, join us at: https://discord.gg/TEH8uxh**

## How does it work?

This video shows Srna. She is one of the children who have received the Cboard Communicator thanks to UNICEF’s ["For every child, a voice"](https://www.unicef.org/innovation/stories/giving-every-child-voice-aac-technology) project.

<a href="https://youtu.be/wqLauXnyLhY"><img src="https://img.youtube.com/vi/wqLauXnyLhY/0.jpg" alt="Real Look Autism Episode 8" width="480" height="360"></a>

## Translations

The app supports 40 languages.
Languages were machine translated and require proofreading: if you want to help proofread, please use our translation management platform: https://crowdin.com/project/cboard

**You do not need to be a programmer!**

Translations play a major role in this project and they contribute a lot for the inclusion of children, specially in non developed countries. Please consider collaborating with us!

### Translations for developers

To add support to a new language, [follow this guide](https://github.com/cboard-org/cboard/wiki/How-to-Add-a-New-Language).

#### Pulling translations from CrowdIn

In order to pull the latest translations from CrowdIn into the codebase, you can run `yarn translations:pull`. This will update all language files such as `en.json` as well as the central `cboard.json` file. Please note that this requires the CrowdIn API key to be available in the `.private` config file. Refer to [Secrets Management](#secrets-management). After the script completes, changes to the translation files will need to be committed to the repo by the usual process.

## Getting Started

### `yarn start`

Runs the app in development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

The page will reload if you make edits.<br>
You will see the build errors and lint warnings in the console.

### `yarn test`

Runs the test watcher in an interactive mode.<br>
By default, runs tests related to files changed since the last commit.

[Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests)

### `yarn build`

Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.<br>
By default, it also [includes a service worker](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) so that Cboard loads from local cache on future visits.

Cboard is ready to be deployed.

### `yarn build-cordova-debug`

Use this to produce non-minified build for use in debugging within Cordova. It uses `craco` & `craco.config` to customize webpack operation without ejecting react.

See [CCBoard](https://github.com/cboard-org/ccboard) repo for packaging this CBoard application within Cordova.

## Docker getting started

### `make image`

Creates a Docker image with cboard built for production. The image is tagged as cboard:latest.

### `make run`

Runs the cboard:latest Docker image on port 5000.

## Secrets Management

Some external services have APIs we need to access, and these require API keys. To prevent open disclosure of these keys in the public repository, while still tracking them with the code, we encrypt some secrets into a GPG file. These files are `env/local-private.gpg` and `env/prod-private.gpg`.

In order to access the secrets, you must request the `ENCRYPTION_KEY` from @shaycojo and then run the decrypt script: `ENCRYPTION_KEY={key-goes-here} yarn decrypt:local` (or `prod`), which will create the file `.private/local.js` with the secrets in plain text where the scripts can access them. **The files in `.private` should never be committed to the repository.**

If you need to add or change a secret, make the change to the `.private/local.js` file, and then run the encryption script: `ENCRYPTION_KEY={key-goes-here} yarn encrypt:local` (or `prod`).

_Note: These keys/secrets are *not* required to run or develop Cboard._ They are used with scripts by some team members.

## Thanks

### Symbols sources

<img src="https://mulberrysymbols.org/assets/examples/hello.svg" href="https://mulberrysymbols.org" alt="Mulberry" width="40" height="40"> [Mulberry](https://mulberrysymbols.org/)

<img src="https://static.arasaac.org/images/arasaac-logo.svg" href="https://mulberrysymbols.org" alt="ARASAAC" width="40" height="40"> [ARASAAC](http://www.arasaac.org/)

<img src="https://globalsymbols.com/assets/logo-with-text-5c57659e34824e7b2907a36895745f9e39e7f1c015ea77d6968eb75a52c8389f.svg" href="https://globalsymbols.com" alt="Global Symbols" width="40" height="40"> [Global Symbols](https://globalsymbols.com/)

### Translation

<img src="https://support.crowdin.com/assets/logos/crowdin-symbol.png" href="https://crowdin.com/" alt="Crowdin" width="40" height="40">[ Crowdin](https://crowdin.com/) - for providing the localization management platform.

### Testing platform

<img src="https://avatars2.githubusercontent.com/u/1119453?s=200&v=4" href="https://www.browserstack.com/" alt="Browserstack" width="40" height="40">[ Browserstack](https://www.browserstack.com/) - for providing the automation infrastructure for testing.

### Development

<img src="./public/images/sponsers/css-tricks.svg" alt="CSS-Tricks" width="120" height="39">[ CSS Tricks](https://css-tricks.com) - for providing feedback and support from the early stage.

## Contributors

This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/cboard-org/cboard/graphs/contributors"><img src="https://opencollective.com/cboard/contributors.svg?width=890&button=false" /></a>

## Backers

Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/cboard#backer)]

<a href="https://opencollective.com/cboard#backers" target="_blank"><img src="https://opencollective.com/cboard/backers.svg?width=890"></a>

## Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/cboard#sponsor)]

<a href="https://opencollective.com/cboard/sponsor/0/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/1/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/2/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/3/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/4/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/5/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/6/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/7/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/8/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/cboard/sponsor/9/website" target="_blank"><img src="https://opencollective.com/cboard/sponsor/9/avatar.svg"></a>

## :memo: Legal & licenses

Copyright © 2017-2024, Assistive Technology LLC & Cboard contributors.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.

- Code - [GPLv3](https://github.com/cboard-org/cboard/blob/master/LICENSE.txt)
- Mulberry Symbols - [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
- ARASAAC Symbols - [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)


================================================
FILE: __mocks__/browser-image-resizer.js
================================================
export default function readAndCompressImage(file, userConfig) {
  return new Promise((resolve, reject) => {
    if (file === 'error') {
      reject(new Error({ message: 'not found' }));
    } else {
      var img = document.createElement('img');
      resolve(img);
    }
  });
}


================================================
FILE: __mocks__/react-redux.js
================================================
// This mock will make sure that we are able to access mapStateToProps, mapDispatchToProps and reactComponent in the test file.

// To use this, just do `jest.mock('react-redux');` in your test.js file.
const mockDispatch = jest.fn(action => action);

module.exports = {
  connect: (mapStateToProps, mapDispatchToProps) => reactComponent => ({
    mapStateToProps,
    mapDispatchToProps: (dispatch = mockDispatch, ownProps) =>
      mapDispatchToProps(dispatch, ownProps),
    reactComponent,
    mockDispatch
  }),
  Provider: ({ children }) => children
};


================================================
FILE: __mocks__/undici.js
================================================
/**
 * Minimal undici mock for Jest tests.
 * Prevents undici from initializing real HTTP machinery (MessagePort,
 * ReadableStream, etc.) which causes crashes in the jsdom test environment.
 * Tests never call fromURL() so no real HTTP implementation is needed.
 */

const noop = () => {};
const asyncNoop = async () => {};

class Dispatcher {}
class Client extends Dispatcher {}
class Pool extends Dispatcher {}
class BalancedPool extends Dispatcher {}
class RoundRobinPool extends Dispatcher {}
class Agent extends Dispatcher {}
class ProxyAgent extends Dispatcher {}
class EnvHttpProxyAgent extends Dispatcher {}
class RetryAgent extends Dispatcher {}
class H2CClient extends Dispatcher {}
class RetryHandler {}
class DecoratorHandler {}
class RedirectHandler {}
class WebSocket {}
class CloseEvent {}
class ErrorEvent {}
class MessageEvent {}

module.exports = {
  Dispatcher,
  Client,
  Pool,
  BalancedPool,
  RoundRobinPool,
  Agent,
  ProxyAgent,
  EnvHttpProxyAgent,
  RetryAgent,
  H2CClient,
  RetryHandler,
  DecoratorHandler,
  RedirectHandler,
  interceptors: {},
  cacheStores: {},
  buildConnector: noop,
  errors: {},
  util: {},
  setGlobalDispatcher: noop,
  getGlobalDispatcher: noop,
  fetch: asyncNoop,
  Headers: class Headers {},
  Response: class Response {},
  Request: class Request {},
  FormData: class FormData {},
  setGlobalOrigin: noop,
  getGlobalOrigin: noop,
  caches: {},
  deleteCookie: noop,
  getCookies: noop,
  getSetCookies: noop,
  setCookie: noop,
  parseCookie: noop,
  parseMIMEType: noop,
  serializeAMimeType: noop,
  WebSocket,
  CloseEvent,
  ErrorEvent,
  MessageEvent,
  ping: asyncNoop
};


================================================
FILE: browserstack.yml
================================================
# This file is used to configure BrowserStack for running Playwright tests.
# For more information, see https://www.browserstack.com/docs/playwright/getting-started
framework: playwright
platforms:
  - os: Windows
    osVersion: 11
    browserName: chrome
    browserVersion: latest
  # - os: android
  #   osVersion: 15.0
  #   browserName: chrome
  #   deviceName: Samsung Galaxy S25
  # - os: OS X
  #   osVersion: Ventura
  #   browserName: playwright-webkit
  #   browserVersion: latest
  # - os: Windows
  #   osVersion: 11
  #   browserName: playwright-firefox
  #   browserVersion: latest
browserstackLocal: false
buildName: cboard-e2e-test
projectName: Cboard AAC
CUSTOM_TAG_1: 'Playwright Test Run'
# Use CUSTOM_TAG_<N> and set more build tags as you need.
debug: true
consoleLogs: info
playwrightVersion: 1.50.0


================================================
FILE: cboard.njsproj.user
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
  </PropertyGroup>
</Project>

================================================
FILE: craco.config.js
================================================
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');

module.exports = {
  webpack: {
    entry: './src/index.js',
    plugins: [new NodePolyfillPlugin({ excludeAliases: ['console'] })],
    resolve: {
      extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx']
    },
    configure: (webpackConfig, { env, paths }) => {
      const isCordovaDebug = process.argv.includes('--cordova-debug');
      if (isCordovaDebug) {
        webpackConfig.mode = 'development';
        webpackConfig.optimization = { minimize: false };
        console.log('Cordova debug mode enabled');
      }

      webpackConfig.ignoreWarnings = [
        function ignoreSourcemapsloaderWarnings(warning) {
          return (
            warning.module?.resource.includes('node_modules') &&
            warning.details?.includes('source-map-loader')
          );
        }
      ];

      return webpackConfig;
    }
  },
  babel: {
    plugins: ['babel-plugin-transform-import-meta']
  },
  jest: {
    configure: {
      setupFiles: ['<rootDir>/src/setupPolyfills.js']
    }
  }
};


================================================
FILE: env/local.js
================================================
/* 
 * LOCAL CONFIG
 * This is intended for devs while working locally.
 */

const APP_URL = 'http://localhost:3000';

module.exports = {
  APP_URL,
  EXAMPLE_CONFIG: 'Local config',
};


================================================
FILE: env/prod-private.gpg
================================================

e뮨48{2}
vn	'F"QqޛVAҔH%<q1Lu|Ia|	ta>N=pvᙂXK3*g7T~Nmmȑyg |;lbjLg#QF	}vڐXWF9ZݠEd=|dB=d۠c]5K 
gf30s
܆]D1;yzXS풶ZV~fF4_?VC`7CM͙n!K4Aeb1xl/5c([:srq?C{BLUŨg݂B1R's]DzJ.	zT.D.0Hq-%~f"

================================================
FILE: env/prod.js
================================================
/* 
 * PROD CONFIG
 * This is intended for deployment to a hosting service.
 */

const APP_URL = 'https://cboard.somehost.com';

module.exports = {
  APP_URL,
  EXAMPLE_CONFIG: 'Prod config',
};

================================================
FILE: funding.json
================================================
{
  "$schema": "https://fundingjson.org/schema/v1.1.0.json",
  "version": "v1.1.0",
  "entity": {
    "type": "organisation",
    "role": "owner",
    "name": "Cboard Contributors & Assistive Communication LLC",
    "email": "support@cboard.io",
    "phone": "12059226861",
    "description": "Cboard is developed and maintained by a community of contributors along with Assistive Communication LLC. We are committed to creating accessible augmentative and alternative communication (AAC) solutions for individuals with speech and language impairments, including those with autism and cerebral palsy. Our mission is to break down communication barriers worldwide through free and open-source technology.",
    "webpageUrl": {
      "url": "https://www.cboard.io",
      "wellKnown": ""
    }
  },
  "projects": [
    {
      "guid": "cboard",
      "name": "Cboard - AAC Communication Board",
      "description": "Cboard is an augmentative and alternative communication (AAC) web application that helps users with speech and language impairments (autism, cerebral palsy) communicate using symbols and text-to-speech. It's a progressive web app available in 40 languages with thousands of symbols from popular AAC symbol libraries. Cboard is recognized as a Digital Public Good and works with UNICEF on the 'For every child, a voice' project to provide communication tools to children worldwide.",
      "webpageUrl": {
        "url": "https://www.cboard.io",
        "wellKnown": ""
      },
      "repositoryUrl": {
        "url": "https://github.com/cboard-org/cboard",
        "wellKnown": ""
      },
      "licenses": ["spdx:GPL-3.0-only"],
      "tags": [
        "aac",
        "accessibility",
        "communication",
        "autism",
        "cerebral-palsy",
        "text-to-speech",
        "progressive-web-app",
        "assistive-technology",
        "healthcare",
        "education"
      ]
    }
  ],
  "funding": {
    "channels": [
      {
        "guid": "opencollective",
        "type": "payment-provider",
        "address": "https://opencollective.com/cboard",
        "description": "Support Cboard through Open Collective with one-time or recurring contributions. Open Collective provides transparent funding management where you can see how funds are used."
      }
    ],
    "plans": [
      {
        "guid": "backer-monthly",
        "status": "active",
        "name": "Monthly Backer",
        "description": "Become a monthly backer and help us cover ongoing costs for hosting, infrastructure, and development. Your contribution helps us maintain and improve Cboard for users worldwide.",
        "amount": 4,
        "currency": "USD",
        "frequency": "monthly",
        "channels": ["opencollective"]
      },
      {
        "guid": "sponsor",
        "status": "active",
        "name": "Project Sponsor",
        "description": "Become a sponsor and your logo will be featured on our GitHub repository and website. Sponsorship helps us dedicate more resources to feature development, accessibility improvements, and expanding language support.",
        "amount": 45,
        "currency": "USD",
        "frequency": "monthly",
        "channels": ["opencollective"]
      },
      {
        "guid": "one-time-donation",
        "status": "active",
        "name": "One-time Contribution",
        "description": "Make a one-time contribution to support Cboard's mission of making communication accessible to everyone. Every contribution helps us reach more users who need AAC tools.",
        "amount": 0,
        "currency": "USD",
        "frequency": "one-time",
        "channels": ["opencollective"]
      },
      {
        "guid": "one-time-donation",
        "status": "active",
        "name": "Voice cloning program contribution",
        "description": "Make a one-time contribution to support Cboard's voice cloning program. Every contribution helps us reach more users who need AAC tools.",
        "amount": 50,
        "currency": "USD",
        "frequency": "one-time",
        "channels": ["opencollective"]
      }
    ],
    "history": [
      {
        "year": 2017,
        "currency": "USD",
        "description": ""
      }
    ]
  }
}


================================================
FILE: package.json
================================================
{
  "name": "cboard",
  "version": "0.1.1",
  "description": "Cboard is an augmentative and alternative communication (AAC) web application, allowing users with speech and language impairments (autism, cerebral palsy) to communicate by symbols and text-to-speech.",
  "keywords": [
    "aac",
    "autism",
    "cerebral-palsy",
    "progressive-web-app",
    "communication-board",
    "speech",
    "language",
    "tts",
    "text-to-speech"
  ],
  "homepage": "https://app.cboard.io",
  "private": false,
  "license": "GPL-3.0-only",
  "dependencies": {
    "@cospired/i18n-iso-languages": "^2.2.0",
    "@crowdin/crowdin-api-client": "^1.48.3",
    "@ctrl/react-adsense": "^1.8.0",
    "@material-ui/core": "^4.12.4",
    "@material-ui/icons": "^4.11.3",
    "@material-ui/lab": "4.0.0-alpha.57",
    "@microsoft/applicationinsights-web": "^2.8.16",
    "@paypal/react-paypal-js": "^7.8.3",
    "@redux-beacon/google-analytics-gtag": "^1.1.0",
    "@redux-beacon/logger": "^1.0.0",
    "@redux-beacon/offline-web": "^1.0.0",
    "axios": "^1.7.8",
    "browser-image-resizer": "^2.4.1",
    "dom-to-image": "^2.6.0",
    "dotenv": "^16.5.0",
    "echarts": "5.5.1",
    "echarts-for-react": "^3.0.2",
    "file-saver": "^2.0.5",
    "fontsource-roboto": "^4.0.0",
    "formik": "^1.3.2",
    "history": "^4.10.1",
    "i18n-iso-countries": "4.3.1",
    "idb": "^7.1.1",
    "intl": "^1.2.5",
    "is-url": "1.2.4",
    "ismobilejs": "^1.1.1",
    "iso-639-1": "2.1.15",
    "jss": "^10.9.2",
    "jss-rtl": "^0.3.0",
    "jszip": "^3.10.1",
    "jszip-utils": "^0.1.0",
    "keycode": "^2.2.1",
    "localforage": "1.10.0",
    "lodash": "^4.17.21",
    "mathjs": "7.6.0",
    "microsoft-cognitiveservices-speech-sdk": "^1.48.0",
    "mime-types": "^2.1.35",
    "moment": "2.29.4",
    "mongoose": "^8.19.2",
    "ogv": "^1.8.9",
    "pdfmake": "^0.2.15",
    "prop-types": "^15.8.1",
    "query-string": "^6.14.1",
    "ramda": "^0.29.1",
    "react": "^17.0.2",
    "react-autosuggest": "^10.1.0",
    "react-color": "^2.19.3",
    "react-cropper": "^2.3.3",
    "react-dnd": "^11.1.3",
    "react-dnd-touch-backend": "^11.1.3",
    "react-dom": "^17.0.2",
    "react-grid-layout": "^0.16.6",
    "react-helmet": "^6.1.0",
    "react-icons": "^4.11.0",
    "react-intl": "^2.7.2",
    "react-joyride": "^2.5.4",
    "react-markdown": "^5.0.3",
    "react-media-recorder": "^0.7.1",
    "react-redux": "^5.1.2",
    "react-router-dom": "^5.3.4",
    "react-scannable": "0.0.18",
    "react-share": "^2.3.1",
    "react-sizeme": "^3.0.2",
    "react-social-login-buttons": "^3.9.1",
    "react-transition-group": "4.4.5",
    "redux": "^4.2.1",
    "redux-beacon": "^2.0.3",
    "redux-persist": "^5.10.0",
    "redux-thunk": "^2.4.2",
    "shortid": "^2.2.16",
    "source-map-explorer": "^2.5.3",
    "swiper": "^6.8.4",
    "yup": "^0.32.11"
  },
  "devDependencies": {
    "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
    "@craco/craco": "^7.1.0",
    "@playwright/test": "1.50.0",
    "@types/mime-types": "^2.1.1",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "@wojtekmaj/enzyme-adapter-react-17": "^0.6.0",
    "babel-plugin-transform-import-meta": "^2.2.1",
    "browserstack-node-sdk": "^1.49.12",
    "decompress-zip": "^0.3.1",
    "enzyme": "^3.11.0",
    "enzyme-to-json": "3.3.5",
    "fake-indexeddb": "^4.0.1",
    "husky": "^1.1.4",
    "jest-mock-axios": "^3.2.0",
    "lint-staged": "^10.0.3",
    "node-polyfill-webpack-plugin": "^2.0.1",
    "prettier": "1.15.3",
    "react-scripts": "5.0.1",
    "react-test-renderer": "^17.0.2",
    "redux-mock-store": "^1.5.4",
    "sw-precache": "^5.2.1",
    "ts-loader": "^9.5.0",
    "typescript": "^4.6.4"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,json,css}": [
      "prettier --write",
      "git add"
    ]
  },
  "jest": {
    "transformIgnorePatterns": [
      "node_modules/(?!idb|microsoft-cognitiveservices-speech-sdk)"
    ],
    "moduleNameMapper": {
      "\\.(css|less)$": "<rootDir>/src/__mocks__/styleMock.js",
      "^undici$": "<rootDir>/__mocks__/undici.js"
    },
    "collectCoverageFrom": [
      "src/**/*.js",
      "!src/polyfills.js",
      "!src/config.js",
      "!src/index.js",
      "!src/common/test_utils.js",
      "!src/components/**/index.js",
      "!src/providers/**/index.js",
      "!src/components/**/*.messages.js",
      "!src/components/**/*.container.js",
      "!src/components/**/*.helpers.js",
      "!src/components/Board/SymbolSearch/SymbolSearch.component.js",
      "!src/components/Board/TileEditor/TileEditor.component.js",
      "!src/registerServiceWorker.js"
    ]
  },
  "scripts": {
    "analyze": "source-map-explorer build/static/js/**/*.js",
    "start": "craco start",
    "build": "craco build  --verbose && sw-precache --config=sw-precache-config.js",
    "build-cordova-debug": "craco build --verbose --cordova-debug",
    "predeploy": "yarn build",
    "deploy": "gh-pages -d build",
    "test": "craco test",
    "test:e2e": "playwright test",
    "test:e2e:headed": "playwright test --headed",
    "test:e2e:debug": "playwright test --debug",
    "test:e2e:report": "playwright show-report",
    "test:e2e:ui": "playwright test --ui",
    "test:e2e:install": "playwright install",
    "decrypt:local": "./scripts/decrypt-private.sh local",
    "encrypt:local": "./scripts/encrypt-private.sh local",
    "decrypt:prod": "./scripts/decrypt-private.sh prod",
    "encrypt:prod": "./scripts/encrypt-private.sh prod",
    "translations:pull": "CBOARD_ENV=local node ./scripts/crowdin-fetch-latest.js",
    "translations:push": "CBOARD_ENV=local node ./scripts/crowdin-push-changes.js",
    "eject": "craco eject",
    "translations:pull-browserstack": "CBOARD_ENV=local browserstack-node-sdk node ./scripts/crowdin-fetch-latest.js",
    "translations:push-browserstack": "CBOARD_ENV=local browserstack-node-sdk node ./scripts/crowdin-push-changes.js",
    "test:e2e-browserstack": "browserstack-node-sdk playwright test",
    "test:e2e:headed-browserstack": "browserstack-node-sdk playwright test --headed",
    "test:e2e:debug-browserstack": "browserstack-node-sdk playwright test --debug",
    "test:e2e:report-browserstack": "browserstack-node-sdk playwright show-report",
    "test:e2e:ui-browserstack": "browserstack-node-sdk playwright test --ui",
    "test:e2e:install-browserstack": "browserstack-node-sdk playwright install"
  },
  "funding": {
    "type": "opencollective",
    "url": "https://opencollective.com/cboard"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}


================================================
FILE: playwright.config.ts
================================================
import { defineConfig, devices } from '@playwright/test';

/**
 * Playwright Configuration for Cboard E2E Tests
 * 
 * Configuration optimized for fastest execution:
 * - Maximum parallel execution with optimal worker count
 * - Headless mode for faster execution
 * - Essential browser coverage only (Chrome + Firefox)
 * - Minimal media capture for speed
 * - Optimized timeouts
 * 
 * @see https://playwright.dev/docs/test-configuration
 */

// Test credentials configuration
export const testCredentials = {
  elevenLabsApiKey: process.env.ELEVENLABS_API_KEY || 'sk_0000000000000000000',
  email: process.env.TEST_USER_EMAIL || 'anything@cboard.io',
  password: process.env.TEST_USER_PASSWORD || 'lote10mza126',
};

// Set environment variables for backward compatibility
process.env.TEST_USER_EMAIL = testCredentials.email;
process.env.TEST_USER_PASSWORD = testCredentials.password;
process.env.ELEVENLABS_API_KEY = testCredentials.elevenLabsApiKey;

const config = defineConfig({
  /* Balanced timeout settings */
  expect: {
    timeout: 10 * 1000,
  },
  
  /* Fail the build on CI if you accidentally left test.only in the source code. */
  forbidOnly: !!process.env.CI,
  
  /* Maximum parallel execution */
  fullyParallel: true,
  
  /* Essential browser coverage for fastest execution */
  projects: [
    {
      name: 'chromium',
      use: { 
        ...devices['Desktop Chrome'],
      },
    },
    {
      name: 'firefox',
      use: { 
        ...devices['Desktop Firefox'],
      },
    },
    /* Single mobile project for mobile coverage */
    {
      name: 'mobile',
      use: { 
        ...devices['Pixel 5'],
      },
    },
  ],
  
  /* Minimal reporter for speed */
  reporter: [['junit', { outputFile: 'test-results/results.xml' }], ['html', { open: 'never' }], ['list']],
  
  /* No retries for faster execution - let failures fail fast */
  retries: 0,
  
  testDir: './tests',
  
  /* Balanced timeout settings */
  timeout: 60 * 1000, // Increased test timeout for slower browsers,
  
  use: {
    /* Balanced timeout settings for reliability vs speed */
    actionTimeout: 15 * 1000,

    /* Base URL to use in actions like `await page.goto('/')`. */
    baseURL: 'https://app.qa.cboard.io',
    
    /* Longer navigation timeout for slow environments */
    navigationTimeout: 60 * 1000,

    /* No traces, videos, or screenshots for maximum speed */
    screenshot: 'off',
    trace: 'off',
    video: 'off',
  },
  
  /* Optimal worker count for cross-browser stability */
  workers: process.env.CI ? 5 : 5, // Reduced for stability

  /* Run your local dev server before starting the tests */
  // webServer: {
  //   command: 'npm run start',
  //   url: 'http://127.0.0.1:3000',
  //   reuseExistingServer: !process.env.CI,
  // },
});

export default config;


================================================
FILE: public/.well-known/assetlinks.json
================================================
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.unicef.cboard",
      "sha256_cert_fingerprints": [
        "0C:E6:E3:49:2D:58:75:83:72:D9:23:00:F6:76:1C:1D:CE:50:47:25:9C:E3:B5:00:67:A0:0E:06:B8:B8:D8:9D"
      ]
    }
  }
]


================================================
FILE: public/_redirects
================================================
/*    /index.html   200

================================================
FILE: public/index.html
================================================
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
  <meta http-equiv="Content-Security-Policy" content="
  default-src 'self'
      'unsafe-inline'
      'unsafe-eval'
      http://*
      https://*
      wss://*.microsoft.com/cognitiveservices/
      blob:
      gap:
      data:;
  img-src * data:  filesystem:  blob:  ;
  connect-src 'self' https://* localhost:* 127.0.0.1:* wss: ws: blob:;
  ">

  <!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
  <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `yarn build`.
    -->

  <!-- Add to homescreen for Chrome on Android -->
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="application-name" content="Cboard">
  <link rel="icon" sizes="192x192" href="%PUBLIC_URL%/images/touch/chrome-touch-icon-192x192.png">

  <!-- Add to homescreen for Safari on iOS -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="Cboard">
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/images/touch/apple-touch-icon.png">
  <link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/images/touch/touch-icon-iphone-retina.png">
  <link rel="apple-touch-icon" sizes="167x167" href="%PUBLIC_URL%/images/touch/touch-icon-ipad-retina.png">

  <!-- Color the status bar on mobile devices -->
  <meta name="theme-color" content="#000000">
  
  <!-- Block indexing on search engines -->
  <meta name="robots" content="noindex">


  <!-- Google tag (gtag.js) -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=G-60S79265FY"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag() { dataLayer.push(arguments); }
    gtag('js', new Date());

    gtag('config', 'G-60S79265FY');
  </script>

  <title>Cboard - AAC Communication Board</title>
</head>

<body>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
  <div id="root"></div>
</body>

</html>

================================================
FILE: public/manifest.json
================================================
{
  "short_name": "Cboard",
  "name": "Cboard - AAC Communication Board",
  "description":
    "Cboard is an augmentative and alternative communication (AAC) application, allowing users with speech and language impairments (Autism, Cerebral Palsy) to communicate with symbols and text-to-speech.",
  "icons": [
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square71x71Logo.scale-400.png",
      "sizes": "284x284"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square71x71Logo.scale-200.png",
      "sizes": "142x142"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square71x71Logo.scale-100.png",
      "sizes": "71x71"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square71x71Logo.scale-150.png",
      "sizes": "107x107"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square71x71Logo.scale-125.png",
      "sizes": "89x89"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square150x150Logo.scale-400.png",
      "sizes": "600x600"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square150x150Logo.scale-200.png",
      "sizes": "300x300"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square150x150Logo.scale-100.png",
      "sizes": "150x150"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square150x150Logo.scale-150.png",
      "sizes": "225x225"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square150x150Logo.scale-125.png",
      "sizes": "188x188"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Wide310x150Logo.scale-400.png",
      "sizes": "1240x600"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Wide310x150Logo.scale-200.png",
      "sizes": "620x300"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Wide310x150Logo.scale-100.png",
      "sizes": "310x150"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Wide310x150Logo.scale-150.png",
      "sizes": "465x225"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Wide310x150Logo.scale-125.png",
      "sizes": "388x188"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square310x310Logo.scale-400.png",
      "sizes": "1240x1240"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square310x310Logo.scale-200.png",
      "sizes": "620x620"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square310x310Logo.scale-100.png",
      "sizes": "310x310"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square310x310Logo.scale-150.png",
      "sizes": "465x465"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square310x310Logo.scale-125.png",
      "sizes": "388x388"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.scale-400.png",
      "sizes": "176x176"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.scale-200.png",
      "sizes": "88x88"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.scale-100.png",
      "sizes": "44x44"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.scale-150.png",
      "sizes": "66x66"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.scale-125.png",
      "sizes": "55x55"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-256.png",
      "sizes": "256x256"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-48.png",
      "sizes": "48x48"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-24.png",
      "sizes": "24x24"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-16.png",
      "sizes": "16x16"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-256_altform-unplated.png",
      "sizes": "256x256"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-48_altform-unplated.png",
      "sizes": "48x48"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-24_altform-unplated.png",
      "sizes": "24x24"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/Square44x44Logo.targetsize-16_altform-unplated.png",
      "sizes": "16x16"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/StoreLogo.scale-400.png",
      "sizes": "200x200"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/StoreLogo.scale-200.png",
      "sizes": "100x100"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/StoreLogo.scale-150.png",
      "sizes": "75x75"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/StoreLogo.scale-125.png",
      "sizes": "63x63"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/StoreLogo.scale-100.png",
      "sizes": "50x50"
    },
    {
      "src": "https://app.cboard.io/images/pwa/windows10/StoreLogo.png",
      "sizes": "50x50"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/SplashScreen.scale-400.png",
      "sizes": "2480x1200"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/SplashScreen.scale-200.png",
      "sizes": "1240x600"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/SplashScreen.scale-150.png",
      "sizes": "930x450"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/SplashScreen.scale-125.png",
      "sizes": "775x375"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows10/SplashScreen.scale-100.png",
      "sizes": "620x300"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-smallsquare-24-24.png",
      "sizes": "24x24"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-smallsquare-30-30.png",
      "sizes": "30x30"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-smallsquare-42-42.png",
      "sizes": "42x42"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-smallsquare-54-54.png",
      "sizes": "54x54"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-splashscreen-1116-540.png",
      "sizes": "1116x540"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-splashscreen-868-420.png",
      "sizes": "868x420"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-splashscreen-620-300.png",
      "sizes": "620x300"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-squarelogo-270-270.png",
      "sizes": "270x270"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-squarelogo-210-210.png",
      "sizes": "210x210"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-squarelogo-150-150.png",
      "sizes": "150x150"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-squarelogo-120-120.png",
      "sizes": "120x120"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-storelogo-90-90.png",
      "sizes": "90x90"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-storelogo-70-70.png",
      "sizes": "70x70"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windows-storelogo-50-50.png",
      "sizes": "50x50"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-appicon-106-106.png",
      "sizes": "106x106"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-appicon-62-62.png",
      "sizes": "62x62"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-appicon-44-44.png",
      "sizes": "44x44"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-mediumtile-360-360.png",
      "sizes": "360x360"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-mediumtile-210-210.png",
      "sizes": "210x210"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-mediumtile-150-150.png",
      "sizes": "150x150"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-smalltile-170-170.png",
      "sizes": "170x170"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-smalltile-99-99.png",
      "sizes": "99x99"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-smalltile-71-71.png",
      "sizes": "71x71"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-storelogo-120-120.png",
      "sizes": "120x120"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-storelogo-70-70.png",
      "sizes": "70x70"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/windows/windowsphone-storelogo-50-50.png",
      "sizes": "50x50"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/android/android-launchericon-512-512.png",
      "sizes": "512x512"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/android/android-launchericon-192-192.png",
      "sizes": "192x192"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/android/android-launchericon-144-144.png",
      "sizes": "144x144"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/android/android-launchericon-96-96.png",
      "sizes": "96x96"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/android/android-launchericon-72-72.png",
      "sizes": "72x72"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/android/android-launchericon-48-48.png",
      "sizes": "48x48"
    },
    {
      "src": "https://app.cboard.io/images/pwa/ios/ios-appicon-1024-1024.png",
      "sizes": "1024x1024"
    },
    {
      "src": "https://app.cboard.io/images/pwa/ios/ios-appicon-180-180.png",
      "sizes": "180x180"
    },
    {
      "src": "https://app.cboard.io/images/pwa/ios/ios-appicon-152-152.png",
      "sizes": "152x152"
    },
    {
      "src": "https://app.cboard.io/images/pwa/ios/ios-appicon-120-120.png",
      "sizes": "120x120"
    },
    {
      "src": "https://app.cboard.io/images/pwa/ios/ios-appicon-76-76.png",
      "sizes": "76x76"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-750-1334.png",
      "sizes": "750x1334"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-1334-750.png",
      "sizes": "1334x750"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-1242-2208.png",
      "sizes": "1242x2208"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-2208-1242.png",
      "sizes": "2208x1242"
    },
    {
      "src": "https://app.cboard.io/images/pwa/ios/ios-launchimage-640-960.png",
      "sizes": "640x960"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-640-1136.png",
      "sizes": "640x1136"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-1536-2048.png",
      "sizes": "1536x2048"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-2048-1536.png",
      "sizes": "2048x1536"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-768-1024.png",
      "sizes": "768x1024"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/ios/ios-launchimage-1024-768.png",
      "sizes": "1024x768"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/chrome/chrome-extensionmanagementpage-48-48.png",
      "sizes": "48x48"
    },
    {
      "src": "https://app.cboard.io/images/pwa/chrome/chrome-favicon-16-16.png",
      "sizes": "16x16"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/chrome/chrome-installprocess-128-128.png",
      "sizes": "128x128"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-marketplace-512-512.png",
      "sizes": "512x512"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-marketplace-128-128.png",
      "sizes": "128x128"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-256-256.png",
      "sizes": "256x256"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-128-128.png",
      "sizes": "128x128"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-90-90.png",
      "sizes": "90x90"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-64-64.png",
      "sizes": "64x64"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-48-48.png",
      "sizes": "48x48"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-32-32.png",
      "sizes": "32x32"
    },
    {
      "src":
        "https://app.cboard.io/images/pwa/firefox/firefox-general-16-16.png",
      "sizes": "16x16"
    }
  ],
  "start_url": "https://app.cboard.io/",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}


================================================
FILE: public/ogv/ogv-decoder-audio-opus-wasm.js
================================================
var OGVDecoderAudioOpusW = (() => {
  var _scriptDir =
    typeof document !== 'undefined' && document.currentScript
      ? document.currentScript.src
      : undefined;
  if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
  return function(OGVDecoderAudioOpusW) {
    OGVDecoderAudioOpusW = OGVDecoderAudioOpusW || {};

    var a;
    a ||
      (a =
        typeof OGVDecoderAudioOpusW !== 'undefined'
          ? OGVDecoderAudioOpusW
          : {});
    var l, m;
    a.ready = new Promise(function(b, c) {
      l = b;
      m = c;
    });
    var q = a,
      t = Object.assign({}, a),
      u = 'object' == typeof window,
      w = 'function' == typeof importScripts,
      x = '',
      y,
      A,
      B,
      fs,
      C,
      D;
    if (
      'object' == typeof process &&
      'object' == typeof process.versions &&
      'string' == typeof process.versions.node
    )
      (x = w ? require('path').dirname(x) + '/' : __dirname + '/'),
        (D = () => {
          C || ((fs = require('fs')), (C = require('path')));
        }),
        (y = function(b, c) {
          D();
          b = C.normalize(b);
          return fs.readFileSync(b, c ? void 0 : 'utf8');
        }),
        (B = b => {
          b = y(b, !0);
          b.buffer || (b = new Uint8Array(b));
          return b;
        }),
        (A = (b, c, e) => {
          D();
          b = C.normalize(b);
          fs.readFile(b, function(d, f) {
            d ? e(d) : c(f.buffer);
          });
        }),
        1 < process.argv.length && process.argv[1].replace(/\\/g, '/'),
        process.argv.slice(2),
        process.on('unhandledRejection', function(b) {
          throw b;
        }),
        (a.inspect = function() {
          return '[Emscripten Module object]';
        });
    else if (u || w)
      w
        ? (x = self.location.href)
        : 'undefined' != typeof document &&
          document.currentScript &&
          (x = document.currentScript.src),
        _scriptDir && (x = _scriptDir),
        0 !== x.indexOf('blob:')
          ? (x = x.substr(0, x.replace(/[?#].*/, '').lastIndexOf('/') + 1))
          : (x = ''),
        (y = b => {
          var c = new XMLHttpRequest();
          c.open('GET', b, !1);
          c.send(null);
          return c.responseText;
        }),
        w &&
          (B = b => {
            var c = new XMLHttpRequest();
            c.open('GET', b, !1);
            c.responseType = 'arraybuffer';
            c.send(null);
            return new Uint8Array(c.response);
          }),
        (A = (b, c, e) => {
          var d = new XMLHttpRequest();
          d.open('GET', b, !0);
          d.responseType = 'arraybuffer';
          d.onload = () => {
            200 == d.status || (0 == d.status && d.response)
              ? c(d.response)
              : e();
          };
          d.onerror = e;
          d.send(null);
        });
    var aa = a.print || console.log.bind(console),
      E = a.printErr || console.warn.bind(console);
    Object.assign(a, t);
    t = null;
    var F;
    a.wasmBinary && (F = a.wasmBinary);
    var noExitRuntime = a.noExitRuntime || !0;
    'object' != typeof WebAssembly && G('no native wasm support detected');
    var H,
      I = !1,
      ba = 'undefined' != typeof TextDecoder ? new TextDecoder('utf8') : void 0,
      ca,
      J,
      K;
    function da() {
      var b = H.buffer;
      ca = b;
      a.HEAP8 = new Int8Array(b);
      a.HEAP16 = new Int16Array(b);
      a.HEAP32 = K = new Int32Array(b);
      a.HEAPU8 = J = new Uint8Array(b);
      a.HEAPU16 = new Uint16Array(b);
      a.HEAPU32 = new Uint32Array(b);
      a.HEAPF32 = new Float32Array(b);
      a.HEAPF64 = new Float64Array(b);
    }
    var ea,
      fa = [],
      ha = [],
      ia = [];
    function ja() {
      var b = a.preRun.shift();
      fa.unshift(b);
    }
    var L = 0,
      M = null,
      N = null;
    a.preloadedImages = {};
    a.preloadedAudios = {};
    function G(b) {
      if (a.onAbort) a.onAbort(b);
      b = 'Aborted(' + b + ')';
      E(b);
      I = !0;
      b = new WebAssembly.RuntimeError(
        b + '. Build with -s ASSERTIONS=1 for more info.'
      );
      m(b);
      throw b;
    }
    function ka() {
      return O.startsWith('data:application/octet-stream;base64,');
    }
    var O;
    O = 'ogv-decoder-audio-opus-wasm.wasm';
    if (!ka()) {
      var la = O;
      O = a.locateFile ? a.locateFile(la, x) : x + la;
    }
    function oa() {
      var b = O;
      try {
        if (b == O && F) return new Uint8Array(F);
        if (B) return B(b);
        throw 'both async and sync fetching of the wasm failed';
      } catch (c) {
        G(c);
      }
    }
    function pa() {
      if (!F && (u || w)) {
        if ('function' == typeof fetch && !O.startsWith('file://'))
          return fetch(O, { credentials: 'same-origin' })
            .then(function(b) {
              if (!b.ok) throw "failed to load wasm binary file at '" + O + "'";
              return b.arrayBuffer();
            })
            .catch(function() {
              return oa();
            });
        if (A)
          return new Promise(function(b, c) {
            A(
              O,
              function(e) {
                b(new Uint8Array(e));
              },
              c
            );
          });
      }
      return Promise.resolve().then(function() {
        return oa();
      });
    }
    function S(b) {
      for (; 0 < b.length; ) {
        var c = b.shift();
        if ('function' == typeof c) c(a);
        else {
          var e = c.u;
          'number' == typeof e
            ? void 0 === c.s
              ? qa(e)()
              : qa(e)(c.s)
            : e(void 0 === c.s ? null : c.s);
        }
      }
    }
    var T = [];
    function qa(b) {
      var c = T[b];
      c || (b >= T.length && (T.length = b + 1), (T[b] = c = ea.get(b)));
      return c;
    }
    var ra = [null, [], []],
      sa = {
        f: function() {
          G('');
        },
        e: function(b, c, e) {
          J.copyWithin(b, c, c + e);
        },
        c: function(b) {
          var c = J.length;
          b >>>= 0;
          if (2147483648 < b) return !1;
          for (var e = 1; 4 >= e; e *= 2) {
            var d = c * (1 + 0.2 / e);
            d = Math.min(d, b + 100663296);
            var f = Math;
            d = Math.max(b, d);
            f = f.min.call(f, 2147483648, d + ((65536 - (d % 65536)) % 65536));
            a: {
              try {
                H.grow((f - ca.byteLength + 65535) >>> 16);
                da();
                var g = 1;
                break a;
              } catch (h) {}
              g = void 0;
            }
            if (g) return !0;
          }
          return !1;
        },
        d: function() {
          return 0;
        },
        b: function() {},
        a: function(b, c, e, d) {
          for (var f = 0, g = 0; g < e; g++) {
            var h = K[c >> 2],
              ma = K[(c + 4) >> 2];
            c += 8;
            for (var P = 0; P < ma; P++) {
              var z = J[h + P],
                Q = ra[b];
              if (0 === z || 10 === z) {
                z = 1 === b ? aa : E;
                var n = Q;
                for (var p = 0, r = p + NaN, v = p; n[v] && !(v >= r); ) ++v;
                if (16 < v - p && n.buffer && ba)
                  n = ba.decode(n.subarray(p, v));
                else {
                  for (r = ''; p < v; ) {
                    var k = n[p++];
                    if (k & 128) {
                      var R = n[p++] & 63;
                      if (192 == (k & 224))
                        r += String.fromCharCode(((k & 31) << 6) | R);
                      else {
                        var na = n[p++] & 63;
                        k =
                          224 == (k & 240)
                            ? ((k & 15) << 12) | (R << 6) | na
                            : ((k & 7) << 18) |
                              (R << 12) |
                              (na << 6) |
                              (n[p++] & 63);
                        65536 > k
                          ? (r += String.fromCharCode(k))
                          : ((k -= 65536),
                            (r += String.fromCharCode(
                              55296 | (k >> 10),
                              56320 | (k & 1023)
                            )));
                      }
                    } else r += String.fromCharCode(k);
                  }
                  n = r;
                }
                z(n);
                Q.length = 0;
              } else Q.push(z);
            }
            f += ma;
          }
          K[d >> 2] = f;
          return 0;
        },
        g: function(b, c, e) {
          var d = H.buffer,
            f = new Uint32Array(d, b, c),
            g = [];
          if (0 !== b)
            for (b = 0; b < c; b++) {
              var h = f[b];
              d.slice
                ? ((h = d.slice(h, h + 4 * e)), (h = new Float32Array(h)))
                : ((h = new Float32Array(d, h, e)), (h = new Float32Array(h)));
              g.push(h);
            }
          a.audioBuffer = g;
        },
        h: function(b, c) {
          a.audioFormat = { channels: b, rate: c };
          a.loadedMetadata = !0;
        }
      };
    (function() {
      function b(f) {
        a.asm = f.exports;
        H = a.asm.i;
        da();
        ea = a.asm.q;
        ha.unshift(a.asm.j);
        L--;
        a.monitorRunDependencies && a.monitorRunDependencies(L);
        0 == L &&
          (null !== M && (clearInterval(M), (M = null)),
          N && ((f = N), (N = null), f()));
      }
      function c(f) {
        b(f.instance);
      }
      function e(f) {
        return pa()
          .then(function(g) {
            return WebAssembly.instantiate(g, d);
          })
          .then(function(g) {
            return g;
          })
          .then(f, function(g) {
            E('failed to asynchronously prepare wasm: ' + g);
            G(g);
          });
      }
      var d = { a: sa };
      L++;
      a.monitorRunDependencies && a.monitorRunDependencies(L);
      if (a.instantiateWasm)
        try {
          return a.instantiateWasm(d, b);
        } catch (f) {
          return (
            E('Module.instantiateWasm callback failed with error: ' + f), !1
          );
        }
      (function() {
        return F ||
          'function' != typeof WebAssembly.instantiateStreaming ||
          ka() ||
          O.startsWith('file://') ||
          'function' != typeof fetch
          ? e(c)
          : fetch(O, { credentials: 'same-origin' }).then(function(f) {
              return WebAssembly.instantiateStreaming(f, d).then(c, function(
                g
              ) {
                E('wasm streaming compile failed: ' + g);
                E('falling back to ArrayBuffer instantiation');
                return e(c);
              });
            });
      })().catch(m);
      return {};
    })();
    a.___wasm_call_ctors = function() {
      return (a.___wasm_call_ctors = a.asm.j).apply(null, arguments);
    };
    a._ogv_audio_decoder_init = function() {
      return (a._ogv_audio_decoder_init = a.asm.k).apply(null, arguments);
    };
    a._ogv_audio_decoder_process_header = function() {
      return (a._ogv_audio_decoder_process_header = a.asm.l).apply(
        null,
        arguments
      );
    };
    a._ogv_audio_decoder_process_audio = function() {
      return (a._ogv_audio_decoder_process_audio = a.asm.m).apply(
        null,
        arguments
      );
    };
    a._malloc = function() {
      return (a._malloc = a.asm.n).apply(null, arguments);
    };
    a._free = function() {
      return (a._free = a.asm.o).apply(null, arguments);
    };
    a._ogv_audio_decoder_destroy = function() {
      return (a._ogv_audio_decoder_destroy = a.asm.p).apply(null, arguments);
    };
    var U;
    N = function ta() {
      U || V();
      U || (N = ta);
    };
    function V() {
      function b() {
        if (!U && ((U = !0), (a.calledRun = !0), !I)) {
          S(ha);
          l(a);
          if (a.onRuntimeInitialized) a.onRuntimeInitialized();
          if (a.postRun)
            for (
              'function' == typeof a.postRun && (a.postRun = [a.postRun]);
              a.postRun.length;

            ) {
              var c = a.postRun.shift();
              ia.unshift(c);
            }
          S(ia);
        }
      }
      if (!(0 < L)) {
        if (a.preRun)
          for (
            'function' == typeof a.preRun && (a.preRun = [a.preRun]);
            a.preRun.length;

          )
            ja();
        S(fa);
        0 < L ||
          (a.setStatus
            ? (a.setStatus('Running...'),
              setTimeout(function() {
                setTimeout(function() {
                  a.setStatus('');
                }, 1);
                b();
              }, 1))
            : b());
      }
    }
    a.run = V;
    if (a.preInit)
      for (
        'function' == typeof a.preInit && (a.preInit = [a.preInit]);
        0 < a.preInit.length;

      )
        a.preInit.pop()();
    V();
    var W, X;
    function ua(b) {
      if (W && X >= b) return W;
      W && a._free(W);
      X = b;
      return (W = a._malloc(X));
    }
    var Y;
    'undefined' === typeof performance || 'undefined' === typeof performance.now
      ? (Y = Date.now)
      : (Y = performance.now.bind(performance));
    function Z(b) {
      var c = Y();
      b = b();
      a.cpuTime += Y() - c;
      return b;
    }
    a.loadedMetadata = !!q.audioFormat;
    a.audioFormat = q.audioFormat || null;
    a.audioBuffer = null;
    a.cpuTime = 0;
    Object.defineProperty(a, 'processing', {
      get: function() {
        return !1;
      }
    });
    a.init = function(b) {
      Z(function() {
        a._ogv_audio_decoder_init();
      });
      b();
    };
    a.processHeader = function(b, c) {
      var e = Z(function() {
        var d = b.byteLength,
          f = ua(d);
        new Uint8Array(H.buffer, f, d).set(new Uint8Array(b));
        return a._ogv_audio_decoder_process_header(f, d);
      });
      c(e);
    };
    a.processAudio = function(b, c) {
      var e = Z(function() {
        var d = b.byteLength,
          f = ua(d);
        new Uint8Array(H.buffer, f, d).set(new Uint8Array(b));
        return a._ogv_audio_decoder_process_audio(f, d);
      });
      c(e);
    };
    a.close = function() {};

    return OGVDecoderAudioOpusW.ready;
  };
})();
if (typeof exports === 'object' && typeof module === 'object')
  module.exports = OGVDecoderAudioOpusW;
else if (typeof define === 'function' && define['amd'])
  define([], function() {
    return OGVDecoderAudioOpusW;
  });
else if (typeof exports === 'object')
  exports['OGVDecoderAudioOpusW'] = OGVDecoderAudioOpusW;


================================================
FILE: public/ogv/ogv-demuxer-ogg-wasm.js
================================================
var OGVDemuxerOggW = (() => {
  var _scriptDir =
    typeof document !== 'undefined' && document.currentScript
      ? document.currentScript.src
      : undefined;
  if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
  return function(OGVDemuxerOggW) {
    OGVDemuxerOggW = OGVDemuxerOggW || {};

    var a;
    a || (a = typeof OGVDemuxerOggW !== 'undefined' ? OGVDemuxerOggW : {});
    var h, k;
    a.ready = new Promise(function(b, c) {
      h = b;
      k = c;
    });
    var l = Object.assign({}, a),
      m = 'object' == typeof window,
      n = 'function' == typeof importScripts,
      p = '',
      q,
      r,
      t,
      fs,
      u,
      v;
    if (
      'object' == typeof process &&
      'object' == typeof process.versions &&
      'string' == typeof process.versions.node
    )
      (p = n ? require('path').dirname(p) + '/' : __dirname + '/'),
        (v = () => {
          u || ((fs = require('fs')), (u = require('path')));
        }),
        (q = function(b, c) {
          v();
          b = u.normalize(b);
          return fs.readFileSync(b, c ? void 0 : 'utf8');
        }),
        (t = b => {
          b = q(b, !0);
          b.buffer || (b = new Uint8Array(b));
          return b;
        }),
        (r = (b, c, d) => {
          v();
          b = u.normalize(b);
          fs.readFile(b, function(e, f) {
            e ? d(e) : c(f.buffer);
          });
        }),
        1 < process.argv.length && process.argv[1].replace(/\\/g, '/'),
        process.argv.slice(2),
        process.on('unhandledRejection', function(b) {
          throw b;
        }),
        (a.inspect = function() {
          return '[Emscripten Module object]';
        });
    else if (m || n)
      n
        ? (p = self.location.href)
        : 'undefined' != typeof document &&
          document.currentScript &&
          (p = document.currentScript.src),
        _scriptDir && (p = _scriptDir),
        0 !== p.indexOf('blob:')
          ? (p = p.substr(0, p.replace(/[?#].*/, '').lastIndexOf('/') + 1))
          : (p = ''),
        (q = b => {
          var c = new XMLHttpRequest();
          c.open('GET', b, !1);
          c.send(null);
          return c.responseText;
        }),
        n &&
          (t = b => {
            var c = new XMLHttpRequest();
            c.open('GET', b, !1);
            c.responseType = 'arraybuffer';
            c.send(null);
            return new Uint8Array(c.response);
          }),
        (r = (b, c, d) => {
          var e = new XMLHttpRequest();
          e.open('GET', b, !0);
          e.responseType = 'arraybuffer';
          e.onload = () => {
            200 == e.status || (0 == e.status && e.response)
              ? c(e.response)
              : d();
          };
          e.onerror = d;
          e.send(null);
        });
    a.print || console.log.bind(console);
    var w = a.printErr || console.warn.bind(console);
    Object.assign(a, l);
    l = null;
    var x;
    a.wasmBinary && (x = a.wasmBinary);
    var noExitRuntime = a.noExitRuntime || !0;
    'object' != typeof WebAssembly && y('no native wasm support detected');
    var z,
      A = !1;
    'undefined' != typeof TextDecoder && new TextDecoder('utf8');
    var B, C, D;
    function E() {
      var b = z.buffer;
      B = b;
      a.HEAP8 = new Int8Array(b);
      a.HEAP16 = new Int16Array(b);
      a.HEAP32 = D = new Int32Array(b);
      a.HEAPU8 = C = new Uint8Array(b);
      a.HEAPU16 = new Uint16Array(b);
      a.HEAPU32 = new Uint32Array(b);
      a.HEAPF32 = new Float32Array(b);
      a.HEAPF64 = new Float64Array(b);
    }
    var F,
      G = [],
      H = [],
      I = [];
    function aa() {
      var b = a.preRun.shift();
      G.unshift(b);
    }
    var J = 0,
      K = null,
      L = null;
    a.preloadedImages = {};
    a.preloadedAudios = {};
    function y(b) {
      if (a.onAbort) a.onAbort(b);
      b = 'Aborted(' + b + ')';
      w(b);
      A = !0;
      b = new WebAssembly.RuntimeError(
        b + '. Build with -s ASSERTIONS=1 for more info.'
      );
      k(b);
      throw b;
    }
    function M() {
      return N.startsWith('data:application/octet-stream;base64,');
    }
    var N;
    N = 'ogv-demuxer-ogg-wasm.wasm';
    if (!M()) {
      var O = N;
      N = a.locateFile ? a.locateFile(O, p) : p + O;
    }
    function P() {
      var b = N;
      try {
        if (b == N && x) return new Uint8Array(x);
        if (t) return t(b);
        throw 'both async and sync fetching of the wasm failed';
      } catch (c) {
        y(c);
      }
    }
    function ba() {
      if (!x && (m || n)) {
        if ('function' == typeof fetch && !N.startsWith('file://'))
          return fetch(N, { credentials: 'same-origin' })
            .then(function(b) {
              if (!b.ok) throw "failed to load wasm binary file at '" + N + "'";
              return b.arrayBuffer();
            })
            .catch(function() {
              return P();
            });
        if (r)
          return new Promise(function(b, c) {
            r(
              N,
              function(d) {
                b(new Uint8Array(d));
              },
              c
            );
          });
      }
      return Promise.resolve().then(function() {
        return P();
      });
    }
    function Q(b) {
      for (; 0 < b.length; ) {
        var c = b.shift();
        if ('function' == typeof c) c(a);
        else {
          var d = c.B;
          'number' == typeof d
            ? void 0 === c.v
              ? R(d)()
              : R(d)(c.v)
            : d(void 0 === c.v ? null : c.v);
        }
      }
    }
    var S = [];
    function R(b) {
      var c = S[b];
      c || (b >= S.length && (S.length = b + 1), (S[b] = c = F.get(b)));
      return c;
    }
    var T = {},
      ca = {
        f: function(b, c, d) {
          C.copyWithin(b, c, c + d);
        },
        d: function(b) {
          var c = C.length;
          b >>>= 0;
          if (2147483648 < b) return !1;
          for (var d = 1; 4 >= d; d *= 2) {
            var e = c * (1 + 0.2 / d);
            e = Math.min(e, b + 100663296);
            var f = Math;
            e = Math.max(b, e);
            f = f.min.call(f, 2147483648, e + ((65536 - (e % 65536)) % 65536));
            a: {
              try {
                z.grow((f - B.byteLength + 65535) >>> 16);
                E();
                var g = 1;
                break a;
              } catch (ea) {}
              g = void 0;
            }
            if (g) return !0;
          }
          return !1;
        },
        e: function(b, c, d, e) {
          b = T.C(b);
          c = T.A(b, c, d);
          D[e >> 2] = c;
          return 0;
        },
        a: function(b, c, d, e) {
          var f = z.buffer;
          a.audioPackets.push({
            data: f.slice
              ? f.slice(b, b + c)
              : new Uint8Array(new Uint8Array(f, b, c)).buffer,
            timestamp: d,
            discardPadding: e
          });
        },
        c: function(b, c) {
          function d(e) {
            for (var f = '', g = new Uint8Array(z.buffer); 0 != g[e]; e++)
              f += String.fromCharCode(g[e]);
            return f;
          }
          b && (a.videoCodec = d(b));
          c && (a.audioCodec = d(c));
          b = a._ogv_demuxer_media_duration();
          a.duration = 0 <= b ? b : NaN;
          a.loadedMetadata = !0;
        },
        b: function(b, c, d, e, f) {
          var g = z.buffer;
          a.videoPackets.push({
            data: g.slice
              ? g.slice(b, b + c)
              : new Uint8Array(new Uint8Array(g, b, c)).buffer,
            timestamp: d,
            keyframeTimestamp: e,
            isKeyframe: !!f
          });
        }
      };
    (function() {
      function b(f) {
        a.asm = f.exports;
        z = a.asm.g;
        E();
        F = a.asm.s;
        H.unshift(a.asm.h);
        J--;
        a.monitorRunDependencies && a.monitorRunDependencies(J);
        0 == J &&
          (null !== K && (clearInterval(K), (K = null)),
          L && ((f = L), (L = null), f()));
      }
      function c(f) {
        b(f.instance);
      }
      function d(f) {
        return ba()
          .then(function(g) {
            return WebAssembly.instantiate(g, e);
          })
          .then(function(g) {
            return g;
          })
          .then(f, function(g) {
            w('failed to asynchronously prepare wasm: ' + g);
            y(g);
          });
      }
      var e = { a: ca };
      J++;
      a.monitorRunDependencies && a.monitorRunDependencies(J);
      if (a.instantiateWasm)
        try {
          return a.instantiateWasm(e, b);
        } catch (f) {
          return (
            w('Module.instantiateWasm callback failed with error: ' + f), !1
          );
        }
      (function() {
        return x ||
          'function' != typeof WebAssembly.instantiateStreaming ||
          M() ||
          N.startsWith('file://') ||
          'function' != typeof fetch
          ? d(c)
          : fetch(N, { credentials: 'same-origin' }).then(function(f) {
              return WebAssembly.instantiateStreaming(f, e).then(c, function(
                g
              ) {
                w('wasm streaming compile failed: ' + g);
                w('falling back to ArrayBuffer instantiation');
                return d(c);
              });
            });
      })().catch(k);
      return {};
    })();
    a.___wasm_call_ctors = function() {
      return (a.___wasm_call_ctors = a.asm.h).apply(null, arguments);
    };
    a._ogv_demuxer_init = function() {
      return (a._ogv_demuxer_init = a.asm.i).apply(null, arguments);
    };
    a._ogv_demuxer_receive_input = function() {
      return (a._ogv_demuxer_receive_input = a.asm.j).apply(null, arguments);
    };
    a._ogv_demuxer_process = function() {
      return (a._ogv_demuxer_process = a.asm.k).apply(null, arguments);
    };
    a._ogv_demuxer_destroy = function() {
      return (a._ogv_demuxer_destroy = a.asm.l).apply(null, arguments);
    };
    a._ogv_demuxer_media_length = function() {
      return (a._ogv_demuxer_media_length = a.asm.m).apply(null, arguments);
    };
    a._ogv_demuxer_media_duration = function() {
      return (a._ogv_demuxer_media_duration = a.asm.n).apply(null, arguments);
    };
    a._ogv_demuxer_seekable = function() {
      return (a._ogv_demuxer_seekable = a.asm.o).apply(null, arguments);
    };
    a._ogv_demuxer_keypoint_offset = function() {
      return (a._ogv_demuxer_keypoint_offset = a.asm.p).apply(null, arguments);
    };
    a._ogv_demuxer_seek_to_keypoint = function() {
      return (a._ogv_demuxer_seek_to_keypoint = a.asm.q).apply(null, arguments);
    };
    a._ogv_demuxer_flush = function() {
      return (a._ogv_demuxer_flush = a.asm.r).apply(null, arguments);
    };
    a._malloc = function() {
      return (a._malloc = a.asm.t).apply(null, arguments);
    };
    a._free = function() {
      return (a._free = a.asm.u).apply(null, arguments);
    };
    var U;
    L = function da() {
      U || V();
      U || (L = da);
    };
    function V() {
      function b() {
        if (!U && ((U = !0), (a.calledRun = !0), !A)) {
          Q(H);
          h(a);
          if (a.onRuntimeInitialized) a.onRuntimeInitialized();
          if (a.postRun)
            for (
              'function' == typeof a.postRun && (a.postRun = [a.postRun]);
              a.postRun.length;

            ) {
              var c = a.postRun.shift();
              I.unshift(c);
            }
          Q(I);
        }
      }
      if (!(0 < J)) {
        if (a.preRun)
          for (
            'function' == typeof a.preRun && (a.preRun = [a.preRun]);
            a.preRun.length;

          )
            aa();
        Q(G);
        0 < J ||
          (a.setStatus
            ? (a.setStatus('Running...'),
              setTimeout(function() {
                setTimeout(function() {
                  a.setStatus('');
                }, 1);
                b();
              }, 1))
            : b());
      }
    }
    a.run = V;
    if (a.preInit)
      for (
        'function' == typeof a.preInit && (a.preInit = [a.preInit]);
        0 < a.preInit.length;

      )
        a.preInit.pop()();
    V();
    var W, X, Y;
    'undefined' === typeof performance || 'undefined' === typeof performance.now
      ? (Y = Date.now)
      : (Y = performance.now.bind(performance));
    function Z(b) {
      var c = Y();
      b = b();
      c = Y() - c;
      a.cpuTime += c;
      return b;
    }
    a.loadedMetadata = !1;
    a.videoCodec = null;
    a.audioCodec = null;
    a.duration = NaN;
    a.onseek = null;
    a.cpuTime = 0;
    a.audioPackets = [];
    Object.defineProperty(a, 'hasAudio', {
      get: function() {
        return a.loadedMetadata && a.audioCodec;
      }
    });
    Object.defineProperty(a, 'audioReady', {
      get: function() {
        return 0 < a.audioPackets.length;
      }
    });
    Object.defineProperty(a, 'audioTimestamp', {
      get: function() {
        return 0 < a.audioPackets.length ? a.audioPackets[0].timestamp : -1;
      }
    });
    a.videoPackets = [];
    Object.defineProperty(a, 'hasVideo', {
      get: function() {
        return a.loadedMetadata && a.videoCodec;
      }
    });
    Object.defineProperty(a, 'frameReady', {
      get: function() {
        return 0 < a.videoPackets.length;
      }
    });
    Object.defineProperty(a, 'frameTimestamp', {
      get: function() {
        return 0 < a.videoPackets.length ? a.videoPackets[0].timestamp : -1;
      }
    });
    Object.defineProperty(a, 'keyframeTimestamp', {
      get: function() {
        return 0 < a.videoPackets.length
          ? a.videoPackets[0].keyframeTimestamp
          : -1;
      }
    });
    Object.defineProperty(a, 'nextKeyframeTimestamp', {
      get: function() {
        for (var b = 0; b < a.videoPackets.length; b++) {
          var c = a.videoPackets[b];
          if (c.isKeyframe) return c.timestamp;
        }
        return -1;
      }
    });
    Object.defineProperty(a, 'processing', {
      get: function() {
        return !1;
      }
    });
    Object.defineProperty(a, 'seekable', {
      get: function() {
        return !!a._ogv_demuxer_seekable();
      }
    });
    a.init = function(b) {
      Z(function() {
        a._ogv_demuxer_init();
      });
      b();
    };
    a.receiveInput = function(b, c) {
      Z(function() {
        var d = b.byteLength;
        (W && X >= d) || (W && a._free(W), (X = d), (W = a._malloc(X)));
        var e = W;
        new Uint8Array(z.buffer, e, d).set(new Uint8Array(b));
        a._ogv_demuxer_receive_input(e, d);
      });
      c();
    };
    a.process = function(b) {
      var c = Z(function() {
        return a._ogv_demuxer_process();
      });
      b(!!c);
    };
    a.dequeueVideoPacket = function(b) {
      if (a.videoPackets.length) {
        var c = a.videoPackets.shift().data;
        b(c);
      } else b(null);
    };
    a.dequeueAudioPacket = function(b) {
      if (a.audioPackets.length) {
        var c = a.audioPackets.shift();
        b(c.data, c.discardPadding);
      } else b(null);
    };
    a.getKeypointOffset = function(b, c) {
      var d = Z(function() {
        return a._ogv_demuxer_keypoint_offset(1e3 * b);
      });
      c(d);
    };
    a.seekToKeypoint = function(b, c) {
      var d = Z(function() {
        return a._ogv_demuxer_seek_to_keypoint(1e3 * b);
      });
      d &&
        (a.audioPackets.splice(0, a.audioPackets.length),
        a.videoPackets.splice(0, a.videoPackets.length));
      c(!!d);
    };
    a.flush = function(b) {
      Z(function() {
        a.audioPackets.splice(0, a.audioPackets.length);
        a.videoPackets.splice(0, a.videoPackets.length);
        a._ogv_demuxer_flush();
      });
      b();
    };
    a.close = function() {};

    return OGVDemuxerOggW.ready;
  };
})();
if (typeof exports === 'object' && typeof module === 'object')
  module.exports = OGVDemuxerOggW;
else if (typeof define === 'function' && define['amd'])
  define([], function() {
    return OGVDemuxerOggW;
  });
else if (typeof exports === 'object')
  exports['OGVDemuxerOggW'] = OGVDemuxerOggW;


================================================
FILE: public/ogv/ogv-worker-audio.js
================================================
(() => {
  var e = {
      506: e => {
        (e.exports = function _assertThisInitialized(e) {
          if (void 0 === e)
            throw new ReferenceError(
              "this hasn't been initialised - super() hasn't been called"
            );
          return e;
        }),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      575: e => {
        (e.exports = function _classCallCheck(e, t) {
          if (!(e instanceof t))
            throw new TypeError('Cannot call a class as a function');
        }),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      913: e => {
        function _defineProperties(e, t) {
          for (var r = 0; r < t.length; r++) {
            var o = t[r];
            (o.enumerable = o.enumerable || !1),
              (o.configurable = !0),
              'value' in o && (o.writable = !0),
              Object.defineProperty(e, o.key, o);
          }
        }
        (e.exports = function _createClass(e, t, r) {
          return (
            t && _defineProperties(e.prototype, t),
            r && _defineProperties(e, r),
            Object.defineProperty(e, 'prototype', { writable: !1 }),
            e
          );
        }),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      754: e => {
        function _getPrototypeOf(t) {
          return (
            (e.exports = _getPrototypeOf = Object.setPrototypeOf
              ? Object.getPrototypeOf
              : function _getPrototypeOf(e) {
                  return e.__proto__ || Object.getPrototypeOf(e);
                }),
            (e.exports.__esModule = !0),
            (e.exports.default = e.exports),
            _getPrototypeOf(t)
          );
        }
        (e.exports = _getPrototypeOf),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      205: (e, t, r) => {
        var o = r(489);
        (e.exports = function _inherits(e, t) {
          if ('function' != typeof t && null !== t)
            throw new TypeError(
              'Super expression must either be null or a function'
            );
          (e.prototype = Object.create(t && t.prototype, {
            constructor: { value: e, writable: !0, configurable: !0 }
          })),
            Object.defineProperty(e, 'prototype', { writable: !1 }),
            t && o(e, t);
        }),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      318: e => {
        (e.exports = function _interopRequireDefault(e) {
          return e && e.__esModule ? e : { default: e };
        }),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      585: (e, t, r) => {
        var o = r(8).default,
          s = r(506);
        (e.exports = function _possibleConstructorReturn(e, t) {
          if (t && ('object' === o(t) || 'function' == typeof t)) return t;
          if (void 0 !== t)
            throw new TypeError(
              'Derived constructors may only return object or undefined'
            );
          return s(e);
        }),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      489: e => {
        function _setPrototypeOf(t, r) {
          return (
            (e.exports = _setPrototypeOf =
              Object.setPrototypeOf ||
              function _setPrototypeOf(e, t) {
                return (e.__proto__ = t), e;
              }),
            (e.exports.__esModule = !0),
            (e.exports.default = e.exports),
            _setPrototypeOf(t, r)
          );
        }
        (e.exports = _setPrototypeOf),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      8: e => {
        function _typeof(t) {
          return (
            (e.exports = _typeof =
              'function' == typeof Symbol && 'symbol' == typeof Symbol.iterator
                ? function(e) {
                    return typeof e;
                  }
                : function(e) {
                    return e &&
                      'function' == typeof Symbol &&
                      e.constructor === Symbol &&
                      e !== Symbol.prototype
                      ? 'symbol'
                      : typeof e;
                  }),
            (e.exports.__esModule = !0),
            (e.exports.default = e.exports),
            _typeof(t)
          );
        }
        (e.exports = _typeof),
          (e.exports.__esModule = !0),
          (e.exports.default = e.exports);
      },
      445: (e, t, r) => {
        'use strict';
        var o = r(318);
        Object.defineProperty(t, '__esModule', { value: !0 }),
          (t.default = void 0);
        var s = o(r(575)),
          n = o(r(913)),
          a = o(r(539)),
          i = '1.8.9-20220406232920-cb5f7ff',
          u = {
            OGVDemuxerOggW: 'ogv-demuxer-ogg-wasm.js',
            OGVDemuxerWebMW: 'ogv-demuxer-webm-wasm.js',
            OGVDecoderAudioOpusW: 'ogv-decoder-audio-opus-wasm.js',
            OGVDecoderAudioVorbisW: 'ogv-decoder-audio-vorbis-wasm.js',
            OGVDecoderVideoTheoraW: 'ogv-decoder-video-theora-wasm.js',
            OGVDecoderVideoVP8W: 'ogv-decoder-video-vp8-wasm.js',
            OGVDecoderVideoVP8MTW: 'ogv-decoder-video-vp8-mt-wasm.js',
            OGVDecoderVideoVP9W: 'ogv-decoder-video-vp9-wasm.js',
            OGVDecoderVideoVP9SIMDW: 'ogv-decoder-video-vp9-simd-wasm.js',
            OGVDecoderVideoVP9MTW: 'ogv-decoder-video-vp9-mt-wasm.js',
            OGVDecoderVideoVP9SIMDMTW: 'ogv-decoder-video-vp9-simd-mt-wasm.js',
            OGVDecoderVideoAV1W: 'ogv-decoder-video-av1-wasm.js',
            OGVDecoderVideoAV1SIMDW: 'ogv-decoder-video-av1-simd-wasm.js',
            OGVDecoderVideoAV1MTW: 'ogv-decoder-video-av1-mt-wasm.js',
            OGVDecoderVideoAV1SIMDMTW: 'ogv-decoder-video-av1-simd-mt-wasm.js'
          },
          d = (function() {
            function OGVLoaderBase() {
              (0, s.default)(this, OGVLoaderBase),
                (this.base = this.defaultBase());
            }
            return (
              (0, n.default)(OGVLoaderBase, [
                { key: 'defaultBase', value: function defaultBase() {} },
                {
                  key: 'wasmSupported',
                  value: function wasmSupported() {
                    return a.default.wasmSupported();
                  }
                },
                {
                  key: 'scriptForClass',
                  value: function scriptForClass(e) {
                    return u[e];
                  }
                },
                {
                  key: 'urlForClass',
                  value: function urlForClass(e) {
                    var t = this.scriptForClass(e);
                    if (t) return this.urlForScript(t);
                    throw new Error('asked for URL for unknown class ' + e);
                  }
                },
                {
                  key: 'urlForScript',
                  value: function urlForScript(e) {
                    if (e) {
                      var t = this.base;
                      return (
                        void 0 === t ? (t = '') : (t += '/'),
                        t + e + '?version=' + encodeURIComponent(i)
                      );
                    }
                    throw new Error('asked for URL for unknown script ' + e);
                  }
                },
                {
                  key: 'loadClass',
                  value: function loadClass(e, t, r) {
                    var o = this;
                    r = r || {};
                    var s = this.getGlobal(),
                      n = this.urlForClass(e),
                      a = function classWrapper(t) {
                        return (
                          ((t = t || {}).locateFile = function(e) {
                            return 'data:' === e.slice(0, 5)
                              ? e
                              : o.urlForScript(e);
                          }),
                          (t.mainScriptUrlOrBlob =
                            o.scriptForClass(e) +
                            '?version=' +
                            encodeURIComponent(i)),
                          s[e](t)
                        );
                      };
                    'function' == typeof s[e]
                      ? t(a)
                      : this.loadScript(n, function() {
                          t(a);
                        });
                  }
                }
              ]),
              OGVLoaderBase
            );
          })();
        t.default = d;
      },
      713: (e, t, r) => {
        'use strict';
        var o = r(318);
        Object.defineProperty(t, '__esModule', { value: !0 }),
          (t.default = void 0);
        var s = o(r(575)),
          n = o(r(913)),
          a = o(r(205)),
          i = o(r(585)),
          u = o(r(754));
        function _createSuper(e) {
          var t = (function _isNativeReflectConstruct() {
            if ('undefined' == typeof Reflect || !Reflect.construct) return !1;
            if (Reflect.construct.sham) return !1;
            if ('function' == typeof Proxy) return !0;
            try {
              return (
                Boolean.prototype.valueOf.call(
                  Reflect.construct(Boolean, [], function() {})
                ),
                !0
              );
            } catch (e) {
              return !1;
            }
          })();
          return function _createSuperInternal() {
            var r,
              o = (0, u.default)(e);
            if (t) {
              var s = (0, u.default)(this).constructor;
              r = Reflect.construct(o, arguments, s);
            } else r = o.apply(this, arguments);
            return (0, i.default)(this, r);
          };
        }
        var d = new ((function(e) {
          (0, a.default)(OGVLoaderWorker, e);
          var t = _createSuper(OGVLoaderWorker);
          function OGVLoaderWorker() {
            return (
              (0, s.default)(this, OGVLoaderWorker), t.apply(this, arguments)
            );
          }
          return (
            (0, n.default)(OGVLoaderWorker, [
              {
                key: 'loadScript',
                value: function loadScript(e, t) {
                  importScripts(e), t();
                }
              },
              {
                key: 'getGlobal',
                value: function getGlobal() {
                  return self;
                }
              }
            ]),
            OGVLoaderWorker
          );
        })(o(r(445)).default))();
        t.default = d;
      },
      607: (e, t, r) => {
        'use strict';
        var o = r(318);
        Object.defineProperty(t, '__esModule', { value: !0 }),
          (t.default = void 0);
        var s = new (o(r(172))).default(
          ['loadedMetadata', 'audioFormat', 'audioBuffer', 'cpuTime'],
          {
            init: function init(e, t) {
              this.target.init(t);
            },
            processHeader: function processHeader(e, t) {
              this.target.processHeader(e[0], function(e) {
                t([e]);
              });
            },
            processAudio: function processAudio(e, t) {
              this.target.processAudio(e[0], function(e) {
                t([e]);
              });
            }
          }
        );
        t.default = s;
      },
      172: (e, t, r) => {
        'use strict';
        var o = r(318);
        Object.defineProperty(t, '__esModule', { value: !0 }),
          (t.default = void 0);
        var s = o(r(8)),
          n = o(r(575)),
          a = o(r(913)),
          i = o(r(713));
        r.g.OGVLoader = i.default;
        var u = (function() {
          function OGVWorkerSupport(e, t) {
            var r = this;
            (0, n.default)(this, OGVWorkerSupport),
              (this.propList = e),
              (this.handlers = t),
              (this.transferables = (function() {
                var e = new ArrayBuffer(1024),
                  t = new Uint8Array(e);
                try {
                  return (
                    postMessage({ action: 'transferTest', bytes: t }, [e]),
                    !e.byteLength
                  );
                } catch (e) {
                  return !1;
                }
              })()),
              (this.target = null),
              (this.sentProps = {}),
              (this.pendingEvents = []),
              (this.handlers.construct = function(e, t) {
                var o = e[0],
                  s = e[1];
                i.default.loadClass(o, function(e) {
                  e(s).then(function(e) {
                    for (r.target = e, t(); r.pendingEvents.length; )
                      r.handleEvent(r.pendingEvents.shift());
                  });
                });
              }),
              addEventListener('message', function(e) {
                r.workerOnMessage(e);
              });
          }
          return (
            (0, a.default)(OGVWorkerSupport, [
              {
                key: 'handleEvent',
                value: function handleEvent(e) {
                  var t = this;
                  this.handlers[e.action].call(this, e.args, function(r) {
                    r = r || [];
                    var o = {},
                      s = [];
                    t.propList.forEach(function(e) {
                      var r = t.target[e];
                      if (t.sentProps[e] !== r)
                        if (
                          ((t.sentProps[e] = r),
                          'duration' == e && isNaN(r) && isNaN(t.sentProps[e]))
                        );
                        else if ('audioBuffer' == e) {
                          if (((o[e] = r), r))
                            for (var n = 0; n < r.length; n++)
                              s.push(r[n].buffer);
                        } else
                          'frameBuffer' == e
                            ? ((o[e] = r),
                              r &&
                                (s.push(r.y.bytes.buffer),
                                s.push(r.u.bytes.buffer),
                                s.push(r.v.bytes.buffer)))
                            : (o[e] = r);
                    });
                    var n = {
                      action: 'callback',
                      callbackId: e.callbackId,
                      args: r,
                      props: o
                    };
                    t.transferables ? postMessage(n, s) : postMessage(n);
                  });
                }
              },
              {
                key: 'workerOnMessage',
                value: function workerOnMessage(e) {
                  var t = e.data;
                  t &&
                    'object' === (0, s.default)(t) &&
                    ('transferTest' == t.action ||
                      ('string' != typeof t.action ||
                      'string' != typeof t.callbackId ||
                      'object' !== (0, s.default)(t.args)
                        ? console.log('invalid message data', t)
                        : t.action in this.handlers
                        ? 'construct' == t.action || this.target
                          ? this.handleEvent(t)
                          : this.pendingEvents.push(t)
                        : console.log('invalid message action', t.action)));
                }
              }
            ]),
            OGVWorkerSupport
          );
        })();
        t.default = u;
      },
      539: (e, t, r) => {
        'use strict';
        var o = r(318);
        Object.defineProperty(t, '__esModule', { value: !0 }),
          (t.default = void 0);
        var s = o(r(8)),
          n = o(r(575)),
          a = o(r(913));
        var i = new ((function() {
          function WebAssemblyChecker() {
            (0, n.default)(this, WebAssemblyChecker),
              (this.tested = !1),
              (this.testResult = void 0);
          }
          return (
            (0, a.default)(WebAssemblyChecker, [
              {
                key: 'wasmSupported',
                value: function wasmSupported() {
                  if (!this.tested) {
                    try {
                      'object' ===
                      ('undefined' == typeof WebAssembly
                        ? 'undefined'
                        : (0, s.default)(WebAssembly))
                        ? (this.testResult = (function testSafariWebAssemblyBug() {
                            var e = new Uint8Array([
                                0,
                                97,
                                115,
                                109,
                                1,
                                0,
                                0,
                                0,
                                1,
                                6,
                                1,
                                96,
                                1,
                                127,
                                1,
                                127,
                                3,
                                2,
                                1,
                                0,
                                5,
                                3,
                                1,
                                0,
                                1,
                                7,
                                8,
                                1,
                                4,
                                116,
                                101,
                                115,
                                116,
                                0,
                                0,
                                10,
                                16,
                                1,
                                14,
                                0,
                                32,
                                0,
                                65,
                                1,
                                54,
                                2,
                                0,
                                32,
                                0,
                                40,
                                2,
                                0,
                                11
                              ]),
                              t = new WebAssembly.Module(e);
                            return (
                              0 !==
                              new WebAssembly.Instance(t, {}).exports.test(4)
                            );
                          })())
                        : (this.testResult = !1);
                    } catch (e) {
                      console.log('Exception while testing WebAssembly', e),
                        (this.testResult = !1);
                    }
                    this.tested = !0;
                  }
                  return this.testResult;
                }
              }
            ]),
            WebAssemblyChecker
          );
        })())();
        t.default = i;
      }
    },
    t = {};
  function __webpack_require__(r) {
    var o = t[r];
    if (void 0 !== o) return o.exports;
    var s = (t[r] = { exports: {} });
    return e[r](s, s.exports, __webpack_require__), s.exports;
  }
  (__webpack_require__.g = (function() {
    if ('object' == typeof globalThis) return globalThis;
    try {
      return this || new Function('return this')();
    } catch (e) {
      if ('object' == typeof window) return window;
    }
  })()),
    (() => {
      'use strict';
      __webpack_require__(318)(__webpack_require__(607));
    })();
})();


================================================
FILE: roadmap.md
================================================
# Cboard Project Roadmap

This document outlines the development plan from a high level and will be updated as development progresses towards version 2.0. It should be noted that this roadmap applies to the project as a whole (frontend/backend).

## ✔️2020  Quarter 4 - Fixed boards support 
The quarter focuses on providing support boards that can be fixed in terms of rows and columns, and easily edited at the same time. This will allow to choose a position on the board to add a new tile, and when deleting a tile when locked, it deletes the tile and other tiles do not move position.

## 2021 Quarter 1 - Cloud TTS voices
The quarter deals mainly with adding the ability to use online services for the text to speech. There are many suitable cloud services (Azure?).
This will allow to speak more naturally, choosing from more voices and more languages and variants. We can customize voices, and access voices with different speaking styles and emotional tones.

## 2021 Quarter 2 - Improve navigation between tiles and folders
Implement machine learning algorithms to offer automated navigation across boards based on usage data, context (time and location), and text predictions.
Milestones are: 
- Platform updates and requirements gathering.
- Python and Sci-kit in our cloud.
- Cboard to support automated navigation on the code (UI updates ).


## 2021 Quarter 3 - Launch Machine learning

The third quarter continues some efforts from Q2, mainly setting up a ML model.
Milestones are: 
- Gathering Data.
- Define boards set.
- Implement data collect via Cboard API using anonymous data.
- Define the model. Decision Trees/Random Forest algorithms?
- Classification best fit

Launch to production and evaluate the model with production data.

================================================
FILE: rootfs/etc/nginx/conf.d/default.conf
================================================
access_log  /proc/self/fd/1;
error_log /proc/self/fd/2 info;

server {
    listen       80 default_server;
    server_name  _;

    root   /usr/share/nginx/html;
    index  index.html index.html;

    try_files $uri $uri/ /index.html;
    
    set $auth_basic off;
    if (-f /auth/.htpasswd) {
      set $auth_basic "Restricted Content";
    }
    auth_basic $auth_basic;
    auth_basic_user_file /auth/.htpasswd;

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

================================================
FILE: rootfs/etc/nginx/conf.d/gzip.conf
================================================
gzip on;
gzip_disable "msie6";

gzip_vary on;
gzip_proxied expired no-cache no-store private auth;

#compression level
gzip_comp_level 6;
gzip_min_length 1000;
gzip_buffers 16 8k;
gzip_http_version 1.1;

# files to gzip
gzip_types text/plain
           text/css
           text/javascript
           text/xml
           application/json
           application/javascript
           application/x-font-ttf
           application/xml
           application/xml+rss 
           image/svg+xml;

================================================
FILE: scripts/arasaac-create-files.js
================================================
const { writeFile } = require('fs');
const https = require('https');

const ARASAAC_BASE_PATH_API = 'https://api.arasaac.org/api/';

const locales = [
  'ar',
  'bg',
  'de',
  'el',
  'en',
  'es',
  'fr',
  'he',
  'hr',
  'hu',
  'it',
  'ko',
  'mk',
  'nl',
  'pl',
  'pt',
  'ro',
  'ru',
  'sk',
  'sq',
  'sv',
  'sr',
  'uk',
  'zh'
];

console.log('Fetching symbol data from ARASAAC API .....');

locales.forEach(async locale => {
  const pictosPath = `pictograms/all/${locale}`;
  let jsonData = [];
  try {
    https.get(ARASAAC_BASE_PATH_API + pictosPath, res => {
      let data = [];

      res.on('data', chunk => {
        data.push(chunk);
      });

      res.on('end', () => {
        console.log('. . . SUCCESS Fetching symbol data for locale ' + locale);
        jsonData = JSON.parse(Buffer.concat(data).toString());
        const result = [];
        jsonData.forEach(element => {
          var keywords = [];
          element['keywords'].forEach(kw => {
            keywords.push(kw['keyword']);
          });
          const picto = {
            id: element['_id'],
            kw: keywords
          };
          result.push(picto);
        });

        writeFile(
          './src/api/arasaac/' + locale + '.json',
          JSON.stringify(result),
          error => {
            if (error) {
              console.log('An error has occurred ', error);
              return;
            }
            console.log('Data written successfully to disk');
          }
        );
      });
    });
  } catch (err) {
    console.log('ERROR Failed to fetch symbol data for locale ' + locale);
    console.log(err.message);
    return;
  }
});


================================================
FILE: scripts/arasaac-download-symbols.js
================================================
const fs = require('fs');
const Axios = require('axios');

const ARASAAC_BASE_PATH_API = 'https://api.arasaac.org/api/';
const jsonData = require('../src/api/arasaac/es.json');
const pictosPath = `pictograms/`;

console.log('Fetching symbol data from ARASAAC API .....');

async function downloadFile(fileUrl, outputLocationPath) {
  const writer = fs.createWriteStream(outputLocationPath);

  return Axios({
    method: 'get',
    url: fileUrl,
    responseType: 'stream'
  }).then(response => {
    //ensure that the user can call `then()` only when the file has
    //been downloaded entirely.

    return new Promise((resolve, reject) => {
      response.data.pipe(writer);
      let error = null;
      writer.on('error', err => {
        error = err;
        writer.close();
        reject(err);
      });
      writer.on('close', () => {
        if (!error) {
          resolve(true);
        }
        //no need to call the reject here, as it will have been called in the
        //'error' stream;
      });
    });
  });
}

for (let i = 1; i < Math.trunc(jsonData.length / 100); i++) {
  (function(ind) {
    setTimeout(function() {
      for (let j = (i - 1) * 100; j < i * 100; j++) {
        const picto = jsonData[j];
        console.log(j);
        try {
          const apipath = ARASAAC_BASE_PATH_API + pictosPath + picto['id'];
          console.log(apipath);
          const path = './src/api/arasaac/symbols/' + picto['id'] + '.png';
          downloadFile(apipath, path).then(console.log('ok!'));
        } catch (err) {
          console.log('ERROR Failed to fetch symbol data');
          console.log(err.message);
          return;
        }
      }
    }, 1000 + 7000 * ind);
  })(i);
}


================================================
FILE: scripts/crowdin-fetch-latest.js
================================================
const crowdinTranslations = require('@crowdin/crowdin-api-client').Translations;
const https = require('https');
const fs = require('fs');
const resolve = require('path').resolve;
const DecompressZip = require('decompress-zip');

// crowdin api key
const CROWDIN_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN;
const CROWDIN_PROJECT_ID = 262825;

// initialization of crowdin client
const credentials = {
  token: CROWDIN_TOKEN,
};

// create an instance of the crowdin client
const translationApi = new crowdinTranslations(credentials);

// paths
const zipFilePath = resolve('./alltx.zip');
const extractPath = resolve('./downloads');
const helpExtractPath = resolve('./src/translations/help');
const moreLanguagesExtractPath = resolve('./src/translations/moreLanguages');
const langExtractPath = resolve('./src/translations');
const cboardSrcPath = resolve('./src/translations/src/cboard.json');

const downloadCboardJson = (onComplete) => {
  console.log('Trying to download latest cboard.json...');

  const cboardJson = fs.createWriteStream(cboardSrcPath);
  https.get(
    `https://api.crowdin.com/api/project/cboard/export-file?file=cboard.json&language=en&key=${CROWDIN_API_KEY}`,
    function (response) {
      response.pipe(cboardJson);
      cboardJson.on('finish', function () {
        console.log('cboard.json download complete.');
        cboardJson.close(onComplete);
      });
      cboardJson.on('error', function (err) {
        console.log('cboard.json download encountered error!');
        console.log(err);
      });
    },
  );
};

const downloadTranslations = async (onComplete) => {
  console.log('Trying to download latest translation strings...');

  // get project build
  const build = await translationApi.listProjectBuilds(CROWDIN_PROJECT_ID);
  const buildId = build.data[0].data.id;
  const download = await translationApi.downloadTranslations(
    CROWDIN_PROJECT_ID,
    buildId,
  );
  const allTxZip = fs.createWriteStream(zipFilePath);
  https.get(download.data.url, function (response) {
    response.pipe(allTxZip);
    allTxZip.on('finish', function () {
      console.log('Translation download complete.');
      allTxZip.close(onComplete);
    });
    allTxZip.on('error', function (err) {
      console.log('Translation download encountered error!');
      console.log(err);
    });
  });
};

const deleteTemporaryDownloadFile = () => {
  console.log('Deleting temp file.');
  try {
    fs.unlinkSync(zipFilePath);
    console.log(`Deleted ${zipFilePath}`);
  } catch (err) {
    console.error(`Error while deleting ${zipFilePath} ` + err.message);
  }
};

const extractTranslations = () => {
  console.log('Extracting zip to translations folder...');

  const unzipper = new DecompressZip(zipFilePath);

  unzipper.on('error', function (err) {
    console.log('DecompressZip Caught an error:', err);
  });

  unzipper.on('extract', function (log) {
    console.log('DecompressZip finished extracting.');
    //   const dirs = fs
    //     .readdirSync(langExtractPath, { withFileTypes: true })
    //     .filter(dirent => dirent.isDirectory())
    //     .map(dirent => dirent.name);
    //   //copy and rename help files
    //   dirs.forEach(dir => {
    //     fs.copyFileSync(
    //       `${langExtractPath}/${dir}/help/help.md`,
    //       `${helpExtractPath}/${dir}.md`
    //     );
    //   });
    //   dirs.forEach(dir => {
    //     fs.copyFileSync(
    //       `${langExtractPath}/${dir}/articles/moreLanguages.md`,
    //       `${moreLanguagesExtractPath}/${dir}.md`
    //     );
    //   });
    //   // delete directory recursively
    //   try {
    //     fs.rmdirSync(`${extractPath}/website`, { recursive: true });
    //     console.log(`website folder was deleted.`);
    //   } catch (err) {
    //     console.error(`Error while deleting ${extractPath}/website`);
    //   }
    deleteTemporaryDownloadFile();
    fs.readdirSync(extractPath).forEach((file) => {
      if (file.endsWith('.json')) {
        fs.copyFileSync(`${extractPath}/${file}`, `${langExtractPath}/${file}`);
      }
   
Download .txt
gitextract_i5_to3kl/

├── .circleci/
│   └── config.yml
├── .dockerignore
├── .editorconfig
├── .eslintrc
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE.md
│   ├── copilot-instructions.md
│   ├── prompts/
│   │   └── tests.prompt.md
│   └── workflows/
│       └── codeql-analysis.yml
├── .gitignore
├── .grenrc.json
├── .nvmrc
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── CBOARD_SYMBOLS_INTEGRATION.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.txt
├── Makefile
├── README.md
├── __mocks__/
│   ├── browser-image-resizer.js
│   ├── react-redux.js
│   └── undici.js
├── browserstack.yml
├── cboard.njsproj.user
├── craco.config.js
├── env/
│   ├── local-private.gpg
│   ├── local.js
│   ├── prod-private.gpg
│   └── prod.js
├── funding.json
├── package.json
├── playwright.config.ts
├── public/
│   ├── .well-known/
│   │   └── assetlinks.json
│   ├── _redirects
│   ├── index.html
│   ├── manifest.json
│   └── ogv/
│       ├── ogv-decoder-audio-opus-wasm.js
│       ├── ogv-decoder-audio-opus-wasm.wasm
│       ├── ogv-demuxer-ogg-wasm.js
│       ├── ogv-demuxer-ogg-wasm.wasm
│       └── ogv-worker-audio.js
├── roadmap.md
├── rootfs/
│   └── etc/
│       └── nginx/
│           └── conf.d/
│               ├── default.conf
│               └── gzip.conf
├── scripts/
│   ├── arasaac-create-files.js
│   ├── arasaac-download-symbols.js
│   ├── crowdin-fetch-latest.js
│   ├── crowdin-push-changes.js
│   ├── decrypt-private.sh
│   └── encrypt-private.sh
├── src/
│   ├── __mocks__/
│   │   ├── axios.js
│   │   ├── react-intl.js
│   │   ├── store.js
│   │   └── styleMock.js
│   ├── __test__/
│   │   ├── helpers.test.js
│   │   ├── i18n.test.js
│   │   ├── reducers.test.js
│   │   ├── registerServiceWorker.test.js
│   │   └── store.test.js
│   ├── analytics.js
│   ├── api/
│   │   ├── __mocks__/
│   │   │   └── api.js
│   │   ├── api.js
│   │   ├── api.test.js
│   │   ├── boards.json
│   │   ├── cboard-symbols.js
│   │   ├── communicators.json
│   │   ├── corePicSeePal.json
│   │   ├── index.js
│   │   └── mulberry-symbols.json
│   ├── appInsights.js
│   ├── common/
│   │   └── test_utils.js
│   ├── components/
│   │   ├── Account/
│   │   │   ├── Activate/
│   │   │   │   ├── Activate.actions.js
│   │   │   │   ├── Activate.container.js
│   │   │   │   ├── Activate.css
│   │   │   │   ├── Activate.messages.js
│   │   │   │   └── index.js
│   │   │   ├── ChangePassword/
│   │   │   │   ├── ChangePassword.actions.js
│   │   │   │   ├── ChangePassword.component.js
│   │   │   │   ├── ChangePassword.constants.js
│   │   │   │   ├── ChangePassword.css
│   │   │   │   ├── ChangePassword.messages.js
│   │   │   │   ├── index.js
│   │   │   │   └── validationSchema.js
│   │   │   ├── Login/
│   │   │   │   ├── Login.actions.js
│   │   │   │   ├── Login.actions.test.js
│   │   │   │   ├── Login.component.js
│   │   │   │   ├── Login.component.test.js
│   │   │   │   ├── Login.constants.js
│   │   │   │   ├── Login.css
│   │   │   │   ├── Login.messages.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Login.component.test.js.snap
│   │   │   │   ├── index.js
│   │   │   │   └── validationSchema.js
│   │   │   ├── OAuthLogin/
│   │   │   │   ├── OAuthLogin.container.js
│   │   │   │   ├── OAuthLogin.css
│   │   │   │   ├── OAuthLogin.messages.js
│   │   │   │   └── index.js
│   │   │   ├── ResetPassword/
│   │   │   │   ├── ResetPassword.actions.js
│   │   │   │   ├── ResetPassword.component.js
│   │   │   │   ├── ResetPassword.constants.js
│   │   │   │   ├── ResetPassword.css
│   │   │   │   ├── ResetPassword.messages.js
│   │   │   │   ├── index.js
│   │   │   │   └── validationSchema.js
│   │   │   └── SignUp/
│   │   │       ├── SignUp.actions.js
│   │   │       ├── SignUp.component.js
│   │   │       ├── SignUp.css
│   │   │       ├── SignUp.messages.js
│   │   │       ├── Signup.component.test.js
│   │   │       ├── __snapshots__/
│   │   │       │   └── Signup.component.test.js.snap
│   │   │       ├── index.js
│   │   │       └── validationSchema.js
│   │   ├── Analytics/
│   │   │   ├── Analytics.component.js
│   │   │   ├── Analytics.container.js
│   │   │   ├── Analytics.css
│   │   │   ├── Analytics.messages.js
│   │   │   └── index.js
│   │   ├── App/
│   │   │   ├── App.actions.js
│   │   │   ├── App.component.js
│   │   │   ├── App.constants.js
│   │   │   ├── App.container.js
│   │   │   ├── App.css
│   │   │   ├── App.messages.js
│   │   │   ├── App.reducer.js
│   │   │   ├── App.selectors.js
│   │   │   ├── __tests__/
│   │   │   │   ├── App.actions.test.js
│   │   │   │   └── App.reducer.test.js
│   │   │   └── index.js
│   │   ├── AppLoading/
│   │   │   ├── AppLoading.css
│   │   │   ├── AppLoading.js
│   │   │   └── index.js
│   │   ├── AuthScreen/
│   │   │   ├── Auth.helpers.js
│   │   │   ├── AuthScreen.component.js
│   │   │   ├── AuthScreen.component.test.js
│   │   │   ├── AuthScreen.css
│   │   │   ├── AuthScreen.messages.js
│   │   │   ├── __snapshots__/
│   │   │   │   └── AuthScreen.component.test.js.snap
│   │   │   └── index.js
│   │   ├── Board/
│   │   │   ├── Board.actions.js
│   │   │   ├── Board.analytics.js
│   │   │   ├── Board.component.js
│   │   │   ├── Board.constants.js
│   │   │   ├── Board.container.js
│   │   │   ├── Board.css
│   │   │   ├── Board.messages.js
│   │   │   ├── Board.reducer.js
│   │   │   ├── BoardShare/
│   │   │   │   ├── BoardShare.component.js
│   │   │   │   ├── BoardShare.css
│   │   │   │   ├── BoardShare.messages.js
│   │   │   │   └── index.js
│   │   │   ├── BoardTour/
│   │   │   │   └── BoardTour.js
│   │   │   ├── EditToolbar/
│   │   │   │   ├── EditToolbar.component.js
│   │   │   │   ├── EditToolbar.css
│   │   │   │   ├── EditToolbar.messages.js
│   │   │   │   ├── EditToolbar.test.js
│   │   │   │   └── index.js
│   │   │   ├── EmptyBoard/
│   │   │   │   ├── EmptyBoard.component.js
│   │   │   │   ├── EmptyBoard.css
│   │   │   │   ├── EmptyBoard.messages.js
│   │   │   │   ├── EmptyBoard.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── EmptyBoard.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── ImageEditor/
│   │   │   │   ├── ImageEditor.component.js
│   │   │   │   ├── ImageEditor.css
│   │   │   │   ├── ImageEditor.messages.js
│   │   │   │   └── index.js
│   │   │   ├── ImprovePhraseOutput/
│   │   │   │   ├── ImprovePhraseOutput.js
│   │   │   │   ├── ImprovePhraseOutput.module.css
│   │   │   │   └── index.js
│   │   │   ├── Navbar/
│   │   │   │   ├── Navbar.css
│   │   │   │   ├── Navbar.js
│   │   │   │   ├── Navbar.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Navbar.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Output/
│   │   │   │   ├── Output.container.js
│   │   │   │   ├── PhraseShare/
│   │   │   │   │   ├── PhraseShare.component.js
│   │   │   │   │   ├── PhraseShare.css
│   │   │   │   │   ├── PhraseShare.messages.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── SymbolOutput/
│   │   │   │   │   ├── BackspaceButton/
│   │   │   │   │   │   ├── BackspaceButton.js
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── ClearButton/
│   │   │   │   │   │   ├── ClearButton.js
│   │   │   │   │   │   ├── ClearButton.test.js
│   │   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   │   └── ClearButton.test.js.snap
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Scroll/
│   │   │   │   │   │   ├── Scroll.css
│   │   │   │   │   │   ├── Scroll.js
│   │   │   │   │   │   ├── Scroll.test.js
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── ShareButton/
│   │   │   │   │   │   ├── ShareButton.js
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── SymbolOutput.css
│   │   │   │   │   ├── SymbolOutput.js
│   │   │   │   │   ├── SymbolOutput.test.js
│   │   │   │   │   └── index.js
│   │   │   │   └── index.js
│   │   │   ├── Symbol/
│   │   │   │   ├── Symbol.css
│   │   │   │   ├── Symbol.js
│   │   │   │   ├── Symbol.test.js
│   │   │   │   └── index.js
│   │   │   ├── SymbolSearch/
│   │   │   │   ├── SymbolNotFound/
│   │   │   │   │   ├── SymbolNotFound.component.js
│   │   │   │   │   ├── SymbolNotFound.css
│   │   │   │   │   ├── SymbolNotFound.messages.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── SymbolSearch.component.js
│   │   │   │   ├── SymbolSearch.component.test.js
│   │   │   │   ├── SymbolSearch.css
│   │   │   │   ├── SymbolSearch.messages.js
│   │   │   │   ├── SymbolSearchTour.component.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── SymbolSearch.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Tile/
│   │   │   │   ├── Tile.component.js
│   │   │   │   ├── Tile.css
│   │   │   │   ├── Tile.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Tile.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── TileEditor/
│   │   │   │   ├── LoadBoardEditor/
│   │   │   │   │   ├── LoadBoardEditor.js
│   │   │   │   │   ├── LoadBoardEditor.messages.js
│   │   │   │   │   ├── LoadBoardEditor.module.css
│   │   │   │   │   └── useAllBoardsFetcher.js
│   │   │   │   ├── LostedFolderForLoadBoardAlert/
│   │   │   │   │   ├── LostedFolderForLoadBoardAlert.tsx
│   │   │   │   │   └── index.ts
│   │   │   │   ├── TileEditor.component.js
│   │   │   │   ├── TileEditor.css
│   │   │   │   ├── TileEditor.messages.js
│   │   │   │   ├── TileEditor.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── TileEditor.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── __tests__/
│   │   │   │   ├── Board.actions.test.js
│   │   │   │   ├── Board.component.test.js
│   │   │   │   └── Board.reducer.test.js
│   │   │   └── index.js
│   │   ├── Communicator/
│   │   │   ├── Communicator.actions.js
│   │   │   ├── Communicator.constants.js
│   │   │   ├── Communicator.reducer.js
│   │   │   ├── CommunicatorDialog/
│   │   │   │   ├── CommunicatorDialog.component.js
│   │   │   │   ├── CommunicatorDialog.constants.js
│   │   │   │   ├── CommunicatorDialog.container.js
│   │   │   │   ├── CommunicatorDialog.css
│   │   │   │   ├── CommunicatorDialog.messages.js
│   │   │   │   ├── CommunicatorDialog.test.js
│   │   │   │   ├── CommunicatorDialogBoardItem.component.js
│   │   │   │   ├── CommunicatorDialogButtons.component.js
│   │   │   │   ├── CommunicatorDialogButtons.test.js
│   │   │   │   ├── CommunicatorDialogTour.component.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   ├── CommunicatorDialog.test.js.snap
│   │   │   │   │   └── CommunicatorDialogButtons.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── CommunicatorToolbar/
│   │   │   │   ├── CommunicatorToolbar.component.js
│   │   │   │   ├── CommunicatorToolbar.container.js
│   │   │   │   ├── CommunicatorToolbar.css
│   │   │   │   ├── CommunicatorToolbar.messages.js
│   │   │   │   ├── CommunicatorToolbar.test.js
│   │   │   │   ├── DefaultBoardSelector/
│   │   │   │   │   ├── DefaultBoardOption.js
│   │   │   │   │   ├── DefaultBoardSelector.js
│   │   │   │   │   ├── DefaultBoardSelector.module.css
│   │   │   │   │   ├── DefaultBoardsGallery.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── CommunicatorToolbar.test.js.snap
│   │   │   │   └── index.js
│   │   │   └── __tests__/
│   │   │       ├── Communicator.actions.test.js
│   │   │       └── Communicator.reducer.test.js
│   │   ├── EditGridButtons/
│   │   │   ├── EditGridButtons.component.js
│   │   │   ├── EditGridButtons.css
│   │   │   └── index.js
│   │   ├── FixedGrid/
│   │   │   ├── DraggableItem/
│   │   │   │   ├── DraggableItem.js
│   │   │   │   └── DraggableItem.module.css
│   │   │   ├── DroppableCell/
│   │   │   │   ├── DroppableCell.js
│   │   │   │   └── DroppableCell.module.css
│   │   │   ├── Grid.js
│   │   │   ├── Grid.module.css
│   │   │   ├── GridBase.js
│   │   │   ├── GridBase.module.css
│   │   │   ├── Row/
│   │   │   │   ├── Row.js
│   │   │   │   └── Row.module.css
│   │   │   ├── index.js
│   │   │   └── utils.ts
│   │   ├── Grid/
│   │   │   ├── Grid.constants.js
│   │   │   ├── Grid.container.js
│   │   │   ├── Grid.css
│   │   │   └── index.js
│   │   ├── LoggedInFeature/
│   │   │   ├── LoginRequiredModal.js
│   │   │   ├── LoginRequiredModal.messages.js
│   │   │   └── LoginRequiredModal.module.css
│   │   ├── NavigationButtons/
│   │   │   ├── NavigationButtons.component.js
│   │   │   ├── NavigationButtons.css
│   │   │   ├── NavigationButtons.test.js
│   │   │   ├── __snapshots__/
│   │   │   │   └── NavigationButtons.test.js.snap
│   │   │   └── index.js
│   │   ├── NotFound/
│   │   │   ├── NotFound.css
│   │   │   ├── NotFound.js
│   │   │   ├── __tests__/
│   │   │   │   ├── NotFound.test.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── NotFound.test.js.snap
│   │   │   └── index.js
│   │   ├── Notifications/
│   │   │   ├── Notifications.actions.js
│   │   │   ├── Notifications.component.js
│   │   │   ├── Notifications.constants.js
│   │   │   ├── Notifications.container.js
│   │   │   ├── Notifications.messages.js
│   │   │   ├── Notifications.reducer.js
│   │   │   ├── __tests__/
│   │   │   │   ├── Notifications.actions.test.js
│   │   │   │   ├── Notifications.component.test.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── Notifications.component.test.js.snap
│   │   │   └── index.js
│   │   ├── PremiumFeature/
│   │   │   ├── PremiumFeature.js
│   │   │   ├── PremiumFeature.messages.js
│   │   │   ├── PremiumRequiredModal.js
│   │   │   ├── PremiumRequiredModal.module.css
│   │   │   └── index.js
│   │   ├── ScrollButtons/
│   │   │   ├── ScrollButtons.js
│   │   │   └── index.js
│   │   ├── Settings/
│   │   │   ├── About/
│   │   │   │   ├── About.component.js
│   │   │   │   ├── About.css
│   │   │   │   ├── About.messages.js
│   │   │   │   ├── About.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── About.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Display/
│   │   │   │   ├── Display.component.js
│   │   │   │   ├── Display.constants.js
│   │   │   │   ├── Display.container.js
│   │   │   │   ├── Display.css
│   │   │   │   ├── Display.messages.js
│   │   │   │   ├── Display.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Display.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Export/
│   │   │   │   ├── Export.component.js
│   │   │   │   ├── Export.constants.js
│   │   │   │   ├── Export.container.js
│   │   │   │   ├── Export.css
│   │   │   │   ├── Export.helpers.js
│   │   │   │   ├── Export.messages.js
│   │   │   │   ├── Export.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Export.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Help/
│   │   │   │   ├── Help.component.js
│   │   │   │   ├── Help.css
│   │   │   │   └── index.js
│   │   │   ├── Import/
│   │   │   │   ├── Import.component.js
│   │   │   │   ├── Import.constants.js
│   │   │   │   ├── Import.container.js
│   │   │   │   ├── Import.css
│   │   │   │   ├── Import.helpers.js
│   │   │   │   ├── Import.messages.js
│   │   │   │   ├── Import.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Import.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Language/
│   │   │   │   ├── DownloadDialog.js
│   │   │   │   ├── Language.component.js
│   │   │   │   ├── Language.container.js
│   │   │   │   ├── Language.messages.js
│   │   │   │   ├── downloadablesTts.json
│   │   │   │   ├── downloadingLangErrorDialog.js
│   │   │   │   └── index.js
│   │   │   ├── Navigation/
│   │   │   │   ├── Navigation.component.js
│   │   │   │   ├── Navigation.constants.js
│   │   │   │   ├── Navigation.container.js
│   │   │   │   ├── Navigation.css
│   │   │   │   ├── Navigation.messages.js
│   │   │   │   ├── Navigation.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Navigation.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── People/
│   │   │   │   ├── DeleteConfirmationDialog.js
│   │   │   │   ├── People.component.js
│   │   │   │   ├── People.component.test.js
│   │   │   │   ├── People.container.js
│   │   │   │   ├── People.messages.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── People.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Scanning/
│   │   │   │   ├── Scanning.component.js
│   │   │   │   ├── Scanning.constants.js
│   │   │   │   ├── Scanning.container.js
│   │   │   │   ├── Scanning.css
│   │   │   │   ├── Scanning.messages.js
│   │   │   │   ├── Scanning.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Scanning.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Settings.component.js
│   │   │   ├── Settings.container.js
│   │   │   ├── Settings.css
│   │   │   ├── Settings.messages.js
│   │   │   ├── Settings.wrapper.js
│   │   │   ├── SettingsSection.component.js
│   │   │   ├── SettingsTour.component.js
│   │   │   ├── Speech/
│   │   │   │   ├── Speech.component.js
│   │   │   │   ├── Speech.component.test.js
│   │   │   │   ├── Speech.constants.js
│   │   │   │   ├── Speech.container.js
│   │   │   │   ├── Speech.css
│   │   │   │   ├── Speech.messages.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Speech.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Subscribe/
│   │   │   │   ├── Subscribe.component.js
│   │   │   │   ├── Subscribe.component.test.js
│   │   │   │   ├── Subscribe.constants.js
│   │   │   │   ├── Subscribe.container.js
│   │   │   │   ├── Subscribe.css
│   │   │   │   ├── Subscribe.helpers.js
│   │   │   │   ├── Subscribe.messages.js
│   │   │   │   ├── SubscriptionInfo.js
│   │   │   │   ├── SubscriptionPlans.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── Subscribe.component.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Symbols/
│   │   │   │   ├── DeleteArasaacDialog.js
│   │   │   │   ├── DownloadArasaacDialog.js
│   │   │   │   ├── NoConnectionDialog.js
│   │   │   │   ├── Symbols.component.js
│   │   │   │   ├── Symbols.container.js
│   │   │   │   ├── Symbols.css
│   │   │   │   ├── Symbols.messages.js
│   │   │   │   └── index.js
│   │   │   └── index.js
│   │   ├── UI/
│   │   │   ├── AnalyticsButton/
│   │   │   │   ├── AnalyticsButton.js
│   │   │   │   ├── AnalyticsButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── AnalyticsButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── BackButton/
│   │   │   │   ├── BackButton.js
│   │   │   │   ├── BackButton.messages.js
│   │   │   │   ├── BackButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── BackButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Barchart/
│   │   │   │   ├── Barchart.component.js
│   │   │   │   ├── Barchart.css
│   │   │   │   └── index.js
│   │   │   ├── ColorSelect/
│   │   │   │   ├── Circle/
│   │   │   │   │   ├── Circle.js
│   │   │   │   │   ├── Circle.test.js
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   └── Circle.test.js.snap
│   │   │   │   │   └── index.js
│   │   │   │   ├── ColorSelect.js
│   │   │   │   ├── ColorSelect.messages.js
│   │   │   │   ├── ColorSelect.test.js
│   │   │   │   ├── ColorSelectDropdown.css
│   │   │   │   ├── HairColor.messages.js
│   │   │   │   ├── HairColorSelect.js
│   │   │   │   ├── HairColorSelect.test.js
│   │   │   │   ├── SkinTone.messages.js
│   │   │   │   ├── SkinToneSelect.js
│   │   │   │   ├── SkinToneSelect.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   ├── ColorSelect.test.js.snap
│   │   │   │   │   ├── HairColorSelect.test.js.snap
│   │   │   │   │   └── SkinToneSelect.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── Doughnut/
│   │   │   │   ├── Doughnut.component.js
│   │   │   │   ├── Doughnut.css
│   │   │   │   └── index.js
│   │   │   ├── Downloader/
│   │   │   │   ├── Downloader.css
│   │   │   │   ├── Downloader.js
│   │   │   │   ├── Downloader.messages.js
│   │   │   │   └── index.js
│   │   │   ├── FilterBar/
│   │   │   │   ├── FilterBar.css
│   │   │   │   ├── FilterBar.js
│   │   │   │   ├── FilterBar.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FilterBar.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FormDialog/
│   │   │   │   ├── FormDialog.css
│   │   │   │   ├── FormDialog.js
│   │   │   │   ├── FormDialog.messages.js
│   │   │   │   ├── FormDialog.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FormDialog.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FormItems/
│   │   │   │   ├── ApiKeyTextField.js
│   │   │   │   ├── FormItems.messages.js
│   │   │   │   ├── PasswordTextField.js
│   │   │   │   ├── TextField.js
│   │   │   │   ├── TextField.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── TextField.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FullScreenButton/
│   │   │   │   ├── FullScreenButton.js
│   │   │   │   ├── FullScreenButton.messages.js
│   │   │   │   ├── FullScreenButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FullScreenButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── FullScreenDialog/
│   │   │   │   ├── FullScreenDialog.css
│   │   │   │   ├── FullScreenDialog.js
│   │   │   │   ├── FullScreenDialog.test.js
│   │   │   │   ├── FullScreenDialogContent.js
│   │   │   │   ├── FullScreenDialogContent.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── FullScreenDialog.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── HelpButton/
│   │   │   │   ├── HelpButton.js
│   │   │   │   └── index.js
│   │   │   ├── IconButton/
│   │   │   │   ├── IconButton.js
│   │   │   │   ├── IconButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── IconButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── InputImage/
│   │   │   │   ├── InputImage.component.js
│   │   │   │   ├── InputImage.css
│   │   │   │   ├── InputImage.messages.js
│   │   │   │   ├── InputImage.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── InputImage.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── LoadingIcon/
│   │   │   │   ├── LoadingIcon.css
│   │   │   │   ├── LoadingIcon.js
│   │   │   │   ├── LoadingIcon.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── LoadingIcon.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── LockToggle/
│   │   │   │   ├── LockToggle.js
│   │   │   │   ├── LockToggle.messages.js
│   │   │   │   ├── __test__/
│   │   │   │   │   ├── LockToggle.test.js
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   ├── LockToggle.test.js.snap
│   │   │   │   │   │   └── childProof.test.js.snap
│   │   │   │   │   └── childProof.test.js
│   │   │   │   ├── childProof.js
│   │   │   │   └── index.js
│   │   │   ├── ModifiedAreaChart/
│   │   │   │   ├── ModifiedAreaChart.component.js
│   │   │   │   └── index.js
│   │   │   ├── PrintBoardButton/
│   │   │   │   ├── PrintBoardButton.component.js
│   │   │   │   ├── PrintBoardButton.container.js
│   │   │   │   ├── PrintBoardButton.messages.js
│   │   │   │   ├── PrintBoardButton.test.js
│   │   │   │   ├── PrintBoardDialog.component.js
│   │   │   │   ├── PrintBoardDialog.css
│   │   │   │   ├── PrintBoardDialog.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   ├── PrintBoardButton.test.js.snap
│   │   │   │   │   └── PrintBoardDialog.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── ResetToursItem/
│   │   │   │   ├── ResetToursItem.component.js
│   │   │   │   ├── ResetToursItem.container.js
│   │   │   │   ├── ResetToursItem.messages.js
│   │   │   │   └── index.js
│   │   │   ├── SelectedCounter/
│   │   │   │   ├── SelectedCounter.component.js
│   │   │   │   ├── SelectedCounter.css
│   │   │   │   ├── SelectedCounter.messages.js
│   │   │   │   ├── SelectedCounter.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── SelectedCounter.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── SettingsButton/
│   │   │   │   ├── SettingsButton.js
│   │   │   │   ├── SettingsButton.messages.js
│   │   │   │   ├── SettingsButton.test.js
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── SettingsButton.test.js.snap
│   │   │   │   └── index.js
│   │   │   ├── StatCards/
│   │   │   │   ├── StatCards.component.js
│   │   │   │   ├── StatCards.css
│   │   │   │   └── index.js
│   │   │   ├── StatCards2/
│   │   │   │   ├── StatCards2.component.js
│   │   │   │   ├── StatCards2.css
│   │   │   │   └── index.js
│   │   │   ├── StyledTable/
│   │   │   │   ├── StyledTable.component.js
│   │   │   │   ├── StyledTable.css
│   │   │   │   └── index.js
│   │   │   ├── TableCard/
│   │   │   │   ├── TableCard.component.js
│   │   │   │   ├── TableCard.css
│   │   │   │   └── index.js
│   │   │   └── UserIcon/
│   │   │       ├── UserIcon.component.js
│   │   │       ├── UserIcon.container.js
│   │   │       ├── UserIcon.messages.js
│   │   │       ├── UserIcon.test.js
│   │   │       ├── __snapshots__/
│   │   │       │   └── UserIcon.test.js.snap
│   │   │       └── index.js
│   │   ├── VoiceRecorder/
│   │   │   ├── VoiceRecorder.component.js
│   │   │   ├── VoiceRecorder.container.js
│   │   │   ├── VoiceRecorder.css
│   │   │   ├── VoiceRecorder.messages.js
│   │   │   ├── __test__/
│   │   │   │   ├── VoiceRecorder.component.test.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── VoiceRecorder.component.test.js.snap
│   │   │   └── index.js
│   │   └── WelcomeScreen/
│   │       ├── CboardLogo/
│   │       │   ├── CboardLogo.component.js
│   │       │   ├── CboardLogo.css
│   │       │   ├── CboardLogo.test.js
│   │       │   ├── __snapshots__/
│   │       │   │   └── CboardLogo.test.js.snap
│   │       │   └── index.js
│   │       ├── WelcomeScreen.container.js
│   │       ├── WelcomeScreen.css
│   │       ├── WelcomeScreen.messages.js
│   │       ├── WelcomeScreen.test.js
│   │       └── index.js
│   ├── config.js
│   ├── constants.js
│   ├── cordova-util.js
│   ├── env.js
│   ├── helpers.js
│   ├── history.js
│   ├── i18n.js
│   ├── idb/
│   │   └── arasaac/
│   │       ├── arasaacdb.ts
│   │       └── jszip.ts
│   ├── index.css
│   ├── index.js
│   ├── polyfills.js
│   ├── providers/
│   │   ├── LanguageProvider/
│   │   │   ├── LanguageProvider.actions.js
│   │   │   ├── LanguageProvider.constants.js
│   │   │   ├── LanguageProvider.container.js
│   │   │   ├── LanguageProvider.reducer.js
│   │   │   ├── __tests__/
│   │   │   │   ├── LanguageProvider.actions.test.js
│   │   │   │   ├── LanguageProvider.reducer.test.js
│   │   │   │   └── LanguageProvider.test.js
│   │   │   └── index.js
│   │   ├── ScannerProvider/
│   │   │   ├── ScannerProvider.actions.js
│   │   │   ├── ScannerProvider.constants.js
│   │   │   ├── ScannerProvider.reducer.js
│   │   │   └── __tests__/
│   │   │       ├── ScannerProvider.actions.test.js
│   │   │       └── ScannerProvider.reducer.test.js
│   │   ├── SpeechProvider/
│   │   │   ├── SpeechProvider.actions.js
│   │   │   ├── SpeechProvider.analytics.js
│   │   │   ├── SpeechProvider.constants.js
│   │   │   ├── SpeechProvider.container.js
│   │   │   ├── SpeechProvider.messages.js
│   │   │   ├── SpeechProvider.reducer.js
│   │   │   ├── __tests__/
│   │   │   │   ├── SpeechProvider.actions.test.js
│   │   │   │   └── SpeechProvider.reducer.test.js
│   │   │   ├── engine/
│   │   │   │   └── elevenlabs.js
│   │   │   ├── index.js
│   │   │   └── tts.js
│   │   ├── SubscriptionProvider/
│   │   │   ├── SubscriptionProvider.actions.js
│   │   │   ├── SubscriptionProvider.constants.js
│   │   │   ├── SubscriptionProvider.container.js
│   │   │   ├── SubscriptionProvider.reducer.js
│   │   │   ├── SubscriptionProvider.selectors.js
│   │   │   └── index.js
│   │   └── ThemeProvider/
│   │       ├── RTLSupport.js
│   │       ├── ThemeProvider.constants.js
│   │       ├── ThemeProvider.container.js
│   │       └── index.js
│   ├── react-app-env.d.ts
│   ├── reducers.js
│   ├── registerServiceWorker.js
│   ├── setupPolyfills.js
│   ├── setupTests.js
│   ├── store.js
│   ├── translations/
│   │   ├── ar-EG.json
│   │   ├── ar-SA.json
│   │   ├── be-BY.json
│   │   ├── bg-BG.json
│   │   ├── bn-BD.json
│   │   ├── ca-ES.json
│   │   ├── cs-CZ.json
│   │   ├── da-DK.json
│   │   ├── de-DE.json
│   │   ├── el-GR.json
│   │   ├── en-GB.json
│   │   ├── en-US.json
│   │   ├── es-ES.json
│   │   ├── fa-IR.json
│   │   ├── fi-FI.json
│   │   ├── fr-FR.json
│   │   ├── he-IL.json
│   │   ├── help/
│   │   │   ├── ar-SA.md
│   │   │   ├── be-BY.md
│   │   │   ├── bg-BG.md
│   │   │   ├── bn-BD.md
│   │   │   ├── ca-ES.md
│   │   │   ├── cs-CZ.md
│   │   │   ├── da-DK.md
│   │   │   ├── de-DE.md
│   │   │   ├── el-GR.md
│   │   │   ├── en-GB.md
│   │   │   ├── en-US.md
│   │   │   ├── es-ES.md
│   │   │   ├── fa-IR.md
│   │   │   ├── fi-FI.md
│   │   │   ├── fr-FR.md
│   │   │   ├── he-IL.md
│   │   │   ├── hi-IN.md
│   │   │   ├── hr-HR.md
│   │   │   ├── hu-HU.md
│   │   │   ├── id-ID.md
│   │   │   ├── it-IT.md
│   │   │   ├── ja-JP.md
│   │   │   ├── km-KH.md
│   │   │   ├── ko-KR.md
│   │   │   ├── me-ME.md
│   │   │   ├── mk-MK.md
│   │   │   ├── ml-IN.md
│   │   │   ├── nb-NO.md
│   │   │   ├── ne-NP.md
│   │   │   ├── nl-NL.md
│   │   │   ├── no-NO.md
│   │   │   ├── pl-PL.md
│   │   │   ├── pt-BR.md
│   │   │   ├── pt-PT.md
│   │   │   ├── pt-TL.md
│   │   │   ├── ro-RO.md
│   │   │   ├── ru-MD.md
│   │   │   ├── ru-RU.md
│   │   │   ├── sh-HR.md
│   │   │   ├── si-LK.md
│   │   │   ├── sk-SK.md
│   │   │   ├── so-SO.md
│   │   │   ├── sq-AL.md
│   │   │   ├── sq-MK.md
│   │   │   ├── sr-CS.md
│   │   │   ├── sr-ME.md
│   │   │   ├── sr-RS.md
│   │   │   ├── sr-SP.md
│   │   │   ├── sv-SE.md
│   │   │   ├── tg-TJ.md
│   │   │   ├── th-TH.md
│   │   │   ├── tk-TM.md
│   │   │   ├── tr-TR.md
│   │   │   ├── tu-TI.md
│   │   │   ├── uk-UA.md
│   │   │   ├── ur-PK.md
│   │   │   ├── vi-VN.md
│   │   │   ├── zh-CN.md
│   │   │   └── zu-ZA.md
│   │   ├── hi-IN.json
│   │   ├── hr-HR.json
│   │   ├── hu-HU.json
│   │   ├── id-ID.json
│   │   ├── ig-NG.json
│   │   ├── it-IT.json
│   │   ├── ja-JP.json
│   │   ├── ka-GE.json
│   │   ├── km-KH.json
│   │   ├── ko-KR.json
│   │   ├── lo-LA.json
│   │   ├── me-ME.json
│   │   ├── mk-MK.json
│   │   ├── ml-IN.json
│   │   ├── moreLanguages/
│   │   │   ├── ar-SA.md
│   │   │   ├── be-BY.md
│   │   │   ├── bg-BG.md
│   │   │   ├── bn-BD.md
│   │   │   ├── ca-ES.md
│   │   │   ├── cs-CZ.md
│   │   │   ├── da-DK.md
│   │   │   ├── de-DE.md
│   │   │   ├── el-GR.md
│   │   │   ├── en-GB.md
│   │   │   ├── en-US.md
│   │   │   ├── es-ES.md
│   │   │   ├── fa-IR.md
│   │   │   ├── fi-FI.md
│   │   │   ├── fr-FR.md
│   │   │   ├── he-IL.md
│   │   │   ├── hi-IN.md
│   │   │   ├── hr-HR.md
│   │   │   ├── hu-HU.md
│   │   │   ├── id-ID.md
│   │   │   ├── it-IT.md
│   │   │   ├── ja-JP.md
│   │   │   ├── km-KH.md
│   │   │   ├── ko-KR.md
│   │   │   ├── lo-LA.md
│   │   │   ├── me-ME.md
│   │   │   ├── mk-MK.md
│   │   │   ├── ml-IN.md
│   │   │   ├── ne-NP.md
│   │   │   ├── nl-NL.md
│   │   │   ├── no-NO.md
│   │   │   ├── pl-PL.md
│   │   │   ├── pt-BR.md
│   │   │   ├── pt-PT.md
│   │   │   ├── ro-RO.md
│   │   │   ├── ru-MD.md
│   │   │   ├── ru-RU.md
│   │   │   ├── sh-HR.md
│   │   │   ├── si-LK.md
│   │   │   ├── sk-SK.md
│   │   │   ├── so-SO.md
│   │   │   ├── sq-AL.md
│   │   │   ├── sq-MK.md
│   │   │   ├── sr-CS.md
│   │   │   ├── sr-SP.md
│   │   │   ├── sv-SE.md
│   │   │   ├── tg-TJ.md
│   │   │   ├── th-TH.md
│   │   │   ├── tk-TM.md
│   │   │   ├── tr-TR.md
│   │   │   ├── tu-TI.md
│   │   │   ├── uk-UA.md
│   │   │   ├── ur-PK.md
│   │   │   ├── vi-VN.md
│   │   │   ├── zh-CN.md
│   │   │   └── zu-ZA.md
│   │   ├── mr-IN.json
│   │   ├── ms-MY.json
│   │   ├── my-MM.json
│   │   ├── nb-NO.json
│   │   ├── ne-NP.json
│   │   ├── nl-NL.json
│   │   ├── no-NO.json
│   │   ├── pl-PL.json
│   │   ├── pt-BR.json
│   │   ├── pt-PT.json
│   │   ├── pt-TL.json
│   │   ├── ro-RO.json
│   │   ├── ru-MD.json
│   │   ├── ru-RU.json
│   │   ├── sh-HR.json
│   │   ├── si-LK.json
│   │   ├── sk-SK.json
│   │   ├── so-SO.json
│   │   ├── sq-AL.json
│   │   ├── sq-MK.json
│   │   ├── sr-CS.json
│   │   ├── sr-ME.json
│   │   ├── sr-RS.json
│   │   ├── sr-SP.json
│   │   ├── src/
│   │   │   └── cboard.json
│   │   ├── sv-SE.json
│   │   ├── ta-IN.json
│   │   ├── tg-TJ.json
│   │   ├── th-TH.json
│   │   ├── tk-TM.json
│   │   ├── tr-TR.json
│   │   ├── tu-TI.json
│   │   ├── uk-UA.json
│   │   ├── ur-PK.json
│   │   ├── vi-VN.json
│   │   ├── zh-CN.json
│   │   └── zu-ZA.json
│   ├── types.ts
│   └── vfs_fonts.js
├── sw-precache-config.js
├── tests/
│   ├── README.md
│   ├── helpers/
│   │   ├── communication-utils.js
│   │   ├── index.js
│   │   ├── navigation-utils.js
│   │   └── overlay-utils.js
│   ├── logged/
│   │   ├── authentication.spec.js
│   │   └── settings/
│   │       └── speech.spec.js
│   ├── page-objects/
│   │   ├── cboard.js
│   │   └── index.js
│   ├── smoke.spec.js
│   ├── unlogged/
│   │   ├── accessibility.spec.js
│   │   ├── basic-functionality.spec.js
│   │   ├── communication-bar.spec.js
│   │   ├── mobile-responsiveness.spec.js
│   │   ├── navigation.spec.js
│   │   ├── public-board-report-restriction.spec.js
│   │   ├── settings/
│   │   │   ├── display.spec.js
│   │   │   ├── export.spec.js
│   │   │   ├── import.spec.js
│   │   │   ├── language.spec.js
│   │   │   ├── scanning.spec.js
│   │   │   └── symbols.spec.js
│   │   └── unlock.spec.js
│   └── utilities/
│       ├── assertions.js
│       ├── index.js
│       ├── test-setup.js
│       └── test-utils.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (1508 symbols across 202 files)

FILE: __mocks__/browser-image-resizer.js
  function readAndCompressImage (line 1) | function readAndCompressImage(file, userConfig) {

FILE: __mocks__/undici.js
  class Dispatcher (line 11) | class Dispatcher {}
  class Client (line 12) | class Client extends Dispatcher {}
  class Pool (line 13) | class Pool extends Dispatcher {}
  class BalancedPool (line 14) | class BalancedPool extends Dispatcher {}
  class RoundRobinPool (line 15) | class RoundRobinPool extends Dispatcher {}
  class Agent (line 16) | class Agent extends Dispatcher {}
  class ProxyAgent (line 17) | class ProxyAgent extends Dispatcher {}
  class EnvHttpProxyAgent (line 18) | class EnvHttpProxyAgent extends Dispatcher {}
  class RetryAgent (line 19) | class RetryAgent extends Dispatcher {}
  class H2CClient (line 20) | class H2CClient extends Dispatcher {}
  class RetryHandler (line 21) | class RetryHandler {}
  class DecoratorHandler (line 22) | class DecoratorHandler {}
  class RedirectHandler (line 23) | class RedirectHandler {}
  class WebSocket (line 24) | class WebSocket {}
  class CloseEvent (line 25) | class CloseEvent {}
  class ErrorEvent (line 26) | class ErrorEvent {}
  class MessageEvent (line 27) | class MessageEvent {}

FILE: env/local.js
  constant APP_URL (line 6) | const APP_URL = 'http://localhost:3000';

FILE: env/prod.js
  constant APP_URL (line 6) | const APP_URL = 'https://cboard.somehost.com';

FILE: public/ogv/ogv-decoder-audio-opus-wasm.js
  function da (line 116) | function da() {
  function ja (line 132) | function ja() {
  function G (line 141) | function G(b) {
  function ka (line 152) | function ka() {
  function oa (line 161) | function oa() {
  function pa (line 171) | function pa() {
  function S (line 197) | function S(b) {
  function qa (line 212) | function qa(b) {
  function b (line 323) | function b(f) {
  function c (line 335) | function c(f) {
  function e (line 338) | function e(f) {
  function V (line 413) | function V() {
  function ua (line 462) | function ua(b) {
  function Z (line 472) | function Z(b) {

FILE: public/ogv/ogv-demuxer-ogg-wasm.js
  function E (line 109) | function E() {
  function aa (line 125) | function aa() {
  function y (line 134) | function y(b) {
  function M (line 145) | function M() {
  function P (line 154) | function P() {
  function ba (line 164) | function ba() {
  function Q (line 190) | function Q(b) {
  function R (line 205) | function R(b) {
  function d (line 255) | function d(e) {
  function b (line 279) | function b(f) {
  function c (line 291) | function c(f) {
  function d (line 294) | function d(f) {
  function V (line 381) | function V() {
  function Z (line 433) | function Z(b) {

FILE: public/ogv/ogv-worker-audio.js
  function _defineProperties (line 23) | function _defineProperties(e, t) {
  function _getPrototypeOf (line 44) | function _getPrototypeOf(t) {
  function _setPrototypeOf (line 98) | function _setPrototypeOf(t, r) {
  function _typeof (line 115) | function _typeof(t) {
  function OGVLoaderBase (line 166) | function OGVLoaderBase() {
  function _createSuper (line 250) | function _createSuper(e) {
  function OGVLoaderWorker (line 279) | function OGVLoaderWorker() {
  function OGVWorkerSupport (line 340) | function OGVWorkerSupport(e, t) {
  function WebAssemblyChecker (line 447) | function WebAssemblyChecker() {
  function __webpack_require__ (line 543) | function __webpack_require__(r) {

FILE: scripts/arasaac-create-files.js
  constant ARASAAC_BASE_PATH_API (line 4) | const ARASAAC_BASE_PATH_API = 'https://api.arasaac.org/api/';

FILE: scripts/arasaac-download-symbols.js
  constant ARASAAC_BASE_PATH_API (line 4) | const ARASAAC_BASE_PATH_API = 'https://api.arasaac.org/api/';
  function downloadFile (line 10) | async function downloadFile(fileUrl, outputLocationPath) {

FILE: scripts/crowdin-fetch-latest.js
  constant CROWDIN_TOKEN (line 8) | const CROWDIN_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN;
  constant CROWDIN_PROJECT_ID (line 9) | const CROWDIN_PROJECT_ID = 262825;

FILE: src/api/__mocks__/api.js
  class API (line 49) | class API {
    method login (line 50) | login(email, password) {
    method getMyBoards (line 60) | getMyBoards({
    method createBoard (line 76) | createBoard(board) {
    method updateBoard (line 86) | updateBoard(board) {
    method deleteBoard (line 96) | deleteBoard(boardId) {
    method uploadFile (line 106) | uploadFile(file, filename) {
    method arasaacPictogramsSearch (line 115) | async arasaacPictogramsSearch(locale, searchText) {
    method oAuthLogin (line 119) | oAuthLogin(type, query) {
    method getBoards (line 129) | async getBoards({
    method getCommunicators (line 139) | async getCommunicators({
    method getBoard (line 149) | async getBoard(id) {
    method updateSettings (line 153) | async updateSettings(newSettings = {}) {
    method createCommunicator (line 157) | async createCommunicator(communicator) {
    method updateCommunicator (line 161) | async updateCommunicator(communicator) {
  constant API_INSTANCE (line 166) | const API_INSTANCE = new API({});

FILE: src/api/api.js
  constant BASE_URL (line 16) | const BASE_URL = API_URL;
  constant LOCAL_COMMUNICATOR_ID (line 17) | const LOCAL_COMMUNICATOR_ID = 'cboard_default';
  class API (line 48) | class API {
    method constructor (line 49) | constructor(config = {}) {
    method getLanguage (line 80) | async getLanguage(lang) {
    method getAzureVoices (line 92) | async getAzureVoices() {
    method arasaacPictogramsSearch (line 110) | async arasaacPictogramsSearch(locale, searchText) {
    method arasaacPictogramsGetImageUrl (line 122) | async arasaacPictogramsGetImageUrl(pictogGetTextPath) {
    method globalsymbolsPictogramsSearch (line 132) | async globalsymbolsPictogramsSearch(locale, searchText) {
    method login (line 152) | async login(email, password) {
    method forgot (line 161) | async forgot(email) {
    method storePassword (line 169) | async storePassword(userid, password, url) {
    method oAuthLogin (line 179) | async oAuthLogin(type, query) {
    method getUserData (line 197) | async getUserData(userId) {
    method getBoards (line 209) | async getBoards({
    method getPublicBoards (line 222) | async getPublicBoards({
    method getMyBoards (line 235) | async getMyBoards({
    method getCommunicators (line 259) | async getCommunicators({
    method getBoard (line 283) | async getBoard(id) {
    method getCbuilderBoard (line 288) | async getCbuilderBoard(id) {
    method getSettings (line 304) | async getSettings() {
    method updateSettings (line 318) | async updateSettings(newSettings = {}) {
    method updateUser (line 335) | async updateUser(user) {
    method createBoard (line 352) | async createBoard(board) {
    method updateBoard (line 367) | async updateBoard(board) {
    method deleteBoard (line 384) | async deleteBoard(boardId) {
    method boardReport (line 401) | async boardReport(reportedBoardData) {
    method uploadFromDataURL (line 419) | async uploadFromDataURL(dataURL, filename, checkExtension = false) {
    method uploadFile (line 430) | async uploadFile(file, filename) {
    method createCommunicator (line 450) | async createCommunicator(communicator) {
    method updateCommunicator (line 476) | async updateCommunicator(communicator) {
    method analyticsReport (line 515) | async analyticsReport(report) {
    method getUserLocation (line 534) | async getUserLocation() {
    method getSubscriber (line 539) | async getSubscriber(userId = getUserData().id, requestOrigin = 'unknow...
    method createSubscriber (line 560) | async createSubscriber(subscriber = {}) {
    method cancelPlan (line 575) | async cancelPlan(subscriptionId = '') {
    method postTransaction (line 593) | async postTransaction(transaction = {}) {
    method updateSubscriber (line 615) | async updateSubscriber(subscriber = {}) {
    method listSubscriptions (line 637) | async listSubscriptions() {
    method deleteAccount (line 642) | async deleteAccount() {
    method improvePhrase (line 660) | async improvePhrase({ phrase, language }) {
  constant API_INSTANCE (line 687) | const API_INSTANCE = new API({});

FILE: src/api/cboard-symbols.js
  constant CBOARD_SYMBOLS_API_BASE (line 14) | const CBOARD_SYMBOLS_API_BASE =
  constant API_TIMEOUT (line 20) | const API_TIMEOUT = 10000;
  function searchCboardSymbols (line 50) | async function searchCboardSymbols(locale, searchText) {
  function mapArasaacToCboardSkinTone (line 119) | function mapArasaacToCboardSkinTone(arasaacSkin) {

FILE: src/common/test_utils.js
  function shallowMatchSnapshot (line 8) | function shallowMatchSnapshot(component) {
  function matchSnapshot (line 13) | function matchSnapshot(component) {
  function matchSnapshotWithIntlProvider (line 18) | function matchSnapshotWithIntlProvider(

FILE: src/components/Account/Activate/Activate.actions.js
  function activate (line 6) | function activate(url) {

FILE: src/components/Account/Activate/Activate.container.js
  function ActivateContainer (line 9) | function ActivateContainer() {

FILE: src/components/Account/ChangePassword/ChangePassword.actions.js
  function storePasswordApiStarted (line 8) | function storePasswordApiStarted() {
  function storePasswordApiFailure (line 14) | function storePasswordApiFailure(message) {
  function storePasswordApiSuccess (line 21) | function storePasswordApiSuccess(board) {
  function storePassword (line 28) | function storePassword(userid, password, url) {

FILE: src/components/Account/ChangePassword/ChangePassword.component.js
  class ChangePassword (line 21) | class ChangePassword extends Component {
    method render (line 64) | render() {

FILE: src/components/Account/ChangePassword/ChangePassword.constants.js
  constant STORE_PASSWORD_API_STARTED (line 1) | const STORE_PASSWORD_API_STARTED =
  constant STORE_PASSWORD_API_SUCCESS (line 3) | const STORE_PASSWORD_API_SUCCESS =
  constant STORE_PASSWORD_API_FAILURE (line 5) | const STORE_PASSWORD_API_FAILURE =

FILE: src/components/Account/Login/Login.actions.js
  function loginSuccess (line 22) | function loginSuccess(payload) {
  function firstLoginActions (line 42) | async function firstLoginActions(dispatch, payload) {
  function logout (line 51) | function logout() {
  function logoutSuccess (line 71) | function logoutSuccess() {
  function login (line 77) | function login({ email, password, activatedData }, type = 'local') {

FILE: src/components/Account/Login/Login.component.js
  function Login (line 25) | function Login({

FILE: src/components/Account/Login/Login.constants.js
  constant LOGIN_SUCCESS (line 1) | const LOGIN_SUCCESS = 'cboard/Login/LOGIN_SUCCESS';
  constant LOGOUT (line 2) | const LOGOUT = 'cboard/Login/LOGOUT';

FILE: src/components/Account/OAuthLogin/OAuthLogin.container.js
  class OAuthLoginContainer (line 10) | class OAuthLoginContainer extends React.Component {
    method constructor (line 17) | constructor(props) {
    method componentDidMount (line 33) | componentDidMount() {
    method componentDidUpdate (line 49) | componentDidUpdate() {
    method checkUser (line 53) | checkUser() {
    method handleError (line 62) | handleError() {
    method render (line 70) | render() {

FILE: src/components/Account/ResetPassword/ResetPassword.actions.js
  function forgotApiStarted (line 8) | function forgotApiStarted() {
  function forgotApiFailure (line 14) | function forgotApiFailure(message) {
  function forgotApiSuccess (line 21) | function forgotApiSuccess(board) {
  function forgot (line 28) | function forgot({ email }) {

FILE: src/components/Account/ResetPassword/ResetPassword.component.js
  function ResetPassword (line 23) | function ResetPassword({ intl, isDialogOpen, onClose, forgot }) {

FILE: src/components/Account/ResetPassword/ResetPassword.constants.js
  constant FORGOT_API_STARTED (line 1) | const FORGOT_API_STARTED = 'cboard/ResetPassword/FORGOT_API_STARTED';
  constant FORGOT_API_SUCCESS (line 2) | const FORGOT_API_SUCCESS = 'cboard/ResetPassword/FORGOT_API_SUCCESS';
  constant FORGOT_API_FAILURE (line 3) | const FORGOT_API_FAILURE = 'cboard/ResetPassword/FORGOT_API_FAILURE';

FILE: src/components/Account/SignUp/SignUp.actions.js
  function signUp (line 6) | function signUp(formValues) {

FILE: src/components/Account/SignUp/SignUp.component.js
  function SignUp (line 22) | function SignUp(props) {

FILE: src/components/Analytics/Analytics.component.js
  class Analytics (line 56) | class Analytics extends PureComponent {
    method constructor (line 58) | constructor(props) {
    method handleDialogClose (line 114) | handleDialogClose() {
    method render (line 120) | render() {

FILE: src/components/Analytics/Analytics.container.js
  class AnalyticsContainer (line 16) | class AnalyticsContainer extends Component {
    method constructor (line 26) | constructor(props) {
    method componentDidMount (line 72) | async componentDidMount() {
    method getSymbolSources (line 95) | getSymbolSources() {
    method getUsage (line 168) | async getUsage(days) {
    method getTotals (line 212) | async getTotals(days) {
    method getReportTotal (line 274) | getReportTotal(report, index = 0, type = 'totals') {
    method getReportRows (line 291) | getReportRows(report, index = 0, type = 'view', max = 10) {
    method getCategoryTotals (line 310) | async getCategoryTotals(days) {
    method getTopUsed (line 352) | getTopUsed(totals) {
    method getTileFromLabel (line 359) | getTileFromLabel(label) {
    method render (line 407) | render() {

FILE: src/components/App/App.actions.js
  function updateConnectivity (line 18) | function updateConnectivity({ isConnected = false }) {
  function updateDisplaySettings (line 25) | function updateDisplaySettings(payload = {}) {
  function updateNavigationSettings (line 32) | function updateNavigationSettings(payload = {}) {
  function updateSymbolsSettings (line 39) | function updateSymbolsSettings(payload = {}) {
  function finishFirstVisit (line 46) | function finishFirstVisit() {
  function disableTour (line 52) | function disableTour(payload = {}) {
  function enableAllTours (line 59) | function enableAllTours() {
  function updateUserData (line 65) | function updateUserData(userData) {
  function setUnloggedUserLocation (line 72) | function setUnloggedUserLocation(location) {
  function updateLoggedUserLocation (line 79) | function updateLoggedUserLocation() {
  function updateUserDataFromAPI (line 109) | function updateUserDataFromAPI() {
  function updateUnloggedUserLocation (line 135) | function updateUnloggedUserLocation() {

FILE: src/components/App/App.component.js
  class App (line 21) | class App extends Component {
    method render (line 49) | render() {

FILE: src/components/App/App.constants.js
  constant FINISH_FIRST_VISIT (line 2) | const FINISH_FIRST_VISIT = 'cboard/App/FINISH_FIRST_VISIT';
  constant DISABLE_TOUR (line 3) | const DISABLE_TOUR = 'cboard/App/DISABLE_TOUR';
  constant ENABLE_ALL_TOURS (line 4) | const ENABLE_ALL_TOURS = 'cboard/App/ENABLE_ALL_TOURS';
  constant UPDATE_CONNECTIVITY (line 5) | const UPDATE_CONNECTIVITY = 'cboard/App/UPDATE_CONNECTIVITY';
  constant UPDATE_DISPLAY_SETTINGS (line 6) | const UPDATE_DISPLAY_SETTINGS = 'cboard/App/UPDATE_DISPLAY_SETTINGS';
  constant UPDATE_SYMBOLS_SETTINGS (line 7) | const UPDATE_SYMBOLS_SETTINGS = 'cboard/App/UPDATE_SYMBOLS_SETTINGS';
  constant UPDATE_NAVIGATION_SETTINGS (line 8) | const UPDATE_NAVIGATION_SETTINGS =
  constant UPDATE_USER_DATA (line 10) | const UPDATE_USER_DATA = 'cboard/App/UPDATE_USER_DATA';
  constant SET_UNLOGGED_USER_LOCATION (line 11) | const SET_UNLOGGED_USER_LOCATION =
  constant DEFAULT_LANG (line 14) | const DEFAULT_LANG = 'en-US';
  constant APP_LANGS (line 15) | const APP_LANGS = [
  constant USER_DATA_PROPERTIES (line 70) | const USER_DATA_PROPERTIES = [

FILE: src/components/App/App.container.js
  class AppContainer (line 20) | class AppContainer extends Component {
    method componentDidMount (line 49) | componentDidMount() {
    method render (line 139) | render() {

FILE: src/components/App/App.reducer.js
  function appReducer (line 74) | function appReducer(state = initialState, action) {

FILE: src/components/AuthScreen/AuthScreen.component.js
  class AuthScreen (line 7) | class AuthScreen extends Component {
    method render (line 12) | render() {

FILE: src/components/Board/Board.actions.js
  constant BOARDS_PAGE_LIMIT (line 63) | const BOARDS_PAGE_LIMIT = 500;
  function importBoards (line 65) | function importBoards(boards) {
  function addBoards (line 72) | function addBoards(boards) {
  function getActiveCommunicator (line 79) | function getActiveCommunicator(getState) {
  function replaceHistoryWithActiveBoardId (line 92) | function replaceHistoryWithActiveBoardId(getState) {
  function changeDefaultBoard (line 98) | function changeDefaultBoard(selectedBoardNameOnJson) {
  function replaceDefaultHomeBoardIfIsNescesary (line 218) | function replaceDefaultHomeBoardIfIsNescesary(prev, current) {
  function replaceBoard (line 233) | function replaceBoard(prev, current) {
  function createBoard (line 240) | function createBoard(boardData) {
  function updateBoard (line 247) | function updateBoard(boardData) {
  function deleteBoard (line 253) | function deleteBoard(boardId) {
  function switchBoard (line 260) | function switchBoard(boardId) {
  function changeBoard (line 267) | function changeBoard(boardId) {
  function previousBoard (line 274) | function previousBoard() {
  function toRootBoard (line 283) | function toRootBoard() {
  function historyRemoveBoard (line 292) | function historyRemoveBoard(removedBoardId) {
  function unmarkBoard (line 299) | function unmarkBoard(boardId) {
  function createTile (line 306) | function createTile(tile, boardId) {
  function deleteTiles (line 314) | function deleteTiles(tiles, boardId) {
  function editTiles (line 322) | function editTiles(tiles, boardId) {
  function focusTile (line 330) | function focusTile(tileId, boardId) {
  function clickSymbol (line 338) | function clickSymbol(symbolLabel) {
  function clickOutput (line 345) | function clickOutput(outputPhrase) {
  function changeOutput (line 352) | function changeOutput(output) {
  function improvePhrase (line 367) | function improvePhrase(output) {
  function changeLiveMode (line 392) | function changeLiveMode() {
  function getApiMyBoardsSuccess (line 398) | function getApiMyBoardsSuccess(boards) {
  function getApiMyBoardsStarted (line 405) | function getApiMyBoardsStarted() {
  function getApiMyBoardsFailure (line 411) | function getApiMyBoardsFailure(message) {
  function createApiBoardSuccess (line 418) | function createApiBoardSuccess(board, boardId) {
  function createApiBoardStarted (line 426) | function createApiBoardStarted() {
  function createApiBoardFailure (line 432) | function createApiBoardFailure(message) {
  function updateApiBoardSuccess (line 439) | function updateApiBoardSuccess(board) {
  function updateApiBoardStarted (line 462) | function updateApiBoardStarted() {
  function updateApiBoardFailure (line 468) | function updateApiBoardFailure(message) {
  function deleteApiBoardSuccess (line 474) | function deleteApiBoardSuccess(board) {
  function deleteApiBoardStarted (line 481) | function deleteApiBoardStarted() {
  function deleteApiBoardFailure (line 487) | function deleteApiBoardFailure(message) {
  function downloadImagesSuccess (line 494) | function downloadImagesSuccess() {
  function downloadImagesStarted (line 500) | function downloadImagesStarted() {
  function downloadImagesFailure (line 506) | function downloadImagesFailure(message) {
  function downloadImageSuccess (line 512) | function downloadImageSuccess(element) {
  function downloadImageFailure (line 519) | function downloadImageFailure(message) {
  function getApiMyBoards (line 526) | function getApiMyBoards() {
  function createApiBoard (line 543) | function createApiBoard(boardData, boardId) {
  function updateApiBoard (line 562) | function updateApiBoard(boardData) {
  function upsertApiBoard (line 577) | function upsertApiBoard(boardData) {
  function deleteApiBoard (line 593) | function deleteApiBoard(boardId) {
  function getApiObjects (line 612) | function getApiObjects() {
  function downloadImages (line 629) | function downloadImages() {
  function storeImage (line 678) | async function storeImage(image, id, type) {
  function getFileNameFromUrl (line 700) | function getFileNameFromUrl(url) {
  function updateApiObjectsNoChild (line 712) | function updateApiObjectsNoChild(
  function updateApiMarkedBoards (line 760) | function updateApiMarkedBoards() {
  function unmarkShouldCreateBoard (line 819) | function unmarkShouldCreateBoard(boardId) {
  function updateApiObjects (line 826) | function updateApiObjects(

FILE: src/components/Board/Board.component.js
  class Board (line 40) | class Board extends Component {
    method constructor (line 122) | constructor(props) {
    method componentDidMount (line 134) | componentDidMount() {
    method renderTiles (line 196) | renderTiles(tiles) {
    method render (line 291) | render() {

FILE: src/components/Board/Board.constants.js
  constant IMPORT_BOARDS (line 1) | const IMPORT_BOARDS = 'cboard/Board/IMPORT_BOARDS';
  constant ADD_BOARDS (line 2) | const ADD_BOARDS = 'cboard/Board/ADD_BOARDS';
  constant CREATE_BOARD (line 3) | const CREATE_BOARD = 'cboard/Board/CREATE_BOARD';
  constant UPDATE_BOARD (line 4) | const UPDATE_BOARD = 'cboard/Board/UPDATE_BOARD';
  constant DELETE_BOARD (line 5) | const DELETE_BOARD = 'cboard/Board/DELETE_BOARD';
  constant CHANGE_BOARD (line 6) | const CHANGE_BOARD = 'cboard/Board/CHANGE_BOARD';
  constant REPLACE_BOARD (line 7) | const REPLACE_BOARD = 'cboard/Board/REPLACE_BOARD';
  constant SWITCH_BOARD (line 8) | const SWITCH_BOARD = 'cboard/Board/SWITCH_BOARD';
  constant PREVIOUS_BOARD (line 9) | const PREVIOUS_BOARD = 'cboard/Board/PREVIOUS_BOARD';
  constant TO_ROOT_BOARD (line 10) | const TO_ROOT_BOARD = 'cboard/Board/TO_ROOT_BOARD';
  constant CREATE_TILE (line 11) | const CREATE_TILE = 'cboard/Board/CREATE_TILE';
  constant DELETE_TILES (line 12) | const DELETE_TILES = 'cboard/Board/DELETE_TILES';
  constant EDIT_TILES (line 13) | const EDIT_TILES = 'cboard/Board/EDIT_TILES';
  constant FOCUS_TILE (line 14) | const FOCUS_TILE = 'cboard/Board/FOCUS_TILE';
  constant CLICK_SYMBOL (line 15) | const CLICK_SYMBOL = 'cboard/Board/CLICK_SYMBOL';
  constant CLICK_OUTPUT (line 16) | const CLICK_OUTPUT = 'cboard/Board/CLICK_OUTPUT';
  constant CHANGE_OUTPUT (line 17) | const CHANGE_OUTPUT = 'cboard/Board/CHANGE_OUTPUT';
  constant CHANGE_IMPROVED_PHRASE (line 18) | const CHANGE_IMPROVED_PHRASE = 'cboard/Board/CHANGE_IMPROVED_PHRASE';
  constant CHANGE_LIVE_MODE (line 19) | const CHANGE_LIVE_MODE = 'cboard/Board/CHANGE_LIVE_MODE';
  constant HISTORY_REMOVE_BOARD (line 20) | const HISTORY_REMOVE_BOARD = 'cboard/Board/HISTORY_REMOVE_BOARD';
  constant UNMARK_BOARD (line 21) | const UNMARK_BOARD = 'cboard/Board/UNMARK_BOARD';
  constant UNMARK_SHOULD_CREATE_API_BOARD (line 22) | const UNMARK_SHOULD_CREATE_API_BOARD =
  constant CREATE_API_BOARD_SUCCESS (line 24) | const CREATE_API_BOARD_SUCCESS = 'cboard/Board/CREATE_API_BOARD_SUCCESS';
  constant CREATE_API_BOARD_FAILURE (line 25) | const CREATE_API_BOARD_FAILURE = 'cboard/Board/CREATE_API_BOARD_FAILURE';
  constant CREATE_API_BOARD_STARTED (line 26) | const CREATE_API_BOARD_STARTED = 'cboard/Board/CREATE_API_BOARD_STARTED';
  constant DELETE_API_BOARD_SUCCESS (line 27) | const DELETE_API_BOARD_SUCCESS = 'cboard/Board/DELETE_API_BOARD_SUCCESS';
  constant DELETE_API_BOARD_FAILURE (line 28) | const DELETE_API_BOARD_FAILURE = 'cboard/Board/DELETE_API_BOARD_FAILURE';
  constant DELETE_API_BOARD_STARTED (line 29) | const DELETE_API_BOARD_STARTED = 'cboard/Board/DELETE_API_BOARD_STARTED';
  constant UPDATE_API_BOARD_SUCCESS (line 30) | const UPDATE_API_BOARD_SUCCESS = 'cboard/Board/UPDATE_API_BOARD_SUCCESS';
  constant UPDATE_API_BOARD_FAILURE (line 31) | const UPDATE_API_BOARD_FAILURE = 'cboard/Board/UPDATE_API_BOARD_FAILURE';
  constant UPDATE_API_BOARD_STARTED (line 32) | const UPDATE_API_BOARD_STARTED = 'cboard/Board/UPDATE_API_BOARD_STARTED';
  constant GET_API_MY_BOARDS_SUCCESS (line 33) | const GET_API_MY_BOARDS_SUCCESS =
  constant GET_API_MY_BOARDS_FAILURE (line 35) | const GET_API_MY_BOARDS_FAILURE =
  constant GET_API_MY_BOARDS_STARTED (line 37) | const GET_API_MY_BOARDS_STARTED =
  constant DOWNLOAD_IMAGES_SUCCESS (line 39) | const DOWNLOAD_IMAGES_SUCCESS = 'cboard/Board/DOWNLOAD_IMAGES_SUCCESS';
  constant DOWNLOAD_IMAGES_FAILURE (line 40) | const DOWNLOAD_IMAGES_FAILURE = 'cboard/Board/DOWNLOAD_IMAGES_FAILURE';
  constant DOWNLOAD_IMAGES_STARTED (line 41) | const DOWNLOAD_IMAGES_STARTED = 'cboard/Board/DOWNLOAD_IMAGES_STARTED';
  constant DOWNLOAD_IMAGE_SUCCESS (line 42) | const DOWNLOAD_IMAGE_SUCCESS = 'cboard/Board/DOWNLOAD_IMAGE_SUCCESS';
  constant DOWNLOAD_IMAGE_FAILURE (line 43) | const DOWNLOAD_IMAGE_FAILURE = 'cboard/Board/DOWNLOAD_IMAGE_FAILURE';
  constant DEFAULT_ROWS_NUMBER (line 44) | const DEFAULT_ROWS_NUMBER = 5;
  constant DEFAULT_COLUMNS_NUMBER (line 45) | const DEFAULT_COLUMNS_NUMBER = 5;
  constant SHORT_ID_MAX_LENGTH (line 46) | const SHORT_ID_MAX_LENGTH = 14;

FILE: src/components/Board/Board.container.js
  class BoardContainer (line 86) | class BoardContainer extends Component {
    method constructor (line 209) | constructor(props) {
    method componentDidMount (line 214) | async componentDidMount() {
    method UNSAFE_componentWillReceiveProps (line 295) | UNSAFE_componentWillReceiveProps(nextProps) {
    method componentDidUpdate (line 329) | componentDidUpdate(prevProps) {
    method toggleSelectMode (line 336) | toggleSelectMode() {
    method selectAllTiles (line 344) | selectAllTiles() {
    method selectTile (line 353) | selectTile(tileId) {
    method deselectTile (line 359) | deselectTile(tileId) {
    method toggleTileSelect (line 366) | toggleTileSelect(tileId) {
    method tryRemoteBoard (line 374) | async tryRemoteBoard(boardId) {
    method captureBoardScreenshot (line 417) | async captureBoardScreenshot() {
    method playAudio (line 427) | async playAudio(src) {
    method uploadTileSound (line 963) | async uploadTileSound(tile) {
    method convertDataURIToBinary (line 979) | convertDataURIToBinary(dataURI) {
    method historyReplaceBoardId (line 1152) | historyReplaceBoardId(boardId) {
    method createBoardsRecursively (line 1204) | async createBoardsRecursively(board, records) {
    method updateBoardReferences (line 1298) | updateBoardReferences(board, newBoard, records) {
    method pasteBoardsRecursively (line 1398) | async pasteBoardsRecursively(folderTile, parentBoardId, firstPastedFol...
    method render (line 1519) | render() {

FILE: src/components/Board/Board.reducer.js
  function reconcileBoards (line 62) | function reconcileBoards(localBoard, remoteBoard) {
  function resolveLastEdited (line 74) | function resolveLastEdited(oldBoard, newBoard) {
  function tileReducer (line 84) | function tileReducer(board, action) {
  function boardReducer (line 110) | function boardReducer(state = initialState, action) {

FILE: src/components/Board/BoardShare/BoardShare.component.js
  function shareBoardOnFacebook (line 33) | function shareBoardOnFacebook(url, intl) {

FILE: src/components/Board/BoardTour/BoardTour.js
  function BoardTour (line 45) | function BoardTour({

FILE: src/components/Board/EditToolbar/EditToolbar.component.js
  function EditToolbar (line 82) | function EditToolbar({

FILE: src/components/Board/EmptyBoard/EmptyBoard.component.js
  function EmptyBoard (line 24) | function EmptyBoard({ classes }) {

FILE: src/components/Board/ImageEditor/ImageEditor.component.js
  class ImageEditor (line 23) | class ImageEditor extends PureComponent {
    method constructor (line 35) | constructor(props) {
    method render (line 98) | render() {

FILE: src/components/Board/ImprovePhraseOutput/ImprovePhraseOutput.js
  function ImprovePhraseOutput (line 14) | function ImprovePhraseOutput({ improvedPhrase, speak }) {

FILE: src/components/Board/Navbar/Navbar.js
  class Navbar (line 21) | class Navbar extends React.Component {
    method constructor (line 22) | constructor(props) {
    method render (line 94) | render() {

FILE: src/components/Board/Output/Output.container.js
  function translateOutput (line 19) | function translateOutput(output, intl) {
  class OutputContainer (line 31) | class OutputContainer extends Component {
    method getDerivedStateFromProps (line 55) | static getDerivedStateFromProps(props, state) {
    method componentDidMount (line 67) | componentDidMount() {
    method componentWillUnmount (line 70) | componentWillUnmount() {
    method outputReducer (line 77) | outputReducer(accumulator, currentValue) {
    method clearOutput (line 89) | clearOutput() {
    method popOutput (line 95) | popOutput() {
    method spliceOutput (line 104) | spliceOutput(index) {
    method speakOutput (line 112) | async speakOutput(text) {
    method groupOutputByType (line 126) | groupOutputByType() {
    method playAudio (line 146) | playAudio(src) {
    method asyncForEach (line 159) | async asyncForEach(array, callback) {
    method play (line 165) | async play(liveText = '') {
    method addLiveOutputTile (line 285) | addLiveOutputTile() {
    method addLiveOutputTileClearOutput (line 291) | addLiveOutputTileClearOutput() {
    method render (line 320) | render() {

FILE: src/components/Board/Output/SymbolOutput/BackspaceButton/BackspaceButton.js
  class BackspaceButton (line 8) | class BackspaceButton extends Component {
    method render (line 18) | render() {

FILE: src/components/Board/Output/SymbolOutput/ClearButton/ClearButton.js
  class ClearButton (line 7) | class ClearButton extends Component {
    method render (line 15) | render() {

FILE: src/components/Board/Output/SymbolOutput/Scroll/Scroll.js
  class Scroll (line 8) | class Scroll extends PureComponent {
    method render (line 16) | render() {

FILE: src/components/Board/Output/SymbolOutput/ShareButton/ShareButton.js
  class ShareButton (line 20) | class ShareButton extends Component {
    method render (line 34) | render() {

FILE: src/components/Board/Output/SymbolOutput/SymbolOutput.js
  class SymbolOutput (line 18) | class SymbolOutput extends PureComponent {
    method constructor (line 19) | constructor(props) {
    method componentDidMount (line 71) | componentDidMount() {
    method componentDidUpdate (line 75) | componentDidUpdate(prevProps) {
    method render (line 80) | render() {

FILE: src/components/Board/Symbol/Symbol.js
  function formatSrc (line 28) | function formatSrc(src) {
  function Symbol (line 32) | function Symbol(props) {

FILE: src/components/Board/SymbolSearch/SymbolNotFound/SymbolNotFound.component.js
  function SymbolNotFound (line 7) | function SymbolNotFound() {

FILE: src/components/Board/SymbolSearch/SymbolSearch.component.js
  class SymbolSearch (line 63) | class SymbolSearch extends PureComponent {
    method constructor (line 79) | constructor(props) {
    method componentDidMount (line 96) | async componentDidMount() {
    method getDerivedStateFromProps (line 104) | static getDerivedStateFromProps(nextProps, prevState) {
    method isSkinToneDisabled (line 114) | get isSkinToneDisabled() {
    method isHairColorDisabled (line 124) | get isHairColorDisabled() {
    method translateSymbols (line 131) | translateSymbols(symbols = []) {
    method getSuggestionValue (line 142) | getSuggestionValue(suggestion) {
    method getMulberrySuggestions (line 146) | getMulberrySuggestions(value) {
    method getSuggestions (line 395) | getSuggestions(value) {
    method renderSuggestion (line 466) | renderSuggestion(suggestion, { query, isHighlighted }) {
    method renderSuggestionsContainer (line 483) | renderSuggestionsContainer(options) {
    method handleClearSuggest (line 529) | handleClearSuggest() {
    method render (line 533) | render() {

FILE: src/components/Board/SymbolSearch/SymbolSearchTour.component.js
  function SymbolSearchTour (line 35) | function SymbolSearchTour({ intl, disableTour, isSymbolSearchTourEnabled...

FILE: src/components/Board/TileEditor/LostedFolderForLoadBoardAlert/LostedFolderForLoadBoardAlert.tsx
  type LostedFolderForLoadBoardAlertProps (line 5) | interface LostedFolderForLoadBoardAlertProps {

FILE: src/components/Board/TileEditor/TileEditor.component.js
  constant NONE_VALUE (line 50) | const NONE_VALUE = 'none';
  class TileEditor (line 51) | class TileEditor extends Component {
    method constructor (line 90) | constructor(props) {
    method UNSAFE_componentWillReceiveProps (line 132) | UNSAFE_componentWillReceiveProps(props) {
    method componentDidUpdate (line 136) | componentDidUpdate(prevProps) {
    method editingTile (line 143) | editingTile() {
    method currentTileProp (line 147) | currentTileProp(prop) {
    method updateEditingTile (line 152) | updateEditingTile(id, property, value) {
    method updateNewTile (line 161) | updateNewTile(property, value) {
    method updateTileProperty (line 168) | updateTileProperty(property, value) {
    method createimageUploadedDataArray (line 280) | createimageUploadedDataArray() {
    method getOriginalTileBackground (line 406) | getOriginalTileBackground() {
    method render (line 488) | render() {

FILE: src/components/Communicator/Communicator.actions.js
  function importCommunicator (line 30) | function importCommunicator(communicator) {
  function createCommunicator (line 37) | function createCommunicator(communicator) {
  function upsertCommunicator (line 44) | function upsertCommunicator(communicator) {
  function upsertApiCommunicator (line 58) | function upsertApiCommunicator(communicator) {
  function editCommunicator (line 84) | function editCommunicator(communicator) {
  function deleteCommunicator (line 91) | function deleteCommunicator(id) {
  function changeCommunicator (line 98) | function changeCommunicator(id) {
  function addBoardCommunicator (line 105) | function addBoardCommunicator(boardId) {
  function deleteBoardCommunicator (line 112) | function deleteBoardCommunicator(boardId) {
  function replaceBoardCommunicator (line 119) | function replaceBoardCommunicator(prevBoardId, nextBoardId) {
  function getApiMyCommunicatorsSuccess (line 127) | function getApiMyCommunicatorsSuccess(communicators) {
  function getApiMyCommunicatorsStarted (line 134) | function getApiMyCommunicatorsStarted() {
  function getApiMyCommunicatorsFailure (line 140) | function getApiMyCommunicatorsFailure(message) {
  function createApiCommunicatorSuccess (line 146) | function createApiCommunicatorSuccess(communicator, communicatorId) {
  function createApiCommunicatorStarted (line 154) | function createApiCommunicatorStarted() {
  function createApiCommunicatorFailure (line 160) | function createApiCommunicatorFailure(message) {
  function updateApiCommunicatorSuccess (line 166) | function updateApiCommunicatorSuccess(communicator) {
  function updateApiCommunicatorStarted (line 173) | function updateApiCommunicatorStarted() {
  function updateApiCommunicatorFailure (line 179) | function updateApiCommunicatorFailure(message) {
  function verifyAndUpsertCommunicator (line 186) | function verifyAndUpsertCommunicator(
  function getApiMyCommunicators (line 239) | function getApiMyCommunicators() {
  function createApiCommunicator (line 261) | function createApiCommunicator(communicatorData, communicatorId) {
  function updateApiCommunicator (line 280) | function updateApiCommunicator(communicatorData) {
  function addDefaultBoardIncluded (line 295) | function addDefaultBoardIncluded(defaultBoardData) {
  function updateDefaultBoardsIncluded (line 302) | function updateDefaultBoardsIncluded(boardAlreadyIncludedData) {
  function syncCommunicators (line 309) | function syncCommunicators(remoteCommunicators) {

FILE: src/components/Communicator/Communicator.constants.js
  constant IMPORT_COMMUNICATOR (line 1) | const IMPORT_COMMUNICATOR = 'cboard/Communicator/IMPORT_COMMUNICATOR';
  constant CREATE_COMMUNICATOR (line 2) | const CREATE_COMMUNICATOR = 'cboard/Communicator/CREATE_COMMUNICATOR';
  constant EDIT_COMMUNICATOR (line 3) | const EDIT_COMMUNICATOR = 'cboard/Communicator/EDIT_COMMUNICATOR';
  constant DELETE_COMMUNICATOR (line 4) | const DELETE_COMMUNICATOR = 'cboard/Communicator/DELETE_COMMUNICATOR';
  constant CHANGE_COMMUNICATOR (line 5) | const CHANGE_COMMUNICATOR = 'cboard/Communicator/CHANGE_COMMUNICATOR';
  constant ADD_BOARD_COMMUNICATOR (line 6) | const ADD_BOARD_COMMUNICATOR =
  constant DELETE_BOARD_COMMUNICATOR (line 8) | const DELETE_BOARD_COMMUNICATOR =
  constant REPLACE_BOARD_COMMUNICATOR (line 10) | const REPLACE_BOARD_COMMUNICATOR =
  constant ADD_DEFAULT_BOARD_INCLUDED (line 12) | const ADD_DEFAULT_BOARD_INCLUDED =
  constant UPDATE_DEFAULT_BOARDS_INCLUDED (line 14) | const UPDATE_DEFAULT_BOARDS_INCLUDED =
  constant CREATE_API_COMMUNICATOR_SUCCESS (line 16) | const CREATE_API_COMMUNICATOR_SUCCESS =
  constant CREATE_API_COMMUNICATOR_FAILURE (line 18) | const CREATE_API_COMMUNICATOR_FAILURE =
  constant CREATE_API_COMMUNICATOR_STARTED (line 20) | const CREATE_API_COMMUNICATOR_STARTED =
  constant UPDATE_API_COMMUNICATOR_SUCCESS (line 22) | const UPDATE_API_COMMUNICATOR_SUCCESS =
  constant UPDATE_API_COMMUNICATOR_FAILURE (line 24) | const UPDATE_API_COMMUNICATOR_FAILURE =
  constant UPDATE_API_COMMUNICATOR_STARTED (line 26) | const UPDATE_API_COMMUNICATOR_STARTED =
  constant GET_API_MY_COMMUNICATORS_SUCCESS (line 28) | const GET_API_MY_COMMUNICATORS_SUCCESS =
  constant GET_API_MY_COMMUNICATORS_FAILURE (line 30) | const GET_API_MY_COMMUNICATORS_FAILURE =
  constant GET_API_MY_COMMUNICATORS_STARTED (line 32) | const GET_API_MY_COMMUNICATORS_STARTED =
  constant SYNC_COMMUNICATORS (line 34) | const SYNC_COMMUNICATORS = 'Communicator/SYNC_COMMUNICATORS';

FILE: src/components/Communicator/Communicator.reducer.js
  function communicatorReducer (line 35) | function communicatorReducer(state = initialState, action) {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialog.constants.js
  constant TAB_INDEXES (line 1) | const TAB_INDEXES = {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialog.container.js
  constant BOARDS_PAGE_LIMIT (line 27) | const BOARDS_PAGE_LIMIT = 10;
  constant INITIAL_STATE (line 28) | const INITIAL_STATE = {
  class CommunicatorDialogContainer (line 60) | class CommunicatorDialogContainer extends React.Component {
    method constructor (line 61) | constructor(props) {
    method onTabChange (line 79) | async onTabChange(event, selectedTab = TAB_INDEXES.COMMUNICATOR_BOARDS) {
    method loadNextPage (line 92) | async loadNextPage() {
    method doSearch (line 106) | async doSearch(
    method onSearch (line 183) | async onSearch(search = this.state.search) {
    method addOrRemoveBoard (line 206) | async addOrRemoveBoard(board) {
    method communicatorBoardsAction (line 216) | async communicatorBoardsAction(board) {
    method copyBoard (line 225) | async copyBoard(board) {
    method createBoardsRecursively (line 236) | async createBoardsRecursively(board, records) {
    method updateBoardReferences (line 331) | updateBoardReferences(board, newBoard, records) {
    method addOrRemoveAction (line 376) | async addOrRemoveAction(board) {
    method updateCommunicatorBoards (line 408) | async updateCommunicatorBoards(boards) {
    method publishBoard (line 434) | async publishBoard(board) {
    method boardReport (line 460) | async boardReport(reportedBoardData) {
    method setRootBoard (line 469) | async setRootBoard(board) {
    method openSearchBar (line 493) | openSearchBar() {
    method deleteMyBoard (line 497) | async deleteMyBoard(board) {
    method updateMyBoard (line 546) | async updateMyBoard(board) {
    method render (line 564) | render() {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialog.test.js
  constant COMPONENT_PROPS (line 79) | const COMPONENT_PROPS = {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialogBoardItem.component.js
  class CommunicatorDialogBoardItem (line 67) | class CommunicatorDialogBoardItem extends React.Component {
    method constructor (line 68) | constructor(props) {
    method openMenu (line 93) | openMenu(e) {
    method closeMenu (line 97) | closeMenu() {
    method handleBoardInfoOpen (line 101) | handleBoardInfoOpen() {
    method handleBoardDeleteOpen (line 107) | handleBoardDeleteOpen() {
    method handleBoardReportOpen (line 113) | handleBoardReportOpen() {
    method handleBoardImageChange (line 134) | handleBoardImageChange(image) {
    method handleBoardPublishOpen (line 138) | async handleBoardPublishOpen(board) {
    method handleBoardCopyOpen (line 159) | handleBoardCopyOpen() {
    method handleBoardCopy (line 165) | async handleBoardCopy(board) {
    method handleBoardDelete (line 180) | async handleBoardDelete(board) {
    method handleBoardImage (line 261) | async handleBoardImage(board) {
    method handleBoardTitleDesc (line 284) | async handleBoardTitleDesc(board) {
    method handleBoardPublish (line 311) | async handleBoardPublish(board) {
    method handleDialogClose (line 337) | handleDialogClose() {
    method setRootBoard (line 349) | async setRootBoard(board) {
    method render (line 353) | render() {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialogButtons.component.js
  class CommunicatorDialogButtons (line 15) | class CommunicatorDialogButtons extends React.Component {
    method constructor (line 16) | constructor(props) {
    method componentDidUpdate (line 24) | componentDidUpdate(prevProps) {
    method openMenu (line 33) | openMenu(e) {
    method closeMenu (line 37) | closeMenu() {
    method onSearch (line 41) | onSearch(event) {
    method render (line 46) | render() {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialogButtons.test.js
  constant COMPONENT_PROPS (line 56) | const COMPONENT_PROPS = {

FILE: src/components/Communicator/CommunicatorDialog/CommunicatorDialogTour.component.js
  function CommunicatorDialogTour (line 44) | function CommunicatorDialogTour({

FILE: src/components/Communicator/CommunicatorToolbar/CommunicatorToolbar.component.js
  class CommunicatorToolbar (line 23) | class CommunicatorToolbar extends React.Component {
    method constructor (line 24) | constructor(props) {
    method openMenu (line 36) | openMenu(e) {
    method closeMenu (line 40) | closeMenu() {
    method switchBoard (line 44) | switchBoard(board) {
    method render (line 105) | render() {

FILE: src/components/Communicator/CommunicatorToolbar/CommunicatorToolbar.container.js
  class CommunicatorContainer (line 19) | class CommunicatorContainer extends React.Component {
    method constructor (line 20) | constructor(props) {
    method openCommunicatorDialog (line 28) | openCommunicatorDialog() {
    method closeCommunicatorDialog (line 32) | closeCommunicatorDialog() {
    method render (line 62) | render() {

FILE: src/components/Communicator/CommunicatorToolbar/CommunicatorToolbar.test.js
  constant COMPONENT_PROPS (line 53) | const COMPONENT_PROPS = {

FILE: src/components/EditGridButtons/EditGridButtons.component.js
  class EditGridButtons (line 12) | class EditGridButtons extends React.Component {
    method constructor (line 22) | constructor(props) {
    method onAddRemoveColumn (line 28) | onAddRemoveColumn(isAdd, isLeftOrTop) {
    method onAddRemoveRow (line 33) | onAddRemoveRow(isAdd, isLeftOrTop) {
    method render (line 76) | render() {

FILE: src/components/FixedGrid/DraggableItem/DraggableItem.js
  function DraggableItem (line 8) | function DraggableItem(props) {

FILE: src/components/FixedGrid/DroppableCell/DroppableCell.js
  function DroppableCell (line 8) | function DroppableCell(props) {

FILE: src/components/FixedGrid/Grid.js
  function chunks (line 8) | function chunks(array, size) {
  function Grid (line 23) | function Grid(props) {

FILE: src/components/FixedGrid/GridBase.js
  function GridBase (line 11) | function GridBase(props) {

FILE: src/components/FixedGrid/Row/Row.js
  function Row (line 7) | function Row(props) {

FILE: src/components/FixedGrid/utils.ts
  type Grid (line 3) | interface Grid {
  type GridOrder (line 9) | type GridOrder = (string | null)[][];
  function createGrid (line 11) | function createGrid(rows: number = 2, columns: number = 2): Grid {
  function createMatrix (line 23) | function createMatrix(rows: number, columns: number): any[][] {
  function moveOrderItem (line 31) | function moveOrderItem(
  function sortGrid (line 58) | function sortGrid({
  function iterateGridItems (line 87) | function iterateGridItems(
  function fillEmptyGridCells (line 98) | function fillEmptyGridCells(grid: any[][], items: any[]) {
  function getNewOrder (line 108) | function getNewOrder({ columns,
  function removeOrderItems (line 125) | function removeOrderItems(ids: string, order: GridOrder): GridOrder {
  function getDeprecatedOrderedPages (line 129) | function getDeprecatedOrderedPages({
  function getTilesListForNewOrder (line 178) | function getTilesListForNewOrder({

FILE: src/components/Grid/Grid.constants.js
  constant GRID_BREAKPOINTS (line 1) | const GRID_BREAKPOINTS = { lg: 1200, md: 996, sm: 768, xs: 567, xxs: 0 };

FILE: src/components/Grid/Grid.container.js
  class GridContainer (line 19) | class GridContainer extends PureComponent {
    method componentDidMount (line 44) | componentDidMount() {
    method configBigScrollBtns (line 48) | configBigScrollBtns() {
    method getBreakpointFromWidth (line 59) | getBreakpointFromWidth(breakpoints, width) {
    method calcRowHeight (line 76) | calcRowHeight(height) {
    method generateLayout (line 101) | generateLayout(cols) {
    method generateLayouts (line 114) | generateLayouts() {
    method render (line 132) | render() {

FILE: src/components/LoggedInFeature/LoginRequiredModal.js
  function LoginRequiredModal (line 18) | function LoginRequiredModal({ hideLoginRequired, loginRequiredModalState...

FILE: src/components/NavigationButtons/NavigationButtons.test.js
  constant COMPONENT_PROPS (line 7) | const COMPONENT_PROPS = {

FILE: src/components/Notifications/Notifications.actions.js
  function showNotification (line 6) | function showNotification(message, kind) {
  function hideNotification (line 15) | function hideNotification() {

FILE: src/components/Notifications/Notifications.component.js
  function onRefreshPage (line 19) | function onRefreshPage() {

FILE: src/components/Notifications/Notifications.constants.js
  constant SHOW_NOTIFICATION (line 1) | const SHOW_NOTIFICATION = 'SHOW_NOTIFICATION';
  constant HIDE_NOTIFICATION (line 2) | const HIDE_NOTIFICATION = 'HIDE_NOTIFICATION';
  constant NOTIFICATION_DELAY (line 3) | const NOTIFICATION_DELAY = 5000;

FILE: src/components/Notifications/Notifications.container.js
  class NotificationsContainer (line 7) | class NotificationsContainer extends Component {
    method shouldComponentUpdate (line 48) | shouldComponentUpdate(nextProps) {
    method render (line 82) | render() {

FILE: src/components/Notifications/Notifications.reducer.js
  function notificationsReducer (line 8) | function notificationsReducer(state = initialState, action) {

FILE: src/components/Notifications/__tests__/Notifications.component.test.js
  constant COMPONENT_PROPS (line 5) | const COMPONENT_PROPS = {

FILE: src/components/PremiumFeature/PremiumFeature.js
  function isUpdateSubscriberStatusNeeded (line 13) | function isUpdateSubscriberStatusNeeded(lastUpdated) {
  function PremiumFeature (line 22) | function PremiumFeature({

FILE: src/components/PremiumFeature/PremiumRequiredModal.js
  function PremiumRequiredModal (line 19) | function PremiumRequiredModal({

FILE: src/components/ScrollButtons/ScrollButtons.js
  constant BOTTOM_OFFSET (line 8) | const BOTTOM_OFFSET = 7;
  constant TOP_OFFSET (line 9) | const TOP_OFFSET = 5;

FILE: src/components/Settings/About/About.component.js
  function About (line 34) | function About({ history, onClose }) {

FILE: src/components/Settings/Display/Display.component.js
  class Display (line 42) | class Display extends React.Component {
    method constructor (line 43) | constructor(props) {
    method onDisplaySettingsChange (line 69) | onDisplaySettingsChange(displaySetting, event) {
    method renderFontFamilySelect (line 76) | renderFontFamilySelect() {
    method renderSelect (line 106) | renderSelect(name) {
    method onSubmit (line 160) | onSubmit() {
    method render (line 164) | render() {

FILE: src/components/Settings/Display/Display.constants.js
  constant DISPLAY_SIZE_STANDARD (line 1) | const DISPLAY_SIZE_STANDARD = 'Standard';
  constant DISPLAY_SIZE_LARGE (line 2) | const DISPLAY_SIZE_LARGE = 'Large';
  constant DISPLAY_SIZE_EXTRALARGE (line 3) | const DISPLAY_SIZE_EXTRALARGE = 'ExtraLarge';
  constant LABEL_POSITION_ABOVE (line 6) | const LABEL_POSITION_ABOVE = 'Above';
  constant LABEL_POSITION_BELOW (line 7) | const LABEL_POSITION_BELOW = 'Below';
  constant LABEL_POSITION_HIDDEN (line 8) | const LABEL_POSITION_HIDDEN = 'Hidden';
  constant DISPLAY_SIZE_GRID_COLS (line 10) | const DISPLAY_SIZE_GRID_COLS = {

FILE: src/components/Settings/Display/Display.container.js
  class DisplayContainer (line 9) | class DisplayContainer extends PureComponent {
    method render (line 17) | render() {

FILE: src/components/Settings/Display/Display.test.js
  constant COMPONENT_PROPS (line 44) | const COMPONENT_PROPS = {

FILE: src/components/Settings/Export/Export.component.js
  class Export (line 42) | class Export extends React.Component {
    method constructor (line 43) | constructor(props) {
    method openMenu (line 57) | openMenu(e) {
    method closeMenu (line 61) | closeMenu() {
    method render (line 116) | render() {

FILE: src/components/Settings/Export/Export.constants.js
  constant CBOARD_OBF_CONSTANTS (line 7) | const CBOARD_OBF_CONSTANTS = {
  constant CBOARD_ZIP_OPTIONS (line 20) | const CBOARD_ZIP_OPTIONS = {
  constant CBOARD_COLUMNS (line 26) | const CBOARD_COLUMNS = 6;
  constant CBOARD_ROWS (line 27) | const CBOARD_ROWS = 4;
  constant CBOARD_EXT_PREFIX (line 28) | const CBOARD_EXT_PREFIX = 'ext_cboard_';
  constant CBOARD_EXT_PROPERTIES (line 29) | const CBOARD_EXT_PROPERTIES = ['labelKey', 'nameKey', 'hidden'];
  constant NOT_FOUND_IMAGE (line 30) | const NOT_FOUND_IMAGE =
  constant EMPTY_IMAGE (line 32) | const EMPTY_IMAGE =
  constant PICSEEPAL_GRID_WIDTH (line 35) | const PICSEEPAL_GRID_WIDTH = 553;
  constant PDF_GRID_WIDTH (line 36) | const PDF_GRID_WIDTH = 800;
  constant PDF_BORDER_WIDTH (line 37) | const PDF_BORDER_WIDTH = 2;
  constant SMALL_FONT_SIZE (line 38) | const SMALL_FONT_SIZE = 9;
  constant MEDIUM_FONT_SIZE (line 39) | const MEDIUM_FONT_SIZE = 12;
  constant LARGE_FONT_SIZE (line 40) | const LARGE_FONT_SIZE = 16;
  constant EXPORT_CONFIG_BY_TYPE (line 42) | const EXPORT_CONFIG_BY_TYPE = {
  constant PDF_GRID_BORDER (line 61) | const PDF_GRID_BORDER = {
  constant FONTS (line 76) | const FONTS = {
  constant PICSEEPAL_IMAGES_WIDTH (line 128) | const PICSEEPAL_IMAGES_WIDTH = {
  constant PDF_IMAGES_WIDTH (line 159) | const PDF_IMAGES_WIDTH = {

FILE: src/components/Settings/Export/Export.container.js
  class ExportContainer (line 12) | class ExportContainer extends PureComponent {
    method render (line 108) | render() {

FILE: src/components/Settings/Export/Export.helpers.js
  function toSnakeCase (line 48) | function toSnakeCase(str) {
  function getOBFButtonProps (line 53) | function getOBFButtonProps(tile = {}, intl) {
  function getBase64Image (line 84) | function getBase64Image(base64Str = '') {
  function getDataUri (line 103) | async function getDataUri(url) {
  function boardToOBF (line 142) | async function boardToOBF(boardsMap, board = {}, intl, { embed = false }) {
  function getPDFTileData (line 270) | function getPDFTileData(tile, intl) {
  function toDataURL (line 279) | async function toDataURL(url, styles = {}, outputFormat = 'image/jpeg') {
  function getCellWidths (line 370) | function getCellWidths(columns, picsee = false) {
  function generatePDFBoard (line 377) | async function generatePDFBoard(
  function chunks (line 443) | function chunks(array, size) {
  function generateFixedBoard (line 454) | async function generateFixedBoard(
  function generateNonFixedBoard (line 526) | async function generateNonFixedBoard(
  function openboardExportAdapter (line 713) | async function openboardExportAdapter(boardOrBoards, intl) {
  function openboardExportOneAdapter (line 719) | async function openboardExportOneAdapter(board, intl) {
  function openboardExportManyAdapter (line 739) | async function openboardExportManyAdapter(boards = [], intl) {
  function getNestedBoards (line 825) | function getNestedBoards(allBoards, rootBoardId) {
  function cboardExportAdapter (line 848) | async function cboardExportAdapter(allBoards = [], board) {
  function pdfExportAdapter (line 891) | async function pdfExportAdapter(
  function generatePDF (line 1013) | function generatePDF(callback) {
  function definePDFfont (line 1027) | function definePDFfont(intl) {

FILE: src/components/Settings/Export/Export.test.js
  constant COMPONENT_PROPS (line 48) | const COMPONENT_PROPS = {

FILE: src/components/Settings/Help/Help.component.js
  class Help (line 18) | class Help extends React.Component {
    method constructor (line 19) | constructor(props) {
    method componentDidMount (line 27) | componentDidMount() {
    method formatTextForCordova (line 57) | formatTextForCordova(text) {
    method componentDidUpdate (line 72) | componentDidUpdate() {
    method render (line 82) | render() {

FILE: src/components/Settings/Import/Import.component.js
  class Import (line 29) | class Import extends React.Component {
    method constructor (line 30) | constructor(props) {
    method onImportClick (line 38) | onImportClick(event) {
    method render (line 51) | render() {

FILE: src/components/Settings/Import/Import.constants.js
  constant IMPORT_CONFIG_BY_EXTENSION (line 11) | const IMPORT_CONFIG_BY_EXTENSION = {
  constant IMPORT_PATHS (line 18) | const IMPORT_PATHS = {
  constant CBOARD_EXT_PREFIX (line 23) | const CBOARD_EXT_PREFIX = EXPORT_CBOARD_EXT_PREFIX;
  constant CBOARD_EXT_PROPERTIES (line 25) | const CBOARD_EXT_PROPERTIES = EXPORT_CBOARD_EXT_PROPERTIES;

FILE: src/components/Settings/Import/Import.container.js
  class ImportContainer (line 18) | class ImportContainer extends PureComponent {
    method updateLoadBoardsIds (line 25) | async updateLoadBoardsIds(boards, shouldUpdate = false) {
    method syncBoardsWithAPI (line 69) | async syncBoardsWithAPI(boards) {
    method addBoardsToCommunicator (line 123) | async addBoardsToCommunicator(boards) {
    method handleImportClick (line 152) | async handleImportClick(e, doneCallback) {
    method render (line 205) | render() {

FILE: src/components/Settings/Import/Import.helpers.js
  function toCamelCase (line 7) | function toCamelCase(scString = '') {
  function readZip (line 14) | async function readZip(file) {
  function obfButtonToCboardButton (line 45) | function obfButtonToCboardButton(button) {
  function getTilesData (line 70) | async function getTilesData(obfBoard, boards = {}, images = {}) {
  function obfToCboard (line 130) | async function obfToCboard(obfBoard, boards = {}, images = {}, allBoards...
  function getBoardsIds (line 168) | function getBoardsIds(boards) {
  function cboardImportAdapter (line 181) | async function cboardImportAdapter(file, intl, allBoards) {
  function obzImportAdapter (line 206) | async function obzImportAdapter(file, intl, allBoards) {
  function obfImportAdapter (line 267) | async function obfImportAdapter(file, intl, allBoards) {
  function requestQuota (line 294) | async function requestQuota(json) {

FILE: src/components/Settings/Import/Import.test.js
  constant COMPONENT_PROPS (line 25) | const COMPONENT_PROPS = {

FILE: src/components/Settings/Language/DownloadDialog.js
  function DownloadDialog (line 25) | function DownloadDialog(props) {

FILE: src/components/Settings/Language/Language.component.js
  class Language (line 37) | class Language extends React.Component {
    method constructor (line 98) | constructor(props) {
    method componentDidMount (line 110) | componentDidMount() {
    method formatTextForCordova (line 140) | formatTextForCordova(text) {
    method componentDidUpdate (line 155) | componentDidUpdate(prevProps) {
    method handleTtsEngineChange (line 164) | async handleTtsEngineChange(event) {
    method handleMoreLangClick (line 181) | handleMoreLangClick() {
    method handleMoreLangClose (line 185) | handleMoreLangClose() {
    method handleTtsErrorDialogClose (line 189) | async handleTtsErrorDialogClose() {
    method isDownloadable (line 195) | isDownloadable(lang) {
    method getFormattedName (line 236) | getFormattedName(lang) {
    method getNativeName (line 247) | getNativeName(lang) {
    method render (line 271) | render() {

FILE: src/components/Settings/Language/Language.container.js
  class LanguageContainer (line 41) | class LanguageContainer extends Component {
    method render (line 514) | render() {

FILE: src/components/Settings/Language/downloadingLangErrorDialog.js
  function DownloadingLangErrorDialog (line 26) | function DownloadingLangErrorDialog(props) {

FILE: src/components/Settings/Navigation/Navigation.component.js
  class Navigation (line 33) | class Navigation extends React.Component {
    method constructor (line 34) | constructor(props) {
    method onNavigationSettingsChange (line 104) | onNavigationSettingsChange(navigationSetting, event) {
    method renderNavigationButtonsLocationSelect (line 111) | renderNavigationButtonsLocationSelect() {
    method render (line 143) | render() {

FILE: src/components/Settings/Navigation/Navigation.constants.js
  constant NAVIGATION_BUTTONS_STYLE_SIDES (line 1) | const NAVIGATION_BUTTONS_STYLE_SIDES = 'sides';
  constant NAVIGATION_BUTTONS_STYLE_TOP (line 2) | const NAVIGATION_BUTTONS_STYLE_TOP = 'top';
  constant NAVIGATION_BUTTONS_STYLES (line 4) | const NAVIGATION_BUTTONS_STYLES = [

FILE: src/components/Settings/Navigation/Navigation.container.js
  class NavigationContainer (line 9) | class NavigationContainer extends PureComponent {
    method render (line 23) | render() {

FILE: src/components/Settings/Navigation/Navigation.test.js
  constant INITIAL_NAVIGATION_SETTINGS (line 25) | const INITIAL_NAVIGATION_SETTINGS = {
  constant COMPONENT_PROPS (line 39) | const COMPONENT_PROPS = {

FILE: src/components/Settings/People/People.container.js
  class PeopleContainer (line 13) | class PeopleContainer extends PureComponent {
    method render (line 76) | render() {

FILE: src/components/Settings/Scanning/Scanning.component.js
  constant SCANNER_MESSAGES_KEYMAP (line 29) | const SCANNER_MESSAGES_KEYMAP = {
  constant DELAY_OPTIONS (line 34) | const DELAY_OPTIONS = [
  class Scanning (line 57) | class Scanning extends React.Component {
    method constructor (line 58) | constructor(props) {
    method render (line 82) | render() {

FILE: src/components/Settings/Scanning/Scanning.constants.js
  constant SCANNING_METHOD_AUTOMATIC (line 1) | const SCANNING_METHOD_AUTOMATIC = 'automatic';
  constant SCANNING_METHOD_MANUAL (line 2) | const SCANNING_METHOD_MANUAL = 'manual';

FILE: src/components/Settings/Scanning/Scanning.container.js
  class ScanningContainer (line 9) | class ScanningContainer extends PureComponent {
    method render (line 21) | render() {

FILE: src/components/Settings/Scanning/Scanning.test.js
  constant INITIAL_SCANNING_SETTINGS (line 53) | const INITIAL_SCANNING_SETTINGS = {
  constant COMPONENT_PROPS (line 61) | const COMPONENT_PROPS = {

FILE: src/components/Settings/Settings.component.js
  class Settings (line 48) | class Settings extends PureComponent {
    method getSettingsSections (line 49) | getSettingsSections() {
    method render (line 226) | render() {

FILE: src/components/Settings/Settings.container.js
  class SettingsContainer (line 11) | class SettingsContainer extends Component {
    method render (line 21) | render() {

FILE: src/components/Settings/SettingsSection.component.js
  class SettingsSection (line 17) | class SettingsSection extends PureComponent {
    method render (line 26) | render() {

FILE: src/components/Settings/SettingsTour.component.js
  function SettingsTour (line 97) | function SettingsTour({ intl, disableTour, isSettingsTourEnabled }) {

FILE: src/components/Settings/Speech/Speech.constants.js
  constant MIN_PITCH (line 1) | const MIN_PITCH = 0.0;
  constant MAX_PITCH (line 2) | const MAX_PITCH = 2.0;
  constant INCREMENT_PITCH (line 3) | const INCREMENT_PITCH = 0.25;
  constant MIN_RATE (line 4) | const MIN_RATE = 0;
  constant MAX_RATE (line 5) | const MAX_RATE = 2;
  constant INCREMENT_RATE (line 6) | const INCREMENT_RATE = 0.25;
  constant MIN_ELEVENLABS_SPEED (line 7) | const MIN_ELEVENLABS_SPEED = 0.7;
  constant MAX_ELEVENLABS_SPEED (line 8) | const MAX_ELEVENLABS_SPEED = 1.2;
  constant INCREMENT_ELEVENLABS_SPEED (line 9) | const INCREMENT_ELEVENLABS_SPEED = 0.05;
  constant MIN_ELEVENLABS_STABILITY (line 10) | const MIN_ELEVENLABS_STABILITY = 0;
  constant MAX_ELEVENLABS_STABILITY (line 11) | const MAX_ELEVENLABS_STABILITY = 1;
  constant INCREMENT_ELEVENLABS_STABILITY (line 12) | const INCREMENT_ELEVENLABS_STABILITY = 0.05;
  constant MIN_ELEVENLABS_SIMILARITY (line 13) | const MIN_ELEVENLABS_SIMILARITY = 0;
  constant MAX_ELEVENLABS_SIMILARITY (line 14) | const MAX_ELEVENLABS_SIMILARITY = 1;
  constant INCREMENT_ELEVENLABS_SIMILARITY (line 15) | const INCREMENT_ELEVENLABS_SIMILARITY = 0.05;
  constant MIN_ELEVENLABS_STYLE (line 16) | const MIN_ELEVENLABS_STYLE = 0;
  constant MAX_ELEVENLABS_STYLE (line 17) | const MAX_ELEVENLABS_STYLE = 1;
  constant INCREMENT_ELEVENLABS_STYLE (line 18) | const INCREMENT_ELEVENLABS_STYLE = 0.05;

FILE: src/components/Settings/Speech/Speech.container.js
  class SpeechContainer (line 31) | class SpeechContainer extends Component {
    method componentDidMount (line 62) | componentDidMount() {
    method componentDidUpdate (line 69) | componentDidUpdate(prevProps) {
    method updateSettings (line 154) | updateSettings(property, value) {
    method getElevenLabsSettings (line 243) | getElevenLabsSettings() {
    method render (line 257) | render() {

FILE: src/components/Settings/Subscribe/Subscribe.constants.js
  constant INCLUDED_FEATURES (line 1) | const INCLUDED_FEATURES = [
  constant ERROR (line 14) | const ERROR = 'error';
  constant EMPTY_PRODUCT (line 15) | const EMPTY_PRODUCT = 'empty_product';
  constant ON_TRIAL_PERIOD (line 16) | const ON_TRIAL_PERIOD = 'on_trial_period';
  constant GOOGLE_PLAY_STORE_URL (line 17) | const GOOGLE_PLAY_STORE_URL =
  constant APP_STORE_URL (line 19) | const APP_STORE_URL = 'https://www.apple.com/app-store/';

FILE: src/components/Settings/Subscribe/Subscribe.container.js
  class SubscribeContainer (line 29) | class SubscribeContainer extends PureComponent {
    method componentDidMount (line 41) | async componentDidMount() {
    method render (line 345) | render() {

FILE: src/components/Settings/Subscribe/SubscriptionInfo.js
  constant LABEL (line 45) | const LABEL = 0;
  constant VALUE (line 46) | const VALUE = 1;

FILE: src/components/Settings/Subscribe/SubscriptionPlans.js
  function errorMessage (line 148) | function errorMessage() {

FILE: src/components/Settings/Symbols/DeleteArasaacDialog.js
  function DeleteArasaacDialog (line 23) | function DeleteArasaacDialog(props) {

FILE: src/components/Settings/Symbols/DownloadArasaacDialog.js
  function DownloadArasaacDialog (line 21) | function DownloadArasaacDialog(props) {

FILE: src/components/Settings/Symbols/NoConnectionDialog.js
  function NoConnectionDialog (line 20) | function NoConnectionDialog(props) {

FILE: src/components/Settings/Symbols/Symbols.component.js
  class Symbols (line 25) | class Symbols extends React.Component {
    method constructor (line 26) | constructor(props) {
    method render (line 60) | render() {

FILE: src/components/Settings/Symbols/Symbols.container.js
  class SymbolsContainer (line 15) | class SymbolsContainer extends PureComponent {
    method constructor (line 20) | constructor(props) {
    method render (line 138) | render() {

FILE: src/components/UI/AnalyticsButton/AnalyticsButton.js
  function AnalyticsButton (line 28) | function AnalyticsButton(props) {

FILE: src/components/UI/BackButton/BackButton.js
  function BackButton (line 30) | function BackButton(props) {

FILE: src/components/UI/ColorSelect/Circle/Circle.js
  function Circle (line 19) | function Circle(props) {

FILE: src/components/UI/ColorSelect/ColorSelect.js
  class ColorSelect (line 52) | class ColorSelect extends React.Component {
    method constructor (line 53) | constructor(props) {
    method render (line 75) | render() {

FILE: src/components/UI/ColorSelect/ColorSelect.test.js
  constant COLORS (line 23) | const COLORS = ['#CE93D8', '#2196F3', '#4CAF50', '#E57373'];

FILE: src/components/UI/ColorSelect/HairColorSelect.js
  class HairColorSelect (line 63) | class HairColorSelect extends React.Component {
    method constructor (line 64) | constructor(props) {
    method componentDidMount (line 80) | componentDidMount() {
    method componentWillUnmount (line 84) | componentWillUnmount() {
    method toggleOpen (line 105) | toggleOpen() {
    method render (line 112) | render() {

FILE: src/components/UI/ColorSelect/HairColorSelect.test.js
  constant HAIR_COLORS (line 20) | const HAIR_COLORS = [

FILE: src/components/UI/ColorSelect/SkinToneSelect.js
  class SkinToneSelect (line 55) | class SkinToneSelect extends React.Component {
    method constructor (line 56) | constructor(props) {
    method componentDidMount (line 69) | componentDidMount() {
    method componentWillUnmount (line 73) | componentWillUnmount() {
    method toggleOpen (line 94) | toggleOpen() {
    method render (line 101) | render() {

FILE: src/components/UI/ColorSelect/SkinToneSelect.test.js
  constant SKIN_TONES (line 20) | const SKIN_TONES = ['#f5e5de', '#a65c17', '#f4ecad', '#e3ab72', '#cf9d7c'];

FILE: src/components/UI/FilterBar/FilterBar.js
  function FilterBar (line 9) | function FilterBar(props) {

FILE: src/components/UI/FormDialog/FormDialog.js
  function FormDialog (line 33) | function FormDialog(props) {

FILE: src/components/UI/FormDialog/FormDialog.test.js
  constant COMPONENT_PROPS (line 18) | const COMPONENT_PROPS = {};

FILE: src/components/UI/FullScreenDialog/FullScreenDialog.js
  function getTransition (line 77) | function getTransition(transition) {
  function FullScreenDialog (line 88) | function FullScreenDialog(props) {

FILE: src/components/UI/FullScreenDialog/FullScreenDialogContent.js
  function FullScreenDialogContent (line 4) | function FullScreenDialogContent({ className, children }) {

FILE: src/components/UI/HelpButton/HelpButton.js
  function HelpButton (line 28) | function HelpButton(props) {

FILE: src/components/UI/IconButton/IconButton.js
  function IconButton (line 37) | function IconButton({ children, component, to, label, disabled, onClick ...

FILE: src/components/UI/InputImage/InputImage.component.js
  class InputImage (line 27) | class InputImage extends Component {
    method resizeImage (line 43) | async resizeImage(file, imageName = null) {
    method render (line 104) | render() {

FILE: src/components/UI/LoadingIcon/LoadingIcon.js
  function LoadingIcon (line 6) | function LoadingIcon() {

FILE: src/components/UI/LockToggle/LockToggle.js
  function LockToggle (line 22) | function LockToggle(props) {

FILE: src/components/UI/LockToggle/childProof.js
  function withChildProof (line 5) | function withChildProof(WrappedComponent) {

FILE: src/components/UI/PrintBoardButton/PrintBoardButton.container.js
  class PrintBoardButtonContainer (line 11) | class PrintBoardButtonContainer extends React.Component {
    method constructor (line 12) | constructor(props) {
    method componentDidMount (line 21) | componentDidMount() {
    method openPrintBoardDialog (line 25) | openPrintBoardDialog() {
    method closePrintBoardDialog (line 29) | closePrintBoardDialog() {
    method onPrintCurrentBoard (line 33) | async onPrintCurrentBoard() {
    method render (line 47) | render() {

FILE: src/components/UI/ResetToursItem/ResetToursItem.component.js
  function ResetToursItem (line 17) | function ResetToursItem(props) {

FILE: src/components/UI/SettingsButton/SettingsButton.js
  function SettingsButton (line 28) | function SettingsButton(props) {

FILE: src/components/UI/UserIcon/UserIcon.test.js
  constant COMPONENT_PROPS (line 14) | const COMPONENT_PROPS = {

FILE: src/components/VoiceRecorder/VoiceRecorder.component.js
  class VoiceRecorder (line 13) | class VoiceRecorder extends Component {
    method render (line 109) | render() {

FILE: src/components/WelcomeScreen/WelcomeScreen.container.js
  class WelcomeScreen (line 56) | class WelcomeScreen extends Component {
    method updateDialogStyle (line 164) | updateDialogStyle() {
    method componentDidUpdate (line 186) | componentDidUpdate(prevProps, prevState) {
    method componentDidMount (line 197) | componentDidMount() {
    method componentWillUnmount (line 204) | componentWillUnmount() {
    method render (line 213) | render() {

FILE: src/config.js
  constant ENV_LIST (line 3) | const ENV_LIST = ['local', 'prod'];
  constant CBOARD_ENV (line 4) | const CBOARD_ENV = process.env.CBOARD_ENV;

FILE: src/constants.js
  constant DEV_API_URL (line 5) | const DEV_API_URL = process.env.REACT_APP_DEV_API_URL || null;
  constant ARASAAC_BASE_PATH_API (line 6) | const ARASAAC_BASE_PATH_API = 'https://api.arasaac.org/api/';
  constant GLOBALSYMBOLS_BASE_PATH_API (line 7) | const GLOBALSYMBOLS_BASE_PATH_API = 'https://globalsymbols.com/api/v1/';
  constant RAW_API_URL (line 9) | const RAW_API_URL = isCordova()
  constant RAW_API_URL_LAST_CHAR (line 12) | const RAW_API_URL_LAST_CHAR = RAW_API_URL.length - 1;
  constant API_URL (line 13) | const API_URL =
  constant AZURE_INST_KEY (line 17) | const AZURE_INST_KEY =
  constant AZURE_SPEECH_SUBSCR_KEY (line 20) | const AZURE_SPEECH_SUBSCR_KEY =
  constant AZURE_SPEECH_SERVICE_REGION (line 22) | const AZURE_SPEECH_SERVICE_REGION =
  constant AZURE_VOICES_BASE_PATH_API (line 24) | const AZURE_VOICES_BASE_PATH_API =
  constant NODE_ENV (line 30) | const NODE_ENV = process.env.NODE_ENV;
  constant IS_PRODUCTION (line 31) | const IS_PRODUCTION = NODE_ENV === 'production';
  constant HOSTNAME (line 32) | const HOSTNAME = window.location.hostname;
  constant ADSENSE_ON_PRODUCTION (line 33) | const ADSENSE_ON_PRODUCTION =
  constant ADTEST_AVAILABLE (line 35) | const ADTEST_AVAILABLE =
  constant ADSENSE_CLIENT (line 37) | const ADSENSE_CLIENT = 'ca-pub-7162313874228987';
  constant ADD_SLOT_SETTINGS_TOP (line 38) | const ADD_SLOT_SETTINGS_TOP = '5250438005';
  constant IS_BROWSING_FROM_APPLE (line 42) | const IS_BROWSING_FROM_APPLE = /iPad|iPhone|iPod|Mac/.test(userAgent);
  constant IS_BROWSING_FROM_APPLE_TOUCH (line 43) | const IS_BROWSING_FROM_APPLE_TOUCH =
  constant IS_BROWSING_FROM_SAFARI (line 45) | const IS_BROWSING_FROM_SAFARI =
  constant PAYPAL_CLIENT_ID (line 53) | const PAYPAL_CLIENT_ID =
  constant GOOGLE_FIREBASE_WEB_CLIENT_ID (line 58) | const GOOGLE_FIREBASE_WEB_CLIENT_ID =
  constant ELEVENLABS_API_BASE_URL (line 63) | const ELEVENLABS_API_BASE_URL = 'https://api.elevenlabs.io';
  constant ELEVENLABS_DEFAULT_TIMEOUT (line 64) | const ELEVENLABS_DEFAULT_TIMEOUT = 10000;

FILE: src/cordova-util.js
  function errorHandler (line 88) | function errorHandler(error) {
  function errorHandler (line 112) | function errorHandler(error) {

FILE: src/helpers.js
  constant DEFAULT_BOARDS (line 4) | const DEFAULT_BOARDS = {

FILE: src/i18n.js
  function importTranslation (line 24) | async function importTranslation(lang) {
  function stripRegionCode (line 28) | function stripRegionCode(lang) {
  function normalizeLanguageCode (line 32) | function normalizeLanguageCode(lang) {
  function standardizeLanguageCode (line 41) | function standardizeLanguageCode(lang) {
  function getDefaultLang (line 63) | function getDefaultLang(langs) {
  function getVoicesLangs (line 72) | function getVoicesLangs(voices) {
  function getSupportedLangs (line 80) | function getSupportedLangs(voices) {
  function filterLocalLangs (line 106) | function filterLocalLangs(voices) {
  function getVoiceURI (line 122) | function getVoiceURI(language, voices) {

FILE: src/idb/arasaac/arasaacdb.ts
  type Image (line 4) | interface Image {
  type Text (line 10) | interface Text {
  type ArasaacDB (line 15) | interface ArasaacDB extends DBSchema {
  constant DB_NAME (line 33) | const DB_NAME = 'arasaac';
  constant DB_VERSION (line 34) | const DB_VERSION = 1;
  method upgrade (line 37) | upgrade(db: IDBPDatabase<ArasaacDB>): void {
  function clearDataBase (line 46) | async function clearDataBase(): Promise<void> {
  function getAllImages (line 53) | async function getAllImages(): Promise<Image[]> {
  function getImageById (line 58) | async function getImageById(id: string): Promise<Image | undefined> {
  function addImage (line 63) | async function addImage(symbol: Image): Promise<string> {
  function addText (line 68) | async function addText(langCode: string, text: Text): Promise<string> {
  function addKeyword (line 73) | async function addKeyword(langCode: string, keywords: Text[]): Promise<s...
  function getImagesByKeyword (line 78) | async function getImagesByKeyword(
  function getTextByLangCode (line 120) | async function getTextByLangCode(langCode: string): Promise<Text | undef...
  function importContent (line 125) | async function importContent({
  function initTextStore (line 158) | async function initTextStore(lang: string): Promise<void> {
  function getImagesText (line 188) | async function getImagesText(

FILE: src/idb/arasaac/jszip.ts
  function readFile (line 4) | async function readFile(file: File): Promise<ParsedZip> {
  type ParsedZip (line 9) | interface ParsedZip {
  function parseZip (line 14) | async function parseZip(zip: JSZip): Promise<ParsedZip> {

FILE: src/providers/LanguageProvider/LanguageProvider.actions.js
  function changeLang (line 7) | function changeLang(lang, isNewVoiceAvailable = false) {
  function setLangs (line 15) | function setLangs(langs, localLangs) {
  function setDownloadingLang (line 23) | function setDownloadingLang(downloadingLangData) {

FILE: src/providers/LanguageProvider/LanguageProvider.constants.js
  constant CHANGE_LANG (line 1) | const CHANGE_LANG = 'cboard/LanguageProvider/CHANGE_LANG';
  constant SET_LANGS (line 2) | const SET_LANGS = 'cboard/LanguageProvider/SET_LANGS';
  constant SET_DOWNLOADING_LANG (line 3) | const SET_DOWNLOADING_LANG =

FILE: src/providers/LanguageProvider/LanguageProvider.container.js
  class LanguageProvider (line 11) | class LanguageProvider extends Component {
    method componentDidMount (line 32) | componentDidMount() {
    method componentDidUpdate (line 42) | componentDidUpdate(prevProps) {
    method fetchMessages (line 48) | fetchMessages(lang) {
    method render (line 73) | render() {

FILE: src/providers/LanguageProvider/LanguageProvider.reducer.js
  function getDir (line 10) | function getDir(lang) {
  function languageProviderReducer (line 24) | function languageProviderReducer(state = initialState, action) {

FILE: src/providers/ScannerProvider/ScannerProvider.actions.js
  function activateScanner (line 8) | function activateScanner() {
  function deactivateScanner (line 12) | function deactivateScanner() {
  function toggleScanner (line 16) | function toggleScanner() {
  function updateScannerSettings (line 20) | function updateScannerSettings(payload = {}) {

FILE: src/providers/ScannerProvider/ScannerProvider.constants.js
  constant ACTIVATE_SCANNER (line 1) | const ACTIVATE_SCANNER = 'cboard/ScannerProvider/ACTIVATE_SCANNER';
  constant DEACTIVATE_SCANNER (line 2) | const DEACTIVATE_SCANNER = 'cboard/ScannerProvider/DEACTIVATE_SCANNER';
  constant TOGGLE_SCANNER (line 3) | const TOGGLE_SCANNER = 'cboard/ScannerProvider/TOGGLE_SCANNER';
  constant UPDATE_SCANNER_SETTINGS (line 4) | const UPDATE_SCANNER_SETTINGS =

FILE: src/providers/ScannerProvider/ScannerProvider.reducer.js
  function scannerProviderReducer (line 16) | function scannerProviderReducer(state = initialState, action) {

FILE: src/providers/SpeechProvider/SpeechProvider.actions.js
  function requestVoices (line 36) | function requestVoices() {
  function receiveVoices (line 42) | function receiveVoices(voices) {
  function requestTtsEngine (line 49) | function requestTtsEngine() {
  function receiveTtsEngine (line 55) | function receiveTtsEngine(ttsEngineName) {
  function getTtsEngines (line 62) | function getTtsEngines() {
  function setTtsEngine (line 70) | function setTtsEngine(selectedTtsEngineName) {
  function updateLangSpeechStatus (line 93) | function updateLangSpeechStatus(voices) {
  function getTtsDefaultEngine (line 137) | function getTtsDefaultEngine() {
  function changeVoice (line 145) | function changeVoice(voiceURI, lang) {
  function changePitch (line 160) | function changePitch(pitch) {
  function changeRate (line 167) | function changeRate(rate) {
  function changeElevenLabsApiKey (line 174) | function changeElevenLabsApiKey(elevenLabsApiKey) {
  function getVoices (line 181) | function getVoices() {
  function startSpeech (line 284) | function startSpeech(message) {
  function endSpeech (line 292) | function endSpeech() {
  function cancelSpeech (line 299) | function cancelSpeech() {
  function speak (line 313) | function speak(text, onend = () => {}) {
  function cacheElevenLabsVoices (line 339) | function cacheElevenLabsVoices(voices) {
  function clearElevenLabsCache (line 346) | function clearElevenLabsCache() {
  function setCurrentVoiceSource (line 352) | function setCurrentVoiceSource() {
  function changeElevenLabsStability (line 362) | function changeElevenLabsStability(stability) {
  function changeElevenLabsSimilarity (line 369) | function changeElevenLabsSimilarity(similarity) {
  function changeElevenLabsStyle (line 376) | function changeElevenLabsStyle(style) {
  function resetElevenLabsSettings (line 383) | function resetElevenLabsSettings() {

FILE: src/providers/SpeechProvider/SpeechProvider.constants.js
  constant REQUEST_VOICES (line 1) | const REQUEST_VOICES = 'cboard/Speech/REQUEST_VOICES';
  constant RECEIVE_VOICES (line 2) | const RECEIVE_VOICES = 'cboard/Speech/RECEIVE_VOICES';
  constant CHANGE_VOICE (line 3) | const CHANGE_VOICE = 'cboard/Speech/CHANGE_VOICE';
  constant CHANGE_PITCH (line 4) | const CHANGE_PITCH = 'cboard/Speech/CHANGE_PITCH';
  constant CHANGE_RATE (line 5) | const CHANGE_RATE = 'cboard/Speech/CHANGE_RATE';
  constant CHANGE_VOLUME (line 6) | const CHANGE_VOLUME = 'cboard/Speech/CHANGE_VOLUME';
  constant CHANGE_ELEVENLABS_API_KEY (line 7) | const CHANGE_ELEVENLABS_API_KEY =
  constant START_SPEECH (line 9) | const START_SPEECH = 'cboard/Speech/START_SPEECH';
  constant END_SPEECH (line 10) | const END_SPEECH = 'cboard/Speech/END_SPEECH';
  constant CANCEL_SPEECH (line 11) | const CANCEL_SPEECH = 'cboard/Speech/CANCEL_SPEECH';
  constant RECEIVE_TTS_ENGINES (line 12) | const RECEIVE_TTS_ENGINES = 'cboard/Speech/RECEIVE_TTS_ENGINES';
  constant RECEIVE_TTS_DEFAULT_ENGINE (line 13) | const RECEIVE_TTS_DEFAULT_ENGINE =
  constant REQUEST_TTS_ENGINE (line 15) | const REQUEST_TTS_ENGINE = 'cboard/Speech/REQUEST_TTS_ENGINE';
  constant RECEIVE_TTS_ENGINE (line 16) | const RECEIVE_TTS_ENGINE = 'cboard/Speech/RECEIVE_TTS_ENGINE';
  constant EMPTY_VOICES (line 17) | const EMPTY_VOICES = 'empty voices';
  constant CACHE_ELEVENLABS_VOICES (line 18) | const CACHE_ELEVENLABS_VOICES = 'cboard/Speech/CACHE_ELEVENLABS_VOICES';
  constant CLEAR_ELEVENLABS_CACHE (line 19) | const CLEAR_ELEVENLABS_CACHE = 'cboard/Speech/CLEAR_ELEVENLABS_CACHE';
  constant CHANGE_ELEVENLABS_STABILITY (line 20) | const CHANGE_ELEVENLABS_STABILITY =
  constant CHANGE_ELEVENLABS_SIMILARITY (line 22) | const CHANGE_ELEVENLABS_SIMILARITY =
  constant CHANGE_ELEVENLABS_STYLE (line 24) | const CHANGE_ELEVENLABS_STYLE = 'cboard/Speech/CHANGE_ELEVENLABS_STYLE';
  constant RESET_ELEVENLABS_SETTINGS (line 25) | const RESET_ELEVENLABS_SETTINGS =
  constant ELEVEN_LABS (line 27) | const ELEVEN_LABS = 'elevenlabs';

FILE: src/providers/SpeechProvider/SpeechProvider.container.js
  class SpeechProvider (line 16) | class SpeechProvider extends Component {
    method componentDidMount (line 23) | async componentDidMount() {
    method render (line 70) | render() {

FILE: src/providers/SpeechProvider/SpeechProvider.reducer.js
  function updateElevenLabsVoiceSetting (line 62) | function updateElevenLabsVoiceSetting(state, settingKey, settingValue) {
  function resetElevenLabsVoiceSettings (line 82) | function resetElevenLabsVoiceSettings(state) {
  function speechProviderReducer (line 103) | function speechProviderReducer(state = initialState, action) {

FILE: src/providers/SpeechProvider/engine/elevenlabs.js
  function validateApiKeyFormat (line 3) | function validateApiKeyFormat(apiKey) {
  class ElevenLabsEngine (line 8) | class ElevenLabsEngine {
    method constructor (line 9) | constructor(apiKey) {
    method isInitialized (line 19) | isInitialized() {
    method getElevenLabsPersonalVoices (line 23) | async getElevenLabsPersonalVoices() {
    method synthesizeSpeechElevenLabs (line 52) | async synthesizeSpeechElevenLabs(text, voiceId, settings = {}) {
    method testConnection (line 113) | async testConnection() {
    method reset (line 140) | reset() {

FILE: src/providers/SpeechProvider/tts.js
  method isSupported (line 117) | isSupported() {
  method initElevenLabsInstance (line 121) | initElevenLabsInstance(apiKey) {
  method testElevenLabsConnection (line 126) | async testElevenLabsConnection() {
  method getVoiceByVoiceURI (line 138) | getVoiceByVoiceURI(VoiceURI) {
  method isConnected (line 143) | isConnected() {
  method getLocalVoiceByVoiceURI (line 147) | getLocalVoiceByVoiceURI(VoiceURI) {
  method _getPlatformVoices (line 151) | _getPlatformVoices() {
  method fetchAzureVoices (line 163) | async fetchAzureVoices() {
  method fetchElevenLabsVoices (line 171) | async fetchElevenLabsVoices() {
  method getPlatformVoicesAsync (line 200) | async getPlatformVoicesAsync() {
  method getVoices (line 234) | async getVoices() {
  method setTtsEngine (line 263) | setTtsEngine(ttsEngineName) {
  method getTtsEngines (line 304) | getTtsEngines() {
  method getTtsDefaultEngine (line 316) | getTtsDefaultEngine() {
  method cancel (line 325) | cancel() {
  method speak (line 331) | async speak(

FILE: src/providers/SubscriptionProvider/SubscriptionProvider.actions.js
  function updateIsInFreeCountry (line 27) | function updateIsInFreeCountry() {
  function updateIsOnTrialPeriod (line 45) | function updateIsOnTrialPeriod() {
  function updateIsSubscribed (line 80) | function updateIsSubscribed(requestOrigin = 'unkwnown') {
  function updatePlans (line 296) | function updatePlans() {
  function updateSubscriberId (line 368) | function updateSubscriberId(payload = {}) {
  function updateSubscription (line 374) | function updateSubscription(payload) {
  function updateSubscriptionError (line 380) | function updateSubscriptionError(payload) {
  function showPremiumRequired (line 387) | function showPremiumRequired(
  function hidePremiumRequired (line 398) | function hidePremiumRequired() {
  function showLoginRequired (line 404) | function showLoginRequired() {
  function hideLoginRequired (line 410) | function hideLoginRequired() {

FILE: src/providers/SubscriptionProvider/SubscriptionProvider.constants.js
  constant UPDATE_SUBSCRIBER_ID (line 1) | const UPDATE_SUBSCRIBER_ID = 'cboard/subscription/UPDATE_SUBSCRIBER_ID';
  constant UPDATE_SUBSCRIPTION (line 2) | const UPDATE_SUBSCRIPTION = 'cboard/subscription/UPDATE_SUBSCRIPTION';
  constant UPDATE_SUBSCRIPTION_ERROR (line 3) | const UPDATE_SUBSCRIPTION_ERROR =
  constant SHOW_PREMIUM_REQUIRED (line 5) | const SHOW_PREMIUM_REQUIRED =
  constant HIDE_PREMIUM_REQUIRED (line 7) | const HIDE_PREMIUM_REQUIRED =
  constant SHOW_LOGIN_REQUIRED (line 9) | const SHOW_LOGIN_REQUIRED = 'cboard/subscription/SHOW_LOGIN_REQUIRED';
  constant HIDE_LOGIN_REQUIRED (line 10) | const HIDE_LOGIN_REQUIRED = 'cboard/subscription/HIDE_LOGIN_REQUIRED';
  constant NOT_SUBSCRIBED (line 12) | const NOT_SUBSCRIBED = 'not_subscribed';
  constant PROCCESING (line 13) | const PROCCESING = 'proccesing';
  constant ACTIVE (line 14) | const ACTIVE = 'active';
  constant CANCELED (line 15) | const CANCELED = 'canceled';
  constant CANCELLED (line 16) | const CANCELLED = 'cancelled';
  constant IN_GRACE_PERIOD (line 17) | const IN_GRACE_PERIOD = 'in_grace_period';
  constant PAUSED (line 18) | const PAUSED = 'paused';
  constant EXPIRED (line 19) | const EXPIRED = 'expired';
  constant ON_HOLD (line 20) | const ON_HOLD = 'on_hold';
  constant UNVERIFIED (line 21) | const UNVERIFIED = 'unverified';
  constant DAYS_TO_TRY (line 23) | const DAYS_TO_TRY = 15;
  constant REQUIRING_PREMIUM_COUNTRIES (line 25) | const REQUIRING_PREMIUM_COUNTRIES = [

FILE: src/providers/SubscriptionProvider/SubscriptionProvider.container.js
  class SubscriptionProvider (line 26) | class SubscriptionProvider extends Component {
    method componentDidMount (line 31) | async componentDidMount() {
    method render (line 221) | render() {

FILE: src/providers/SubscriptionProvider/SubscriptionProvider.reducer.js
  function subscriptionProviderReducer (line 49) | function subscriptionProviderReducer(state = initialState, action) {

FILE: src/providers/ThemeProvider/RTLSupport.js
  function RTLSupport (line 19) | function RTLSupport({ children }) {

FILE: src/providers/ThemeProvider/ThemeProvider.constants.js
  constant DEFAULT_THEME (line 1) | const DEFAULT_THEME = 'light';
  constant DARK_THEME (line 2) | const DARK_THEME = 'dark';
  constant FONTS_FAMILIES_LIST (line 4) | const FONTS_FAMILIES_LIST = [
  constant DEFAULT_FONT_FAMILY (line 36) | const DEFAULT_FONT_FAMILY = FONTS_FAMILIES_LIST[0].fontName;
  constant FONTS_FAMILIES (line 38) | const FONTS_FAMILIES = FONTS_FAMILIES_LIST.sort(

FILE: src/providers/ThemeProvider/ThemeProvider.container.js
  class ThemeProvider (line 19) | class ThemeProvider extends Component {
    method render (line 31) | render() {

FILE: src/reducers.js
  method getItem (line 38) | async getItem(key) {
  method setItem (line 99) | async setItem(key, value) {
  method removeItem (line 107) | async removeItem(key) {
  function createReducer (line 140) | function createReducer() {

FILE: src/registerServiceWorker.js
  function register (line 21) | function register(onNewContentAvailable, onContentCached) {
  function registerValidSW (line 46) | function registerValidSW(
  function checkValidServiceWorker (line 81) | function checkValidServiceWorker(swUrl) {
  function unregister (line 108) | function unregister() {

FILE: src/store.js
  function configureStore (line 9) | function configureStore(initialState = {}) {

FILE: src/types.ts
  type TileItem (line 1) | interface TileItem {

FILE: sw-precache-config.js
  function mapImagesToGlobs (line 3) | function mapImagesToGlobs(boards, globPrefix) {

FILE: tests/helpers/communication-utils.js
  function addWordToCommunicationBar (line 10) | async function addWordToCommunicationBar(page, word) {
  function clearCommunicationBar (line 18) | async function clearCommunicationBar(page) {
  function backspaceInCommunicationBar (line 26) | async function backspaceInCommunicationBar(page) {
  function verifyTextInCommunicationBar (line 35) | async function verifyTextInCommunicationBar(page, text) {
  function verifyTextNotInCommunicationBar (line 45) | async function verifyTextNotInCommunicationBar(page, text) {

FILE: tests/helpers/navigation-utils.js
  function navigateToRoot (line 9) | async function navigateToRoot(page) {
  function navigateToCategory (line 19) | async function navigateToCategory(page, categoryName) {
  function goBack (line 28) | async function goBack(page) {

FILE: tests/helpers/overlay-utils.js
  function dismissOverlays (line 9) | async function dismissOverlays(page) {
  function dismissNotifications (line 58) | async function dismissNotifications(page) {

FILE: tests/page-objects/cboard.js
  class Cboard (line 8) | class Cboard {
    method constructor (line 9) | constructor(page) {
    method getElevenLabsApiKey (line 20) | getElevenLabsApiKey() {
    method goto (line 26) | async goto(path = '/board/root') {
    method gotoLoginSignup (line 76) | async gotoLoginSignup() {
    method dismissOverlays (line 80) | async dismissOverlays() {
    method buttons (line 132) | get buttons() {
    method mainBoardHeading (line 152) | get mainBoardHeading() {
    method foodCategoryHeading (line 158) | get foodCategoryHeading() {
    method emotionsCategoryHeading (line 162) | get emotionsCategoryHeading() {
    method activitiesCategoryHeading (line 166) | get activitiesCategoryHeading() {
    method getCategoryHeading (line 170) | getCategoryHeading(categoryName) {
    method goBackButton (line 175) | get goBackButton() {
    method loginButton (line 179) | get loginButton() {
    method settingsButton (line 183) | get settingsButton() {
    method unlockButton (line 187) | get unlockButton() {
    method loginPageLoginButton (line 192) | get loginPageLoginButton() {
    method signUpPageButton (line 196) | get signUpPageButton() {
    method googleSignInButton (line 200) | get googleSignInButton() {
    method facebookSignInButton (line 204) | get facebookSignInButton() {
    method appleSignInButton (line 208) | get appleSignInButton() {
    method closeButton (line 212) | get closeButton() {
    method loginDialog (line 217) | get loginDialog() {
    method signUpDialog (line 221) | get signUpDialog() {
    method passwordResetDialog (line 225) | get passwordResetDialog() {
    method emailField (line 232) | get emailField() {
    method loginEmailField (line 236) | get loginEmailField() {
    method signUpEmailField (line 240) | get signUpEmailField() {
    method passwordResetEmailField (line 244) | get passwordResetEmailField() {
    method passwordField (line 248) | get passwordField() {
    method nameField (line 252) | get nameField() {
    method createPasswordField (line 256) | get createPasswordField() {
    method confirmPasswordField (line 260) | get confirmPasswordField() {
    method termsCheckbox (line 264) | get termsCheckbox() {
    method loginFormLoginButton (line 269) | get loginFormLoginButton() {
    method loginFormCancelButton (line 273) | get loginFormCancelButton() {
    method forgotPasswordButton (line 277) | get forgotPasswordButton() {
    method signUpFormSubmitButton (line 281) | get signUpFormSubmitButton() {
    method signUpFormCancelButton (line 285) | get signUpFormCancelButton() {
    method passwordResetSendButton (line 289) | get passwordResetSendButton() {
    method passwordResetCancelButton (line 293) | get passwordResetCancelButton() {
    method loginHeading (line 298) | get loginHeading() {
    method signUpHeading (line 304) | get signUpHeading() {
    method passwordResetHeading (line 310) | get passwordResetHeading() {
    method privacyPolicyLink (line 317) | get privacyPolicyLink() {
    method termsLink (line 321) | get termsLink() {
    method cboardLogo (line 325) | get cboardLogo() {
    method passwordResetInstructions (line 330) | get passwordResetInstructions() {
    method backspaceButton (line 337) | get backspaceButton() {
    method clearButton (line 341) | get clearButton() {
    method yesButton (line 345) | get yesButton() {
    method noButton (line 348) | get noButton() {
    method quickChatButton (line 351) | get quickChatButton() {
    method foodButton (line 358) | get foodButton() {
    method drinksButton (line 362) | get drinksButton() {
    method emotionsButton (line 366) | get emotionsButton() {
    method activitiesButton (line 370) | get activitiesButton() {
    method bodyButton (line 374) | get bodyButton() {
    method clothingButton (line 378) | get clothingButton() {
    method peopleButton (line 382) | get peopleButton() {
    method describeButton (line 386) | get describeButton() {
    method kitchenButton (line 390) | get kitchenButton() {
    method schoolButton (line 394) | get schoolButton() {
    method animalsButton (line 398) | get animalsButton() {
    method technologyButton (line 402) | get technologyButton() {
    method weatherButton (line 406) | get weatherButton() {
    method plantsButton (line 410) | get plantsButton() {
    method sportsButton (line 414) | get sportsButton() {
    method transportButton (line 418) | get transportButton() {
    method placesButton (line 422) | get placesButton() {
    method positionButton (line 426) | get positionButton() {
    method toysButton (line 430) | get toysButton() {
    method actionsButton (line 434) | get actionsButton() {
    method questionsButton (line 438) | get questionsButton() {
    method furnitureButton (line 442) | get furnitureButton() {
    method hygieneButton (line 446) | get hygieneButton() {
    method numbersButton (line 450) | get numbersButton() {
    method timeButton (line 454) | get timeButton() {
    method snacksButton (line 458) | get snacksButton() {
    method pizzaButton (line 463) | get pizzaButton() {
    method breadButton (line 467) | get breadButton() {
    method soupButton (line 471) | get soupButton() {
    method imHungryButton (line 475) | get imHungryButton() {
    method iWantButton (line 479) | get iWantButton() {
    method andButton (line 483) | get andButton() {
    method iDislikeButton (line 487) | get iDislikeButton() {
    method vegetablesButton (line 491) | get vegetablesButton() {
    method fruitButton (line 495) | get fruitButton() {
    method boiledEggButton (line 499) | get boiledEggButton() {
    method friedEggButton (line 503) | get friedEggButton() {
    method croissantButton (line 507) | get croissantButton() {
    method cerealButton (line 511) | get cerealButton() {
    method porridgeButton (line 515) | get porridgeButton() {
    method pancakesButton (line 519) | get pancakesButton() {
    method pastaButton (line 523) | get pastaButton() {
    method poultryButton (line 527) | get poultryButton() {
    method beefButton (line 531) | get beefButton() {
    method fishButton (line 535) | get fishButton() {
    method spaghettiBolonaiseButton (line 539) | get spaghettiBolonaiseButton() {
    method hamburgerButton (line 543) | get hamburgerButton() {
    method hotDogButton (line 547) | get hotDogButton() {
    method pieButton (line 551) | get pieButton() {
    method sandwichButton (line 555) | get sandwichButton() {
    method bagelButton (line 559) | get bagelButton() {
    method toastButton (line 563) | get toastButton() {
    method cheeseButton (line 567) | get cheeseButton() {
    method noodlesButton (line 571) | get noodlesButton() {
    method chipsButton (line 575) | get chipsButton() {
    method getTextInCommunicationBar (line 578) | getTextInCommunicationBar(text) {
    method lockedProfileAlert (line 584) | get lockedProfileAlert() {
    method unlockClicksAlert (line 590) | get unlockClicksAlert() {
    method getButtonByName (line 596) | getButtonByName(name, exact = false) {
    method getButtonByText (line 600) | getButtonByText(text) {
    method printBoardButton (line 605) | get printBoardButton() {
    method shareButton (line 609) | get shareButton() {
    method fullScreenButton (line 613) | get fullScreenButton() {
    method userHelpButton (line 617) | get userHelpButton() {
    method lockButton (line 621) | get lockButton() {
    method boardsTab (line 626) | get boardsTab() {
    method buildTab (line 630) | get buildTab() {
    method editBoardTilesButton (line 635) | get editBoardTilesButton() {
    method addTileButton (line 639) | get addTileButton() {
    method languageSettingsButton (line 644) | get languageSettingsButton() {
    method speechSettingsButton (line 648) | get speechSettingsButton() {
    method displaySettingsButton (line 652) | get displaySettingsButton() {
    method exportSettingsButton (line 656) | get exportSettingsButton() {
    method importSettingsButton (line 660) | get importSettingsButton() {
    method symbolsSettingsButton (line 664) | get symbolsSettingsButton() {
    method scanningSettingsButton (line 668) | get scanningSettingsButton() {
    method clickCommunicationButton (line 673) | async clickCommunicationButton(buttonText) {
    method navigateToCategory (line 678) | async navigateToCategory(categoryName) {
    method navigateBack (line 683) | async navigateBack() {
    method clearCommunicationBar (line 688) | async clearCommunicationBar() {
    method backspaceInCommunicationBar (line 692) | async backspaceInCommunicationBar() {
    method clickUnlock (line 696) | async clickUnlock() {
    method clickLogin (line 700) | async clickLogin() {
    method clickSettings (line 704) | async clickSettings() {
    method waitForNavigation (line 708) | async waitForNavigation() {
    method goBackToMainBoard (line 713) | async goBackToMainBoard() {
    method expectRootBoardUrl (line 723) | expectRootBoardUrl() {
    method expectFoodBoardUrl (line 727) | expectFoodBoardUrl() {
    method expectCorrectTitle (line 732) | expectCorrectTitle() {
    method expectWordInCommunicationBar (line 737) | async expectWordInCommunicationBar(word) {
    method expectWordNotInCommunicationBar (line 757) | async expectWordNotInCommunicationBar(word) {
    method expectButtonVisible (line 810) | async expectButtonVisible(buttonGetter) {
    method expectButtonDisabled (line 814) | async expectButtonDisabled(buttonGetter) {
    method expectButtonEnabled (line 818) | async expectButtonEnabled(buttonGetter) {
    method expectButtonNotVisible (line 821) | async expectButtonNotVisible(buttonGetter) {
    method openLoginDialog (line 826) | async openLoginDialog() {
    method openSignUpDialog (line 831) | async openSignUpDialog() {
    method openPasswordResetDialog (line 836) | async openPasswordResetDialog() {
    method closeLoginDialog (line 842) | async closeLoginDialog() {
    method closeSignUpDialog (line 847) | async closeSignUpDialog() {
    method closePasswordResetDialog (line 852) | async closePasswordResetDialog() {
    method fillLoginForm (line 857) | async fillLoginForm(email, password) {
    method submitLoginForm (line 862) | async submitLoginForm() {
    method attemptLogin (line 866) | async attemptLogin(email, password) {
    method fillSignUpForm (line 872) | async fillSignUpForm(
    method submitSignUpForm (line 889) | async submitSignUpForm() {
    method attemptSignUp (line 893) | async attemptSignUp(
    method requestPasswordReset (line 911) | async requestPasswordReset(email) {
    method togglePasswordVisibility (line 917) | async togglePasswordVisibility() {
    method clickSocialLogin (line 925) | async clickSocialLogin(provider) {
    method expectLoginFormVisible (line 941) | async expectLoginFormVisible() {
    method expectSignUpFormVisible (line 951) | async expectSignUpFormVisible() {
    method expectPasswordResetFormVisible (line 963) | async expectPasswordResetFormVisible() {
    method expectSocialLoginButtonsVisible (line 972) | async expectSocialLoginButtonsVisible() {
    method expectAuthenticationPageElements (line 978) | async expectAuthenticationPageElements() {
    method expectPasswordFieldType (line 987) | async expectPasswordFieldType(fieldType, expectedType = 'password') {
    method expectEmailValidation (line 997) | async expectEmailValidation() {
    method expectRequiredFields (line 1012) | async expectRequiredFields() {
    method expectTermsRequired (line 1032) | async expectTermsRequired() {
    method expectPrivacyPolicyLink (line 1041) | async expectPrivacyPolicyLink() {
    method expectTermsLink (line 1049) | async expectTermsLink() {
    method verifyHomeHeadingVisible (line 1058) | async verifyHomeHeadingVisible() {
    method verifyButtonVisible (line 1061) | async verifyButtonVisible(buttonName) {
    method verifyCategoryHeadingVisible (line 1077) | async verifyCategoryHeadingVisible(categoryName) {
    method verifyCommunicationBarHasText (line 1082) | async verifyCommunicationBarHasText(text) {
    method verifyCommunicationBarEmpty (line 1086) | async verifyCommunicationBarEmpty() {
    method verifyPageTitle (line 1091) | async verifyPageTitle() {
    method clickButton (line 1094) | async clickButton(buttonName) {
    method clickGoBackButton (line 1115) | async clickGoBackButton() {
    method clickClearButton (line 1119) | async clickClearButton() {
    method clickUnlockButton (line 1123) | async clickUnlockButton() {
    method clickLock (line 1137) | async clickLock() {
    method verifyUnlockMessageVisible (line 1141) | async verifyUnlockMessageVisible() {
    method expectCommunicationBarEmpty (line 1146) | async expectCommunicationBarEmpty() {
    method settingsContainer (line 1189) | get settingsContainer() {
    method settingsHeading (line 1198) | get settingsHeading() {
    method languageTab (line 1203) | get languageTab() {
    method speechTab (line 1207) | get speechTab() {
    method displayTab (line 1211) | get displayTab() {
    method exportTab (line 1215) | get exportTab() {
    method importTab (line 1219) | get importTab() {
    method symbolsTab (line 1223) | get symbolsTab() {
    method scanningTab (line 1227) | get scanningTab() {
    method navigationAndButtonsTab (line 1231) | get navigationAndButtonsTab() {
    method languagePanel (line 1235) | get languagePanel() {
    method languageHeading (line 1241) | get languageHeading() {
    method languageSaveButton (line 1245) | get languageSaveButton() {
    method languageGoBackButton (line 1249) | get languageGoBackButton() {
    method languageDropdown (line 1253) | get languageDropdown() {
    method englishUSButton (line 1260) | get englishUSButton() {
    method spanishButton (line 1264) | get spanishButton() {
    method frenchButton (line 1268) | get frenchButton() {
    method germanButton (line 1272) | get germanButton() {
    method moreLanguagesButton (line 1276) | get moreLanguagesButton() {
    method selectedLanguageCheckmark (line 1279) | get selectedLanguageCheckmark() {
    method speechPanel (line 1286) | get speechPanel() {
    method speechHeading (line 1292) | get speechHeading() {
    method speechSaveButton (line 1296) | get speechSaveButton() {
    method speechGoBackButton (line 1300) | get speechGoBackButton() {
    method voiceButton (line 1304) | get voiceButton() {
    method voiceDropdown (line 1308) | get voiceDropdown() {
    method currentVoiceDisplay (line 1314) | get currentVoiceDisplay() {
    method pitchSlider (line 1318) | get pitchSlider() {
    method rateSlider (line 1322) | get rateSlider() {
    method speechRateSlider (line 1326) | get speechRateSlider() {
    method speechPitchSlider (line 1330) | get speechPitchSlider() {
    method speechVolumeSlider (line 1334) | get speechVolumeSlider() {
    method testSpeechButton (line 1338) | get testSpeechButton() {
    method displayPanel (line 1343) | get displayPanel() {
    method displayHeading (line 1349) | get displayHeading() {
    method saveButton (line 1353) | get saveButton() {
    method displayGoBackButton (line 1357) | get displayGoBackButton() {
    method uiSizeDropdown (line 1362) | get uiSizeDropdown() {
    method fontFamilyDropdown (line 1367) | get fontFamilyDropdown() {
    method fontSizeDropdown (line 1375) | get fontSizeDropdown() {
    method labelPositionDropdown (line 1380) | get labelPositionDropdown() {
    method hideOutputBarCheckbox (line 1385) | get hideOutputBarCheckbox() {
    method actionButtonsCheckbox (line 1389) | get actionButtonsCheckbox() {
    method darkThemeCheckbox (line 1393) | get darkThemeCheckbox() {
    method fontSizeSlider (line 1397) | get fontSizeSlider() {
    method boardSizeDropdown (line 1403) | get boardSizeDropdown() {
    method darkModeSwitch (line 1409) | get darkModeSwitch() {
    method exportPanel (line 1413) | get exportPanel() {
    method exportHeading (line 1419) | get exportHeading() {
    method exportGoBackButton (line 1423) | get exportGoBackButton() {
    method boardsDropdown (line 1428) | get boardsDropdown() {
    method singleExportFormatDropdown (line 1432) | get singleExportFormatDropdown() {
    method singleExportActionButton (line 1436) | get singleExportActionButton() {
    method allBoardsExportFormatDropdown (line 1445) | get allBoardsExportFormatDropdown() {
    method allBoardsExportActionButton (line 1449) | get allBoardsExportActionButton() {
    method pdfFontSizeDropdown (line 1458) | get pdfFontSizeDropdown() {
    method pdfFontSizeInput (line 1462) | get pdfFontSizeInput() {
    method cboardFormatLinks (line 1467) | get cboardFormatLinks() {
    method openboardFormatLinks (line 1471) | get openboardFormatLinks() {
    method exportPdfButton (line 1475) | get exportPdfButton() {
    method exportCboardButton (line 1479) | get exportCboardButton() {
    method exportOpenBoardButton (line 1482) | get exportOpenBoardButton() {
    method exportCboardFormatLink (line 1485) | get exportCboardFormatLink() {
    method exportOpenboardFormatLink (line 1488) | get exportOpenboardFormatLink() {
    method importPanel (line 1492) | get importPanel() {
    method importHeading (line 1498) | get importHeading() {
    method importGoBackButton (line 1502) | get importGoBackButton() {
    method importButton (line 1506) | get importButton() {
    method importCboardButton (line 1510) | get importCboardButton() {
    method importOpenBoardButton (line 1514) | get importOpenBoardButton() {
    method fileInput (line 1518) | get fileInput() {
    method importCboardFormatLink (line 1523) | get importCboardFormatLink() {
    method importOpenboardFormatLink (line 1527) | get importOpenboardFormatLink() {
    method symbolsPanel (line 1531) | get symbolsPanel() {
    method symbolsHeading (line 1537) | get symbolsHeading() {
    method symbolsGoBackButton (line 1541) | get symbolsGoBackButton() {
    method downloadArasaacTitle (line 1545) | get downloadArasaacTitle() {
    method downloadArasaacDescription (line 1549) | get downloadArasaacDescription() {
    method downloadButton (line 1554) | get downloadButton() {
    method downloadArasaacButton (line 1558) | get downloadArasaacButton() {
    method offlineBenefitsText (line 1562) | get offlineBenefitsText() {
    method symbolSetDropdown (line 1568) | get symbolSetDropdown() {
    method symbolSizeSlider (line 1574) | get symbolSizeSlider() {
    method scanningPanel (line 1580) | get scanningPanel() {
    method scanningHeading (line 1586) | get scanningHeading() {
    method scanningSaveButton (line 1590) | get scanningSaveButton() {
    method scanningGoBackButton (line 1594) | get scanningGoBackButton() {
    method enableScanningCheckbox (line 1598) | get enableScanningCheckbox() {
    method timeDelayDropdown (line 1602) | get timeDelayDropdown() {
    method timeDelayInput (line 1606) | get timeDelayInput() {
    method scanMethodDropdown (line 1610) | get scanMethodDropdown() {
    method scanMethodInput (line 1614) | get scanMethodInput() {
    method scanningEnabledSwitch (line 1618) | get scanningEnabledSwitch() {
    method scanDelaySlider (line 1624) | get scanDelaySlider() {
    method scanKeyDropdown (line 1630) | get scanKeyDropdown() {
    method navigationAndButtonsPanel (line 1635) | get navigationAndButtonsPanel() {
    method removeOutputWordsSwitch (line 1641) | get removeOutputWordsSwitch() {
    method shareShowLabelsSwitch (line 1647) | get shareShowLabelsSwitch() {
    method backButtonSwitch (line 1653) | get backButtonSwitch() {
    method navigateToSettings (line 1660) | async navigateToSettings() {
    method dismissTourPopup (line 1698) | async dismissTourPopup() {
    method waitForSettingsToAppear (line 1727) | async waitForSettingsToAppear() {
    method clickSettingsTab (line 1760) | async clickSettingsTab(tabName) {
    method verifySettingsVisible (line 1780) | async verifySettingsVisible() {
    method verifySettingsTabVisible (line 1796) | async verifySettingsTabVisible(tabName) {
    method verifySettingsPanelVisible (line 1816) | async verifySettingsPanelVisible(panelName) {
    method verifyLanguageSettingsElements (line 1836) | async verifyLanguageSettingsElements() {
    method verifyScanningSettingsUI (line 1844) | async verifyScanningSettingsUI() {
    method verifyEnableScanningToggle (line 1850) | async verifyEnableScanningToggle() {
    method verifyTimeDelaySettings (line 1858) | async verifyTimeDelaySettings() {
    method verifyScanMethodSettings (line 1867) | async verifyScanMethodSettings() {
    method verifyScanningUsageInstructions (line 1876) | async verifyScanningUsageInstructions() {
    method toggleScanningEnabled (line 1887) | async toggleScanningEnabled() {
    method clickTimeDelayDropdown (line 1895) | async clickTimeDelayDropdown() {
    method clickScanMethodDropdown (line 1899) | async clickScanMethodDropdown() {
    method saveScanningSettings (line 1903) | async saveScanningSettings() {
    method toggleScanningAndSave (line 1907) | async toggleScanningAndSave() {
    method goBackFromScanning (line 1912) | async goBackFromScanning() {
    method verifyClearAccessibilityInstructions (line 1922) | async verifyClearAccessibilityInstructions() {
    method verifyReasonableDefaultTiming (line 1934) | async verifyReasonableDefaultTiming() {
    method verifyCurrentScanningState (line 1939) | async verifyCurrentScanningState() {
    method verifyScanningAccessibilityPurpose (line 1945) | async verifyScanningAccessibilityPurpose() {
    method verifyScanningSettingsElements (line 1957) | async verifyScanningSettingsElements() {
    method verifySpeechSettingsUI (line 1965) | async verifySpeechSettingsUI() {
    method verifyVoiceSelection (line 1970) | async verifyVoiceSelection() {
    method verifyPitchControl (line 1974) | async verifyPitchControl() {
    method verifyRateControl (line 1982) | async verifyRateControl() {
    method clickVoiceSelection (line 1990) | async clickVoiceSelection() {
    method adjustPitchSlider (line 1993) | async adjustPitchSlider(value) {
    method adjustRateSlider (line 2021) | async adjustRateSlider(value) {
    method goBackFromSpeech (line 2050) | async goBackFromSpeech() {
    method verifyVoiceDescription (line 2060) | async verifyVoiceDescription() {
    method verifyHelpfulTooltips (line 2064) | async verifyHelpfulTooltips() {
    method verifySpeechSettingsElements (line 2073) | async verifySpeechSettingsElements() {
    method verifyElevenLabsApiKeyField (line 2083) | async verifyElevenLabsApiKeyField() {
    method setElevenLabsApiKey (line 2091) | async setElevenLabsApiKey() {
    method testElevenLabsApiKeyValidation (line 2097) | async testElevenLabsApiKeyValidation() {
    method verifyElevenLabsConnectionStatus (line 2121) | async verifyElevenLabsConnectionStatus() {
    method verifyVoiceMenuOpening (line 2168) | async verifyVoiceMenuOpening() {
    method verifyVoiceTypeVariety (line 2176) | async verifyVoiceTypeVariety() {
    method testLocalVoiceSelection (line 2195) | async testLocalVoiceSelection() {
    method testCloudVoiceSelection (line 2207) | async testCloudVoiceSelection() {
    method testOnlineVoiceControlsDisabled (line 2222) | async testOnlineVoiceControlsDisabled() {
    method testLocalVoiceControlsEnabledAndFunctional (line 2240) | async testLocalVoiceControlsEnabledAndFunctional() {
    method testPitchSliderFunctionality (line 2261) | async testPitchSliderFunctionality() {
    method testRateSliderFunctionality (line 2284) | async testRateSliderFunctionality() {
    method testControlStateChangesWithVoiceTypes (line 2305) | async testControlStateChangesWithVoiceTypes() {
    method verifyOnlineVoiceNotification (line 2348) | async verifyOnlineVoiceNotification() {
    method testLocalVoiceControlsEnabled (line 2365) | async testLocalVoiceControlsEnabled() {
    method verifyElevenLabsHelpText (line 2370) | async verifyElevenLabsHelpText() {
    method verifyApiKeyPasswordToggle (line 2380) | async verifyApiKeyPasswordToggle() {
    method verifyVoiceChips (line 2394) | async verifyVoiceChips() {
    method testVoiceSelectionPersistence (line 2404) | async testVoiceSelectionPersistence() {
    method testPitchSliderRange (line 2422) | async testPitchSliderRange() {
    method testRateSliderRange (line 2439) | async testRateSliderRange() {
    method testElevenLabsRateRange (line 2455) | async testElevenLabsRateRange() {
    method testElevenLabsVoicesAddedAfterApiKey (line 2498) | async testElevenLabsVoicesAddedAfterApiKey() {
    method verifyVoiceLabelsAndDescriptions (line 2567) | async verifyVoiceLabelsAndDescriptions() {
    method testVoiceMenuKeyboardNavigation (line 2586) | async testVoiceMenuKeyboardNavigation() {
    method testVoiceMenuCloseOnOutsideClick (line 2601) | async testVoiceMenuCloseOnOutsideClick() {
    method verifyRegionalAccentVoices (line 2622) | async verifyRegionalAccentVoices() {
    method verifyMultilingualVoices (line 2642) | async verifyMultilingualVoices() {
    method testVoiceLoadingStates (line 2657) | async testVoiceLoadingStates() {
    method testSliderIncrementSteps (line 2669) | async testSliderIncrementSteps() {
    method testVoiceSelectionErrorHandling (line 2686) | async testVoiceSelectionErrorHandling() {
    method verifyDisplaySettingsUI (line 2705) | async verifyDisplaySettingsUI() {
    method verifyUISettings (line 2710) | async verifyUISettings() {
    method verifyFontFamilySettings (line 2715) | async verifyFontFamilySettings() {
    method verifyFontSizeSettings (line 2719) | async verifyFontSizeSettings() {
    method verifyOutputBarSettings (line 2723) | async verifyOutputBarSettings() {
    method verifyActionButtonsSettings (line 2727) | async verifyActionButtonsSettings() {
    method verifyLabelPositionSettings (line 2732) | async verifyLabelPositionSettings() {
    method verifyDarkThemeSettings (line 2737) | async verifyDarkThemeSettings() {
    method clickUISize (line 2742) | async clickUISize() {
    method clickFontFamily (line 2746) | async clickFontFamily() {
    method clickFontSize (line 2750) | async clickFontSize() {
    method clickLabelPosition (line 2754) | async clickLabelPosition() {
    method toggleCheckbox (line 2758) | async toggleCheckbox(checkbox) {
    method saveDisplaySettings (line 2765) | async saveDisplaySettings() {
    method goBackFromDisplay (line 2769) | async goBackFromDisplay() {
    method verifyDisplaySettingsElements (line 2779) | async verifyDisplaySettingsElements() {
    method verifyExportSettingsUI (line 2787) | async verifyExportSettingsUI() {
    method verifySingleBoardExportSection (line 2791) | async verifySingleBoardExportSection() {
    method verifySingleBoardExportControls (line 2796) | async verifySingleBoardExportControls() {
    method verifyAllBoardsExportSection (line 2803) | async verifyAllBoardsExportSection() {
    method verifyAllBoardsExportControls (line 2808) | async verifyAllBoardsExportControls() {
    method selectSingleBoard (line 2814) | async selectSingleBoard(boardName) {
    method selectSingleBoardFormat (line 2819) | async selectSingleBoardFormat(format) {
    method verifySingleExportButtonEnabled (line 2824) | async verifySingleExportButtonEnabled() {
    method verifySingleExportButtonDisabled (line 2828) | async verifySingleExportButtonDisabled() {
    method selectAllBoardsFormat (line 2832) | async selectAllBoardsFormat(format) {
    method verifyAllBoardsExportButtonEnabled (line 2837) | async verifyAllBoardsExportButtonEnabled() {
    method verifyAllBoardsExportButtonDisabled (line 2841) | async verifyAllBoardsExportButtonDisabled() {
    method verifyPdfSettingsSection (line 2845) | async verifyPdfSettingsSection() {
    method verifyPdfFontSizeControl (line 2849) | async verifyPdfFontSizeControl() {
    method clickBoardsDropdown (line 2854) | async clickBoardsDropdown() {
    method clickSingleExportFormat (line 2858) | async clickSingleExportFormat() {
    method clickAllBoardsExportFormat (line 2862) | async clickAllBoardsExportFormat() {
    method clickPdfFontSize (line 2866) | async clickPdfFontSize() {
    method verifyFormatDocumentationLinks (line 2870) | async verifyFormatDocumentationLinks() {
    method verifyExportBehaviorExplanations (line 2874) | async verifyExportBehaviorExplanations() {
    method goBackFromExport (line 2879) | async goBackFromExport() {
    method verifyPdfSettingsOrganization (line 2889) | async verifyPdfSettingsOrganization() {
    method verifyExportSettingsElements (line 2894) | async verifyExportSettingsElements() {
    method verifyImportSettingsUI (line 2905) | async verifyImportSettingsUI() {
    method verifyImportDescription (line 2909) | async verifyImportDescription() {
    method verifySupportedFormats (line 2913) | async verifySupportedFormats() {
    method verifyImportFormatDocumentationLinks (line 2920) | async verifyImportFormatDocumentationLinks() {
    method verifyImportButton (line 2924) | async verifyImportButton() {
    method clickImportButton (line 2928) | async clickImportButton() {
    method verifySelectiveImportBehavior (line 2932) | async verifySelectiveImportBehavior() {
    method verifyBothAAcFormatsSupported (line 2936) | async verifyBothAAcFormatsSupported() {
    method goBackFromImport (line 2941) | async goBackFromImport() {
    method verifySimpleInterfaceDesign (line 2950) | async verifySimpleInterfaceDesign() {
    method verifySmartImportBehavior (line 2955) | async verifySmartImportBehavior() {
    method verifyImportSettingsElements (line 2959) | async verifyImportSettingsElements() {
    method verifyLanguageSettingsUI (line 2967) | async verifyLanguageSettingsUI() {
    method verifyCurrentlySelectedLanguage (line 2971) | async verifyCurrentlySelectedLanguage() {
    method verifyComprehensiveLanguageList (line 2977) | async verifyComprehensiveLanguageList() {
    method verifyOnlineLanguageRequirement (line 2983) | async verifyOnlineLanguageRequirement() {
    method verifyMoreLanguagesOption (line 2988) | async verifyMoreLanguagesOption() {
    method selectSpanishAndSave (line 2992) | async selectSpanishAndSave() {
    method goBackFromLanguage (line 2997) | async goBackFromLanguage() {
    method verifyLanguageVariants (line 3007) | async verifyLanguageVariants() {
    method verifySymbolsSettingsUI (line 3014) | async verifySymbolsSettingsUI() {
    method verifyArasaacDownloadSection (line 3018) | async verifyArasaacDownloadSection() {
    method verifyDownloadButton (line 3023) | async verifyDownloadButton() {
    method verifyOfflineBenefitsExplanation (line 3026) | async verifyOfflineBenefitsExplanation() {
    method clickDownloadArasaac (line 3031) | async clickDownloadArasaac() {
    method verifyOfflineCapabilityEmphasis (line 3034) | async verifyOfflineCapabilityEmphasis() {
    method verifySymbolUsageContext (line 3038) | async verifySymbolUsageContext() {
    method verifyCompleteSymbolPackage (line 3042) | async verifyCompleteSymbolPackage() {
    method goBackFromSymbols (line 3047) | async goBackFromSymbols() {
    method verifySimpleFocusedInterface (line 3056) | async verifySimpleFocusedInterface() {
    method verifyClearValueProposition (line 3060) | async verifyClearValueProposition() {
    method verifyArasaacProperReference (line 3064) | async verifyArasaacProperReference() {
    method verifySymbolsSettingsElements (line 3067) | async verifySymbolsSettingsElements() {
    method safeClick (line 3074) | async safeClick(locator, options = {}) {
    method waitForTimeout (line 3111) | async waitForTimeout(ms = 1000) {
    method verifyFontFamilyOptions (line 3116) | async verifyFontFamilyOptions(expectedOptions) {
    method selectFontFamilyOption (line 3125) | async selectFontFamilyOption(optionName) {
    method verifyFontFamilySelected (line 3132) | async verifyFontFamilySelected(expectedFont) {
    method verifyFontFamilyChanged (line 3139) | async verifyFontFamilyChanged() {
    method verifyUISizeOptions (line 3150) | async verifyUISizeOptions(expectedOptions) {
    method selectUISizeOption (line 3159) | async selectUISizeOption(optionName) {
    method verifyUISizeSelected (line 3166) | async verifyUISizeSelected(expectedSize) {
    method verifyUIChanged (line 3173) | async verifyUIChanged() {
    method verifyFontSizeOptions (line 3184) | async verifyFontSizeOptions(expectedOptions) {
    method selectFontSizeOption (line 3193) | async selectFontSizeOption(optionName) {
    method verifyFontSizeSelected (line 3200) | async verifyFontSizeSelected(expectedSize) {
    method verifyFontSizeChanged (line 3207) | async verifyFontSizeChanged() {
    method getOutputBarElement (line 3218) | async getOutputBarElement() {
    method verifyOutputBarVisible (line 3254) | async verifyOutputBarVisible() {
    method verifyOutputBarHidden (line 3273) | async verifyOutputBarHidden() {
    method getOutputBarVisibilityState (line 3291) | async getOutputBarVisibilityState() {
    method verifyOutputBarToggled (line 3310) | async verifyOutputBarToggled(expectedVisibility) {
    method toggleOutputBarVisibility (line 3319) | async toggleOutputBarVisibility() {
    method verifyOutputBarVisibilityChanged (line 3329) | async verifyOutputBarVisibilityChanged() {
    method getActionButtonsElements (line 3342) | async getActionButtonsElements() {
    method verifyActionButtonsVisible (line 3355) | async verifyActionButtonsVisible() {
    method getActionButtonsSizeState (line 3375) | async getActionButtonsSizeState() {
    method toggleActionButtonsSize (line 3403) | async toggleActionButtonsSize() {
    method verifyActionButtonsSizeChanged (line 3412) | async verifyActionButtonsSizeChanged() {
    method verifyActionButtonsSizeToggled (line 3430) | async verifyActionButtonsSizeToggled(expectedLargerSize) {
    method openCommunicatorDialog (line 3487) | async openCommunicatorDialog() {
    method navigateToPublicBoardsTab (line 3503) | async navigateToPublicBoardsTab() {
    method getPublicBoardItems (line 3512) | async getPublicBoardItems() {
    method getReportButton (line 3516) | async getReportButton(boardItem) {
    method expectReportButtonDisabled (line 3521) | async expectReportButtonDisabled() {
    method expectReportDialogOpen (line 3527) | async expectReportDialogOpen() {
    method expectNoReportDialogOpen (line 3534) | async expectNoReportDialogOpen() {
  function createCboard (line 3543) | function createCboard(page) {

FILE: tests/utilities/assertions.js
  function verifyButtonVisible (line 12) | async function verifyButtonVisible(page, buttonName) {
  function verifyButtonDisabled (line 23) | async function verifyButtonDisabled(page, buttonName) {
  function verifyPageTitle (line 33) | async function verifyPageTitle(
  function verifyHeadingVisible (line 45) | async function verifyHeadingVisible(page, headingText) {
  function verifyCSSProperty (line 56) | async function verifyCSSProperty(element, property, value) {

FILE: tests/utilities/test-setup.js
  function waitForPageReady (line 11) | async function waitForPageReady(page) {
  function setupTestPage (line 23) | async function setupTestPage(page, path = '/board/root') {
  function setMobileViewport (line 33) | async function setMobileViewport(
  function setTabletViewport (line 45) | async function setTabletViewport(

FILE: tests/utilities/test-utils.js
  function dismissOverlays (line 9) | async function dismissOverlays(page) {
  function waitForPageReady (line 29) | async function waitForPageReady(page) {
  function clickCommunicationButton (line 41) | async function clickCommunicationButton(page, buttonText) {
  function navigateToRoot (line 53) | async function navigateToRoot(page) {
Copy disabled (too large) Download .json
Condensed preview — 836 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (25,461K chars).
[
  {
    "path": ".circleci/config.yml",
    "chars": 5716,
    "preview": "# Javascript Node CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-javascript/ for more "
  },
  {
    "path": ".dockerignore",
    "chars": 29,
    "preview": ".git\n.idea\nnode_modules\nbuild"
  },
  {
    "path": ".editorconfig",
    "chars": 207,
    "preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
  },
  {
    "path": ".eslintrc",
    "chars": 28,
    "preview": "{\n  \"extends\": \"react-app\"\n}"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 71,
    "preview": "# These are supported funding model platforms\n\nopen_collective: cboard\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 108,
    "preview": "<!-- Love cboard? Please consider supporting our collective:\n👉  https://opencollective.com/cboard/donate -->"
  },
  {
    "path": ".github/copilot-instructions.md",
    "chars": 449,
    "preview": "# Copilot Instructions for Cboard\n\n## Project Overview\nCboard is an augmentative and alternative communication (AAC) web"
  },
  {
    "path": ".github/prompts/tests.prompt.md",
    "chars": 1120,
    "preview": "---\nmode: 'agent'\ndescription: 'testing the Cboard web app'\n---\nYou are a playwright test generator. Ensure the Cboard w"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2331,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".gitignore",
    "chars": 640,
    "preview": "# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# Secrets\n.private/\n\n# dependencies\n/node_mo"
  },
  {
    "path": ".grenrc.json",
    "chars": 178,
    "preview": "{\n  \"dataSource\": \"milestones\",\n  \"groupBy\": {\n    \"New Features\": [\"enhancement\", \"feature\", \"internal\"],\n    \"Bug Fixe"
  },
  {
    "path": ".nvmrc",
    "chars": 9,
    "preview": "v22.14.0\n"
  },
  {
    "path": ".prettierrc",
    "chars": 46,
    "preview": "{\n  \"printWidth\": 80,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 286,
    "preview": "{\n  // See http://go.microsoft.com/fwlink/?LinkId=827846\n  // for the documentation about the extensions.json format\n  \""
  },
  {
    "path": ".vscode/launch.json",
    "chars": 534,
    "preview": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Laun"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 511,
    "preview": "{\n  \"editor.formatOnSave\": true,\n  \"chat.tools.terminal.autoApprove\": {\n    \"/^yarn test --watchAll=false --forceExit --"
  },
  {
    "path": "CBOARD_SYMBOLS_INTEGRATION.md",
    "chars": 10990,
    "preview": "# Cboard Symbols Integration Guide\n\nThis document explains how to set up and use the Cboard Symbols integration in the m"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 18275,
    "preview": "# Changelog\n\n## 1.7.1 (23/12/2020)\n\n#### Bug Fixes:\n\n- [**bug**] Shared boards have some titles of symbols translated ba"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3214,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 5538,
    "preview": "# Contributing to Cboard\n\nInterested in contributing to Cboard? Thanks! There are plenty of ways you can help.\n\nPlease t"
  },
  {
    "path": "Dockerfile",
    "chars": 388,
    "preview": "# Stage 1 - the build process\nFROM node:22.14.0 as build-deps\nWORKDIR /usr/src/app\nCOPY package.json yarn.lock ./\nRUN ya"
  },
  {
    "path": "LICENSE.txt",
    "chars": 35141,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "Makefile",
    "chars": 91,
    "preview": "image:\n\tdocker build -t cboard/cboard .\n\nrun:\n\tdocker run -p 5000:3000 cboard/cboard:latest"
  },
  {
    "path": "README.md",
    "chars": 10245,
    "preview": "[![DPG Badge](https://img.shields.io/badge/Verified-DPG-3333AB?logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iMzEiIGhl"
  },
  {
    "path": "__mocks__/browser-image-resizer.js",
    "chars": 282,
    "preview": "export default function readAndCompressImage(file, userConfig) {\n  return new Promise((resolve, reject) => {\n    if (fil"
  },
  {
    "path": "__mocks__/react-redux.js",
    "chars": 559,
    "preview": "// This mock will make sure that we are able to access mapStateToProps, mapDispatchToProps and reactComponent in the tes"
  },
  {
    "path": "__mocks__/undici.js",
    "chars": 1645,
    "preview": "/**\n * Minimal undici mock for Jest tests.\n * Prevents undici from initializing real HTTP machinery (MessagePort,\n * Rea"
  },
  {
    "path": "browserstack.yml",
    "chars": 823,
    "preview": "# This file is used to configure BrowserStack for running Playwright tests.\n# For more information, see https://www.brow"
  },
  {
    "path": "cboard.njsproj.user",
    "chars": 256,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/m"
  },
  {
    "path": "craco.config.js",
    "chars": 1086,
    "preview": "const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');\n\nmodule.exports = {\n  webpack: {\n    entry: './src/i"
  },
  {
    "path": "env/local.js",
    "chars": 186,
    "preview": "/* \n * LOCAL CONFIG\n * This is intended for devs while working locally.\n */\n\nconst APP_URL = 'http://localhost:3000';\n\nm"
  },
  {
    "path": "env/prod-private.gpg",
    "chars": 217,
    "preview": "\r\u0004\u0003\u0003\u0002e뮨\u001948{\u001d2\u0011}\nvn\t'\u0018\u0013F\"QqޛVAҔH%<q1L\u0016u|Ia|\u0003\tta>\u0012\u001fN=pv\u0010\u0011ᙂXK3*g7T~\u0010Nmmȑyg\u0005 |;lbjLg#QF\t}vڐ\u0001XWF9\u0018ZݠEd=|\u001edB=d۠c\b]5K \u0007\rgf30s\r܆"
  },
  {
    "path": "env/prod.js",
    "chars": 194,
    "preview": "/* \n * PROD CONFIG\n * This is intended for deployment to a hosting service.\n */\n\nconst APP_URL = 'https://cboard.somehos"
  },
  {
    "path": "funding.json",
    "chars": 4208,
    "preview": "{\n  \"$schema\": \"https://fundingjson.org/schema/v1.1.0.json\",\n  \"version\": \"v1.1.0\",\n  \"entity\": {\n    \"type\": \"organisat"
  },
  {
    "path": "package.json",
    "chars": 6676,
    "preview": "{\n  \"name\": \"cboard\",\n  \"version\": \"0.1.1\",\n  \"description\": \"Cboard is an augmentative and alternative communication (A"
  },
  {
    "path": "playwright.config.ts",
    "chars": 2801,
    "preview": "import { defineConfig, devices } from '@playwright/test';\n\n/**\n * Playwright Configuration for Cboard E2E Tests\n * \n * C"
  },
  {
    "path": "public/.well-known/assetlinks.json",
    "chars": 325,
    "preview": "[\n  {\n    \"relation\": [\"delegate_permission/common.handle_all_urls\"],\n    \"target\": {\n      \"namespace\": \"android_app\",\n"
  },
  {
    "path": "public/_redirects",
    "chars": 23,
    "preview": "/*    /index.html   200"
  },
  {
    "path": "public/index.html",
    "chars": 2785,
    "preview": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, i"
  },
  {
    "path": "public/manifest.json",
    "chars": 14126,
    "preview": "{\n  \"short_name\": \"Cboard\",\n  \"name\": \"Cboard - AAC Communication Board\",\n  \"description\":\n    \"Cboard is an augmentativ"
  },
  {
    "path": "public/ogv/ogv-decoder-audio-opus-wasm.js",
    "chars": 14922,
    "preview": "var OGVDecoderAudioOpusW = (() => {\n  var _scriptDir =\n    typeof document !== 'undefined' && document.currentScript\n   "
  },
  {
    "path": "public/ogv/ogv-demuxer-ogg-wasm.js",
    "chars": 16275,
    "preview": "var OGVDemuxerOggW = (() => {\n  var _scriptDir =\n    typeof document !== 'undefined' && document.currentScript\n      ? d"
  },
  {
    "path": "public/ogv/ogv-worker-audio.js",
    "chars": 20012,
    "preview": "(() => {\n  var e = {\n      506: e => {\n        (e.exports = function _assertThisInitialized(e) {\n          if (void 0 =="
  },
  {
    "path": "roadmap.md",
    "chars": 1752,
    "preview": "# Cboard Project Roadmap\n\nThis document outlines the development plan from a high level and will be updated as developme"
  },
  {
    "path": "rootfs/etc/nginx/conf.d/default.conf",
    "chars": 533,
    "preview": "access_log  /proc/self/fd/1;\nerror_log /proc/self/fd/2 info;\n\nserver {\n    listen       80 default_server;\n    server_na"
  },
  {
    "path": "rootfs/etc/nginx/conf.d/gzip.conf",
    "chars": 489,
    "preview": "gzip on;\ngzip_disable \"msie6\";\n\ngzip_vary on;\ngzip_proxied expired no-cache no-store private auth;\n\n#compression level\ng"
  },
  {
    "path": "scripts/arasaac-create-files.js",
    "chars": 1667,
    "preview": "const { writeFile } = require('fs');\nconst https = require('https');\n\nconst ARASAAC_BASE_PATH_API = 'https://api.arasaac"
  },
  {
    "path": "scripts/arasaac-download-symbols.js",
    "chars": 1711,
    "preview": "const fs = require('fs');\nconst Axios = require('axios');\n\nconst ARASAAC_BASE_PATH_API = 'https://api.arasaac.org/api/';"
  },
  {
    "path": "scripts/crowdin-fetch-latest.js",
    "chars": 5259,
    "preview": "const crowdinTranslations = require('@crowdin/crowdin-api-client').Translations;\nconst https = require('https');\nconst f"
  },
  {
    "path": "scripts/crowdin-push-changes.js",
    "chars": 112,
    "preview": "console.log('Updating remote translations.');\nconsole.log('Not yet implemented (TODO) -- no operation taken.');\n"
  },
  {
    "path": "scripts/decrypt-private.sh",
    "chars": 285,
    "preview": "#!/bin/bash\n\necho \"Decrypting $1 private config gpg\"\necho $ENCRYPTION_KEY | gpg -d --batch --yes --no-tty --passphrase-f"
  },
  {
    "path": "scripts/encrypt-private.sh",
    "chars": 315,
    "preview": "#!/bin/bash\n\necho \"Encrypting $1 private config file\"\ntar -czvf ./private_env_temp-$1.tar.gz ./.private/$1.js\ngpg --batc"
  },
  {
    "path": "src/__mocks__/axios.js",
    "chars": 67,
    "preview": "import mockAxios from 'jest-mock-axios';\nexport default mockAxios;\n"
  },
  {
    "path": "src/__mocks__/react-intl.js",
    "chars": 446,
    "preview": "import React from 'react';\nconst Intl = jest.genMockFromModule('react-intl');\n\n// Here goes intl context injected into c"
  },
  {
    "path": "src/__mocks__/store.js",
    "chars": 1279,
    "preview": "import configureMockStore from 'redux-mock-store';\nimport thunk from 'redux-thunk';\nconst middlewares = [thunk];\nconst m"
  },
  {
    "path": "src/__mocks__/styleMock.js",
    "chars": 21,
    "preview": "module.exports = {};\n"
  },
  {
    "path": "src/__test__/helpers.test.js",
    "chars": 546,
    "preview": "import * as helpers from '../helpers';\ndescribe('helpers', () => {\n  it('should create a file from data', () => {\n    co"
  },
  {
    "path": "src/__test__/i18n.test.js",
    "chars": 551,
    "preview": "import * as i18n from '../i18n';\nimport { error } from 'console';\n\ndescribe('i18n', () => {\n  it('should strip Region Co"
  },
  {
    "path": "src/__test__/reducers.test.js",
    "chars": 3562,
    "preview": "import createReducer, { createMigratingStorage } from '../reducers';\n\ndescribe('reducers', () => {\n  it('should create R"
  },
  {
    "path": "src/__test__/registerServiceWorker.test.js",
    "chars": 630,
    "preview": "import register from '../registerServiceWorker';\nimport unregister from '../registerServiceWorker';\n\ndescribe('registerS"
  },
  {
    "path": "src/__test__/store.test.js",
    "chars": 183,
    "preview": "import configureStore from '../store';\ndescribe('store', () => {\n  it('should configure Store', () => {\n    const store "
  },
  {
    "path": "src/analytics.js",
    "chars": 723,
    "preview": "import { createMiddleware } from 'redux-beacon';\nimport GoogleAnalyticsGtag from '@redux-beacon/google-analytics-gtag';\n"
  },
  {
    "path": "src/api/__mocks__/api.js",
    "chars": 3569,
    "preview": "const mockBoard = {\n  name: 'tewt',\n  id: '12345678901234567',\n  tiles: [{ id: '1234567890123456', loadBoard: '456456456"
  },
  {
    "path": "src/api/api.js",
    "chars": 17318,
    "preview": "import axios from 'axios';\nimport history from '../history';\nimport { alpha2ToAlpha3T } from '@cospired/i18n-iso-languag"
  },
  {
    "path": "src/api/api.test.js",
    "chars": 6353,
    "preview": "import API from './api';\nimport mockAxios from 'jest-mock-axios';\nimport configureMockStore from 'redux-mock-store';\nimp"
  },
  {
    "path": "src/api/boards.json",
    "chars": 190486,
    "preview": "{\n  \"beginner\": [],\n  \"advanced\": [\n    {\n      \"id\": \"root\",\n      \"name\": \"Cboard Classic Home\",\n      \"nameKey\": \"cbo"
  },
  {
    "path": "src/api/cboard-symbols.js",
    "chars": 3859,
    "preview": "/**\n * Cboard Symbols API Client\n *\n * This module provides functionality to search the Cboard Symbol library\n * hosted "
  },
  {
    "path": "src/api/communicators.json",
    "chars": 398,
    "preview": "[\n  {\n    \"id\": \"cboard_default\",\n    \"name\": \"Cboard's Communicator\",\n    \"description\": \"Cboard's default communicator"
  },
  {
    "path": "src/api/corePicSeePal.json",
    "chars": 53979,
    "preview": "[\n  {\n    \"isPublic\": false,\n    \"tiles\": [\n      {\n        \"labelKey\": \"cboard.symbol.yes\",\n        \"image\": \"/symbols/"
  },
  {
    "path": "src/api/index.js",
    "chars": 46,
    "preview": "import API from './api';\n\nexport default API;\n"
  },
  {
    "path": "src/api/mulberry-symbols.json",
    "chars": 352256,
    "preview": "[\n  {\n    \"id\": \"symbol.descriptivePosition.above\",\n    \"src\": \"/symbols/mulberry/above.svg\"\n  },\n  {\n    \"id\": \"symbol."
  },
  {
    "path": "src/appInsights.js",
    "chars": 1275,
    "preview": "import { ApplicationInsights } from '@microsoft/applicationinsights-web';\nimport { NODE_ENV, AZURE_INST_KEY } from './co"
  },
  {
    "path": "src/common/test_utils.js",
    "chars": 697,
    "preview": "import React from 'react';\nimport { IntlProvider } from 'react-intl';\nimport ShallowRenderer from 'react-test-renderer/s"
  },
  {
    "path": "src/components/Account/Activate/Activate.actions.js",
    "chars": 316,
    "preview": "import axios from 'axios';\nimport get from 'lodash/fp/get';\n\nimport { API_URL } from '../../../constants';\n\nexport funct"
  },
  {
    "path": "src/components/Account/Activate/Activate.container.js",
    "chars": 1856,
    "preview": "import React, { Fragment, useCallback, useEffect, useState } from 'react';\nimport { Link, useParams, useHistory } from '"
  },
  {
    "path": "src/components/Account/Activate/Activate.css",
    "chars": 292,
    "preview": ".Activate {\n  height: 100%;\n  padding: 1em;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  alig"
  },
  {
    "path": "src/components/Account/Activate/Activate.messages.js",
    "chars": 581,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  activating: {\n    id: 'cboard.components"
  },
  {
    "path": "src/components/Account/Activate/index.js",
    "chars": 48,
    "preview": "export { default } from './Activate.container';\n"
  },
  {
    "path": "src/components/Account/ChangePassword/ChangePassword.actions.js",
    "chars": 1105,
    "preview": "import API from '../../../api';\nimport {\n  STORE_PASSWORD_API_FAILURE,\n  STORE_PASSWORD_API_STARTED,\n  STORE_PASSWORD_AP"
  },
  {
    "path": "src/components/Account/ChangePassword/ChangePassword.component.js",
    "chars": 5370,
    "preview": "import React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { FormattedMessage, injectIntl, "
  },
  {
    "path": "src/components/Account/ChangePassword/ChangePassword.constants.js",
    "chars": 285,
    "preview": "export const STORE_PASSWORD_API_STARTED =\n  'cboard/ResetPassword/STORE_PASSWORD_API_STARTED';\nexport const STORE_PASSWO"
  },
  {
    "path": "src/components/Account/ChangePassword/ChangePassword.css",
    "chars": 580,
    "preview": ".ChangePassword {\n  height: 100%;\n  padding: 1em;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n"
  },
  {
    "path": "src/components/Account/ChangePassword/ChangePassword.messages.js",
    "chars": 991,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  password: {\n    id: 'cboard.components.C"
  },
  {
    "path": "src/components/Account/ChangePassword/index.js",
    "chars": 54,
    "preview": "export { default } from './ChangePassword.component';\n"
  },
  {
    "path": "src/components/Account/ChangePassword/validationSchema.js",
    "chars": 393,
    "preview": "import * as yup from 'yup';\n\nconst validationSchema = yup.object().shape({\n  password: yup\n    .string()\n    .min(6, 'Pa"
  },
  {
    "path": "src/components/Account/Login/Login.actions.js",
    "chars": 6633,
    "preview": "import API from '../../../api';\nimport { LOGIN_SUCCESS, LOGOUT } from './Login.constants';\nimport { addBoards } from '.."
  },
  {
    "path": "src/components/Account/Login/Login.actions.test.js",
    "chars": 12288,
    "preview": "import * as actions from './Login.actions';\nimport { LOGIN_SUCCESS, LOGOUT } from './Login.constants';\nimport { CHANGE_V"
  },
  {
    "path": "src/components/Account/Login/Login.component.js",
    "chars": 4033,
    "preview": "import React, { useEffect, useState } from 'react';\nimport { connect } from 'react-redux';\nimport { FormattedMessage, in"
  },
  {
    "path": "src/components/Account/Login/Login.component.test.js",
    "chars": 963,
    "preview": "import React from 'react';\nimport { shallow, mount } from 'enzyme';\nimport { shallowMatchSnapshot } from '../../../commo"
  },
  {
    "path": "src/components/Account/Login/Login.constants.js",
    "chars": 104,
    "preview": "export const LOGIN_SUCCESS = 'cboard/Login/LOGIN_SUCCESS';\nexport const LOGOUT = 'cboard/Login/LOGOUT';\n"
  },
  {
    "path": "src/components/Account/Login/Login.css",
    "chars": 231,
    "preview": ".Login__form > div {\n  width: 100%;\n}\n\n.Login__form > div:not(:first-child) {\n  margin-top: 0.75em;\n}\n\n.Login__status {\n"
  },
  {
    "path": "src/components/Account/Login/Login.messages.js",
    "chars": 547,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  login: {\n    id: 'cboard.components.Logi"
  },
  {
    "path": "src/components/Account/Login/__snapshots__/Login.component.test.js.snap",
    "chars": 109,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Login tests default renderer 1`] = `ShallowWrapper {}`;\n"
  },
  {
    "path": "src/components/Account/Login/index.js",
    "chars": 45,
    "preview": "export { default } from './Login.component';\n"
  },
  {
    "path": "src/components/Account/Login/validationSchema.js",
    "chars": 241,
    "preview": "import * as yup from 'yup';\n\nconst validationSchema = yup.object().shape({\n  email: yup\n    .string()\n    .email('Invali"
  },
  {
    "path": "src/components/Account/OAuthLogin/OAuthLogin.container.js",
    "chars": 1963,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { connect } from 'react-redux';\nimport { Formatted"
  },
  {
    "path": "src/components/Account/OAuthLogin/OAuthLogin.css",
    "chars": 241,
    "preview": ".OAuthContainer {\n  height: 100%;\n  padding: 1em;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n"
  },
  {
    "path": "src/components/Account/OAuthLogin/OAuthLogin.messages.js",
    "chars": 303,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  loading: {\n    id: 'cboard.components.OA"
  },
  {
    "path": "src/components/Account/OAuthLogin/index.js",
    "chars": 50,
    "preview": "export { default } from './OAuthLogin.container';\n"
  },
  {
    "path": "src/components/Account/ResetPassword/ResetPassword.actions.js",
    "chars": 976,
    "preview": "import API from '../../../api';\nimport {\n  FORGOT_API_FAILURE,\n  FORGOT_API_STARTED,\n  FORGOT_API_SUCCESS\n} from './Rese"
  },
  {
    "path": "src/components/Account/ResetPassword/ResetPassword.component.js",
    "chars": 4240,
    "preview": "import React, { useState, useEffect } from 'react';\nimport PropTypes from 'prop-types';\nimport { connect } from 'react-r"
  },
  {
    "path": "src/components/Account/ResetPassword/ResetPassword.constants.js",
    "chars": 231,
    "preview": "export const FORGOT_API_STARTED = 'cboard/ResetPassword/FORGOT_API_STARTED';\nexport const FORGOT_API_SUCCESS = 'cboard/R"
  },
  {
    "path": "src/components/Account/ResetPassword/ResetPassword.css",
    "chars": 236,
    "preview": ".Forgot__form > div {\n  width: 100%;\n}\n\n.Forgot__form > div:not(:first-child) {\n  margin-top: 0.75em;\n}\n\n.Forgot__status"
  },
  {
    "path": "src/components/Account/ResetPassword/ResetPassword.messages.js",
    "chars": 1001,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  email: {\n    id: 'cboard.components.Rese"
  },
  {
    "path": "src/components/Account/ResetPassword/index.js",
    "chars": 53,
    "preview": "export { default } from './ResetPassword.component';\n"
  },
  {
    "path": "src/components/Account/ResetPassword/validationSchema.js",
    "chars": 194,
    "preview": "import * as yup from 'yup';\n\nconst validationSchema = yup.object().shape({\n  email: yup\n    .string()\n    .email('Invali"
  },
  {
    "path": "src/components/Account/SignUp/SignUp.actions.js",
    "chars": 245,
    "preview": "import axios from 'axios';\nimport get from 'lodash/fp/get';\n\nimport { API_URL } from '../../../constants';\n\nexport funct"
  },
  {
    "path": "src/components/Account/SignUp/SignUp.component.js",
    "chars": 6516,
    "preview": "import React, { useState, useEffect } from 'react';\nimport PropTypes from 'prop-types';\nimport { FormattedMessage, injec"
  },
  {
    "path": "src/components/Account/SignUp/SignUp.css",
    "chars": 658,
    "preview": ".SignUp__form > div {\n  width: 100%;\n}\n\n.SignUp__form > div:not(:first-child) {\n  margin-top: 0.75em;\n}\n\n.SignUp__form >"
  },
  {
    "path": "src/components/Account/SignUp/SignUp.messages.js",
    "chars": 1276,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  signUp: {\n    id: 'cboard.components.Sig"
  },
  {
    "path": "src/components/Account/SignUp/Signup.component.test.js",
    "chars": 1401,
    "preview": "import React from 'react';\nimport { mount } from 'enzyme';\nimport SignUp from './SignUp.component';\n\njest.mock('./SignUp"
  },
  {
    "path": "src/components/Account/SignUp/__snapshots__/Signup.component.test.js.snap",
    "chars": 108,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`SignUp tests default renderer 1`] = `ReactWrapper {}`;\n"
  },
  {
    "path": "src/components/Account/SignUp/index.js",
    "chars": 46,
    "preview": "export { default } from './SignUp.component';\n"
  },
  {
    "path": "src/components/Account/SignUp/validationSchema.js",
    "chars": 589,
    "preview": "import * as yup from 'yup';\n\nconst validationSchema = yup.object().shape({\n  password: yup\n    .string()\n    .required('"
  },
  {
    "path": "src/components/Analytics/Analytics.component.js",
    "chars": 8077,
    "preview": "import React, { PureComponent, Fragment } from 'react';\nimport PropTypes from 'prop-types';\nimport { FormattedMessage, i"
  },
  {
    "path": "src/components/Analytics/Analytics.container.js",
    "chars": 11861,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { connect } from 'react-redux';\nimp"
  },
  {
    "path": "src/components/Analytics/Analytics.css",
    "chars": 1069,
    "preview": ".Analytics {\n  display: flex;\n  flex-direction: column;\n  position: relative;\n  flex: 1 1;\n}\n\n.Analytics__Graph {\n  disp"
  },
  {
    "path": "src/components/Analytics/Analytics.messages.js",
    "chars": 2310,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  analytics: {\n    id: 'cboard.components."
  },
  {
    "path": "src/components/Analytics/index.js",
    "chars": 49,
    "preview": "export { default } from './Analytics.container';\n"
  },
  {
    "path": "src/components/App/App.actions.js",
    "chars": 3650,
    "preview": "import API from '../../api';\nimport {\n  FINISH_FIRST_VISIT,\n  UPDATE_DISPLAY_SETTINGS,\n  UPDATE_NAVIGATION_SETTINGS,\n  U"
  },
  {
    "path": "src/components/App/App.component.js",
    "chars": 2803,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport Helmet from 'react-helmet';\nimport "
  },
  {
    "path": "src/components/App/App.constants.js",
    "chars": 1633,
    "preview": "// action types constants\nexport const FINISH_FIRST_VISIT = 'cboard/App/FINISH_FIRST_VISIT';\nexport const DISABLE_TOUR ="
  },
  {
    "path": "src/components/App/App.container.js",
    "chars": 4881,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { connect } from 'react-redux';\nimp"
  },
  {
    "path": "src/components/App/App.css",
    "chars": 180,
    "preview": ".App {\n  height: 100%;\n}\n\n.Cboard__FontSize__Standard {\n  font-size: 14px;\n}\n\n.Cboard__FontSize__Large {\n  font-size: 18"
  },
  {
    "path": "src/components/App/App.messages.js",
    "chars": 440,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  save: {\n    id: 'cboard.components.App.s"
  },
  {
    "path": "src/components/App/App.reducer.js",
    "chars": 4734,
    "preview": "import {\n  FINISH_FIRST_VISIT,\n  UPDATE_CONNECTIVITY,\n  UPDATE_DISPLAY_SETTINGS,\n  UPDATE_NAVIGATION_SETTINGS,\n  UPDATE_"
  },
  {
    "path": "src/components/App/App.selectors.js",
    "chars": 211,
    "preview": "import isEmpty from 'lodash/isEmpty';\n\nexport const getUser = state => state.app.userData;\nexport const isLogged = state"
  },
  {
    "path": "src/components/App/__tests__/App.actions.test.js",
    "chars": 1863,
    "preview": "import * as actions from '../App.actions';\nimport * as types from '../App.constants';\nimport { getUser, isFirstVisit, is"
  },
  {
    "path": "src/components/App/__tests__/App.reducer.test.js",
    "chars": 4439,
    "preview": "import { DEFAULT_FONT_FAMILY } from '../../../providers/ThemeProvider/ThemeProvider.constants';\nimport { LOGIN_SUCCESS, "
  },
  {
    "path": "src/components/App/index.js",
    "chars": 43,
    "preview": "export { default } from './App.container';\n"
  },
  {
    "path": "src/components/AppLoading/AppLoading.css",
    "chars": 376,
    "preview": ".AppLoading {\n  height: 100%;\n  padding: 1em;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  al"
  },
  {
    "path": "src/components/AppLoading/AppLoading.js",
    "chars": 410,
    "preview": "import React from 'react';\nimport CircularProgress from '@material-ui/core/CircularProgress';\n\nimport './AppLoading.css'"
  },
  {
    "path": "src/components/AppLoading/index.js",
    "chars": 40,
    "preview": "export { default } from './AppLoading';\n"
  },
  {
    "path": "src/components/AuthScreen/Auth.helpers.js",
    "chars": 537,
    "preview": "import React from 'react';\nimport { Route, Redirect } from 'react-router-dom';\n\nexport const PrivateRoute = ({\n  compone"
  },
  {
    "path": "src/components/AuthScreen/AuthScreen.component.js",
    "chars": 750,
    "preview": "import React, { Component } from 'react';\nimport { injectIntl, intlShape } from 'react-intl';\nimport WelcomeScreen from "
  },
  {
    "path": "src/components/AuthScreen/AuthScreen.component.test.js",
    "chars": 964,
    "preview": "import React from 'react';\nimport { shallow, mount } from 'enzyme';\nimport { shallowMatchSnapshot } from '../../common/t"
  },
  {
    "path": "src/components/AuthScreen/AuthScreen.css",
    "chars": 1493,
    "preview": ".AuthScreen {\n  position: absolute;\n  top: 0;\n  left: 0;\n  height: 100%;\n  width: 100%;\n  padding: 1em 1em 2.5em;\n  z-in"
  },
  {
    "path": "src/components/AuthScreen/AuthScreen.messages.js",
    "chars": 311,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  heading: {\n    id: 'cboard.components.Au"
  },
  {
    "path": "src/components/AuthScreen/__snapshots__/AuthScreen.component.test.js.snap",
    "chars": 243,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`AuthScreen tests default renderer 1`] = `\n<AuthScreen\n  history={\n "
  },
  {
    "path": "src/components/AuthScreen/index.js",
    "chars": 82,
    "preview": "export { default } from './AuthScreen.component';\nexport * from './Auth.helpers';\n"
  },
  {
    "path": "src/components/Board/Board.actions.js",
    "chars": 22300,
    "preview": "import isUrl from 'is-url';\n\nimport {\n  IMPORT_BOARDS,\n  ADD_BOARDS,\n  CHANGE_BOARD,\n  SWITCH_BOARD,\n  PREVIOUS_BOARD,\n "
  },
  {
    "path": "src/components/Board/Board.analytics.js",
    "chars": 3688,
    "preview": "import { trackEvent } from '@redux-beacon/google-analytics-gtag';\nimport { isCordova, cvaTrackEvent } from '../../cordov"
  },
  {
    "path": "src/components/Board/Board.component.js",
    "chars": 18409,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport keycode from 'keycode';\nimport clas"
  },
  {
    "path": "src/components/Board/Board.constants.js",
    "chars": 2827,
    "preview": "export const IMPORT_BOARDS = 'cboard/Board/IMPORT_BOARDS';\nexport const ADD_BOARDS = 'cboard/Board/ADD_BOARDS';\nexport c"
  },
  {
    "path": "src/components/Board/Board.container.js",
    "chars": 51740,
    "preview": "import React, { Component, Fragment } from 'react';\nimport PropTypes from 'prop-types';\nimport { connect } from 'react-r"
  },
  {
    "path": "src/components/Board/Board.css",
    "chars": 1518,
    "preview": ".Board {\n  height: 100%;\n  overflow-y: hidden;\n  display: flex;\n  flex-direction: column;\n}\n\n.Board__output {\n  position"
  },
  {
    "path": "src/components/Board/Board.messages.js",
    "chars": 8041,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  tilesDeleted: {\n    id: 'cboard.componen"
  },
  {
    "path": "src/components/Board/Board.reducer.js",
    "chars": 12652,
    "preview": "import moment from 'moment';\n\nimport { DEFAULT_BOARDS, deepCopy } from '../../helpers';\n\nimport {\n  IMPORT_BOARDS,\n  ADD"
  },
  {
    "path": "src/components/Board/BoardShare/BoardShare.component.js",
    "chars": 7029,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Link } from 'react-router-dom';\nimport Dialog fr"
  },
  {
    "path": "src/components/Board/BoardShare/BoardShare.css",
    "chars": 1196,
    "preview": ".ShareDialog__container .ShareDialog__title {\n  border-bottom: 1px solid #ccc;\n  margin-bottom: 18px;\n  padding: 20px 24"
  },
  {
    "path": "src/components/Board/BoardShare/BoardShare.messages.js",
    "chars": 1812,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  title: {\n    id: 'cboard.components.Boar"
  },
  {
    "path": "src/components/Board/BoardShare/index.js",
    "chars": 77,
    "preview": "import BoardShare from './BoardShare.component';\n\nexport default BoardShare;\n"
  },
  {
    "path": "src/components/Board/BoardTour/BoardTour.js",
    "chars": 5061,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { FormattedMessage } from 'react-intl';\nimport Joy"
  },
  {
    "path": "src/components/Board/EditToolbar/EditToolbar.component.js",
    "chars": 8041,
    "preview": "import React, { Fragment, useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { injectIntl, intlShape } "
  },
  {
    "path": "src/components/Board/EditToolbar/EditToolbar.css",
    "chars": 1179,
    "preview": ".EditToolbar {\n  position: relative;\n  display: flex;\n  justify-content: space-around;\n  height: 40px;\n  color: #fff;\n  "
  },
  {
    "path": "src/components/Board/EditToolbar/EditToolbar.messages.js",
    "chars": 1727,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  select: {\n    id: 'cboard.components.Boa"
  },
  {
    "path": "src/components/Board/EditToolbar/EditToolbar.test.js",
    "chars": 209,
    "preview": "import React from 'react';\nimport { shallow } from 'enzyme';\n\nimport EditToolbar from './EditToolbar.component';\n\nit('re"
  },
  {
    "path": "src/components/Board/EditToolbar/index.js",
    "chars": 51,
    "preview": "export { default } from './EditToolbar.component';\n"
  },
  {
    "path": "src/components/Board/EmptyBoard/EmptyBoard.component.js",
    "chars": 836,
    "preview": "import React from 'react';\nimport classNames from 'classnames';\nimport { FormattedMessage } from 'react-intl';\nimport Vi"
  },
  {
    "path": "src/components/Board/EmptyBoard/EmptyBoard.css",
    "chars": 196,
    "preview": ".EmptyBoard {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 100"
  },
  {
    "path": "src/components/Board/EmptyBoard/EmptyBoard.messages.js",
    "chars": 194,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  boardIsEmpty: {\n    id: 'cboard.componen"
  },
  {
    "path": "src/components/Board/EmptyBoard/EmptyBoard.test.js",
    "chars": 574,
    "preview": "import React from 'react';\nimport { mount, shallow } from 'enzyme';\nimport { shallowMatchSnapshot } from '../../../commo"
  },
  {
    "path": "src/components/Board/EmptyBoard/__snapshots__/EmptyBoard.test.js.snap",
    "chars": 265,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`EmptyBoard tests default renderer 1`] = `\n<EmptyBoard\n  classes={\n "
  },
  {
    "path": "src/components/Board/EmptyBoard/index.js",
    "chars": 50,
    "preview": "export { default } from './EmptyBoard.component';\n"
  },
  {
    "path": "src/components/Board/ImageEditor/ImageEditor.component.js",
    "chars": 6180,
    "preview": "import React, { PureComponent } from 'react';\nimport PropTypes from 'prop-types';\nimport { FormattedMessage } from 'reac"
  },
  {
    "path": "src/components/Board/ImageEditor/ImageEditor.css",
    "chars": 327,
    "preview": ".ImageEditor__container {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.ImageEditor__act"
  },
  {
    "path": "src/components/Board/ImageEditor/ImageEditor.messages.js",
    "chars": 920,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  title: {\n    id: 'cboard.components.Boar"
  },
  {
    "path": "src/components/Board/ImageEditor/index.js",
    "chars": 51,
    "preview": "export { default } from './ImageEditor.component';\n"
  },
  {
    "path": "src/components/Board/ImprovePhraseOutput/ImprovePhraseOutput.js",
    "chars": 1048,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\n\nimport styles from './ImprovePhraseOutput.module.css';\n\n"
  },
  {
    "path": "src/components/Board/ImprovePhraseOutput/ImprovePhraseOutput.module.css",
    "chars": 726,
    "preview": ".text_and_controls {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  box-shadow: 0px 3px 4px"
  },
  {
    "path": "src/components/Board/ImprovePhraseOutput/index.js",
    "chars": 49,
    "preview": "export { default } from './ImprovePhraseOutput';\n"
  },
  {
    "path": "src/components/Board/Navbar/Navbar.css",
    "chars": 1126,
    "preview": ".Navbar {\n  position: relative;\n  display: flex;\n  justify-content: space-around;\n  height: 48px;\n  background: #222;\n  "
  },
  {
    "path": "src/components/Board/Navbar/Navbar.js",
    "chars": 6123,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\nimport { Link, withRouter } from 'react-router-dom';\nimpo"
  },
  {
    "path": "src/components/Board/Navbar/Navbar.test.js",
    "chars": 1048,
    "preview": "import React from 'react';\nimport { shallow, mount } from 'enzyme';\n\nimport Navbar from './Navbar';\n\nconst mockBoard = {"
  },
  {
    "path": "src/components/Board/Navbar/__snapshots__/Navbar.test.js.snap",
    "chars": 200,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`NavBar tests renders without crashing 1`] = `ShallowWrapper {}`;\n\ne"
  },
  {
    "path": "src/components/Board/Navbar/index.js",
    "chars": 36,
    "preview": "export { default } from './Navbar';\n"
  },
  {
    "path": "src/components/Board/Output/Output.container.js",
    "chars": 9731,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { injectIntl, intlShape } from 'rea"
  },
  {
    "path": "src/components/Board/Output/PhraseShare/PhraseShare.component.js",
    "chars": 4460,
    "preview": "import React from 'react';\nimport PropTypes from 'prop-types';\n//import { Link } from 'react-router-dom';\nimport Dialog "
  },
  {
    "path": "src/components/Board/Output/PhraseShare/PhraseShare.css",
    "chars": 1093,
    "preview": ".SharePhraseDialog__container .SharePhraseDialog__title {\n  border-bottom: 1px solid #ccc;\n  margin-bottom: 18px;\n  padd"
  },
  {
    "path": "src/components/Board/Output/PhraseShare/PhraseShare.messages.js",
    "chars": 1328,
    "preview": "import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n  title: {\n    id: 'cboard.components.Phra"
  },
  {
    "path": "src/components/Board/Output/PhraseShare/index.js",
    "chars": 80,
    "preview": "import PhraseShare from './PhraseShare.component';\n\nexport default PhraseShare;\n"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/BackspaceButton/BackspaceButton.js",
    "chars": 1214,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { withStyles } from '@material-ui/c"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/BackspaceButton/index.js",
    "chars": 45,
    "preview": "export { default } from './BackspaceButton';\n"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/ClearButton/ClearButton.js",
    "chars": 919,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport IconButton from '@material-ui/core/"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/ClearButton/ClearButton.test.js",
    "chars": 269,
    "preview": "import React from 'react';\nimport { shallowMatchSnapshot } from '../../../../../common/test_utils';\n\nimport ClearButton "
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/ClearButton/__snapshots__/ClearButton.test.js.snap",
    "chars": 312,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ClearButton tests default renderer 1`] = `\n<Unknown>\n  <WithStyles("
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/ClearButton/index.js",
    "chars": 41,
    "preview": "export { default } from './ClearButton';\n"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/Scroll/Scroll.css",
    "chars": 480,
    "preview": ".Scroll__container {\n  position: relative;\n  width: auto;\n  height: 100%;\n  display: flex;\n  flex-grow: 1;\n  align-items"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/Scroll/Scroll.js",
    "chars": 818,
    "preview": "import React, { PureComponent } from 'react';\nimport PropTypes from 'prop-types';\nimport { withStyles } from '@material-"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/Scroll/Scroll.test.js",
    "chars": 160,
    "preview": "import React from 'react';\nimport { shallow } from 'enzyme';\n\nimport Scroll from './Scroll';\n\nit('renders without crashi"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/Scroll/index.js",
    "chars": 36,
    "preview": "export { default } from './Scroll';\n"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/ShareButton/ShareButton.js",
    "chars": 1157,
    "preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { withStyles } from '@material-ui/c"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/ShareButton/index.js",
    "chars": 41,
    "preview": "export { default } from './ShareButton';\n"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/SymbolOutput.css",
    "chars": 2289,
    "preview": ".SymbolOutput {\n  height: 100%;\n  width: 100%;\n  display: flex;\n  background: #fff;\n  color: rgba(0, 0, 0, 0.54);\n}\n\n.is"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/SymbolOutput.js",
    "chars": 6150,
    "preview": "import React, { PureComponent } from 'react';\nimport PropTypes from 'prop-types';\n\nimport ClearIcon from '@material-ui/i"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/SymbolOutput.test.js",
    "chars": 1338,
    "preview": "import React from 'react';\nimport { shallow, mount } from 'enzyme';\n\nimport SymbolOutput from './SymbolOutput';\nimport C"
  },
  {
    "path": "src/components/Board/Output/SymbolOutput/index.js",
    "chars": 42,
    "preview": "export { default } from './SymbolOutput';\n"
  },
  {
    "path": "src/components/Board/Output/index.js",
    "chars": 46,
    "preview": "export { default } from './Output.container';\n"
  },
  {
    "path": "src/components/Board/Symbol/Symbol.css",
    "chars": 1692,
    "preview": ".Symbol {\n  height: 100%;\n  width: 100%;\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-end;\n  overf"
  },
  {
    "path": "src/components/Board/Symbol/Symbol.js",
    "chars": 4029,
    "preview": "import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport PropTypes from 'prop-types';\nimport clas"
  }
]

// ... and 636 more files (download for full content)

About this extraction

This page contains the full source code of the shayc/cboard GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 836 files (20.7 MB), approximately 5.5M tokens, and a symbol index with 1508 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!